ちなみに

火曜日の空は僕を押しつぶした。

rbenv global 2.0.0-rc1 した話

ruby 2.0.0-rc1が出たので、普段使いのRubyを2.0にしてみました。

インストール

($ brew install openssl readline)
$ CONFIGURE_OPTS="--with-openssl-dir=`brew --prefix openssl` --with-readline-dir=`brew --prefix readline` --enable-bundled-libyaml" rbenv install 2.0.0-rc1
Downloading ruby-2.0.0-rc1.tar.gz...
-> http://ftp.ruby-lang.org/pub/ruby/2.0/ruby-2.0.0-rc1.tar.gz
Installing ruby-2.0.0-rc1...
Installed ruby-2.0.0-rc1 to /Users/tomohiro/.rbenv/versions/2.0.0-rc1

$ rbenv global 2.0.0-rc1

(01/24修正) configureに--enable-bundled-libyamlオプションを追加/brew install libyaml削除)

ReadlineとOpenSSLはHomeBrewで入れたものを使うように指定しています。

余談だけど、いつからかHomeBrewがビールの絵文字出すようになっててかわいい。

気になった変更

以下を読んで気になったところをピックアップしました。

キーワード引数

本物のキーワード引数が実装されたので、Hashを使わなくてもよくなった。

def config(key, default: true, force: false, **options)
  [key, default, force, **options]
end

p config
#=> wrong number of arguments (0 for 1) (ArgumentError)
p config(:a)
#=> [:a, true, false, {}]
p config(:b, force: true)
#=> [:b, true, true, {}]
p config(:c, default: false)
#=> [:c, false, false, {}]
p config(:d, new: true)
#=> [:d, true, false, {:new=>true}]
p config(:e, default: false, time_out: true)
#=> [:e, false, false, {:time_out=>true}]

**がきもいですね。

Module#refine と main.using

  • Module

    • added Module#refine, which extends a class or module locally. [experimental]
  • toplevel

    • added method:
      • added main.using, which imports refinements into the current file or eval string. [experimental]

紆余曲折の結果 Refinements がExperimentalなフィーチャとして入ることになったようです。

モンキーパッチすると広範囲に影響が及んでしまってつらいときに、影響を特定のスコープに閉じ込めるために使えます。 ただし、今回は実験的な追加であるために、usingしたファイルのスコープに限られ、モジュールやクラスのスコープに閉じ込めることができません。 本格的にはModule#usingみたいなのが実装されてからでしょうか。

あと、refineするとwarning: Refinements are experimental, and the behavior may change in future versions of Ruby!みたな警告がでます。

module Catable
  refine String do
    def nyan
      "#{self}にゃん!"
    end
  end
end

puts 'こんにちは'.nyan
#=> undefined method error

using Catable
puts 'こんにちは'.nyan
#=> こんにちはにゃん!

Module#prepend

  • Module
    • added method:
      • added Module#prepend which is similar to Module#include, however a method in the prepended module overrides the corresponding method in the prepending module.

ふつうはモジュールをincludeしても、同名のメソッドがinclude先のクラスなどで定義されていたらそちらが優先される。

module Catable
  def greet
    puts 'にゃん'
  end
end

class Japanese
  include Catable
  def greet
    puts 'こんにちは'
  end
end

Japanese.new.greet #=> こんにちは

追加されたModule#prependを使うと、モジュールで定義したメソッドを優先させることができる。

class Japanese
  prepend Catable
  def greet
    puts 'こんにちは'
  end
end

Japanese.new.greet #=> にゃん

もちろんModule#prependedModule#prepend_featuresも追加されているので、includeと同じように使えます。

%iの追加

  • Added %i and %I for symbol list creation (similar to %w and %W).

%wのようにSymbolのリストが作れます。べんりすぎる。大文字のIだと式展開とバッククォートが有効になります。

p %i[ a b c d e f] #=> [:a, :b, :c, :d, :e, :f]

デフォルトのエンコーディングがUTF-8

  • Default source encoding is changed to UTF-8. (was US-ASCII)

互換性を気にしなくて良い場合は、マジックコメントを書かなくてもよくなったということ。地味だけどうれしい。

Enumerable#Lazy

  • Enumerable
    • added method:
      • added Enumerable#lazy method for lazy enumeration.

Enumeratorオブジェクト(正確にはEnumerableEnumerator::Lazy)の評価を実際に値が必要になるまで遅延しくれる。(たぶんAR::Relationみたいなの) 各所で語られているので省略。

Kernel#Hash

  • Kernel
    • added method:
      • added Kernel#Hash conversion method like Array() or Float().

Kernel#HashKernel#Arrayなどのように、引数をto_hashを使ってHashにしようとしてくれます。 何がべんりかと言うと、引数がnilなのかHashなのか分からないときに、とにかく突っ込めばHashにしてくれるところです。

p Hash(a: 1) #=> {:a => 1}
p Hash(nil) #=> {}
p Hash([]) #=> {}

ただし、この用途では、NilClass#to_hHash#to_hも追加されているので、以下のようにすることもできます。

p nil.to_h #=> {}
p {a: 1}.to_h #=> {:a => 1}

