How Does Rails Implement Before_Filter

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



Leave a reply



Submit