探究Rails.application.configure块未传变量却可运行的原理
config Argument Great question! The difference between Rails' configure pattern and Clearance's comes down to a clever Ruby metaprogramming trick: changing the execution context of the block using instance_eval (or module_eval), instead of passing the configuration object via yield.
Let’s break this down step by step:
1. The Core: instance_eval in Ruby
When you call instance_eval on an object and pass a block, Ruby changes the value of self inside that block to be the object you called instance_eval on. That means any method calls inside the block (without an explicit receiver) will be called directly on that object.
Rails leverages this in its configure method. Instead of yielding the configuration object to the block (like Clearance does), it runs the block in the context of the configuration object itself.
2. Rails' Simplified Implementation
Here’s a stripped-down version of how Rails might implement its configure logic to make this clear:
module Rails class << self def application @application ||= Application.new end end class Application def configure(&block) # Get or initialize the configuration object @config ||= Configuration.new # Run the block in the context of @config @config.instance_eval(&block) end end class Configuration attr_accessor :some_option, :another_option # The magic: define a `config` method that returns `self` def config self end def initialize # Set default values @some_option = "default_value" end end end # Usage (just like Rails!) Rails.application.configure do # `self` here is the Configuration instance config.some_option = "custom_value" # You could even skip `config` entirely: another_option = "another_custom_value" end
3. Why config Works Without Being Passed
Inside the configure block:
selfis the Configuration instance (thanks toinstance_eval).- Rails adds a
configmethod to the Configuration class that simply returnsself. So when you writeconfig.some_option, you’re actually calling theconfigmethod on the currentself(the Configuration instance), getting back the same instance, and then callingsome_option=on it.
You could even omit config entirely and just write some_option = "value"—since self is the Configuration instance, Ruby will look for the setter method on self first.
4. Comparing to Clearance's Approach
Clearance uses the more straightforward yield(configuration) pattern, which requires the block to accept an argument (e.g., {|config| ...}). This is totally valid, but Rails' approach makes the syntax a bit cleaner by removing the need for the block parameter.
5. The Configurable Module
You mentioned Rails' Configurable module—this is where Rails encapsulates this logic. It provides the configure method, handles initializing the configuration object, and sets up the config method so you can reference the configuration instance easily inside the block.
内容的提问来源于stack exchange,提问作者sandre89

