Notes on Personal Notes on Learning Rails 3

Notes from Bootstrap and custom CSS.

Add a gem to the Gemfile.

gem 'bootstrap-sass', '2.3.2.0'

Install it. And restart the server.

$ bundle install

Add a line, config.assets.precompile += %w(*.png *.jpg *.jpeg *.gif) to config/application.rb

require File.expand_path('../boot', __FILE__)
.
.
.
module SampleApp
  class Application < Rails::Application
    .
    .
    .
    config.assets.precompile += %w(*.png *.jpg *.jpeg *.gif)
  end
end

Any stylesheets in app/assets/stylesheets will be included as part of the application.css.

Adding Bootstrap CSS.

app/assets/stylesheets/custom.css.scss

@import "bootstrap";

Partials

<%= render 'layouts/shim' %>

This will insert app/views/layouts/_shim.html.erb. The underscore is the universal convention for naming partials.

Asset pipeline

Asset directories

app/assets: assets specific to the present application lib/assets: assets for libraries written by your dev team vendor/assets: assets from third-party vendors

Manifest file

app/assets/stylesheets/application.css

*= require_tree .

Ensures that all CSS files in the app/assets/stylesheets directory (including the tree subdirectories) are included into the application CSS.

*= require_self

application.css itself gets included.

RailsGuides asset pipeline

The Bootstrap page of LESS variables

LESS uses an “at” sign @, Sass uses a dollar sign $. $grayLight.

Layout links

Commenting out.

<%#= link_to "Contact", '#' %>

Rails routes

# change from
get 'static_pages/help'
# to
match '/help', to: 'static_pages#help', via: 'get'

match ’/about’ also automatically creates named routes for use in the controllers and views:

about_path -> '/about'
about_url  -> 'http://localhost:3000/about'

Use the url form for redirects since the HTTP standard technically requires a full URL, although in most browsers it will work either way.

For the root route.

# You can have the root of your site routed with "root"
root 'welcome#index'
# for this sample_app
root  'static_pages#home'

# this will give us
root_path -> '/'
root_url  -> 'http://localhost:3000/'
# run all the request specs by passing the whole directory 
$ bundle exec rspec spec/requests/

# to run all the specs:
$ bundle exec rspec spec/
# or
$ bundle exec rake spec

User model

Generate a User model.

$ rails generate model User name:string email:string

Migration for the User model

The above rails generate command creates db/migrate/[timestamp]createusers.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.

# 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.

$ rails console --sandbox

User.new returns an object with all nil attributes.

>> 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

>> 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

>> 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

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.

$ 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"]

In 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
Written on September 17, 2013