Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

restore 2.x config loading behavior #3492

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -115,10 +115,10 @@ Metrics/CyclomaticComplexity:

Metrics/MethodLength:
CountComments: false
Max: 29 # TODO: Lower to 15
Max: 30 # TODO: Lower to 15

Metrics/ModuleLength:
Max: 217 # TODO: Lower to 100
Max: 249 # TODO: Lower to 100

Metrics/ParameterLists:
Max: 8 # TODO: Lower to 4
Expand Down
2 changes: 1 addition & 1 deletion lib/rails_admin.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def self.config(entity = nil, &block)
if entity
RailsAdmin::Config.model(entity, &block)
elsif block_given?
RailsAdmin::Config.apply(&block)
RailsAdmin::Config.apply_core(&block)
else
RailsAdmin::Config
end
Expand Down
4 changes: 1 addition & 3 deletions lib/rails_admin/adapters/mongoid/extension.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
80 changes: 63 additions & 17 deletions lib/rails_admin/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,12 @@ module Config
DEFAULT_CURRENT_USER = proc {}

# Variables to track initialization process
@initialized = false
@deferred_blocks = []
@initialized_core = false
@initializing_core = false
@deferred_core_blocks = []
@initialized_models = false
@initializing_models = false
@deferred_model_blocks = []

class << self
# Application title, can be an array of two elements
Expand Down Expand Up @@ -85,19 +89,42 @@ class << self
# Set where RailsAdmin fetches JS/CSS from, defaults to :sprockets
attr_writer :asset_source

# Finish initialization by executing deferred configuration blocks
def initialize!
@deferred_blocks.each { |block| block.call(self) }
@deferred_blocks.clear
@initialized = true
# Finish initialization by executing deferred non-model configuration blocks
def initialize_core!
return if @initializing_core

@initializing_core = true
@deferred_core_blocks.each { |block| block.call(self) }
@deferred_core_blocks.clear
@initialized_core = true
end

# Evaluate the given non-model block either immediately or lazily, based on initialization status.
def apply_core(&block)
if @initialized_core
yield(self)
else
@deferred_core_blocks << block
end
end

# Evaluate the given block either immediately or lazily, based on initialization status.
def apply(&block)
if @initialized
# Finish initialization by executing deferred model configuration blocks
def initialize_models!
return if @initializing_models

initialize_core!
@initializing_models = true
@deferred_model_blocks.each { |block| block.call(self) }
@deferred_model_blocks.clear
@initialized_models = true
end

# Evaluate the given model block either immediately or lazily, based on initialization status.
def apply_model(&block)
if @initialized_models
yield(self)
else
@deferred_blocks << block
@deferred_model_blocks << block
end
end

Expand Down Expand Up @@ -226,6 +253,7 @@ def default_search_operator=(operator)

# pool of all found model names from the whole application
def models_pool
initialize_models!
(viable_models - excluded_models.collect(&:to_s)).uniq.sort
end

Expand All @@ -238,7 +266,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)
Expand All @@ -254,9 +282,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_model do
m = model(entity)
m.instance_eval(&block) if @registry[key].abstract_model
end
nil
else
initialize_models!
@registry[key] ||= RailsAdmin::Config::Model.new(entity)
end
end

def asset_source
Expand Down Expand Up @@ -318,6 +361,10 @@ def models
#
# @see RailsAdmin::Config.registry
def reset
@initialized_core = @initializing_core = false
@deferred_core_blocks.clear
@initialized_models = @initializing_models = false
@deferred_model_blocks.clear
@compact_show_view = true
@browser_validations = true
@authenticate = nil
Expand Down Expand Up @@ -356,10 +403,9 @@ def reset_model(model)

# Perform reset, then load RailsAdmin initializer again
def reload!
@initialized = false
reset
load RailsAdmin::Engine.config.initializer_path
initialize!
initialize_core!
end

# Get all models that are configured as visible sorted by their weight and label.
Expand Down
2 changes: 1 addition & 1 deletion lib/rails_admin/engine.rb
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ class Engine < Rails::Engine
ERROR
end

RailsAdmin::Config.initialize!
RailsAdmin::Config.initialize_core!

# Force route reload, since it doesn't reflect RailsAdmin action configuration yet
app.reload_routes!
Expand Down
2 changes: 1 addition & 1 deletion spec/dummy_app/config/initializers/rails_admin.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

RailsAdmin.config do |c|
c.asset_source = CI_ASSET
c.model 'Team' do
c.model Team do
include_all_fields
field :color, :color
end
Expand Down
49 changes: 38 additions & 11 deletions spec/rails_admin/config_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -377,40 +395,44 @@ class TestController < ActionController::Base; end
end
end

describe '.apply' do
subject { RailsAdmin::Config.apply(&block) }
describe '.apply_core' do
subject { RailsAdmin::Config.apply_core(&block) }
let(:block) do
proc do |config|
config.model Team do
register_instance_option('parameter') # an arbitrary instance method we can spy on
end
end
end
before { RailsAdmin::Config.instance_variable_set(:@initialized, false) }
after do
RailsAdmin::Config.instance_variable_set(:@initialized, true)
RailsAdmin::Config.instance_variable_set(:@deferred_blocks, [])
end
after { RailsAdmin::Config.reset }

it "doesn't evaluate the block immediately" do
expect_any_instance_of(RailsAdmin::Config::Model).not_to receive(:register_instance_option)
subject
end

it 'evaluates block when initialize! is finished' do
it "doesn't evaluate the block when initialize_core! is finished" do
expect_any_instance_of(RailsAdmin::Config::Model).not_to receive(:register_instance_option).with('parameter')
subject
RailsAdmin::Config.initialize_core!
end

it 'evaluates block when initialize_models! is finished' do
expect_any_instance_of(RailsAdmin::Config::Model).to receive(:register_instance_option).with('parameter')
subject
RailsAdmin::Config.initialize!
RailsAdmin::Config.initialize_models!
end

it 'evaluates config block only once' do
expect_any_instance_of(RailsAdmin::Config::Model).to receive(:register_instance_option).once.with('parameter')
subject
RailsAdmin::Config.initialize!
RailsAdmin::Config.initialize!
RailsAdmin::Config.initialize_models!
RailsAdmin::Config.initialize_models!
end

context 'with a non-existent class' do
before { RailsAdmin::Config.reset }

let(:block) do
proc do |config|
config.model UnknownClass do
Expand Down Expand Up @@ -445,6 +467,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_models!)
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|
Expand Down
1 change: 1 addition & 0 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@
DatabaseCleaner.start
RailsAdmin::Config.reset
RailsAdmin::Config.asset_source = CI_ASSET
RailsAdmin::Config.initialize_core!
end

config.after(:each) do
Expand Down