1

Sorry about the awkward phrasing of the title -- not quite sure of the best way to title this but here's what I'm seeing assistance with:

In a Rails app, let's say we've got a model for a Product, and one of the attributes of the product is Price.

On the admin side of my app, I'd like to be able to set a "default" price that could be referred to if any new Product created isn't assigned a Price. If a Product does have a value for Price, then it would be used.

This is, of course, an example -- and I ask this question because I've got to imagine this is a common pattern. This can be applied to any resource that might have user-configurable global defaults or resource-specific values.

In pure Ruby, this would be solved, I think, with a class variable, so I'd be able to define @@default_price within the Product class and be able to refer to Product.default_price if the instantiated object's value doesn't exist.

My research here has pointed me towards the rails-settings-cached gem, which would allow for something like MyApp.default_price, but I'm wondering if there's a more elegant (non-plugin) way to accomplish this within the base Rails framework.

Note I'd like to setup this structure in code, but I want to be able to define the actual values through my app (i.e. config files aren't the solution I'm looking for).

Can someone enlighten me with the Rails way of handling this?

nlh
  • 1,055
  • 1
  • 10
  • 15

1 Answers1

0

ActiveRecord picks up default attribute values from the database schema. However, these are baked into the migration and table schema and not configurable.

If you want configurability, the pattern that I've used is a before_validation callback method to set a value if the attribute is blank, e.g.:

class Product < ActiveRecord::Base

  before_validation :set_price_if_blank
  validates :price, :presence => true  # sanity check in case the default is missing

  has_one :price

  private

  def set_price_if_blank
    self.price = Price.default if self.price.blank?
  end      

end

class Price < ActiveRecord::Base

  def self.default
    @@default ||= Price.where(:default => true).first
  end

end

This assumes that your price table is populated with a row that has a default flag. You could achieve this, e.g. through a seeds.rb file. I've added a validation rule to make sure that you still get an error if no default exists. It adds robustness to your application.

Also note that it's best to use Integers or Decimals for price data, not floats. See this answer.

Community
  • 1
  • 1
Wolfram Arnold
  • 7,159
  • 5
  • 44
  • 64
  • 1
    This is great - thank you Wolfram. I guess I wasn't so far off in my instinct to lean towards class variables :) One question -- it seems that making Price its own class might add a layer of complexity. Is there a way to achieve this without doing that (i.e. keeping Price as simply an attribute of the Product model) ? Thanks again. – nlh Jan 30 '12 at 06:15