I a trying to follow a tutorial with Ruby, but am getting very confused. Everywhere I find seems to say that defining an instance variable is done like so;
class Example
def fun
# CODE
end
end
e = Example.new
e.fun # <- Will run your code
Bu I really really don't get why this isn't working
class Example
include Enumerable
def initialise
#members = ["a", "b"]
end
def each
#members.each do |member|
yield member
end
end
end
When I call
e = Example.new
e.each do |elmt|
puts elmt
end
I get the error
NoMethodError: undefined method `each' for nil:NilClass
Can anybody help me figure out how to get this working. I cant find out what's wrong, below are 3 of the many sources that lead me to believe this should work. I am obviously doing something wrong, but I just cant see it
sources;
http://ruby.about.com/od/advancedruby/ss/Using-The-Enumerable-Module.htm
http://www.railstips.org/blog/archives/2009/05/11/class-and-instance-methods-in-ruby/
Book: Engineering Software as a Service
You have a typo. It's initialize, not initialise. Your #members instance var was never assigned to, that's why it's nil.
Related
I have searched around for the answer to this and I can see a lot of similar problems but I still do not understand what I am doing wrong here. I have declared a Ruby class and attempted to new it and then call some instance methods on the instance, so why do I get the NoMethodError on my start method?
class MyClass
def initialize
self.class.reset
end
def self.reset
...
end
def self.start(port)
...
end
end
test = MyClass.new
test.start '8082' <- here <- undefined method `start' for #<MyClass:0x2f494b0> (NoMethodError)
As you can see I am a Ruby noob. Any help would be appreciated. I can change my class structure but I would really like to understand what I am doing wrong here.
here start is a class method.
By your current approach, you can use it in the following way
MyClass.start '8080'
But if you want to use it on instance of class then use the following code
class MyClass
def initialize
self.class.reset
end
def self.reset
...
end
def start(port)
...
end
end
test = MyClass.new
test.start '8080'
You are using start as a Class variable, the method names preceded with self-keyword make those methods as Class methods. So if you really want to not change your class then you should call it like this:
MyClass.start '8080'
Else you can remove the self from your reset and start methods and make them as Instance methods and use them as:
test = MyClass.new
test.start '8082'
I’m a bit disappointed by singleton_method/singleton_methods behaviour. Say, we have the following module:
module M
extend self
def m_i
puts "m_i"
end
def m
puts "include? = [#{singleton_methods.include?(:m_i)}]"
puts "call = [#{singleton_method(:m_i).call}]" rescue puts $!
end
end
The output of a call to M::m will be surprisingly:
# include? = [true]
# undefined singleton method `m_i' for `M'
The method seems to be listed within singleton_methods array (which is fine, due to extend self directive,) but is not callable, which is frustrating. By explicit defining the method as singleton (self.m_i) it became callable.
What am I missing?
UPD The method singleton_method was introduced in 2.1 and it looks like I have met a bug in implementation.
UPD2 Filed as https://bugs.ruby-lang.org/issues/10092
This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 11 years ago.
I am doing the beginning ruby book and this is what I have for my code, which gives the following error:
in 'show_current_description': undefined method 'full_description' for nil:NilClass (NoMethodError)
The code is as follows. Any help is appreciated. Thanks!
class Dungeon
attr_accessor :player
def initialize(player_name,start_location)
#player=Player.new(player_name,start_location)
puts #player.location
#rooms=[]
show_current_description
end
def show_current_description
#rm=find_room_in_dungeon(#player.location)
#rm.full_description
end
def find_room_in_dungeon(reference)
#rooms.detect{|room| room.reference == reference; puts room.full_description}
end
def add_room(reference,name,description,connections)
#rooms << Room.new(reference,name,description,connections)
end
Player=Struct.new(:name,:location)
class Room
attr_accessor :reference, :name, :description, :connections
def initialize(reference,name,description,connections)
#reference=reference
#name=name
#description=description
#connections=connections
end
def full_description
"You are in " + #description
end
end
end
d=Dungeon.new("Good Man",:small_cave)
d.add_room(:small_cave,"Small Cave","This is a small claustrophobic cave", {:east => :largecave})
d.add_room(:large_cave,"Large Cave","This is a large cavernous cave", {:west => :smallcave})
puts d.player.name
d.show_current_description
The error that you posted means that this line...
#rm.full_description
...is trying to call full_description on an object that has no such method, because the #rm object is nil. From that we can deduce that the previous line...
#rm=find_room_in_dungeon(#player.location)
...set #rm to nil as the result of the find_room_in_dungeon method. If we look at that method...
#rooms.detect{|room| room.reference == reference; puts room.full_description}
...we see one problem. The detect method uses the return value of the block to figure out if a room should be used or not. However, the puts method (which is the last in the block, and hence the return value of the block) always returns nil. Hence, #rooms.detect will never find anything. So let's fix that by removing the puts altogether:
#rooms.detect{|room| room.reference == reference }
That should help, but it didn't solve the problem. We're still getting nil from this method. Let's look deeper.
First, let's validate our assumptions about what the precondition for this method is. Before that changed line, we add p #rooms, reference to the find_room_in_dungeon method. Running the code now, we see this output:
[]
:small_cave
Ah-ha! So that's the problem. We're looking for the small_cave room, but our list of rooms is empty! We know that we're calling d.add_room at the bottom of the script...why isn't it working?
Let's see where we are when this code breaks. Here's the full backtrace from the error message:
C:/tmp.rb:13:in `show_current_description': undefined method `full_description' for nil:NilClass (NoMethodError)
from C:/Users/gkistner.NVIDIA.COM/Desktop/tmp.rb:8:in `initialize'
from C:/Users/gkistner.NVIDIA.COM/Desktop/tmp.rb:43:in `new'
from C:/Users/gkistner.NVIDIA.COM/Desktop/tmp.rb:43:in `<main>'
Ah-HA! The problem is that we call show_current_description as part of our initialize method, before we've had a chance to add rooms to the dungeon.
Here are some ways we could fix this:
Change the Dungeon constructor to accept a list of rooms as part of initialization, so we can do this all at once. Here are three different ways one could do it:
# Method number one, accept an array of rooms
class Dungeon
def initialize(name,start,rooms=[])
#player = ...
#rooms = rooms
yield(self) if block_given?
show_current_description
end
end
d = Dungeon.new( "Bob", :small_cave, [
Dungeon::Room.new(...),
Dungeon::Room.new(...)
] )
# Method 2: Allow an initializer block
class Dungeon
def initialize(name,start)
#player = ...
#rooms = []
yield(self) if block_given?
show_current_description
end
end
d = Dungeon.new( "Bob", :small_cave ) do |dungeon|
dungeon.add_room(...)
dungeon.add_room(...)
end
# Method 3 (experts only!): Initialize block runs in scope
class Dungeon
def initialize(name,start,&initializer)
#player = ...
#rooms = []
instance_eval(&initializer) if initializer
show_current_description
end
end
d = Dungeon.new( "Bob", :small_cave ) do
add_room(...)
add_room(...)
end
Don't show the current description as part of the initializer. Just remove that line, and when running show the description when construction is complete:
d = Dungeon.new(...)
d.add_room(...)
d.add_room(...)
d.show_current_description
Make your show_current_description method more robust, so that it can handle the nil case:
def show_current_description
#rm = find_room_in_dungeon(#player.location)
puts #rm.full_description if #rm
end
You could choose to do 1 or 2, but I'd suggest also doing 3.
The 4th line from the bottom, #rooms :largecave}), is a syntax error. The real code surely looks different.
Anyway, "undefined method full_description for nil:NilClass" means that #rm, returned by find_room_in_dungeon, is nil.
Did you remove the puts in the #rooms.detect {...} ?
Remove it, and it should work.
I'll explain what i'm looking for in code as thats probably the most succinct:
module Mixin
def method
puts "Foo"
end
end
class Whatever
include Mixin
end
w = Whatever.new
w.method
=> "Foo"
# some magic here
w2 = Whatever.new
w.method
=> NoMethodError
I had tried just undefining the Mixin module using remove_const, but this doesn't seem to make any difference to Whatever. I had assumed that #include just added a reference to the module into the class's method resolution chain - but this behaviour doesn't agree with that.
Can anyone tell me what include actually does behind the scenes, and how to reverse this?
As it seems probably you want to accomplish these on instances instead of the whole class, so I would change klochner's code a bit to handle just one instance instead of all the instances of a class.
module ModuleRemover
def remove_module(mod, options = {})
metaclass = class << self; self end
mod.instance_methods.each {|method_name| metaclass.class_eval { undef_method(method_name.to_sym) }}
end
end
As Mladen pointed out, it would be cool to avoid removing methods that are overwritten on the host class, so an [only, exclude] options for this method would be ideal.
>> c1 = C.new
>> c1.foo
=> fooing
>> c1.extend(ModuleRemover)
>> c1.remove_module(Mod)
>> c1.foo
=> NoMethodError: undefined method `foo' for #< C:0x11b0d90>
>> c2 = C.new
>> c2.foo
=> fooing
module Mod
def foo
puts "fooing"
end
end
class C
include Mod
def self.remove_module(m)
m.instance_methods.each{|m| undef_method(m)}
end
end
>> c = C.new
>> c.foo
fooing
>> C.remove_module(Mod)
=> ["foo"]
>> c.foo
NoMethodError: undefined method `foo' for #< C:0x11b0d90>
I'm not sure what you're trying to accomplish, but perhaps instead of using include to add instance methods, what you want to do is use extend to add methods just to particular instances of the class, then you wouldn't need to remove them.
More information on the difference between include and extend
Some years ago I used the gem evil for un-including modules etc., but apparently it is no longer maintained. So I just tried un instead (only on my old ruby 1.8.7). Worked fine as advertised:
DESCRIPTION:
un provides unextend and uninclude to allow for a better prototype-oriented programming experience.
If you replace your "# some magic here" (after installing un) by
require 'un'
Whatever.uninclude Mixin
you get the behavior as described by you - almost. Object has already a method called method, so you get a "wrong number of arguments" error instead.
It would be nice if someone tries it on ruby 1.9 or on jruby and reports the results (I make the answer community wiki for this).
Hi i want to do the following. I simply want to overload the [] method in order to access the instance variables... I know, it doesn't make great sense at all, but i want to do this for some strange reason :P
It will be something like this...
class Wata
attr_accessor :nombre, :edad
def initialize(n,e)
#nombre = n
#edad = e
end
def [](iv)
self.iv
end
end
juan = Wata.new('juan',123)
puts juan['nombre']
But this throw the following error:
overload.rb:11:in `[]': undefined method 'iv' for # (NoMethodError)
How can i do that?
EDIT
I have found also this solution:
def [](iv)
eval("self."+iv)
end
Variables and messages live in a different namespace. In order to send the variable as a message, you'd need to define it as either:
def [](iv)
send iv
end
(if you want to get it through an accessor)
or
def [](iv)
instance_variable_get "##{iv}"
end
(if you want to access the ivar directly)
try instance_variable_get instead:
def [](iv)
instance_variable_get("##{iv}")
end