Article / October 27, 2009

Good Ruby Times

By David Palm/5899 Views/21 Comments

In the last few weeks I've had the chance to glance into several different codebases, some written years ago by devs in their Rails-infancy (rainfancy?).

Doing the travelling codes-man like that is a golden opportunity to see what people's stumbling blocks are and how a few easy tricks can improve the code substantially. What follows is not your new cutting edge coffe making kitchen sink script-fu, but a rather dull list of everyday Ruby that I learned people need to learn.

One-stop requires

This is a short one: requires go into the environment.rb.

Really, don't use it elsewhere. I've seen people reason along the lines of "I keep the 'require hpricot' in my xe.com currency rates scraping class as to keep things local and closely knit". There is some truth to that argument and if a gem is only used by a rake task, maybe it even saves us a second during app startup and some RAM. Still, load order issues, poor readability and maintainability makes it a real pain to deal with down the road.

Trust me: don't. Put it in environment.rb with a comment describing its purpose in your project.

Whenever possible use the "config.gem". Advantages:
  • allows you to specify the lib name even if the gem name is different (e.g. for github gems)
  • allows you to require sub libs

    Not used very often perhaps but put to good use by e.g. the right_aws gem where you can choose to load just the SQS part with:

    config.gem 'right_aws', :lib => 'sqs/right_sqs'
    

    or maybe you just wanted the SDB part?

    config.gem 'right_aws', :lib => 'sdb/right_sdb_interface'
    

    I personally believe this particular feature is more confusing than helpful, but it's good to know it's there.

  • allows you to specify the library version

    Towards the end of the development cycle, before rolling out to production, gems should generally be frozen. An alternative is to define exact versions in the config. I generally advise doing both as having the environment.rb as the one-stop source for gem requirements is a boon for everybody involved, capistrano scripts or new devs on your team.

  • all in one place, easy to find out requirements

Singleton methods (creation)

Ruby provides three (common) ways to define a singleton method:

  1. Explicitly name the class

    class RapidInterventionGroupTeam < ActiveRecord::Base
      def RapidInterventionGroupTeam.load_group_allocations_and_teams(project_id, curr_user)
        # Code
      end
    end
            
    

    PROs: hmm, dunno. Makes it easy to know what class you're looking at if the source code file is very long?

    CONs: long, ugly

  2. Use self

    class RapidInterventionGroupTeam < ActiveRecord::Base
      def self.load_group_allocations_and_teams(project_id, curr_user)
        # Code
      end
    end
          
    

    PROs: easy to spot here-comes-a-singleton-marker. Some people like to use the 'self' keyword. I call them self-ish people. Sorta short.

    CONs: I think it's ugly. For more than two singletons that repeating 'self' annoys me.

  3. Use the metaclass

          
    class RapidInterventionGroupTeam < ActiveRecord::Base
      # ==========================
      # = Class methods go here! =
      # ==========================
      class << self
        def load_group_allocations_and_teams(project_id, cur_user)
          # Code
        end
      
        def team_allocations
          # Code
        end
      
        def group_allocations
          # Code
        end
      end
    
      # ============================
      # = Instance methods go here =
      # ============================
      def beef_cow_and_fowl
        # Code
      end
    end
          
    

    PROs: shortest. Encourages devs to gather singletons in one spot, and perhaps they will notice when it gets out of hand and start creating mixins. Maybe. Also allows devs to show off their Ruby metaclass knowledge at bars. Maybe.

    CONs: that class << self is funky and if funky doesn't rub well with you (or your boss) then I guess it's no good.

I personally favor c) whenever the number of class methods goes above two or three; b) is fine when there are just a few of them.

Invoking singleton methods

