Rails Guides

# ActiveRecord::Base is out, ApplicationRecord is in
class Product < ApplicationRecord
end
 
 
# didn't know about this one, or have had any reason to use it
# skips validations!
Model.update_all "deleted = 1"
# looks good (set it to run with every rake db:migrate)
"The annotate_models gem automatically adds and updates comments at the top of
each model summarizing the schema if you desire that functionality."
 
 
# if your have a migration that executes custom SQL sql statements that cannot
# be expressed in schema.rb then switch the schema output to to :sql in
# config/application.rb ( doesn't look like its there when the default is :ruby
# )
# These (among more obscure ones) skip validations so watch out!  These just
# seem dangerous to me, especially since they are so readily available.  They
# also update the DB.  I might write a gem that overrides these with a warning.
# Name it "no_invalid_updates" and rename to update_all_without_validation?,
# update_all_without_further_consideration?  update_all_with_extreme_prejudice
# update_attributes_without_validations?
obj.update_all
obj.update_attribute
obj.update_column
obj.update_columns
 
# Update will updated the DB but run the validations.
obj.update(attributes)
Model.update(id, attributes)
 
# Note that validations are only run when validation is saved() or create(), so
# errors will not be reported.  valid? is another way to run the validations,
# which will fill errors.messages.  In other words, dont look in object.errors
# until you've run either saved, create, or valid?
 
# get errors for specific field with ( but only after validations have been run )
obj.errors[:attribute]
 
irb> Person.new.errors[:name].any?  false
irb> Person.create.errors[:name].any?  true
 
# These should work too but I can't test them now
irb> p.valid?
irb> p.errros[:name].any?  true
 
irb> p.validate # alias of valid?
irb> p.errros[:name].any?  true
 
 
# finally rails 5 stopped being stupid and gives you access to error messges as
# keys and not strings, not sure yet if you can add your own
class Person < ApplicationRecord
  validates :name, presence: true
end
 
>> person = Person.new
>> person.valid?
>> person.errors.details[:name]  [{error: :blank}]
 
# validation: acceptance, if not in database a virutal field will be created for it
validates :terms_of_service, acceptance: true # for checkboxes
 
# if using confirmation validation don't forget the presence validation too
class Person < ApplicationRecord
  validates :email, confirmation: true
  validates :email_confirmation, presence: true
end
 
 
# uniqueness validations are case sensitive by default!  But mysql is case
# insensitive by default!
# email must be case insensitive
# username probably should be case sensitive, or should it?
validates :username, uniqueness: { case_sensitive: true } # <--- default!
validates :email, uniqueness: { case_sensitive: false }
 
 
# A Proc :message value is given two arguments: the object being validated, and
# a hash with :model, :attribute, and :value key-value pairs.   I don't
# understand what :value key-value pairs are.
validates :username, message: ->(object, data) do
  "Sorry #{object.name}, is already taken" 
end
 
# the default ":on" in a validation is upon update and create, but you can
# change that with
validates username, uniqueness: true, on: create # dupes allowed on update
validates username, uniqueness: true, on: update # dupes allow on create
 
# Note: The valid? method will verify that the errors collection is empty
 
# errors add is same as appending an error to messages arrays 
errors.add(:username, 'blah')
errors.messages[:name] << 'blah'
 
 
errors.add(:name, :invalid_characters)
errors.add(:name, "cannot contain the characters !@#%*()_-+=")
 
 
 
# error details
def my_custom_validator
  errors.add(:username, 
end
# the AR lifecycle callbacks can take a block
before_create |x|
  # do something
end
 
# after_save is run both upon create and update.
 
# Use after_initialize if you are tempted to override initialize.
 
# after_find is called whenever AR loads a record from the database, good spot
# for caching?
 
# The callback chain is wrapped in database transation.  If any BEFORE callback
# returns false a rollback happens.  'after' callbacks can only break out of
# transaction by raising an exception.
# Finally migrations support foreign key constraints, and in a nice way.
# But I dont see a cascading delete.
create_table :accounts do |t|
  t.belongs_to :supplier, index: true, unique: true, foreign_key: true
  # ...
end
 
 
# handy way to bust the cache provided by reload
@author = @book.reload.author
# Don't forget take(), seems useful but I've never used it before.  I'd
# describe it as a limited all(), in which case it prefer it was named some(x)
client = Client.take # defaults to 1
clients = Client.take(2)
 
# To avoid big record set memory problems you can use find_each
User.find_each do |user|
  # do something
end
 
# can end a more complicated query also
# NOTE: cannot use order or limit
User.where(weekly_subscriber: true).find_each do |user|
  # ...
end
 
# also returns an enumerator
Person.find_each.with_index do |person, index|
  # ...
end
 
# if you prefer batches (defaults to 1000)
Person.where("age > 21").find_in_batches do |group|
end
 
 
# GREAT: use none() if you want no results that wont break code
if something_true
  return Articles.where(something:x)
else
  return Articles.none
end
 
### scopes work the same as class methods
 
class Article < ApplicationRecord
  scope :published, -> { where(published: true) }
end
 
# not sure how AR automagically chains this
class Article < ApplicationRecord
  def self.published
    where(published: true)
  end
end
 
 
### default scopes can also be class methods
 
class Client < ApplicationRecord
  default_scope { where("removed_at IS NULL") }
end
 
 
class Client < ApplicationRecord
  def self.default_scope
    # Should return an ActiveRecord::Relation.
  end
end
 
### NOTE: The default_scope is also applied while creating/building a record.
# It is not applied while updating a record.
 
 
# turn off scoping with unscope
Client.unscoped.all
 
# unscope can also take a block
Client.unscoped {
  Client.created_before(Time.zone.now)
}
 
 
 
# enums map to integer field
class Book < ApplicationRecord
  enum availability: [:available, :unavailable]
end
 
Book.where(availability: :available)
Book.available
 
 
# looks like exists? is some kinda of bool converter, probably like !!
Client.exists?(1)
Client.exists?(id: [1,2,3]) # works like ||
 
# also any? and many?
# To send display html from a view up to a layout, for example, if your layout
# file has a links margin that your current view needs to insert into
 
# in layouts/application.html.erb
<body>
  <%= yield :links %>
  <%= yield %>
</body>
 
# in a view
<% content_for :links do %>
  <a href='/home'>Home</a>
<% end %>
 
 
# there's a hash local_assigns that contains locals passed into template and
# its suggested it is used for determining if a local is set
 
<% if local_assigns[:full] %>
<%= simple_format article.body %>
<% else %>
<%= truncate article.body %>
<% end %>
 
# but i'm not sure why you can't just used defined?
<% if defined? full %>
<%= simple_format article.body %>
<% else %>
<%= truncate article.body %>
<% end %>
 
# render and "render partial" are slightly different.  render takes arguments
# but isn't smart enough to process things like locals:{} and object:.
<:% render object:@user, locals:{var1:1} %> # doesnt work
<:% render partial: object:@user, locals:{var1:1} %> # works
 
# why ever just use render?  Not sure, but this shorthand seems popular.  It
# will run the partial _customer.html.erb and populate the variable customer
# with @customer
<% render @customer %>
 
# render a collection, partial is run once for every element, and assigned each
# element to variable with name of partial, 'one' in this case
<%= render partial: 'shared/one', collection:[1,2,3] %>