rails best practices_slides
Post on 12-Sep-2014
985 views
DESCRIPTION
TRANSCRIPT
![Page 1: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/1.jpg)
1
![Page 2: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/2.jpg)
controllers In space
2
![Page 3: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/3.jpg)
level 1
sad Code
Happy Code
controllers In space
3
![Page 4: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/4.jpg)
LEVEL 1 controllers In Space
Example
4
![Page 5: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/5.jpg)
elsif .retweets.where(:user_id =>tweet
tweet.user == current_userif
current_user.id).present?
LEVEL 1 controllers In Space
FAT MODEL, SKINNY CONTROLLER/app/controllers/tweets_controller.rb
class TweetsController < ApplicationController def retweet tweet = Tweet.find(params[:id])
flash[:notice] =
flash[:notice] =
"Sorry, you can't retweet your own tweets"
"You already retweeted!"
t = Tweet.new t.status = "RT #{tweet.user.name}: #{tweet.status}" t.original_tweet = tweet t.user = current_user t.save
flash[:notice] = "Succesfully retweeted"
redirect_to tweet endend
end
else
5
![Page 6: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/6.jpg)
LEVEL 1 controllers In Space
elsif .retweets.where(:user_id =>self
self.user == retweeter
FAT MODEL, SKINNY CONTROLLER/app/controllers/tweets_controller.rb
class TweetsController < ApplicationController def retweet tweet = Tweet.find(params[:id])
flash[:notice] =
"Sorry, you can't retweet your own tweets"
"You already retweeted!"
"Succesfully retweeted"
redirect_to tweet endend
end
else...
tweet.retweet_by(current_user)
/app/models/tweet.rb
class Tweet < ActiveRecord::Base def retweet_by(retweeter)
endend
retweeter.id).present?
if
6
![Page 7: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/7.jpg)
@trending = Topic.find( :all, :conditions => ["started_trending > ?", 1.day.ago], :order => 'mentions desc', :limit => 5 )
LEVEL 1 controllers In Space
Scope it out/app/controllers/tweets_controller.rb
def index @tweets = Tweet. find(
:all, :conditions => {:user_id => current_user.id},::
'created_at desc',order =>=> 10limit
...end
)
7
![Page 8: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/8.jpg)
LEVEL 1 controllers In Space
Scope it out/app/controllers/tweets_controller.rb
def index @tweets = Tweet.
...end
where( ).( ).( )
:user_id => current_user.id'created_at desc'order
limit 10
@trending = Topic. 'started_trending > ?', 1.day.agoorder 'mentions desc'limit 5
where( ).( ).( )
orderlimit
current_userdef index @tweets =
8
![Page 9: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/9.jpg)
LEVEL 1 controllers In Space
Scope it out/app/controllers/tweets_controller.rb
...end
( ).( )'created_at desc'order
limit 10
current_userdef index @tweets = .tweets.
Scope to the user
9
![Page 10: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/10.jpg)
LEVEL 1 controllers In Space
Scope it out/app/controllers/tweets_controller.rb
...end
(
( )
'created_at desc'order
limit 10current_userdef index @tweets = .tweets.
/app/models/tweet.rb
...end
)
recent.
scopeclass Tweet < ActiveRecord::Base
:recent,
10
![Page 11: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/11.jpg)
default_
LEVEL 1 controllers In Space
Scope it out/app/controllers/tweets_controller.rb
...end
(
( )
'created_at desc'
limit 10current_userdef index @tweets = .tweets.
/app/models/tweet.rb
...end
)scopeclass Tweet < ActiveRecord::Base
order
11
![Page 12: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/12.jpg)
limit(5)
LEVEL 1 controllers In Space
Scope it out/app/controllers/tweets_controller.rb
...end
/app/models/topic.rb
...end
scopeclass Topic < ActiveRecord::Base
:trending,
def index@trending = Topic.
'started_trending > ?', 1.day.agoorder 'mentions desc'where(
(
trending
)).
where('started_trending > ?', '12-01-2010 14:02')
Will only work once
where('started_trending > ?', '12-01-2010 14:02')
1
2
same time!
.
12
![Page 13: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/13.jpg)
limit(5)
LEVEL 1 controllers In Space
Scope it out/app/controllers/tweets_controller.rb
...end
/app/models/topic.rb
...end
scopeclass Topic < ActiveRecord::Base
:trending,
def index@trending = Topic.
'started_trending > ?', 1.day.agoorder 'mentions desc'where(
(
trending
)).lambda {
}
.
13
![Page 14: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/14.jpg)
||num
LEVEL 1 controllers In Space
Scope it out/app/controllers/tweets_controller.rb
...end
/app/models/topic.rb
...end
scopeclass Topic < ActiveRecord::Base
:trending,
def index@trending = Topic.
'started_trending > ?', 1.day.agoorder 'mentions desc'where(
( )).lambda {
}.
limit(num)
(5)trending
@trending = Topic.trending(5)
@trending = Topic.trending
wrong number of args, 0 for 1
14
![Page 15: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/15.jpg)
|num
LEVEL 1 controllers In Space
Scope it out/app/controllers/tweets_controller.rb
...end
/app/models/topic.rb
...end
scopeclass Topic < ActiveRecord::Base
:trending,
def index@trending = Topic.
'started_trending > ?', 1.day.agoorder 'mentions desc'where(
( )).lambda {
}.
limit(num)
(5)trending
@trending = Topic.trending(5)
@trending = Topic.trending
| = nil
Ruby 1.9 FTW!
15
![Page 16: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/16.jpg)
default_
LEVEL 1 controllers In Space
Scope it out
('created_at desc'
/app/models/tweet.rb
...end
)scopeclass Tweet < ActiveRecord::Base
order
How do we override default scope?
order(:status).limit(10)@tweets = current_user.tweets.unscoped.
@tweets = current_user.tweets.order(:status).limit(10)
16
![Page 17: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/17.jpg)
LEVEL 1 controllers In Space
Scope it out/app/controllers/tweets_controller.rb
t = Tweet.newt.status = "RT #{@tweet.user.name}: #{@tweet.status}"t.original_tweet = @tweet
current_user has many tweets....
t.user = current_usert.save
current_user"RT #{@tweet.user.name}: #{@tweet.status}"status
original_tweet @tweet:: =>)
,=>.tweets.create(
17
![Page 18: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/18.jpg)
LEVEL 1 controllers In Space
fantastic filters/app/controllers/tweets_controller.rb
class TweetsController < ApplicationController
end
def edit
def update
def destroy
@tweet = Tweet.find(params[:id])
end
@tweet = Tweet.find(params[:id])
@tweet = Tweet.find(params[:id])
end
end...
...
...
18
![Page 19: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/19.jpg)
LEVEL 1 controllers In Space
/app/controllers/tweets_controller.rb
class TweetsController < ApplicationController
end
def edit
def update
def destroy
@tweet = Tweet.find(params[:id])
end
@tweet = Tweet.find(params[:id])@tweet = Tweet.find(params[:id])
end
end...
...
...
def get_tweet
end
before_filter :get_tweet, :only => [:edit, :update, :destroy]
fantastic filters
19
![Page 20: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/20.jpg)
LEVEL 1 controllers In Space
@tweet = Tweet.find(params[:id])
/app/controllers/tweets_controller.rb
class TweetsController < ApplicationController
end
def edit
def update
def destroyend
end
end...
...
...
def get_tweet
end
before_filter :get_tweet, :only => [:edit, :update, :destroy]
private
Why are you hiding instance variables?
Tweet.find(params[:id])@tweet = @tweet = @tweet = get_tweetget_tweetget_tweet
fantastic filters
20
![Page 21: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/21.jpg)
@tweet =
LEVEL 1 controllers In Space
class TweetsController < ApplicationController
end
def edit
def update
def destroy
end
end
end
def get_tweet
end
private
get_tweet
@tweet = get_tweet
@tweet = get_tweet
Keeping parameters in ac.ons
params[:id])(
params[:id])(
params[:id])(
(tweet_id)
fantastic filters
tweet_id)Tweet.find(
21
![Page 22: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/22.jpg)
LEVEL 1 controllers In Space
What should they be used for?
authorization
fantastic filters
Loggingwizards
22
![Page 23: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/23.jpg)
LEVEL 1 controllers In Space
fantastic filters/app/controllers/tweets_controller.rb
class TweetsController < ApplicationControllerbefore_filter :auth, :only => [:edit, :update, :destroy]
:except => [:index, :create]
class ApplicationController < ActionController::Base before_filter :require_login
class SessionsController < ApplicationController skip_before_filter :require_login, :only => [:new, :create]
But what about the login page itself?
Global Filters
23
![Page 24: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/24.jpg)
level 2controller command
24
![Page 25: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/25.jpg)
LEVEL 1 CONTROLLING YOUR CONTROLLERS
Example
25
![Page 26: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/26.jpg)
/app/models/user.rb
class User < ActiveRecord::Base has_one :account_setting, :dependent => :destroyend
<div class="field"> <%= a.label :public_email %><br /> <%= a.check_box :public_email %> </div> <div class="field"> <%= a.label :show_media %><br /> <%= a.check_box :show_media %> </div> <div class="field"> <%= a.label :protect_tweets %><br /> <%= a.check_box :protect_tweets %> </div> <% end %>
<%= fields_for :account_setting do |a| %>
Nested attributes
/app/views/users/edit.html.erb
LEVEL 2 controller command26
![Page 27: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/27.jpg)
Nested attributes/app/controllers/users_controller.rb
@account_setting.save
@user = User.new(params[:user])@account_setting = AccountSetting.new(params[:account_setting])
class UsersController < ApplicationController def create
if @user.save@account_setting.user = @user
redirect_to(@user, :notice => 'User was successfully created.') else render :action => "new" end end end
LEVEL 2 controller command27
![Page 28: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/28.jpg)
Nested attributes/app/controllers/users_controller.rb
@user = User.new(params[:user])
class UsersController < ApplicationController def create
if @user.save redirect_to(@user, :notice => 'User was successfully created.') else render :action => "new" end end end
using Nested A8ributes
LEVEL 2 controller command28
![Page 29: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/29.jpg)
/app/models/user.rb
Nested attributes
/app/views/users/edit.html.erb
end
class UsersController < ApplicationController def new
@user = User.new end end
/app/controllers/users_controller.rb
(:account_setting => AccountSetting.new)
accepts_nested_attributes_for :account_setting
<%= ...
f.
class User < ActiveRecord::Base has_one :account_setting, :dependent => :destroy
fields_for :account_setting do |a| %>
<%= form_for(@user) do |f| %>
LEVEL 2 controller command29
![Page 30: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/30.jpg)
Models without the database
LEVEL 2 controller command30
![Page 31: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/31.jpg)
/app/views/contact_us/new.html.erb
Models without the database
<h1>Contact Us</h1>
<%= form_for :contact, :url => send_email_path do |f| %> <div class="field"> <%= f.label :name %><br /> <%= f.text_field :name %> </div> <div class="field"> <%= f.label :email %><br /> <%= f.text_field :email %> </div> <div class="field"> <%= f.label :body %><br /> <%= f.text_area :body %> </div> <div class="actions"> <%= f.submit %> </div><% end %>
LEVEL 2 controller command31
![Page 32: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/32.jpg)
/app/controllers/contact_us_controller.rb
Models without the database
class ContactUsController < ApplicationController
def new end
def send_email
).deliverflash[:notice] = "Email sent, we'll get back to you"redirect_to root_path
end endend
name, email, body
name.blank? || email.blank? || body.blank?flash.now[:notice] = "Please fill out all fields"
if
name = params[:contact][:name]email = params[:contact][:email]body = params[:contact][:body]
render :action => 'new'elseNotifications.contact_us(
LEVEL 2 controller command32
![Page 33: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/33.jpg)
@contact_form.valid?!
Models without the database
class ContactUsController < ApplicationController
def new
end
def send_email
).deliver
end endend
if
@contact_form = ContactForm.new(params[:contact_form])
@contact_form
@contact_form = ContactForm.new
/app/controllers/contact_us_controller.rb
render :action => 'new'elseNotifications.contact_us(flash[:notice] = "Email sent, we'll get back to you"redirect_to root_path
LEVEL 2 controller command33
![Page 34: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/34.jpg)
@contact_form.valid?
Models without the database
class ContactUsController < ApplicationController
def new
end
def send_email
).deliver
end endend
if
@contact_form = ContactForm.new(params[:contact_form])
@contact_form
@contact_form = ContactForm.new
/app/controllers/contact_us_controller.rb
render :action => 'new'else
Notifications.contact_us(flash[:notice] = "Email sent, we'll get back to you"redirect_to root_path
Use the positive inflection
LEVEL 2 controller command34
![Page 35: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/35.jpg)
@contact_form.valid?
Models without the database
class ContactUsController < ApplicationController
def new
end
def send_email
).deliver
end endend
if
@contact_form = ContactForm.new(params[:contact_form])
@contact_form
@contact_form = ContactForm.new
/app/controllers/contact_us_controller.rb
render :action => 'new'else
Notifications.contact_us(
use the redirect notice syntax
:notice "Email sent, we'll get back to you"redirect_to root_path, =>
LEVEL 2 controller command35
![Page 36: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/36.jpg)
render
@contact_form.valid?
Models without the database
class ContactUsController < ApplicationController
def new
end
def send_email
).deliver
end endend
if
@contact_form = ContactForm.new(params[:contact_form])
@contact_form
@contact_form = ContactForm.new
/app/controllers/contact_us_controller.rb
else
Notifications.contact_us(
shorten the render
:notice "Email sent, we'll get back to you"redirect_to root_path, =>
new:
LEVEL 2 controller command36
![Page 37: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/37.jpg)
/app/views/contact_us/new.html.erb
Models without the database
<%= form_for , :url => send_email_path do |f| %>
<h1>Contact Us</h1>
@contact_form
LEVEL 2 controller command37
![Page 38: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/38.jpg)
/app/models/contact_form.rb
Models without the database
class ContactForm
attr_accessor :name, :email, :body
end
validates_presence_of :name, :email, :body
include ActiveModel::Validationsinclude ActiveModel::Conversion
def initialize(attributes = {}) attributes.each do |name, value| send("#{name}=", value) end end
def persisted? false end
<%= form_for @contact_form
ContactForm.new(params[:contact_form])
LEVEL 2 controller command38
![Page 39: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/39.jpg)
Models without the database
LEVEL 2 controller command39
![Page 40: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/40.jpg)
really Rest
class UsersController
def
< ApplicationController
subscribe_mailing_list current_user.subscribe(params[:id]) redirect_to current_user, :notice => "You've been subscribed" end def unsubscribe_mailing_list current_user.unsubscribe(params[:id]) redirect_to current_user, :notice => "You have been unsubscribed" end
end
/app/controllers/users_controller.rb
LEVEL 2 controller command40
![Page 41: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/41.jpg)
/app/controllers/subscrip5ons_controller.rb
really Rest
class SubscriptionsController
def
< ApplicationController
create current_user.subscribe(params[:id]) redirect_to current_user, :notice => "You've been subscribed" end def destroy current_user.unsubscribe(params[:id]) redirect_to current_user, :notice => "You have been unsubscribed" end
end
LEVEL 2 controller command41
![Page 42: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/42.jpg)
really Rest Use your best judgement
More than 2 levels is bad/users/1/posts/2/comments/3
Not using REST is okay/config/routes.rb
get "contact_us/new"post "contact_us/send_email", :as => "send_email"
LEVEL 2 controller command42
![Page 43: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/43.jpg)
Enter the Presenters
LEVEL 2 controller command43
![Page 44: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/44.jpg)
Enter the Presenters/app/controllers/tweets_controller.rb
@followers_tweets = current_user.followers_tweets.limit(20) @recent_tweet = current_user.tweets.first @following = current_user.following.limit(5) @followers = current_user.followers.limit(5) @recent_favorite = current_user.favorite_tweets.first @recent_listed = current_user.recently_listed.limit(5) if current_user.trend_option == "worldwide" @trends = Trend.worldwide.by_promoted.limit(10) else @trends = Trend.filter_by(current_user.trend_option).limit(10) end ....
def index
end
LEVEL 2 controller command44
![Page 45: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/45.jpg)
Enter the Presenters/app/controllers/tweets_controller.rb
def index
end@presenter = Tweets::IndexPresenter.new(current_user)
/config/applica5on.rb
config.autoload_paths += [config.root.join("app/presenters")]
/app/presenters/tweets/index_presenter.rb
def initialize(user) @user = user end
class Tweets::IndexPresenter
LEVEL 2 controller command45
![Page 46: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/46.jpg)
Enter the Presenters/app/presenters/tweets/index_presenter.rb
def initialize(user) @user = user end
class Tweets::IndexPresenter
def index
end
...
Old Controller
@followers_tweets = current_user.followers_tweets.limit(20)@recent_tweet = current_user.tweets.first
if .trend_option == "worldwide"current_user
.trend_option).limit(10)end
current_user
@trends = Trend.worldwide.by_promoted.limit(10)else@trends = Trend.filter_by(
LEVEL 2 controller command46
![Page 47: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/47.jpg)
Enter the Presenters/app/presenters/tweets/index_presenter.rb
def initialize(user) @user = user end
class Tweets::IndexPresenter
end
.followers_tweets.limit(20)
.tweets.first
if .trend_option == "worldwide"
.trend_option).limit(10)end
def followers_tweets @user. end
Trend.worldwide.by_promoted.limit(10)elseTrend.filter_by(@user.
@user.
def recent_tweet@user
end
def trends
LEVEL 2 controller command47
![Page 48: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/48.jpg)
Enter the Presenters/app/presenters/tweets/index_presenter.rb
def initialize(user) @user = user end
class Tweets::IndexPresenter
/app/views/tweets/index.html.erb
<%= @presenter.recent_tweet.created_at %><%= @presenter.recent_tweet.body %>
/app/controllers/tweets_controller.rb
def index
end@presenter = Tweets::IndexPresenter.new(current_user)
.tweets.firstdef recent_tweet
end
Two objects!
@user
LEVEL 2 controller command48
![Page 49: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/49.jpg)
Enter the Presenters/app/presenters/tweets/index_presenter.rb
def initialize(user) @user = user end
class Tweets::IndexPresenter
@recent_tweet ||= .tweets.firstdef recent_tweet
@user
/app/views/tweets/index.html.erb
<%= @presenter.recent_tweet.created_at %><%= @presenter.recent_tweet.body %>
/app/controllers/tweets_controller.rb
def index@presenter = Tweets::IndexPresenter.new(current_user)
end
end
Memoized
One object!
LEVEL 2 controller command49
![Page 50: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/50.jpg)
Enter the Presenters/app/presenters/tweets/index_presenter.rb
def initialize(user) @user = user end
class Tweets::IndexPresenter
.tweets.firstdef recent_tweet@user
/app/views/tweets/index.html.erb
<%= @presenter.recent_tweet.created_at %><%= @presenter.recent_tweet.body %>
/app/controllers/tweets_controller.rb
def index@presenter = Tweets::IndexPresenter.new(current_user)
end
end
extend ActiveSupport::Memoizable
memoize :recent_tweet, :followers_tweet, ...
LEVEL 2 controller command50
![Page 51: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/51.jpg)
Memoizationone is better than the other
value is not stored if false or nil is returned
||=
extend ActiveSupport::Memoizablememoize :recent_tweet, :followers_tweet, ...
def expensive(num) # lots of processingend
memoize :expensive
expensive(2)expensive(4)
expensive(2)expensive(4)
loaded from cache
LEVEL 2 controller command51
![Page 52: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/52.jpg)
reject sql injection
User.where("name = #{params[:name]}")
User.where("name = ?", params[:name])
Tweet.where("created_at >= :start_date AND created_at <= :end_date", {:start_date => params[:start_date], :end_date => params[:end_date]})
Tweet.where(:created_at => (params[:start_date].to_date)..(params[:end_date].to_date))
User.where(:name => params[:name])
LEVEL 2 controller command52
![Page 53: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/53.jpg)
/app/controllers/users_controller.rb
Rails 3 responder syntax
do |format| format.html # show.html.erb format.xml { render :xml => @user } end
respond_to
do |format| format.html format.xml { render :xml => @users.to_xml } end
respond_to
def index @users = User.all
end...
end
def show @user = User.find(params[:id])
class UsersController < ApplicationController
LEVEL 2 controller command53
![Page 54: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/54.jpg)
/app/controllers/users_controller.rb
Rails 3 responder syntax
respond_to
def index @users = User.all
end...
end
def show @user = User.find(params[:id])
class UsersController < ApplicationController:html, :xml, :json
respond_with(@users)
respond_to
respond_with(@user)
LEVEL 2 controller command54
![Page 55: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/55.jpg)
level 3
Model Mayhem
55
![Page 56: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/56.jpg)
LEVEL 3 Model mayhem
Loving your indices
current_user.tweets
class AddIndexesToTables < ActiveRecord::Migration def self.up add_index :tweets, :user_id end
def self.down remove_index :tweets, :user_id endend
56
![Page 57: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/57.jpg)
LEVEL 3 Model mayhem
current_user.tweets.order('created_at desc').limit(10)Topic.where("started_trending > ?", 1.day.ago).order('mentions desc').limit(5)
class AddIndexesToTables < ActiveRecord::Migration def self.up add_index :tweets, [:user_id, :created_at]
end
def self.down
endend
remove_index :tweets, [:user_id, :created_at]remove_index :topics, [:started_trending, :mentions]
add_index :topics, [:started_trending, :mentions]
If these queries are run a great deal
Loving your indices
57
![Page 58: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/58.jpg)
Use your best judgement
More indices, more time it takes to reindex
If a 2 second query runs 5 times a week,
LEVEL 3 Model mayhem
who cares?
Loving your indices
58
![Page 59: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/59.jpg)
LEVEL 3 Model mayhem59
![Page 60: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/60.jpg)
LEVEL 3 Model mayhem60
![Page 61: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/61.jpg)
LEVEL 3 Model mayhem
$ curl -d "user[login]=hacked&user[is_admin]=true&user[password]=password&user[password_confirmation]=password&user[email][email protected]" http://url_not_shown/users
user[is_admin]=true
61
![Page 62: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/62.jpg)
protecting your attributes/app/models/user.rb
class User < ActiveRecord::Base attr_protected :is_adminend
/app/models/user.rb
class User < ActiveRecord::Base attr_accessible :email, :password, :password_confirmationend
whitelists are better for security
LEVEL 3 Model mayhem62
![Page 63: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/63.jpg)
default values/app/models/account_se7ng.rb
before_create :set_default_timezone def set_default_timezone self.time_zone = "EST"end
end
class AccountSetting < ActiveRecord::Base belongs_to :user
LEVEL 3 Model mayhem63
![Page 64: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/64.jpg)
default values/app/models/account_se7ng.rb
end
class AccountSetting < ActiveRecord::Base belongs_to :user
class AddDefaultTimeZoneToAccountSettings < ActiveRecord::Migration def self.up change_column_default :account_settings, :time_zone, 'EST' end
def self.down change_column :account_settings, :time_zone, :string, nil endend
/db/migrate/20110119150620_add_default_5me_zone_account_se7ngs.rb
LEVEL 3 Model mayhem64
![Page 65: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/65.jpg)
Proper use of callbacks/app/models/topic.rb
LEVEL 3 Model mayhem
Time.now + (60 * 60 * 24 * 7)self.finish_trending = end
end
class Topic < ActiveRecord::Base
before_create :set_trend_ending
private
def set_trend_ending
65
![Page 66: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/66.jpg)
Proper use of callbacks/app/models/topic.rb
LEVEL 3 Model mayhem
1.weekself.finish_trending = end
end
.from_now
class Topic < ActiveRecord::Base
before_create :set_trend_ending
private
def set_trend_ending
66
![Page 67: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/67.jpg)
Proper use of callbacks/app/models/topic.rb
before_create :set_trend_ending
private
def set_trend_ending
LEVEL 3 Model mayhem
1.week
self.finish_trending = end
end
.from_now
class Topic < ActiveRecord::BaseTRENDING_PERIOD =
TRENDING_PERIOD
67
![Page 68: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/68.jpg)
Proper use of callbacks
LEVEL 3 Model mayhem
Crea;ng an object
before_validationafter_validationbefore_saveafter_savebefore_createaround_createafter_create
Upda;ng an object Dele;ng an object
before_validationafter_validationbefore_saveafter_savebefore_updatearound_updateafter_update
before_destroyafter_destroyaround_destroy
68
![Page 69: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/69.jpg)
Rails date helpers
LEVEL 3 Model mayhem
Date Helpers
1.minute2.hour3.days4.week5.months6.year
Modifiers More Modifiers
beginning_of_daybeginning_of_weekbeginning_of_monthbeginning_of_quarterbeginning_of_year
2.weeks.ago3.weeks.from_now
next_weeknext_monthnext_year
* singular or plural
* end can be used
* prev can be used
69
![Page 70: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/70.jpg)
Proper use of callbacks
LEVEL 3 Model mayhem70
![Page 71: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/71.jpg)
Proper use of callbacks
LEVEL 3 Model mayhem
/app/models/following.rb
class Following < ActiveRecord::Base after_create :send_follower_notification
def send_follower_notificationifqueue_new_follower_email
endend
end
self.followed_user.receive_emails?
71
![Page 72: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/72.jpg)
Proper use of callbacks
LEVEL 3 Model mayhem
/app/models/following.rb
class Following < ActiveRecord::Base after_create :
self.followed_user.receive_emails?def followed_can_receive_emails?
queue_new_follower_email
endend
:if => :followed_can_receive_emails?queue_new_follower_email,
72
![Page 73: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/73.jpg)
Proper use of callbacks
LEVEL 3 Model mayhem
/app/models/following.rb
class Following < ActiveRecord::Base after_create :queue_new_follower_email
end
:if => ,
Proc.new {|f| .followed_user.receive_emails?f }
73
![Page 74: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/74.jpg)
improved validation
LEVEL 3 Model mayhem
.errors.add(:name, 'is inappropriate')selfunless ContentModerator.is_suitable?( .name)
selfend
/app/models/topic.rb
end
end
def validate
class Topic < ActiveRecord::Base
74
![Page 75: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/75.jpg)
improved validation
LEVEL 3 Model mayhem
.errors.add(:name, 'is inappropriate')
/app/models/topic.rb
end
end
class Topic < ActiveRecord::Basevalidate :appropriate_content
private
def appropriate_contentselfunless ContentModerator.is_suitable?( .name)
selfend
75
![Page 76: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/76.jpg)
improved validation
LEVEL 3 Model mayhem
/app/models/topic.rb
endend
class Topic < ActiveRecord::Basevalidates :name, :appropriate => true
end
/lib/appropriate_validator.rb
class AppropriateValidator < ActiveRecord::EachValidator def validate_each(record, attribute, value)
.errors.add( , 'is inappropriate')unless ContentModerator.is_suitable?(value)
endrecord attribute
Don’t forget to require this/lib isn’t auto-‐loaded by default
76
![Page 77: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/77.jpg)
Sowing the Seeds
LEVEL 3 Model mayhem
/db/migrate/20110114221048_create_topics.rb
class CreateTopics < ActiveRecord::Migration def self.up create_table :topics do |t| t.string :name t.datetime :started_trending t.integer :mentions
t.timestamps end
end
def self.down drop_table :topics endend
Topic.create(Topic.create(Topic.create(
)):name => "Ruby5", :mentions => 2312
:name => "Top Ruby Jobs", :mentions => 231:name => "Rails for Zombies", :mentions => 1023)
77
![Page 78: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/78.jpg)
Sowing the Seeds
LEVEL 3 Model mayhem
/db/seeds.rb
$ rake db:seed
Run from command line
mentions Won’t be set!
class Topic < ActiveRecord::Base attr_protected :mentionsend
/app/models/topic.rb
Topic.create(Topic.create(Topic.create(
))
):name => "Ruby5", :mentions => 2312:name => "Top Ruby Jobs", :mentions => 231:name => "Rails for Zombies", :mentions => 1023
78
![Page 79: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/79.jpg)
Sowing the Seeds
LEVEL 3 Model mayhem
/db/seeds.rb
:name => "Ruby5", :mentions => 2312:name => "Top Ruby Jobs", :mentions => 231:name => "Rails for Zombies", :mentions => 1023
topics.each do |attributes|Topic.create do |t|t.name = attributes[:name]
topics = [{ },{ },{ }
]
What if we want to be able to update the seed?
t.mentions = attributes[:mentions] endend
79
![Page 80: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/80.jpg)
Sowing the Seeds
LEVEL 3 Model mayhem
/db/seeds.rb
:name => "Ruby5", :mentions => 2312:name => "Top Ruby Jobs", :mentions => 231:name => "Rails for Zombies", :mentions => 1023
topics.each do |attributes|Topic.create do |t|t.name = attributes[:name]
topics = [{ },{ },{ }
]
Topic.destroy_all
Dangerous if there are lots of relationships
t.mentions = attributes[:mentions] endend
80
![Page 81: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/81.jpg)
Sowing the Seeds
LEVEL 3 Model mayhem
/db/seeds.rb
:name => "Ruby5", :mentions => 2312:name => "Top Ruby Jobs", :mentions => 231:name => "Rails for Zombies", :mentions => 1023
topics.each do |attributes|Topic. attributes[:name]
topics = [{ },{ },{ }
]
find_or_initialize_by_name( ).tap do |t| t.mentions = attributes[:mentions]
endend
t.save!
81
![Page 82: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/82.jpg)
Level 4Model Bert
82
![Page 83: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/83.jpg)
N+1 is not for fun/app/models/user.rb
class User def recent_followers self.followers.recent.collect{ |f| f.user.name }.to_sentence endend
=> "Gregg, Eric, Dray, and Nate"
Select followers where user_id=1
Select user where id=2
Select user where id=3
Select user where id=4
Select user where id=5
LEVEL 4 Model Bert83
![Page 84: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/84.jpg)
N+1 is not for fun/app/models/user.rb
class User def recent_followers self.followers.recent .collect{ |f| f.user.name }.to_sentence endend
.includes(:user)
Select followers where user_id=1
Select users where user_id in (2,3,4,5)
2 queries instead of 5!
h"ps://github.com/flyerhzm/bulletBullet gem
To find all your n+1 queries
LEVEL 4 Model Bert84
![Page 85: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/85.jpg)
counter_cache Money/app/views/tweets/index.html.erb
<% @tweets.each do |tweet| %>
<div class="tweet"> <%= tweet.status %> <span class="retweets"> <%= tweet.retweets.length ReTweets%> </span> </div> <% end %>
2 ReTweets1 ReTweets0 ReTweets
Bad English
LEVEL 4 Model Bert85
![Page 86: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/86.jpg)
counter_cache Money/app/views/tweets/index.html.erb
<% @tweets.each do |tweet| %>
<div class="tweet"> <%= tweet.status %> <span class="retweets"> <%= tweet.retweets.length %> </span> </div> <% end %>
pluralize( , "ReTweet")
2 ReTweets1 ReTweet0 ReTweets
LEVEL 4 Model Bert86
![Page 87: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/87.jpg)
counter_cache Money/app/views/tweets/index.html.erb
<% @tweets.each do |tweet| %>
<div class="tweet"> <%= tweet.status %> <span class="retweets"> <%= tweet.retweets.length %> </span> </div> <% end %>
, "ReTweet")
1. Select all retweets where user_id=X2. Populate an array of tweet objects3. Call length on that array
For Each tweetLots of unneeded objects
pluralize(
LEVEL 4 Model Bert87
![Page 88: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/88.jpg)
counter_cache Money/app/views/tweets/index.html.erb
<% @tweets.each do |tweet| %>
<div class="tweet"> <%= tweet.status %> <span class="retweets"> <%= tweet.retweets.count %> </span> </div> <% end %>
pluralize( , "ReTweet")
1. Select all retweets where user_id=X2. do a count query for retweets
For Each tweet
possibly 10+ count queriesLEVEL 4 Model Bert
88
![Page 89: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/89.jpg)
counter_cache Money/app/views/tweets/index.html.erb
<% @tweets.each do |tweet| %>
<div class="tweet"> <%= tweet.status %> <span class="retweets"> <%= tweet.retweets.size %> </span> </div> <% end %>
pluralize( , "ReTweet")
1. Select all retweets where user_id=XFor Each tweet
There is no step 2
with counter_cache
89
![Page 90: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/90.jpg)
counter_cache Money
Tweet Tweet
retweets
original_tweet
has_many
belongs_to
original retweet
/app/models/tweet.rb
has_many :retweets, :class_name => 'Tweet', :foreign_key => :tweet_idend
class Tweet < ActiveRecord::Base belongs_to :original_tweet, :class_name => 'Tweet', :foreign_key => :tweet_id
class AddCountRetweets def self.up add_column :tweets, :retweets_count, :integer, :default => 0 end ...
Our migra;on
retweets_count
LEVEL 4 Model Bert90
![Page 91: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/91.jpg)
counter_cache Money
true:counter_cache =>
/app/models/tweet.rb
has_many :retweets, :class_name => 'Tweet', :foreign_key => :tweet_idend
class Tweet < ActiveRecord::Base belongs_to :original_tweet, :class_name => 'Tweet', :foreign_key => :tweet_id
retweets_count
, unable to find tweets_count
LEVEL 4 Model Bert91
![Page 92: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/92.jpg)
counter_cache Money
:counter_cache =>
/app/models/tweet.rb
has_many :retweets, :class_name => 'Tweet', :foreign_key => :tweet_idend
class Tweet < ActiveRecord::Base belongs_to :original_tweet, :class_name => 'Tweet', :foreign_key => :tweet_id,
retweets_count:
current_user.tweets.create( :status => "RT #{self.user.name}: #{self.status}", :original_tweet => self )
UPDATE "tweets" SET "retweets_count" = "retweets_count" + 1 WHERE ("tweets"."id" = 42)
will cause an insert AND
LEVEL 4 Model Bert92
![Page 93: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/93.jpg)
counter_cache Money
t.retweets.length pull all recordsthen calls .length
pull all recordsthen calls .length
t.retweets.count count query count query
t.retweets.size count queryno query
look at cache
Without Cache Counter With Cache Counter
LEVEL 4 Model Bert93
![Page 94: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/94.jpg)
Batches of find_each/lib/tasks/long_running_task.rake
Not so good if you have millions of tweets
desc 'Task involving all tweets'task :tweet_task => :environment do
Tweet.all.each do |tweet| p "task for #{tweet}" end
end
LEVEL 4 Model Bert94
![Page 95: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/95.jpg)
Batches of find_each/lib/tasks/long_running_task.rake
desc 'Task involving all tweets'task :tweet_task => :environment do
Tweet each do |tweet| p "task for #{tweet}" end
end
.find_
pulls batches of 1,000 at a time
LEVEL 4 Model Bert95
![Page 96: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/96.jpg)
Batches of find_each/lib/tasks/long_running_task.rake
desc 'Task involving all tweets'task :tweet_task => :environment do
Tweet each do |tweet| p "task for #{tweet}" end
end
.find_ (:batch_size => 200)
pulls batches of 200 at a time
LEVEL 4 Model Bert96
![Page 97: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/97.jpg)
Law of DemeterEach unit should have limited knowledge about other units
“don’t talk to strangers”
tweet user account settings
XLEVEL 4 Model Bert
97
![Page 98: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/98.jpg)
Law of Demeter/app/models/tweet.rb
The tweet shouldn’t know about account_setting!
class Tweet < ActiveRecord::Base def location_data if self.user.account_setting.location_on_tweets self.location else "unavailable" end endend
LEVEL 4 Model Bert98
![Page 99: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/99.jpg)
Law of Demeter/app/models/tweet.rb
class Tweet < ActiveRecord::Base def location_data if self.user.location_on_tweets self.location else "unavailable" end endend
class User < ActiveRecord::Base has_one :account_setting, :dependent => :destroy
end
delegate :location_on_tweets,
/app/models/user.rb
:to => :account_setting:public_email,
Additional MethodsLEVEL 4 Model Bert
99
![Page 100: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/100.jpg)
Law of Demeter
class User < ActiveRecord::Base has_one :account_setting, :dependent => :destroy
end
delegate :location_on_tweets,
/app/models/user.rb
:to => :account_setting:public_email,
tweet user account settingsX
self.user.location_on_tweets
ERROR!! account_setting is nil!
LEVEL 4 Model Bert100
![Page 101: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/101.jpg)
Law of Demeter
class User < ActiveRecord::Base has_one :account_setting, :dependent => :destroy
end
delegate :location_on_tweets,
/app/models/user.rb
:to => :account_setting:public_email
tweet user account settingsX
self.user.location_on_tweets
:allow_nil => true
,,
Returns nil when Account_Settings is missing
LEVEL 4 Model Bert101
![Page 102: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/102.jpg)
Head to to_s /app/models/user.rb
<%= @user.display_name %>
class User < ActiveRecord::Base def display_name "#{first_name} #{last_name}" endend
LEVEL 4 Model Bert102
![Page 103: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/103.jpg)
Head to to_s /app/models/user.rb
<%= @user %>
class User < ActiveRecord::Base def to_s "#{first_name} #{last_name}" endend
LEVEL 4 Model Bert103
![Page 104: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/104.jpg)
to_param-alama ding dong
/app/models/topic.rb
/post/2133
class Topic < ActiveRecord::Base def to_param "#{id}-#{name.parameterize}" endend
/post/rails-best-practices
SEO Friendly URLS
/post/2133-rails-best-practices
LEVEL 4 Model Bert104
![Page 105: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/105.jpg)
to_param-alama ding dong/app/models/topic.rb
class Topic < ActiveRecord::Base def to_param "#{id}-#{name.parameterize}" endend
/post/2133-rails-best-practices
<%= link_to topic.name, topic %>
Will generate
Topic.find(params[:id])
{:id => "2133-rails-best-practices"}
Will call to_i
Topic.find(2133)
LEVEL 4 Model Bert105
![Page 106: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/106.jpg)
Level 5Froggy Views
106
![Page 107: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/107.jpg)
The example
LEVEL 5 Froggy Views107
![Page 108: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/108.jpg)
LEVEL 5 Froggy Views
No queries in your view!/app/views/tweets/index.html.erb
current_user.who_to_follow.limit(5)<% <li><%= f.name %> - <%= link_to "Follow", follow_user_path(f) %></li><% end %>
.each do |f| %>
Query shouldn’t be in our view!
108
![Page 109: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/109.jpg)
LEVEL 5 Froggy Views
No queries in your view!/app/views/tweets/index.html.erb
<% <li><%= f.name %> - <%= link_to "Follow", follow_user_path(f) %></li><% end %>
.each do |f| %>
/app/controllers/tweets_controller.rb
class TweetsController < ApplicationController
def index @who_to_follow = end
end
current_user.who_to_follow.limit(5)
@who_to_follow
109
![Page 110: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/110.jpg)
LEVEL 5 Froggy Views
Helper Skelter/app/views/tweets/index.html.erb
@followers_count %></span><% @recent_followers.each do |f| %>
<a href="<%= user_path(f) %>"> <img src="<%= f.avatar.url(:thumb) %>" /> </a> <% end %></div>
<div class="following">
<div class="followers">Followers<span><%=
Following<span><%= @following_count %></span><% @recent_following.each do |f| %>
<a href="<%= user_path(f) %>"> <img src="<%= f.avatar.url(:thumb) %>" /> </a> <% end %></div>
110
![Page 111: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/111.jpg)
LEVEL 5 Froggy Views
/app/views/tweets/index.html.erb
@followers_count @recent_followers@following_count @recent_following
<%= follow_box(<%= follow_box(
"Followers", , ) %>"Following", , ) %>FollowersFollowing
/app/helpers/tweets_helper.rb
def follow_box(title, count, recent)
end
end
str = "<div class=\"#{title.downcase \">" + "#{title}<span>#{count}</span>"
recent.each do |user|str += "<a href=\"#{user_path(user)}\">"str += "<img src=\"#{user.avatar.url(:thumb)}\">"str += "</a>"
Use proper link_to and image_tag
+= "</div>")raw(str
}
Helper Skelter
111
![Page 112: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/112.jpg)
LEVEL 5 Froggy Views
/app/views/tweets/index.html.erb
@followers_count @recent_followers@following_count @recent_following
<%= follow_box(<%= follow_box(
"Followers", , ) %>"Following", , ) %>
def follow_box(title, count, recent)
end
end
str = "<div class=\"#{title.downcase \">" + "#{title}<span>#{count}</span>"
recent.each do |user|str +=
user.avatar.url(:thumb)link_to user do
image_tag(end
)
Use html helpers?
+= "</div>")raw(str
/app/helpers/tweets_helper.rb
Helper Skelter
112
![Page 113: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/113.jpg)
LEVEL 5 Froggy Views
/app/views/tweets/index.html.erb
@followers_count @recent_followers@following_count @recent_following
<%= follow_box(<%= follow_box(
"Followers", , ) %>"Following", , ) %>
def follow_box(title, count, recent)
end
end
title.downcasetitle count
recent.each do |user|str +=
user.avatar.url(:thumb)link_to user do
image_tag(end
)
)
content_tag :div, :class => dostr = )
end
raw(str
+ content_tag(:span,
Annoying str variable
/app/helpers/tweets_helper.rb
Helper Skelter
113
![Page 114: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/114.jpg)
LEVEL 5 Froggy Views
/app/views/tweets/index.html.erb
@followers_count @recent_followers@following_count @recent_following
<%= follow_box(<%= follow_box(
"Followers", , ) %>"Following", , ) %>
def follow_box(title, count, recent)
end
end
title.downcase
titlecount
recent.
user.avatar.url(:thumb)link_to user do
image_tag(end
)
)
content_tag :div, :class => do
)
end
raw(+
content_tag(:span, +collect do |user|
.join
/app/helpers/tweets_helper.rb
Helper Skelter
114
![Page 115: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/115.jpg)
The Example
LEVEL 5 Froggy Views115
![Page 116: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/116.jpg)
LEVEL 5 Froggy Views
Partial sanity/app/views/tweets/index.html.erb
/app/views/tweets/_trending.html.erb
<h3><%= @user.trending_area %></h3><ul> <% @trending.each do |topic| %>
<li> <%= link_to topic.name, topic %> <% if topic.promoted? %> <%= link_to image_tag('promoted.jpg'), topic %> <% end %></li>
<% end %></ul>
<%= render :partial => 'trending' %><h2>Trends</h2>
116
![Page 117: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/117.jpg)
LEVEL 5 Froggy Views
Partial sanity/app/views/tweets/index.html.erb
/app/views/tweets/_trending.html.erb
<h3><%= @user.trending_area %></h3><ul> <% @trending.each do |topic| %>
<li> <%= link_to topic.name, topic %> <% if topic.promoted? %> <%= link_to image_tag('promoted.jpg'), topic %> <% end %></li>
<% end %></ul>
<%= render 'trending' %><h2>Trends</h2>
There are instance variables
in our partial!
117
![Page 118: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/118.jpg)
LEVEL 5 Froggy Views
Partial sanity/app/views/tweets/index.html.erb
/app/views/tweets/_trending.html.erb
<h3><%=
@user.trending_area
%></h3><ul> <%
@trending
.each do |topic| %><li> <%= link_to topic.name, topic %> <% if topic.promoted? %> <%= link_to image_tag('promoted.jpg'), topic %> <% end %></li>
<% end %></ul>
<%= render 'trending'%>
<h2>Trends</h2>, :area => ,:topics =>
area
topics
118
![Page 119: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/119.jpg)
LEVEL 5 Froggy Views
Partial sanity/app/views/tweets/_trending.html.erb
<h3><%= %></h3><ul> <% .each do |topic| %>
<li> <%= link_to topic.name, topic %> <% if topic.promoted? %> <%= link_to image_tag('promoted.jpg'), topic %> <% end %></li>
<% end %></ul>
area
topics
/app/views/topics/_topic.html.erb
<%= render topic %>'topics/topic', :topic =>
119
![Page 120: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/120.jpg)
LEVEL 5 Froggy Views
Partial sanity/app/views/tweets/_trending.html.erb
<h3><%= %></h3><ul> <% .each do |topic| %>
<li> <%= link_to topic.name, topic %> <% if topic.promoted? %> <%= link_to image_tag('promoted.jpg'), topic %> <% end %></li>
<% end %></ul>
area
topics
/app/views/topics/_topic.html.erb
<%= render topic %>
<ul>
Using Class name to find partial
120
![Page 121: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/121.jpg)
LEVEL 5 Froggy Views
Partial sanity/app/views/tweets/_trending.html.erb
<h3><%= %></h3>
<li> <%= link_to topic.name, topic %> <% if topic.promoted? %> <%= link_to image_tag('promoted.jpg'), topic %> <% end %></li>
</ul>
area
topics
/app/views/topics/_topic.html.erb
<%= render<ul>
%>:partial => 'topics/topic', :collection =>
121
![Page 122: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/122.jpg)
LEVEL 5 Froggy Views
Partial sanity/app/views/tweets/_trending.html.erb
<h3><%= %></h3>
<li> <%= link_to topic.name, topic %> <% if topic.promoted? %> <%= link_to image_tag('promoted.jpg'), topic %> <% end %></li>
</ul>
area
topics
/app/views/topics/_topic.html.erb
<%= render<ul>
%>
122
![Page 123: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/123.jpg)
LEVEL 5 Froggy Views
empty string things
<% if @user.email.blank? %>
<% unless @user.email? %>
<% if @user.email.present? %>
<% if @user.email? %>
123
![Page 124: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/124.jpg)
LEVEL 5 Froggy Views
empty string things
<%= @user.city || @user.state || "Unknown" %>
If city is empty “” it will print “”
city = @user.city if @user.city.present?state = @user.state if @user.state.present?
=> “”
<%= city || state || "Unknown" %>
124
![Page 125: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/125.jpg)
LEVEL 5 Froggy Views
empty string things
<%= @user.city || @user.state || "Unknown" %>
<%= @user.city.presence || @user.state.presence || "Unknown" %>
125
![Page 126: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/126.jpg)
LEVEL 5 Froggy Views
empty string things
city is nil
undefined method `titleize' for nil:NilClass
<%= @user.city.titleize %>|| "Unknown"
126
![Page 127: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/127.jpg)
LEVEL 5 Froggy Views
empty string things
<%= @user.city.titleize %><% if @user.city %>
<% else %> Unknown<% end %>
<%= @user.city ? @user.city.titleize : "Unknown" %>
<%= @user.city.try(:titleize) || "Unknown" %>
127
![Page 128: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/128.jpg)
The example
LEVEL 5 Froggy Views128
![Page 129: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/129.jpg)
LEVEL 5 Froggy Views
rock your block helpers
<% @presenter.tweets.each do |tweet| %>
<% end %>
<div id="tweet_<%= tweet.id %>" class="<%= 'favorite' if tweet.is_a_favorite?(current_user) %>"><%= tweet.status %>
</div>
/app/views/tweets/index.html.erb
129
![Page 130: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/130.jpg)
LEVEL 5 Froggy Views
rock your block helpers
<% @presenter.tweets.each do |tweet| %>
<% end %>
<%= tweet.status %>
/app/views/tweets/index.html.erb
<%= tweet_div_for(tweet, current_user) do %>
<% end %>
/app/helpers/tweets_helper.rb
def tweet_div_for(tweet, user, &block)klass = 'favorite' if tweet.is_a_favorite?(user)
content_tag tweet klass do yield endend
, :class =>
id="tweet_<%= tweet.id %>"
130
![Page 131: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/131.jpg)
LEVEL 5 Froggy Views
Yield to the content_for/app/views/layouts/applica5on.html.erb
<!DOCTYPE html><html><body> <h1>Twitter</h1>
Need to insert content here!
<% if flash[:notice] %> <span style="color: green"><%= flash[:notice] %></span> <% end %> <%= yield %></body></html>
131
![Page 132: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/132.jpg)
LEVEL 5 Froggy Views
Yield to the content_for/app/views/layouts/applica5on.html.erb
<!DOCTYPE html><html><body> <h1>Twitter</h1>
<%= yield :sidebar %>
/app/views/tweets/index.html.erb
<% content_for(:sidebar) do %> ... html here ...<% end %>
<% if flash[:notice] %> <span style="color: green"><%= flash[:notice] %></span> <% end %> <%= yield %></body></html>
132
![Page 133: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/133.jpg)
LEVEL 5 Froggy Views
/app/controllers/tweets_controller.rb
what if all actions in the tweet controller need the sidebar?
class TweetsController < ApplicationControllerlayout 'with_sidebar'
end
/app/views/layouts/with_sidebar.html.erb
<% content_for(:sidebar) do %> ... html here ...<% end %><%= render :file => 'layouts/application' %>
/app/views/layouts/applica5on.html.erb
<%= yield :sidebar %>
<% if flash[:notice] %> <span style="color: green"><%= flash[:notice] %></span> <% end %> <%= yield %>
1
2
3
133
![Page 134: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/134.jpg)
LEVEL 5 Froggy Views
The example
134
![Page 135: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/135.jpg)
LEVEL 5 Froggy Views
meta Yield/app/views/layouts/applica5on.html.erb
cluttering your controller
& polluting with view concerns
class TweetsController < ApplicationController def show @tweet = Tweet.find(params[:id]) @title = @tweet.user.name @description = @tweet.status @keywords = @tweet.hash_tags.join(",") endend
/app/controllers/tweets_controller.rb
@description || "The best way ..." %>"> <meta name ="keywords" content="<%= @keywords || "social,tweets ..." %>">...
<!DOCTYPE html><html><head> <title>Twitter <%= %></title> <meta name="description" content="<%=
@title
135
![Page 136: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/136.jpg)
LEVEL 5 Froggy Views
meta Yield/app/views/layouts/applica5on.html.erb
<% content_for(:title, @tweet.user.name)content_for(:description, @tweet.status)content_for(:keywords, @tweet.hash_tags.join(","))%>
/app/views/tweets/show.html.erb
<meta name ="keywords" content="<%=...
<!DOCTYPE html><html><head> <title>Twitter <%= %></title> <meta name="description" content="<%=
yield(:title)
yield(:description)
yield(:keywords)
|| "The best way ..." %>">
|| "social,tweets ..." %>">
136
![Page 137: Rails best practices_slides](https://reader033.vdocument.in/reader033/viewer/2022051512/5412bfc58d7f720a4e8b46a6/html5/thumbnails/137.jpg)
LEVEL 5 Froggy Views
meta Yield
<% title @tweet.user.name description @tweet.status keywords @tweet.hash_tags.join(",")%>
/app/views/tweets/show.html.erb
/app/helpers/applica5on_helper.rb
def title(title) content_for(:title, title)end
def description(description) content_for(:description, description)end
def keywords(keywords) content_for(:keywords, keywords)end
137