SlideShare a Scribd company logo
1 of 137
Download to read offline
1
controllers In space
2
level 1
sad Code
Happy Code
controllers In space
3
LEVEL 1 controllers In Space
Example
4
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
end
end
end
else
5
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
end
end
end
else
...
tweet.retweet_by(current_user)
/app/models/tweet.rb
class Tweet < ActiveRecord::Base
def retweet_by(retweeter)
end
end
retweeter.id).present?
if
6
@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
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.ago
order 'mentions desc'
limit 5
where( ).
( ).
( )
order
limit
current_user
def index
@tweets =
8
LEVEL 1 controllers In Space
Scope it out
/app/controllers/tweets_controller.rb
...
end
( ).
( )
'created_at desc'order
limit 10
current_user
def index
@tweets = .tweets.
Scope to the user
9
LEVEL 1 controllers In Space
Scope it out
/app/controllers/tweets_controller.rb
...
end
(
( )
'created_at desc'order
limit 10current_user
def index
@tweets = .tweets.
/app/models/tweet.rb
...
end
)
recent.
scope
class Tweet < ActiveRecord::Base
:recent,
10
default_
LEVEL 1 controllers In Space
Scope it out
/app/controllers/tweets_controller.rb
...
end
(
( )
'created_at desc'
limit 10current_user
def index
@tweets = .tweets.
/app/models/tweet.rb
...
end
)scope
class Tweet < ActiveRecord::Base
order
11
limit(5)
LEVEL 1 controllers In Space
Scope it out
/app/controllers/tweets_controller.rb
...
end
/app/models/topic.rb
...
end
scope
class Topic < ActiveRecord::Base
:trending,
def index
@trending = Topic.
'started_trending > ?', 1.day.ago
order '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
limit(5)
LEVEL 1 controllers In Space
Scope it out
/app/controllers/tweets_controller.rb
...
end
/app/models/topic.rb
...
end
scope
class Topic < ActiveRecord::Base
:trending,
def index
@trending = Topic.
'started_trending > ?', 1.day.ago
order 'mentions desc'
where(
(
trending
)
).lambda {
}
.
13
||num
LEVEL 1 controllers In Space
Scope it out
/app/controllers/tweets_controller.rb
...
end
/app/models/topic.rb
...
end
scope
class Topic < ActiveRecord::Base
:trending,
def index
@trending = Topic.
'started_trending > ?', 1.day.ago
order 'mentions desc'
where(
( )
).lambda {
}
.
limit(num)
(5)trending
@trending = Topic.trending(5)
@trending = Topic.trending
wrong number of args, 0 for 1
14
|num
LEVEL 1 controllers In Space
Scope it out
/app/controllers/tweets_controller.rb
...
end
/app/models/topic.rb
...
end
scope
class Topic < ActiveRecord::Base
:trending,
def index
@trending = Topic.
'started_trending > ?', 1.day.ago
order 'mentions desc'
where(
( )
).lambda {
}
.
limit(num)
(5)trending
@trending = Topic.trending(5)
@trending = Topic.trending
|= nil
Ruby 1.9 FTW!
15
default_
LEVEL 1 controllers In Space
Scope it out
('created_at desc'
/app/models/tweet.rb
...
end
)scope
class 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
LEVEL 1 controllers In Space
Scope it out
/app/controllers/tweets_controller.rb
t = Tweet.new
t.status = "RT #{@tweet.user.name}: #{@tweet.status}"
t.original_tweet = @tweet
current_user has many tweets....
t.user = current_user
t.save
current_user
"RT #{@tweet.user.name}: #{@tweet.status}"status
original_tweet @tweet
:
: =>
)
,=>
.tweets.create(
17
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
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
LEVEL 1 controllers In Space
@tweet = Tweet.find(params[:id])
/app/controllers/tweets_controller.rb
class TweetsController < ApplicationController
end
def edit
def update
def destroy
end
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
@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
LEVEL 1 controllers In Space
What should they be used for?
authorization
fantastic filters
Logging
wizards
22
LEVEL 1 controllers In Space
fantastic filters
/app/controllers/tweets_controller.rb
class TweetsController < ApplicationController
before_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
level 2
controller command
24
LEVEL 1 CONTROLLING YOUR CONTROLLERS
Example
25
/app/models/user.rb
class User < ActiveRecord::Base
has_one :account_setting, :dependent => :destroy
end
<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 command
26
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 command
27
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 command
28
/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 command
29
Models without the database
LEVEL 2 controller command
30
/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 command
31
/app/controllers/contact_us_controller.rb
Models without the database
class ContactUsController < ApplicationController
def new
end
def send_email
).deliver
flash[:notice] = "Email sent, we'll get back to you"
redirect_to root_path
end
end
end
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'
else
Notifications.contact_us(
LEVEL 2 controller command
32
@contact_form.valid?!
Models without the database
class ContactUsController < ApplicationController
def new
end
def send_email
).deliver
end
end
end
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
LEVEL 2 controller command
33
@contact_form.valid?
Models without the database
class ContactUsController < ApplicationController
def new
end
def send_email
).deliver
end
end
end
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 command
34
@contact_form.valid?
Models without the database
class ContactUsController < ApplicationController
def new
end
def send_email
).deliver
end
end
end
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 command
35
render
@contact_form.valid?
Models without the database
class ContactUsController < ApplicationController
def new
end
def send_email
).deliver
end
end
end
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 command
36
/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 command
37
/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::Validations
include 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 command
38
Models without the database
LEVEL 2 controller command
39
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 command
40
/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 command
41
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 command
42
Enter the Presenters
LEVEL 2 controller command
43
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 command
44
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 command
45
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 command
46
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)
else
Trend.filter_by(@user.
@user.
def recent_tweet
@user
end
def trends
LEVEL 2 controller command
47
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.first
def recent_tweet
end
Two objects!
@user
LEVEL 2 controller command
48
Enter the Presenters
/app/presenters/tweets/index_presenter.rb
def initialize(user)
@user = user
end
class Tweets::IndexPresenter
@recent_tweet ||= .tweets.first
def 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 command
49
Enter the Presenters
/app/presenters/tweets/index_presenter.rb
def initialize(user)
@user = user
end
class Tweets::IndexPresenter
.tweets.first
def 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 command
50
Memoization
one is better than the other
value is not stored
if false or nil is returned
||=
extend ActiveSupport::Memoizable
memoize :recent_tweet, :followers_tweet, ...
def expensive(num)
# lots of processing
end
memoize :expensive
expensive(2)
expensive(4)
expensive(2)
expensive(4)
loaded from cache
LEVEL 2 controller command
51
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 command
52
/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 command
53
/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 command
54
level 3
Model Mayhem
55
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
end
end
56
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
end
end
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
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
LEVEL 3 Model mayhem
59
LEVEL 3 Model mayhem
60
LEVEL 3 Model mayhem
$ curl -d "user[login]=hacked&user[is_admin]=true&user[password]
=password&user[password_confirmation]=password&user[email]=hacked@by.me"
http://url_not_shown/users
user[is_admin]=true
61
protecting your attributes
/app/models/user.rb
class User < ActiveRecord::Base
attr_protected :is_admin
end
/app/models/user.rb
class User < ActiveRecord::Base
attr_accessible :email, :password, :password_confirmation
end
whitelists are better for security
LEVEL 3 Model mayhem
62
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 mayhem
63
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
end
end
/db/migrate/20110119150620_add_default_5me_zone_account_se7ngs.rb
LEVEL 3 Model mayhem
64
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
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
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::Base
TRENDING_PERIOD =
TRENDING_PERIOD
67
Proper use of callbacks
LEVEL 3 Model mayhem
Crea;ng	
  an	
  object
before_validation
after_validation
before_save
after_save
before_create
around_create
after_create
Upda;ng	
  an	
  object Dele;ng	
  an	
  object
before_validation
after_validation
before_save
after_save
before_update
around_update
after_update
before_destroy
after_destroy
around_destroy
68
Rails date helpers
LEVEL 3 Model mayhem
Date	
  Helpers
1.minute
2.hour
3.days
4.week
5.months
6.year
Modifiers More	
  Modifiers
beginning_of_day
beginning_of_week
beginning_of_month
beginning_of_quarter
beginning_of_year
2.weeks.ago
3.weeks.from_now
next_week
next_month
next_year
* singular
or plural
* end can be used
* prev can be used
69
Proper use of callbacks
LEVEL 3 Model mayhem
70
Proper use of callbacks
LEVEL 3 Model mayhem
/app/models/following.rb
class Following < ActiveRecord::Base
after_create :send_follower_notification
def send_follower_notification
if
queue_new_follower_email
end
end
end
self.followed_user.receive_emails?
71
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
end
end
:if => :followed_can_receive_emails?
queue_new_follower_email,
72
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
improved validation
LEVEL 3 Model mayhem
.errors.add(:name, 'is inappropriate')
selfunless ContentModerator.is_suitable?( .name)
self
end
/app/models/topic.rb
end
end
def validate
class Topic < ActiveRecord::Base
74
improved validation
LEVEL 3 Model mayhem
.errors.add(:name, 'is inappropriate')
/app/models/topic.rb
end
end
class Topic < ActiveRecord::Base
validate :appropriate_content
private
def appropriate_content
selfunless ContentModerator.is_suitable?( .name)
self
end
75
improved validation
LEVEL 3 Model mayhem
/app/models/topic.rb
end
end
class Topic < ActiveRecord::Base
validates :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)
end
record attribute
Don’t forget to require this
/lib	
  isn’t	
  auto-­‐loaded	
  by	
  default
