[ANN] rails_best_practices 1.1.0 released

flyerhzm Posted by flyerhzm on October 06, 2011


I just released rails_best_practices 1.1.0, the changes are as follows:

1. add "restrict auto-generated routes" check, http://rails-bestpractices.com/posts/86-restrict-auto-generated-routes

2. remove forced haml dependency (thanks @square)

3. add controller_prepare to remember all controllers' methods

4. use official progressbar gem instead of ruby-progressbar

Check it here, https://github.com/flyerhzm/rails_best_practices


flyerhzm Posted by flyerhzm on October 05, 2011


I was finding a cache implementation a few days ago, I prefer cache-money gem, it provides a transparent cache way, really great. But it doesn't support rails3, ngmoco repository has a rails3 branch, but it doesn't work yet.

So I decided to write a simple cache system by myself. Here are the apis that I expected

1. cache by id

User.find(1) => User.find_cached(1)

2. cache by attribute

User.find_by_email("flyerhzm@gmail.com") => User.find_cached_by_email("flyerhzm@gmail.com")

3. cache by method

user.popular_tags => user.cached_popular_tags

4. cache by association (belongs_to, has_one and has_many)

post.user => post.cached_user

And the cached object will be expired after the object is updated.

I just released the gem simple_cacheable, https://github.com/flyerhzm/simple_cacheable, it provides a DSL to add cache ability to your models, it's easy to use, here is an example

class Post < ActiveRecord::Base
  include Cacheable

  belongs_to :user
  has_many :comments

  model_cache do
    with_key  # Post.find_cached(1)
    with_attribute :title  # Post.find_cached_by_title("title")
    with_association :user  # post.cached_user
    with_method :last_comment  # post.cached_last_comment

  def last_comment

first, you should include Cacheable module provided by simple_cacheable, then define the cache by model_cache, there are 4 caches methods

1. with_key, cache for find(id)
2. with_attribute, cache for find_by_xxx
3. with_method, cache for a model method
4. with_association, cache for association (belongs_to, has_one and has_many)

I already used it in developing the website, works well now.

[ANN] rails_best_practices 1.0.0 released

flyerhzm Posted by flyerhzm on September 24, 2011


I just released rails_best_practices 1.0.0, the changes are as follows:

1. The parser is rewritten, I use ripper instead of ruby_parser, it supports ruby 1.9 perfectly now.

2. Use ripper18 gem to make it work in ruby 1.8 as well.

Check it here https://github.com/flyerhzm/rails_best_practices

Updated: I also released 1.0.1 to fix the line output error for constant node.

use ripper instead of ruby_parser

flyerhzm Posted by flyerhzm on September 24, 2011


I have used ripper instead of ruby_parser from rails_best_practices version 1.0.0


I gave a presentation about rails_best_practices gem in RubyKaigi 2011 Tokyo this July.

One of the attendees asked me a question,

You implement the rails_best_practices based on ruby_parser, but ruby_parser can't support ruby 1.9 well, e.g. json hash, do you have plan to solve this?

My answer is,

Yes, there are two options, one is to change the source codes of ruby_parser, make it support the new ruby 1.9 syntax, the other is to use ripper, which is one of the new libraries in ruby 1.9, it supports all the ruby 1.9 new syntax, but doesn't support ruby 1.8.

After that, I don't have enough time to take care of that, but more and more developer open tickets on github to ask for support ruby 1.9 json hash, so I spent these two weekends to rewrite the rails_best_practices gem, replacing the ruby_parser with ripper. The following is my experience to use ripper to parse your ruby codes.

Difference between ruby_parser and ripper

So what's the biggest difference between ruby_parser and ripper?

ruby_parser provides the DOM-style way to parse the ruby source codes to S-expression, wrapped by sexp_processor.

ripper provides both the DOM-style and SAX-style to parse the ruby source codes, but the parsing result is just multidimensional arrays, not wrapped by sexp_processor. 

Another big difference is that the parsing results of ruby_parser and ripper are totally different. 

The result of ruby_parser contains scope, but ripper doesn't have.

The sexp type of their results are different, and not one to one correspondence.

ruby_parser treats user.logn = "richard" as a method call, but ripper treats it as an assign.

ruby_parser treats user == current_user as a method call, but ripper treats it as an binary operation.

ruby_parser will put multiple arguments or statements in the same dimension in the array, but ripper will put them in the multiple dimensions.

There are a lot of other differences that you will encounter when you change the parser from ruby_parser to ripper.

What I use in rails_best_practices

So what I use now? it's DOM-style ripper. Why?

I decided to use ripper instead of ruby_parser, because ripper is the standard library in ruby 1.9, that means it can support all the new syntax introduced by ruby 1.9. In future, I think it will work in 1.9.3, and even in ruby 2.0.

Then I make some experiments to check if I should use SAX-style or DOM-style ripper.

I tried SAX-style ripper first, extend Ripper::SexpBuilder class, register the events that I interested in, it works but not for my case. e.g. if the source codes are

class User
  def name

the SAX-style tells you the parsing result from bottom to top, first is string "richard", then method define name, and finally the class User, which is the opposite to what I expected.

So I choose DOM-style ripper, it provides the top to bottom result just like ruby_parser. To visit the result easier, I use sexp_processor to wrap.


Now result of the above code is the S-expression for the provided source code, one thing should notice that ruby_parser will set the filename and line number to every sexp node, but the result of above code doesn't contain.

If you use SAX-style, ripper provide the accessor to filename and lineno for you, but in DOM-style, you have to take care of the filename by yourself, luckily  the result of DOM-style ripper contains line number and column number for some nodes, like [:const_ref, [:@const, "User", [1, 8]]], 1 is the line number and 8 is the column number.


Here I want to thank @lsegal, who writes ripper18 gem, which makes ripper to be avaiable to ruby 1.8, check it here: https://github.com/lsegal/ripper18

You can get more usages of ripper in rails_best_practices source codes, https://github.com/flyerhzm/rails_best_practices

[ANN] rails_best_practices 0.10.0 released

flyerhzm Posted by flyerhzm on July 04, 2011


I just released rails_best_practices 0.10.0, thank raydog153's contributes, the changes are as follows:

1. add "remove tab" check, http://rails-bestpractices.com/posts/81-remove-tab

2. fix sorting in infinute loop

Check it here, https://github.com/flyerhzm/rails_best_practices