diff --git a/.rubocop.yml b/.rubocop.yml index f9e7add9f9..702224887a 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -118,7 +118,7 @@ Metrics/MethodLength: Max: 29 # TODO: Lower to 15 Metrics/ModuleLength: - Max: 217 # TODO: Lower to 100 + Max: 227 # TODO: Lower to 100 Metrics/ParameterLists: Max: 8 # TODO: Lower to 4 diff --git a/lib/rails_admin.rb b/lib/rails_admin.rb index 7f70d4d507..fee9e267d6 100644 --- a/lib/rails_admin.rb +++ b/lib/rails_admin.rb @@ -29,7 +29,9 @@ def self.config(entity = nil, &block) if entity RailsAdmin::Config.model(entity, &block) elsif block_given? - RailsAdmin::Config.apply(&block) + # Immediately evaluate non-model (initializer) config. It's needed to + # properly configure routes when there are custom actions. + yield RailsAdmin::Config else RailsAdmin::Config end diff --git a/lib/rails_admin/adapters/mongoid/extension.rb b/lib/rails_admin/adapters/mongoid/extension.rb index e669c693a5..3803525a24 100644 --- a/lib/rails_admin/adapters/mongoid/extension.rb +++ b/lib/rails_admin/adapters/mongoid/extension.rb @@ -11,9 +11,7 @@ module Extension self.nested_attributes_options = {} class << self def rails_admin(&block) - RailsAdmin.config do |config| - config.model(self, &block) - end + RailsAdmin.config(self, &block) end alias_method :accepts_nested_attributes_for_without_rails_admin, :accepts_nested_attributes_for diff --git a/lib/rails_admin/config.rb b/lib/rails_admin/config.rb index 531b6701b4..48c0426403 100644 --- a/lib/rails_admin/config.rb +++ b/lib/rails_admin/config.rb @@ -24,6 +24,7 @@ module Config # Variables to track initialization process @initialized = false + @initializing = false @deferred_blocks = [] class << self @@ -87,6 +88,9 @@ class << self # Finish initialization by executing deferred configuration blocks def initialize! + return if @initializing + + @initializing = true @deferred_blocks.each { |block| block.call(self) } @deferred_blocks.clear @initialized = true @@ -226,6 +230,7 @@ def default_search_operator=(operator) # pool of all found model names from the whole application def models_pool + initialize! (viable_models - excluded_models.collect(&:to_s)).uniq.sort end @@ -238,7 +243,7 @@ def models_pool # # If a block is given it is evaluated in the context of configuration instance. # - # Returns given model's configuration + # Otherwise returns given model's configuration. # # @see RailsAdmin::Config.registry def model(entity, &block) @@ -254,9 +259,24 @@ def model(entity, &block) entity.class.name.to_sym end - @registry[key] ||= RailsAdmin::Config::Model.new(entity) - @registry[key].instance_eval(&block) if block && @registry[key].abstract_model - @registry[key] + # When a block is provided defer evaluation until initialize so that: + # + # 1) performing configuration in the initializer does not attempt to + # autoload model classes, and + # 2) model config blocks run after models are fully loaded and + # associations are defined. + # + # Do not defer when called without a block as the model must be returned. + if block + RailsAdmin::Config.apply do + m = model(entity) + m.instance_eval(&block) if @registry[key].abstract_model + end + nil + else + initialize! + @registry[key] ||= RailsAdmin::Config::Model.new(entity) + end end def asset_source @@ -318,6 +338,8 @@ def models # # @see RailsAdmin::Config.registry def reset + @initialized = @initializing = false + @deferred_blocks.clear @compact_show_view = true @browser_validations = true @authenticate = nil @@ -356,10 +378,8 @@ def reset_model(model) # Perform reset, then load RailsAdmin initializer again def reload! - @initialized = false reset load RailsAdmin::Engine.config.initializer_path - initialize! end # Get all models that are configured as visible sorted by their weight and label. diff --git a/lib/rails_admin/engine.rb b/lib/rails_admin/engine.rb index 788071eff6..b462fb557d 100644 --- a/lib/rails_admin/engine.rb +++ b/lib/rails_admin/engine.rb @@ -68,11 +68,6 @@ class Engine < Rails::Engine ERROR end - RailsAdmin::Config.initialize! - - # Force route reload, since it doesn't reflect RailsAdmin action configuration yet - app.reload_routes! - RailsAdmin::Version.warn_with_js_version end end diff --git a/spec/rails_admin/config_spec.rb b/spec/rails_admin/config_spec.rb index 521515960c..bf1f27dd04 100644 --- a/spec/rails_admin/config_spec.rb +++ b/spec/rails_admin/config_spec.rb @@ -327,6 +327,24 @@ class TestController < ActionController::Base; end end end + context 'when a block is not provided' do + let(:model) { described_class.model(Team) } + it 'returns the model config' do + expect(model).to be_a(RailsAdmin::Config::Model) + end + end + + context 'when a block is provided' do + let(:model) do + described_class.model(Team) do + field :fans + end + end + it 'does not return the model config' do + expect(model).to be_nil + end + end + context 'when model expanded' do before do described_class.model(Team) do @@ -387,6 +405,7 @@ class TestController < ActionController::Base; end end end before { RailsAdmin::Config.instance_variable_set(:@initialized, false) } + before { RailsAdmin::Config.instance_variable_set(:@initializing, false) } after do RailsAdmin::Config.instance_variable_set(:@initialized, true) RailsAdmin::Config.instance_variable_set(:@deferred_blocks, []) @@ -445,6 +464,11 @@ class TestController < ActionController::Base; end expect(RailsAdmin::Config.model(Team).fields.find { |f| f.name == :color }.type).to eq :color end + it 'does not immediately apply model configuration' do + expect(RailsAdmin::Config).not_to receive(:initialize!) + RailsAdmin::Config.reload! + end + it "applies the initializer's configuration first, then models' configurations" do # simulate the situation that Team model is loaded in the middle of processing RailsAdmin initializer allow_any_instance_of(RailsAdmin::Config::Model).to receive(:include_all_fields).and_wrap_original do |method|