Permalink
Browse files

Rename all action callbacks from *_filter to *_action

  • Loading branch information...
1 parent c205284 commit 9d62e04838f01f5589fa50b0baa480d60c815e2c @dhh dhh committed Dec 7, 2012
View
@@ -1,4 +1,31 @@
## Rails 4.0.0 (unreleased) ##
+
+* Rename all action callbacks from *_filter to *_action to avoid the misconception that these
+ callbacks are only suited for transforming or halting the response. With the new style,
+ it's more inviting to use them as they were intended, like setting shared ivars for views.
+
+ Example:
+
+ class PeopleController < ActionController::Base
+ before_action :set_person, except: [ :index, :new, :create ]
+ before_action :ensure_permission, only: [ :edit, :update ]
+
+ ...
+
+ private
+ def set_person
+ @person = current_account.people.find(params[:id])
+ end
+
+ def ensure_permission
+ current_person.change_change?(@person)
@garethrees

garethrees Dec 7, 2012

Contributor

Should that read current_person.can_change?(@person)?

@dhh

dhh via email Dec 7, 2012

Owner
@garethrees

garethrees Dec 7, 2012

Contributor

No problem, nice change 👍

+ end
+ end
+
+ The old *_filter methods still work with no deprecation notice.
+
+ *DHH*
+
* Add :if / :unless conditions to fragment cache:
<%= cache @model, if: some_condition(@model) do %>
@@ -40,18 +40,21 @@ def _normalize_callback_option(options, from, to) # :nodoc:
end
end
- # Skip before, after, and around filters matching any of the names
+ # Skip before, after, and around action callbacks matching any of the names
+ # Aliased as skip_filter.
#
# ==== Parameters
# * <tt>names</tt> - A list of valid names that could be used for
# callbacks. Note that skipping uses Ruby equality, so it's
# impossible to skip a callback defined using an anonymous proc
# using #skip_filter
- def skip_filter(*names)
- skip_before_filter(*names)
- skip_after_filter(*names)
- skip_around_filter(*names)
+ def skip_action_callback(*names)
+ skip_before_action(*names)
+ skip_after_action(*names)
+ skip_around_action(*names)
end
+
+ alias_method :skip_filter, :skip_action_callback
# Take callback names and an optional callback proc, normalize them,
# then call the block with each callback. This allows us to abstract
@@ -75,119 +78,138 @@ def _insert_callbacks(callbacks, block = nil)
end
##
- # :method: before_filter
+ # :method: before_action
#
- # :call-seq: before_filter(names, block)
+ # :call-seq: before_action(names, block)
#
- # Append a before filter. See _insert_callbacks for parameter details.
+ # Append a callback before actions. See _insert_callbacks for parameter details.
+ # Aliased as before_filter.
##
- # :method: prepend_before_filter
+ # :method: prepend_before_action
#
- # :call-seq: prepend_before_filter(names, block)
+ # :call-seq: prepend_before_action(names, block)
#
- # Prepend a before filter. See _insert_callbacks for parameter details.
+ # Prepend a callback before actions. See _insert_callbacks for parameter details.
+ # Aliased as prepend_before_action.
##
- # :method: skip_before_filter
+ # :method: skip_before_action
#
- # :call-seq: skip_before_filter(names)
+ # :call-seq: skip_before_action(names)
#
- # Skip a before filter. See _insert_callbacks for parameter details.
+ # Skip a callback before actions. See _insert_callbacks for parameter details.
+ # Aliased as skip_before_filter.
##
- # :method: append_before_filter
+ # :method: append_before_action
#
- # :call-seq: append_before_filter(names, block)
+ # :call-seq: append_before_action(names, block)
#
- # Append a before filter. See _insert_callbacks for parameter details.
+ # Append a callback before actions. See _insert_callbacks for parameter details.
+ # Aliased as append_before_filter.
##
- # :method: after_filter
+ # :method: after_action
#
- # :call-seq: after_filter(names, block)
+ # :call-seq: after_action(names, block)
#
- # Append an after filter. See _insert_callbacks for parameter details.
+ # Append a callback after actions. See _insert_callbacks for parameter details.
+ # Aliased as after_filter.
##
- # :method: prepend_after_filter
+ # :method: prepend_after_action
#
- # :call-seq: prepend_after_filter(names, block)
+ # :call-seq: prepend_after_action(names, block)
#
- # Prepend an after filter. See _insert_callbacks for parameter details.
+ # Prepend a callback after actions. See _insert_callbacks for parameter details.
+ # Aliased as prepend_after_filter.
##
- # :method: skip_after_filter
+ # :method: skip_after_action
#
- # :call-seq: skip_after_filter(names)
+ # :call-seq: skip_after_action(names)
#
- # Skip an after filter. See _insert_callbacks for parameter details.
+ # Skip a callback after actions. See _insert_callbacks for parameter details.
+ # Aliased as skip_after_filter.
##
- # :method: append_after_filter
+ # :method: append_after_action
#
- # :call-seq: append_after_filter(names, block)
+ # :call-seq: append_after_action(names, block)
#
- # Append an after filter. See _insert_callbacks for parameter details.
+ # Append a callback after actions. See _insert_callbacks for parameter details.
+ # Aliased as append_after_filter.
##
- # :method: around_filter
+ # :method: around_action
#
- # :call-seq: around_filter(names, block)
+ # :call-seq: around_action(names, block)
#
- # Append an around filter. See _insert_callbacks for parameter details.
+ # Append a callback around actions. See _insert_callbacks for parameter details.
+ # Aliased as around_filter.
##
- # :method: prepend_around_filter
+ # :method: prepend_around_action
#
- # :call-seq: prepend_around_filter(names, block)
+ # :call-seq: prepend_around_action(names, block)
#
- # Prepend an around filter. See _insert_callbacks for parameter details.
+ # Prepend a callback around actions. See _insert_callbacks for parameter details.
+ # Aliased as prepend_around_filter.
##
- # :method: skip_around_filter
+ # :method: skip_around_action
#
- # :call-seq: skip_around_filter(names)
+ # :call-seq: skip_around_action(names)
#
- # Skip an around filter. See _insert_callbacks for parameter details.
+ # Skip a callback around actions. See _insert_callbacks for parameter details.
+ # Aliased as skip_around_filter.
##
- # :method: append_around_filter
+ # :method: append_around_action
#
- # :call-seq: append_around_filter(names, block)
+ # :call-seq: append_around_action(names, block)
#
- # Append an around filter. See _insert_callbacks for parameter details.
+ # Append a callback around actions. See _insert_callbacks for parameter details.
+ # Aliased as append_around_filter.
- # set up before_filter, prepend_before_filter, skip_before_filter, etc.
+ # set up before_action, prepend_before_action, skip_before_action, etc.
# for each of before, after, and around.
- [:before, :after, :around].each do |filter|
+ [:before, :after, :around].each do |callback|
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
- # Append a before, after or around filter. See _insert_callbacks
+ # Append a before, after or around callback. See _insert_callbacks
# for details on the allowed parameters.
- def #{filter}_filter(*names, &blk) # def before_filter(*names, &blk)
- _insert_callbacks(names, blk) do |name, options| # _insert_callbacks(names, blk) do |name, options|
- set_callback(:process_action, :#{filter}, name, options) # set_callback(:process_action, :before, name, options)
- end # end
- end # end
+ def #{callback}_action(*names, &blk) # def before_action(*names, &blk)
+ _insert_callbacks(names, blk) do |name, options| # _insert_callbacks(names, blk) do |name, options|
+ set_callback(:process_action, :#{callback}, name, options) # set_callback(:process_action, :before, name, options)
+ end # end
+ end # end
- # Prepend a before, after or around filter. See _insert_callbacks
+ alias_method :#{callback}_filter, :#{callback}_action
+
+ # Prepend a before, after or around callback. See _insert_callbacks
# for details on the allowed parameters.
- def prepend_#{filter}_filter(*names, &blk) # def prepend_before_filter(*names, &blk)
- _insert_callbacks(names, blk) do |name, options| # _insert_callbacks(names, blk) do |name, options|
- set_callback(:process_action, :#{filter}, name, options.merge(:prepend => true)) # set_callback(:process_action, :before, name, options.merge(:prepend => true))
- end # end
- end # end
+ def prepend_#{callback}_action(*names, &blk) # def prepend_before_action(*names, &blk)
+ _insert_callbacks(names, blk) do |name, options| # _insert_callbacks(names, blk) do |name, options|
+ set_callback(:process_action, :#{callback}, name, options.merge(:prepend => true)) # set_callback(:process_action, :before, name, options.merge(:prepend => true))
+ end # end
+ end # end
+
+ alias_method :prepend_#{callback}_filter, :prepend_#{callback}_action
- # Skip a before, after or around filter. See _insert_callbacks
+ # Skip a before, after or around callback. See _insert_callbacks
# for details on the allowed parameters.
- def skip_#{filter}_filter(*names) # def skip_before_filter(*names)
- _insert_callbacks(names) do |name, options| # _insert_callbacks(names) do |name, options|
- skip_callback(:process_action, :#{filter}, name, options) # skip_callback(:process_action, :before, name, options)
- end # end
- end # end
-
- # *_filter is the same as append_*_filter
- alias_method :append_#{filter}_filter, :#{filter}_filter # alias_method :append_before_filter, :before_filter
+ def skip_#{callback}_action(*names) # def skip_before_action(*names)
+ _insert_callbacks(names) do |name, options| # _insert_callbacks(names) do |name, options|
+ skip_callback(:process_action, :#{callback}, name, options) # skip_callback(:process_action, :before, name, options)
+ end # end
+ end # end
+
+ alias_method :skip_#{callback}_filter, :skip_#{callback}_action
+
+ # *_action is the same as append_*_action
+ alias_method :append_#{callback}_action, :#{callback}_action # alias_method :append_before_action, :before_action
+ alias_method :append_#{callback}_filter, :#{callback}_action # alias_method :append_before_filter, :before_action
RUBY_EVAL
end
end
@@ -84,11 +84,11 @@ def setup
end
class Callback3 < ControllerWithCallbacks
- before_filter do |c|
+ before_action do |c|
c.instance_variable_set("@text", "Hello world")
end
- after_filter do |c|
+ after_action do |c|
c.instance_variable_set("@second", "Goodbye")
end
@@ -114,8 +114,8 @@ def setup
end
class CallbacksWithConditions < ControllerWithCallbacks
- before_filter :list, :only => :index
- before_filter :authenticate, :except => :index
+ before_action :list, :only => :index
+ before_action :authenticate, :except => :index
def index
self.response_body = @list.join(", ")
@@ -202,7 +202,7 @@ def setup
end
class ChangedConditions < Callback2
- before_filter :first, :only => :index
+ before_action :first, :only => :index
def not_index
@text ||= nil

34 comments on commit 9d62e04

Owner

jeremy replied Dec 7, 2012

👍

nice one!

Contributor

imkmf replied Dec 7, 2012

Changing this to action makes a lot more sense for people learning these things (like me) in understanding that there's a verb going on here (as in :authenticate as an action). Thanks for this.
👍

Member

josh replied Dec 7, 2012

I dig it. ⚡️

Small changes like this is Rails that make it better semantically are what keep me using it. Great change.

👍

Contributor

wildchild replied Dec 7, 2012

I miss Merb's before and after.

👍

nice!

Does the return value (true or false) still control the flow?
It looks more natural for me to expect method with such a name before_action to raise exception or call some another method like halt!.

works for me

Owner

fxn replied Dec 7, 2012

The return value was taken into account in the early days, but it's been a long time since filters are halted by rendering or redirecting.

3den replied Dec 7, 2012

@dhh why not just before :action, :do_something?

Owner

dhh replied Dec 7, 2012

Marcelo, because that's terribly ambiguous. before what? before rendering? before :authenticate "before authenticate"? before_action :authenticate is much clearer.

Member

mislav replied Dec 7, 2012

I don't like the example in the documentation:

def set_person
  @person = current_account.people.find(params[:id])
end

Having a separate method just to set an ivar is an antipattern. I don't want to teach this to less experienced developer.

A much better implementation would be: (although, then it quits being an example for before_action)

def current_person
  @current_person ||= current_account.people.find(params[:id])
end

before_filter did confuse me a little bit at the beginning. Nice change. I was thinking before_do , after_do .

Owner

dhh replied Dec 7, 2012

Contributor

Dorian replied Dec 7, 2012

Nice! Great to see that you can always change the API even if it's a popular method.

3den replied Dec 7, 2012

Hi @dhh, Thanks for your reply. One of the best thinks about rails is it clean and easy to read syntax. For sure _action is more meaningful than _filter but both add redundancy to de code. In plain english before :save, :calculate_total reads much cleaner than before_action :save, :calculate_total.

Owner

dhh replied Dec 7, 2012

Contributor

Dorian replied Dec 7, 2012

@3den: Maybe before and after are too general words to be used here (they may be used in action names) but renaming to before/after would be better.

Contributor

eavgerinos replied Dec 7, 2012

👍

👍

Contributor

matthewrobertson replied Dec 7, 2012

I feel 😐 about this. I think it would have been nice if it were called before_action from the beginning of time but I dunno if you actually get a lot of milage from the principle of least surprise . As someone thats been coding rails for a while I'm going to be like: "WTF is a before_action!?" If it aint broke, why fix it?

why not an alias method which inform about the removing version - dont take all the old legacy code with every version. better clean it. too much similar methods also confuses newcomers

alias_deprecated_method 4.1, :old, :new

Member

steveklabnik replied Dec 7, 2012

@muescha because it is not deprecated at this time. Just introducing a new name. If it's significantly nicer, we'll deprecate the old one.

Contributor

PikachuEXE replied Dec 8, 2012

This name makes much more sense! 👍

Contributor

ck3g replied Dec 8, 2012

👍

对代码有洁癖

Makes sense. +1

I think _action is self-explanatory. It'd be nice though if we had a backport for 3.2.10, just one more thing we need to remember to do for the upgrade. Before you all retaliate I know before_filter and after_filter aren't deprecated yet, but most will want to use the new syntax, so moving to _action will become the norm.

@meetdom sorry, no new features are backported to 3.2, only bug fixes. But it should be dead easy to alias the methods before/after filter in your application controller, and start using as before/after action.

good job ,it's more appropriate

长姿势了:+1

Please sign in to comment.