Personal notes: Ruby on Rails Tutorial, Chapter 6


## [User model](http://ruby.railstutorial.org/chapters/modeling-users#sec-user_model)

Generate a User model.

~~~ teminal
$ rails generate model User name:string email:string
~~~

Migration for the User model

The above rails generate command creates db/migrate/[timestamp]_create_users.rb
	
~~~ db/migrate/[timestamp]_create_users.rb
class CreateUsers < ActiveRecord::Migration
  def change
    create_table :users do |t|
      t.string :name
      t.string :email

      t.timestamps
    end
  end
end
~~~

Here the table name is plural (users) even though the model name is singular (User), which reflects a linguistic convention followed by Rails: a model represents a single user, whereas a database table consists of many users. 

~~~ terminal
# run the migration
$ bundle exec rake db:migrate

# to migrate down/undo
$ bundle exec rake db:rollback

# Migrate up again
$ bundle exec rake db:migrate
~~~


Running the migration created a file db/development.sqlite3 and model/user.rb.

Using sandbox which will be rolled back on exit.

~~~ terminal
$ rails console --sandbox
~~~

User.new returns an object with all nil attributes.
	
~~~ terminal
>> User.new

>> user = User.new(name: "Michael Hartl", email: "mhartl@example.com")
>> user.save
>> user

# or use create 
>> User.create(name: "A Nother", email: "another@example.org")

>> foo = User.create(name: "Foo", email: "foo@bar.com")

# The inverse of create is destroy:
>> foo.destroy
~~~

## Finding user objects

  
~~~ terminal
>> User.find(1)
>> User.find_by_email("mhartl@example.com")
>> User.find_by(email: "mhartl@example.com")

# The preferred method to find by attribute is 
>> User.find_by(email: "mhartl@example.com")

>> User.first
>> User.all

~~~

## Updating user objects

  
~~~terminal
>> user.email = "mhartl@example.net"
=> "mhartl@example.net"
>> user.save

# We can see what happens without a save by using reload, which reloads the object based on the database information
>> user.email
=> "mhartl@example.net"
>> user.email = "foo@bar.com"
=> "foo@bar.com"
>> user.reload.email
=> "mhartl@example.net"

# Update with update_attributes
>> user.update_attributes(name: "The Dude", email: "dude@abides.org")
=> true
>> user.name
=> "The Dude"
>> user.email
=> "dude@abides.org"

# Update with update_attribute (note singular)
>> user.update_attribute(:name, "The Dude")
~~~

## Validations

    $ bundle exec rake test:prepare

This just ensures that the data model from the development database, db/development.sqlite3, is reflected in the test database, db/test.sqlite3.

respond_to methods accepts a symbol and returns true if the object responds to the given method or attribute and false
  
~~~ spec/models/user_spec.rb
it "should respond to 'name'" do
  expect(@user).to respond_to(:name)
end

# This can be written with one-line style
subject { @user }
it { should respond_to(:name) }

~~~


## Validating the presence of attributes


~~~ app/models/user.rb
class User < ActiveRecord::Base
  # You can use this one 
  # validates :name, presence: true
  # or this one
  validates(:name, presence: true)
end
~~~

Checking it in the console.
  
~~~ terminal
$ rails console --sandbox
>> user = User.new(name: "", email: "mhartl@example.com")
>> user.save
=> false
>> user.valid?
=> false
>> user.errors.full_messages
=> ["Name can't be blank"]
~~~
  
~~~ spec/models/user_spec.rb
it "should be valid" do
  expect(@user).to be_valid
end

# can be written with one-line style
it { should be_valid }
~~~
  
~~~ spec/models/user_spec.rb
require 'spec_helper'

describe User do
  before { @user = User.new(name: "Example User", email: "user@example.com") }

  subject { @user }

  it { should respond_to(:name) }
  it { should respond_to(:email) }

  it { should be_valid }

  describe "when name is not present" do
    before { @user.name = " " }
    it { should_not be_valid }
  end

    describe "when email is not present" do
    before { @user.email = " " }
    it { should_not be_valid }
  end
end

~~~
  
~~~ app/models/user.rb
class User < ActiveRecord::Base
  validates :name,  presence: true
  validates :email, presence: true
end
~~~


## Validating length

~~~ spec/models/user_spec.rb
  describe "when name is too long" do
    before { @user.name = "a" * 51 }
    it { should_not be_valid }
  end
~~~

~~~ app/models/user.rb
class User < ActiveRecord::Base
  validates :name,  presence: true, length: { maximum: 50 }
  validates :email, presence: true
end
~~~

## Validating format

### testing valid and invalid addresses in rails console --sandbox

Iterating over an array.

~~~terminal
>> %w[foo bar baz]
=> ["foo", "bar", "baz"]
>> addresses = %w[user@foo.COM THE_US-ER@foo.bar.org first.last@foo.jp]
=> ["user@foo.COM", "THE_US-ER@foo.bar.org", "first.last@foo.jp"]
>> addresses.each do |address|
>>   puts address
>> end
user@foo.COM
THE_US-ER@foo.bar.org
first.last@foo.jp
~~~

Using this and write basic email format validation tests.

~~~spec/models/user_spec.rb
...
  describe "when email format is invalid" do
    it "should be invalid" do
      addresses = %w[user@foo,com user_at_foo.org example.user@foo.
                     foo@bar_baz.com foo@bar+baz.com]
      addresses.each do |invalid_address|
        @user.email = invalid_address
        expect(@user).not_to be_valid
      end
    end
  end

  describe "when email format is valid" do
    it "should be valid" do
      addresses = %w[user@foo.COM A_US-ER@f.b.org frst.lst@foo.jp a+b@baz.cn]
      addresses.each do |valid_address|
        @user.email = valid_address
        expect(@user).to be_valid
      end
    end
  end
end
~~~

~~~app/models/user.rb
class User < ActiveRecord::Base
  validates :name,  presence: true, length: { maximum: 50 }
  VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
  validates :email, presence: true, format: { with: VALID_EMAIL_REGEX }
end
~~~

[Rubular](http://www.rubular.com/)