This is a somewhat random list of things that were interesting or surprising to me when I read Ruby for Rails. The previous post in the series is Ruby: A Python Programmer's Perspective Part III.
It's possible to add methods directly to an object instead of to a class:
There's even this weird syntax to attach additional methods to a class's singleton class:
Ruby has a global named $SAFE:
Proc objects handle arguments in a different way than methods do. It's sloppier--more like JavaScript and Perl:
Here's another example of this. Notice how Ruby will or will not unpack a list depending on context:
The lambda keyword lets you create an anonymous function. A lambda is an object of class Proc. However, lambdas and Proc.new behave differently when it comes to the return statement:
Ruby also supports bound methods:
Here's something I personally haven't seen in any other language. You can capture the act of subclassing a class, mixing in a module, or adding a method to a class as a runtime event. For instance, ActiveRecord in Ruby on Rails uses this functionality. Anytime someone subclasses ActiveRecord::Base, it captures the event (by defining a class method called "inherited") and adds the class to a list named "@@subclasses".
To alias a function under a new name, use the alias keyword: "alias :old_name :new_name". This is interesting, because in Python, you would just write "old_name = new_name". In Python, it's mandatory to use parenthesis when you call a function, so "old_name = new_name" is no different than saying "a = b" if a and b are both ints. The same isn't true of Ruby because "old_name = new_name" is essentially like "old_name() = new_name()" which doesn't even make sense. It's not a big deal, but it's just interesting to note that by making parenthesis non-mandatory, it leads to having an alias keyword. It also leads to writing "my_proc.call" instead of "my_proc()".
Everyone knows Ruby's string interpolation syntax: "Foo: #{bar}". However, Ruby also appears to support Python-like string interpolation:
Okay, that's it. If you've made it this far, thanks for reading!
It's possible to add methods directly to an object instead of to a class:
>> obj = Object.newThe methods are attached to what's called a singleton class. There is a singleton class that is associated with every object:
=> #<Object:0x348c44>
>> def obj.talk
>> puts "Hi!"
>> end
=> nil
>> obj.talk
Hi!
=> nil
You can think of an object's singleton class as an exclusive stash of methods, tailor-made for that object and not shared with other objects--not even with other instances of the object's classClass methods are similarly methods that are attached to the singleton class of an object of type Class.
There's even this weird syntax to attach additional methods to a class's singleton class:
?> class << CIt's possible in Python to attach methods directly to an object in Python too, but there is no formal notion of a singleton class. However, every object in Python does have a __dict__ which is a dict containing all the state and methods for that object:
>> def f2
>> puts 'f2'
>> end
>> end
=> nil
>>
?> C.f1
f1
=> nil
>> C.f2
f2
=> nil
>>> class C:Moving on--you can inject a module's methods directly into an object:
... def f(self): pass
...
>>> c = C()
>>> c.foo = 'bar'
>>> c.__dict__
{'foo': 'bar'}
>>> C.__dict__
{'__module__': '__main__', '__doc__': None, 'f': <function f at 0x6fef0>}
obj = Object.newHere's a useful way to inspect the class hierarchy:
obj.extend(SomeModule)
obj.some_method
?> class D < CIn Ruby, singleton classes of class objects are sometimes called metaclasses. This is confusingly different from metaclasses in Python. In Python, a metaclass is the class (aka type) of a class. Hence, Python would call the Ruby class named Class a metaclass, whereas in Ruby, the metaclass of a class named C is an anonymous container for the class methods of C. Does your brain hurt yet? ;)
>> p ancestors
>> end
[D, C, Object, Kernel]
=> nil
Ruby has a global named $SAFE:
It's very difficult to clean up user input...to the point where you can feel safe about running eval on it. Ruby maintains a global variable called $SAFE, which you can set to a higher number (on a scale of 0 to 4) to gain protection from dangers like rogue file-writing requests. $SAFE makes life with eval a lot safer. Still, the best habit to get into is the habit of not using eval. [p. 348]You can break into an object's private data using instance_eval:
>> class CHere's a fun way to inject methods dynamically into a class:
>> def initialize
>> @x = 1
>> end
>> end
=> nil
>>
?> c = C.new
=> #<C:0x360718 @x=1>
>> c.instance_eval { puts @x }
1
=> nil
>> Object.class_eval { define_method("foo") { puts 'hi' } }Unlike Python, JavaScript, and Scheme, you don't get closures just by nesting functions:
=> #<Proc:0x0035b100@(irb):15>
>> obj = Object.new
=> #<Object:0x3599cc>
>> obj.foo
hi
=> nil
?> def fThat's because method scopes don't stack on top of one another. However, you can create a closure using Proc.new:
>> def g
>> puts a
>> end
>> a = 1
>> g
>> end
=> nil
>>
?> f
NameError: undefined local variable or method `a' for main:Object
from (irb):29:in `g'
from (irb):32:in `f'
from (irb):35
>> def fI think I like the Scheme approach better.
>> a = "hi"
>> g = Proc.new do
?> puts a
>> end
>> g.call
>> end
=> nil
>>
?> f
hi
=> nil
Proc objects handle arguments in a different way than methods do. It's sloppier--more like JavaScript and Perl:
>> def f(x)Notice that calling a proc with the wrong number of arguments resulted in a warning instead of an exception and that all the arguments were pushed into a list in order to fit into one parameter.
>> p x
>> end
=> nil
>>
?> f(1, 2, 3)
ArgumentError: wrong number of arguments (3 for 1)
from (irb):56:in `f'
from (irb):56
>>
?> p = Proc.new {|x| p x}
=> #<Proc:0x0033a414@(irb):58>
>>
?> p.call(1, 2, 3)
(irb):58: warning: multiple values for a block parameter (3 for 1)
from (irb):60
[1, 2, 3]
=> nil
Here's another example of this. Notice how Ruby will or will not unpack a list depending on context:
>> [[1, 2]].each {|x| p x}I suspect this might be convenient, but I wonder if it occasionally bites unsuspecting programmers.
[1, 2]
=> [[1, 2]]
>> [[1, 2]].each {|x, y| p(x, y)}
1
2
=> [[1, 2]]
The lambda keyword lets you create an anonymous function. A lambda is an object of class Proc. However, lambdas and Proc.new behave differently when it comes to the return statement:
>> def return_testThe book says:
>> l = lambda { return } # This return exits the lambda.
>> l.call
>> puts "Still here!"
>> p = Proc.new { return } # This return exits return_test.
>> p.call
>> puts "You won't see this."
>> end
=> nil
>>
?> return_test
Still here!
=> nil
The matter of how Proc objects, code blocks, and lambdas related to each other has been, and still is, in a certain amount of flux. Don't be surprised if you see differences...from one version of Ruby to another. [p. 355]To confuse things further:
It's worth mentioning that lambda has a synonym: proc. However, because proc and Proc.new look and sound so similar, but don't do exactly the same thing, Matz has agreed in principle to phase out proc, leaving just Proc.new and lambda. [p. 356]Code blocks are not instances of Proc. They exist only in the syntax of Ruby. There is no such thing as a Block class or a Block object. However, you can convert a code block into a Proc object.
Ruby also supports bound methods:
>> class CYou can also unbind and rebind a bound method to another instance.
>> def f
>> puts 'f'
>> end
>> end
=> nil
>>
?> c = C.new
=> #<C:0x326a7c>
>> my_f = c.method(:f)
=> #<Method: C#f>
>> my_f.call
f
=> nil
Here's something I personally haven't seen in any other language. You can capture the act of subclassing a class, mixing in a module, or adding a method to a class as a runtime event. For instance, ActiveRecord in Ruby on Rails uses this functionality. Anytime someone subclasses ActiveRecord::Base, it captures the event (by defining a class method called "inherited") and adds the class to a list named "@@subclasses".
To alias a function under a new name, use the alias keyword: "alias :old_name :new_name". This is interesting, because in Python, you would just write "old_name = new_name". In Python, it's mandatory to use parenthesis when you call a function, so "old_name = new_name" is no different than saying "a = b" if a and b are both ints. The same isn't true of Ruby because "old_name = new_name" is essentially like "old_name() = new_name()" which doesn't even make sense. It's not a big deal, but it's just interesting to note that by making parenthesis non-mandatory, it leads to having an alias keyword. It also leads to writing "my_proc.call" instead of "my_proc()".
Everyone knows Ruby's string interpolation syntax: "Foo: #{bar}". However, Ruby also appears to support Python-like string interpolation:
>> puts "I weigh %.2f pounds." % 205.3Ruby returns the value of the last expression encountered in the function. This is convenient if you don't want to have to explicitly type "return". However, I think it can lead to some unexpected results. Namely, unless a function documents what it's going to return, you should just assume it's going to return garbage:
I weigh 205.30 pounds.
def do_somethingIn the above code, talk_to_user will return 5 even though 5 has absolutely nothing to do with talk_to_user. In Python, if you don't explicitly "return" something, the function will return None by default.
# Doing something.
5
end
def print_name(name)
puts "Name is #{name}"
do_something
end
def talk_to_user
puts "What's your name?"
name = gets
print_name(name)
end
talk_to_user
Okay, that's it. If you've made it this far, thanks for reading!
Comments
I'm somewhat amazed at how Python and Ruby are so fundamentally different and still so similar in syntax (superficially), code structure and programming approach.
I do really like the fact that in Ruby, you can just use "foo" as if it were a local variable, whereas actually it's a method that might be doing something interesting.
Python can do this too, but setting up properties is just a bit harder in Python, and there's the whole "self." bit.
def messenger
@messenger ||= StringIO.new
end
Now you can access messenger without using @, and it'll get initialized on first use.
Note that StringIO is a string buffer in both Ruby and Python ;)
Dir[File.expand_path("#{File.dirname(__FILE__)}/../app/controllers/*.rb")]
"include" adds the instance methods from a module to an entire class. You often use "include" to add instance methods to a class as instance methods.
Hence, I keep seeing code that does:
...
module Methods
def self.included(klass)
klass.class_eval do
extend ClassMethods
include InstanceMethods
...
Where ClassMethods and InstanceMethods are separate modules being mixed into klass.
delegate :link_to, :h, :to => 'ActionController::Base.helpers'
Rating = Struct.new(:name, :rating)
That's nice.