airbnb / ruby

Ruby Style Guide

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Add guideline about method calls

jamesreggio opened this issue · comments

I'm not sure if this is an intentional omission, so I figured I'd throw it out there.

Ruby has a lot of syntactic sugar around method invocation. These are my rules around the use of that syntax:

  • Use parenthesis...

    • If the method returns a value

      # bad
      @current_user = User.find_by_id 1964192
      
      # good
      @current_user = User.find_by_id(1964192)
    • If the first argument to the method uses a parenthesis

      # bad
      put! (x + y) % len, value
      
      # good
      put!((x + y) % len, value)
  • Omit the parenthesis...

    • If the method accepts no arguments

      # bad
      nil?()
      
      # good
      nil?
    • If the method doesn't return a value (or we don't care about the return)

      # bad
      render(:partial => 'foo')
      
      # good
      render :partial => 'foo'
  • If a method accepts an options hash as the last argument, do not use { } during invocation

    # bad
    get '/v1/reservations', { :id => 54875 }
    
    # good
    get '/v1/reservations', :id => 54875

My exposure to Ruby lacks breadth, so regardless of whether the omission is intentional, I'd like to know if these are sound practices.

I think if a call spans multiple lines it should probably have parentheses

thoughts?

#bad
render :partial => 'foo', 
       :something => 'the quick brown fox', 
       :something_else => 'jumps over the lazy dog'

#good
render(:partial => 'foo', 
       :something => 'the quick brown fox', 
       :something_else => 'jumps over the lazy dog')

We should definitely omit parentheses when not passing any arguments.

Aside from that, I don't have strong feelings about the other rules. They seem like pretty reasonable principles for something that otherwise feels very case-by-case. I'm happy to accept these guidelines for a while, see where they take us with pull requests in the wild, and revise as new cases/exceptions arise.

Re Allen's multi-line render examples: I am indifferent between the two.

@robotpistol I prefer the former. Don't see the value in the latter.

I also do like @current_user = User.find_by_id 1964192 - seems quite tidy and readable.

I also question whether enforcing standards around points like my two above actually add any value.

I like the distinction based on return values (@current_user = User.find_by_id 1964192 fills me with rage, for instance)

I much prefer the no-parens version of render and I also prefer that usage with class level instructions. In general, omitting parens seems to work well in DSL contexts (which tend to eschew return values), but not so well in plain old code.

Multiline invocations without parentheses are really frustrating for me. +1 to requiring parentheses for multiline invocations.

I like James's original guidelines plus the multi-line rule.

+1 to @current_user = User.find_by_id 1964192 being rage-inducing

Here is an example of unreadability induced by lack of parens. It is hard to tell which arguments are arguments of which function call (link_to vs t vs url_for).

<%= link_to t('email.employee_travel_coupon_notification.coupon_code_link', :default=>"Click Here"), url_for({:host=>@host,:only_path=>false, :controller => :coupons, :action => :employee_travel_coupons}), :target => "_blank" %>

Of course, the situation is better if the call is properly formatted. This indentation-without-parens itself seems a little weird to me.

          <%= link_to t('email.employee_travel_coupon_notification.coupon_code_link',
                        :default=>"Click Here"),
                      url_for(:host => @host,
                              :only_path => false,
                              :controller => :coupons,
                              :action => :employee_travel_coupons),
                      :target => "_blank" %>

With parens it is absolutely clear what is getting passed to what.

          <%= link_to(t('email.employee_travel_coupon_notification.coupon_code_link',
                        :default=>"Click Here"),
                      url_for(:host => @host,
                              :only_path => false,
                              :controller => :coupons,
                              :action => :employee_travel_coupons),
                      :target => "_blank") %>

@current_user = User.find_by_id 1964192 just looks wrong to me.

In general I'm of the school of thought that if it has arguments, it should have parens. What can I say I'm old fashioned and actually like parentheses. They more effectively delineiate where a call begins and ends.

I also am of the school of thought that if it has arguments, it should have parens.

Parens++

For example:

render :action => :my_action

Looks innocuous at first, but as more is added it becomes:

render :controller => :my_controller, :action => :my_action, :id => SomeId, :template => BLAH

Once this happens, I greatly prefer:

render({
  :controller => :my_controller,
  :action => :my_action,
  :id => SomeId
})

This works especially well in the case of complicated link_to statements, since the method takes two hashes! I prefer, or example:

link_to("Some Link", {
  :action => :my_action!
}, {
  :id => "awesome_link",
  :class => "beautify"
})

I prefer this approach because I believe ambiguity to be the root of all evil.

@jamesreggio James! Could you send a PR for this?

This was fixed by #23.