tags:

views:

1431

answers:

15

With the Ruby 1.9.2 release on the horizon, it's time to get developers excited about Ruby 1.9. What are some nice things you can do in Ruby 1.9 that you can't do in Ruby 1.8?

+7  A: 

I personally love the new hash syntax: {:a => 1} becomes {a:1}

James Deville
Agreed, but it'll be a while before I start using it in code I contribute back to the community. It's not quite cool enough to warrant breaking backwards compatibility all by itself. (I do use it in tests, though, or with code that wouldn't work in 1.8 because of other features I've used.)
SFEley
Agreed. 1.8.x is still too prevalent to use a lot of the 1.9.x features.
James Deville
+8  A: 

Enumerators.

["a", "b", "c"].map {|elem, i| "#{elem} - #{i}" }
# => ["a - ", "b - ", "c - "]
["a", "b", "c"].each_with_index.map {|elem, i| "#{elem} - #{i}" }
# => ["a - 1", "b - 2", "c - 3"]

Enumerable methods returns an instance of Enumerator when no block is passed to it. In this case, it's used to give map an index argument, taken from each_with_index.

This has been backported to 1.8.7 as well.

August Lilleaas
+8  A: 

I like Enumerators a lot -- not just for existing types, but writing my own collections as Enumerator classes. Since switching to 1.9, I've twice had to build API adapters to external Web services that pull down large JSON or XML result sets. Sometimes I'd be capped on how many records I could retrieve at once, meaning I'd need to do multiple requests. (Get the first 500, then get records 501 to 1000, etc.)

The "old" way I'd have processed these would have been to grab the first batch, iterate over it all at once with .each or .collect, and create an equal-sized array of Ruby objects. If I couldn't get all the records in one request, I'd loop through API requests as well, adding to the array each time. This means all the time is front-loaded, perceived as a slow retrieval, and I'm chewing up a lot of memory: for the source data, for an equal number of Ruby objects, and sometimes for intermediate array operations. This is wasteful when I'm probably only operating on one object at a time.

With Enumerators, I can grab the first batch, hold the source data as my "authoritative" collection, and process and yield each Ruby object as I step into it. When I pass the last element, if I know there's more data to be pulled from the source, I can make the next API call then. (I.e., lazy loading.) This means a much faster return on the retrieval method call, and much better memory usage. Each Ruby object is eligible for garbage collection as soon as I'm done with it and have moved to the next one.

An abstract implementation of the idea looks like this:

class ThingyCollection < Enumerator
  attr_reader :total

  # Returns a new collection of thingies.
  def initialize(options={})

    # Make the request for the first batch
    response = ThingyAPIClient.get_thingies(options)
    @total = response.total   # Number of ALL thingies, not just first batch
    records = response.data  # Some array of JSON/XML/etc. from the API 

    # Create a closure which serves as our enumerator code
    enum = Proc.new do |yielder|
      counter = 0              # Initialize our iterator
      while counter < @total

        # If we're at the end of this batch, get more records
        if counter == records.length  
          more = ThingyAPIClient.get_next_thingies(counter, options)
          records += more.data
        end

        # Return a Ruby object for the current record 
        yielder.yield Thingy.new(records[counter])   
        counter += 1
      end
    end

    # Pass that closure to the Enumerator class
    super(&enum)
  end
end

Once you have that, you can walk over them like:

thingies = ThingyCollection.new(foo: bar) # Whatever search options are relevant
puts "Our first thingy is #{thingies.next}"
puts "Our second thingy is #{thingies.next}"
thingies.rewind
thingies.each do |thingy|
  do_stuff(thingy)
end

What do you lose? Mostly the ability to easily jump to a particular element by reference. (Which means you also lose "last," sorts, etc.) Just getting .next and a couple of .each variants is not as rich as array functionality, but for my most common use cases it's all i need.

Yes, you can do this with Ruby 1.8.7 thanks to backporting. But 1.9 is much faster at it thanks to the internal use of fibers. And if it hadn't been for 1.9, there wouldn't have been a 1.8.7, so I've decided it still qualifies as my favorite 1.9 feature.

SFEley
+2  A: 

Oh, and: it's more than twice as fast. Faster still if you're building a multithreaded application with lots of I/O delays. With all the work that still goes into trying to pump a bit more performance out of 1.8, or fix its threading, etc., it amazes me that people aren't getting more excited about 1.9's speed or its native threads.

SFEley
I suppose they aren't getting more excited because 1.9 is a dev branch. It is not 2.0 (and will probably undergo a lot fluctuation until we get there).
hurikhan77
It may be transitional but it's most certainly not a "dev branch." We had years of 1.9 release candidates before we got to 1.9.1, which was declared as production-ready. And people _are_ shifting en masse from 1.8.6 to 1.8.7 (witness recent releases of JRuby and REE) so I don't think you can simply call it resistance to any change at all.
SFEley
This is bullshit. (Pardon my french.) The even=stable, odd=unstable version numbering scheme was abandoned years ago. Ruby 1.9 is the current stable, production version of the language. Period.
Jörg W Mittag
+2  A: 

instance_exec, and class_exec are great new features, but to me it's mainly the small changes (that have already been backported to 1.8.7). Things like Method#owner is great - ever wondered where exactly in the inheritance chain a particular method was defined? my_object.method(:blah).owner will tell you :)

Other things i like about 1.9 are the more consistent scoping rules esp. in eval contexts. It was a silly omission (IMO) that constants and class variables were not looked up in an instance_eval, 1.9 fixes this :)

banister
Nice, I didn't know about #owner. The parameter inspection features are nice too.
James Deville
+2  A: 

