2012-02-28_08.18.14
undefined method `model name` for NilClass:Class
Means there is a problem with the model
So instantiate it in the controller from the session
How to override application.html.erb for a controller or action
For orders controller, app/views/layouts/order.html.erb
To specify layout:
in controller:
layout 'application', :only => :index
or in def new:
def new @cart = Cart.find(session[:cart_id]) @order = Order.new render :layout => 'orders' end
we need to encrypt the credit card number. Look in config/application.rb
We will look at
config.filter_parameters +=[:password]
This will filter password out of the log
So we can add to the array
config.filter_parameters +=[:password, :card_number]
asset pipeline: We will talk about it throughout the week. Look in the assets folder. Images, javascript and stylesheets.
It takes all the javascript and puts it in one file, so the browser only has to make one request
Same for css
For css, you should go to application.css and specify files and/or directories
*= require_self *= require_tree
require_tree will do everything aplhabetically. You can put them in your own order.
*= require brands *= require products *= require reviews *= require scaffolds *= require users
In development mode, there is no asset pipeline. You use it in production.
In config/environments/development.rb
config.assets.debug=false
if you set that to false, you won’t see the GET requests in the logs for EVERY js and css file
Jeff can get to other order pages as well as his own
We have authentication. What about authorization?
In orders controller:
def show @order = @user.orders.find(params[:id]) end
WHat if that ID is not in the order table?
def show @order = @user.orders.find(params[:id]) if order.nil? redirect_to root_url, :notice => "Nice try" end end
You could do this:
redirect_to root_url, :notice => "Nice try" if order.nil?
But then it looks like redirect is the default.
Or
redirect_to root_url, :notice => "Nice try" unless order
Moving this to a one-liner could make it a but confusing
You could also do
if order.blank? redirect_to root_url, :notice => "Nice try" end
or
unless @order redirect_to root_url, :notice => "Nice try" end
Stay at same level of abstraction throughout the html.erb files
if you have render tags with low-level html, that could look bad
Class method that makes an ActiveRecord call
Use the scope facility
Instead of:
def self.most_recent order("updated_at desc").limit(3) end
scope :most_recent, order("updated_at desc").limit(3)
scope creates a class-level method, and returns rows
You cannot just return one row
Maybe we want to pass a param from the view to the class-level method or scope
Easy for method.
For a scope:
create a lambda: create an unnamed method on-the-fly scope :most_recent, lambda { |n| order("updated at desc").limit(n) }
Orders Controller:
We have a before_filter
We have model callbacks in cart_item.rb
Those are sort of like lambdas as well.
We do not call it, Rails calls it for us at some point.
Let’s do a scope on price for products:
scope :expensive, where('price > 50')
Then in the view you could chain the scopes
Lambda with a default value for min:
scope :expensive, lambda { |min=80| where('price > #{min}') }
Getting the data you want:
.find .find_by_* .where(hash) .where("SQL") .where("SQL", ....) .where("color = BLUE''") # sql syntax - single = sign is comparison .where("color like 'BLUE'")
You can also use % for wildcards
.where("name like 'M%'") .where("name like ?", "M%")
In Products Controller:
@products = Product.where("name like '#{params[:search]}'")
or
@products = Product.where("name like '#{params[:search]}%'")
So why not do this? Jeff will explain
You could get SQL injection
So do this:
a = params[:search] @products = Product.where("name like '%#a%'")
Use the question mark placeholder
@products = Product.where("name like ?", params[:search])
ActiveRecord will create the SQL for you
Fuzzy search:
@products = Product.where("name like ?", "%#{params[:search]}%")
Then in the controller you could chain the methods
@products = @products.limit(1000).order('name asc')
It is not until you get to the @products.each (or a .all) in the view that you run the query
Should you use the products controller for searching, or make a search controller?
the form_tag goes to ‘/products’ with a get, which will go to the index action
—-
New app:
User Story Analysis
1. Identify resources
2. Create Models
3. Implement business rules
4. Create User Interface
Some people go in the opposite order
Air Academy User story
1. As a visitor, list the flights
In Rails 3.2, when you generate model, if you do not specify the types it will assume that it is a string
rails g model Airport code:string city:string
ericm@finance:~/ruby/ca_files/air$ more db/migrate/20120228174012_create_airports.rb
class CreateAirports < ActiveRecord::Migration def change create_table :airports do |t| t.string :code t.string :city t.timestamps end add_index :airports, :code end end
rails g model Airport code:string city:string
emacsnw db/migrate/20120228174012_create_airports.rb
For flights, we need two airports, not just one departure_airport_id, arrival_airport_id
class Flight belongs_to :departure_airport, :class_name => 'Airport' belongs_to :arrivale_airport, :class_name => 'Airport' end
If you say belongs_to :blah, the table needs a column blah_id
If that is the only belongs_to, Rails will assume there is a Blah model
rails g model Flight departure_airport_id:integer, arrival_airport_id:integer number:string distance:integer duration:integer departs_at:time
2012-03-01_08.38.49
Pagination:
Kaminari, Will_paginate, roll your own
User story: As a user, I want to make a reservation
As opposed to doing something “as a visitor”
Different permissions, etc
So for “As a user” that user story needs an account
User story analysis
1. Identify resources
2. Create models
3. Implement business rules
4. Create user interface
reservation model has user, a flight, chosen departure date, credit card number
Associations:
user can have many reservations
flight has many reservations
reservation belongs to flight and belongs to user
rails g scaffold Reservation user_id:integer flight_id:integer credit_card_number:string departs_on:date
departs_on for dates, departs_at for times
How to decide to model or scaffold? How much of scaffold will you use?
add_index on the reservation table
add_index is outside the create_table block
add_index :reservations, :user_id add_index :reservations, :flight_id
Good to have indexes for foreign keys
Then run
rake db:migrate
rake db:migrate:reset will drop database and recreate tables
rake db:seed
You could do
rake db:migrate:reset db:seed
rails g model User first last email passwod_digest
——————
Now JC from DevMynd is talking
database: structured set of data on a computer, accessible in various ways
five categories: relational, graph, key-value, document, column family databases
Relational database: built around relational theory (first-order predicate calculus), schema defined in tables of columns and rows, data is related through matching keys
Graph: based on graph theory. These DBs use nodes, properties and edges to describe a “web” of data. They can be powerful for complex ancestral queries (Amazon: People who bought X bought Y)
Key-value: primary way of retrieving objects is by a single key. What can be stored as a value varies.
Redis is used for queing systems
Document database: storing document-oriented, semi-structured data. Normally within a loose schema and the ability to store/retrieve nested structures
Column-family database: inverse of relational model. Big table.
Choosing data store can be pretty complex.
Considerations: indexing, querying, scaling, modeling, mapping, analyzing, recovering
Indexes: a parallel description of your data optimized for fast lookup
If you will ever do an order_by or a conditional query
Finding by department and last name is different than index that finds by last name and first name
Querying: getting info from db. Do we use a template object, a query language, or a string key?
Mongo can use query syntax, or you can pass in a template doc
scaling: scale up (faster, bigger hardware) or out (distributing data, indices and queries across more machines, aggregating the results)
mongo can shard well, so can redis
modeling: is your schema fixed or flexible? a few large things, lots of small things
Mapping (ORM) – ActiveRecord for RDBMS, Mongoid for MongoDB, Redis ruby driver
how will you interact with db?
analysis: what does the database provide?
recovery: relational: replication, push log to another system
Mongo: backup and restore tools
Heroku, other cloud backups are not that great
optimization: this is usually the first bottleneck
universal: careful indexing, duplication
relational: de-normalization, pivoting, materlialized views
normalized: each concept in a different table, less repetition
pivot: making rows into columns, and vice versa
materialized views:
non-relational: nest relationships, parallel queries
downside of indexing: it can take a LOT of space, plus index might be held in memory instead of disk
Put query logic in your model, not your controller
So if you change the datastore, you can just change it in the model class
relational tables good for complex joins
Think carefully about uniqueness and nullability early on
——————
Back to Jeff
For flight page, give some info about flight
the user is part of the session
reservation for
So in reservation controller, in new method: @user = User.find(session[:user_id])
You could do it from show page for a flight
@user = User.find()
Get stuff from flight show page
Reuse the flight_details partial
But we could get a Nil
So in the reservations controller
We will do something in def new
We could have a link from flight show page
@flight.id) %>
So in new,
@flight = Flight.find(params[:flight_id])
So we want to put that link in an if on the flight show page
Put a filter on the reservations new controller
before_filter :require_login, :only => :new def require_login if session[:user_id].blank? redirect_to root_url, notice: "Get your act together" end end
@flight.id) %>
In reservation form:
in reservation controller.new:
@reservation.flight = @flight
validation on reservations:
create method in reservationcontroller:
def create @reservation = Reservation.new() @reservation.user = User.find(session[:user_id]) end
Sending email in Rails – it could depend on your host
rails g mailer ReservationMailer rails g mailer ReservationMailer
In our reservation controller, we will use this in respond_to block
email = ReservationMailer.confirmation(@reservation) email.deliver
in ReservationMailer
def confirmation(reservation) @reservation = reservation mail(:subject => "Thanks for your reservation", :from => "reservations@academyair.com", :to => reservation.user.email ) end
So in app/views/reservation_mailer/confirmation.text.erb
Hello, ,
Thanks for sending us $300.
Flight details
———————
Flight number:
Image from World Digital Library, assumed allowed under Fair Use. Image from the Ashburnham Pentateuch, or Tours Pentateuch, a Latin manuscript of the first five books of the Old Testament from the 6th century or 7th century. Its place of origin is unknown.