Metaprogramming Basics
Method Missing Intro
method_missing can handle a small set of dynamic method names when normal lookup fails.
method missing
When `method_missing` is used, `respond_to_missing?` should describe the same dynamic behavior.
Method Missing Intro
method_missing_intro.rb
Replay: real traced execution (multi-file project)
class Record
def initialize(data)
@data = data
end
def method_missing(method_name, *args)
key = method_name.to_s
return @data[key] if args.empty? && @data.key?(key)
super
end
def respond_to_missing?(method_name, include_private = false)
@data.key?(method_name.to_s) || super
end
end
field_name = "name"
record = Record.new("name" => "Ada", "role" => "mentor", "level" => "beginner")
can_read = record.respond_to?(field_name)
value = can_read ? record.public_send(field_name) : "missing"
puts "field=#{field_name}"
puts "can_read=#{can_read}"
puts "value=#{value}"
class Record
def initialize(data)
@data = data
end
def method_missing(method_name, *args)
key = method_name.to_s
return @data[key] if args.empty? && @data.key?(key)
super
end
def respond_to_missing?(method_name, include_private = false)
@data.key?(method_name.to_s) || super
end
end
field_name = "role"
record = Record.new("name" => "Ada", "role" => "mentor", "level" => "beginner")
can_read = record.respond_to?(field_name)
value = can_read ? record.public_send(field_name) : "missing"
puts "field=#{field_name}"
puts "can_read=#{can_read}"
puts "value=#{value}"
class Record
def initialize(data)
@data = data
end
def method_missing(method_name, *args)
key = method_name.to_s
return @data[key] if args.empty? && @data.key?(key)
super
end
def respond_to_missing?(method_name, include_private = false)
@data.key?(method_name.to_s) || super
end
end
field_name = "level"
record = Record.new("name" => "Ada", "role" => "mentor", "level" => "beginner")
can_read = record.respond_to?(field_name)
value = can_read ? record.public_send(field_name) : "missing"
puts "field=#{field_name}"
puts "can_read=#{can_read}"
puts "value=#{value}"
field_name ← name
18field_name→ name = "name" #@field_name="role", "level"19record = Record.new("name" => "Ada", "role" => "mentor", "level" => "beginner")@data ← {"name"=>"Ada", "role"=>"mentor", "level"=>"beginner"}
1class Record2 def initialize(data{"name"=>"Ada", "role"=>"mentor", "level"=>"beginner"})3 @data→ {"name"=>"Ada", "role"=>"mentor", "level"=>"beginner"} = data{"name"=>"Ada", "role"=>"mentor", "level"=>"beginner"}4 endcan_read = record.respond_to?(field_name)
21can_read = record.respond_to?(field_name)true22value = can_read ? record.public_send(field_name) : "missing"def respond_to_missing?(method_name, include_private = false)
13def respond_to_missing?(method_namename, include_private = false)14 @data.key?(method_name.to_s)true || super15endcan_read ← true
21can_read→ true = record.respond_to?(field_name)true22value = can_readtrue ? record.public_send(field_name)Ada : "missing"key ← name
6def method_missing(method_namename, *args[])7 key→ name = method_name.to_sname8 return @data[key]Ada if args.empty?true && @data.key?(key)truevalue ← Ada
21can_read = record.respond_to?(field_name)22value→ Ada = can_readtrue ? record.public_send(field_name)Ada : "missing"2324puts "field=#{field_namename}"25puts "can_read=#{can_readtrue}"26puts "value=#{valueAda}"outputfield=name can_read=true value=Ada
field_name ← role
18field_name→ role = "role"19record = Record.new("name" => "Ada", "role" => "mentor", "level" => "beginner")@data ← {"name"=>"Ada", "role"=>"mentor", "level"=>"beginner"}
1class Record2 def initialize(data{"name"=>"Ada", "role"=>"mentor", "level"=>"beginner"})3 @data→ {"name"=>"Ada", "role"=>"mentor", "level"=>"beginner"} = data{"name"=>"Ada", "role"=>"mentor", "level"=>"beginner"}4 endcan_read = record.respond_to?(field_name)
21can_read = record.respond_to?(field_name)true22value = can_read ? record.public_send(field_name) : "missing"def respond_to_missing?(method_name, include_private = false)
13def respond_to_missing?(method_namerole, include_private = false)14 @data.key?(method_name.to_s)true || super15endcan_read ← true
21can_read→ true = record.respond_to?(field_name)true22value = can_readtrue ? record.public_send(field_name)mentor : "missing"key ← role
6def method_missing(method_namerole, *args[])7 key→ role = method_name.to_srole8 return @data[key]mentor if args.empty?true && @data.key?(key)truevalue ← mentor
21can_read = record.respond_to?(field_name)22value→ mentor = can_readtrue ? record.public_send(field_name)mentor : "missing"2324puts "field=#{field_namerole}"25puts "can_read=#{can_readtrue}"26puts "value=#{valuementor}"outputfield=role can_read=true value=mentor
field_name ← level
18field_name→ level = "level"19record = Record.new("name" => "Ada", "role" => "mentor", "level" => "beginner")@data ← {"name"=>"Ada", "role"=>"mentor", "level"=>"beginner"}
1class Record2 def initialize(data{"name"=>"Ada", "role"=>"mentor", "level"=>"beginner"})3 @data→ {"name"=>"Ada", "role"=>"mentor", "level"=>"beginner"} = data{"name"=>"Ada", "role"=>"mentor", "level"=>"beginner"}4 endcan_read = record.respond_to?(field_name)
21can_read = record.respond_to?(field_name)true22value = can_read ? record.public_send(field_name) : "missing"def respond_to_missing?(method_name, include_private = false)
13def respond_to_missing?(method_namelevel, include_private = false)14 @data.key?(method_name.to_s)true || super15endcan_read ← true
21can_read→ true = record.respond_to?(field_name)true22value = can_readtrue ? record.public_send(field_name)beginner : "missing"key ← level
6def method_missing(method_namelevel, *args[])7 key→ level = method_name.to_slevel8 return @data[key]beginner if args.empty?true && @data.key?(key)truevalue ← beginner
21can_read = record.respond_to?(field_name)22value→ beginner = can_readtrue ? record.public_send(field_name)beginner : "missing"2324puts "field=#{field_namelevel}"25puts "can_read=#{can_readtrue}"26puts "value=#{valuebeginner}"outputfield=level can_read=true value=beginner