<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array"/>
  <modified type="array">
    <modified>
      <diff>@@ -1,3 +1,9 @@
+03/17/2009
+----------
+* more sane way to specify nested parameters (honestly, I don't know what I was thinking before)
+* the old method of specifying nested parameters (&quot;user/first_name&quot;, &quot;user/last_name&quot;) DOES NOT WORK ANYMORE.
+* changed param_protected/param_accessible's :exclude argument to :except to better fit in with Rails
+
 07/16/2008
 ----------
 * rewrote the entire plugin (it should actually work now)
\ No newline at end of file</diff>
      <filename>CHANGELOG</filename>
    </modified>
    <modified>
      <diff>@@ -25,44 +25,20 @@ Any of these combinations should work.
  param_protected :client_id
  param_protected [:client_id, :user_id]
  param_protected :client_id, :only =&gt; 'my_action'
- param_protected :client_id, :exclude =&gt; [:your_action, :my_action]
+ param_protected :client_id, :except =&gt; [:your_action, :my_action]
 
 == Whitelisting
 Any of these combinations should work.
  param_accessible :client_id
  param_accessible :[:client_id, :user_id]
  param_accessible :client_id, :only =&gt; 'my_action'
- param_accessible :client_id, :exclude =&gt; [:your_action, :my_action]
+ param_accessible :client_id, :except =&gt; [:your_action, :my_action]
 
 == Nested Params
-There is a language to protect nested params, but it has some caveats.
-
- param_protected 'user/fname'
-&lt;tt&gt;params[:user][:fname]&lt;/tt&gt; will be removed, but &lt;tt&gt;params[:user][:client_id]&lt;/tt&gt; won't (or anything else for that matter.)
-
- param_protected 'user'
-This works as expected... it removes &lt;tt&gt;params[:user]&lt;/tt&gt;, even if it is a Hash.
-
- param_accessible 'user/fname'
-This will filter &lt;tt&gt;params[:user][:lname]&lt;/tt&gt; and anything that is not &lt;tt&gt;params[:user][:fname]&lt;/tt&gt;.
-
- param_accessible 'user'
-This has no effect if &lt;tt&gt;params[:user]&lt;/tt&gt; is a Hash.
-
-== Array Params
-If you have an array of params like
- params[:person][:nicknames][0]
- params[:person][:nicknames][1]
- ...
- params[:person][:nicknames][n]
-
-You can remove all of them by saying
- param_protected 'person/nicknames'
-
-&lt;tt&gt;param_accessible&lt;/tt&gt; also works with array params.
+You can use combinations of arrays and hashes to specify nested params, much the same way ActiveRecord::Base#find's
+:include argument works.
+ param_accessible [:account_name, :user =&gt; [:first_name, :last_name, :address =&gt; [:street, :city, :state]]]
+ param_protected [:id, :password, :user =&gt; [:id, :password]]
 
 == Caveats
 Both &lt;tt&gt;param_protected&lt;/tt&gt; and &lt;tt&gt;param_accessible&lt;/tt&gt; are really just calls to &lt;tt&gt;prepend_before_filter&lt;/tt&gt;.  Thus any methods in your filter chain that run before either of these methods will have full access to the &lt;em&gt;unprotected&lt;/em&gt; &lt;tt&gt;params&lt;/tt&gt; Hash.
-
-== Notes
-You should be able to see all the parameters that have been filtered out in your log (log level info).  They get printed out directly after the 'Parameters:' line.
\ No newline at end of file</diff>
      <filename>README.rdoc</filename>
    </modified>
    <modified>
      <diff>@@ -31,24 +31,21 @@ module Cjbottaro
     module InstanceMethods
     
       def do_param_protected
-        params_to_kill = Helpers.params_to_live_or_kill(self.class.pp_protected, action_name.to_s,self)
-        params_by_path = Helpers.params_by_path(params)
-        params_to_kill.each do |param|
-          next unless params_by_path.has_key?(param)
-          params_by_path[param].each { |h, v| h.delete(v) }
+        self.class.pp_protected.each do |protected_params, actions|
+          scope, actions = actions.first, actions[1..-1]
+          Helpers.do_param_protected(protected_params, self.params) \
+            if Helpers.action_matches?(scope, actions, self.action_name)
         end
       end
       
       def do_param_accessible
-        params_to_live = Helpers.params_to_live_or_kill(self.class.pp_accessible, action_name.to_s,self)
-        return if params_to_live.blank?
-        params_by_path = Helpers.params_by_path(params)
-        params_by_path.each do |path, hash|
-          next if params_to_live.include?(path)
-          hash.each { |h, v| h.delete(v) }
+        self.class.pp_accessible.each do |accessible_params, actions|
+          scope, actions = actions.first, actions[1..-1]
+          Helpers.do_param_accessible(accessible_params, self.params) \
+            if Helpers.action_matches?(scope, actions, self.action_name)
         end
       end
-    
+      
     end
     
     module Helpers
@@ -61,71 +58,62 @@ module Cjbottaro
         klass.pp_accessible = [] if klass.pp_accessible.nil?
       end
       
-      def self.normalize_params(params)
-        params = [params] unless params.instance_of?(Array)
-        params.collect{ |param| param.to_s }
+      def self.normalize_params(params, params_out = {})
+        if params.instance_of?(Array)
+          params.each{ |param| normalize_params(param, params_out) }
+        elsif params.instance_of?(Hash)
+          params.each do |k, v|
+            k = k.to_s
+            params_out[k] = {}
+            normalize_params(v, params_out[k])
+          end
+        else
+          params_out[params.to_s] = nil
+        end
+        params_out
       end
       
       def self.normalize_actions(actions)
-        return [:except] if actions.blank?
+        error_message = &quot;invalid actions, use :only =&gt; ..., :except =&gt; ..., or nil&quot;
+        return [:except, nil] if actions.blank?
+        raise ArgumentError, error_message unless actions.instance_of?(Hash)
+        raise ArgumentError, error_message unless actions.length == 1
+        raise ArgumentError, error_message unless [:only, :except].include?(actions.keys.first)
+        
         scope, actions = actions.keys.first, actions.values.first
         actions = [actions] unless actions.instance_of?(Array)
-        actions = actions.collect{ |action| action.to_s } unless actions.first.instance_of?(Proc)
+        actions = actions.collect{ |action| action.to_s }
         [scope, *actions]
       end
       
-      # specs will something like...
-      # [[param1, param2], [:only, action1, action2]
-      #  [param2, param3], [:except, action3, action4]]
-      def self.params_to_live_or_kill(specs, action, controller)
-        specs.inject([]) do |memo, params_actions|
-          params, actions = params_actions
-          scope = actions.first # actions is a reference to a part of pp_protected or pp_accessible, so we don't want to alter it
-          actions = case actions[1]
-          when Array
-            actions[1..-1]
-          when Proc
-            actions[1].bind(controller).call()
-          else
-            []
-          end
-          memo += params if scope == :only and actions.include?(action)
-          memo += params if scope == :except and !actions.include?(action)
-          memo
+      def self.action_matches?(scope, valid_actions, action_name)
+        if scope == :only
+          valid_actions.include?(action_name)
+        elsif scope == :except
+          !valid_actions.include?(action_name)
+        else
+          raise ArgumentError, &quot;unexpected scope (#{scope}), expected :only or :except&quot;
         end
       end
-
-      # Takes a params hash and returns a hash where the keys are an xpath like string and the values are the
-      # information needed to remove the entry from the inputted params hash.
-      # Examples:
-      #  params = { :user =&gt; {:first =&gt; 'calia', :last =&gt; 'rose' } }
-      #  params_by_xpathy(params) =&gt; { 'user' =&gt; [[params, :user]],
-      #                                'user/first' =&gt; [[params[:user], :first]],
-      #                                'user/last'  =&gt; [[params[:user], :last]] }
-      # Notice the values are arrays of doubles.  That's so we can properly filter on params that are arrays, like so:
-      #  params = { :users =&gt; [ {:first =&gt; 'calia', :last =&gt; 'rose'},
-      #                         {:first =&gt; 'coco',  :last =&gt; 'rae' } ] }
-      #  params_by_xpathy(params) =&gt; { 'users' =&gt; [[params, :user]],
-      #                                'users/first' =&gt; [[params[:user][0], :first], [params[:user][1], :first]],
-      #                                'users/last'  =&gt; [[params[:user][0], :last ], [params[:user][0], :last ]] }
-      # Now if we want to protect parameter 'users/first', we can simply do...
-      #  params_by_xpathy(params)['users/first'].each { |h, v| h.delete(v) }      
-      def self.params_by_path(node, path_so_far = '', result = {})
-        node.each do |k, v|
-          path = path_so_far.blank? ? k.to_s : path_so_far + &quot;/#{k}&quot;
-          result[path] ||= []
-          result[path] &lt;&lt; [node, k]
-          if v.is_a?(Hash)
-            params_by_path(v, path, result)
-          elsif v.is_a?(Array)
-            v.each{ |e| params_by_path(e, path, result)  }
-          end
-        end
-        return result
+      
+      def self.do_param_protected(protected_params, params)
+        return unless params.kind_of?(Hash)
+        return if protected_params.nil?
+        params.delete_if{ |k, v| protected_params.has_key?(k) and protected_params[k].nil? }
+        params.each{ |k, v| do_param_protected(protected_params[k], v) }
+        params
+      end
+      
+      def self.do_param_accessible(accessible_params, params)
+        return unless params.kind_of?(Hash)
+        return if accessible_params.nil?
+        params.delete_if{ |k, v| !accessible_params.has_key?(k) }
+        params.each{ |k, v| do_param_accessible(accessible_params[k], v) }
+        params
       end
       
     end
-
+    
   end
-
+  
 end
\ No newline at end of file</diff>
      <filename>lib/param_protected.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,94 +1,126 @@
 require File.dirname(__FILE__) + '/../../../../test/test_helper'
-require File.dirname(__FILE__) + '/../init.rb'
 
-class FakeController &lt; ActionController::Base
-
-  def fake_action1
-    render :text =&gt; ''
-  end
-
-  def fake_action2
-    render :text =&gt; ''
-  end
-
-  def fake_action3
-    render :text =&gt; ''
-  end
+class HelpersTest &lt; Test::Unit::TestCase
   
-  protected
-
-  def rescue_action(e)
-    raise e
+  # a little aliasing so I don't have to type so much.
+  Helpers = Cjbottaro::ParamProtected::Helpers
+  
+  def test_normalize_params
+    params = Helpers.normalize_params(:something)
+    assert_equal({&quot;something&quot; =&gt; nil}, params)
+    
+    params = Helpers.normalize_params([:something, :else])
+    assert_equal({&quot;something&quot; =&gt; nil, &quot;else&quot; =&gt; nil}, params)
+    
+    params = Helpers.normalize_params(:something =&gt; [:stuff, :blah])
+    assert_equal({&quot;something&quot; =&gt; {&quot;stuff&quot; =&gt; nil, &quot;blah&quot; =&gt; nil}}, params)
+    
+    params = Helpers.normalize_params(:something =&gt; [:stuff, {:blah =&gt; :bleck}])
+    assert_equal({&quot;something&quot; =&gt; {&quot;stuff&quot; =&gt; nil, &quot;blah&quot; =&gt; {&quot;bleck&quot; =&gt; nil}}}, params)
   end
-
-end
-
-class HelpersTest &lt; Test::Unit::TestCase
-
-  def setup
-    class &lt;&lt; FakeController
-      attr_accessor :pp_protected, :pp_accessible
-    end
-    FakeController.pp_protected  = []
-    FakeController.pp_accessible = []
-    @controller = FakeController.new
-    @request    = ActionController::TestRequest.new
-    @response   = ActionController::TestResponse.new
+  
+  def test_normalize_actions
+    actions = Helpers.normalize_actions(nil)
+    assert_equal [:except, nil], actions
+    
+    actions = Helpers.normalize_actions(:only =&gt; :blah)
+    assert_equal [:only, &quot;blah&quot;], actions
+    
+    actions = Helpers.normalize_actions(:only =&gt; [:blah, :bleck])
+    assert_equal [:only, &quot;blah&quot;, &quot;bleck&quot;], actions
+    
+    actions = Helpers.normalize_actions(:except =&gt; :blah)
+    assert_equal [:except, &quot;blah&quot;], actions
+    
+    actions = Helpers.normalize_actions(:except =&gt; [:blah, :bleck])
+    assert_equal [:except, &quot;blah&quot;, &quot;bleck&quot;], actions
+    
+    assert_raises(ArgumentError){ Helpers.normalize_actions(:onlyy =&gt; :blah) }
+    assert_raises(ArgumentError){ Helpers.normalize_actions(:blah) }
+    assert_raises(ArgumentError){ Helpers.normalize_actions(:only =&gt; :something, :except =&gt; :something) }
   end
   
-  def test_the_xpathy_thing
-    params = {
-      :scalar =&gt; 'test',
-      :user =&gt; { :name =&gt; { :fname =&gt; 'chris', :lname =&gt; 'bottaro'},
-                 :id   =&gt; 1 },
-      :pets =&gt; [ {:fname =&gt; 'calia', :mname =&gt; 'rose'},
-                 {:fname =&gt; 'coco',  :mname =&gt; 'rae' } ]              
-    }
-    expected_xpathy_thing = {
-      'scalar' =&gt; [[params, :scalar]],
-      'user' =&gt; [[params, :user]],
-      'user/name' =&gt; [[params[:user], :name]],
-      'user/name/fname' =&gt; [[params[:user][:name], :fname]],
-      'user/name/lname' =&gt; [[params[:user][:name], :lname]],
-      'user/id' =&gt; [[params[:user], :id]],
-      'pets' =&gt; [[params, :pets]],
-      'pets/fname' =&gt; [[params[:pets][0], :fname], [params[:pets][1], :fname]],
-      'pets/mname' =&gt; [[params[:pets][0], :mname], [params[:pets][1], :mname]]
-    }
-    xpathy_thing = Cjbottaro::ParamProtected::Helpers.params_by_path(params)
-    assert_equal expected_xpathy_thing, xpathy_thing
+  def test_action_matches
+    assert  Helpers.action_matches?(:only, [&quot;blah&quot;, &quot;bleck&quot;], &quot;blah&quot;)
+    assert  Helpers.action_matches?(:only, [&quot;blah&quot;, &quot;bleck&quot;], &quot;bleck&quot;)
+    assert !Helpers.action_matches?(:only, [&quot;blah&quot;, &quot;bleck&quot;], &quot;not&quot;)
+    
+    assert !Helpers.action_matches?(:except, [&quot;blah&quot;, &quot;bleck&quot;], &quot;blah&quot;)
+    assert !Helpers.action_matches?(:except, [&quot;blah&quot;, &quot;bleck&quot;], &quot;bleck&quot;)
+    assert  Helpers.action_matches?(:except, [&quot;blah&quot;, &quot;bleck&quot;], &quot;not&quot;)
+    
+    assert_raises(ArgumentError){ Helpers.action_matches?(:bad_scope, [&quot;blah&quot;, &quot;bleck&quot;], &quot;not&quot;) }
   end
-
-  def test_accessible_map
-    expected_map = []
+  
+  def test_do_param_accessible
     
-    FakeController.param_accessible :user_id
-    expected_map &lt;&lt; [['user_id'], [:except]]
-    assert_equal expected_map, FakeController.pp_accessible
+    accessible_params = { :account_id =&gt; nil,
+                          :user_id =&gt; nil }
+    params            = { :account_id =&gt; 123,
+                          :user_id =&gt; 456,
+                          :profile_id =&gt; 789 }
+    expected_results  = { :account_id =&gt; 123,
+                          :user_id =&gt; 456 }
+    assert_equal expected_results, Helpers.do_param_accessible(accessible_params, params)
     
-    FakeController.param_accessible :client_id
-    expected_map &lt;&lt; [['client_id'], [:except]]
-    assert_equal expected_map, FakeController.pp_accessible
+    accessible_params = { :account_id =&gt; nil,
+                          :user =&gt; nil }
+    params            = { :account_id =&gt; 123,
+                          :user =&gt; { :first_name =&gt; &quot;christopher&quot;, :last_name =&gt; &quot;bottaro&quot; },
+                          :profile_ids =&gt; [789, 987] }
+    expected_results  = { :account_id =&gt; 123,
+                          :user =&gt; { :first_name =&gt; &quot;christopher&quot;, :last_name =&gt; &quot;bottaro&quot;} }
+    assert_equal expected_results, Helpers.do_param_accessible(accessible_params, params)
     
-    FakeController.param_accessible :account_id, :only =&gt; :fake_action1
-    expected_map &lt;&lt; [['account_id'], [:only, 'fake_action1']]
-    assert_equal expected_map, FakeController.pp_accessible
+    accessible_params = { :account_id =&gt; nil,
+                          :user =&gt; {:first_name =&gt; nil, :last_name =&gt; nil} }
+    params            = { :account_id =&gt; 123,
+                          :user =&gt; { :first_name =&gt; &quot;christopher&quot;, :last_name =&gt; &quot;bottaro&quot;, :middle_name =&gt; &quot;james&quot; },
+                          :profile_ids =&gt; [789, 987] }
+    expected_results  = { :account_id =&gt; 123,
+                          :user =&gt; { :first_name =&gt; &quot;christopher&quot;, :last_name =&gt; &quot;bottaro&quot;} }
+    assert_equal expected_results, Helpers.do_param_accessible(accessible_params, params)
     
-    FakeController.param_accessible :account_id, :except =&gt; :fake_action1
-    expected_map &lt;&lt; [['account_id'], [:except, 'fake_action1']]
-    assert_equal expected_map, FakeController.pp_accessible
+    accessible_params = { :account_id =&gt; nil,
+                          :user =&gt; {:name =&gt; {:first =&gt; nil, :last =&gt; nil}} }
+    params            = { :account_id =&gt; 123,
+                          :user =&gt; { :city =&gt; &quot;Austin&quot;,
+                                     :name =&gt; {:first =&gt; &quot;christopher&quot;, :last =&gt; &quot;bottaro&quot;, :middle =&gt; &quot;james&quot;} },
+                          :profile_ids =&gt; [789, 987] }
+    expected_results  = { :account_id =&gt; 123,
+                          :user =&gt; { :name =&gt; {:first =&gt; &quot;christopher&quot;, :last =&gt; &quot;bottaro&quot;} } }
+    assert_equal expected_results, Helpers.do_param_accessible(accessible_params, params)
   end
   
-  def test_accessible_map_with_arrays
-    expected_map = []
+  def test_do_param_protected
     
-    FakeController.param_accessible [:user_id, :client_id], :only =&gt; [:fake_action1, :fake_action2]
-    expected_map &lt;&lt; [['user_id', 'client_id'], [:only, 'fake_action1', 'fake_action2']]
-    assert_equal expected_map, FakeController.pp_accessible
+    protected_params  = { :account_id =&gt; nil,
+                          :user =&gt; nil }
+    params            = { :account_id =&gt; 123,
+                          :user =&gt; { :first_name =&gt; &quot;christopher&quot;, :last_name =&gt; &quot;bottaro&quot; },
+                          :profile_ids =&gt; [789, 987] }
+    expected_results  = { :profile_ids =&gt; [789, 987] }
+    assert_equal expected_results, Helpers.do_param_protected(protected_params, params)
     
-    FakeController.param_accessible [:user_id, :client_id], :except =&gt; [:fake_action1, :fake_action2]
-    expected_map &lt;&lt; [['user_id', 'client_id'], [:except, 'fake_action1', 'fake_action2']]
-    assert_equal expected_map, FakeController.pp_accessible
+    protected_params  = { :account_id =&gt; nil,
+                          :user =&gt; {:middle_name =&gt; nil} }
+    params            = { :account_id =&gt; 123,
+                          :user =&gt; { :first_name =&gt; &quot;christopher&quot;, :last_name =&gt; &quot;bottaro&quot;, :middle_name =&gt; &quot;james&quot; },
+                          :profile_ids =&gt; [789, 987] }
+    expected_results  = { :user =&gt; {:first_name =&gt; &quot;christopher&quot;, :last_name =&gt; &quot;bottaro&quot;},
+                          :profile_ids =&gt; [789, 987] }
+    assert_equal expected_results, Helpers.do_param_protected(protected_params, params)
+    
+    protected_params  = { :account_id =&gt; nil,
+                          :user =&gt; {:name =&gt; {:middle =&gt; nil}} }
+    params            = { :account_id =&gt; 123,
+                          :user =&gt; { :city =&gt; &quot;Austin&quot;,
+                                     :name =&gt; {:first =&gt; &quot;christopher&quot;, :last =&gt; &quot;bottaro&quot;, :middle =&gt; &quot;james&quot;} },
+                          :profile_ids =&gt; [789, 987] }
+    expected_results  = { :profile_ids =&gt; [789, 987],
+                          :user =&gt; { :city =&gt; &quot;Austin&quot;,
+                          :name =&gt; {:first =&gt; &quot;christopher&quot;, :last =&gt; &quot;bottaro&quot;} } }
+    assert_equal expected_results, Helpers.do_param_protected(protected_params, params)
   end
-
+  
 end</diff>
      <filename>test/helpers_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -184,7 +184,7 @@ class ParamAccessibleTest &lt; Test::Unit::TestCase
   end
 
   def test_nested
-    @controller.class.param_accessible ['user', 'user/user_id'], :only =&gt; :fake_action1
+    @controller.class.param_accessible({:user =&gt; :user_id}, :only =&gt; :fake_action1)
 
     get :fake_action1, :user =&gt; { :user_id =&gt; 123, :good_id =&gt; 321 }, :user_id =&gt; 456, :good_id =&gt; 789
     assert @controller.params[:user].has_key?(:user_id) == true
@@ -211,8 +211,8 @@ class ParamAccessibleTest &lt; Test::Unit::TestCase
 
     get :fake_action1, :user =&gt; { :user_id =&gt; 123, :good_id =&gt; 321 }, :user_id =&gt; 456, :good_id =&gt; 789
     assert @controller.params.has_key?(:user) == true
-    assert @controller.params[:user].has_key?(:user_id) == false
-    assert @controller.params[:user].has_key?(:good_id) == false
+    assert @controller.params[:user].has_key?(:user_id) == true
+    assert @controller.params[:user].has_key?(:good_id) == true
     assert @controller.params.has_key?(:user_id) == false
     assert @controller.params.has_key?(:good_id) == false
 
@@ -228,5 +228,5 @@ class ParamAccessibleTest &lt; Test::Unit::TestCase
     assert @controller.params.has_key?(:user_id) == true
     assert @controller.params.has_key?(:good_id) == true
   end
-
+  
 end</diff>
      <filename>test/param_accessible_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -158,7 +158,7 @@ class ParamProtectedTest &lt; Test::Unit::TestCase
   end
 
   def test_nested
-    @controller.class.param_protected 'user/user_id', :only =&gt; :fake_action1
+    @controller.class.param_protected({:user =&gt; :user_id}, :only =&gt; :fake_action1)
 
     get :fake_action1, :user =&gt; { :user_id =&gt; 123, :good_id =&gt; 321 }, :user_id =&gt; 456, :good_id =&gt; 789
     assert @controller.params[:user].has_key?(:user_id) == false</diff>
      <filename>test/param_protected_test.rb</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>317ee309a7a1c4a542fb207f479403a6f9d39def</id>
    </parent>
  </parents>
  <author>
    <name>Scott Diedrick</name>
    <email>swalterd@gmail.com</email>
  </author>
  <url>http://github.com/mumboe/param_protected/commit/32d6260d9439069e1e434428051560561373bf72</url>
  <id>32d6260d9439069e1e434428051560561373bf72</id>
  <committed-date>2009-03-28T16:47:25-07:00</committed-date>
  <authored-date>2009-03-28T16:47:25-07:00</authored-date>
  <message>Porting over changes made by cjbottaro.  I will add back Proc support in the next commit.</message>
  <tree>47f3f64ac453034a6a953aed5eed51ae3d5a61b6</tree>
  <committer>
    <name>Scott Diedrick</name>
    <email>swalterd@gmail.com</email>
  </committer>
</commit>
