Question
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.
Solution
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' ]
OTHER TIPS
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...
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})" }
I like this:
str = "Something evil this way comes!"
regexp = /(\w[aeiou])/
str[regexp, 1] # <- This
Which is (roughly) equivalent to:
str_match = str.match(regexp)
str_match[1] unless str_match.nil?
Or at least that's what I've used to replace such blocks.
I would suggest reading through the code of popular and well designed plugins or gems from people you admire and respect.
Some examples I've run into:
if params[:controller] == 'discussions' or params[:controller] == 'account'
# do something here
end
corresponding to
if ['account', 'discussions'].include? params[:controller]
# do something here
end
which later would be refactored to
if ALLOWED_CONTROLLERS.include? params[:controller]
# do something here
end
Here's a few, culled from various sources:
use "unless" and "until" instead of "if not" and "while not". Try not to use "unless" when an "else" condition exists, though.
Remember you can assign multiple variables at once:
a,b,c = 1,2,3
and even swap variable without a temp:
a,b = b,a
Use trailing conditionals where appropriate, e.g.
do_something_interesting unless want_to_be_bored?
Be aware of a commonly-used but not instantly obvious (to me at least) way of defining class methods:
class Animal
class<<self
def class_method
puts "call me using Animal.class_method"
end
end
end
Some references:
By the way, from the referenced question
a ||= b
is equivalent to
if a == nil a = b end
That's subtly incorrect, and is a source of bugs in newcomers' Ruby applications.
Since both (and only) nil
and false
evaluate to a boolean false, a ||= b
is actually (almost*) equivalent to:
if a == nil || a == false
a = b
end
Or, to rewrite it with another Ruby idiom:
a = b unless a
(*Since every statement has a value, these are not technically equivalent to a ||= b
. But if you're not relying on the value of the statement, you won't see a difference.)
I maintain a wiki page that covers some Ruby idioms and formatting:
I always forget the exact syntax of this shorthand if else statement (and the name of the operator. comments anyone?) I think it's widely used outside of ruby, but in case someone else wants the syntax here it is:
refactor < 3 ? puts("No need to refactor YET") : puts("You need to refactor this into a method")
expands to
if refactor < 3
puts("No need to refactor YET")
else
puts("You need to refactor this into a method")
end
update
called the ternary operator:
return myvar ? myvar.size : 0
You can deepcopy with Marshaling object easily. - taken from The Ruby Programming Language
def deepcopy(o)
Marshal.load(Marshal.dump(o))
end
Note that files and I/O streams, as well as Method and Binding objects, are too dynamic to be marshaled; there would be no reliable way to restore their state.
a = (b && b.attribute) || "default"
is roughly:
if ( ! b.nil? && ! b == false) && ( ! b.attribute.nil? && ! b.attribute.false) a = b
else a = "default"
I use this when b is a record which may or may not have been found, and I need to get one of its attributes.
I like how If-then-elses or case-when could be shortened because they return a value:
if test>0
result = "positive"
elsif test==0
result = "zero"
else
result = "negative"
end
could be rewriten
result = if test>0
"positive"
elsif test==0
"zero"
else
"negative"
end
The same could be applied to case-when:
result = case test
when test>0 ; "positive"
when test==0 ; "zero"
else "negative"
end
Array.pack and String.unpack for working with binary files:
# extracts four binary sint32s to four Integers in an Array
data.unpack("iiii")
method missing magick
class Dummy
def method_missing(m, *args, &block)
"You just called method with name #{m} and arguments- #{args}"
end
end
Dummy.new.anything(10, 20)
=> "You just called method with name anything and arguments- [10, 20]"
if you call methods that not exists in ruby objects, ruby interpreter will call method called 'method_missing' if its defined, you could user this for some tricks, like writing api wrappers, or dsl, where you don;t know all methods and parameters names
Nice question!
As I think the more intuitive & faster the code is, a better software we’re building. I will show you how I express my thoughts using Ruby in little snippets of code. Read more here
Map
We can use map method in different ways:
user_ids = users.map { |user| user.id }
Or:
user_ids = users.map(&:id)
Sample
We can use rand method:
[1, 2, 3][rand(3)]
Shuffle:
[1, 2, 3].shuffle.first
And the idiomatic, simple and easiest way... sample!
[1, 2, 3].sample
Double Pipe Equals / Memoization
As you said in the description, we can use memoization:
some_variable ||= 10
puts some_variable # => 10
some_variable ||= 99
puts some_variable # => 10
Static Method / Class Method
I like to use class methods, I feel it is a really idiomatic way to create & use classes:
GetSearchResult.call(params)
Simple. Beautiful. Intuitive. What happens in the background?
class GetSearchResult
def self.call(params)
new(params).call
end
def initialize(params)
@params = params
end
def call
# ... your code here ...
end
end
For more info to write idiomatic Ruby code, read here