0

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.

2
  • If your Ruby DSL requires you to contravene the established Ruby syntax for defining constants, then you are digging yourself into a giant hole. Commented Jul 31, 2017 at 21:44
  • That is why I would like to have any change really just inside an instance_eval. The DSL files are loaded at a well defined stage and don't mix with code. Commented Jul 31, 2017 at 21:49

2 Answers 2

2

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.

Sign up to request clarification or add additional context in comments.

3 Comments

Yes, I got that wrong. But I would like to write just MYCONST and delegate to a.
Thanks Kyle! Is there a way to monkey_patch const_missing on Object, while I am in the instance_eval?
@ChristopherOezbek I'm honestly not sure. There may be a way to do it without having to monkey patch Object. Not sure if this would work but you may want to look into Refinements in Ruby.
1

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
}

3 Comments

Not helping, I don't want to put DSL::ENTER_STATE_A
That's a bit harsh. His answer would help you if you'd look at it. Constants exist on the Class or Singleton level. You're expecting an instance level method call to trigger a class level constant. You have to call the constant through the class hence why you need A::MYCONST
True, I understand, sorry for not being more open to the advice. But A::MYCONST is really not what I am looking for as a solution. Technically, I don't think it is a big problem to go from class level to my instance (Thread local storage was my thought)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.