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

field_name
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}"
  1. field_name ← name

    18field_name→ name = "name"  #@field_name="role", "level"19record = Record.new("name" => "Ada", "role" => "mentor", "level" => "beginner")
  2. @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  end
  3. can_read = record.respond_to?(field_name)

    21can_read = record.respond_to?(field_name)true22value = can_read ? record.public_send(field_name) : "missing"
  4. 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 || super15end
  5. can_read ← true

    21can_read→ true = record.respond_to?(field_name)true22value = can_readtrue ? record.public_send(field_name)Ada : "missing"
  6. 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)true
  7. value ← 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
  1. field_name ← role

    18field_name→ role = "role"19record = Record.new("name" => "Ada", "role" => "mentor", "level" => "beginner")
  2. @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  end
  3. can_read = record.respond_to?(field_name)

    21can_read = record.respond_to?(field_name)true22value = can_read ? record.public_send(field_name) : "missing"
  4. 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 || super15end
  5. can_read ← true

    21can_read→ true = record.respond_to?(field_name)true22value = can_readtrue ? record.public_send(field_name)mentor : "missing"
  6. 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)true
  7. value ← 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
  1. field_name ← level

    18field_name→ level = "level"19record = Record.new("name" => "Ada", "role" => "mentor", "level" => "beginner")
  2. @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  end
  3. can_read = record.respond_to?(field_name)

    21can_read = record.respond_to?(field_name)true22value = can_read ? record.public_send(field_name) : "missing"
  4. 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 || super15end
  5. can_read ← true

    21can_read→ true = record.respond_to?(field_name)true22value = can_readtrue ? record.public_send(field_name)beginner : "missing"
  6. 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)true
  7. value ← 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