0

I'm curious as to whether it's feasible to develop a scheme for setting the default values for new ActiveRecord records. Based on some of the answers here, and this post on setting attributes, I've come up with something like this:

class Taco < ActiveRecord::Base

  DEFAULT_ATTR = {spice_level: 4}

  before_save do |taco|
    if new_record?
      DEFAULT_ATTR.each do |k,v|
        taco[k] ||= v
      end 
    end 
  end 

end

For the paranoid, the constant could be used to also set the default in the migration:

class CreateTacos  < ActiveRecord::Migration

  def defaults
    Taco::DEFAULT_ATTR
  end 

  def change
    add_column :tacos, :spice_level, :integer, :default => defaults[:spice_level]
  end 
end

What could be useful (until someone points out some obvious aspect I've overlooked!) is if this scheme was built into ActiveRecord as a callback, ala before_save (something like "new_record_defaults"). You could override the method and return a hash of symbol => default pairs, and maybe even the paranoid migration code could also leverage this.

I'm still fairly new to Rails, so I'm prepared to be told a scheme already exists or why this is a dumb idea, but feedback is welcome. :)

Update: Per Anton Grigoriev's answer, I think the attribute-defaults gem is the way to go. For posterity, here is an additional Concern, based on the author's original, for getting access to the defaults created:

module ActiveRecord
  module AttributesWithDefaultsAccessor
    extend ActiveSupport::Concern
    def all_defaults
      defaults = {}
      self.private_methods.each do |method|
        if method =~ /^__eval_attr_default_for_(.+)$/
          defaults[$1.to_sym] = self.send(method)
        end
      end
      defaults
    end
  end
  class Base
    include AttributesWithDefaultsAccessor
  end
end
Community
  • 1
  • 1
twelve17
  • 688
  • 8
  • 20

2 Answers2

1

You could use this gem https://github.com/bsm/attribute-defaults for setting attribute default values

Anton Grigoryev
  • 1,199
  • 11
  • 20
  • Thanks for pointing me to this module. The callback logic [is slightly different](https://github.com/bsm/attribute-defaults/blob/master/lib/attribute_defaults.rb#L33) but seems compatible. – twelve17 Apr 30 '13 at 17:35
0

I would prefer to keep this logic in application level instead of database level(by migration).

In application level, this is simply an overwriting of attribute

class Taco < ActiveRecord::Base
  def spice_level
    read_attribute(:spice_level) || 4
  end
end

The basic is, if you have set this attribute to something say 5, it is 5. If not, it is 4.

Ref about overwriting attribute: http://api.rubyonrails.org/classes/ActiveRecord/Base.html#label-Overwriting+default+accessors

Billy Chan
  • 24,625
  • 4
  • 52
  • 68
  • I too agree in keeping the application logic i the app instead of the database, but I wanted to leverage the use of defaults in the model to be used in migrations as a secondary enforcement since migrations offer it. Sometimes people like to poke around in the database, or otherwise muck with the data outside of the domain of AR, and while whether such activity is valid can be debated, it's handy to to get the second layer if it is easy to do, IMHO. – twelve17 Apr 30 '13 at 17:57
  • It depends on how important this setting is to you. But I prefer business logic to business area. What if you want to change it to 3? Another migration. If somebody want to play with db directly, he should be responsible for data integrity or he is breaking the app. By the way, if you really don't have too much default value to handle with, using a gem is an overkill. Avoid adding gems at reasonable cost, or you are inviting more things to maintain, more uncertainty and more overhead. – Billy Chan Apr 30 '13 at 18:12
  • Good points as well. This also brings up the fact that moving the default to be referenced from the model also breaks versioning of the migration itself, as the model could change and then the migration now means something different. Thanks. – twelve17 Apr 30 '13 at 18:16