In Ruby there are many ways to invoke a class method. A few common ones:

  1. Name the class explicitly:

    user = User.find(123)
    Predators.feed_to_wolves(1, user)
          
    
  2. When used from an instance of the class, use the context: self.class

    class Predators < ActiveRecord::Base
      def self.feed_to_wolves(pack_id, meat)
        # code
      end
      
      # we know how we were born...
      def feed_us!(meat)
        self.class.feed_to_wolves(id, meat)
      end
    end
        
    
  3. Use a helper instance method

        
    class Steak
      def self.logger
        @logger ||= Logger.new('log/steak.log')
      def
    
      def logger
        self.class.logger
      end
    
      def beef
        logger.debug "MONDAY BEEF!"
        beef_for!(:monday)
      end
    end
          
    

    Using an instance method this way to invoke a class level method is of course useful only if you invoke it very often and readability becomes very important. In the above example we're also memoizing (caching) the class level Logger object for fast access.

I personally try 1) to avoid singletons altogether, 2) use method b) whenever possible and 3) start asking myself hard and awkward questions when doing a) more than 3 times a day. Too many singletons might be a codesmell.

Multiple return values

First an example of how people often fake multiple value returns in Ruby:

class Something
  def many
    [calc_monday_beef, calc_total_beef]
  end
end

s = Something.new
retval = s.many
  

DON'T: when coding a method and you realize you need to return more than one value from a method, you should stop and think. You're probably doing something wrong.
This is actually the reason why Ruby does not provide a way to return multiple values.

If you're sure you know what you're doing and want to return more than value here are some tips:

  1. do NOT do what I did in the example.

  2. use descriptive variable names. "retval" above sucks. Use Ruby multiple value assignments like so:

    >> cows, sheep, fish = [1, 5, 3]
    => [1, 5, 3]
    >> cows
    => 1
    >> sheep
    => 5
    >> fish
    => 3
          
    

    Use this to assign multiple return values to local variables with helpful names:

          
    s = Something.new
    monday_beef, total_beef = s.many
          
    
  3. c) if you're returning many values and you don't know beforehand how many values are coming back, use the splat operator (*):

    >> me, *others = [:abe, :bob, :caesar, :donald]
    => [:abe, :bob, :caesar, :donald]
    >> me
    => :abe
    >> others
    => [:bob, :caesar, :donald]
          
    
  4. d) avoid using array indexes and prefer using Array#first, Array#last:

    • BAD:

        
      s = Something.new
      retval = s.many
      Beef.find(retval[0], :limit => retval[1])
                
      
    • STILL BAD SOMEWHAT BETTER:

      s = Something.new
      retval = s.many
      Beef.find(retval.first, :limit => retval.last)
                
      

      Why better? Because you communicate to the reader that that 'retval' variable only have two values. (Yeah, it still sucks I know)

    • ALSO BAD BUT SOMEWHAT-ER BETTER STILL:

      s = Something.new
      monday_beef, beef_count = s.many
      Beef.find(monday_beef, :limit => beef_count)
                
      
    • GOOD:

      s = Something.new
      Beef.find(s.monday_beef, :limit => s.total_beef_count)
                
      

Drop the return

Use the fact that Ruby methods return the value of the last expression. The only place where I think an explicit return is legit is when we want to return early and that's a performance optimization and as we know, premature optimization is the root of all evil, so unless you have benchmarks at hand proving the need for speed, just don't user 'return'.

  • Instead of:

    def course_description
      retval = course_shortname 
      retval = course_shortname + "(#{project.shortname})" if project
      return retval
    end
          
    
  • Do:

          
    def course_description
      course_shortname + (project ? "(#{project.shortname})" : '')
    end
          
    

Enumerable

Learn to use and love ruby Enumerable module. It's really really useful.

  • Instead of:

          
    def Vacation.holidaytype_selectbox
      types = Array.new
      for vt in VacationTypes
        types << vt[:name]
      end
      @holidaytype_selectbox_model ||= types
    end
        
    
  • Use:

        
    def self.holidaytype_selectbox
      @holidaytype_selectbox_model ||= VacationTypes.map(&:name)
    end
        
    
  • Instead of:

    for result in @candidate.results do
      result.destroy
    end
        
    
  • Use:

    @candidate.results.each(&:destroy)
    
  • Instead of:

    @approved = 0
    unless @do_it.nil?
      @events = Event.hourreporting_entries(@person.user_id, params[:projectid], @week.first.date, @week.last.date)
    
      for e in @events
        if e.approved == 1
          @approved += 1
        end
      end
    end
        
    
  • Do:

        
    if @do_it
      @events = Event.hourreporting_entries(@person.user_id, params[:projectid], @week.first.date, @week.last.date)
      @approved = @events.sum{|e| e.approved ? e.approved : 0}
    end
        
    

    The Array#sum method is a Rails addition.

