OpenStruct のコードリーディング(Ruby)
~/Documents/Ruby/コードリーディング/ostruct.rb の一部
class OpenStruct def initialize(hash=nil) #Hash{@table} @table = {} if hash hash.each_pair do |k, v| k = k.to_sym @table[k] = v end end end attr_reader :table def modifiable #@modifiable >> Hash{@table} begin @modifiable = true #@modefiable は他に出てこない。 rescue raise RuntimeError, "can't modify frozen #{self.class}", caller(3) end @table end protected :modifiable def new_ostruct_member(name) #String{name} -> Symbol{name} >> Symbol{name} name = name.to_sym unless singleton_class.method_defined?(name) #特異メソッドが定義されていなければ定義する。Object#singleton_class define_singleton_method(name) { @table[name] } define_singleton_method("#{name}=") { |x| modifiable[name] = x } end name end protected :new_ostruct_member def method_missing(mid, *args) # mid は呼び出しに失敗したメソッド名 #Symbol{mid}, *args -> len, mname, err >> Object{args[0] or @table[mid]} len = args.length if mname = mid[/.*(?==\z)/m] #「=」で終わる文字列のその前の部分を取り出す。正規表現の「(?=pat)」は「肯定先読み」なんだって。「m」は「.」が改行にもマッチするようにする。 if len != 1 raise ArgumentError, "wrong number of arguments (#{len} for 1)", caller(1) #Kernel.#caller end modifiable[new_ostruct_member(mname)] = args[0] #@table に args[0] を入れる。またそれが返り値。 elsif len == 0 if @table.key?(mid) new_ostruct_member(mid) @table[mid] end else err = NoMethodError.new "undefined method `#{mid}' for #{self}", mid, args err.set_backtrace caller(1) raise err end end def [](name) @table[name.to_sym] end def []=(name, value) modifiable[new_ostruct_member(name)] = value end end
正規表現の「(?=pat)」については以下。
正規表現 (Ruby 2.3.0)
先読みを使ったパターン - 先読み - Ruby正規表現の使い方