ちなみにKernel#Arrayは以下のように使います。べんり

p Array(1) #=> [1]
p Array([1]) #=> [1]

Kernel#__dir__

  • Kernel
    • added method:
      • added Kernel#dir which returns a current dirname.

実行中のファイルが配置されているディレクトリが取れる。

File.dirname(__FILE__)

の代りに、

__dir__

と書ける。

Module#const_get の拡張

  • Module
    • extended method:
      • Module#const_get accepts a qualified constant string, e.g. Object.const_get("Foo::Bar::Baz")

ネストした書き方が出きるようになったみたいです。

Struct#to_h (OpenStruct#to_h)

  • Struct
    • added method:
      • added Struct#to_h returning values with keys corresponding to the instance variable names.

StructHashに変換したいときにto_hを自前で実装しなくてよくなったようです。 あとOpenStructにもいくつかメソッドが追加されて地味にべんりになってました。

Array#bsearch

  • Array
    • added method:
      • added Array#bsearch for binary search.

イナリサーチができる。 1000000個の配列から存在しない値を探してみたらEnumerable#findと比べて恐しく速かった。

require 'benchmark'
INFINITE = 1/0.0
a = 1000000.times.map { rand(100000000) }.sort
Benchmark.bmbm do |x|
  x.report { a.find {|i| i > INFINITE } }
  x.report { a.bsearch {|i| i > INFINITE }}
end

Rehearsal ------------------------------------
   0.110000   0.000000   0.110000 (  0.106729)
   0.000000   0.000000   0.000000 (  0.000009)
--------------------------- total: 0.110000sec

       user     system      total        real
   0.110000   0.000000   0.110000 (  0.106397)
   0.000000   0.000000   0.000000 (  0.000007)

TracePoint

  • TracePoint
    • new class. This class is replacement of set_trace_func. Easy to use and efficient implementation.

Kernel.set_trace_funcの代りに以下のように書けるようになりました。べんりですね。

module DefaultMethods
  def hoge
    p 'hoge'
  end
end

trace = TracePoint.new(:class) {|tp|
  tp.self.__send__(:include, DefaultMethods)
}

trace.enable do
  class Hoge
  end
end

class Piyo
end

Hoge.new.hoge
#=> "hoge"
Piyo.new.hoge
#=> undefined method `hoge' for #<Piyo:0x007ff394866228> (NoMethodError)

RubyGems

  • RubyGems RubyGems 2.0.0 features the following improvements:
    • gem search now defaults to --remote and is anchored like gem list.
    • Added --document to replace --rdoc and --ri. Use --no-document to disable documentation, --document=rdoc to only generate rdoc.
    • Only ri-format documentation is generated by default.

いまさら感ありますが、gem searchのときにデフォルトで--remoteが付くようになりました。 また--no-ri --no-rdoc--no-documentで済むようになったようです。 ついでにRDocがデフォルトでは生成されなくなったので、ちょっと速くなってます。

Syckの削除

  • yaml
    • Syck has been removed. YAML now completely depends on libyaml being installed.

psychに統一されました。syckを使っている場合は注意が必要。Railsアプリの移行ではまるところありそう。 また、psychがlibyamlを使っているので、インストールしておく必要があるようです。 HomeBrewならさくっと入ります。 (01/24追記) psychにlibyamlがバンドルされているので、configureのオプションで--enable-bundled-libyamlを渡してやると、優先的にそちらを使うようになります。 ruby-buildでインストールする場合は以下の方法でオプションを渡せます。

CONFIGURE_OPTS="--enable-bundled-libyaml" rbenv install 2.0.0-rc1

まとめ

2.0のリリースは2月24日(日/Ruby 20th anniversary!)です。移行しない手はないので、少なくとも普段使いは2.0にしましょう。

追記

@znz さんからつっこみをいただきました。ありがとうございます。タイポは修正済です。

追記2

@yhara さんよりコメントでつっこみをいただきました。ありがとうございます。

正確にはEnumerable::Lazy 残念、Enumerator::Lazyだ (2.0の紹介記事書く人の50%くらいはこの間違いしそうですね)

そもそもEnumerableオブジェクトって何って話ですね。 Enumeratorオブジェクト(正確にはEnumerator::Lazy) が正しいですね。

追記3

@kakutani さんのTweetに対して、Jeremy Kemper(!!!) さんよりフィードバックいただきました。 @znzさんのフィードバックと同様で、libyaml は不要だそうです、configureのオプションに--enable-bundled-libyamlを加えることでpsychにバンドルされたlibyamlが優先的に使うようになります。

(これっぽい: http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?revision=37977&view=revision via: http://d.hatena.ne.jp/nagachika/20121129/ruby_trunk_changes_37948_38001 )

追記4

@bitsweat さんがHomeBrewなどでインストールしたOpenSSLを優先的に使ってビルドするruby-buildのパッチを書いてくださったようです。これが入ると--with-openssl-dir=brew --prefix openssl`と書かなくて良くなります。LifeChanging。

https://github.com/sstephenson/ruby-build/pull/273

追記5

libyamlの件がNEWSとChangeLogに追記されました。@sora_h さん++

http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=38929