In the app where I spotted the above, the default value for the 'approved' field is set to NULL. Had the default value been 0 instead, we could have written the above in an even shorter way:

@events.sum(&:approved)

Truth

In Ruby, everything except false and nil evaluates to true. For realz.

  • Instead of:

      
    if options[:add] == true && check_id.to_i != 0
      meanie = MeanGuy.find(check_id)
      if meanie.reason != '' && !meanie.reason.nil? && meanie.type == MeanGuy.allowed_types[0]
        # Code
      end
    end
        
    
  • Do:

      
    if options[:add] && !check_id.zero?
      meanie = MeanGuy.find(check_id)
      if !meanie.reason.blank? && meanie.type == MeanGuy.allowed_types.first
        # Code
      end
    end
        
    

When chaining conditional checks like above with "&&", remember that Ruby will stop the checks at the first failure, so always put the 'cheapest' checks first (e.g. put any checks that require a database query last).

In the above snippet we see the very common "!something.blank?" idiom. I personally really don't like the prepended "!" and always try to avoid it. Hurts my eyes and makes me stumble while reading. Ideally the above should be:

if meanie.reason && meanie.type == MeanGuy.allowed_type

but if the meanie reason is allowed to be the empty string -- which Ruby will consider True -- then we need the explicit blank?-check.

Unrelated neat trick using &&:

  • Instead of:

    def safe_death(dude)
      if dude
        if dude.destroy
          if DudeMailer.deliver_destruction_notification
            Call.his_mum
          end
        end
      end
    end
    safe_death(Dude.first)
          
    
  • Do:

    def safe_death(dude)
      dude && dude.destroy && DudeMailer.deliver_destruction_notification && Call.his_mum
    end
    safe_death(Dude.first)
          
    
  • This works well only if you don't care about handling any error conditions and success depends on *all* method calls being successful.

    To improve readability in general create as many predicate methods as possible. They are short, cheap and really helps readability.

    • Instead of:

        
      if @person && @person.active == true && @person.projects.include?(:beef_on_monday) && (@person.type == :human || @person.type == :dog)
        # Code
      end        
          
      
    • Do:

      class Person
        def active_monday_beefer?
          actvive? && member_of?(:beef_on_monday) && (human? || dog?)
        end
      end
      
      if @person.active_monday_beefer?
        # Code
      end
          
      

    Method naming

    When naming your methods, avoid prepending "get_" or "set_". The Ruby convention is that getters are just the value name and the setter has the "=" postfix. Avoid the quasi-get_'s too: load_, retrieve_, store_, put_. Sometimes they're ok, but just stop a second and think if:

    store_incoming_beef()
    

    is really really better than

    more_beef_in_the_holds()
    

    or

    beefers_keepers()
    

    or

    not_pork_not_sheep_not_fowl_but?()
    

    As always rules are made to be broken, judiciously. ;)

    DON'T:

    #get_premium, #get_unix_time_stamp, #load_dates, #retrieve_mums etc
    

    Do:

    #premium, #unix_timestamp, #dates, #mums, #beefs
    

    DON'T:

    Server.set_clock(Time.now)
    @doc.store_signer(Dude.new)
    @tellus.put_fire(:wild)
    Time.set_back(1.year.ago)
    

    Do:

    Server.clock = Time.now
    @doc.signer = Dude.new
    @tellus.fire!(:wild)
    Time.now = 1.year.ago
    

    Keep method names short: e.g. #shorten_timespaces_because_of_special_holiday is too long. Long method names are often a sign of complex methods. Complex methods are often a sign we should refactor the code into smaller pieces.

    Sometimes the work done by a method is simply too complicated to be expressed properly by a short and self-explanatory method name. If so, don't even try. Leave the method name short and cryptical. It becomes a marker for the reader of "hey, wanna understand this one? Sorry but you really really have to go read the code!".

    DON'T:

    #shorten_timespaces_because_of_special_holiday
    

    DO:

    #holiday_adjustment
    

    Use the "?" for predicates (should *always* return true/false or at least something that evaluates to true false, such as "abc"/nil)

    class Session
      def finished?
        finished_at
      end
    end
    
    sess = Session.new
    sess.finished_at = Time.now
    sess.finished?
    

    GOOD:

    if sess.finished? 
      coffee!
    end
    

    BAD:

    if sess.finished_at
      coffee!
    end
    

    Use rdoc!

    User rdoc for your rails apps. It's actually nice. When your mum asks you what it is you actually do you can show her the rdocs with links and text and colors and lists and it will make her happy too. And very useful for people trying to read your code (ok, they can run the freakin' rake task themselves, but you should do it too!).

      rake doc:app
      open doc/app/index
    

