简体   繁体   中英

Ruby instance_eval and const_missing

I would like to call const_missing in an instance_eval , but seem to be missing something:

class A
  def self.const_missing(c)
    puts "Constant missing: #{c}"
  end

  def method_missing(m, *args, &block)
    puts "Method missing: #{m}"
  end       
end

# Works:
a = A.new
a.myMethod # OK: "Method missing: myMethod" 
a.MYCONST  # Sort of OK: "Method missing: MYCONST"

# Doesn't work
a.instance_eval {
  myMethod # OK: "Method missing: myMethod"
  MYCONST  # uninitialized constant MYCONST (NameError)
}

Q: Where does the const_missing call in the instance_eval go? How can redirect it to class A ? (Ruby 1.9.3 if this matters)

Context: I am writing a small DSL, where upper-case letters are used as state names.

DSL.evaluate {
  ENTER_STATE_A
  WAIT_FOR_INPUT
}

These should be mapped to methods, but I would like to keep them upper case.

You're trying to access a constant w/ a.MYCONST which ruby is interpreting as a method. You instead need to use :: along w/ access the class itself and not the instance of that class:

a = A.new
a.instance_eval {
  self::class::MYCONST # => Constant missing: MYCONST
}

It seems that when calling MYCONST inside of instance_eval it's being caught by Object.const_missing . For example if you override it you'll see the behavior you're after:

def Object.const_missing(c)
  puts "Constant missing: #{c}"
end 

a = A.new
a.instance_eval {
  MYCONST # => Constant missing: MYCONST
}

I don't recommend actually doing this though. I'm only showing this to illustrate what's happening.

Note that when you do

a.MYCONST

you got Method missing: MYCONST (and not Constant missing: MYCONST ).

You should do the following:

a.instance_eval {
  myMethod
  A::MYCONST
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM