2

I want to call a module function to define a constant in a utility module in ruby. However, when I try this I get an error message. Here comes the code and the error:

module M
  ABC = fun

  module_function

  def self.fun
    "works"
  end
end

Error message:

NameError: undefined local variable or method `fun' for M:Module

Any ideas? I also tried it without self and with M.fun but no success...

2
  • So you want to set the value for constant ABC using the result of method fun, isn't it? Commented Mar 3, 2017 at 22:20
  • correct. I want it computed once and never changed again. So ideally, I would put a freeze on it, but that would complicate the current question. Commented Mar 3, 2017 at 22:23

2 Answers 2

8

It is just that the method is not defined when you assign fun to ABC. Just change the order:

module M
  def self.fun
    "works"
  end

  ABC = fun
end

M::ABC
#=> "works"

If you dislike the order (constants below methods), you might want to consider to have the method itself to memorize its return value. A common pattern looks like:

module M
  def self.fun
    @cached_fun ||= begin
      sleep 4     # complex calculation
      Time.now    # return value
    end
  end
end

M.fun
# returns after 4 seconds => 2017-03-03 23:48:57 +0100
M.fun
# returns immediately => 2017-03-03 23:48:57 +0100
Sign up to request clarification or add additional context in comments.

5 Comments

wow, that is weird. I don't need to define private methods before I use them in a class. So why do I need to do it in a module?
Because constants are assigned when the Ruby files is loaded. Whereas methods call other methods at runtime, after the file was loaded.
ok, so basically no way to define the constants first (which is the main information, that I want on top) and the methods later.
@bln-tom: Why is that weird? You call the method on line 2, at which point the method definition on line 6 hasn't even been parsed yet, let alone executed. How could Ruby possibly know what code to execute, if it hasn't even parsed the definition yet; it can't time-travel into the future? This is the same with private methods, by the way. If you call them before they are defined, that won't work either.
@JörgWMittag To be more precise I meant private methods of classes. In this case, it is totally fine to first write public methods and then private methods. I even consider it a good practice, since the public interface of the class is what you see when you open the file.
2

Test this in you irb console:

$ irb 
2.3.3 :001 > module M
2.3.3 :002?>   def self.fun
2.3.3 :003?>     "worked"
2.3.3 :004?>   end
2.3.3 :005?>   
2.3.3 :006 >   ABC = fun
2.3.3 :007?> end
 => "worked" 
2.3.3 :008 > M
 => M 
2.3.3 :009 > M::ABC
 => "worked" 
2.3.3 :010 > 

The fact is that now you defined self.fun before using it.

In your code you used the method before defining it.

2 Comments

basically the same answer. I voted for the faster one, sorry.
Sure! I was probably typing when he posted. But the important part is that your question was answered.

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.