0

I want to implement a method that checks if a model's instance has only nil or empty attributes, except from its id or timestamps.

I've made use of an auxiliary method that removes a key from Hash and return the remaining hash ( question 6227600)

class ActiveRecord::Base
    def blank?
        self.attributes.remove("id","created_at","updated_at").reject{|attr| self[attr].blank?}.empty?
    end
end

I guess that there may be much simpler, efficient or safer way to do this. Any suggestion?

Community
  • 1
  • 1
Claudio Floreani
  • 2,441
  • 28
  • 34
  • Why you want to create empty instance? Just create instance when you have information to fill it in. – megas Oct 13 '11 at 16:50
  • Are you wanting to check if a model object has changed? For example, if a user has not entered any content, then it will not have changed. – Jesse Wolgamott Oct 13 '11 at 17:21

2 Answers2

1
def blank?
  self.attributes.all?{|k,v| v.blank? || %w(id created_at updated_at).include?(k)}
end

My response is almost the same that tadman gave, but expressed in a more concise way. Be careful with two situations:

- **blank?** is not a good choice as name, since if you call **object_a.object_b.blank?** trying to know if there is or not a object_b inside object_a, you'll get true event if the object exists. **empty?** seems a better name

- If databases sets defaults values, it can be tricky.

EDIT: Since build an array every iteration is slow (thanks tadman), a beter solution is:

def empty?
  ignored_attrs = {'id' => 1, 'created_at' => 1, 'updated_at' => 1}
  self.attributes.all?{|k,v| v.blank? || ignored_attrs[k]}
end
miguel.camba
  • 1,777
  • 1
  • 14
  • 19
  • Thanks also for the observation on the method name. I never found Object#blank? very useful, but in any case you're right, it is better not to override the default methods. – Claudio Floreani Oct 14 '11 at 13:12
  • 1
    Declaring the array inline each time through the loop creates a significant performance penalty. You should consider creating a constant that contains the attributes you'd prefer to ignore, or better, transform it into a hash so the test is `IGNORED_ATTRIBUTES[k]` instead of `ARRAY.include?(k)` – tadman Oct 14 '11 at 16:35
  • Its true, but performance was not the point. I'll edit the response :D – miguel.camba Oct 14 '11 at 19:28
0

You could just check that all the properties in the attributes hash are not present, or the converse:

class ActiveRecord::Base
  def blank?
    !self.attributes.find do |key, value| 
      case (key)
      when 'id', 'created_at', 'updated_at'
        false
      else
        value.present?
      end
    end
  end
end

Unfortunately this will not account for things that are set with a default in your database, if any relationship keys are assigned, among other things. You will have to add those as exceptions, or compare the values to a known default state of some sort.

This sort of thing is probably best implemented on a case by case basis.

tadman
  • 208,517
  • 23
  • 234
  • 262
  • Thank you tadman. I can only accept one answer and I choose the one from Cibernox mainly because I prefer to avoid case constucts. However you have the credit for the first good answer! – Claudio Floreani Oct 14 '11 at 13:09
  • 1
    You should reconsider your stance, then. `case` is nearly O(1) time, where `Array#include?` is O(N) time. Even with just a few items in the list, `case` outperforms by a factor of five. – tadman Oct 14 '11 at 14:50