Skip to content
Merged
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: 1 addition & 3 deletions config/initializers/active_record_extensions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@
module ActiveRecord
class Base
def self.rails_admin(&block)
RailsAdmin.config do |config|
config.model(self, &block)
end
RailsAdmin.config(self, &block)
end

def rails_admin_default_object_label_method
Expand Down
5 changes: 4 additions & 1 deletion lib/rails_admin.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
require 'rails_admin/engine'
require 'rails_admin/abstract_model'
require 'rails_admin/config'
require 'rails_admin/config/const_load_suppressor'
require 'rails_admin/extension'
require 'rails_admin/extensions/cancancan'
require 'rails_admin/extensions/pundit'
Expand All @@ -12,6 +13,8 @@
require 'yaml'

module RailsAdmin
extend RailsAdmin::Config::ConstLoadSuppressor

# Setup RailsAdmin
#
# Given the first argument is a model class, a model class name
Expand All @@ -29,7 +32,7 @@ def self.config(entity = nil, &block)
if entity
RailsAdmin::Config.model(entity, &block)
elsif block_given?
RailsAdmin::Config.apply(&block)
suppress_const_load { yield(RailsAdmin::Config) }
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
43 changes: 12 additions & 31 deletions lib/rails_admin/config.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# frozen_string_literal: true

require 'rails_admin/config/model'
require 'rails_admin/config/lazy_model'
require 'rails_admin/config/sections/list'
require 'active_support/core_ext/module/attribute_accessors'

Expand All @@ -22,10 +22,6 @@ module Config

DEFAULT_CURRENT_USER = proc {}

# Variables to track initialization process
@initialized = false
@deferred_blocks = []

class << self
# Application title, can be an array of two elements
attr_accessor :main_app_name
Expand Down Expand Up @@ -88,22 +84,6 @@ 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
end

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

# Setup authentication to be run as a before filter
# This is run inside the controller instance so you can setup any authentication you need to
#
Expand Down Expand Up @@ -249,27 +229,30 @@ def model(entity, &block)
case entity
when RailsAdmin::AbstractModel
entity.model.try(:name).try :to_sym
when Class
when Class, ConstLoadSuppressor::ConstProxy
entity.name.to_sym
when String, Symbol
entity.to_sym
else
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] ||= RailsAdmin::Config::LazyModel.new(key.to_s)
@registry[key].add_deferred_block(&block) if block
@registry[key]
end

def asset_source
@asset_source ||=
begin
warn <<~MSG
[Warning] After upgrading RailsAdmin to 3.x you haven't set asset_source yet, using :sprockets as the default.
To suppress this message, run 'rails rails_admin:install' to setup the asset delivery method suitable to you.
MSG
:sprockets
detected = defined?(Sprockets) ? :sprockets : :invalid
unless ARGV.join(' ').include? 'rails_admin:install'
warn <<~MSG
[Warning] After upgrading RailsAdmin to 3.x you haven't set asset_source yet, using :#{detected} as the default.
To suppress this message, run 'rails rails_admin:install' to setup the asset delivery method suitable to you.
MSG
end
detected
end
end

Expand Down Expand Up @@ -360,10 +343,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.
Expand Down
50 changes: 50 additions & 0 deletions lib/rails_admin/config/const_load_suppressor.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# frozen_string_literal: true

module RailsAdmin
module Config
module ConstLoadSuppressor
def suppress_const_load
original = Object.method(:const_missing)
Object.define_singleton_method(:const_missing) do |name|
ConstProxy.new(name.to_s)
end
yield
ensure
Object.define_singleton_method(:const_missing, original)
end

class ConstProxy < BasicObject
attr_reader :name

def initialize(name)
@name = name
end

def klass
@klass ||=
begin
unless ::Object.const_defined?(name)
::Kernel.raise <<~MESSAGE
The constant #{name} is not loaded yet upon the execution of the RailsAdmin initializer.
We don't recommend to do this and may lead to issues, but if you really have to do so you can explicitly require it by adding:

require '#{name.underscore}'

on top of config/initializers/rails_admin.rb.
MESSAGE
end
name.constantize
end
end

def method_missing(method_name, *args, &block)
klass.send(method_name, *args, &block)
end

def respond_to_missing?(method_name, include_private = false)
super || klass.respond_to?(method_name, include_private)
end
end
end
end
end
74 changes: 74 additions & 0 deletions lib/rails_admin/config/lazy_model.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# frozen_string_literal: true

require 'rails_admin/config/model'

module RailsAdmin
module Config
class LazyModel < BasicObject
def initialize(entity, &block)
@entity = entity
@deferred_blocks = [*block]
@initialized = false
end

def add_deferred_block(&block)
if @initialized
@model.instance_eval(&block)
else
@deferred_blocks << block
end
end

