たしか
- 作者: Paolo Perrotta,角征典
- 出版社/メーカー: KADOKAWA/アスキー・メディアワークス
- 発売日: 2010/08/28
- メディア: 大型本
- 購入: 18人 クリック: 533回
- この商品を含むブログ (125件) を見る
ということで検索してみて、やっと見つかりました。
class Hash def method_missing(name) return self[name] if key? name self.each { |k,v| return v if k.to_s.to_sym == name } super.method_missing name end endhttp://www.goodercode.com/wp/convert-your-hash-keys-to-object-properties-in-ruby/
method_missingの引数は Symbol なので、ハッシュキーが Symbol のものを優先して返します。
ハッシュキーが Symbol じゃない場合には、要素全てをループしてキーを探していますね。
そういえば、このエントリのタイトルで「メソッド名」と書きましたが、JavaScript的に言えばプロパティですかね。
キーをString限定に
ですが、例えば JSON.parse で返されたハッシュだと、だいたいのキーは String だと思います。なので、
class Hash def method_missing(name) return self[name.to_s] if self.has_key?(name.to_s) super.method_missing(name) end end
でも良いかと(ついでにkey?をself.has_key?にしたのは私の趣味)。
キーの種類を String に決め打ちしてますので、キーが Symbol だと使えません。(Symbol キー版はもう少し下をご覧ください)
代入も('14/3/25追記)
下にありますが
http://enterpriserails.rubyforge.org/hash_extension/
のスクリプトでは代入も出来ます。のでそれに倣って、代入も出来るようにしました。
もちろん、キーは String 限定です。
class Hash def method_missing(name, *params) name_str = name.to_s # substitution if name_str[-1] == "=" self[name_str[0..-2]] = params.first # self has name as a key? elsif self.has_key?(name_str) self[name_str] # super else super.method_missing(name, *params) end end end
Symbol 優先で代入も出来るように('14/3/25追記)
JSON相手だから String 優先で良いよな、と思っていたのですが、最近わたしがよく使っているTwitter gemだと、#to_h または #attrs(おなじものです)で返されてくるハッシュのキーが Symbol。なので Symbol キー優先版を書いてみました。上げておきます。
代入もキーが Symbol のものを使うようにしました。
class Hash def method_missing(name, *params) name_str = name.to_s # substitution if name_str[-1] == "=" self[name_str[0..-2].to_sym] = params.first # self has name as a key? elsif self.has_key?(name) # name.class == Symbol self[name] elsif self.has_key?(name_str) # name.class == String self[name_str] # super else super.method_missing(name, *params) end end end
おまけ:訳してみた
XMLやJSONデータをたくさん使うソフトを書いてるんだけど、データを表示させるときに book[‘author’][‘first_name’] じゃなくて book.author.first_name と呼びたいんだよね。でググったらこういう記事を見つけたんだ。ここに書き写しとくね。
class ::Hash # add keys to hash def to_obj self.each do |k,v| if v.kind_of? Hash v.to_obj end k=k.gsub(/\.|\s|-|\/|\'/, '_').downcase.to_sym ## create and initialize an instance variable for this key/value pair self.instance_variable_set("@#{k}", v) ## create the getter that returns the instance variable self.class.send(:define_method, k, proc{self.instance_variable_get("@#{k}")}) ## create the setter that sets the instance variable self.class.send(:define_method, "#{k}=", proc{|v| self.instance_variable_set("@#{k}", v)}) end return self end endちゃんと動くよ。キーをハッシュ呼び出しメソッドにしてくれてる。
でもさ、なんかイマイチなんだよね。何でって、キーの9割は多分ほとんど使わないよね。使わないメソッドのためにいちいちメソッド作るのって手間掛かるだけじゃないの?
ということで魔法のメソッド method_missing を使ってみた。
class ::Hash def method_missing(name) return self[name] if key? name self.each { |k,v| return v if k.to_s.to_sym == name } super.method_missing name end end僕の目的により合うメソッドになったね。シンプルだし、メソッド名のSymbolがハッシュキーの場合にはそのまま、ハッシュキーがStringとかその他の場合にはループでキーを探すんだよ。
- #3 by Ajasja Ljubetič at May 5th, 2010
なんで OpenStruct 使わないの?
- #4 by kerry at May 5th, 2010
知らなかったよw
- #5 by kerry at May 5th, 2010
JSONフィードが元の、入れ子になったハッシュを扱うときに、OpenStructだと望み通りに行かないんだ。
例えばmy_hash = { :a => :b, :c => {:d => :e, :f => :g} }ってハッシュをOpenStructで扱うと:eを呼ぶときに openstruct.c[:d] って書かなきゃいけない。僕はもっとシンプルに my_hash.c.d と書きたいのさ。
- #10 by Dan at August 16th, 2011
hash_extensionも同じようなことをしてるよ。しかもセッターメソッドも付いてるよ。
*1:つまり、かなりてきとー