SoFunction
Updated on 2025-04-07

ActiveRecord Programming Guide in Ruby on Rails


Avoid changing the default ActiveRecord (table name, primary key, etc.) unless you have a very good reason (like a database that is not under your control).
Put macro style methods before category definition (has_many, validates, etc.).

Preference has_many :through better than has_and_belongs_to_many. Use has_many :through to allow additional attributes and verification in the join model

   

 # Use has_and_belongs_to_many  class User < ActiveRecord::Base
   has_and_belongs_to_many :groups
  end

  class Group < ActiveRecord::Base
   has_and_belongs_to_many :users
  end

  # Preference method - using has_many :through  class User < ActiveRecord::Base
   has_many :memberships
   has_many :groups, through: :memberships
  end

  class Membership < ActiveRecord::Base
   belongs_to :user
   belongs_to :group
  end

  class Group < ActiveRecord::Base
   has_many :memberships
   has_many :users, through: :memberships
  end

Use the new one"sexy" validation

Create an idiomatic validator file when an idiomatic validator is used more than once or if the validation is a regular expression map.

  # Difference  class Person
   validates :email, format: { with: /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i }
  end

  # good  class EmailValidator < ActiveModel::EachValidator
   def validate_each(record, attribute, value)
    [attribute] << (options[:message] || 'is not a valid email') unless value =~ /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i
   end
  end

  class Person
   validates :email, email: true
  end

All idiomatic validators should be placed in a shared gem.

Freely use named scopes.

   

 class User < ActiveRecord::Base
   scope :active, -> { where(active: true) }
   scope :inactive, -> { where(active: false) }

   scope :with_orders, -> { joins(:orders).select('distinct()') }
  end

Initialize the named scope in a lambda lazy.

 

  # Poor  class User &lt; ActiveRecord::Base
   scope :active, where(active: true)
   scope :inactive, where(active: false)

   scope :with_orders, joins(:orders).select('distinct()')
  end

  # good  class User &lt; ActiveRecord::Base
   scope :active, -&gt; { where(active: true) }
   scope :inactive, -&gt; { where(active: false) }

   scope :with_orders, -&gt; { joins(:orders).select('distinct()') }
  end

When a scope defined by lambda and parameters becomes too complex, a better way is to create a class method for the same purpose and return an ActiveRecord::Relation object. You can also define a more streamlined scope like this.

  class User < ActiveRecord::Base
   def self.with_orders
    joins(:orders).select('distinct()')
   end
  end

Note the behavior of the update_attribute method. It does not run model validation (unlike update_attributes ) and may mess up the model state.

Use user-friendly URLs. Show descriptive model attributes at the URL, not just id.
There are more than one way to achieve:

Override the to_param method of the model. This is the method Rails uses to build URLs for objects. The default implementation returns the record of the id as a string. It can be overridden by another human-readable attribute.

    class Person
     def to_param
      "#{id} #{name}".parameterize
     end
    end

In order to convert to URL-friendly values, the string should call parameterize. The id of the object should be placed at the beginning so that it can be found for ActiveRecord's find method.
* Use this friendly_id gem. It allows the creation of human-readable URLs through certain descriptive model attributes rather than using ids.

  Ruby
  class Person
  extend FriendlyId
  friendly_id :name, use: :slugged
  end

CheckgemDocumentation Get more information about usage.