RubyでJavaScriptのオブジェクトのように振舞う何か

JavaScriptを触っていて気持ちいい一つの原因として、ハッシュとオブジェクトの境界が無いことがある。データとして扱いやすいため、メタ操作もやりやすい。先の勉強会の後の議論で出てきたJavaScript的Hashを実装。

class BlankSlate
  instance_methods.each { |m| undef_method m unless m =~ /^__/ }
end

class JSHash < BlankSlate

  def initialize(hash)
    @hash = hash
  end
  
  def method_missing(sym, *args) 
    name = sym.to_s
    if name =~ /=$/
      name = name[0..-2]
      val = args[0]
      if val.respond_to?(:call)
        JSHash.__send__(:define_method,name) { |*_args| 
          val.call(self,*_args)
        }
      end
      @hash[name] = val
      return self
    else
      return @hash[sym] || @hash[name]
    end
  end

  def inspect
    return @hash.inspect
  end

  def [](key)
    return @hash[key]
  end
  
  def []=(key, value)
    return @hash[key] = value
  end
  
  # このあたりは好みの問題
  def each
    @hash.each {|i|
      yield i[0]
    }
  end
  def size
    return @hash.size
  end
  
end

class Hash
  def to_h
    return self.inject(JSHash.new({})) {|memo,i|
      k,v = i
      # SymbolはJSに合わせてStringに変換
      memo[ (k.kind_of? Symbol) ? k.to_s : k ] = v
      memo
    }
  end
end

alias function lambda

##################################################
# 使い方サンプル

a = {
  :aaa => "Inou", "bbb" => "Kotoshiba", 
  :ccc => 7550001, "string key" => "value!",
  8888 => 9999
}

b = a.to_h

# プロパティでアクセスできます
puts b.aaa, b.bbb, b.ccc, b["string key"], b[8888]

# 変更できます
b.aaa = "object!"
b.bbb = "Iwahana"
b["string key"] = 12345678
b[8888] = nil

# toString のようなもの
puts b.inspect
# 数とか
puts "size = #{b.size}"

# eachで総当りのアクセスも可能
b.each {|i|
  puts "key:#{i}  value:#{b[i]}"
}

# function はちょっと苦しい
b.hello = function {|this,i| # this...
  puts "Hello #{i} #{this.aaa}"
}
b.hello("JavaScript") # => Hello JavaScript object!

c = {}.to_h
c.aaa = "function..."
c.hello = b["hello"] # b.hello はムリ
c.hello("Ruby") # => Hello Ruby function...

今回は、Starting with a Blank SlateCreating DSLs with Rubyで使われているBlankSlateを利用した。

最後の function の無理やり具合が、RubyJavaScriptの一つの境界線なんだと思う。

追記:後で見てタイトルが意味不明だったので、タイトルを変更。