Comments

Posted by Gregor Martynus on 3 months ago62710fbec13503dfcac363787e6d7e38?s=30

thanks David Palm!
This is super awesome.

Posted by Arya Asemanfar on 3 months agoC6c3d4bbf353456dbec83ac6618d6561?s=30

Great writeup, lots of good practices in here!

Regarding the explicit return statement: another good reason not to use it unless you need to is that it's slower. See Chris Wanstrath's benchmarks: http://gist.github.com/160718

An additional con for using class << self for class methods is that you may not be able to tell whether this is a class or instance method just by looking at it without scrolling up to see if it's inside a class << self block.

For predicate methods (i.e. finished?), you can precede the return value with !! which forces it to true/false.

Posted by jeff on 3 months agoD69d23d8e811e8ab2a8593380d6ede63?s=30

Is it more accurate to call the "singleton" methods "class" methods? To me, singletons are objects that may be instantiated only once.

Posted by David Palm on 3 months ago101b2be3b760357be098a70244a21561?s=30

@jeff: do a search in the ruby-lang ML archives. That question has been discussed and re-discussed many many times over the years! Matz calls them 'singleton methods' in the C sources and hence many feel that settles it. You're right though, 'class methods' is the more common idiom. I think there was a third nomenclature as well. Can't remember right now.

As long as we all know what we're talking about I guess it's ok, right? :)

Posted by David Palm on 3 months ago101b2be3b760357be098a70244a21561?s=30

@arya: I didn't know about the !! trick, very cool. :

I'd argue that predicate methods should always be used as such and their return value never used for anything but true/false-like tests, so it's not really important that the actually return TrueClass/FalseClass.

I know many programmers that feel queasy about returning, say, a Date object from a predicate method and if an extra cast or two makes them feel safer I'm all for it.

:)

Posted by Peter Cooper on 3 months ago6268c7528d855f1cef5696a00d159909?s=30
This is a short one: requires go into the environment.rb. You can, but that's not really the place it for stuff like that anymore. Since somewhere around Rails 2.1, custom code like that is meant to go into a file in config/initializers.
Posted by remix on 3 months agoEd4900151f64d5daf2c200759da6fa30?s=30

thanks for a nice list! my 2 cents:

- you can use something.present? instead of the ugly !something.blank? (present? was added in some recent Rails version)

- i like "andand" gem, f.ex, not: if beast && beast.loose?, but:
if beast.andand.loose? A little bit more DRY.

- sometimes i find useful this construction:

if (someobject. ..some ugly chain.. rescue false)
Posted by David Palm on 3 months ago101b2be3b760357be098a70244a21561?s=30

@remix: #present? is a great idea, thanks! (use #any? if you want to check collections only).

