Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Common Ruby Idioms

Tags:

idioms

ruby

One thing I love about ruby is that mostly it is a very readable language (which is great for self-documenting code)

However, inspired by this question: Ruby Code explained and the description of how ||= works in ruby, I was thinking about the ruby idioms I don't use, as frankly, I don't fully grok them.

So my question is, similar to the example from the referenced question, what common, but not obvious, ruby idioms do I need to be aware of to be a truly proficient ruby programmer?

By the way, from the referenced question

a ||= b 

is equivalent to

if a == nil || a == false
  a = b
end

(Thanks to Ian Terrell for the correction)

Edit: It turns out this point is not totally uncontroversial. The correct expansion is in fact

(a || (a = (b))) 

See these links for why:

  • http://DABlog.RubyPAL.Com/2008/3/25/a-short-circuit-edge-case/
  • http://DABlog.RubyPAL.Com/2008/3/26/short-circuit-post-correction/
  • http://ProcNew.Com/ruby-short-circuit-edge-case-response.html

Thanks to Jörg W Mittag for pointing this out.

like image 452
DanSingerman Avatar asked Mar 05 '09 08:03

DanSingerman


3 Answers

The magic if clause that lets the same file serve as a library or a script:

if __FILE__ == $0
  # this library may be run as a standalone script
end

Packing and unpacking arrays:

# put the first two words in a and b and the rest in arr
a,b,*arr = *%w{a dog was following me, but then he decided to chase bob}
# this holds for method definitions to
def catall(first, *rest)
  rest.map { |word| first + word }
end
catall( 'franken', 'stein', 'berry', 'sense' ) #=> [ 'frankenstein', 'frankenberry', 'frankensense' ]

The syntatical sugar for hashes as method arguments

this(:is => :the, :same => :as)
this({:is => :the, :same => :as})

Hash initializers:

# this
animals = Hash.new { [] }
animals[:dogs] << :Scooby
animals[:dogs] << :Scrappy
animals[:dogs] << :DynoMutt
animals[:squirrels] << :Rocket
animals[:squirrels] << :Secret
animals #=> {}
# is not the same as this
animals = Hash.new { |_animals, type| _animals[type] = [] }
animals[:dogs] << :Scooby
animals[:dogs] << :Scrappy
animals[:dogs] << :DynoMutt
animals[:squirrels] << :Rocket
animals[:squirrels] << :Secret
animals #=> {:squirrels=>[:Rocket, :Secret], :dogs=>[:Scooby, :Scrappy, :DynoMutt]}

metaclass syntax

x = Array.new
y = Array.new
class << x
  # this acts like a class definition, but only applies to x
  def custom_method
     :pow
  end
end
x.custom_method #=> :pow
y.custom_method # raises NoMethodError

class instance variables

class Ticket
  @remaining = 3
  def self.new
    if @remaining > 0
      @remaining -= 1
      super
    else
      "IOU"
    end
  end
end
Ticket.new #=> Ticket
Ticket.new #=> Ticket
Ticket.new #=> Ticket
Ticket.new #=> "IOU"

Blocks, procs, and lambdas. Live and breathe them.

 # know how to pack them into an object
 block = lambda { |e| puts e }
 # unpack them for a method
 %w{ and then what? }.each(&block)
 # create them as needed
 %w{ I saw a ghost! }.each { |w| puts w.upcase }
 # and from the method side, how to call them
 def ok
   yield :ok
 end
 # or pack them into a block to give to someone else
 def ok_dokey_ok(&block)
    ok(&block)
    block[:dokey] # same as block.call(:dokey)
    ok(&block)
 end
 # know where the parentheses go when a method takes arguments and a block.
 %w{ a bunch of words }.inject(0) { |size,w| size + 1 } #=> 4
 pusher = lambda { |array, word| array.unshift(word) }
 %w{ eat more fish }.inject([], &pusher) #=> ['fish', 'more', 'eat' ]
like image 101
rampion Avatar answered Oct 21 '22 11:10

rampion


This slideshow is quite complete on the main Ruby idioms, as in:

  • Swap two values:

    x, y = y, x

  • Parameters that, if not specified, take on some default value

    def somemethod(x, y=nil)

  • Batches up extraneous parameters into an array

    def substitute(re, str, *rest)

And so on...

like image 40
VonC Avatar answered Oct 21 '22 11:10

VonC


Some more idioms:

Use of the %w, %r and %( delimiters

%w{ An array of strings %}
%r{ ^http:// }
%{ I don't care if the string has 'single' or "double" strings }

Type comparison in case statements

def something(x)
  case x
    when Array
      # Do something with array
    when String
      # Do something with string
    else
      # You should really teach your objects how to 'quack', don't you?
  end
end

... and overall abuse of the === method in case statements

case x
  when 'something concrete' then ...
  when SomeClass then ...
  when /matches this/ then ...
  when (10...20) then ...
  when some_condition >= some_value then ...
  else ...
end

Something that should look natural to Rubyists, but maybe not so to people coming from other languages: the use of each in favor of for .. in

some_iterable_object.each{|item| ... }

In Ruby 1.9+, Rails, or by patching the Symbol#to_proc method, this is becoming an increasingly popular idiom:

strings.map(&:upcase)

Conditional method/constant definition

SOME_CONSTANT = "value" unless defined?(SOME_CONSTANT)

Query methods and destructive (bang) methods

def is_awesome?
  # Return some state of the object, usually a boolean
end

def make_awesome!
  # Modify the state of the object
end

Implicit splat parameters

[[1, 2], [3, 4], [5, 6]].each{ |first, second| puts "(#{first}, #{second})" }
like image 8
Chubas Avatar answered Oct 21 '22 12:10

Chubas