aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib/action_controller/filters.rb
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack/lib/action_controller/filters.rb')
-rw-r--r--actionpack/lib/action_controller/filters.rb100
1 files changed, 85 insertions, 15 deletions
diff --git a/actionpack/lib/action_controller/filters.rb b/actionpack/lib/action_controller/filters.rb
index 1ae2ea2251..85df451501 100644
--- a/actionpack/lib/action_controller/filters.rb
+++ b/actionpack/lib/action_controller/filters.rb
@@ -126,18 +126,46 @@ module ActionController #:nodoc:
# report_result
# end
# end
+ #
+ # == Filter conditions
+ #
+ # Filters can be limited to run for only specific actions. This can be expressed either by listing the actions to
+ # exclude or the actions to include when executing the filter. Available conditions are +:only+ or +:except+, both
+ # of which accept an arbitrary number of method references. For example:
+ #
+ # class Journal < ActionController::Base
+ # # only require authentication if the current action is edit or delete
+ # before_filter :authorize, :only => [ :edit, :delete ]
+ #
+ # private
+ # def authorize
+ # # redirect to login unless authenticated
+ # end
+ # end
+ #
+ # When setting conditions on inline method (proc) filters the condition must come first and be placed in parenthesis.
+ #
+ # class UserPreferences < ActionController::Base
+ # before_filter(:except => :new) { # some proc ... }
+ # # ...
+ # end
+ #
module ClassMethods
# The passed <tt>filters</tt> will be appended to the array of filters that's run _before_ actions
# on this controller are performed.
def append_before_filter(*filters, &block)
+ conditions = extract_conditions!(filters)
filters << block if block_given?
+ add_action_conditions(filters, conditions)
append_filter_to_chain("before", filters)
end
# The passed <tt>filters</tt> will be prepended to the array of filters that's run _before_ actions
# on this controller are performed.
def prepend_before_filter(*filters, &block)
+ conditions = extract_conditions!(filters)
filters << block if block_given?
+ add_action_conditions(filters, conditions)
prepend_filter_to_chain("before", filters)
end
@@ -147,14 +175,18 @@ module ActionController #:nodoc:
# The passed <tt>filters</tt> will be appended to the array of filters that's run _after_ actions
# on this controller are performed.
def append_after_filter(*filters, &block)
+ conditions = extract_conditions!(filters)
filters << block if block_given?
+ add_action_conditions(filters, conditions)
append_filter_to_chain("after", filters)
end
# The passed <tt>filters</tt> will be prepended to the array of filters that's run _after_ actions
# on this controller are performed.
def prepend_after_filter(*filters, &block)
+ conditions = extract_conditions!(filters)
filters << block if block_given?
+ add_action_conditions(filters, conditions)
prepend_filter_to_chain("after", filters)
end
@@ -169,8 +201,8 @@ module ActionController #:nodoc:
# A#before
# A#after
# B#after
- def append_around_filter(filters)
- for filter in [filters].flatten
+ def append_around_filter(*filters)
+ for filter in filters.flatten
ensure_filter_responds_to_before_and_after(filter)
append_before_filter { |c| filter.before(c) }
prepend_after_filter { |c| filter.after(c) }
@@ -185,8 +217,8 @@ module ActionController #:nodoc:
# B#before
# B#after
# A#after
- def prepend_around_filter(filters)
- for filter in [filters].flatten
+ def prepend_around_filter(*filters)
+ for filter in filters.flatten
ensure_filter_responds_to_before_and_after(filter)
prepend_before_filter { |c| filter.before(c) }
append_after_filter { |c| filter.after(c) }
@@ -206,6 +238,16 @@ module ActionController #:nodoc:
read_inheritable_attribute("after_filters")
end
+ # Returns a mapping between filters and the actions that may run them.
+ def included_actions #:nodoc:
+ read_inheritable_attribute("included_actions") || {}
+ end
+
+ # Returns a mapping between filters and actions that may not run them.
+ def excluded_actions #:nodoc:
+ read_inheritable_attribute("excluded_actions") || {}
+ end
+
private
def append_filter_to_chain(condition, filters)
write_inheritable_array("#{condition}_filters", filters)
@@ -220,6 +262,22 @@ module ActionController #:nodoc:
raise ActionControllerError, "Filter object must respond to both before and after"
end
end
+
+ def extract_conditions!(filters)
+ return nil unless filters.last.is_a? Hash
+ filters.pop
+ end
+
+ def add_action_conditions(filters, conditions)
+ return unless conditions
+ included, excluded = conditions[:only], conditions[:except]
+ write_inheritable_hash("included_actions", condition_hash(filters, included)) && return if included
+ write_inheritable_hash("excluded_actions", condition_hash(filters, excluded)) if excluded
+ end
+
+ def condition_hash(filters, *actions)
+ filters.inject({}) {|hash, filter| hash.merge(filter => actions.flatten.map {|action| action.to_s})}
+ end
end
module InstanceMethods # :nodoc:
@@ -252,18 +310,21 @@ module ActionController #:nodoc:
private
def call_filters(filters)
filters.each do |filter|
- if Symbol === filter
- if self.send(filter) == false then return false end
- elsif filter_block?(filter)
- if filter.call(self) == false then return false end
- elsif filter_class?(filter)
- if filter.filter(self) == false then return false end
- else
- raise(
- ActionControllerError,
- "Filters need to be either a symbol, proc/method, or class implementing a static filter method"
- )
+ next if action_exempted?(filter)
+ filter_result = case
+ when filter.is_a?(Symbol)
+ self.send(filter)
+ when filter_block?(filter)
+ filter.call(self)
+ when filter_class?(filter)
+ filter.filter(self)
+ else
+ raise(
+ ActionControllerError,
+ "Filters need to be either a symbol, proc/method, or class implementing a static filter method"
+ )
end
+ return false if filter_result == false
end
end
@@ -274,6 +335,15 @@ module ActionController #:nodoc:
def filter_class?(filter)
filter.respond_to?("filter")
end
+
+ def action_exempted?(filter)
+ case
+ when self.class.included_actions[filter]
+ !self.class.included_actions[filter].include?(action_name)
+ when self.class.excluded_actions[filter]
+ self.class.excluded_actions[filter].include?(action_name)
+ end
+ end
end
end
end