Re-andand and similar. I used to be a great fan of the andand (much better than #try imho) but in the end I feel that spelling it out wins:
if @dude && @dude.surfs? vs. if @dude.andand.surfs?

Both are ok though I think.

My point in the article though, was that it really pays off if you can code in such as to be able to trust your data not to be nil ever (or put differently, in such a way so that Nils are exceptions and bugs to be fixed).

@peter: I disagree. config/initializers is where I put *app* initialization code, not where I like my environment to be created. I see your point and using config/initializers works excellently. I for one don't want to browse through 10 files to see what libs are required.= to run the app. I want *one* spot.

Posted by Brandon on 3 months agoBd2501c832b4e52fbc0d0f8758292e5e?s=30

Useful and hilarious. Lazy returns should be used 99% of the time, once in a while you need to make it explicit (def contains a few if statements, etc).

Posted by chris on 3 months ago52ea3d18cb0beaf6450822bfb884c373?s=30

@events.compact.sum(&:approved) can handle those nil's

Posted by David Palm on 3 months ago101b2be3b760357be098a70244a21561?s=30

@chris: nah, it's not the @events that may contain Nils, it's the attribute values that, allowed to be NULL in the DB, can come across as Nils instead of 0. :)

Posted by Michael Dvorkin on 3 months ago1ee9ae1826345b676471808945964808?s=30

Great stuff, enjoyed reading it! How about @approved = @events.sum{|e| e.approved || 0}

Posted by mlambie on 3 months ago81dffa5b15cbbb928678ebb660676dd5?s=30

Am I missing something, or is DO:

Time.set_back(1.year.ago)
The same as DON'T:
Time.set_back(1.year.ago)
Great article - I've found a few things I can clean up in my own code.
Posted by David Palm on 3 months ago101b2be3b760357be098a70244a21561?s=30

@mlambie: you're right, typo. Thanks!

Posted by David Palm on 3 months ago101b2be3b760357be098a70244a21561?s=30

@michael: you're right, that's a great solution. :)

Posted by Mike Woodhouse on 3 months ago5978e2ed3a31f1bd2361dbb919d92012?s=30

Regarding multiple return values, I suggest that "you communicate to the reader that that 'retval' variable only have two values" is perhaps optimistic: all that using #first and #last tells me is that I'm only interested in the two extremeities of a possibly aribitrarily long array! But as you say, it sucks. I think it's an evolutionary thing: at some point we learn that we can return multiple values into a list of receivers (possible terminology fail there) and we feel much more Ruby-ish. Then - and I'm only just arriving there at the moment - we realise how we could be more intention-revealing. I've been having a lot more fun returning Structs or OpenStructs (for the lazy) of late.

Posted by David Palm on 3 months ago46f61a8a15c5e234ad416b590a2676c9?s=30

@mike: you're right of course and #first/#last is merely the first baby step to unsuckify the code. Often times when talking to inexperienced programmers I find that I have to be mighty careful not to step on egos and hurt peoples' feelings. In those cases it's useful to convince them of small changes and then come back the week after suggesting a helper method or two, and in the end help them see how refacotoring the whole thing not to need multiple returns at all is the way to go...

Posted by Daniel Lopes on 3 months ago89e57a28dfdb85e07b33f92783dbe349?s=30

Amazing list, thanks :)

Posted by ara.t.howard on 3 months ago1bac2e65d64faf472cf2ebc94f0f5ee0?s=30

you forgot the most elegant way to open the singleton class:

class C
class << C
def singleton_method() end
end
end

i personally always add this method

def singleton_class(&block)
@singleton_class ||= (
class << self
self
end
)
block ? @singleton_class.module_eval(&block) : @singleton_class
end

to pretty much every ruby project. it allows for

class C
singleton_class do
def singleton_method() end
end
end

and also

a = Array.new
a.singleton_class.module_eval{ alias_method 'length', 'size' }

really very useful.
end

Posted by David Palm on 3 months ago46f61a8a15c5e234ad416b590a2676c9?s=30

@ara: how is

class B
class << B
def beefer() end
end
end

...different from what I suggested:

class B
class << self
def beefer() end
end
end

I agree that having a reference to the metaclass around is very handy and the 'singleton_method(&blk)' is neat, but the beauty of it becomes apparent only when you begin to grasp how the whole class/metaclass machinery fits together. I believe many programmers don't (yet!) and I often meet resistance at the "class << self" part. There is so much bad Ruby code out there right now that need the basics taken care of first that I'm hesitant rising the level further...

:/

Posted by andresgutgon on 3 months ago46f61a8a15c5e234ad416b590a2676c9?s=30

thanks :)

Add a Comment