2012-02-07_08.32.40
He went over SQL in Ruby/Rails
Team has many players, Player belongs to Team
User story:
As a user, I want to see a list of products I can buy
A product has a name, color, and price. A product belongs to a single brand.
Prices are always in exact dollar amounts (no cents).
As a user, I want to add a review to a product
A review has a rating from 1-5 and a brief description
new rails app
products and brands
products belong to brand, brand has many products
Generate scaffold for brand and product
go back to index after create
Go over the scaffold
To get the brand in the collection select
Go to the form partial for the product
Only change the number field, not the label field
brand_id is the foreign key
<%= f.collection_select :brand_id, Brand.all, :id, :name %>
:id is param for Brand.all, :name is the field in the brand. That is what will be displayed
You may not want to always do Brand.all. We will go over that later.
View should not decide what data to show.
Put a variable in the new method in the controller
Now listing the products, we still get the brand ID
Make the description for a form a text field, not string
f.collection_select :product_id, Product.all, :id, :name
For rating, we want a list of to 5
f.select :rating, [1,2,3,4,5]
You should still add a validates method in the model
What if you want 1 to 100?
There is a way to do a range
:rating, (1..100).to_a
to_a converts it to an array
Make the description box smaller. Go to the form partial
:rows => 5
Gems:
You can have more than one version
gem install rails
You may see:
sudo gem install rails
That will install it with admin rights
With rvm: never use sudo
It will install latest by default
gem will handle dependencies
railties is the guts/engines of rails
You can call
gem sources
to see where it will look for gems
Vince’s app:
We could edit the Gemfile
gem 'gmaps4rails'
Then run
bundle install
Back to Jeff
How to get words to appear on pull-down
Numbers:
f.select:rating, (1..5).to_a
Or
f.select:rating, [1,2,3,4,5]
or
f.select :rating, {"Excellent" => 5, "Very Good" => 4, "Okay" => 3, "Not so good" => 2, "Bad" => 1}
To validate that the rating will be between 1 and 5
Jeff starts with the guides
Jeff used
validates :rating, :numericality => { :only_integer => true } validates :rating, :numericality => { :only_integer => true, :greater_than_or_equal_to => 1, :less_than_or_equal_to => 5 }
Test in the Rails console
2012-02-09_08.25.05
To support 1 URL: pages/social
One way:
rails g controller pages social
rails new friendBC
cd friendBC
-> Memorize the routes for resources
in routes.rb
get “pages/social”
in app/controllers add a controller: PagesController < ApplicationController
add a method called “social”
add app/views/pages/social.html.erb
get an image, put it in
<% image_tag "logo.png" %>
in assets/global directory
Make the background grey
app/assets/stylesheets/application.css
How to get JSON data?
He put some stuff in his controller
require 'open-uri' and require 'json' result = JSON.parse(open("Some URL").read)
Put result in view file
In hashes, symbols and strings are not interchangeable. Usually they are.
To get the image:
in the view:
<%= image_tag @first_result["video"]["thumbnailUrl"] => <img src="@first_result["video"]["thumbnailUrl"]">
Now he is calling @result @channel
—
Now Jeff is going through it
Agile practices: single responsibility, clear intention, DRY
These are the top 3
Rails cries out to help you do those things
When you download someone else’s code, you may need to run “bundle install”
Jeff moved some of the HTML onto a partial
Then change the variable in partial to one called “channel”
Then in the view call
<% render 'ribbon', :channel => @Whatever %>
In the partial, do not use raw “a” and “img” tags
<% link_to "Video goes here", "http://www.youtube.com#{post["video]["hostId"]}" %>
Now add the image:
<% link_to image_tag(post["video]["thunmbnailUrl"], :size => "240x180"), "http://www.youtube.com#{post["video]["hostId"]}", :target => :blank %>
Now back in view, we call the render three times. What if we pull another channel, we will have to add another line.
So in controller, create an array
@channels = [@facebook, @twitter, @youtube]
In the view:
<% @channels.each do |channel_data| %> <%= render 'ribbon', :channel => channel_data %> <% end %>
Now they do not need to be instance variables in the controller
We still have repetition
def get_json_for_source(source) return JSON.parse((open("https://")).read) end
Then in other method:
facebook = get_json_for_source('FACEBOOK')
This is Ruby skillz
A good pattern to know:
An array of strings. You are transforming the array into another array.
@channels = ['a', 'b', 'c'].map do |source| get_json_for_source(source) end
map is like each
Look it up
collect is another good one to look up.
@names = users.collect dp |user| user.name end
Could be
@names = users.collect {|user| user.name}
We saw one-line blocks in scaffolds in index
respond_to do |format| format.json {render json: @brands} end format.json do render json: @brands end
Look in the dev/tth/shop folder
—————————————————–
Now: git and github
Look up “git – the simple guide”
git init
makes a new repository
git clone /path/to
to checkout a repository
Workflow:
working dir has files, index to stage commits, the head is the last commit
adding and committing
git add <filename> git add * git commit -m "message"
Now it is in the head, but not remote
git push origin master
afterwards, you can just do git push
To add a branch
git remote add origin <server> git checkout -b feature_x git pull to update - that pulls down latest changes
to reset:
git fetch origin; git reset --hard origin/master
gitk – is a good GUI
Neal making live changes
git status
He is on master branch
git branch
git status now tells him he made a change
git add . git commit -am "updated the Readme" git push origin master
or just
git push
There is also heroku
git push heroku master
or
git push heroku
Heroku requires postgres
Neal makes a new app
rails new ca_boat_party
It would be nice to stop an existing rails server
You could also specify a port
rails s -p 4000
It is now live on our local machine
git init
go to github.com
make a new repository
ca_boat_party - same name as directory git add . git commit -am ""
No good
git remote add origin git@github.com:nealg223/ca_boat_party git push -u origin master
Now put it up to heroku
gem install heroku
ssh keys are a pain
heroku create ca-boat-party --stack cedar
They have different stacks. bamboo is the current stack, good for rails up to 3.0. For rails 3.1 use cedar
Now push it to heroku
Update the gem file Gemfile to add postgres
group :production do gem 'pg' end
Put sqlite3 in group :development
bundle install
now commit changes to git
git add git commit -am "changed Gemfile" git push
Now push to heroku
git push heroku master
another command: heroku open
assets, like images, there could be some problems
another command is heroku logs
or
heroku run console
it’s like an irb for heroku
heroku run rake db:migrate
in file app/config/environments/production.rb
set config.assets.compile = true
You can go to gitref.org
————————————————————–
Working with Shop app:
Get the average of reviews for a product
p = Product.find(1) p.reviews
Calling product.reviews: It gets rows from our table. It looks like an array, and behave like an array, but it can do more than what an array can do. It returns a proxy class. It is a proxy for the data you want.
So you can say p.reviews.count – it makes an SQL statement
You can do a lot of array stuff: p.reviews.first, p.reviews.sum(“rating”)
Arrays do not have sum method, but ActiveRecord does have sum method
p.reviews.average(“rating”) it gives us a BigDecimal instance
p.reviews.average(“rating”).to_s
Another association concept
A brand has many products.
A product has many reviews.
A brand has many products throught its products.
class Brand < ActiveRecord::Base has_many :products has_many :reviews, :through => :products end
The lines must be in that order
Review does not have brand id, but product has brand id, and review has product ID
So you can just do Brand.reviews without looping
Users want to see average rating for brand
So in brand show page – put it there for each brand
So to brand index page:
<%= brand.name %>
Rating:
<%= brand.reviews.average(:rating) %>
For reviewing the product form, it would be great if it knew which product you were using
No params hash for /reviews/new
Put a placeholder in the route
Put it in URL via ?key=value
Now you have a params hash
In product/show.html.erb
<%= link_to "Review This Product", new_review_url(:product_id => @product.id) %>
So now you see ?product_id=1 in url for new form
so in controller, look at new action in review controller
def new @review = Review.new @review.product_id = params[:product_id] # or @review.product = Product.find(params[:product_id]) respond_to end
so in reviews/new.html.erb
New review for
<%= @review.product.name %>
Next: Leave out the pull-down form. But get product ID
replace collection_select with a hidden field
Image from Wikimedia, assumed allowed under Fair Use. Image from the Vatican Virgil, a 5th century manuscript of poems by Virgil.