Skip to content
This repository
Browse code

Allow you to pass :all_blank to :reject_if option to automatically cr…

…eate a Proc that will reject any record with blank attributes.
  • Loading branch information...
commit 9010ed27559ed5ab89ea71b4b16f4c8e56d03dbb 1 parent 026b78f
Mike Breen authored May 10, 2009 NZKoz committed May 10, 2009
11  activerecord/lib/active_record/nested_attributes.rb
@@ -180,10 +180,14 @@ module ClassMethods
180 180
       #   and the Proc should return either +true+ or +false+. When no Proc
181 181
       #   is specified a record will be built for all attribute hashes that
182 182
       #   do not have a <tt>_delete</tt> that evaluates to true.
  183
+      #   Passing <tt>:all_blank</tt> instead of a Proc will create a proc
  184
+      #   that will reject a record where all the attributes are blank.
183 185
       #
184 186
       # Examples:
185 187
       #   # creates avatar_attributes=
186 188
       #   accepts_nested_attributes_for :avatar, :reject_if => proc { |attributes| attributes['name'].blank? }
  189
+      #   # creates avatar_attributes=
  190
+      #   accepts_nested_attributes_for :avatar, :reject_if => :all_blank
187 191
       #   # creates avatar_attributes= and posts_attributes=
188 192
       #   accepts_nested_attributes_for :avatar, :posts, :allow_destroy => true
189 193
       def accepts_nested_attributes_for(*attr_names)
@@ -201,7 +205,12 @@ def accepts_nested_attributes_for(*attr_names)
201 205
             end
202 206
 
203 207
             reflection.options[:autosave] = true
204  
-            self.reject_new_nested_attributes_procs[association_name.to_sym] = options[:reject_if]
  208
+
  209
+            self.reject_new_nested_attributes_procs[association_name.to_sym] = if options[:reject_if] == :all_blank
  210
+              proc { |attributes| attributes.all? {|k,v| v.blank?} }
  211
+            else
  212
+              options[:reject_if]
  213
+            end
205 214
 
206 215
             # def pirate_attributes=(attributes)
207 216
             #   assign_nested_attributes_for_one_to_one_association(:pirate, attributes, false)
18  activerecord/test/cases/nested_attributes_test.rb
@@ -31,11 +31,27 @@ def test_base_should_have_an_empty_reject_new_nested_attributes_procs
31 31
   end
32 32
 
33 33
   def test_should_add_a_proc_to_reject_new_nested_attributes_procs
34  
-    [:parrots, :birds].each do |name|
  34
+    [:parrots, :birds, :birds_with_reject_all_blank].each do |name|
35 35
       assert_instance_of Proc, Pirate.reject_new_nested_attributes_procs[name]
36 36
     end
37 37
   end
38 38
 
  39
+  def test_should_not_build_a_new_record_if_reject_all_blank_returns_false
  40
+    pirate = Pirate.create!(:catchphrase => "Don' botharrr talkin' like one, savvy?")
  41
+    pirate.birds_with_reject_all_blank_attributes = [{:name => '', :color => ''}]
  42
+    pirate.save!
  43
+
  44
+    assert pirate.birds_with_reject_all_blank.empty?
  45
+  end
  46
+
  47
+  def test_should_build_a_new_record_if_reject_all_blank_does_not_return_false
  48
+    pirate = Pirate.create!(:catchphrase => "Don' botharrr talkin' like one, savvy?")
  49
+    pirate.birds_with_reject_all_blank_attributes = [{:name => 'Tweetie', :color => ''}]
  50
+    pirate.save!
  51
+
  52
+    assert_equal 1, pirate.birds_with_reject_all_blank.count
  53
+  end
  54
+
39 55
   def test_should_raise_an_ArgumentError_for_non_existing_associations
40 56
     assert_raise_with_message ArgumentError, "No association found for name `honesty'. Has it been defined yet?" do
41 57
       Pirate.accepts_nested_attributes_for :honesty
2  activerecord/test/models/pirate.rb
@@ -28,11 +28,13 @@ class Pirate < ActiveRecord::Base
28 28
     :after_add      => proc {|p,b| p.ship_log << "after_adding_proc_bird_#{b.id || '<new>'}"},
29 29
     :before_remove  => proc {|p,b| p.ship_log << "before_removing_proc_bird_#{b.id}"},
30 30
     :after_remove   => proc {|p,b| p.ship_log << "after_removing_proc_bird_#{b.id}"}
  31
+  has_many :birds_with_reject_all_blank, :class_name => "Bird"
31 32
 
32 33
   accepts_nested_attributes_for :parrots, :birds, :allow_destroy => true, :reject_if => proc { |attributes| attributes.empty? }
33 34
   accepts_nested_attributes_for :ship, :allow_destroy => true, :reject_if => proc { |attributes| attributes.empty? }
34 35
   accepts_nested_attributes_for :parrots_with_method_callbacks, :parrots_with_proc_callbacks,
35 36
     :birds_with_method_callbacks, :birds_with_proc_callbacks, :allow_destroy => true
  37
+  accepts_nested_attributes_for :birds_with_reject_all_blank, :reject_if => :all_blank
36 38
 
37 39
   validates_presence_of :catchphrase
38 40
 
1  activerecord/test/schema/schema.rb
@@ -57,6 +57,7 @@ def create_table(*args, &block)
57 57
 
58 58
   create_table :birds, :force => true do |t|
59 59
     t.string :name
  60
+    t.string :color
60 61
     t.integer :pirate_id
61 62
   end
62 63
 

5 notes on commit 9010ed2

Brad Langhorst

I think it's pretty typical to end up with something like this
"choices_attributes"=>{"0"=>{"upperlimit"=>"", "_delete"=>"0", "lowerlimit"=>"", "value"=>""}}

which will fail the :all_blank test since _delete => 0

maybe all except hidden field should be blank? (detecting hiddenness by the _? - ugh)

Mike Breen

Well you could do proc { |attributes| attributes.reject {|k,v| k == '_delete'}.all? {|k,v| v.blank?} }

Mike Breen

But if you follow this convention you shouldn't have a problem

<% unless order_form.object.new_record? %> 
  <div>
    <%= order_form.label :_delete, 'Remove:' %>
    <%= order_form.check_box :_delete %>
 </div>
<% end %>
Thomas Watson Steen

Love this feature... Any reason why this haven't made it into 2-3-stable yet?

Carter Thaxton

I'd prefer the following:

lambda { |attrs| attrs.all? { |k,v| k == '_delete' || v.blank? } }

It uses lambda, instead of proc, which has a different meaning in ruby 1.9
I don't like to have the new_record? check in my views. Why is that necessary?

Please sign in to comment.
Something went wrong with that request. Please try again.