Sunday, February 1, 2009

Migrating to Ruby 1.9

Bruce Williams
Rubyist since 2001

Perpetrator of much random Ruby hackery, language tourist

(Full-time since 2005)

Open source developer, contributer, technical editor, designer Occasionally blogs at http://codefluency.com

Ruby 1.8
Stable. The syntax and language features you know and probably love. The performance profile you know and might hate a little.

Ruby 1.9
Unstable, transitional. Many new syntax and language features. Better performance, especially for computationally intensive operations.

1.9 is a hint.

1.9 is a hint.
Get ready for 2.0.

Standard Library + ~ rubygems (+ prelude & ruby --disable-gems), rake, json (pure, ext), ripper, probeprofiler, securerandom, HMAC digests csv replaced by FasterCSV implementation soap, wsdl, base64, some rarely used, old libraries

Parser Changes

Flexibility and Obscurity (for the moment)

Parser Changes
  • New Hash Literal
    {a: "foo"} # => {:a=>"foo"} {a: "bar", :b => "baz"} # => {:a=>"bar", :b=>"baz"}
  • New Proc Literal
    multiply_by_2 = ->(x) { x * 2 } # => # multiply_by_2.(4) # => 8
  • Splat more flexibly
    names = %w(joe john bill) [*names, 'jack'] # => ["joe", "john", "bill", "jack"]
  • Method parameter ordering
    def say(language=:english, text) puts Translator[language].translate(text) end say "hello" # hello say :spanish, "hello" # hola
Migration Risk Factors
Text processing “Clever” assignment with blocks Some Hash enumerations Metaprogramming, code generation

Tests are Good
I was surprised at how much work my 11th hour integration of the FasterCSV code was. It was a pure Ruby library that really didn't do a lot of fancy tricks, but I had to track down about 20 little issues to get it running under Ruby 1.9. Thank goodness it had terrific test coverage to lead me to the problem areas.

- James Edward Gray II
Follow-up at http://blog.grayproductions.net/articles/getting_code_ready_for_ruby_19

Block Local Variables
Arguments are always local

Ruby 1.8
item = 1 2.upto(4) do |item| p item end # Outputs: # 2 # 3 # 4 item # => 4
Ruby 1.9
item = 1 2.upto(4) do |item| p item end # Outputs: # 2 # 3 # 4 item # => 1

Shadowing Variables
You’ll get a warning

Ruby 1.8
i=1 lambda { |i| p i }.call(3) # Outputs #3 i # => 3
Ruby 1.9
i=1 lambda { |i| p i }.call(3) # Outputs #3 i # => 1
-e:2: warning: shadowing outer local variable - i

Shadowing Variables
Locals, but warned
No local, reassigns
d=2 -> { d = 1 }.() d # => 1

Local, shadowed
d=2 ->(;d) { d = 1 }.() d # => 2
-e:2: warning: shadowing outer local variable - d

(Ruby 1.9)
Hash#select (etc)
Ruby 1.8
conferences.select do |data| p data end # [:euruko, "Prague"] # [:scotland_on_rails, "Edinburgh"] # [:railsconf_europe, "Berlin"]

Changes to yielded arguments

Ruby 1.9
conferences.select do |data| p data end # :euruko # :scotland_on_rails # :railsconf_europe conferences.select do |name, city| p [name, city] end # [:euruko, "Prague"] # [:scotland_on_rails, "Edinburgh"] # [:railsconf_europe, "Berlin"]
warning: multiple values for a block parameter (2 for 1)

Hash#select (etc)
Returns a Hash

Ruby 1.8
conferences.select do |name, _| name == :scotland_on_rails end # => [[:scotland_on_rails, "Edinburgh"]]
Ruby 1.9
conferences.select do |name, _| name == :scotland_on_rails end # => {:scotland_on_rails=>"Edinburgh"}

Features
Lots of changes, some big ones

