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