From 677d92299b36a0eeaf8ec6aec211f5e6e325fe0d Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Sun, 9 Jan 2005 17:14:47 +0000 Subject: Added conditional filters #431 [Marcel] git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@354 5ecf4fe2-1ee6-0310-87b1-e25e094e27de --- actionpack/lib/action_controller/filters.rb | 100 +++++++++++++++++++++++----- 1 file changed, 85 insertions(+), 15 deletions(-) (limited to 'actionpack/lib/action_controller') 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 filters 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 filters 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 filters 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 filters 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 -- cgit v1.2.3