I like Symbol#to_proc, which saves you having to write a lambda every time you use a higher-order function. So whereas summing an array used to be arr.inject(0) {|memo, val| memo + val}, now you can just write arr.inject(&:+), and rather than houses.collect {|house| house.price}, you can write houses.collect(&:price).

Some libraries (e.g. ActiveSupport) provide the same functionality under 1.8, but it's still nice to have it be part of the core language, and the 1.9 implementation is a lot better optimized than the library approach.

Chuck
I don't know if it is matter of backporting, but this feature is already in 1.8, so I wouldn't consider it brand new ;)
samuil
No, this feature did not exist in 1.8.6. Much like standard Enumerators, it was backported into 1.8.7 after being introduced in 1.9, but it is a novel 1.9 feature.
Chuck
banister
@banister: I agree about the syntax, but it's still preferable to writing out a whole function when all you want to say is "use `+`". I'd rather have utilitarian syntax than mounds of unnecessary code.
Chuck
+4  A: 

Ruby 1.9 has different block behaviors:

  • Block parameters are always local to their block, and invocations of the block never assign values to existing variables:

  • Block syntax has been extended to allow you to declare block-local variables that are guaranteed to be local, even if a variable by the same name already exists in the enclosing scope.

Threads are also different:

  • Ruby 1.8 uses only a single native thread and runs all Ruby threads within that one native thread. This means threads are very lightweight but they never run in parallel.

  • Ruby 1.9 is different, it allocates a native thread for each Ruby thread. But because some C libraries used are not themselves thread-safe, Ruby is very conservative and never allows more than one of its native threads to run at the same time (this restriction may be relaxed in later releases)

Other minor change is the inclusion of RubyGems in the load path, no need to require "rubygems" anymore.

Miguel Fonseca
The threads have nothing to do with Ruby. They are purely an implementation issue. JRuby, IronRuby, Rubinius have *exactly* the same threading implementation for Ruby 1.8 and Ruby 1.9. JRuby has had native threads for Ruby 1.8 (and, indeed, Ruby 1.6) for almost a decade. Also, at least JRuby *does* run multiple threads in parallel and has done so for years.
Jörg W Mittag
I'm talking about the reference implementation (MRI)
Miguel Fonseca
The stricter block behaviour is great. Non-block local variables was was one of the biggest gotchas in 1.8.
guns
+3  A: 

Full native support for multibyte character encodings, particularly Unicode.

Ken Bloom
Ruby 1.9's comprehensive built-in unicode support is a huge change that gives ruby first class multibyte/unicode support.
shinzui
+17  A: 

I can't believe that this hasn't been mentioned yet: the single biggest feature of Ruby 1.9.2+ is that for the first time in 17 years, Ruby will have a specification.

You might have heard that all release schedule for Ruby 1.9.2 (which was supposed to be released in the Spring of 2010) has been canceled, and this is the reason: first, a complete specification of Ruby 1.9.2 will be developed in the RubySpec project, then Ruby 1.9.2 (the programming language) will be released, and only then will YARV 1.9.2 be released, after it passes the RubySpec testsuite.

This is exactly backwards from how it worked before: first MRI was released, then all the other implementors read the (not very well designed, and generally badly documented) C source code of MRI, to try and figure out what the heck that new feature was supposed to do, then they tried to write executable specifications, and only then did they even have a remote chance of actual compatibility. But by that time, generally, a new version of YARV had already been released, and the cycle began anew ... Not to mention that the maintainers of MRI and YARV didn't even run the RubySpecs.

This has huge ramifications. For example, despite the fact that currently more than a dozen different Ruby implementations are in active development, and over the years of its existence there have been more than 30 different implementations of the Ruby programming language, this fact has not been acknowledged by the maintainers of the Ruby programming language. For them, Ruby and MRI (or more recently Ruby and YARV) have always been one and the same thing: MRI was both the language and the execution engine, Ruby was both the execution engine and the language. The "specification" of the Ruby programming language was the C source code of MRI.

As of five weeks ago, this has changed: now, the official specification of the Ruby programming language (at least version 1.9.2 and later) is the executable testsuite of the RubySpec project. And YARV is just another Ruby implementation, completely equal to MacRuby, IronRuby, JRuby, Cardinal, tinyrb, SmallRuby, BlueRuby, MagLev and the others.

This means that so called "alternate" implementations (which as of now should no longer be called "alternate" because YARV has lost its special status) now have a chance to actually catch up with the latest language features implemented in YARV. In fact, since most of the other implementations are actually both much better designed and implemented in much better languages than YARV (which is basically a huge spaghetti mess of C), plus having more manpower, it is entirely plausible that other implementations will actually be Ruby 1.9.2 compliant before YARV.

Jörg W Mittag
RubySpecs is great. It's a huge win for the ruby community.
shinzui
While not exactly answering his question, this was a most excellent reply/rant.
jcm
+6  A: 

Ruby 1.9.2 supports gettting information about method parameters. You can get the name of the parameters, and information about them such as optional, required or block.

View Method#params for an example.

shinzui
+1  A: 

Ruby 1.9 Fibers offers a powerful new concurrency construct. Ruby Fibers: 8 Useful Reads On Ruby’s New Concurrency Feature has links to fibers related articles.

shinzui
+2  A: 

YARV. The new Ruby VM in 1.9 offers a new modern VM that is significantly faster.

shinzui
+1  A: 

Improved regular expression support. Ruby 1.9 supports named regex groups -- among many other improvements -- that you can recall later in your regex. Dave Thomas gives a great example.

shinzui
+4  A: 

Hashes are ordered in ruby 1.9. It's very useful when implementing some algorithms. You have to depend on a gem or write your own ordered hash in ruby 1.8.

shinzui
I considered this to be a newbie-friendly feature, until I used it recently to elegantly improve a production Sinatra app. Was pretty happy about it then.
guns
+1  A: 
Chris McCauley