tags:

views:

146

answers:

4

Why is is that the following fails?

module Test
    def test
        puts "test"
    end

    puts "testing"
    test
end

I get this output:

testing
test.rb:6:in `test': wrong number of arguments (ArgumentError)
    from test.rb:6:in `<module:Test>'
    from test.rb:1:in `<main>'

Is it because the module hasn't been "compiled" yet, since the end keyword hasn't been reached?

+3  A: 

I think the definition needs to be:

def Test.test
    puts "test"
end
Mark Wilkins
That does work, but it works outside the module (w/ an 'include' line) even if I don't use the Test. prefix. Is ruby doing some name mangling on the call to test inside the module?
slide_rule
Putting the "Test." (or self.) in front of it defines it as a class method. Without it, it is an instance method, which allows it to be used (mixed in) another class.
Mark Wilkins
+1  A: 

test is not calling the method you defined: it is invoking Kernel.test, which expects 2 arguments -- thus the ArgumentError exception.

zetetic
+3  A: 

Using a previously unused name might clear up your confusion:

module Test
  def my_test
    puts "my_test"
  end
  puts "testing"
  my_test
end

results in

testing
NameError: undefined local variable or method `my_test' for Test:Module

Inside the module...end block, when you invoke my_test, what is self (the implicit receiver)? It is the module Test. And there is no my_test "module method". The my_test method defined above is like an instance method, to be sent to some object that includes this module.

You need to define my_test as a "module method":

module Test
  def self.my_test
    puts "my_test"
  end
  puts "testing"
  my_test
end

results in

testing
my_test

If you want my_test as an instance method and you want to invoke it inside your module definition:

method Test
  puts "testing"
  Object.new.extend(self).my_test
end

gives

testing
my_test
glenn jackman
Thanks, that makes sense. I had run into the problem with a method of a different name, and whipped up the example code to demonstrate - didn't look at the resulting error very closely, so the NameError was actually what I was interested in. Sorry for the confusion.
slide_rule
+1  A: 
module Test
  def test # This is an instance method
    puts "test"
  end

  puts "testing"
  test     # This is a call to a module method
end

The two are completely unrelated. Somewhere higher up in your inheritance chain, you have a module method named test which takes at least one argument. (I'm guessing it is the Kernel#test method, which takes two arguments.) Since you call it without an argument, you get an ArgumentError exception.

If you were to provide a little more detail about what the actual problem is, that you are trying to solve, it would be possible to give a better answer. Until then, here's a couple of ideas:

Make the method a module method:

module Test
  def self.test; puts "test" end

  puts "testing"
  test
end

Extend the module with itself:

module Test
  def test; puts "test" end

  extend self

  puts "testing"
  test
end

Create an instance of the module:

module Test
  def test; puts "test" end
end

puts "testing"
Object.new.extend(Test).test

Mix the module into a class, and create an instance of that:

module Test
  def test; puts "test" end
end

class Foo; include Test end

puts "testing"
Foo.new.test

Mix the module into Module:

module Test
  def test; puts "test" end
end

class Module; include Test end

module Test
  puts "testing"
  test
end

Mix the module into Object:

module Test
  def test; puts "test" end
end

class Object; include Test end

puts "testing"
test

Mix the module into the main object:

module Test
  def test; puts "test" end
end

include Test

puts "testing"
test
Jörg W Mittag
Thanks, very detailed. I wasn't looking for a solution to a problem, I was confused about the behavior I was observing. The failing code didn't belong in the module block, so I moved it out and all went well. I was just trying to understand what was going on.
slide_rule
loved your solutions!
Allen Bargi