Note that there are some explanatory texts on larger screens.

plurals
  1. POHow does reject_if: :all_blank for accepts_nested_attributes_for work when working with doubly nested associations?
    primarykey
    data
    text
    <p>I have my model setup as below. Everything works fine except blank part records are allowed even if all part and chapter fields are blank.</p> <pre><code>class Book &lt; ActiveRecord::Base has_many :parts, inverse_of: :book accepts_nested_attributes_for :parts, reject_if: :all_blank end class Part &lt; ActiveRecord::Base belongs_to :book, inverse_of: :parts has_many :chapters, inverse_of: :part accepts_nested_attributes_for :chapters, reject_if: :all_blank end class Chapter &lt; ActiveRecord::Base belongs_to :part, inverse_of: :chapters end </code></pre> <p>Spelunking the code, <code>:all_blank</code> gets replaced with <code>proc { |attributes| attributes.all? { |key, value| key == '_destroy' || value.blank? } }</code>. So, I use that instead of <code>:all_blank</code> and add in some debugging. Looks like what is happening is the part's chapters attribute is responding to <code>blank?</code> with <code>false</code> because it is an instantiated hash object, even though all it contains is another hash that only contains blank values:</p> <pre><code>chapters_attributes: !ruby/hash:ActionController::Parameters '0': !ruby/hash:ActionController::Parameters title: '' text: '' </code></pre> <p>Is it just not meant to work this way?</p> <p>I've found a workaround:</p> <pre><code>accepts_nested_attributes_for :parts, reject_if: proc { |attributes| attributes.all? do |key, value| key == '_destroy' || value.blank? || (value.is_a?(Hash) &amp;&amp; value.all? { |key2, value2| value2.all? { |key3, value3| key3 == '_destroy' || value3.blank? } }) end } </code></pre> <p>But I was hoping I was missing a better way to handle this.</p> <hr> <p><strong>Update 1:</strong> I tried redefining <code>blank?</code> for <code>Hash</code> but that causes probs.</p> <pre><code>class Hash def blank? :empty? || all? { |k,v| v.blank? } end end </code></pre> <hr> <p><strong>Update 2:</strong> This makes <code>:all_blank</code> work as I was expecting it to, but it is ugly and not well-tested.</p> <pre><code>module ActiveRecord::NestedAttributes::ClassMethods REJECT_ALL_BLANK_PROC = proc { |attributes| attributes.all? { |k, v| k == '_destroy' || v.valueless? } } end class Object alias_method :valueless?, :blank? end class Hash def valueless? blank? || all? { |k, v| v.valueless? } end end </code></pre> <hr> <p><strong>Update 3:</strong> Doh! Update 1 had a typo in it. This version does seem to work. </p> <pre><code>class Hash def blank? empty? || all? { |k,v| v.blank? } end end </code></pre> <p>Does this have too much potential for unintended consequences to be a viable option? If this is a good option, where in my app should this code live?</p>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload