I’m trying to include a module and in that module I want to set an
instance variable. I have the following:
class Queue
include LBo::InstanceMethods
end
module LBo
module InstanceMethods
attr_accessor :position
self.position = 1
end
end
q = Queue.new
q.position
But I’m getting this error:
undefined method `position=’ for LBo::InstanceMethods:Module
(NoMethodError)
I’m feeling like a moron because I can’t understand why this is not
working.
I tried setting the @position variable directly, but when I do
q.position it gives back nil.
self at this point is the Module object LBo::InstanceMethods. You’re
calling the #position method on that Module object.
end
end
q = Queue.new
q.position
But I’m getting this error:
undefined method `position=’ for LBo::InstanceMethods:Module
(NoMethodError)
See above.
I’m feeling like a moron because I can’t understand why this is not
working.
I tried setting the @position variable directly, but when I do
q.position it gives back nil.
Could somebody lend me a hand with this one?
It’s mostly about ‘self’. You have to be aware of what self is when
you use it. Inside a module definition, it’s the Module object itself.
Inside an instance method, it’s the instance.
The job of the module is (mainly) to define instance methods that will
be executed by objects yet unborn. The module itself has no direct
access to the instance variables of those objects. But it can define
methods that do things with them.
For example:
module LBo
module InstanceMethods
attr_writer :position
def position @position ||= 1
end
end
end
class C
include LBo::InstanceMethods
end
C.new.position # 1
Here, I’m writing an instance method #position, which when executed
with a C instance as ‘self’ will set the appropriate instance
variable if that variable isn’t set already.
Can I “connect” with the instance if it get’s instanciated? Because I
wanted to do something like:
if not self.respond_to? :each
return false
end
The bit above can be shortened to
return false unless respond_to? :each
There are at least these three options:
do not check at all
This is my preferred solution, as it is the simplest and works well.
irb(main):001:0> class Foo
irb(main):002:1> include Enumerable
irb(main):003:1> end
=> Foo
irb(main):004:0> f=Foo.new
=> #Foo:0x7ff8bff4
irb(main):005:0> f.map {|x| x+1}
NoMethodError: undefined method each' for #<Foo:0x7ff8bff4> from (irb):5:inmap’
from (irb):5
from :0
check on the class level, this should generally be sufficient
irb(main):001:0> module Checker
irb(main):002:1> def self.included(cl)
irb(main):003:2> cl.instance_method :each
irb(main):004:2> end
irb(main):005:1> end
=> nil
irb(main):006:0> class Baz
irb(main):007:1> include Checker
irb(main):008:1> end
NameError: undefined method each' for classBaz’
from (irb):3:in instance_method' from (irb):3:inincluded’
from (irb):7:in `include’
from (irb):7
from :0
irb(main):009:0> Baz
=> Baz
irb(main):010:0> Baz.ancestors
=> [Baz, Checker, Object, Kernel]
irb(main):011:0>
Note that even the exception does not prevent inclusion. But you can
react on this and define the method for example.
on instantiation, in this case you need to be in the inheritance
chain
irb(main):001:0> module Checker
irb(main):002:1> def initialize(*a,&b)
irb(main):003:2> method :each
irb(main):004:2> end
irb(main):005:1> end
=> nil
irb(main):006:0> class Foo
irb(main):007:1> include Checker
irb(main):008:1> def initialize
irb(main):009:2> super
irb(main):010:2> end
irb(main):011:1> end
=> nil
irb(main):012:0> Foo.new
NameError: undefined method each' for classFoo’
from (irb):3:in method' from (irb):3:ininitialize’
from (irb):9:in initialize' from (irb):12:innew’
from (irb):12
from :0
irb(main):013:0>
check when individual objects are extended
irb(main):001:0> module Checker
irb(main):002:1> def self.extended(o)
irb(main):003:2> o.method :each
irb(main):004:2> end
irb(main):005:1> end
=> nil
irb(main):006:0> x = Object.new
=> #Object:0x7ff89f4c
irb(main):007:0> x.extend Checker
NameError: undefined method each' for classObject’
from (irb):3:in method' from (irb):3:inextended’
from (irb):7:in `extend’
from (irb):7
from :0
irb(main):008:0>
So it is never possible to set an instance variable via a mixin?
Except of course, via a method that gets run when the, uhm…, stuff has
transformed into an object?
It’s difficult to apprehend how this stuff works in Ruby. Especially if
you read some stuff like instance_eval and the lot.
It’s not that hard; it’s all based on a small number of principles
that never change.
Some object is always “self”
Every instance variable belongs to “self”
obj.instance_eval {…} temporarily (for the duration of the code
block) sets self to obj.
The hardest one seems to be #2. But it makes sense once you realize
that there is always a “self”. So when you see this:
module M @x = 1
def my_method @x = 1
end
end
you’re seeing two “self” contexts: the outer level of the module
definition (where self is M) and the inside of the method definition
(where self is, or rather will be, whatever object calls my_method.
Therefore, the two "@x"s have nothing whatsoever to do with each
other.
I should add:
Classes and modules are objects
which is, as I’m fond of saying to my trainees, the answer to about
75% of all questions about Ruby
You also have to keep in mind that self is not the same as local
scope. self changes, and local scope changes, but not always in sync
with each other. They’re two different things.
On Sat, Mar 22, 2008 at 11:34 AM, Robert K. [email protected] wrote:
This sets an instance variable of X.
String.new(‘test’).include(X)
‘test’.include X
Do you mean extend?
And if so what good would that do, even if we had a reference to test
as the module does not define any methods.
I am very confused by this thread but somehow have the feeling that OP
wants this
module X
def set
@x=[*1…3]
end
end
class C
attr_reader :x
include X
end
c=C.new
c.set
p c.x
and might get confused by
a=‘test’.extend X
a.set
p a.instance_variable_get(“@x”)
but at least it clearly shows that the included or extended method
that was defined in a module does precisely what OP doubted possible:
Accessing ivars.
will never work. Because you can’t get self to become the instantiated
object.
Which is kind of obvious because the object does not even exist when you
define the module.
If you want a module to manipulate instance variables you do it the same
way as with ordinary class instance methods. The major difference is
that it’s harder to initialize them during object creation because the
class needs to cooperate (calling super in #initialize). The more
robust approach is to assume that they are not initialized as David has
shown in his example with @position.
A small addition: modules are a nice case where accessing instance
variables via accessor methods instead of directly pays off because then
you can centralize initialization:
a bit silly example
module ItemManager
def items @items ||= []
end
I’d say that this defers rather than centralizes initialization. In
fact it might be said that it decentralizes it, since the
initialization of such instance variables don’t occur in an initialize
method.
That’s not a criticism. This is a form of the lazy initialization
pattern which is common in dynamic languages. The neat thing about
ruby is that the instance variables don’t even exist until they are
needed, unlike in languages like Smalltalk where they need to be
pre-declared in the class definition.
I’m reminded that there used to be a tendency among some Smalltalkers
to call this “laissez faire” initialization, which would be an
entirely different thing I think. This just showed a lack of
knowledge of French, and the use of this phrase instead of lazy
initialization was one of Ralph Johnson’s pet peeves IIRC.
I’d say that this defers rather than centralizes initialization. In
fact it might be said that it decentralizes it, since the
initialization of such instance variables don’t occur in an initialize
method.
I can see what you mean, but it is still in a single and hence central
location. The alternative would be to spread the @items ||= [] all over
the methods defined in the module (maybe I should’ve mentioned that in
the first place). It’s not as central as #initialize since that method
is /usually/ (nut not always) used to initialize an object’s state
though, so I can 45% agree - but also have to 55% disagree.
You are right, my answer was too short. The bit I showed is indeed
equivalent to the original - however both are dysfunctional.
And if so what good would that do, even if we had a reference to test
as the module does not define any methods.
My point was that String.new “foo” is superfluous because “foo” does
already create a new object.
I am very confused by this thread
Please don’t. I would feel sorry to have caused confusion.
but somehow have the feeling that OP
wants this
Actually we (I) do not exactly now what the OP exactly wants to do. So
far I know only that he wants to access instance variables from a
module. I am guessing that he thinks it’s somehow different than from
class instance methods. But it isn’t (apart from the initialization
story, see previous posting).
A small addition: modules are a nice case where accessing instance
variables via accessor methods instead of directly pays off because then
you can centralize initialization:
a bit silly example
module ItemManager
def items @items ||= []
end
def add(item)
items << item
end
def delete(item)
items.delete item
end
def reorder
items.sort!
end
end
but at least it clearly shows that the included or extended method
that was defined in a module does precisely what OP doubted possible:
Accessing ivars.
Please don’t. I would feel sorry to have caused confusion.
No not your post, I meant what OP really meant, but see below…
but somehow have the feeling that OP
wants this
Actually we (I) do not exactly now what the OP exactly wants to do.
That’s what I meant above
So
far I know only that he wants to access instance variables from a
module. I am guessing that he thinks it’s somehow different than from
class instance methods. But it isn’t (apart from the initialization
story, see previous posting).
That is the key sentence he should read I agree, and Rick’s technique
is often useful in doing so especially
in cases where the Mixin does not mix #initialize in (does not define
a method initialize).
A small addition: modules are a nice case where accessing instance
variables via accessor methods instead of directly pays off because then
you can centralize initialization:
Yup; I all invite you to read Ricks Blog he wrote a great summary of
that “pattern” on his Blog.
Did you have problems with spam Rick, as comments are disabled? Wanted
to tell you how much I enjoyed the read, I honestly
think it is a valuable addition to what Kent says in the book.
Yup; I all invite you to read Ricks Blog he wrote a great summary of
that “pattern” on his Blog.
Thanks.
Did you have problems with spam Rick, as comments are disabled? Wanted
to tell you how much I enjoyed the read, I honestly
think it is a valuable addition to what Kent says in the book.
I’ve got it set up to disable comments once an article is 30 days old.
Ah I have heard of that (RSS) already, I will try to let Firefox, work
it out :-0
Frankly, I would not do that. In fact, I have tried it but FF’s RSS
reading is pretty awkward IMHO. I prefer Google’s RSS reader or
NewsGator because they have the advantage to be webbased and are so good
suited when you change systems frequently. My 0.02 EUR.