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 Slate や Creating DSLs with Rubyで使われているBlankSlateを利用した。
最後の function の無理やり具合が、RubyとJavaScriptの一つの境界線なんだと思う。
追記:後で見てタイトルが意味不明だったので、タイトルを変更。