Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

Changeset 354

Show
Ignore:
Timestamp:
01/09/05 17:14:47 (4 years ago)
Author:
david
Message:

Added conditional filters #431 [Marcel]

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/actionpack/CHANGELOG

    r352 r354  
    11*SVN* 
     2 
     3* Added conditional filters #431 [Marcel]. Example: 
     4 
     5    class JournalController < ActionController::Base 
     6      # only require authentication if the current action is edit or delete 
     7      before_filter :authorize, :only_on => [ :edit, :delete ] 
     8     
     9      private 
     10        def authorize 
     11          # redirect to login unless authenticated 
     12        end 
     13    end 
    214 
    315* Added authentication framework to protect actions behind a condition and redirect on failure. See ActionController::Authentication for more. 
  • trunk/actionpack/lib/action_controller/filters.rb

    r351 r354  
    127127    #     end 
    128128    #   end 
     129    # 
     130    # == Filter conditions 
     131    # 
     132    # Filters can be limited to run for only specific actions. This can be expressed either by listing the actions to 
     133    # exclude or the actions to include when executing the filter. Available conditions are +:only+ or +:except+, both  
     134    # of which accept an arbitrary number of method references. For example: 
     135    # 
     136    #   class Journal < ActionController::Base 
     137    #     # only require authentication if the current action is edit or delete 
     138    #     before_filter :authorize, :only => [ :edit, :delete ] 
     139    #     
     140    #     private 
     141    #       def authorize 
     142    #         # redirect to login unless authenticated 
     143    #       end 
     144    #   end 
     145    #  
     146    # When setting conditions on inline method (proc) filters the condition must come first and be placed in parenthesis. 
     147    # 
     148    #   class UserPreferences < ActionController::Base 
     149    #     before_filter(:except => :new) { # some proc ... } 
     150    #     # ... 
     151    #   end 
     152    # 
    129153    module ClassMethods 
    130154      # The passed <tt>filters</tt> will be appended to the array of filters that's run _before_ actions 
    131155      # on this controller are performed. 
    132156      def append_before_filter(*filters, &block) 
     157        conditions = extract_conditions!(filters) 
    133158        filters << block if block_given? 
     159        add_action_conditions(filters, conditions) 
    134160        append_filter_to_chain("before", filters) 
    135161      end 
     
    138164      # on this controller are performed. 
    139165      def prepend_before_filter(*filters, &block) 
     166        conditions = extract_conditions!(filters)  
    140167        filters << block if block_given? 
     168        add_action_conditions(filters, conditions) 
    141169        prepend_filter_to_chain("before", filters) 
    142170      end 
     
    148176      # on this controller are performed. 
    149177      def append_after_filter(*filters, &block) 
     178        conditions = extract_conditions!(filters)  
    150179        filters << block if block_given? 
     180        add_action_conditions(filters, conditions) 
    151181        append_filter_to_chain("after", filters) 
    152182      end 
     
    155185      # on this controller are performed. 
    156186      def prepend_after_filter(*filters, &block) 
     187        conditions = extract_conditions!(filters)  
    157188        filters << block if block_given? 
     189        add_action_conditions(filters, conditions) 
    158190        prepend_filter_to_chain("after", filters) 
    159191      end 
     
    170202      #     A#after 
    171203      #   B#after 
    172       def append_around_filter(filters) 
    173         for filter in [filters].flatten 
     204      def append_around_filter(*filters) 
     205        for filter in filters.flatten 
    174206          ensure_filter_responds_to_before_and_after(filter) 
    175207          append_before_filter { |c| filter.before(c) } 
     
    186218      #     B#after 
    187219      #   A#after 
    188       def prepend_around_filter(filters) 
    189         for filter in [filters].flatten 
     220      def prepend_around_filter(*filters) 
     221        for filter in filters.flatten 
    190222          ensure_filter_responds_to_before_and_after(filter) 
    191223          prepend_before_filter { |c| filter.before(c) } 
     
    205237      def after_filters #:nodoc: 
    206238        read_inheritable_attribute("after_filters") 
     239      end 
     240       
     241      # Returns a mapping between filters and the actions that may run them. 
     242      def included_actions #:nodoc: 
     243        read_inheritable_attribute("included_actions") || {} 
     244      end 
     245       
     246      # Returns a mapping between filters and actions that may not run them. 
     247      def excluded_actions #:nodoc: 
     248        read_inheritable_attribute("excluded_actions") || {} 
    207249      end 
    208250       
     
    220262            raise ActionControllerError, "Filter object must respond to both before and after" 
    221263          end 
     264        end 
     265 
     266        def extract_conditions!(filters) 
     267          return nil unless filters.last.is_a? Hash 
     268          filters.pop 
     269        end 
     270 
     271        def add_action_conditions(filters, conditions) 
     272          return unless conditions 
     273          included, excluded = conditions[:only], conditions[:except] 
     274          write_inheritable_hash("included_actions", condition_hash(filters, included)) && return if included 
     275          write_inheritable_hash("excluded_actions", condition_hash(filters, excluded)) if excluded 
     276        end 
     277 
     278        def condition_hash(filters, *actions) 
     279          filters.inject({}) {|hash, filter| hash.merge(filter => actions.flatten.map {|action| action.to_s})} 
    222280        end 
    223281    end 
     
    253311        def call_filters(filters) 
    254312          filters.each do |filter|  
    255             if Symbol === filter 
    256               if self.send(filter) == false then return false end 
    257             elsif filter_block?(filter) 
    258               if filter.call(self) == false then return false end 
    259             elsif filter_class?(filter) 
    260               if filter.filter(self) == false then return false end 
    261             else 
    262               raise( 
    263                 ActionControllerError,  
    264                 "Filters need to be either a symbol, proc/method, or class implementing a static filter method" 
    265               ) 
     313            next if action_exempted?(filter) 
     314            filter_result = case 
     315              when filter.is_a?(Symbol) 
     316                self.send(filter) 
     317              when filter_block?(filter) 
     318                filter.call(self) 
     319              when filter_class?(filter) 
     320                filter.filter(self) 
     321              else 
     322                raise( 
     323                  ActionControllerError,  
     324                  "Filters need to be either a symbol, proc/method, or class implementing a static filter method" 
     325                ) 
    266326            end 
     327            return false if filter_result == false 
    267328          end 
    268329        end 
     
    275336          filter.respond_to?("filter") 
    276337        end 
     338 
     339        def action_exempted?(filter) 
     340          case 
     341            when self.class.included_actions[filter] 
     342              !self.class.included_actions[filter].include?(action_name) 
     343            when self.class.excluded_actions[filter]  
     344              self.class.excluded_actions[filter].include?(action_name) 
     345          end 
     346        end 
    277347    end 
    278348  end 
  • trunk/actionpack/test/controller/filters_test.rb

    r347 r354  
    1616  end 
    1717   
     18  class ConditionalFilterController < ActionController::Base 
     19    def show 
     20      render_text "ran action" 
     21    end 
     22 
     23    def another_action 
     24      render_text "ran action" 
     25    end 
     26 
     27    def show_without_filter 
     28      render_text "ran action without filter" 
     29    end 
     30 
     31    private 
     32      def ensure_login 
     33        @ran_filter ||= [] 
     34        @ran_filter << "ensure_login" 
     35      end 
     36 
     37      def clean_up_tmp 
     38        @ran_filter ||= [] 
     39        @ran_filter << "clean_up_tmp" 
     40      end 
     41       
     42      def rescue_action(e) raise(e) end 
     43  end 
     44 
     45  class ConditionalCollectionFilterController < ConditionalFilterController 
     46    before_filter :ensure_login, :except => [ :show_without_filter, :another_action ] 
     47  end 
     48 
     49  class OnlyConditionSymController < ConditionalFilterController  
     50    before_filter :ensure_login, :only => :show 
     51  end 
     52 
     53  class ExceptConditionSymController < ConditionalFilterController 
     54    before_filter :ensure_login, :except => :show_without_filter 
     55  end 
     56 
     57  class BeforeAndAfterConditionController < ConditionalFilterController 
     58    before_filter :ensure_login, :only => :show 
     59    after_filter  :clean_up_tmp, :only => :show  
     60  end 
     61   
     62  class OnlyConditionProcController < ConditionalFilterController  
     63    before_filter(:only => :show) {|c| c.assigns["ran_proc_filter"] = true } 
     64  end 
     65 
     66  class ExceptConditionProcController < ConditionalFilterController 
     67    before_filter(:except => :show_without_filter) {|c| c.assigns["ran_proc_filter"] = true } 
     68  end 
     69 
     70  class ConditionalClassFilter 
     71    def self.filter(controller) controller.assigns["ran_class_filter"] = true end 
     72  end 
     73 
     74  class OnlyConditionClassController < ConditionalFilterController 
     75    before_filter ConditionalClassFilter, :only => :show 
     76  end 
     77 
     78  class ExceptConditionClassController < ConditionalFilterController 
     79    before_filter ConditionalClassFilter, :except => :show_without_filter 
     80  end 
     81 
     82  class AnomolousYetValidConditionController < ConditionalFilterController 
     83    before_filter(ConditionalClassFilter, :ensure_login, Proc.new {|c| c.assigns["ran_proc_filter1"] = true }, :except => :show_without_filter) { |c| c.assigns["ran_proc_filter2"] = true} 
     84  end 
     85 
    1886  class PrependingController < TestController 
    1987    prepend_before_filter :wonderful_life 
     
    127195  end 
    128196 
     197  def test_running_anomolous_yet_valid_condition_filters 
     198    response = test_process(AnomolousYetValidConditionController) 
     199    assert_equal %w( ensure_login ), response.template.assigns["ran_filter"] 
     200    assert response.template.assigns["ran_class_filter"] 
     201    assert response.template.assigns["ran_proc_filter1"] 
     202    assert response.template.assigns["ran_proc_filter2"] 
     203     
     204    response = test_process(AnomolousYetValidConditionController, "show_without_filter") 
     205    assert_equal nil, response.template.assigns["ran_filter"] 
     206    assert !response.template.assigns["ran_class_filter"] 
     207    assert !response.template.assigns["ran_proc_filter1"] 
     208    assert !response.template.assigns["ran_proc_filter2"] 
     209  end 
     210 
     211  def test_running_collection_condition_filters 
     212    assert_equal %w( ensure_login ), test_process(ConditionalCollectionFilterController).template.assigns["ran_filter"] 
     213    assert_equal nil, test_process(ConditionalCollectionFilterController, "show_without_filter").template.assigns["ran_filter"] 
     214    assert_equal nil, test_process(ConditionalCollectionFilterController, "another_action").template.assigns["ran_filter"] 
     215  end 
     216 
     217  def test_running_only_condition_filters 
     218    assert_equal %w( ensure_login ), test_process(OnlyConditionSymController).template.assigns["ran_filter"] 
     219    assert_equal nil, test_process(OnlyConditionSymController, "show_without_filter").template.assigns["ran_filter"] 
     220 
     221    assert test_process(OnlyConditionProcController).template.assigns["ran_proc_filter"] 
     222    assert !test_process(OnlyConditionProcController, "show_without_filter").template.assigns["ran_proc_filter"] 
     223 
     224    assert test_process(OnlyConditionClassController).template.assigns["ran_class_filter"] 
     225    assert !test_process(OnlyConditionClassController, "show_without_filter").template.assigns["ran_class_filter"] 
     226  end 
     227 
     228  def test_running_except_condition_filters 
     229    assert_equal %w( ensure_login ), test_process(ExceptConditionSymController).template.assigns["ran_filter"] 
     230    assert_equal nil, test_process(ExceptConditionSymController, "show_without_filter").template.assigns["ran_filter"] 
     231 
     232    assert test_process(ExceptConditionProcController).template.assigns["ran_proc_filter"] 
     233    assert !test_process(ExceptConditionProcController, "show_without_filter").template.assigns["ran_proc_filter"] 
     234 
     235    assert test_process(ExceptConditionClassController).template.assigns["ran_class_filter"] 
     236    assert !test_process(ExceptConditionClassController, "show_without_filter").template.assigns["ran_class_filter"] 
     237  end 
     238 
     239  def test_running_before_and_after_condition_filters 
     240    assert_equal %w( ensure_login clean_up_tmp), test_process(BeforeAndAfterConditionController).template.assigns["ran_filter"] 
     241    assert_equal nil, test_process(BeforeAndAfterConditionController, "show_without_filter").template.assigns["ran_filter"] 
     242  end 
     243   
    129244  def test_bad_filter 
    130245    assert_raises(ActionController::ActionControllerError) {