Gram Model

First Story

As a
    User,
I want to be able to
    create/read/delete/ a post (text only)
so that
    I have my minigram

Review of MVC

MVC - models

We think in terms of Model, View and Controller when design application.

First question is, what is our data? Where do we store?

Model - where we store?

Minigram:

Our grams table will look like this in the database:

title content location

Model

http://guides.rubyonrails.org/active_record_basics.html

Active Record as an Object Relational Mapping (ORM) Framework.

Active Record gives us several mechanisms, the most important being the ability to:

  • Represent models and their data
  • Represent associations between these models
  • Represent inheritance hierarchies through related models
  • Validate models before they get persisted to the database
  • Perform database operations in an object-oriented fashion

Why are Models important?

In building any web app, you will most probably have data and a need to store data.

So, you will definitely need to:

  • Create the database
  • Create the table
  • Set up the Ruby class to interact with the database

Remember this as the first building block of a Rails web app!

$ bin/rails generate model create_gram
      invoke  active_record
      create    db/migrate/20160321030039_create_create_grams.rb
      create    app/models/create_gram.rb

First. what is db/migrate/<timestamp>_create_products.rb?

That is a Rails Migration file (http://guides.rubyonrails.org/migrations.html).

  • See how you can generate a new migration in the Docs.

Migration

Rails Migration codifies changes to database. Allow you to deploy the same changes to databases in multiple environments without having to type SQL.

This is one of the single most important unique selling point of Rails when it came out.

You can add columns to a migration with the syntax attr:type:index

And Rails will also created these attributes automagically:

  • id
  • created_at
  • updated_at

Open the migration file db/migrate/<timestamp>_create_create_grams.rb:

class CreateCreateGrams < ActiveRecord::Migration
  def change
    create_table :create_grams do |t|

      t.timestamps null: false
    end
  end
end

Add our columns:

class CreateCreateGrams < ActiveRecord::Migration
  def change
    create_table :create_grams do |t|
      t.string :title
      t.text   :content
      t.string :location

      t.timestamps null: false
    end
  end
end

Create database first:

$> bin/rake db:create

Let’s go through what rake db:create is.. rake db:create creates the database as specified in config/database.yml (postgres, gem ‘pg’)

This is required, before you can create any tables!

Then migrate the database:

$> bin/rake db:migrate
== 20160321030039 CreateCreateGrams: migrating ================================
-- create_table(:create_grams)
   -> 0.0166s
== 20160321030039 CreateCreateGrams: migrated (0.0167s) =======================

Let’s go through what rake db:migrate is.. rake db:migrate executes all db/migration/<timestamp>.rb files that have not been ran, and updates the database accordingly.

To see the status of migrations in a database, do rake db:migrate:status.

To undo the most recent change to the database, do rake db:rollback.

This is destructive and can delete your data. Use with care.

Model file

# app/models/gram.rb

class Gram < ActiveRecord::Base

end

This is a class. A Gram class. This Gram class’s parent is the ActiveRecord::Base class. So, it has whatever methods that its parent has (inheritance). The methods in the parent allows Gram class to interact with the grams table, based on naming conventions.

rails console

How do we use this Gram class to interact with database?

$> rails console --sandbox

--sandbox option: Rollback database modifications on exit.

>> Gram
>> Gram.connection

>> gram = Gram.new ..
>> gram.save

>> Gram.create ..

>> Gram.update ..

>> Gram.destroy

Creating a Gram from the console

  • Gram.new
  • gram.save
  • Gram.create(...)

Updating a Gram from the console

  • gram.update(...)
  • getter / setters / save
    • gram.title = nil
    • gram.title = "Food"
    • gram.save
    • gram.title # => "Food"

Destroying a Gram from the console

  • gram.destroy

Learn these methods from:

Query the database

>> Gram.all
>> Gram.first; Gram.last
>> Gram.count
>> Gram.find(1)
>> Gram.find_by(title: “Book”)
>> Gram.where(title: “Book”)
>> Gram.order(id: :desc)
>> Gram.limit(2)

Learn these methods from:

SQL versus ORM

Structured Query Language v.s. Object-relational Mapping

>> Gram.all
Gram Load (0.3ms)  SELECT "grams".* FROM "grams"

>> Gram.first
Gram Load (0.7ms)  SELECT  "grams".* FROM "grams"  ORDER BY "grams"."id" ASC LIMIT 1

>> Gram.find(1)
Gram Load (0.2ms)  SELECT  "grams".* FROM "grams" WHERE "grams"."id" = $1 LIMIT 1  [["id", 1]]

ORM

Gram.all
Gram.first
Gram.find(1)

SQL

SELECT "grams".* FROM "grams"
SELECT  "grams".* FROM "grams"  ORDER BY "grams"."id" ASC LIMIT 1
SELECT  "grams".* FROM "grams" WHERE "grams"."id" = $1 LIMIT 1  [["id", 1]]

Validations

http://guides.rubyonrails.org/active_record_validations.html

  • Database Agnostic
  • Unavoidable
  • Convenient to Test
  • Convenient to Maintain

Pros and Cons of:

  • Database constraints and/or Stored Procedures
  • Client Side Validations
  • Controller-level Validations
  • No Validations?!

Trigger Validations

create create! --> save valid? save! invalid? update --> update!

The bang versions (e.g. save!) raise an exception if the record is invalid. The non-bang versions don't, save and update return false, create just returns the object.

validates_presence_of
validates_absence_of
validates_length_of
validates_format_of
validates_inclusion_of
validates_exclusion_of
validates_numericality_of
validates_acceptance_of
validates_associated
validates_confirmation_of
validates_each
validates_uniqueness_of
validates_with

These also come with different style:

http://api.rubyonrails.org/classes/ActiveModel/Validations/HelperMethods.html

Skip Validations

decrement!
decrement_counter
increment!
increment_counter
toggle!
touch
update_all
update_attribute
update_column
update_columns
update_counters
save(validate: false)

http://guides.rubyonrails.org/active_record_validations.html#skipping-validations

Go through:

https://github.com/jollygoodcode/property_expert/blob/lectures/validations/lectures/02-validation.md