I'm interested in how rails implement filters like before_filter
.
But after reading the source code, I'm still confused.
I noticed that rails' framework maintains a filter_chain
, and run the filters before the target method.
But, I do not understand an important process: how does rails capture a method calling?
I mean, for example, I have a class Dog
, and set a before_filter
to the method bark.
When I call dog.bark
, rails should capture this calling in some way, and run its modified method instead.
However, I do not find such code in the source code.
Can anyone tell me the idea or point out where the code lies?
Filters run in respect to controller actions - before, after or around them. These methods can halt the action processing by redirecting or set up common data to every action in the controller.
When writing controllers in Ruby on rails, using before_action (used to be called before_filter in earlier versions) is your bread-and-butter for structuring your business logic in a useful way. It's what you want to use to "prepare" the data necessary before the action executes.
The Rails controller is the logical center of your application. It coordinates the interaction between the user, the views, and the model. The controller is also a home to a number of important ancillary services. It is responsible for routing external requests to internal actions.
With params . Inside your controller action's you can call params to access form & URL query data. What is params , exactly? It's a method that returns an ActionController::Parameters object, in practice it behaves a lot like a hash.
When you set a before_filter
, or any similar filter (think after_filter
, around_filter
), you're doing so with either a Symbol or a Proc, lambda or block.
before_filter :bark
before_filter Proc.new { |k| k.bark }
The above appends the symbols or blocks to a stack here, by calling set_callback
. This builds the 'chain' you're referring to.
Each item in this 'chain' is an instance of the ActiveSupport::Callbacks::Callback
class. This class knows
:bark
method)
Dog
class)
The Callback
instances are appended to a ActiveSupport::Callbacks::CallbackChain
in __update_callbacks
.
When each Callback
class is initialized, _compile_filter
is run to normalize the filter from the Symbol
, Proc
, lambda, or block into a common, callable format.
Finally, when the CallbackChain
is run, it will call start
on each Callback
instance, and its at this point that the filter is actually executed within the proper context.
It's important to point out that you would not create a filter like
before_filter dog.bark
this is going to execute dog.bark
and pass it's return value to before_filter
to be appended to the CallbackChain
. The intention is to pass some sort of instruction on to before_filter
for Rails to later execute for you. You would instead do something like
before_filter Proc.new { d = Dog.new; d.bark }
The code within the Proc
is not executed. when the line above is run by Rails. Instead, Rails is being told to pass the Proc
to the CallbackChain
. The Proc
is the 'instruction' you're passing on to Rails to execute at the appropriate time.
how in the first place does rails know I have called :bark
As for this, let's say your Dog
class is simply defined as
class Dog
def bark
end
def eat
end
end
(Though this is a terrible example), you might want to have something like
before_bark :eat
This requires you define the bark
callback, and then tell your bark
method to run the related bark
callbacks.
class Dog
extend ActiveModel::Callbacks
define_callbacks :bark
before_bark :eat
def bark
run_callbacks(:bark) { YOUR BARK CODE HERE }
end
def eat
end
end
You can see how ActiveRecord::Callbacks
does this.
This really is a bad example though because you can (and should) just call eat
directly from bark
, but this should get the point across.
Rails doesn't capture method calls the way you describe. If you look at AbstractController::Base.process
it will look up the method to be called for the dispatched action, run the filters and then call the actual method. In other words, your controller method is not called directly, but through this process
method.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With