16

With the following code :

class TestsController < ApplicationController
  skip_before_filter :load_something,      # how to skip these only 
                     :load_something_else  # if the request is xhr ?

  def index
    respond_to do |format|
      format.html
      format.js
    end
  end

end

Is there a way to skip before filters depending if the request is a javascript call without changing the :load_something and :load_something_else methods ?

Thanks !

jrichardlai
  • 3,317
  • 4
  • 21
  • 24

5 Answers5

40

Try this line to skip the filtering by a condition:

skip_before_filter :load_something, :load_something_else,
   :if => proc { request.xhr? }

For you can also use lambdas (which have a strictness in argument passing):

skip_before_filter :load_something, :load_something_else,
   if: -> { request.xhr? }

or with an argument:

skip_before_filter :load_something, :load_something_else,
   if: ->(arg) { request.xhr? && arg }
Малъ Скрылевъ
  • 16,187
  • 5
  • 56
  • 69
21

There used to be no clean solution to this. Assuming you do not have control over the before_filters (or do not want to change them), i would leave the skip_before_filter and add an extra before_filter that only executes the wanted methods if it is an xhr method.

So something like

skip_before_filter :load_something,      # how to skip these only 
                   :load_something_else  # if the request is xhr ?
before_filter :do_loads_if_xhr

def do_loads_if_xhr
  if request.xhr?
    load_something
    load_something_else
  end
end

The method described here only works because the conditional is global for the application: the conditional is only evaluated when the class is created. Not on each request.

[UPDATED] There is however, a cleaner way to make the skip_before_filter conditional. Write it like this:

skip_before_filter :load_something, :load_something_else, :if => proc {|c| request.xhr?}

Also note that while skip_before_filter is still completely supported, and not deprecated, since rails 4 the documentation seems to prefer the (identical) skip_before_action. Also the documentation is totally not clear about this, had to verify in code.

Community
  • 1
  • 1
nathanvda
  • 49,707
  • 13
  • 117
  • 139
14

All of these answers take the wrong approach in my opinion. Using a skip_before_action with a condition is doing it backwards, and isn't as explicit and clear as adding the condition to the before_action itself; so this is my proposal:

before_action :require_password_change, unless: -> { request.xhr? }

This way, to someone reading your code it's immediately clear under what conditions this before_action runs or doesn't run, without having to rely on them happening across this skip_before_action somewhere in the controller hierarchy.

Mike Campbell
  • 7,921
  • 2
  • 38
  • 51
4

Use alias_method_chain to decorate the filters, so they aren't executed if the request is xhr.

def load_something_with_noxhr(*args)
  load_something_without_noxhr(*args) unless request.xhr?
end

alias_method_chain :load_something, :noxhr

(change load_something into whatever needed, the _with_ and _without_ are literal, and the suffix noxhr must be the same you pass as second argument in the alias_method_chain call.)

rewritten
  • 16,280
  • 2
  • 47
  • 50
  • Looks like `alias_method_chain` might be deprecated / not recommended http://stackoverflow.com/questions/3689736/rails-3-alias-method-chain-still-used – colllin Jun 23 '14 at 08:22
  • 1
    Not that it's not recommended, but now (Ruby 2.1) there is the marvellos `prepend MyModule` which puts your module _in front_ of the call chain, so the funcion in the module can actually call `super` and call the same-named funcion in the "including" class. – rewritten Jul 07 '14 at 01:19
3

You coded that filters? In that case you can write an if clause inside them without using skip_before_filter:

def load_something
  unless request.xhr?
    #Code
  end
end

In the other hand, you can make yout own filter and delegate:

before_filter :my_own_filter

def my_own_filter
  unless request.xhr?
    load_something
    load_something_else
  end
end

I think this should work.

jrichardlai
  • 3,317
  • 4
  • 21
  • 24
pablorc
  • 940
  • 1
  • 8
  • 20
  • Shouldn't the methods be &&'d together so that if a previous one returns false then the others won't run and the filter chain will be halted? – Brendon Muir Oct 04 '11 at 01:30