Vous êtes sur la page 1sur 47

Ruby on Rails Pitfall

Or just stupid mistakes we made

Robin Lu
IN-SRC Studio
robinlu@in-src.com
RubyConfChina2009

Friday, May 22, 2009


IN-SRC Studio

• http://www.in-src.com
• Team behind Caibangzi.com
• Full stack Ruby On Rails Development
• Projects from Pepboys,Vitality, Healthwise...

Friday, May 22, 2009


‘and’ or ‘&&’
What does this mean?
result = func(arg) and render(:text => result)

Friday, May 22, 2009


‘and’ or ‘&&’
What does this mean?
result = func(arg) and render(:text => result)

Why not this?


result = func(arg) && render(:text => result)

Friday, May 22, 2009


‘and’ or ‘&&’
What does this mean?
result = func(arg) and render(:text => result)

Why not this?


result = func(arg) && render(:text => result)

Be aware of the operator precedence

Friday, May 22, 2009


strip_tags

Display user input text without tags

What we did:

Friday, May 22, 2009


strip_tags
When
text =
‘<img title="http://example.com/x.js?" src="#"’

the page becomes:

<p> <img title="http://example.com/x.js?" src="#" </p>

Friday, May 22, 2009


strip_tags

strip_tags is not safe by itself

h strip_tags(text)

Friday, May 22, 2009


cache
class Blog1Controller < ApplicationController
def list
unless read_fragment(:action => 'list')
@articles = Article.find_recent Controller
end
end
end

<% cache do %>


<ul>
<% for article in @articles -%>
<li><p><%= h(article.body) %></p></li> list.html.erb
<% end -%>
</ul>
<% end %>
Friday, May 22, 2009
cache
Result:
sometime got crash due to
uninitialized @articles

Friday, May 22, 2009


cache
article list

Friday, May 22, 2009


cache
article list

check cache

Friday, May 22, 2009


cache
article list

check cache list

Friday, May 22, 2009


cache
article list

check cache list

render

Friday, May 22, 2009


cache
article list article new

check cache list

render

Friday, May 22, 2009


cache
article list article new

check cache list expire cache

render

Friday, May 22, 2009


cache
article list article new

check cache list expire cache

render

Friday, May 22, 2009


cache
article list article new

check cache list expire cache

render

check cache

Friday, May 22, 2009


cache
article list article new

check cache list expire cache

render

check cache

crashed by non-init
@articles
Friday, May 22, 2009
cache
Solutions?

• defensive: handle the exception


• postpone init of @articles
• update caches instead of expiring them
none of them is perfect

Friday, May 22, 2009


object id

Friday, May 22, 2009


object id
Check nil? everywhere?

Friday, May 22, 2009


object id
config.whiny_nil = true

Friday, May 22, 2009


validate_uniqueness_of

Friday, May 22, 2009


validate_uniqueness_of
We always get errors like this:

A ActiveRecord::StatementInvalid occurred in
fund#add_watch_fund:

 Mysql::Error: Duplicate entry '1234-271' for key 2:


INSERT INTO `watch_funds` (`account_id`,
`position`, `fund_id`, `created_at`) VALUES(1234, 19,
271, '2009-05-06 19:13:50')

Friday, May 22, 2009


validate_uniqueness_of
Process A
Process B

Friday, May 22, 2009


validate_uniqueness_of
Process A
Process B

unique?

Friday, May 22, 2009


validate_uniqueness_of
Process A
Process B

unique? select ....

Friday, May 22, 2009


validate_uniqueness_of
Process A
Process B

unique? select ....


unique?

Friday, May 22, 2009


validate_uniqueness_of
Process A
Process B

unique? select ....


unique?

Insert

Friday, May 22, 2009


validate_uniqueness_of
Process A
Process B

unique? select ....


unique?

Insert
Insert

Friday, May 22, 2009


validate_uniqueness_of
Process A
Process B

unique? select ....


unique?

Insert
Insert

crash!

Friday, May 22, 2009


validate_uniqueness_of

validate_uniqueness_of may not


guarantee the uniqueness

use your own lock if the uniqueness is


critical to you.

Friday, May 22, 2009


conditions
Background:
• category has many subcategories
• subcategory has many posts
• post belongs to subcategory
we need to select all posts in a category.

Friday, May 22, 2009


conditions
What we did:
named_scope :in_category, lambda { |cat|
conditions = [cat.subcategories.map {|subcat|
'posts.subcategory_id = ?'
}.join(" OR ")]
cat.subcategories.each {|subcat|
conditions << subcat.id }
{:conditions => conditions}
}

Friday, May 22, 2009


conditions
Result:
we get all posts when a category has no
subcategories

Friday, May 22, 2009


conditions
When category has no subcategory
named_scope :in_category, lambda { |cat|
conditions = [cat.subcategories.map {|subcat|
'posts.subcategory_id = ?'
}.join(" OR ")]
cat.subcategories.each {|subcat|
conditions << subcat.id }
{:conditions => conditions}
}

Friday, May 22, 2009


conditions

When you compose conditions, be


aware that sometime nothing to
compose means
the conditions should match nothing,
not the conditions should be empty.

Friday, May 22, 2009


before_create
set a flag if the author of the post is an admin

What we did:

Friday, May 22, 2009


before_create
Result:

Only post by admin can be saved

Friday, May 22, 2009


before_create

All these callbacks are Filters


Be careful not to break the filter chain by
what you return from the filters!

Friday, May 22, 2009


after_create
send a mail whenever a new record is created

What we did:

Friday, May 22, 2009


after_create
Result:
sometime the record save failed but we
still get mail notification

Friday, May 22, 2009


after_create
before_create begin
...
create ...
commit
after_create

all in one transaction all the steps between this


should be transactional

Friday, May 22, 2009


after_create
What are non-transactional actions?
• send a mail
• delete a file
• expire a cache

Friday, May 22, 2009


after_create

• try not put non-transaction actions into


transactions.
• after_commit
• in controller

Friday, May 22, 2009


Thanks!

Friday, May 22, 2009