76
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
end
end
Topic.create(
Topic.create(
Topic.create(
)
):name => "Ruby5", :mentions => 2312
:name => "Top Ruby Jobs", :mentions => 231
:name => "Rails for Zombies", :mentions => 1023)
77
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 :mentions
end
/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
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]
end
end
79
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]
end
end
80
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]
end
end
t.save!
81
Level 4Model Bert
82
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
end
end
=> "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 Bert
83
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
end
end
.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/bullet
Bullet gem
To find all your n+1 queries
LEVEL 4 Model Bert
84
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 ReTweets
1 ReTweets
0 ReTweets
Bad English
LEVEL 4 Model Bert
85
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 ReTweets
1 ReTweet
0 ReTweets
LEVEL 4 Model Bert
86
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=X
2. Populate an array of tweet objects
3. Call length on that array
For Each tweet
Lots of unneeded objects
pluralize(
LEVEL 4 Model Bert
87
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=X
2. do a count query for retweets
For Each tweet
possibly 10+ count queries
LEVEL 4 Model Bert
88
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=X
For Each tweet
There is no step 2
with	
  counter_cache
89
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_id
end
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 Bert
90
counter_cache Money
true:counter_cache =>
/app/models/tweet.rb
has_many :retweets,
:class_name => 'Tweet',
:foreign_key => :tweet_id
end
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 Bert
91
counter_cache Money
:counter_cache =>
/app/models/tweet.rb
has_many :retweets,
:class_name => 'Tweet',
:foreign_key => :tweet_id
end
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 Bert
92
counter_cache Money
t.retweets.length
pull	
  all	
  records
then	
  calls	
  .length
pull	
  all	
  records
then	
  calls	
  .length
t.retweets.count count	
  query count	
  query
t.retweets.size count	
  query
no	
  query
look	
  at	
  cache
Without Cache Counter With Cache Counter
LEVEL 4 Model Bert
93
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 Bert
94
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 Bert
95
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 Bert
96
Law of Demeter
Each unit should have limited knowledge about other units
“don’t talk to strangers”
tweet user account settings
X
LEVEL 4 Model Bert
97
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
end
end
LEVEL 4 Model Bert
98
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
end
end
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 Methods
LEVEL 4 Model Bert
99
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 settings
X
self.user.location_on_tweets
ERROR!! account_setting is nil!
LEVEL 4 Model Bert
100
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 settings
X
self.user.location_on_tweets
:allow_nil => true
,
,
Returns nil when Account_Settings is missing
LEVEL 4 Model Bert
101
Head to to_s
/app/models/user.rb
<%= @user.display_name %>
class User < ActiveRecord::Base
def display_name
"#{first_name} #{last_name}"
end
end
LEVEL 4 Model Bert
102
Head to to_s
/app/models/user.rb
<%= @user %>
class User < ActiveRecord::Base
def to_s
"#{first_name} #{last_name}"
end
end
LEVEL 4 Model Bert
103
to_param-alama ding dong
/app/models/topic.rb
/post/2133
class Topic < ActiveRecord::Base
def to_param
"#{id}-#{name.parameterize}"
end
end
/post/rails-best-practices
SEO Friendly URLS
/post/2133-rails-best-practices
LEVEL 4 Model Bert
104
to_param-alama ding dong
/app/models/topic.rb
class Topic < ActiveRecord::Base
def to_param
"#{id}-#{name.parameterize}"
end
end
/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 Bert
105
Level 5
Froggy Views
106
The example
LEVEL 5 Froggy Views
107
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
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
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
LEVEL 5 Froggy Views
/app/views/tweets/index.html.erb
@followers_count @recent_followers
@following_count @recent_following
<%= follow_box(
<%= follow_box(
"Followers", , ) %>
"Following", , ) %>
Followers
Following
/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
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
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
title count
recent.each do |user|
str +=
user.avatar.url(:thumb)
link_to user do
image_tag(
end
)
)
content_tag :div, :class => do
str = )
end
raw(str
+ content_tag(:span,
Annoying str variable
/app/helpers/tweets_helper.rb
Helper Skelter
113
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
title
count
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
The Example
LEVEL 5 Froggy Views
115
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
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
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
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
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
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
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
LEVEL 5 Froggy Views
empty string things
<% if @user.email.blank? %>
<% unless @user.email? %>
<% if @user.email.present? %>
<% if @user.email? %>
123
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
LEVEL 5 Froggy Views
empty string things
<%= @user.city || @user.state || "Unknown" %>
<%= @user.city.presence || @user.state.presence || "Unknown" %>
125
LEVEL 5 Froggy Views
empty string things
city is nil
undefined method `titleize' for nil:NilClass
<%= @user.city.titleize %>|| "Unknown"
126
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
The example
LEVEL 5 Froggy Views
128
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
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
end
end
, :class =>
id="tweet_<%= tweet.id %>"
130
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
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
LEVEL 5 Froggy Views
/app/controllers/tweets_controller.rb
what if all actions in the
tweet controller need the sidebar?
class TweetsController < ApplicationController
layout '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
LEVEL 5 Froggy Views
The example
134
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(",")
end
end
/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
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
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

More Related Content

Viewers also liked

Civil Society: Eliza Mngale, Tegemeo Women Group, 16th January UN Water Zarag...
Civil Society: Eliza Mngale, Tegemeo Women Group, 16th January UN Water Zarag...Civil Society: Eliza Mngale, Tegemeo Women Group, 16th January UN Water Zarag...
Civil Society: Eliza Mngale, Tegemeo Women Group, 16th January UN Water Zarag...water-decade
 
Presentation Josefina Maestu, Conference Wrap up Speech, 17th January UN Wate...
Presentation Josefina Maestu, Conference Wrap up Speech, 17th January UN Wate...Presentation Josefina Maestu, Conference Wrap up Speech, 17th January UN Wate...
Presentation Josefina Maestu, Conference Wrap up Speech, 17th January UN Wate...water-decade
 
Geometric algebra-1224338640583032-8
Geometric algebra-1224338640583032-8Geometric algebra-1224338640583032-8
Geometric algebra-1224338640583032-8Hafil Ahmad
 
Abengoa-FB Side Event – Collado, Abengoa, 14th January, 2015 UN Water Confere...
Abengoa-FB Side Event – Collado, Abengoa, 14th January, 2015 UN Water Confere...Abengoa-FB Side Event – Collado, Abengoa, 14th January, 2015 UN Water Confere...
Abengoa-FB Side Event – Collado, Abengoa, 14th January, 2015 UN Water Confere...water-decade
 
Walking home
Walking homeWalking home
Walking home42364339
 
UNECE Session: Luis Simas, ERSAR, 15th January UN Water Zaragoza Conference 2015
UNECE Session: Luis Simas, ERSAR, 15th January UN Water Zaragoza Conference 2015UNECE Session: Luis Simas, ERSAR, 15th January UN Water Zaragoza Conference 2015
UNECE Session: Luis Simas, ERSAR, 15th January UN Water Zaragoza Conference 2015water-decade
 
Presentation1 osmosis and diffusion
Presentation1 osmosis and diffusionPresentation1 osmosis and diffusion
Presentation1 osmosis and diffusionFarhana L.
 
Governments: Faraj El-Awar,UN Habitat, WCCE, 16th January UN Water Zaragoza C...
Governments: Faraj El-Awar,UN Habitat, WCCE, 16th January UN Water Zaragoza C...Governments: Faraj El-Awar,UN Habitat, WCCE, 16th January UN Water Zaragoza C...
Governments: Faraj El-Awar,UN Habitat, WCCE, 16th January UN Water Zaragoza C...water-decade
 
Academia: Richard Lawford, Morgan State University, 16th January UN Water Zar...
Academia: Richard Lawford, Morgan State University, 16th January UN Water Zar...Academia: Richard Lawford, Morgan State University, 16th January UN Water Zar...
Academia: Richard Lawford, Morgan State University, 16th January UN Water Zar...water-decade
 
Side event huma Right Water:Cristina Monge, ECODES, 14th January UN Water Con...
Side event huma Right Water:Cristina Monge, ECODES, 14th January UN Water Con...Side event huma Right Water:Cristina Monge, ECODES, 14th January UN Water Con...
Side event huma Right Water:Cristina Monge, ECODES, 14th January UN Water Con...water-decade
 
Side event WFN_ Ruth Mathews, WFN, 14th January UN Water Conference Zaragoza ...
Side event WFN_ Ruth Mathews, WFN, 14th January UN Water Conference Zaragoza ...Side event WFN_ Ruth Mathews, WFN, 14th January UN Water Conference Zaragoza ...
Side event WFN_ Ruth Mathews, WFN, 14th January UN Water Conference Zaragoza ...water-decade
 
UNECE Session: Nataliya Nikiforova, UNECE, 15th January UN Water Zaragoza Con...
UNECE Session: Nataliya Nikiforova, UNECE, 15th January UN Water Zaragoza Con...UNECE Session: Nataliya Nikiforova, UNECE, 15th January UN Water Zaragoza Con...
UNECE Session: Nataliya Nikiforova, UNECE, 15th January UN Water Zaragoza Con...water-decade
 
Presentation Moez Allaoui, Waterlex, Report on Financing, 17th January UN Wat...
Presentation Moez Allaoui, Waterlex, Report on Financing, 17th January UN Wat...Presentation Moez Allaoui, Waterlex, Report on Financing, 17th January UN Wat...
Presentation Moez Allaoui, Waterlex, Report on Financing, 17th January UN Wat...water-decade
 
Presentation Elisa Tonda, UNEP, 15th January UN Zaragoza Conference 2015
Presentation Elisa Tonda, UNEP, 15th January UN Zaragoza Conference 2015Presentation Elisa Tonda, UNEP, 15th January UN Zaragoza Conference 2015
Presentation Elisa Tonda, UNEP, 15th January UN Zaragoza Conference 2015water-decade
 
Business: Gavin Power, UN Global Compact, 16th January UN Water Zaragoza Conf...
Business: Gavin Power, UN Global Compact, 16th January UN Water Zaragoza Conf...Business: Gavin Power, UN Global Compact, 16th January UN Water Zaragoza Conf...
Business: Gavin Power, UN Global Compact, 16th January UN Water Zaragoza Conf...water-decade
 
Media: Meenakshi Shedde, Mumbai, India, 16th January UN Water Zaragoza Confer...
Media: Meenakshi Shedde, Mumbai, India, 16th January UN Water Zaragoza Confer...Media: Meenakshi Shedde, Mumbai, India, 16th January UN Water Zaragoza Confer...
Media: Meenakshi Shedde, Mumbai, India, 16th January UN Water Zaragoza Confer...water-decade
 
Academia session: Dabo Guan, University of East Anglia, 16th January UN Water...
Academia session: Dabo Guan, University of East Anglia, 16th January UN Water...Academia session: Dabo Guan, University of East Anglia, 16th January UN Water...
Academia session: Dabo Guan, University of East Anglia, 16th January UN Water...water-decade
 
Governments: Niels Vlaanderen, Dutch Ministry of Environment and infrastructu...
Governments: Niels Vlaanderen, Dutch Ministry of Environment and infrastructu...Governments: Niels Vlaanderen, Dutch Ministry of Environment and infrastructu...
Governments: Niels Vlaanderen, Dutch Ministry of Environment and infrastructu...water-decade
 
Benvinguts a cinquè
Benvinguts a cinquèBenvinguts a cinquè
Benvinguts a cinquèANTSEB
 

Viewers also liked (20)

Civil Society: Eliza Mngale, Tegemeo Women Group, 16th January UN Water Zarag...
Civil Society: Eliza Mngale, Tegemeo Women Group, 16th January UN Water Zarag...Civil Society: Eliza Mngale, Tegemeo Women Group, 16th January UN Water Zarag...
Civil Society: Eliza Mngale, Tegemeo Women Group, 16th January UN Water Zarag...
 
Presentation Josefina Maestu, Conference Wrap up Speech, 17th January UN Wate...
Presentation Josefina Maestu, Conference Wrap up Speech, 17th January UN Wate...Presentation Josefina Maestu, Conference Wrap up Speech, 17th January UN Wate...
Presentation Josefina Maestu, Conference Wrap up Speech, 17th January UN Wate...
 
Geometric algebra-1224338640583032-8
Geometric algebra-1224338640583032-8Geometric algebra-1224338640583032-8
Geometric algebra-1224338640583032-8
 
Abengoa-FB Side Event – Collado, Abengoa, 14th January, 2015 UN Water Confere...
Abengoa-FB Side Event – Collado, Abengoa, 14th January, 2015 UN Water Confere...Abengoa-FB Side Event – Collado, Abengoa, 14th January, 2015 UN Water Confere...
Abengoa-FB Side Event – Collado, Abengoa, 14th January, 2015 UN Water Confere...
 
Walking home
Walking homeWalking home
Walking home
 
UNECE Session: Luis Simas, ERSAR, 15th January UN Water Zaragoza Conference 2015
UNECE Session: Luis Simas, ERSAR, 15th January UN Water Zaragoza Conference 2015UNECE Session: Luis Simas, ERSAR, 15th January UN Water Zaragoza Conference 2015
UNECE Session: Luis Simas, ERSAR, 15th January UN Water Zaragoza Conference 2015
 
Presentation1 osmosis and diffusion
Presentation1 osmosis and diffusionPresentation1 osmosis and diffusion
Presentation1 osmosis and diffusion
 
Governments: Faraj El-Awar,UN Habitat, WCCE, 16th January UN Water Zaragoza C...
Governments: Faraj El-Awar,UN Habitat, WCCE, 16th January UN Water Zaragoza C...Governments: Faraj El-Awar,UN Habitat, WCCE, 16th January UN Water Zaragoza C...
Governments: Faraj El-Awar,UN Habitat, WCCE, 16th January UN Water Zaragoza C...
 
Academia: Richard Lawford, Morgan State University, 16th January UN Water Zar...
Academia: Richard Lawford, Morgan State University, 16th January UN Water Zar...Academia: Richard Lawford, Morgan State University, 16th January UN Water Zar...
Academia: Richard Lawford, Morgan State University, 16th January UN Water Zar...
 
Side event huma Right Water:Cristina Monge, ECODES, 14th January UN Water Con...
Side event huma Right Water:Cristina Monge, ECODES, 14th January UN Water Con...Side event huma Right Water:Cristina Monge, ECODES, 14th January UN Water Con...
Side event huma Right Water:Cristina Monge, ECODES, 14th January UN Water Con...
 
Side event WFN_ Ruth Mathews, WFN, 14th January UN Water Conference Zaragoza ...
Side event WFN_ Ruth Mathews, WFN, 14th January UN Water Conference Zaragoza ...Side event WFN_ Ruth Mathews, WFN, 14th January UN Water Conference Zaragoza ...
Side event WFN_ Ruth Mathews, WFN, 14th January UN Water Conference Zaragoza ...
 
UNECE Session: Nataliya Nikiforova, UNECE, 15th January UN Water Zaragoza Con...
UNECE Session: Nataliya Nikiforova, UNECE, 15th January UN Water Zaragoza Con...UNECE Session: Nataliya Nikiforova, UNECE, 15th January UN Water Zaragoza Con...
UNECE Session: Nataliya Nikiforova, UNECE, 15th January UN Water Zaragoza Con...
 
Presentation Moez Allaoui, Waterlex, Report on Financing, 17th January UN Wat...
Presentation Moez Allaoui, Waterlex, Report on Financing, 17th January UN Wat...Presentation Moez Allaoui, Waterlex, Report on Financing, 17th January UN Wat...
Presentation Moez Allaoui, Waterlex, Report on Financing, 17th January UN Wat...
 
Presentation Elisa Tonda, UNEP, 15th January UN Zaragoza Conference 2015
Presentation Elisa Tonda, UNEP, 15th January UN Zaragoza Conference 2015Presentation Elisa Tonda, UNEP, 15th January UN Zaragoza Conference 2015
Presentation Elisa Tonda, UNEP, 15th January UN Zaragoza Conference 2015
 
MLK Tribute
MLK TributeMLK Tribute
MLK Tribute
 
Business: Gavin Power, UN Global Compact, 16th January UN Water Zaragoza Conf...
Business: Gavin Power, UN Global Compact, 16th January UN Water Zaragoza Conf...Business: Gavin Power, UN Global Compact, 16th January UN Water Zaragoza Conf...
Business: Gavin Power, UN Global Compact, 16th January UN Water Zaragoza Conf...
 
Media: Meenakshi Shedde, Mumbai, India, 16th January UN Water Zaragoza Confer...
Media: Meenakshi Shedde, Mumbai, India, 16th January UN Water Zaragoza Confer...Media: Meenakshi Shedde, Mumbai, India, 16th January UN Water Zaragoza Confer...
Media: Meenakshi Shedde, Mumbai, India, 16th January UN Water Zaragoza Confer...
 
Academia session: Dabo Guan, University of East Anglia, 16th January UN Water...
Academia session: Dabo Guan, University of East Anglia, 16th January UN Water...Academia session: Dabo Guan, University of East Anglia, 16th January UN Water...
Academia session: Dabo Guan, University of East Anglia, 16th January UN Water...
 
Governments: Niels Vlaanderen, Dutch Ministry of Environment and infrastructu...
Governments: Niels Vlaanderen, Dutch Ministry of Environment and infrastructu...Governments: Niels Vlaanderen, Dutch Ministry of Environment and infrastructu...
Governments: Niels Vlaanderen, Dutch Ministry of Environment and infrastructu...
 
Benvinguts a cinquè
Benvinguts a cinquèBenvinguts a cinquè
Benvinguts a cinquè
 

Similar to Rails best practices_slides

Rupicon 2014 Action pack
Rupicon 2014 Action packRupicon 2014 Action pack
Rupicon 2014 Action packrupicon
 
Simple restfull app_s
Simple restfull app_sSimple restfull app_s
Simple restfull app_snetwix
 
Código Saudável => Programador Feliz - Rs on Rails 2010
Código Saudável => Programador Feliz - Rs on Rails 2010Código Saudável => Programador Feliz - Rs on Rails 2010
Código Saudável => Programador Feliz - Rs on Rails 2010Plataformatec
 
Ruby/Rails
Ruby/RailsRuby/Rails
Ruby/Railsrstankov
 
Rails World 2023: Powerful Rails Features You Might Not Know
Rails World 2023: Powerful Rails Features You Might Not KnowRails World 2023: Powerful Rails Features You Might Not Know
Rails World 2023: Powerful Rails Features You Might Not KnowChris Oliver
 
Simplify Your Rails Controllers With a Vengeance
Simplify Your Rails Controllers With a VengeanceSimplify Your Rails Controllers With a Vengeance
Simplify Your Rails Controllers With a Vengeancebrianauton
 
Ruby on rails
Ruby on rails Ruby on rails
Ruby on rails Mohit Jain
 
OSDC 2009 Rails Turtorial
OSDC 2009 Rails TurtorialOSDC 2009 Rails Turtorial
OSDC 2009 Rails TurtorialYi-Ting Cheng
 
More to RoC weibo
More to RoC weiboMore to RoC weibo
More to RoC weiboshaokun
 
Ruby on Rails at PROMPT ISEL '11
Ruby on Rails at PROMPT ISEL '11Ruby on Rails at PROMPT ISEL '11
Ruby on Rails at PROMPT ISEL '11Pedro Cunha
 
Desenvolvimento web com Ruby on Rails (parte 4)
Desenvolvimento web com Ruby on Rails (parte 4)Desenvolvimento web com Ruby on Rails (parte 4)
Desenvolvimento web com Ruby on Rails (parte 4)Joao Lucas Santana
 
Ruby on Rails : RESTful 和 Ajax
Ruby on Rails : RESTful 和 AjaxRuby on Rails : RESTful 和 Ajax
Ruby on Rails : RESTful 和 AjaxWen-Tien Chang
 
Como programar un blog REST
Como programar un blog RESTComo programar un blog REST
Como programar un blog RESTJavier Vidal
 
Testing Legacy Rails Apps
Testing Legacy Rails AppsTesting Legacy Rails Apps
Testing Legacy Rails AppsRabble .
 
Rails3ハンズオン資料
Rails3ハンズオン資料Rails3ハンズオン資料
Rails3ハンズオン資料Shinsaku Chikura
 
Resource and view
Resource and viewResource and view
Resource and viewPapp Laszlo
 
Advanced RESTful Rails
Advanced RESTful RailsAdvanced RESTful Rails
Advanced RESTful RailsViget Labs
 
Advanced RESTful Rails
Advanced RESTful RailsAdvanced RESTful Rails
Advanced RESTful RailsBen Scofield
 

Similar to Rails best practices_slides (20)

Rupicon 2014 Action pack
Rupicon 2014 Action packRupicon 2014 Action pack
Rupicon 2014 Action pack
 
Simple restfull app_s
Simple restfull app_sSimple restfull app_s
Simple restfull app_s
 
Código Saudável => Programador Feliz - Rs on Rails 2010
Código Saudável => Programador Feliz - Rs on Rails 2010Código Saudável => Programador Feliz - Rs on Rails 2010
Código Saudável => Programador Feliz - Rs on Rails 2010
 
Ruby/Rails
Ruby/RailsRuby/Rails
Ruby/Rails
 
Rails World 2023: Powerful Rails Features You Might Not Know
Rails World 2023: Powerful Rails Features You Might Not KnowRails World 2023: Powerful Rails Features You Might Not Know
Rails World 2023: Powerful Rails Features You Might Not Know
 
Simplify Your Rails Controllers With a Vengeance
Simplify Your Rails Controllers With a VengeanceSimplify Your Rails Controllers With a Vengeance
Simplify Your Rails Controllers With a Vengeance
 
Ruby on rails
Ruby on rails Ruby on rails
Ruby on rails
 
OSDC 2009 Rails Turtorial
OSDC 2009 Rails TurtorialOSDC 2009 Rails Turtorial
OSDC 2009 Rails Turtorial
 
More to RoC weibo
More to RoC weiboMore to RoC weibo
More to RoC weibo
 
Ruby on Rails at PROMPT ISEL '11
Ruby on Rails at PROMPT ISEL '11Ruby on Rails at PROMPT ISEL '11
Ruby on Rails at PROMPT ISEL '11
 
Desenvolvimento web com Ruby on Rails (parte 4)
Desenvolvimento web com Ruby on Rails (parte 4)Desenvolvimento web com Ruby on Rails (parte 4)
Desenvolvimento web com Ruby on Rails (parte 4)
 
Ruby on Rails : RESTful 和 Ajax
Ruby on Rails : RESTful 和 AjaxRuby on Rails : RESTful 和 Ajax
Ruby on Rails : RESTful 和 Ajax
 
Como programar un blog REST
Como programar un blog RESTComo programar un blog REST
Como programar un blog REST
 
Testing Legacy Rails Apps
Testing Legacy Rails AppsTesting Legacy Rails Apps
Testing Legacy Rails Apps
 
Why ruby
Why rubyWhy ruby
Why ruby
 
Rails3ハンズオン資料
Rails3ハンズオン資料Rails3ハンズオン資料
Rails3ハンズオン資料
 
Resource and view
Resource and viewResource and view
Resource and view
 
Advanced RESTful Rails
Advanced RESTful RailsAdvanced RESTful Rails
Advanced RESTful Rails
 
Advanced RESTful Rails
Advanced RESTful RailsAdvanced RESTful Rails
Advanced RESTful Rails
 
Laravel tips-2019-04
Laravel tips-2019-04Laravel tips-2019-04
Laravel tips-2019-04
 

Recently uploaded

Visit to a blind student's school🧑‍🦯🧑‍🦯(community medicine)
Visit to a blind student's school🧑‍🦯🧑‍🦯(community medicine)Visit to a blind student's school🧑‍🦯🧑‍🦯(community medicine)
Visit to a blind student's school🧑‍🦯🧑‍🦯(community medicine)lakshayb543
 
Choosing the Right CBSE School A Comprehensive Guide for Parents
Choosing the Right CBSE School A Comprehensive Guide for ParentsChoosing the Right CBSE School A Comprehensive Guide for Parents
Choosing the Right CBSE School A Comprehensive Guide for Parentsnavabharathschool99
 
GRADE 4 - SUMMATIVE TEST QUARTER 4 ALL SUBJECTS
GRADE 4 - SUMMATIVE TEST QUARTER 4 ALL SUBJECTSGRADE 4 - SUMMATIVE TEST QUARTER 4 ALL SUBJECTS
GRADE 4 - SUMMATIVE TEST QUARTER 4 ALL SUBJECTSJoshuaGantuangco2
 
Procuring digital preservation CAN be quick and painless with our new dynamic...
Procuring digital preservation CAN be quick and painless with our new dynamic...Procuring digital preservation CAN be quick and painless with our new dynamic...
Procuring digital preservation CAN be quick and painless with our new dynamic...Jisc
 
USPS® Forced Meter Migration - How to Know if Your Postage Meter Will Soon be...
USPS® Forced Meter Migration - How to Know if Your Postage Meter Will Soon be...USPS® Forced Meter Migration - How to Know if Your Postage Meter Will Soon be...
USPS® Forced Meter Migration - How to Know if Your Postage Meter Will Soon be...Postal Advocate Inc.
 
Barangay Council for the Protection of Children (BCPC) Orientation.pptx
Barangay Council for the Protection of Children (BCPC) Orientation.pptxBarangay Council for the Protection of Children (BCPC) Orientation.pptx
Barangay Council for the Protection of Children (BCPC) Orientation.pptxCarlos105
 
Student Profile Sample - We help schools to connect the data they have, with ...
Student Profile Sample - We help schools to connect the data they have, with ...Student Profile Sample - We help schools to connect the data they have, with ...
Student Profile Sample - We help schools to connect the data they have, with ...Seán Kennedy
 
MULTIDISCIPLINRY NATURE OF THE ENVIRONMENTAL STUDIES.pptx
MULTIDISCIPLINRY NATURE OF THE ENVIRONMENTAL STUDIES.pptxMULTIDISCIPLINRY NATURE OF THE ENVIRONMENTAL STUDIES.pptx
MULTIDISCIPLINRY NATURE OF THE ENVIRONMENTAL STUDIES.pptxAnupkumar Sharma
 
HỌC TỐT TIẾNG ANH 11 THEO CHƯƠNG TRÌNH GLOBAL SUCCESS ĐÁP ÁN CHI TIẾT - CẢ NĂ...
HỌC TỐT TIẾNG ANH 11 THEO CHƯƠNG TRÌNH GLOBAL SUCCESS ĐÁP ÁN CHI TIẾT - CẢ NĂ...HỌC TỐT TIẾNG ANH 11 THEO CHƯƠNG TRÌNH GLOBAL SUCCESS ĐÁP ÁN CHI TIẾT - CẢ NĂ...
HỌC TỐT TIẾNG ANH 11 THEO CHƯƠNG TRÌNH GLOBAL SUCCESS ĐÁP ÁN CHI TIẾT - CẢ NĂ...Nguyen Thanh Tu Collection
 
Field Attribute Index Feature in Odoo 17
Field Attribute Index Feature in Odoo 17Field Attribute Index Feature in Odoo 17
Field Attribute Index Feature in Odoo 17Celine George
 
Transaction Management in Database Management System
Transaction Management in Database Management SystemTransaction Management in Database Management System
Transaction Management in Database Management SystemChristalin Nelson
 
THEORIES OF ORGANIZATION-PUBLIC ADMINISTRATION
THEORIES OF ORGANIZATION-PUBLIC ADMINISTRATIONTHEORIES OF ORGANIZATION-PUBLIC ADMINISTRATION
THEORIES OF ORGANIZATION-PUBLIC ADMINISTRATIONHumphrey A Beña
 
Karra SKD Conference Presentation Revised.pptx
Karra SKD Conference Presentation Revised.pptxKarra SKD Conference Presentation Revised.pptx
Karra SKD Conference Presentation Revised.pptxAshokKarra1
 
ISYU TUNGKOL SA SEKSWLADIDA (ISSUE ABOUT SEXUALITY
ISYU TUNGKOL SA SEKSWLADIDA (ISSUE ABOUT SEXUALITYISYU TUNGKOL SA SEKSWLADIDA (ISSUE ABOUT SEXUALITY
ISYU TUNGKOL SA SEKSWLADIDA (ISSUE ABOUT SEXUALITYKayeClaireEstoconing
 
ENGLISH6-Q4-W3.pptxqurter our high choom
ENGLISH6-Q4-W3.pptxqurter our high choomENGLISH6-Q4-W3.pptxqurter our high choom
ENGLISH6-Q4-W3.pptxqurter our high choomnelietumpap1
 
FILIPINO PSYCHology sikolohiyang pilipino
FILIPINO PSYCHology sikolohiyang pilipinoFILIPINO PSYCHology sikolohiyang pilipino
FILIPINO PSYCHology sikolohiyang pilipinojohnmickonozaleda
 

Recently uploaded (20)

Visit to a blind student's school🧑‍🦯🧑‍🦯(community medicine)
Visit to a blind student's school🧑‍🦯🧑‍🦯(community medicine)Visit to a blind student's school🧑‍🦯🧑‍🦯(community medicine)
Visit to a blind student's school🧑‍🦯🧑‍🦯(community medicine)
 
Choosing the Right CBSE School A Comprehensive Guide for Parents
Choosing the Right CBSE School A Comprehensive Guide for ParentsChoosing the Right CBSE School A Comprehensive Guide for Parents
Choosing the Right CBSE School A Comprehensive Guide for Parents
 
GRADE 4 - SUMMATIVE TEST QUARTER 4 ALL SUBJECTS
GRADE 4 - SUMMATIVE TEST QUARTER 4 ALL SUBJECTSGRADE 4 - SUMMATIVE TEST QUARTER 4 ALL SUBJECTS
GRADE 4 - SUMMATIVE TEST QUARTER 4 ALL SUBJECTS
 
Procuring digital preservation CAN be quick and painless with our new dynamic...
Procuring digital preservation CAN be quick and painless with our new dynamic...Procuring digital preservation CAN be quick and painless with our new dynamic...
Procuring digital preservation CAN be quick and painless with our new dynamic...
 
Raw materials used in Herbal Cosmetics.pptx
Raw materials used in Herbal Cosmetics.pptxRaw materials used in Herbal Cosmetics.pptx
Raw materials used in Herbal Cosmetics.pptx
 
USPS® Forced Meter Migration - How to Know if Your Postage Meter Will Soon be...
USPS® Forced Meter Migration - How to Know if Your Postage Meter Will Soon be...USPS® Forced Meter Migration - How to Know if Your Postage Meter Will Soon be...
USPS® Forced Meter Migration - How to Know if Your Postage Meter Will Soon be...
 
Barangay Council for the Protection of Children (BCPC) Orientation.pptx
Barangay Council for the Protection of Children (BCPC) Orientation.pptxBarangay Council for the Protection of Children (BCPC) Orientation.pptx
Barangay Council for the Protection of Children (BCPC) Orientation.pptx
 
Student Profile Sample - We help schools to connect the data they have, with ...
Student Profile Sample - We help schools to connect the data they have, with ...Student Profile Sample - We help schools to connect the data they have, with ...
Student Profile Sample - We help schools to connect the data they have, with ...
 
Model Call Girl in Tilak Nagar Delhi reach out to us at 🔝9953056974🔝
Model Call Girl in Tilak Nagar Delhi reach out to us at 🔝9953056974🔝Model Call Girl in Tilak Nagar Delhi reach out to us at 🔝9953056974🔝
Model Call Girl in Tilak Nagar Delhi reach out to us at 🔝9953056974🔝
 
MULTIDISCIPLINRY NATURE OF THE ENVIRONMENTAL STUDIES.pptx
MULTIDISCIPLINRY NATURE OF THE ENVIRONMENTAL STUDIES.pptxMULTIDISCIPLINRY NATURE OF THE ENVIRONMENTAL STUDIES.pptx
MULTIDISCIPLINRY NATURE OF THE ENVIRONMENTAL STUDIES.pptx
 
HỌC TỐT TIẾNG ANH 11 THEO CHƯƠNG TRÌNH GLOBAL SUCCESS ĐÁP ÁN CHI TIẾT - CẢ NĂ...
HỌC TỐT TIẾNG ANH 11 THEO CHƯƠNG TRÌNH GLOBAL SUCCESS ĐÁP ÁN CHI TIẾT - CẢ NĂ...HỌC TỐT TIẾNG ANH 11 THEO CHƯƠNG TRÌNH GLOBAL SUCCESS ĐÁP ÁN CHI TIẾT - CẢ NĂ...
HỌC TỐT TIẾNG ANH 11 THEO CHƯƠNG TRÌNH GLOBAL SUCCESS ĐÁP ÁN CHI TIẾT - CẢ NĂ...
 
LEFT_ON_C'N_ PRELIMS_EL_DORADO_2024.pptx
LEFT_ON_C'N_ PRELIMS_EL_DORADO_2024.pptxLEFT_ON_C'N_ PRELIMS_EL_DORADO_2024.pptx
LEFT_ON_C'N_ PRELIMS_EL_DORADO_2024.pptx
 
Field Attribute Index Feature in Odoo 17
Field Attribute Index Feature in Odoo 17Field Attribute Index Feature in Odoo 17
Field Attribute Index Feature in Odoo 17
 
Transaction Management in Database Management System
Transaction Management in Database Management SystemTransaction Management in Database Management System
Transaction Management in Database Management System
 
THEORIES OF ORGANIZATION-PUBLIC ADMINISTRATION
THEORIES OF ORGANIZATION-PUBLIC ADMINISTRATIONTHEORIES OF ORGANIZATION-PUBLIC ADMINISTRATION
THEORIES OF ORGANIZATION-PUBLIC ADMINISTRATION
 
FINALS_OF_LEFT_ON_C'N_EL_DORADO_2024.pptx
FINALS_OF_LEFT_ON_C'N_EL_DORADO_2024.pptxFINALS_OF_LEFT_ON_C'N_EL_DORADO_2024.pptx
FINALS_OF_LEFT_ON_C'N_EL_DORADO_2024.pptx
 
Karra SKD Conference Presentation Revised.pptx
Karra SKD Conference Presentation Revised.pptxKarra SKD Conference Presentation Revised.pptx
Karra SKD Conference Presentation Revised.pptx
 
ISYU TUNGKOL SA SEKSWLADIDA (ISSUE ABOUT SEXUALITY
ISYU TUNGKOL SA SEKSWLADIDA (ISSUE ABOUT SEXUALITYISYU TUNGKOL SA SEKSWLADIDA (ISSUE ABOUT SEXUALITY
ISYU TUNGKOL SA SEKSWLADIDA (ISSUE ABOUT SEXUALITY
 
ENGLISH6-Q4-W3.pptxqurter our high choom
ENGLISH6-Q4-W3.pptxqurter our high choomENGLISH6-Q4-W3.pptxqurter our high choom
ENGLISH6-Q4-W3.pptxqurter our high choom
 
FILIPINO PSYCHology sikolohiyang pilipino
FILIPINO PSYCHology sikolohiyang pilipinoFILIPINO PSYCHology sikolohiyang pilipino
FILIPINO PSYCHology sikolohiyang pilipino
 

Rails best practices_slides

  • 1. 1
  • 3. level 1 sad Code Happy Code controllers In space 3
  • 4. LEVEL 1 controllers In Space Example 4
  • 5. 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 end end end else 5
  • 6. 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 end end end else ... tweet.retweet_by(current_user) /app/models/tweet.rb class Tweet < ActiveRecord::Base def retweet_by(retweeter) end end retweeter.id).present? if 6
  • 7. @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
  • 8. 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.ago order 'mentions desc' limit 5 where( ). ( ). ( ) order limit current_user def index @tweets = 8
  • 9. LEVEL 1 controllers In Space Scope it out /app/controllers/tweets_controller.rb ... end ( ). ( ) 'created_at desc'order limit 10 current_user def index @tweets = .tweets. Scope to the user 9
  • 10. LEVEL 1 controllers In Space Scope it out /app/controllers/tweets_controller.rb ... end ( ( ) 'created_at desc'order limit 10current_user def index @tweets = .tweets. /app/models/tweet.rb ... end ) recent. scope class Tweet < ActiveRecord::Base :recent, 10
  • 11. default_ LEVEL 1 controllers In Space Scope it out /app/controllers/tweets_controller.rb ... end ( ( ) 'created_at desc' limit 10current_user def index @tweets = .tweets. /app/models/tweet.rb ... end )scope class Tweet < ActiveRecord::Base order 11
  • 12. limit(5) LEVEL 1 controllers In Space Scope it out /app/controllers/tweets_controller.rb ... end /app/models/topic.rb ... end scope class Topic < ActiveRecord::Base :trending, def index @trending = Topic. 'started_trending > ?', 1.day.ago order '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
  • 13. limit(5) LEVEL 1 controllers In Space Scope it out /app/controllers/tweets_controller.rb ... end /app/models/topic.rb ... end scope class Topic < ActiveRecord::Base :trending, def index @trending = Topic. 'started_trending > ?', 1.day.ago order 'mentions desc' where( ( trending ) ).lambda { } . 13
  • 14. ||num LEVEL 1 controllers In Space Scope it out /app/controllers/tweets_controller.rb ... end /app/models/topic.rb ... end scope class Topic < ActiveRecord::Base :trending, def index @trending = Topic. 'started_trending > ?', 1.day.ago order 'mentions desc' where( ( ) ).lambda { } . limit(num) (5)trending @trending = Topic.trending(5) @trending = Topic.trending wrong number of args, 0 for 1 14
  • 15. |num LEVEL 1 controllers In Space Scope it out /app/controllers/tweets_controller.rb ... end /app/models/topic.rb ... end scope class Topic < ActiveRecord::Base :trending, def index @trending = Topic. 'started_trending > ?', 1.day.ago order 'mentions desc' where( ( ) ).lambda { } . limit(num) (5)trending @trending = Topic.trending(5) @trending = Topic.trending |= nil Ruby 1.9 FTW! 15
  • 16. default_ LEVEL 1 controllers In Space Scope it out ('created_at desc' /app/models/tweet.rb ... end )scope class 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
  • 17. LEVEL 1 controllers In Space Scope it out /app/controllers/tweets_controller.rb t = Tweet.new t.status = "RT #{@tweet.user.name}: #{@tweet.status}" t.original_tweet = @tweet current_user has many tweets.... t.user = current_user t.save current_user "RT #{@tweet.user.name}: #{@tweet.status}"status original_tweet @tweet : : => ) ,=> .tweets.create( 17
  • 18. 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
  • 19. 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
  • 20. LEVEL 1 controllers In Space @tweet = Tweet.find(params[:id]) /app/controllers/tweets_controller.rb class TweetsController < ApplicationController end def edit def update def destroy end 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
  • 21. @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
  • 22. LEVEL 1 controllers In Space What should they be used for? authorization fantastic filters Logging wizards 22
  • 23. LEVEL 1 controllers In Space fantastic filters /app/controllers/tweets_controller.rb class TweetsController < ApplicationController before_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
  • 25. LEVEL 1 CONTROLLING YOUR CONTROLLERS Example 25
  • 26. /app/models/user.rb class User < ActiveRecord::Base has_one :account_setting, :dependent => :destroy end <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 command 26
  • 27. 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 command 27
  • 28. 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 command 28
  • 29. /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 command 29
  • 30. Models without the database LEVEL 2 controller command 30
  • 31. /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 command 31
  • 32. /app/controllers/contact_us_controller.rb Models without the database class ContactUsController < ApplicationController def new end def send_email ).deliver flash[:notice] = "Email sent, we'll get back to you" redirect_to root_path end end end 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' else Notifications.contact_us( LEVEL 2 controller command 32
  • 33. @contact_form.valid?! Models without the database class ContactUsController < ApplicationController def new end def send_email ).deliver end end end 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 LEVEL 2 controller command 33
  • 34. @contact_form.valid? Models without the database class ContactUsController < ApplicationController def new end def send_email ).deliver end end end 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 command 34
  • 35. @contact_form.valid? Models without the database class ContactUsController < ApplicationController def new end def send_email ).deliver end end end 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 command 35
  • 36. render @contact_form.valid? Models without the database class ContactUsController < ApplicationController def new end def send_email ).deliver end end end 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 command 36
  • 37. /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 command 37
  • 38. /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::Validations include 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 command 38
  • 39. Models without the database LEVEL 2 controller command 39
  • 40. 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 command 40
  • 41. /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 command 41
  • 42. 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 command 42
  • 43. Enter the Presenters LEVEL 2 controller command 43
  • 44. 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 command 44
  • 45. 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 command 45
  • 46. 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 command 46
  • 47. 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) else Trend.filter_by(@user. @user. def recent_tweet @user end def trends LEVEL 2 controller command 47
  • 48. 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.first def recent_tweet end Two objects! @user LEVEL 2 controller command 48
  • 49. Enter the Presenters /app/presenters/tweets/index_presenter.rb def initialize(user) @user = user end class Tweets::IndexPresenter @recent_tweet ||= .tweets.first def 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 command 49
  • 50. Enter the Presenters /app/presenters/tweets/index_presenter.rb def initialize(user) @user = user end class Tweets::IndexPresenter .tweets.first def 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 command 50
  • 51. Memoization one is better than the other value is not stored if false or nil is returned ||= extend ActiveSupport::Memoizable memoize :recent_tweet, :followers_tweet, ... def expensive(num) # lots of processing end memoize :expensive expensive(2) expensive(4) expensive(2) expensive(4) loaded from cache LEVEL 2 controller command 51
  • 52. 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 command 52
  • 53. /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 command 53
  • 54. /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 command 54
  • 56. 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 end end 56
  • 57. 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 end end 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
  • 58. 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
  • 59. LEVEL 3 Model mayhem 59
  • 60. LEVEL 3 Model mayhem 60
  • 61. LEVEL 3 Model mayhem $ curl -d "user[login]=hacked&user[is_admin]=true&user[password] =password&user[password_confirmation]=password&user[email]=hacked@by.me" http://url_not_shown/users user[is_admin]=true 61
  • 62. protecting your attributes /app/models/user.rb class User < ActiveRecord::Base attr_protected :is_admin end /app/models/user.rb class User < ActiveRecord::Base attr_accessible :email, :password, :password_confirmation end whitelists are better for security LEVEL 3 Model mayhem 62
  • 63. 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 mayhem 63
  • 64. 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 end end /db/migrate/20110119150620_add_default_5me_zone_account_se7ngs.rb LEVEL 3 Model mayhem 64
  • 65. 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
  • 66. 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
  • 67. 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::Base TRENDING_PERIOD = TRENDING_PERIOD 67
  • 68. Proper use of callbacks LEVEL 3 Model mayhem Crea;ng  an  object before_validation after_validation before_save after_save before_create around_create after_create Upda;ng  an  object Dele;ng  an  object before_validation after_validation before_save after_save before_update around_update after_update before_destroy after_destroy around_destroy 68
  • 69. Rails date helpers LEVEL 3 Model mayhem Date  Helpers 1.minute 2.hour 3.days 4.week 5.months 6.year Modifiers More  Modifiers beginning_of_day beginning_of_week beginning_of_month beginning_of_quarter beginning_of_year 2.weeks.ago 3.weeks.from_now next_week next_month next_year * singular or plural * end can be used * prev can be used 69
  • 70. Proper use of callbacks LEVEL 3 Model mayhem 70
  • 71. Proper use of callbacks LEVEL 3 Model mayhem /app/models/following.rb class Following < ActiveRecord::Base after_create :send_follower_notification def send_follower_notification if queue_new_follower_email end end end self.followed_user.receive_emails? 71
  • 72. 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 end end :if => :followed_can_receive_emails? queue_new_follower_email, 72
  • 73. 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
  • 74. improved validation LEVEL 3 Model mayhem .errors.add(:name, 'is inappropriate') selfunless ContentModerator.is_suitable?( .name) self end /app/models/topic.rb end end def validate class Topic < ActiveRecord::Base 74
  • 75. improved validation LEVEL 3 Model mayhem .errors.add(:name, 'is inappropriate') /app/models/topic.rb end end class Topic < ActiveRecord::Base validate :appropriate_content private def appropriate_content selfunless ContentModerator.is_suitable?( .name) self end 75
  • 76. improved validation LEVEL 3 Model mayhem /app/models/topic.rb end end class Topic < ActiveRecord::Base validates :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) end record attribute Don’t forget to require this /lib  isn’t  auto-­‐loaded  by  default 76
  • 77. 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 end end Topic.create( Topic.create( Topic.create( ) ):name => "Ruby5", :mentions => 2312 :name => "Top Ruby Jobs", :mentions => 231 :name => "Rails for Zombies", :mentions => 1023) 77
  • 78. 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 :mentions end /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
  • 79. 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] end end 79
  • 80. 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] end end 80
  • 81. 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] end end t.save! 81
  • 83. 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 end end => "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 Bert 83
  • 84. 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 end end .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/bullet Bullet gem To find all your n+1 queries LEVEL 4 Model Bert 84
  • 85. 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 ReTweets 1 ReTweets 0 ReTweets Bad English LEVEL 4 Model Bert 85
  • 86. 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 ReTweets 1 ReTweet 0 ReTweets LEVEL 4 Model Bert 86
  • 87. 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=X 2. Populate an array of tweet objects 3. Call length on that array For Each tweet Lots of unneeded objects pluralize( LEVEL 4 Model Bert 87
  • 88. 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=X 2. do a count query for retweets For Each tweet possibly 10+ count queries LEVEL 4 Model Bert 88
  • 89. 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=X For Each tweet There is no step 2 with  counter_cache 89
  • 90. 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_id end 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 Bert 90
  • 91. counter_cache Money true:counter_cache => /app/models/tweet.rb has_many :retweets, :class_name => 'Tweet', :foreign_key => :tweet_id end 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 Bert 91
  • 92. counter_cache Money :counter_cache => /app/models/tweet.rb has_many :retweets, :class_name => 'Tweet', :foreign_key => :tweet_id end 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 Bert 92
  • 93. counter_cache Money t.retweets.length pull  all  records then  calls  .length pull  all  records then  calls  .length t.retweets.count count  query count  query t.retweets.size count  query no  query look  at  cache Without Cache Counter With Cache Counter LEVEL 4 Model Bert 93
  • 94. 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 Bert 94
  • 95. 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 Bert 95
  • 96. 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 Bert 96
  • 97. Law of Demeter Each unit should have limited knowledge about other units “don’t talk to strangers” tweet user account settings X LEVEL 4 Model Bert 97
  • 98. 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 end end LEVEL 4 Model Bert 98
  • 99. 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 end end 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 Methods LEVEL 4 Model Bert 99
  • 100. 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 settings X self.user.location_on_tweets ERROR!! account_setting is nil! LEVEL 4 Model Bert 100
  • 101. 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 settings X self.user.location_on_tweets :allow_nil => true , , Returns nil when Account_Settings is missing LEVEL 4 Model Bert 101
  • 102. Head to to_s /app/models/user.rb <%= @user.display_name %> class User < ActiveRecord::Base def display_name "#{first_name} #{last_name}" end end LEVEL 4 Model Bert 102
  • 103. Head to to_s /app/models/user.rb <%= @user %> class User < ActiveRecord::Base def to_s "#{first_name} #{last_name}" end end LEVEL 4 Model Bert 103
  • 104. to_param-alama ding dong /app/models/topic.rb /post/2133 class Topic < ActiveRecord::Base def to_param "#{id}-#{name.parameterize}" end end /post/rails-best-practices SEO Friendly URLS /post/2133-rails-best-practices LEVEL 4 Model Bert 104
  • 105. to_param-alama ding dong /app/models/topic.rb class Topic < ActiveRecord::Base def to_param "#{id}-#{name.parameterize}" end end /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 Bert 105
  • 107. The example LEVEL 5 Froggy Views 107
  • 108. 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
  • 109. 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
  • 110. 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
  • 111. LEVEL 5 Froggy Views /app/views/tweets/index.html.erb @followers_count @recent_followers @following_count @recent_following <%= follow_box( <%= follow_box( "Followers", , ) %> "Following", , ) %> Followers Following /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
  • 112. 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
  • 113. 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 title count recent.each do |user| str += user.avatar.url(:thumb) link_to user do image_tag( end ) ) content_tag :div, :class => do str = ) end raw(str + content_tag(:span, Annoying str variable /app/helpers/tweets_helper.rb Helper Skelter 113
  • 114. 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 title count 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
  • 115. The Example LEVEL 5 Froggy Views 115
  • 116. 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
  • 117. 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
  • 118. 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
  • 119. 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
  • 120. 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
  • 121. 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
  • 122. 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
  • 123. LEVEL 5 Froggy Views empty string things <% if @user.email.blank? %> <% unless @user.email? %> <% if @user.email.present? %> <% if @user.email? %> 123
  • 124. 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
  • 125. LEVEL 5 Froggy Views empty string things <%= @user.city || @user.state || "Unknown" %> <%= @user.city.presence || @user.state.presence || "Unknown" %> 125
  • 126. LEVEL 5 Froggy Views empty string things city is nil undefined method `titleize' for nil:NilClass <%= @user.city.titleize %>|| "Unknown" 126
  • 127. 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
  • 128. The example LEVEL 5 Froggy Views 128
  • 129. 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
  • 130. 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 end end , :class => id="tweet_<%= tweet.id %>" 130
  • 131. 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
  • 132. 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
  • 133. LEVEL 5 Froggy Views /app/controllers/tweets_controller.rb what if all actions in the tweet controller need the sidebar? class TweetsController < ApplicationController layout '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
  • 134. LEVEL 5 Froggy Views The example 134
  • 135. 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(",") end end /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
  • 136. 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
  • 137. 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