Skip to content

Commit

Permalink
maintain 3.x deferred initializer block config
Browse files Browse the repository at this point in the history
In 3.x the initializer config blocks are intentionally deferred to allow
referencing model classes without quoting. Here is a first pass at keeping that
behavior on top of ec03eb6. It could probably use a little cleanup but here's
an idea of an approach for feedback.

See
railsadminteam#3492 (comment)
for details.
  • Loading branch information
q3aiml committed Mar 25, 2022
1 parent 8562761 commit 6c43f92
Show file tree
Hide file tree
Showing 7 changed files with 71 additions and 38 deletions.
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: 228 # TODO: Lower to 100
Max: 249 # TODO: Lower to 100

Metrics/ParameterLists:
Max: 8 # TODO: Lower to 4
Expand Down
4 changes: 1 addition & 3 deletions lib/rails_admin.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,7 @@ def self.config(entity = nil, &block)
if entity
RailsAdmin::Config.model(entity, &block)
elsif block_given?
# Immediately evaluate non-model (initializer) config. It's needed to
# properly configure routes when there are custom actions.
yield RailsAdmin::Config
RailsAdmin::Config.apply_core(&block)
else
RailsAdmin::Config
end
Expand Down
64 changes: 45 additions & 19 deletions lib/rails_admin/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,12 @@ module Config
DEFAULT_CURRENT_USER = proc {}

# Variables to track initialization process
@initialized = false
@initializing = 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 @@ -86,22 +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!
return if @initializing
# Finish initialization by executing deferred non-model configuration blocks
def initialize_core!
return if @initializing_core

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

# Evaluate the given block either immediately or lazily, based on initialization status.
def apply(&block)
if @initialized
# 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_blocks << block
@deferred_core_blocks << block
end
end

# 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_model_blocks << block
end
end

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

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

Expand Down Expand Up @@ -268,13 +291,13 @@ def model(entity, &block)
#
# Do not defer when called without a block as the model must be returned.
if block
RailsAdmin::Config.apply do
RailsAdmin::Config.apply_model do
m = model(entity)
m.instance_eval(&block) if @registry[key].abstract_model
end
nil
else
initialize!
initialize_models!
@registry[key] ||= RailsAdmin::Config::Model.new(entity)
end
end
Expand Down Expand Up @@ -338,8 +361,10 @@ def models
#
# @see RailsAdmin::Config.registry
def reset
@initialized = @initializing = false
@deferred_blocks.clear
@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 @@ -380,6 +405,7 @@ def reset_model(model)
def reload!
reset
load RailsAdmin::Engine.config.initializer_path
initialize_core!
end

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

RailsAdmin::Config.initialize_core!

# Force route reload, since it doesn't reflect RailsAdmin action configuration yet
app.reload_routes!

RailsAdmin::Version.warn_with_js_version
end
end
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
29 changes: 16 additions & 13 deletions spec/rails_admin/config_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -395,41 +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) }
before { RailsAdmin::Config.instance_variable_set(:@initializing, 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 @@ -465,7 +468,7 @@ class TestController < ActionController::Base; end
end

it 'does not immediately apply model configuration' do
expect(RailsAdmin::Config).not_to receive(:initialize!)
expect(RailsAdmin::Config).not_to receive(:initialize_models!)
RailsAdmin::Config.reload!
end

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

0 comments on commit 6c43f92

Please sign in to comment.