Multilingualization
(m17n)
There is one type of string, and the encoding is mutable Strings are no longer Enumerable (use #each_char, #each_line, etc) The encoding is ‘lazy’ and can be set by probing with String#ascii_only? and String#valid_encoding?. Various ways to set default encoding (commandline, magic comments)
String#[] now returns a String, not a Fixnum (use ord)

[:ASCII_8BIT, :Big5, :BIG5, :CP949, :EUC_JP, :EUC_KR, :EUC_TW, :GB18030, :GBK, :ISO_885 9_1, :ISO_8859_2, :ISO_8859_3, :ISO_8859_4, :ISO_8859_5, :ISO_8859_6, :ISO_8859_7, :IS O_8859_8, :ISO_8859_9, :ISO_8859_10, :ISO_8859_11, :ISO_8859_13, :ISO_8859_14, :ISO _8859_15, :ISO_8859_16, :KOI8_R, :KOI8_U, :Shift_JIS, :SHIFT_JIS, :US_ASCII, :UTF_8, :UTF _16BE, :UTF_16LE, :UTF_32BE, :UTF_32LE, :Windows_1251, :WINDOWS_1251, :BINARY, :I BM437, :CP437, :IBM737, :CP737, :IBM775, :CP775, :CP850, :IBM850, :IBM852, :CP852, :IBM85 5, :CP855, :IBM857, :CP857, :IBM860, :CP860, :IBM861, :CP861, :IBM862, :CP862, :IBM863, :CP 863, :IBM864, :CP864, :IBM865, :CP865, :IBM866, :CP866, :IBM869, :CP869, :Windows_1258, : WINDOWS_1258, :CP1258, :GB1988, :MacCentEuro, :MACCENTEURO, :MacCroatian, :MA CCROATIAN, :MacCyrillic, :MACCYRILLIC, :MacGreek, :MACGREEK, :MacIceland, :MACICE LAND, :MacRoman, :MACROMAN, :MacRomania, :MACROMANIA, :MacThai, :MACTHAI, :M acTurkish, :MACTURKISH, :MacUkraine, :MACUKRAINE, :CP950, :EucJP, :EUCJP, :EucJP_ms, :E UCJP_MS, :EUC_JP_MS, :CP51932, :EucKR, :EUCKR, :EucTW, :EUCTW, :EUC_CN, :EucCN, : EUCCN, :GB12345, :CP936, :ISO_2022_JP, :ISO2022_JP, :ISO_2022_JP_2, :ISO2022_JP2, :ISO 8859_1, :Windows_1252, :WINDOWS_1252, :CP1252, :ISO8859_2, :Windows_1250, :WIN DOWS_1250, :CP1250, :ISO8859_3, :ISO8859_4, :ISO8859_5, :ISO8859_6, :Windows_1256, :WINDOWS_1256, :CP1256, :ISO8859_7, :Windows_1253, :WINDOWS_1253, :CP1253, :IS O8859_8, :Windows_1255, :WINDOWS_1255, :CP1255, :ISO8859_9, :Windows_1254, :WI NDOWS_1254, :CP1254, :ISO8859_10, :ISO8859_11, :TIS_620, :Windows_874, :WINDOW S_874, :CP874, :ISO8859_13, :Windows_1257, :WINDOWS_1257, :CP1257, :ISO8859_14, :I SO8859_15, :ISO8859_16, :CP878, :SJIS, :Windows_31J, :WINDOWS_31J, :CP932, :CsWindo ws31J, :CSWINDOWS31J, :MacJapanese, :MACJAPANESE, :MacJapan, :MACJAPAN, :ASCII, :A NSI_X3_4_1968, :UTF_7, :CP65000, :CP65001, :UCS_2BE, :UCS_4BE, :UCS_4LE, :CP1251]

Read a file with File.read
File.read("input.txt").encoding # => # File.read("input.txt", encoding: 'ascii-8bit').encoding # => #

Read a file with File.open
result = File.open("input.txt", "r:euc-jp") do |f| f.read end result.encoding # => # result.valid_encoding? # => true

Regular Expressions
Integrated “oniguruma” engine Same basic API Much better performance Support for encodings Extended Syntax
Look-ahead (?=), (?!), look-behind (?<), (?), backreferences, etc

Named Groups
"His name is Joe".match(/name is (?\S+)/)[:name] # => "Joe"

Enumerable
Enumerator built-in, returned from Enumerable methods (and
those in Array, Dir, Hash, IO, Range, String or Struct that serve the same purposes). Added Enumerator#with_index

Map with index
%w(Joe John Jack).map.with_index do |name, offset| "#{name} is #{offset + 1}" end # => ["Joe is #1", "John is #2", "Jack is #3"]

Enumerable
reduce (inject)

[1,2,3,4].reduce(:+) # => 10
New Enumerable methods take, group_by, drop, min_by, max_by, count, and others.

take
array = [1, 2, 3, 4, 5] array.take(3) # => [1, 2, 3] array # => [1, 2, 3, 4, 5]
drop
array = [1, 2, 3, 4, 5] array.drop(3) # => [4, 5] array # => [1, 2, 3, 4, 5]

Hash Changes
Insertion order preserved
conferences = { euruko: 'Prague', scotland_on_rails: 'Edinburgh' } conferences[:railsconf_europe] = 'Berlin' conferences.each do |name, city| p "#{name} is in #{city}" end # "euruko is in Prague" # "scotland_on_rails is in Edinburgh" # "railsconf_europe is in Berlin" conferences.delete(:scotland_on_rails) conferences[:scotland_on_rails] = 'Edinburgh' conferences.each do |name, city| p "#{name} is in #{city}" end # "euruko is in Prague" # "railsconf_europe is in Berlin" # "scotland_on_rails is in Edinburgh"

Object
Added tap

thing = Thing.new.tap do |thing| thing.something = 1 thing.something_else = 2 end

Lambda Changes
Obfuscation, ahoy!
New literal syntax more flexible Not possible in { | | ... } style literals

Passing blocks
m = ->(x, &b) { b.(x * 2) if b } m.(3) do |result| puts result end # Output #6

Default arguments
->(a, b=2) { a * b }.(3) # => 6

Symbol Changes
Less sibling rivalry
Added to_proc Added =~, [] like String (to_s less needed), sortable Object#methods, etc now return an array of symbols

Indexing into
:foo[1] # => "o"

Comparing with a String
:this === "this" # => true

Fibers

“Semi-coroutines”
Similar to Python’s generators Owe method naming lineage to Lua Out of scope of the talk, but very cool For some examples, see:
http://pragdave.blogs.pragprog.com/pragdave/2007/12/pipelines-using.html (and follow-up) http://www.davidflanagan.com/blog/2007_08.html (older) Revactor project (Actors in 1.9 using Fibers + Threads) InfoQ, others...

This was really just an introduction.

Bruce Williams

bruce@codefluency.com

http://www.scribd.com/doc/2589469/Migrating-to-Ruby-19

No comments: