The following blog post contains material either currently found or soon to be incorporated into my new book, "Easy Active Record for Rails Developers". Among many other topics, you'll learn about model generation, migrations, validations, associations, scopes, joins, includes, forms integration, nested forms, and model testing with RSpec and FactoryGirl. The book is now available, head over to this website's home page to learn more.


Active Record provides Rails developers with an incredibly convenient way to interact with the tables comprising the application’s underlying database using a series of classes known as models. Each class generally behaves like a PORO (Plain Old Ruby Object), however also includes several additional features thanks to inheriting from ActiveRecord::Base. Thanks to its superclass, it doesn’t take much for a model to be viable. For instance this is a perfectly capable (although impractical due to lack of validations) model:

class Game < ActiveRecord::Base
end

Once the Game model’s underlying table schema has been created (migrated) within the database, presuming the games table included attributes such as name, description, and approved (the latter being useful if a moderator has to for instance review a submitted arcade game before it enters the public catalog), then creating a new record is easily accomplished by instantiating a new instance of the Game class, setting the attributes, and saving the object:

game = Game.new
game.name = 'Space Invaders'
game.description = 'Battle the aliens. Save the world.'
game.approved = false
game.save

Yet because you want all submitted games to be identified as unapproved until a moderator has a chance to review the submission, so why not set this value by default since it should always be false? There are two practical ways one can go about doing so, either by setting a default value in the migration or within an after_initialize callback. When you created the Game class, the terminal command probably looked something like this:

$ rails g model Game name:string description:string approved:boolean
      invoke  active_record
      create    db/migrate/20140326032428_create_games.rb
      create    app/models/game.rb
      invoke    test_unit
      create      test/models/game_test.rb
      create      test/fixtures/games.yml

A corresponding migration (db/migrate/20140326032428_create_games.rb) was created when the model was generated. By default the migration looks like this:

class CreateGames < ActiveRecord::Migration
  def change
    create_table :games do |t|
      t.string :name
      t.string :description
      t.boolean :approved

      t.timestamps
    end
  end
end

You can edit this migration file, adding the desired default values using the :default option. For instance to ensure the approved column is automatically set to false should another value (in the case of a Boolean, true is the only other acceptable value), you’ll append , :default => false to the attribute definition. Just for kicks I’ll also assign a default value to the description attribute:

class CreateGames < ActiveRecord::Migration
  def change
    create_table :games do |t|
      t.string :name
      t.string :description, :default => 'No description provided'
      t.boolean :approved, :default => false

      t.timestamps
    end
  end
end

Alternatively, you can choose to manage your default values within the Game model. The after_initialize callback is called whenever a new Active Record object is instantiated (both via new and when a record is retrieved). This is important, because you might be tempted to define the call back like this:

class Game < ActiveRecord::Base

  after_initialize :defaults

  def defaults
    self.description = 'No description provided'
    self.approved = false
  end

end

Because after_initialize is triggered both when a new Game object is instantiated and when a record is retrieved, defining the callback like this will cause description and approved to always be overwritten with the defaults! Instead, you’ll want to define your callback like this:

class Game < ActiveRecord::Base

  after_initialize :defaults

  def defaults
    self.description ||= 'No description provided'
    self.approved = false if self.approved.nil?
  end

end

If you’re not familiar with the ||=, it is Ruby’s conditional assignment operator, and it offers a simple way to assign a value to a variable if the variable is already set to false, nil, or undefined. Because variables of type Boolean could of course be assigned false, you don’t want to use ||= in conjunction with a Boolean and therefore in the above example I employ a different approach for assigning a default value to approved.

Easter Egg: This blog entry is currently the most popular on EasyActiveRecord.com. Did you know default model attribute values are discussed in my new book, “Easy Active Record for Rails Developers” along with many other confusing Active Record-related topics? Use the promotional code popular to receive an extra 5% of any package sold on the homepage.

Whether your decide to set the default value in the table schema or within and after_initialize callback, the default values will automatically be set when a new Game object is created:

>> game = Game.new
=> #<Game id: nil, name: nil, description: "No description provided", approved: false, created_at: nil, updated_at: nil>
>> game.name = 'Space Invaders'
>> g
=> #<Game id: nil, name: "Space Invaders", description: "No description provided", approved: false, created_at: nil, updated_at: nil>
 (0.1ms)  begin transaction
 SQL (5.1ms)  INSERT INTO "games" ("created_at", "name", "updated_at") VALUES (?, ?, ?)  
 [["created_at", Wed, 26 Mar 2014 12:23:16 UTC +00:00], ["name", "Space Invaders"], 
 ["updated_at", Wed, 26 Mar 2014 12:23:16 UTC +00:00]]
 (0.6ms)  commit transaction
=> true

Comments