Skip to content

Instantly share code, notes, and snippets.

@maraigue
Created September 26, 2013 07:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save maraigue/6710983 to your computer and use it in GitHub Desktop.
Save maraigue/6710983 to your computer and use it in GitHub Desktop.
[Ruby] Hashのキーをドット繋ぎでもアクセス可能にする(hoge[:piyo] → hoge.piyo)
#!/usr/bin/env ruby
# -*- coding: utf-8 -*-
class Hash
# hashobj[:hoge] を hashobj.hoge でもアクセス可能にする
# ・値の読み取りはどんなキーでも対象
# ・値の書き込みはすでに存在するキーのみ対象
# ・すでに存在するメソッドと名前が重複した場合は、
#  すでに存在するメソッドへのアクセスが優先される
# ・当該名称のキーがStringとSymbolの両方で存在する場合は例外
#  (例えば、{:a => 1, "a" => 3}.method_type_access.a は例外)
def method_type_access
instance_eval do
def method_missing(method_name, *args)
n = method_name.to_s
if n[-1] == "="
n = n[0..-2]
access = :write
raise ArgumentError, "wrong number of arguments(#{args.size} for 1)" if args.size != 1
else
access = :read
raise ArgumentError, "wrong number of arguments(#{args.size} for 0)" if args.size != 0
end
if self[n]
if self[n.intern]
raise KeyError, "duplicated keys: #{n.inspect} and #{n.intern.inspect}"
else
target = n
end
elsif self[n.intern]
target = n.intern
else
raise NameError, "undefined method `#{method_name}' for: #{self.inspect}:#{self.class}"
end
if access == :write
self[target] = args.first
else
self[target]
end
end
end
self
end
end
if $0 == __FILE__
# 普通に使う例
puts "[hash1]"
hash1 = {:a => 1, :b => 2, :object_id => "hoge"}
hash1.method_type_access
p hash1.a
p hash1.b
hash1.b = 5
p hash1.b
begin
hash1.c # 例外発生
rescue Exception => e
puts "ERROR(#{e.class}): #{e}"
end
p hash1.object_id # "hoge"ではなく、本来のobject_idメソッドが呼び出される
hash1.object_id = "piyo" # でもこれはハッシュの値へのアクセスになる
p hash1[:object_id] # なのでここでは"piyo"が表示される
# キーが重複している例
puts "[hash2]"
hash2 = {:a => 1, "a" => 3}
hash2.method_type_access
begin
hash2.a # 例外発生
rescue Exception => e
puts "ERROR(#{e.class}): #{e}"
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment