skip_before_filter strange behavior when using if and only together #9703

Closed
ginter opened this Issue Mar 13, 2013 · 7 comments

6 participants

@ginter

I'm running into an issue when using skip_before_filter with both the "if" and "only" options.

before_filter :test
skip_before_filter :test, only: [:show], if: -> { false }

When running the above, I expect "test" to be called when hitting the show action, but this isn't the case.

before_filter :test
skip_before_filter :test, only: [:random], if: -> { false }

When running the above, I expect "test" to be called regardless of the conditional when hitting the show action, but this isn't the case. It is called in this case when the conditional evaluates to false but it is not called if the conditional evaluates to true.

@senny
Ruby on Rails member

I agree that this seems strange. I'll take a look and report back. Please format your code using ``` (I edited your message).

@imanel

The problem is that both only and if are treated separately - if either of them will be fulfilled then skip will be applied. So in case of code:

before_filter :test
skip_before_filter :test, only: [:show], if: -> { false }

it is treated as code:

before_filter :test
skip_before_filter :test, only: [:show]
skip_before_filter :test, if: -> { false }

Unfortunately documentation is not explaining that so it required deeper testing to find that out. To confirm that you need to check output of ActiveSupport::Callbacks.skip_callback.

@ginter

Is that intended behavior or would it be worth trying to fix/change?

@korny

The problem is that skip_before_filter behaves differently than before_filter in this regard. This just confused a coworker and me for almost an hour, and we're not the only ones. It should be clearly documented as a strange behavior, because I guess you can't just change/fix it now that people are depending on the way it used to work…

Since skip_before_filter is used a lot for security requirements, the unexpected behavior can lead to very ugly bugs; thinking of:

skip_before_filter :login_required, only: :show, if: :trusted_origin?

😱

@korny

By the way, the easiest way to work around this seems to be to repeat the before_filter with the inverse condition after the skipping.

Wrong:

before_filter :test
skip_before_filter :test, only: [:show], if: :skip_test?

Correct:

before_filter :test
skip_before_filter :test, only: [:show]
before_filter      :test, only: [:show], unless: :skip_test?
@rafaelfranca
Ruby on Rails member

Closed by #18404

@bluehallu
bluehallu commented Jun 3, 2016 edited

@rafaelfranca That commit didn't close the issue, it merely documented this weird behaviour. Still present in Rails 5 rc1. Hate to use the old "least surprise principle" argument, but if this isn't totally unexpected behaviour I don't know what it is!

Current best workaround is to code the only: part yourself as such:

skip_before_action :authenticate_with_jwt, if: -> { query_params.empty? && action_name == "index" }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment