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 < ActiveRecord::Base scope :active, where(active: true) scope :inactive, where(active: false) scope :with_orders, joins(:orders).select('distinct()') end # good class User < ActiveRecord::Base scope :active, -> { where(active: true) } scope :inactive, -> { where(active: false) } scope :with_orders, -> { 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.