How does rails implement before_filter?
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
- The the method (Symbol) or block it must run (ie. your class'
:bark
method) - The context which it must run within (ie. your
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.
implement a rails before_filter in ruby without rails
You can get a callback on any method being created with Module#method_added, alias the old method, then define a new method that calls the before_filter method first. Here's my (extremely) rough first concept:
module Filter
def before_filter name
@@filter = name
end
def method_added name
return if @filtering # Don't add filters to original_ methods
return if @@filter == name # Don't filter filters
return if name == :initialize
@filtering = true
alias_method :"original_#{name}", name
define_method name do |*args|
self.send @@filter, name
self.send :"original_#{name}", *args
end
@filtering = false
end
end
class FilterTest
extend Filter
before_filter :prepare_logs
def baz
puts "#{@msg_prefix} message goes here"
end
def prepare_logs name
@msg_prefix = "#{self.class}::#{name}"
end
end
ft = FilterTest.new
ft.baz
By using __method__
like you were in create_prefix
, you'll get the name of the filter method, not the original method, so you have to pass the method name in. There might be other solutions to make that a bit cleaner.
When is a before_filter executed?
before_filter
is not a Ruby feature, it is a class method provided by Ruby on Rails (the web framework) that you can use in your controllers to execute a piece of code before executing any action in your controller.
So, how does Ruby on Rails does that?
When you are defining a class in Ruby you are actually executing code, try this in irb:
class Hello
puts "defining hello..."
def greet
puts "Hello there"
end
end
You'll see that "defining hello..." gets printed in the terminal when you define the class. You have not instantiated any object, you just have defined a class, but you can execute any code in the middle of defining a class.
You know that you can define “class methods” and “instance methods”, and what's interesting is that you can call your class methods while you are still defining your class:
class MyClass
def self.add_component(component)
# Here @@components is a class variable
@@components ||= [] # set the variable to an empty array if not already set.
@@components << component # add the component to the array
end
add_component(:mouse)
add_component(:keyboard)
add_component(:screen)
def components
@@components # The @@ gets you the class variable
end
end
MyClass.new.components
=> [:mouse, :keyboard, :screen]
def self.add_component
defines a class method that you can call while still defining your class. In this example add_component
adds a keyboard to a list in a class variable, and the def components
instance method (which is called on an instance of this class) access this class variable. The class method could have been defined in a parent class, and it would have worked the same. That example may be a little bit weird.
Let's do another example.
class RubyOnSlugsController
def self.before_filter(callback)
@@callbacks ||= []
@@callbacks << callback
end
def execute_callbacks
@@callbacks.each { |callback| callback.call() }
return "callbacks executed"
end
end
class MyController < RubyOnSlugsController
before_filter Proc.new { puts "I'm a before filter!" }
before_filter Proc.new { puts "2 + 2 is #{2 + 2}" }
end
controller = MyController.new
controller.execute_callbacks
will output:
I'm a before filter!
2 + 2 is 4
=> "callbacks executed"
Ruby on Rails does something similar (but quite more complex) with before_filter
, and it makes sure that all callbacks you define with it are called before your normal controller methods.
I hope this clears things a little bit for you.
Rails before_filter for specific actions in controller
Create in your ApplicationController
method:
def check_privileges!
redirect_to "/", notice: 'You dont have enough permissions to be here' unless current_admin || current_company
end
And then in your controller:
before_filter :check_privileges!, only: [:new, :create, :edit, :save]
Or
before_filter :check_privileges!, except: [:index, :show]
What kind of logic should be in Rails before filter
Yes, finding a record and setting it as an instance variable is a common convention for controller filters. Generally though, any piece of code that gets run for multiple actions is a good candidate. Say you want to redirect to the log in page if the current user is not logged in.
class UsersController < ApplicationController
before_action :require_login
before_action :set_user, only: [:show, :edit, :update, :destroy]
private
def require_login
unless logged_in?
flash[:error] = "You must be logged in to access this section"
redirect_to new_login_url # halts request cycle
end
end
def set_user
@user = User.find(params[:id])
end
end
Rails use not condition with before_filter
You could pass a block to before_filter
:
before_filter { |c| !c.logged_in? }
But this wouldn't really do anything, since the return value from the before filter isn't going anywhere. If you want to execute an action if a user is not logged in, then you should be putting that action into the before_filter.
For example, if the action was to redirect to the login page, path, you could do this:
before_filter { |c| redirect_to login_path unless c.logged_in? }
That's actually long enough to justify a method of its own:
before_filter :login_required
def login_required
redirect_to login_path unless logged_in?
end
Rails before_filter and action identification
The action_name
method on the controller should give you what you're looking for. It's not documented, though, so there is no guarantee it won't disappear someday.
before_filter { |controller| logger.debug "Running before the #{controller.action_name} action" }
How to skip before filter in model with ruby on rails 5.2?
I think this is the good case to use skip_callback method:
BusinessRule.skip_callback(:create, :before, :set_hierarchy)
# your code to create BusinessRule objects without setting hierarchy
# ...
BusinessRule.set_callback(:create, :before, :set_hierarchy)
If you're going to skip/set callbacks quite often you could simplify it using special helping method:
# config/initializers/without_callback.rb
module ActiveSupport::Callbacks::ClassMethods
def without_callback(*args, &block)
skip_callback(*args)
yield
set_callback(*args)
end
end
And you will be able to skip a callback like this:
BusinessRule.without_callback(:create, :before, :set_hierarchy) do
# your code to create BusinessRule objects without setting hierarchy
# ...
end
In rails, does a method called in the before filter of a controller run for every action?
before_filter
will call the method for every request to the controller. You can set it to run only for some actions:
before_filter :authorize, :only => :delete
Or even prevent it from running in particular actions:
before_filter :authorize, :except => [:index, :show]
BTW, the new syntax for before_filter
is before_action
Related Topics
Confusing Behaviour of Const_Get in Ruby
How Are Symbols Used to Identify Arguments in Ruby Methods
How to Uninstall Ruby on Rails and Do a Clean Install
MAC Os X Mountain Lion "Rails Is Not Currently Installed on This System."
How to Use Rails and Paperclip to Store Photos on Google Cloud Storage
Checking If a Method Is Defined on the Class
Anybody Tried the Crystal Programming Language (Machine-Code Compiled Ruby)
Sidekiq List All Jobs [Queued + Running]
Ruby Method with Maximum Number of Parameters
Ruby on Rails: Provide VS Content_For
No Implicit Conversion of String into Integer (Typeerror)
Conditional Tag Wrapping in Rails/Erb
Adding a Method to Built-In Class in Rails App