This is the last of 8 presentations given at University of Texas during my Beginner to Builder Rails 3 Class. For more info and the whole series including video presentations at my blog:
http://schneems.com/tagged/Rails-3-beginner-to-builder-2011
3. Dealing with Nil
user = User.where(:username => "schneems").first
#<User ... >
user.image_url
>> "/schneems.png"
user = nil
user.image_url
# NameError: undefined local variable or method `user'
for #<Object:0x1001dc288>
@Schneems
Monday, August 8, 11
4. try to deal
# use try from ActiveSupport
user = User.where(:username => "schneems").first
#<User ... >
user.try(:image_url) || "/not_found.png"
>> "/schneems.png"
user = nil
user.try(:image_url) || "/not_found.png"
>> "/not_found.png"
@Schneems
Monday, August 8, 11
5. || & ||=
per_page = params[:per_page]
per_page = 10 if per_page.blank?
# instead
per_page = params[:per_page]||10
# or
per_page = params[:per_page]
per_page ||= 10
@Schneems
Monday, August 8, 11
6. ‘if’ returns result
# if returns to where it was called
result = if true
"foo"
else
"bar"
end
puts result
>> "foo"
result = if false
"foo"
else
"bar"
end
puts result
>> "bar"
@Schneems
Monday, August 8, 11
7. Acceptance Testing
• Run your full application stack
• Behavior Driven Development (BDD)
• High payout for little time testing
• slow
• potentially brittle
@Schneems
Monday, August 8, 11
8. Capybara & Rspec
• Capybara
• Headless browser
• open your webpages
• click links ...
Gemfile
gem 'capybara', '0.3.7'
gem 'rspec-rails', '1.3.2'
@Schneems
Monday, August 8, 11
9. Capybara & Rspec
# go to a path
visit root_path
visit "/"
# click buttons
click "sign in"
# fill out forms
fill_in ‘Email’, :with => "schneems@example.com"
@Schneems
Monday, August 8, 11
10. Capybara & Rspec
http://codingfrontier.com/integration-testing-setup-with-rspec-and-capy
/spec/acceptance/signin_spec.rb
context "as a guest on the sign in page" do
# Generate a valid user
let(:user) { Factory(:user) }
context ‘with valid credentials’ do
#Fill in the form with the user’s credentials and submit it.
before do
visit root_path
click ‘Sign In’
fill_in ‘Email’, :with => user.email
fill_in ‘Password’, :with => ‘password’
click ‘Submit’
end
it "has a sign out link" do
page.should have_xpath(‘//a’, :text => ‘Sign Out’)
end
it "knows who I am" do
page.should have_content(“Welcome, #{user.email}!”)
end
end
end
@Schneems
Monday, August 8, 11
11. DRY Views
• Helpers
• Small resusable components
• content_for
• Control page flow of compnents out of
normal
placement
• Partials
• Large reusable components
• Cells
• type logic is needed when controller
Reusable components
@Schneems
Monday, August 8, 11
12. ViewHelpers
• Rails has build in helpers
• link_to
• image_tag
• etc.
• Write your Own
@Schneems
Monday, August 8, 11
17. Partials
• Reusable chunks of view code
<%# renders 'spots.html.erb' with @spot %>
<%= render :partial => "spot", :object => @spot %>
<%= render "spot", :object => @spot %>
<%= render @spot %>
<%# renders 'spots.html.erb' for each spot in @spots %>
<%= render @spots %>
<%= render "spot", :collection => @spots %>
@Schneems
Monday, August 8, 11
18. Partials
<%# renders 'spots.html.erb' for each spot in @spots %>
<%= render @spots %>
/spots/_spots.html.erb
<div>
<h3>Name:</h3>
<p><%= spot.name %></p>
<p><%= spot.description %></p>
</div>
@Schneems
Monday, August 8, 11
19. Partials
• Locals
<%# renders 'spots.html.erb' for each spot in @spots %>
<%= render @spots, :locals => {:foo => ”bar”} %>
/spots/_spots.html.erb
<div>
<h3>Name:</h3>
<p><%= spot.name %></p>
<p><%= spot.description %></p>
<p><%= foo %></p>
</div>
@Schneems
Monday, August 8, 11
20. Partials
• Optional Locals
<%# renders 'spot.html.erb' for each spot in @spots %>
<%= render @spot2 %>
/spots/_spot.html.erb
<div>
<h3>Name:</h3>
<p><%= spot.name %></p>
<p><%= spot.description %></p>
<p><%= local_assigns[:foo]||'something else' %></p>
</div>
@Schneems
Monday, August 8, 11
21. Cells
• View Components
• Look and Feel like Controllers
gem install cells
@Schneems
Monday, August 8, 11
22. Cells
/app/views/product/index.html
<div id="header">
<%= render_cell :cart, :display, :user => @current_user %>
/app/cells/cart_cell.rb
class CartCell < Cell::Rails
def display(options)
user = args[:user]
@items = user.items_in_cart
render # renders display.html.haml
end
end
/app/cells/cart.display.html.erb
<p>You have <%= @items.size %></p>
@Schneems
Monday, August 8, 11
23. DRY Views
• Helpers
• Small resusable components
• content_for
• Control page flow of compnents out of
normal
placement
• Partials
• Large reusable components
• Cells
• type logic is needed when controller
Reusable components
@Schneems
Monday, August 8, 11
24. Caching
• Store data in alater so it can be
served quicker
cache
@Schneems
Monday, August 8, 11
25. Page Caching
• Super Fast
• Limited use cases
class ProductsController < ActionController
caches_page :index
def index
@products = Products.all
end
end
http://guides.rubyonrails.org/caching_with_rails.html
@Schneems
Monday, August 8, 11
26. Page Caching
class ProductsController < ActionController
caches_page :index
def index
@products = Products.all
end
def create
expire_page :action => :index
end
end
http://guides.rubyonrails.org/caching_with_rails.html
@Schneems
Monday, August 8, 11
27. Action Caching
• Allows Authentication checks
class ProductsController < ActionController
before_filter :authenticate
caches_action :index
def index
@products = Product.all
end
def create
expire_action :action => :index
end
end
@Schneems
Monday, August 8, 11
28. Action Caching
• :layout => false
• allows dynamic currentthe layout to
render such as
info in
user
• :if & :unless
•
caches_action :index, :layout => false
caches_action :index, :if => lambda {current_user.present?}
caches_action :index, :unless => lambda {current_user.blank?}
@Schneems
Monday, August 8, 11
29. Fragment Caching
• Cache individual parts of pages
<% cache('all_available_products') do %>
All available products:
<% end %>
@Schneems
Monday, August 8, 11
30. Fragment Caching
• Cache individual parts of pages
<% cache(:action => 'recent',
:action_suffix => 'all_products') do %>
All available products:
...
<%- end -%>
@Schneems
Monday, August 8, 11
31. Fragment Caching
• Expire fragments
expire_fragment(:controller => 'products',
:action => 'recent',
:action_suffix => 'all_products')
@Schneems
Monday, August 8, 11
32. Sweeping Cache
class ProductsController < ActionController
before_filter :authenticate
caches_action :index
cache_sweeper :product_sweeper
def index
@products = Product.all
end
end
@Schneems
Monday, August 8, 11
33. Sweeping Cache
class ProductSweeper < ActionController::Caching::Sweeper
observe Product # This sweeper is going to keep an eye on the Product model
# If our sweeper detects that a Product was created call this
def after_create(product)
expire_cache_for(product)
end
private
def expire_cache_for(product)
# Expire the index page now that we added a new product
expire_page(:controller => 'products', :action => 'index')
expire_fragment('all_available_products')
end
end
@Schneems
Monday, August 8, 11
34. Cache Stores
• Set the Cache store in your
environment
ActionController::Base.cache_store = :memory_store
ActionController::Base.cache_store = :drb_store,
"druby://localhost:9192"
ActionController::Base.cache_store = :mem_cache_store,
"localhost"
@Schneems
Monday, August 8, 11
41. Memcache
• Cache frequent queries
class User < ActiveRecord::Base
def self.fetch(username)
Rails.cache.fetch("user:#{username}") do
User.where(:username => "#{username}").first
end
end
end
=> User.fetch("schneems") #=> #<User id: 263595 ... >
@Schneems
Monday, August 8, 11
42. Memcache
• Cache expensive queries
class User < ActiveRecord::Base
def fetch_friends
Rails.cache.fetch("users:friends:#{self.id}") do
friends = Friendships.where(:user_1_id => self.id)
end
end
end
=> User.fetch("schneems").fetch_friend_ids
@Schneems
Monday, August 8, 11
43. Memcache
• Blazing Fast
# Database
Benchmark.measure do
User.where(:username => "schneems").first
end.real
=> 0.09 s
# Memcache
Benchmark.measure do
User.fetch("schneems")
end.real
=> 0.0009 s
@Schneems
Monday, August 8, 11
44. Memcache
• Key Value Store
• Holds ruby objects
• Extremely fast
• volatile
• available on Heroku
>> heroku addons:add memcache
@Schneems
Monday, August 8, 11
45. Bonus - Keytar
class User < ActiveRecord::Base
• includeKeytar to generate Keys
Use Keytar
define_keys :username, :friends
•
end Use those keys in Memcache
User.username_key("schneems")
=> "user:username:schneems"
user = Rails.cache.fetch(User.username_key("schneems"))
=> # < User id:33 ... >
user.friends_key
=> "users:friends:33"
friends = Rails.cache.fetch(user.friends_key)
=> #[#< User ... >, #< User ... >, ... ]
@Schneems
Monday, August 8, 11
46. Questions?
http://guides.rubyonrails.org
http://stackoverflow.com
http://peepcode.com
@Schneems
Monday, August 8, 11