Ruby on Rails Pitfall

47
Ruby on Rails Pitfall Or just stupid mistakes we made Robin Lu IN-SRC Studio [email protected] RubyConfChina2009 Friday, May 22, 2009

description

This is a presentation by IN-SRC.com at Ruby Conference China 2009.8 interesting pitfalls we met during our development by using ruby on rails. Either mistakes we made or something you should be aware of.

Transcript of Ruby on Rails Pitfall

Page 1: Ruby on Rails Pitfall

Ruby on Rails PitfallOr just stupid mistakes we made

Robin LuIN-SRC Studio

[email protected]

Friday, May 22, 2009

Page 2: Ruby on Rails Pitfall

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

Page 3: Ruby on Rails Pitfall

‘and’ or ‘&&’

What does this mean?

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

Friday, May 22, 2009

Page 4: Ruby on Rails Pitfall

‘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

Page 5: Ruby on Rails Pitfall

‘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

Page 6: Ruby on Rails Pitfall

strip_tags

Display user input text without tags

What we did:

Friday, May 22, 2009

Page 7: Ruby on Rails Pitfall

strip_tags

Whentext = ‘<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

Page 8: Ruby on Rails Pitfall

strip_tags

strip_tags is not safe by itself

h strip_tags(text)

Friday, May 22, 2009

Page 9: Ruby on Rails Pitfall

cache

Controller

class Blog1Controller < ApplicationControllerdef list

unless read_fragment(:action => 'list') @articles = Article.find_recent

end end

end

<% cache do %> <ul> <% for article in @articles -%>

<li><p><%= h(article.body) %></p></li> <% end -%> </ul>

<% end %>

list.html.erb

Friday, May 22, 2009

Page 10: Ruby on Rails Pitfall

cache

Result:

sometime got crash due to uninitialized @articles

Friday, May 22, 2009

Page 11: Ruby on Rails Pitfall

cachearticle list

Friday, May 22, 2009

Page 12: Ruby on Rails Pitfall

cache

check cache

article list

Friday, May 22, 2009

Page 13: Ruby on Rails Pitfall

cache

check cache list

article list

Friday, May 22, 2009

Page 14: Ruby on Rails Pitfall

cache

check cache list

article list

render

Friday, May 22, 2009

Page 15: Ruby on Rails Pitfall

cache

check cache list

article list

render

article new

Friday, May 22, 2009

Page 16: Ruby on Rails Pitfall

cache

check cache list

article list

expire cache

render

article new

Friday, May 22, 2009

Page 17: Ruby on Rails Pitfall

cache

check cache list

article list

expire cache

render

article new

Friday, May 22, 2009

Page 18: Ruby on Rails Pitfall

cache

check cache list

article list

expire cache

render

check cache

article new

Friday, May 22, 2009

Page 19: Ruby on Rails Pitfall

cache

check cache list

article list

expire cache

render

check cache

crashed by non-init @articles

article new

Friday, May 22, 2009

Page 20: Ruby on Rails Pitfall

cache

• defensive: handle the exception

• postpone init of @articles

• update caches instead of expiring them

Solutions?

none of them is perfect

Friday, May 22, 2009

Page 21: Ruby on Rails Pitfall

object id

Friday, May 22, 2009

Page 22: Ruby on Rails Pitfall

object id

Check nil? everywhere?

Friday, May 22, 2009

Page 23: Ruby on Rails Pitfall

object idconfig.whiny_nil = true

Friday, May 22, 2009

Page 24: Ruby on Rails Pitfall

validate_uniqueness_of

Friday, May 22, 2009

Page 25: Ruby on Rails Pitfall

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

Page 26: Ruby on Rails Pitfall

validate_uniqueness_ofProcess A

Process B

Friday, May 22, 2009

Page 27: Ruby on Rails Pitfall

validate_uniqueness_ofProcess A

Process B

unique?

Friday, May 22, 2009

Page 28: Ruby on Rails Pitfall

validate_uniqueness_ofProcess A

Process B

unique? select ....

Friday, May 22, 2009

Page 29: Ruby on Rails Pitfall

validate_uniqueness_ofProcess A

Process B

unique? select ....unique?

Friday, May 22, 2009

Page 30: Ruby on Rails Pitfall

validate_uniqueness_ofProcess A

Process B

unique? select ....unique?

Insert

Friday, May 22, 2009

Page 31: Ruby on Rails Pitfall

validate_uniqueness_ofProcess A

Process B

unique? select ....unique?

Insert

Insert

Friday, May 22, 2009

Page 32: Ruby on Rails Pitfall

validate_uniqueness_ofProcess A

Process B

unique? select ....unique?

Insert

Insert

crash!

Friday, May 22, 2009

Page 33: Ruby on Rails Pitfall

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

Page 34: Ruby on Rails Pitfall

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

Page 35: Ruby on Rails Pitfall

conditionsWhat 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

Page 36: Ruby on Rails Pitfall

conditions

Result:

we get all posts when a category has no subcategories

Friday, May 22, 2009

Page 37: Ruby on Rails Pitfall

conditionsWhen 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

Page 38: Ruby on Rails Pitfall

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

Page 39: Ruby on Rails Pitfall

before_create

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

What we did:

Friday, May 22, 2009

Page 40: Ruby on Rails Pitfall

before_create

Result:

Only post by admin can be saved

Friday, May 22, 2009

Page 41: Ruby on Rails Pitfall

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

Page 42: Ruby on Rails Pitfall

after_create

send a mail whenever a new record is created

What we did:

Friday, May 22, 2009

Page 43: Ruby on Rails Pitfall

after_create

Result:

sometime the record save failed but we still get mail notification

Friday, May 22, 2009

Page 44: Ruby on Rails Pitfall

after_create

before_create

create

after_create

all in one transaction

begin......commit

all the steps between this should be transactional

Friday, May 22, 2009

Page 45: Ruby on Rails Pitfall

after_create

• send a mail

• delete a file

• expire a cache

What are non-transactional actions?

Friday, May 22, 2009

Page 46: Ruby on Rails Pitfall

after_create

• try not put non-transaction actions into transactions.

• after_commit

• in controller

Friday, May 22, 2009

Page 47: Ruby on Rails Pitfall

Thanks!

Friday, May 22, 2009