dnestoff
9/9/2016 - 1:38 AM

Rails: Active Record

Includes:

  • How to set up and change tables in active record
  • Active Record basics
[RailsGuides: Active Record Migrations](http://edgeguides.rubyonrails.org/active_record_migrations.html)

## Creating Tables

class CreateDogs < ActiveRecord::Migration
  def change
    create_table :dogs do |t|
      t.string   :name, { null: false, limit: 50 }
      t.string   :license, { null: false }
      t.integer  :age
      t.integer  :weight
      t.integer  :owner_id

      t.timestamps(null: false)
    end
  end
end

# Note: Remove any spaces between timestamps column and constraints being passed.

## UPDATING TABLES

### Removing columns with `#remove_column`
# Supply this method with the table name and column name on the same line.

class RemoveWeightFromDogs < ActiveRecord::Migration
  def change
    remove_column :dogs, :weight
  end
end

### Add columns with the `#add_column`
#The method takes table name, column name, and data type (in that order).

class AddBreedToDogs < ActiveRecord::Migration
  def change
    add_column :dogs, :breed, :string
  end
end

  #with default
class AddBreedToDogs < ActiveRecord::Migration
  def change
    add_column :dogs, :breed, :string, null: false, default: "Title"
  end
end

### Change column name

# Use the `#rename_column` method followed by table name, old column name, and then new column name.

class RenameRaterIdToJudgeIdOnRatings < ActiveRecord::Migration
  def change
    rename_column :ratings, :rater_id, :judge_id
  end
end
# https://www.sitepoint.com/optimizing-mysql-application/

# For each entry in the index, MySQL also internally maintains a “pointer” to the correct row 
# in the actual data file. 
# So if I want to get the value of peopleid when the name is Mike (SELECT peopleid FROM people WHERE name='Mike';), 
# MySQL can look in the name index for Mike, jump directly to the correct row in the data file, 
# and return the correct value of peopleid (999). MySQL only has to look at one row to get the result. 
# Without an index on “name”, MySQL would’ve scanned all 1000 rows in the data file! 
# In general, the less rows MySQL has to evaluate, the quicker it can do its job.
# Active Record Cheat Sheet
Cheat sheet for the syntax and concepts of Active Record

## INSERTING DATA

# We can use the `#create` method to instantiate and save a record to the database at the same time. This is the equivalent of using a `#new` and `#save` method together.

Dog.create(name: "Marty")

Dog.create [{name: "Toot"}, {name: "Cosmo"}]

# In the figure above, we also have a way to use the `#create` method for creating multiple objects at the same time AND saving them to the database. Pass the method an array with hashes for each instance we want to create. We can also use `#find_or_initialize_by` to look for an instance, and create it if doesn't exist. However, we need to run `#save` on the instance, because it's not automatically saved to the database.

Dog.find_or_initialize_by(license: "OH-9384764")

Dog.find_or_create_by(name: "Taj", license: "OH-0984736")

# `#find_or_create_by` instantiates and saves a new record if one does not exist.

## RETRIEVING DATA

# When passing arguments into `#where` class function, use a colon (:) instead of an equals (=).

Dog.where(age: 1)

# We can pass a SQL argument into double quotes after a `'WHERE'` statement to use SQL language.

Dog.where("age = ? and name like ?", 1, '%Te%')

### Other methods for manipulating the records being returned

Dog.order(column_name: :desc)

Dog.limit(2)
Dog.first
Dog.find(3)
Dog.find_by(name: "Jayda")

Dog.pluck(:name, :age)
[["Tenley", 1], ["Eleanor", 1], ["Jayda", 3]]

#Along with `#where`, `#pluck` and `#order` return collections of objects. Other methods like `#find`, `#first`, and `#find_by` return a single objects.

#Note: Use `#find` when primary key is known, and `#find_by` for finding with other column values (`#find_by` returns only the first value found).

#If desired, we can pass an array of id's to `#find` method, which will return us a collection of objects as well.

Dog.find [1,3]

### Methods that run aggregate functions

Dog.count
# => 3

## Updating Data

#With an Active Record model, getter and setter methods are provided automatically. There's no need to create them ourselves. For every column in the database, Active Record provides getter and setter instance methods. Important: When we use our setter methods, this data is NOT saved to the database.

jayda = Dog.find_by(name: "Jayda")
jayda.age
# => 3
jayda.age = 4
# => 4
jayda.age
# => 4

Dog.find_by(name: "Jayda").age
# => 3

# If we want changes to persist, we need to call our `#save` method.

### Updating multiple records at a time

tenley = Dog.find_by(name: "Tenley")
tenley.age
# => 1
tenley.license
# => "OH-9384764"

tenley.assign_attributes(age: 3, license: "OH-1234567")

tenley.age
# => 3
tenley.license
# => "OH-1234567"

# Both `#assign_attributes` and `#update_attributes` change multiple attributes at a time. Of the two methods, only `#update_attributes` persists.

## Removing Data

rabid_dog = Dog.create(name: "Old Yeller", age: 5, breed: "Black Mouth Cur")
rabid_dog.destroy

# We can remove an object's data from the database with the `#destroy` method (see above). 
# When we call #destroy Active Record generates and executes a DELETE SQL query like DELETE FROM "dogs" WHERE "dogs"."id" = ? [["id", 4]].

## Common Active Record Console Errors

irb(main):003:0> Dog.new(color:"brown")

ActiveRecord::UnknownAttributeError: unknown attribute 'color' for Dog.

#### When a column we're trying to add a value to doesn't exist


## Resources

[RailsGuides: Active Record Query Interface](http://guides.rubyonrails.org/active_record_querying.html#pure-string-conditions)
# http://guides.rubyonrails.org/active_record_validations.html

# exists
validates :title, :presence => true

# validates that two fields are unique to each other
  validates :name, uniqueness: { scope: :year,
    message: "should happen once per year" }
    
#custom validation with error 
  validate :patron_email_nonexistent

  def patron_email_nonexistent
    if Patron.find_by(email: self.email)
      errors.add :email, "is taken"
    end
  end
#short-form
resources :articles do
  resources :comments, except: [:show, :index]
end


# long-form nesting of resources
resources :articles do
  member do
    resources :comments except: [:show, :index]
  end
end