palkan / anyway_config

Configuration library for Ruby gems and applications

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Rails custom inflection support

nicolas-brousse opened this issue · comments

What did you do?

I've a config file in Rails application that follow an inflection acronym.

# config/initializers/inflections.rb

ActiveSupport::Inflector.inflections do |inflect|
  inflect.acronym "API"
end
# config/configs/payzen_api_config.rb

class PayzenAPIConfig < ApplicationConfig
  env_prefix :payzen_api
  attr_config :username, :password, :public_key, :sha256key
end

What did you expect to happen?

Code works and load correctly.

What actually happened?

I've got the following error.

expected file /home/user/Projects/rails_app/config/configs/payzen_api_config.rb to define constant PayzenApiConfig, but didn't

Additional context

I suspect eager load not to use rails loader or not the rails inflector.
https://github.com/palkan/anyway_config/blob/master/lib/anyway/railtie.rb#L16

Environment

Ruby Version: ruby 2.7.3p183 (2021-04-05 revision 6847ee089d) [x86_64-linux]

Framework Version (Rails, whatever): Rails 6.0.3.7

Anyway Config Version: 2.1.0

We do use Rails inflector:

loader.inflector = ActiveSupport::Dependencies::ZeitwerkIntegration::Inflector

Can you show me the full backtrace? I guess, that could happen when you try to load the config class before the inflector initializer is loaded.

@palkan thanks for your answer. I'll have a look on it an may try to create a sample application that have the same issue.

I try to reproduce it on a rails template but I can't reproduce the issue.

In my application, I've the following full backtrace on page loading:

Backtrace
zeitwerk (2.4.2) lib/zeitwerk/loader/callbacks.rb:18:in `on_file_autoloaded'
zeitwerk (2.4.2) lib/zeitwerk/kernel.rb:27:in `block in require'
zeitwerk (2.4.2) lib/zeitwerk/kernel.rb:26:in `tap'
zeitwerk (2.4.2) lib/zeitwerk/kernel.rb:26:in `require'
lib/pay_zen.rb:22:in `public_key'
app/helpers/payment_helper.rb:8:in `block in payzen_headers'
actionview (6.0.3.7) lib/action_view/helpers/capture_helper.rb:45:in `block in capture'
actionview (6.0.3.7) lib/action_view/helpers/capture_helper.rb:209:in `with_output_buffer'
actionview (6.0.3.7) lib/action_view/helpers/capture_helper.rb:45:in `capture'
actionview (6.0.3.7) lib/action_view/helpers/capture_helper.rb:176:in `provide'
app/helpers/payment_helper.rb:7:in `payzen_headers'
app/views/carts/payments/new.html.slim:1
actionview (6.0.3.7) lib/action_view/base.rb:274:in `_run'
actionview (6.0.3.7) lib/action_view/template.rb:185:in `block in render'
activesupport (6.0.3.7) lib/active_support/notifications.rb:180:in `block in instrument'
activesupport (6.0.3.7) lib/active_support/notifications/instrumenter.rb:24:in `instrument'
sentry-rails (4.3.4) lib/sentry/rails/tracing.rb:41:in `instrument'
activesupport (6.0.3.7) lib/active_support/notifications.rb:180:in `instrument'
actionview (6.0.3.7) lib/action_view/template.rb:385:in `instrument_render_template'
actionview (6.0.3.7) lib/action_view/template.rb:183:in `render'
actionview (6.0.3.7) lib/action_view/renderer/template_renderer.rb:58:in `block (2 levels) in render_template'
actionview (6.0.3.7) lib/action_view/renderer/abstract_renderer.rb:88:in `block in instrument'
activesupport (6.0.3.7) lib/active_support/notifications.rb:180:in `block in instrument'
activesupport (6.0.3.7) lib/active_support/notifications/instrumenter.rb:24:in `instrument'
sentry-rails (4.3.4) lib/sentry/rails/tracing.rb:41:in `instrument'
activesupport (6.0.3.7) lib/active_support/notifications.rb:180:in `instrument'
actionview (6.0.3.7) lib/action_view/renderer/abstract_renderer.rb:87:in `instrument'
actionview (6.0.3.7) lib/action_view/renderer/template_renderer.rb:57:in `block in render_template'
actionview (6.0.3.7) lib/action_view/renderer/template_renderer.rb:65:in `render_with_layout'
actionview (6.0.3.7) lib/action_view/renderer/template_renderer.rb:56:in `render_template'
actionview (6.0.3.7) lib/action_view/renderer/template_renderer.rb:13:in `render'
actionview (6.0.3.7) lib/action_view/renderer/renderer.rb:61:in `render_template_to_object'
actionview (6.0.3.7) lib/action_view/renderer/renderer.rb:29:in `render_to_object'
actionview (6.0.3.7) lib/action_view/rendering.rb:117:in `block in _render_template'
actionview (6.0.3.7) lib/action_view/base.rb:304:in `in_rendering_context'
actionview (6.0.3.7) lib/action_view/rendering.rb:116:in `_render_template'
actionpack (6.0.3.7) lib/action_controller/metal/streaming.rb:218:in `_render_template'
actionview (6.0.3.7) lib/action_view/rendering.rb:103:in `render_to_body'
actionpack (6.0.3.7) lib/action_controller/metal/rendering.rb:52:in `render_to_body'
actionpack (6.0.3.7) lib/action_controller/metal/renderers.rb:142:in `render_to_body'
actionpack (6.0.3.7) lib/abstract_controller/rendering.rb:25:in `render'
actionpack (6.0.3.7) lib/action_controller/metal/rendering.rb:36:in `render'
actionpack (6.0.3.7) lib/action_controller/metal/instrumentation.rb:44:in `block (2 levels) in render'
activesupport (6.0.3.7) lib/active_support/core_ext/benchmark.rb:14:in `block in ms'
/home/n.brousse/.rbenv/versions/2.7.3/lib/ruby/2.7.0/benchmark.rb:308:in `realtime'
activesupport (6.0.3.7) lib/active_support/core_ext/benchmark.rb:14:in `ms'
actionpack (6.0.3.7) lib/action_controller/metal/instrumentation.rb:44:in `block in render'
actionpack (6.0.3.7) lib/action_controller/metal/instrumentation.rb:84:in `cleanup_view_runtime'
activerecord (6.0.3.7) lib/active_record/railties/controller_runtime.rb:34:in `cleanup_view_runtime'
actionpack (6.0.3.7) lib/action_controller/metal/instrumentation.rb:43:in `render'
view_component (2.31.1) lib/view_component/rendering_monkey_patch.rb:9:in `render'
actionpack (6.0.3.7) lib/action_controller/metal/implicit_render.rb:35:in `default_render'
actionpack (6.0.3.7) lib/action_controller/metal/basic_implicit_render.rb:6:in `block in send_action'
actionpack (6.0.3.7) lib/action_controller/metal/basic_implicit_render.rb:6:in `tap'
actionpack (6.0.3.7) lib/action_controller/metal/basic_implicit_render.rb:6:in `send_action'
actionpack (6.0.3.7) lib/abstract_controller/base.rb:195:in `process_action'
actionpack (6.0.3.7) lib/action_controller/metal/rendering.rb:30:in `process_action'
actionpack (6.0.3.7) lib/abstract_controller/callbacks.rb:42:in `block in process_action'
activesupport (6.0.3.7) lib/active_support/callbacks.rb:112:in `block in run_callbacks'
app/controllers/concerns/localization.rb:36:in `block in set_locale'
i18n (1.8.10) lib/i18n.rb:314:in `with_locale'
app/controllers/concerns/localization.rb:33:in `set_locale'
activesupport (6.0.3.7) lib/active_support/callbacks.rb:121:in `block in run_callbacks'
activesupport (6.0.3.7) lib/active_support/callbacks.rb:139:in `run_callbacks'
actionpack (6.0.3.7) lib/abstract_controller/callbacks.rb:41:in `process_action'
actionpack (6.0.3.7) lib/action_controller/metal/rescue.rb:22:in `process_action'
actionpack (6.0.3.7) lib/action_controller/metal/instrumentation.rb:33:in `block in process_action'
activesupport (6.0.3.7) lib/active_support/notifications.rb:180:in `block in instrument'
activesupport (6.0.3.7) lib/active_support/notifications/instrumenter.rb:24:in `instrument'
sentry-rails (4.3.4) lib/sentry/rails/tracing.rb:41:in `instrument'
activesupport (6.0.3.7) lib/active_support/notifications.rb:180:in `instrument'
actionpack (6.0.3.7) lib/action_controller/metal/instrumentation.rb:32:in `process_action'
actionpack (6.0.3.7) lib/action_controller/metal/params_wrapper.rb:245:in `process_action'
activerecord (6.0.3.7) lib/active_record/railties/controller_runtime.rb:27:in `process_action'
actionpack (6.0.3.7) lib/abstract_controller/base.rb:136:in `process'
actionview (6.0.3.7) lib/action_view/rendering.rb:39:in `process'
actionpack (6.0.3.7) lib/action_controller/metal.rb:190:in `dispatch'
actionpack (6.0.3.7) lib/action_controller/metal.rb:254:in `dispatch'
actionpack (6.0.3.7) lib/action_dispatch/routing/route_set.rb:50:in `dispatch'
actionpack (6.0.3.7) lib/action_dispatch/routing/route_set.rb:33:in `serve'
actionpack (6.0.3.7) lib/action_dispatch/journey/router.rb:49:in `block in serve'
actionpack (6.0.3.7) lib/action_dispatch/journey/router.rb:32:in `each'
actionpack (6.0.3.7) lib/action_dispatch/journey/router.rb:32:in `serve'
actionpack (6.0.3.7) lib/action_dispatch/routing/route_set.rb:834:in `call'
sentry-rails (4.3.4) lib/sentry/rails/rescued_exception_interceptor.rb:12:in `call'
http_accept_language (2.1.1) lib/http_accept_language/middleware.rb:14:in `call'
bullet (6.1.0) lib/bullet/rack.rb:15:in `call'
rack (2.2.3) lib/rack/tempfile_reaper.rb:15:in `call'
rack (2.2.3) lib/rack/etag.rb:27:in `call'
rack (2.2.3) lib/rack/conditional_get.rb:27:in `call'
rack (2.2.3) lib/rack/head.rb:12:in `call'
actionpack (6.0.3.7) lib/action_dispatch/http/content_security_policy.rb:18:in `call'
rack (2.2.3) lib/rack/session/abstract/id.rb:266:in `context'
rack (2.2.3) lib/rack/session/abstract/id.rb:260:in `call'
actionpack (6.0.3.7) lib/action_dispatch/middleware/cookies.rb:648:in `call'
activerecord (6.0.3.7) lib/active_record/migration.rb:567:in `call'
actionpack (6.0.3.7) lib/action_dispatch/middleware/callbacks.rb:27:in `block in call'
activesupport (6.0.3.7) lib/active_support/callbacks.rb:101:in `run_callbacks'
actionpack (6.0.3.7) lib/action_dispatch/middleware/callbacks.rb:26:in `call'
actionpack (6.0.3.7) lib/action_dispatch/middleware/executor.rb:14:in `call'
actionpack (6.0.3.7) lib/action_dispatch/middleware/actionable_exceptions.rb:18:in `call'
actionpack (6.0.3.7) lib/action_dispatch/middleware/debug_exceptions.rb:32:in `call'
web-console (4.1.0) lib/web_console/middleware.rb:132:in `call_app'
web-console (4.1.0) lib/web_console/middleware.rb:28:in `block in call'
web-console (4.1.0) lib/web_console/middleware.rb:17:in `catch'
web-console (4.1.0) lib/web_console/middleware.rb:17:in `call'
actionpack (6.0.3.7) lib/action_dispatch/middleware/show_exceptions.rb:33:in `call'
railties (6.0.3.7) lib/rails/rack/logger.rb:37:in `call_app'
railties (6.0.3.7) lib/rails/rack/logger.rb:26:in `block in call'
activesupport (6.0.3.7) lib/active_support/tagged_logging.rb:80:in `block in tagged'
activesupport (6.0.3.7) lib/active_support/tagged_logging.rb:28:in `tagged'
activesupport (6.0.3.7) lib/active_support/tagged_logging.rb:80:in `tagged'
railties (6.0.3.7) lib/rails/rack/logger.rb:26:in `call'
sprockets-rails (3.2.2) lib/sprockets/rails/quiet_assets.rb:13:in `call'
actionpack (6.0.3.7) lib/action_dispatch/middleware/remote_ip.rb:81:in `call'
request_store (1.5.0) lib/request_store/middleware.rb:19:in `call'
actionpack (6.0.3.7) lib/action_dispatch/middleware/request_id.rb:27:in `call'
rack (2.2.3) lib/rack/method_override.rb:24:in `call'
rack (2.2.3) lib/rack/runtime.rb:22:in `call'
activesupport (6.0.3.7) lib/active_support/cache/strategy/local_cache_middleware.rb:29:in `call'
actionpack (6.0.3.7) lib/action_dispatch/middleware/executor.rb:14:in `call'
actionpack (6.0.3.7) lib/action_dispatch/middleware/static.rb:126:in `call'
rack (2.2.3) lib/rack/sendfile.rb:110:in `call'
actionpack (6.0.3.7) lib/action_dispatch/middleware/host_authorization.rb:82:in `call'
sentry-ruby-core (4.3.2) lib/sentry/rack/capture_exceptions.rb:23:in `block in call'
sentry-ruby-core (4.3.2) lib/sentry/hub.rb:52:in `with_scope'
sentry-ruby-core (4.3.2) lib/sentry-ruby.rb:147:in `with_scope'
sentry-ruby-core (4.3.2) lib/sentry/rack/capture_exceptions.rb:14:in `call'
rack-mini-profiler (2.3.2) lib/mini_profiler/profiler.rb:384:in `call'
webpacker (5.4.0) lib/webpacker/dev_server_proxy.rb:25:in `perform_request'
rack-proxy (0.6.5) lib/rack/proxy.rb:57:in `call'
railties (6.0.3.7) lib/rails/engine.rb:527:in `call'
puma (5.3.1) lib/puma/configuration.rb:249:in `call'
puma (5.3.1) lib/puma/request.rb:76:in `block in handle_request'
puma (5.3.1) lib/puma/thread_pool.rb:338:in `with_force_shutdown'
puma (5.3.1) lib/puma/request.rb:75:in `handle_request'
puma (5.3.1) lib/puma/server.rb:437:in `process_client'
puma (5.3.1) lib/puma/thread_pool.rb:145:in `block in spawn_thread' 

I've the issue on both webpage and console. On webpage it's on a specific page that call Payzen API, and so call config file to get credentials.

I've inspect the code a bit more.

config.before_configuration do
next if ::Rails.application.initialized?
config.anyway_config.autoload_static_config_path = DEFAULT_CONFIGS_PATH
end

If I'm not wrong, before_configuration execute code before initializers are loaded. I guess this is done to have access to config file in initializers.
But because of this, config/initializers/inflections.rb is not loaded yet.

What I've done to try to understand what the code doing is to put a binding.pry at the end of the following method:

def autoload_static_config_path=(val)
raise "Cannot setup autoloader after application has been initialized" if ::Rails.application.initialized?
return unless ::Rails.root.join(val).exist?
autoloader&.unload
@autoload_static_config_path = val
# See https://github.com/rails/rails/blob/8ab4fd12f18203b83d0f252db96d10731485ff6a/railties/lib/rails/autoloaders.rb#L10
@autoloader = Zeitwerk::Loader.new.tap do |loader|
loader.tag = "anyway.config"
loader.inflector = ActiveSupport::Dependencies::ZeitwerkIntegration::Inflector
loader.push_dir(::Rails.root.join(val))
loader.setup
end
end

And I notice the following:

[4] pry(Anyway::Settings)> @autoloader.autoloads
=> {"/home/user/Projects/rails/config/configs/application_config.rb"=>[Object, :ApplicationConfig],
 "/home/user/Projects/rails/config/configs/payzen_api_config.rb"=>[Object, :PayzenApiConfig]

Seems the class name already defined, before inflection initializers loaded.

Thanks for the investigation!

Seems the class name already defined, before inflection initializers loaded.

Yeah, we call loader.setup before initialization. Since the point of this loader is to configure autoload before initialization, we cannot have an access to initializers here.

I suggest configuring inflections in the very beginning of the application boot process, e.g., in your config/application.rb:

require_relative "initializers/inflections"

module MyApp
  class Application < Rails::Application 
    # ...
  end
end

Added a note to readme: 883c781

@palkan thank you for the support!