def target
@model ||= ::RailsAdmin::Config::Model.new(@entity)
# When evaluating multiple configuration blocks, the order of
# execution is important. As one would expect (in my opinion),
# options defined within a resource should take precedence over
# more general options defined in an initializer. This way,
# general settings for a number of resources could be specified
# in the initializer, while models could override these settings
# later, if required.
#
# CAVEAT: It cannot be guaranteed that blocks defined in an initializer
# will be loaded (and adde to @deferred_blocks) first. For instance, if
# the initializer references a model class before defining
# a RailsAdmin configuration block, the configuration from the
# resource will get added to @deferred_blocks first:
#
# # app/models/some_model.rb
# class SomeModel
# rails_admin do
# :
# end
# end
#
# # config/initializers/rails_admin.rb
# model = 'SomeModel'.constantize # blocks from SomeModel get loaded
# model.config model do # blocks from initializer gets loaded
# :
# end
#
# Thus, sort all blocks to excute for a resource by Proc.source_path,
# to guarantee that blocks from 'config/initializers' evaluate before
# blocks defined within a model class.
unless @deferred_blocks.empty?
@deferred_blocks.
partition { |block| block.source_location.first =~ %r{config/initializers} }.
flatten.
each { |block| @model.instance_eval(&block) }
@deferred_blocks = []
end
@initialized = true
@model
end

def method_missing(method_name, *args, &block)
target.send(method_name, *args, &block)
end

def respond_to_missing?(method_name, include_private = false)
super || target.respond_to?(method_name, include_private)
end
end
end
end
4 changes: 3 additions & 1 deletion lib/rails_admin/config/model.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,10 @@ def initialize(entity)
case entity
when RailsAdmin::AbstractModel
entity
when Class, String, Symbol
when Class, String
RailsAdmin::AbstractModel.new(entity)
when Symbol
RailsAdmin::AbstractModel.new(entity.to_s)
else
RailsAdmin::AbstractModel.new(entity.class)
end
Expand Down
25 changes: 8 additions & 17 deletions lib/rails_admin/engine.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,25 +35,16 @@ class Engine < Rails::Engine
end
end

initializer 'RailsAdmin apply configuration', after: :eager_load! do |app|
RailsAdmin::Config.initialize!

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

initializer 'RailsAdmin precompile hook', group: :all, after: 'RailsAdmin apply configuration' do |app|
initializer 'RailsAdmin precompile hook', group: :all do |app|
case RailsAdmin.config.asset_source
when :sprockets
if defined?(Sprockets)
app.config.assets.precompile += %w[
rails_admin/application.js
rails_admin/application.css
]
app.config.assets.paths << RailsAdmin::Engine.root.join('src')
require 'rails_admin/support/esmodule_preprocessor'
Sprockets.register_preprocessor 'application/javascript', RailsAdmin::ESModulePreprocessor
end
app.config.assets.precompile += %w[
rails_admin/application.js
rails_admin/application.css
]
app.config.assets.paths << RailsAdmin::Engine.root.join('src')
require 'rails_admin/support/esmodule_preprocessor'
Sprockets.register_preprocessor 'application/javascript', RailsAdmin::ESModulePreprocessor
when :importmap
self.importmap = Importmap::Map.new.draw(app.root.join('config/importmap.rails_admin.rb'))
end
Expand Down
4 changes: 4 additions & 0 deletions spec/dummy_app/app/active_record/team.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,8 @@ def color_enum
scope :green, -> { where(color: 'red') }
scope :red, -> { where(color: 'red') }
scope :white, -> { where(color: 'white') }

rails_admin do
field :color, :color
end
end
4 changes: 0 additions & 4 deletions spec/dummy_app/app/mongoid/eager_loaded/basketball.rb

This file was deleted.

2 changes: 1 addition & 1 deletion spec/dummy_app/config/application.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class Application < Rails::Application
# -- all .rb files in that directory are automatically loaded.
config.load_defaults Rails.version[0, 3]
config.eager_load_paths.reject! { |p| p =~ %r{/app/([^/]+)} && !%W[controllers jobs locales mailers #{CI_ORM}].include?(Regexp.last_match[1]) }
config.eager_load_paths += %W[#{config.root}/app/#{CI_ORM}/eager_loaded]
config.eager_load_paths += %W[#{config.root}/app/eager_loaded]
config.autoload_paths += %W[#{config.root}/lib]
config.i18n.load_path += Dir[Rails.root.join('app', 'locales', '*.{rb,yml}').to_s]
config.active_record.time_zone_aware_types = %i[datetime time] if CI_ORM == :active_record
Expand Down
4 changes: 2 additions & 2 deletions spec/dummy_app/config/initializers/rails_admin.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

RailsAdmin.config do |c|
c.asset_source = CI_ASSET
c.model 'Team' do
c.model Team do
include_all_fields
field :color, :color
field :color, :hidden
end
end
Loading