Ruby 1.9.1 Favorite New Features
I just grabbed Ruby 1.9.1 today (it was released January 30th 2009, so I’m 24 hours late) and decided to play with it. I’m interested in the changes they’ve been making to the parser, something I’m particularly interested in given my Ruby documentation tool, YARD, is highly dependent on syntax (and I’m messing with the parser), but some of the other changes are quite nice too. I came across this nice migration guide that covers some of the basic changes, but here is a more distilled list of my favorites:
1. Hash entries are Ordered!
h = { cat: 1, zebra: 2, dog: 3 }
h.delete(:zebra)
h[:monkey] = 4
h.each {|key, val| puts "#{key}: #{val}" }
# Guaranteed to print:
# cat: 1
# dog: 3
# monkey: 4
I’ve been praying for this for who knows how long. Everyone knows hashes don’t maintain order internally, but much of Ruby development makes use of Hashes as dictionaries because of the simplistic syntax. The problem is that dictionaries, unlike straight hash objects, usually require order to be maintained. There are tons of places in YARD where I had to resort to nested Arrays (eg. [[key, val], ...]
) to maintain order of a set of associated objects… I’ve even written countless OrderedHash classes to solve this problem. This is definitely the biggest feature in 1.9 to me.
PS. You might have noticed the Pythonish {a: 1}
syntax— yep, that’s new too, but it’s not quite cool enough to make it’s own item in our list but it’s really convenient to type.
2. Symbols are now Intuitively Comparable
YARD calls #to_s on about a million things. Last I checked, Rails does too. This is because internally we try to keep everything stored as a Symbol for obvious efficiency. The problem is that when writing API’s we’re usually allowing developers to pass in a String or a Symbol, which means comparison is always annoying. It looks like the Ruby devs looked at a lot of this code, puked all over their keyboards, and cleaned up both their keyboards and the code to deal with this. We can now do things like:
:hello =~ /e/ # => 1
:hello === "hello" # => true
:hello[1] == "e" # => true
3. Enumerations on a Hash return a Hash
For the same reasons as above, this is awesome. Performing a #select on a hash was always a pain in Ruby 1.8 because you would end up with an Array… totally not a Hash. This inconsistency has been dealt with and we can now do:
{a:1,b:2,c:3}.reject {|k,v| k == :b }
=> {:a=>1, :c=>3}
4. Proper Unicode Support
Let’s not get too far into this list without giving credit for the awesome work Ruby 1.9 has done in fixing the Unicode support. In short, it is now possible to do things like:
"Hi!".encode("utf-16be") # => "x00Hx00ix00!"
File.read('test.txt', encoding: 'utf-8').encoding
We also get Enumerations like #each_char
that iterate properly over such strings. There’s plenty more goodies regarding Unicode, but I still don’t know all of the details. Interestingly enough, I ran into a Ruby Unicode problem the other day, so I think I’ll be revisiting the problem with these new tools soon and write about what happened.
5. Regexps get Look-ahead/Look-behind
Probably the most powerful feature of regular expressions have been missing from Ruby for the longest time. I can’t remember any specific times when I hit this limitation, but I imagine it will simplify a lot of hacky code attempting to work around the feature omission in 1.8.
6. Object #tap
This is an easy one because anyone could have implemented it, but it’s a nice way to silently hook into a method call chain. The Ruby 1.9 docs give a cool example of this, but I think a better example is when dealing with method calls that aren’t chainable like Hash#delete
:
# In 1.8 we need to do this because #delete returns nil:
h = {:a => 1, :b => 2, :c => 3}
h.delete(:b)
h.each {|k,v| puts k }
# In 1.9 we can do this in one line:
{a:1,b:2,c:3}.tap {|o| o.delete(:b) }.each {|k,v| puts k }
7. Fibers are lighter Threads
Still have yet to dig into this one, but Dave Thomas covers the new Fiber class. In short, they’re basically lambdas encapsulated in threads— but of course it’s not the implementation but the way you use these guys that makes them quite elegant. I can think of a few things right off the top of my head that involve the quick creation of threads from inline storable procedures.
8. New Hash Key Syntax
UPDATE: Okay, I wasn’t going to put this in the list until I realized how the syntax can be applied to method calls. Consider the following 1.8 code:
def open(filename, opts = {}) end
open('test.txt', :access => :read, :close => true)
The same code in 1.9 can be called as:
def open(filename, opts = {}) end
open('test.txt', access: :read, close: true)
That is cool.