This document discusses several Ruby on Rails best practices including maintaining DRY code, using fat models and skinny controllers, avoiding N+1 queries, preventing SQL injection, using scopes to define common query patterns, and leveraging counter caches to improve performance. Key practices emphasized include organizing application logic in models, defining common queries with scopes, sanitizing user input to prevent SQL injection, preloading associated data to reduce the number of queries, and using counter caches to optimize counting related records.
8. Fat models, skinny controllers
class TweetsController < ApplicationController
def index
if params[:search].present?
@tweets = Tweet.all
else
@tweets = Tweet.where("content LIKE ?", "%#{params[:search]}%")
end
end
end
Friday, August 31, 12
9. Fat models, skinny controllers
class TweetsController < ApplicationController
def index
if params[:search].present?
@tweets = Tweet.all
else
@tweets = Tweet.where("content LIKE ?", "%#{params[:search]}%")
end
end
end
Friday, August 31, 12
10. Fat models, skinny controllers
class TweetsController < ApplicationController
def index
if params[:search].present?
@tweets = Tweet.all
else
@tweets = Tweet.where("content LIKE ?", "%#{params[:search]}%")
end
end
end
class TweetsController < ApplicationController
def index
@tweets = Tweet.search(params)
end
end
Friday, August 31, 12
11. Fat models, skinny controllers
class TweetsController < ApplicationController
def index
if params[:search].present?
@tweets = Tweet.all
else
@tweets = Tweet.where("content LIKE ?", "%#{params[:search]}%")
end
end
end
class TweetsController < ApplicationController
def self.search(params= {})
if params[:search].present
def index
where("content LIKE ?", "%#{params[:search]}%")
@tweets = Tweet.search(params)
else
end
all
end
end end
Friday, August 31, 12
12. Fat models, skinny controllers
class TweetsController < ApplicationController
def index
if params[:search].present?
@tweets = Tweet.all
else
@tweets = Tweet.where("content LIKE ?", "%#{params[:search]}%")
end
end
end
class TweetsController < ApplicationController
def self.search(params= {})
if params[:search].present
def index
where("content LIKE ?", "%#{params[:search]}%")
@tweets = Tweet.search(params)
else
end
all
end
end end
Friday, August 31, 12
14. Scope it out
class UsersController < ApplicationController
def show
@user = User.find(params[:id])
@tweets = @user.tweets.where("retweets_count > 5").order('created_at DESC').limit(5)
end
end
Friday, August 31, 12
15. Scope it out
class UsersController < ApplicationController
def show
@user = User.find(params[:id])
@tweets = @user.tweets.where("retweets_count > 5").order('created_at DESC').limit(5)
end
end
Friday, August 31, 12
16. Scope it out
class UsersController < ApplicationController
def show
@user = User.find(params[:id])
@tweets = @user.tweets.where("retweets_count > 5").order('created_at DESC').limit(5)
end
end
Friday, August 31, 12
17. Scope it out
class UsersController < ApplicationController
def show
@user = User.find(params[:id])
@tweets = @user.tweets.where("retweets_count > 5").order('created_at DESC').limit(5)
end
end
class UsersController < ApplicationController
def show
@user = User.find(params[:id])
@tweets = @user.tweets.with_many_retweets.recent.limit(5)
end
end
Friday, August 31, 12
18. Scope it out
class UsersController < ApplicationController
def show
@user = User.find(params[:id])
@tweets = @user.tweets.where("retweets_count > 5").order('created_at DESC').limit(5)
end
end
class UsersController < ApplicationController
def show
@user = User.find(params[:id])
@tweets = @user.tweets.with_many_retweets.recent.limit(5)
end
end
Friday, August 31, 12
19. Scope it out
class UsersController < ApplicationController
def show
@user = User.find(params[:id])
@tweets = @user.tweets.where("retweets_count > 5").order('created_at DESC').limit(5)
end
end
class Tweet < ActiveRecord::Base
class UsersController < ApplicationController
attr_accessible :content, :user_id
def show
belongs_to :user
@user = User.find(params[:id])
@tweets = @user.tweets.with_many_retweets.recent.limit(5)
scope :recent, order('created_at DESC')
end
scope :with_many_retweets, where("retweets_count
> 5")
end
end
Friday, August 31, 12
20. Scope it out
class UsersController < ApplicationController
def show
@user = User.find(params[:id])
@tweets = @user.tweets.where("retweets_count > 5").order('created_at DESC').limit(5)
end
end
class Tweet < ActiveRecord::Base
class UsersController < ApplicationController
attr_accessible :content, :user_id
def show
belongs_to :user
@user = User.find(params[:id])
@tweets = @user.tweets.with_many_retweets.recent.limit(5)
scope :recent, order('created_at DESC')
end
scope :with_many_retweets, where("retweets_count
> 5")
end
end
Friday, August 31, 12
27. Model without DB && REST
#encoding: utf-8 class ContactsController < ApplicationController
class Contact def new
include ActiveModel::Validations @contact = Contact.new
include ActiveModel::Conversion end
attr_accessor :name, :email, :message def create
validates :name, :email, :company, :reason, @contact = Contact.new(params[:contact])
:message, :presence => true if @contact.valid?
ContactMailer.contact_message(@contact).deliver
def initialize(attributes = {}) redirect_to root_path, :notice => "Sweet!"
attributes.each do |name, value| else
send("#{name}=", value) render :new
end end
end end
end
def persisted?
false
end
end
REST:
Don’t be too deep - /users/1/tweets/
3/comments/2
Not using REST is ok too
Friday, August 31, 12
28. Model without DB && REST
#encoding: utf-8 class ContactsController < ApplicationController
class Contact def new
include ActiveModel::Validations @contact = Contact.new
include ActiveModel::Conversion end
attr_accessor :name, :email, :message def create
validates :name, :email, :company, :reason, @contact = Contact.new(params[:contact])
:message, :presence => true if @contact.valid?
ContactMailer.contact_message(@contact).deliver
def initialize(attributes = {}) redirect_to root_path, :notice => "Sweet!"
attributes.each do |name, value| else
send("#{name}=", value) render :new
end end
end end
end
def persisted?
false
end
end
REST:
Don’t be too deep - /users/1/tweets/
3/comments/2
Not using REST is ok too
Friday, August 31, 12
29. Model without DB && REST
#encoding: utf-8 class ContactsController < ApplicationController
class Contact def new
include ActiveModel::Validations @contact = Contact.new
include ActiveModel::Conversion end
attr_accessor :name, :email, :message def create
validates :name, :email, :company, :reason, @contact = Contact.new(params[:contact])
:message, :presence => true if @contact.valid?
ContactMailer.contact_message(@contact).deliver
def initialize(attributes = {}) redirect_to root_path, :notice => "Sweet!"
attributes.each do |name, value| else
send("#{name}=", value) render :new
end end
end end
end
def persisted?
false
end
end
REST:
Don’t be too deep - /users/1/tweets/
3/comments/2
Not using REST is ok too
Friday, August 31, 12
30. Model without DB && REST
#encoding: utf-8 class ContactsController < ApplicationController
class Contact def new
include ActiveModel::Validations @contact = Contact.new
include ActiveModel::Conversion end
attr_accessor :name, :email, :message def create
validates :name, :email, :company, :reason, @contact = Contact.new(params[:contact])
:message, :presence => true if @contact.valid?
ContactMailer.contact_message(@contact).deliver
def initialize(attributes = {}) redirect_to root_path, :notice => "Sweet!"
attributes.each do |name, value| else
send("#{name}=", value) render :new
end end
end end
end
def persisted?
false
end
end
REST:
Don’t be too deep - /users/1/tweets/
3/comments/2
Not using REST is ok too
Friday, August 31, 12
32. N+1 Queries are not cool
def recent_followers
self.followers.recent.collect {|f| f.user.name }.to_sentence
end
Friday, August 31, 12
33. N+1 Queries are not cool
def recent_followers
self.followers.recent.collect {|f| f.user.name }.to_sentence
end
Friday, August 31, 12
34. N+1 Queries are not cool
def recent_followers
self.followers.recent.collect {|f| f.user.name }.to_sentence
end
Friday, August 31, 12
35. N+1 Queries are not cool
def recent_followers
self.followers.recent.collect {|f| f.user.name }.to_sentence
end
=> “Edo, Lentes, Javi”
Friday, August 31, 12
36. N+1 Queries are not cool
def recent_followers
self.followers.recent.collect {|f| f.user.name }.to_sentence
end
=> “Edo, Lentes, Javi”
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
5 fat queries
Friday, August 31, 12
37. N+1 Queries are not cool
def recent_followers
self.followers.recent.collect {|f| f.user.name }.to_sentence
end
=> “Edo, Lentes, Javi”
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
5 fat queries
Friday, August 31, 12
39. N+1 Queries are not cool
def recent_followers
self.followers.recent.includes(:user).collect {|f| f.user.name }.to_sentence
end
Friday, August 31, 12
40. N+1 Queries are not cool
def recent_followers
self.followers.recent.includes(:user).collect {|f| f.user.name }.to_sentence
end
=> “Edo, Lentes, Javi”
Friday, August 31, 12
41. N+1 Queries are not cool
def recent_followers
self.followers.recent.includes(:user).collect {|f| f.user.name }.to_sentence
end
=> “Edo, Lentes, Javi”
Select followers where user_id = 1
Friday, August 31, 12
42. N+1 Queries are not cool
def recent_followers
self.followers.recent.includes(:user).collect {|f| f.user.name }.to_sentence
end
=> “Edo, Lentes, Javi”
Select followers where user_id = 1
Select users where user_id in (2,3,4,5)
2 queries
Friday, August 31, 12
43. N+1 Queries are not cool
def recent_followers
self.followers.recent.includes(:user).collect {|f| f.user.name }.to_sentence
end
=> “Edo, Lentes, Javi”
Select followers where user_id = 1
Select users where user_id in (2,3,4,5)
2 queries
Check out the bullet gem at:
h"ps://github.com/flyerhzm/bullet
Friday, August 31, 12
46. Counter cache FTW!
1. Select all tweets from user
<%= pluralize @user.tweets.length, 'tweet' %>
2. Populate an array of objects
3. Call length on the array
Friday, August 31, 12
47. Counter cache FTW!
1. Select all tweets from user
<%= pluralize @user.tweets.length, 'tweet' %>
2. Populate an array of objects
3. Call length on the array
Friday, August 31, 12
48. Counter cache FTW!
1. Select all tweets from user
<%= pluralize @user.tweets.length, 'tweet' %>
2. Populate an array of objects
3. Call length on the array
<%= pluralize @user.tweets.count, 'tweet' %>
Friday, August 31, 12
49. Counter cache FTW!
1. Select all tweets from user
<%= pluralize @user.tweets.length, 'tweet' %>
2. Populate an array of objects
3. Call length on the array
<%= pluralize @user.tweets.count, 'tweet' %> 1. Select all tweets from user
2. Do count query for tweets
Friday, August 31, 12
50. Counter cache FTW!
1. Select all tweets from user
<%= pluralize @user.tweets.length, 'tweet' %>
2. Populate an array of objects
3. Call length on the array
<%= pluralize @user.tweets.count, 'tweet' %> 1. Select all tweets from user
2. Do count query for tweets
Friday, August 31, 12
51. Counter cache FTW!
1. Select all tweets from user
<%= pluralize @user.tweets.length, 'tweet' %>
2. Populate an array of objects
3. Call length on the array
<%= pluralize @user.tweets.count, 'tweet' %> 1. Select all tweets from user
2. Do count query for tweets
Friday, August 31, 12
52. Counter cache FTW!
1. Select all tweets from user
<%= pluralize @user.tweets.length, 'tweet' %>
2. Populate an array of objects
3. Call length on the array
<%= pluralize @user.tweets.count, 'tweet' %> 1. Select all tweets from user
2. Do count query for tweets
<%= pluralize @user.tweets.size, 'tweet' %>
Friday, August 31, 12
53. Counter cache FTW!
1. Select all tweets from user
<%= pluralize @user.tweets.length, 'tweet' %>
2. Populate an array of objects
3. Call length on the array
<%= pluralize @user.tweets.count, 'tweet' %> 1. Select all tweets from user
2. Do count query for tweets
<%= pluralize @user.tweets.size, 'tweet' %>
Friday, August 31, 12
54. Counter cache FTW!
1. Select all tweets from user
<%= pluralize @user.tweets.length, 'tweet' %>
2. Populate an array of objects
3. Call length on the array
<%= pluralize @user.tweets.count, 'tweet' %> 1. Select all tweets from user
2. Do count query for tweets
<%= pluralize @user.tweets.size, 'tweet' %> 1. Select all tweets from user
Friday, August 31, 12
55. Counter cache FTW!
1. Select all tweets from user
<%= pluralize @user.tweets.length, 'tweet' %>
2. Populate an array of objects
3. Call length on the array
<%= pluralize @user.tweets.count, 'tweet' %> 1. Select all tweets from user
2. Do count query for tweets
<%= pluralize @user.tweets.size, 'tweet' %> 1. Select all tweets from user
Friday, August 31, 12
56. Counter cache FTW!
1. Select all tweets from user
<%= pluralize @user.tweets.length, 'tweet' %>
2. Populate an array of objects
3. Call length on the array
<%= pluralize @user.tweets.count, 'tweet' %> 1. Select all tweets from user
2. Do count query for tweets
<%= pluralize @user.tweets.size, 'tweet' %> 1. Select all tweets from user
belongs_to :user, :counter_cache => true
Friday, August 31, 12
57. Counter cache FTW!
1. Select all tweets from user
<%= pluralize @user.tweets.length, 'tweet' %>
2. Populate an array of objects
3. Call length on the array
<%= pluralize @user.tweets.count, 'tweet' %> 1. Select all tweets from user
2. Do count query for tweets
<%= pluralize @user.tweets.size, 'tweet' %> 1. Select all tweets from user
def self.up
belongs_to :user, :counter_cache => true add_column :users, :tweets_count, :integer, :default => 0
end
def self.down
remove_column :users, :tweets_count
end
Friday, August 31, 12