palkan / anyway_config

Configuration library for Ruby gems and applications
MIT License
778 stars 52 forks source link

Rails custom inflection support #81

Closed nicolas-brousse closed 3 years ago

nicolas-brousse commented 3 years ago

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

palkan commented 3 years ago

We do use Rails inflector: https://github.com/palkan/anyway_config/blob/fb2622bf30e61a834567d0945568606b2b34e12a/lib/anyway/rails/settings.rb#L33

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.

nicolas-brousse commented 3 years ago

@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.

nicolas-brousse commented 3 years ago

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.

nicolas-brousse commented 3 years ago

I've inspect the code a bit more.

https://github.com/palkan/anyway_config/blob/068fdf1504aa21ee04863a43fa99152c53581753/lib/anyway/railtie.rb#L10-L13

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: https://github.com/palkan/anyway_config/blob/068fdf1504aa21ee04863a43fa99152c53581753/lib/anyway/rails/settings.rb#L21-L37

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.

palkan commented 3 years ago

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
palkan commented 3 years ago

Added a note to readme: https://github.com/palkan/anyway_config/commit/883c78172823def2bbedd48c8f488e8112199378

nicolas-brousse commented 3 years ago

@palkan thank you for the support!