Accessor Methods with a Twist

I am surprised that the code shown below returns, 'Doug'. I would
expect that it would return, 'Fred'. Can someone please tell me why?

@name = 'Doug'

def name=(name)
  @name = name
end
def name()
  return @name
end

name=('Fred')
puts(name())

Thanks.

       ... doug

P.S. - Please understand that this question is academic to help me
understand things. Naturally, I would not do this in practice. Thanks
again.

···

--
Posted via http://www.ruby-forum.com/.

"name=('Fred')" is interpreted as local variable assignment, not
method call. You need to use self.name=.

-- Matma Rex

name=('Fred') is interpreted as an assignment to a local variable
called name, not the call to the method. When you have an
"assignment-like" method (a method ending in the equals sign), you
have to use self. to call it, otherwise Ruby will think you are
assigning to a local variable:

1.9.2p290 :001 > def name=(name)
1.9.2p290 :002?> @name = name
1.9.2p290 :003?> end
=> nil
1.9.2p290 :004 > defined? name
=> nil
1.9.2p290 :005 > name=('fred')
=> "fred"
1.9.2p290 :006 > @name
=> nil
1.9.2p290 :007 > name
=> "fred"
1.9.2p290 :008 > self.name=('fred')
=> "fred"
1.9.2p290 :009 > @name
=> "fred"

Jesus.

···

On Fri, Jun 29, 2012 at 5:33 PM, Doug Jolley <lists@ruby-forum.com> wrote:

I am surprised that the code shown below returns, 'Doug'. I would
expect that it would return, 'Fred'. Can someone please tell me why?

@name = 'Doug'

def name=(name)
@name = name
end
def name()
return @name
end

name=('Fred')
puts(name())

`name=('Fred)` is not calling your `name=` method. It's parsed as `name = 'Fred'`, i.e. a local variable assignment.

You can see this if you do `puts name` vs. `puts name()`

To achieve what you expect, you can use `self.name = 'Fred'`

-Justin

···

On 06/29/2012 08:33 AM, Doug Jolley wrote:

I am surprised that the code shown below returns, 'Doug'. I would
expect that it would return, 'Fred'. Can someone please tell me why?

@name = 'Doug'

def name=(name)
   @name = name
end
def name()
   return @name
end

name=('Fred')
puts(name())

Thanks.

        ... doug

P.S. - Please understand that this question is academic to help me
understand things. Naturally, I would not do this in practice. Thanks
again.

Doug Jolley wrote in post #1066832:

The thing is this: I have this object (i.e., an instance of a class).
This particular instance is used for a multitude of purposes. Among
them is that the instance is needed by several methods contained in a
module. The methods of the module will never require any other
instance. They always require only this particular instance. I guess I
could pass the instance to each of these methods that needs it as an
argument. Since there are so many of the methods of the module that
need the instance, I was hoping that there might be another more
sanitary way of doing it.

I don't see why you want to use a module for this. I'd rather use a
*class* and pass the object to the initialize method. You can then save
it in an instance variable and use it in any method.

For example:

# create a database connection
database = Database.new username: 'root', passwort: '123456'
# create an instance of PictureArchive and pass it the database object
picture_archive = PictureArchive.new database
# the picture archive can now use the database connection
my_holiday_pictures = picture_archive.search year: 2011

I think you should only use modules for three things: as a namespace, as
a collection of "static" functions (like Math) or as mixin to provide
methods to classes (like Enumerable).

Your description doesn't fit any of these cases. But again: If you tell
use what you actually want to do, we can think about alternative
solutions. Or is it a secret?

···

--
Posted via http://www.ruby-forum.com/\.

Oh. I see. My knee-jerk reaction from reading the responses was, "Why
haven't I encountered this issue before?" I think the answer is that
heretofore I have always specified an explicit receiver. So, now I see.
Thanks very much.

There is one additional issue. I'm wondering if there is any way that I
could call these accessor methods from within a module. IOW, it would
seem to me that I should be able to do something like this:

@name = 'Doug'

def name=(name)
  @name = name
end
def name()
  return @name
end

module MyModule
  def MyModule.get()
    puts(some_receiver.name())
  end
end

MyModule::get()

I guess the problem is that I don't know what receiver to use.
Thoughts?

Thanks to all who contributed.

         ... doug

···

--
Posted via http://www.ruby-forum.com/.

Hi,

Doug Jolley wrote in post #1066764:

I guess the problem is that I don't know what receiver to use.
Thoughts?

I seems you haven't fully understood the concept of object orientation.
It makes no real sense to create instance variables and define methods
on the top level. While this does work and may actually be useful in
some special cases, the code should rather be in a *class*.

For example:

class Person

  def initialize name
    @name = name
  end

  def name
    @name
  end

  def name= new_name
    @name = new_name
  end

end

This creates a class Person with three method definitions in it. When
you create a Person object via Person.new, you have to first provide a
name. This name will be saved in the @name variable of the object (this
is done by the initialize method):

person_1 = Person.new 'Doug'

You can now call "name" or "name=" on the object in order to get or set
the @name variable:

puts person_1.name # this outputs "Doug"
person_1.name = 'Fred' # change the @name variable of person_1 to
"Fred"
puts person_1.name # now you get "Fred"

Of course, you can do this not only on the top level but also in modules
or anywhere else. But you have to create an instance of Person first.

Some other things to note:
- You don't have to write getters and setters by hand. You can simply
call "attr_accessor" within the class body and supply the variable names
as Symbols:

class Person

  def initialize name
    @name = name
  end

  attr_accessor :name

end

This automatically creates a "name" and a "name=" method.

- You don't have to use parantheses for method calls. While many people
still use them for clarity, it's common to omit them for basic methods
like "puts" and getter methods (this makes it look like you were
actually accessing the variables of the object rather than calling
methods).

- I wouldn't use "::" when calling module or class methods. This is
allowed, but I'd rather stick with the normal dot and use the double
colon only for constants.

- When define class methods inside a class body, you should use "self"
as the receiver rather than write out the actual class name. This makes
the code much more flexible, because renaming the class won't break the
whole code.

···

--
Posted via http://www.ruby-forum.com/\.

Doug Jolley писал 30.06.2012 20:05:

Oh. I see. My knee-jerk reaction from reading the responses was, "Why
haven't I encountered this issue before?" I think the answer is that
heretofore I have always specified an explicit receiver. So, now I see.
Thanks very much.

There is one additional issue. I'm wondering if there is any way that I
could call these accessor methods from within a module. IOW, it would
seem to me that I should be able to do something like this:

@name = 'Doug'

def name=(name)
  @name = name
end
def name()
  return @name
end

module MyModule
  def MyModule.get()
    puts(some_receiver.name())
  end
end

MyModule::get()

I guess the problem is that I don't know what receiver to use.
Thoughts?

Thanks to all who contributed.

         ... doug

Jan E. has replied with an excellent explanation, but I'll answer your
question directly. In this case, the receiver you are trying to use is
the toplevel object. There is nothing special about the toplevel object
except for the fact that it is, well, toplevel--i.e. every code written
in a script directly is executed in its context.

There is no way to get the toplevel object except for referencing `self'
from its context. The code doing that is as follows:

module MyModule
   def MyModule.get(obj)
     puts(obj.name())
   end
end

MyModule::get(obj)

I want to note that the above code is not written with Ruby's common
conventions nor is it a good design at all. I have just answered your
question, not given you a design advice.

Also note that methods defined on toplevel object become available in
each Object descendant and not only on the toplevel object itself. E.g.
this code will print "A":

def print_a
   puts "A"
end

Object.new.send :print_a

In this case I used method #send, as the default access specifier for the
toplevel object is `private' (as opposed to `public' in every other
context).

···

--
   WBR, Peter Zotov.

Thanks, Jan, so much for that *VERY* thorough response.

I seems you haven't fully understood the concept of object orientation.

I'm sure that's an understatement. However, I am wrestling with it. :slight_smile:

I was aware of many of the things that you mentioned in your response.
I think that my real issue turns on this statement in your response:

Of course, you can do this not only on the top level but also in modules
or anywhere else. But you have to create an instance of Person first.

My understanding was that when I did:

person_1 = Person.new 'Doug'

the left-hand-side was a local variable. After reading your response, I
thought that maybe it isn't. Maybe it's something else that would be
available in a module. I used the following code (which draws heavily
on your example) to test this hypothesis:

class Person

  def initialize name
    @name = name
  end

  def name()
    @name
  end

  def name=(new_name)
    @name = new_name
  end

end

person_1 = Person.new 'Doug'

module MyModule
  def self.show_name()
    puts(person_1.name())
  end
end

MyModule.show_name()

=> ./test2:23:in `show_name': undefined local variable or method
`person_1' for MyModule:Module (NameError)

So, I'm not exactly sure what you mean when you say:

Of course, you can do this not only on the top level but also in modules
or anywhere else.

I cannot seem to access the accessor methods from within a module.
That's what I'd like to be able to do.

Thanks ever so much for hanging in there with me. I think I'm on the
verge of a major breakthrough in understanding. Thanks again.

Some other things to note:
- You don't have to write getters and setters by hand. You can simply
call "attr_accessor" within the class body and supply the variable names
as Symbols:

Understood.

- You don't have to use parantheses for method calls. While many people
still use them for clarity, it's common to omit them for basic methods
like "puts" and getter methods (this makes it look like you were
actually accessing the variables of the object rather than calling
methods).

I understand that and within the context of simple things like puts, I
agree. It's just that I am trying to force myself to remember to use
parenthesis with methods so that when I go to review the code it's more
clear which identifiers are variables and which are methods.

- I wouldn't use "::" when calling module or class methods. This is
allowed, but I'd rather stick with the normal dot and use the double
colon only for constants.

That's a revelation. Thanks.

- When define class methods inside a class body, you should use "self"
as the receiver rather than write out the actual class name. This makes
the code much more flexible, because renaming the class won't break the
whole code.

Good point. I'll try to remember that.

      ... doug

···

--
Posted via http://www.ruby-forum.com/\.

Doug Jolley wrote in post #1066813:

My understanding was that when I did:

person_1 = Person.new 'Doug'

the left-hand-side was a local variable. After reading your response, I
thought that maybe it isn't. Maybe it's something else that would be
available in a module. I used the following code (which draws heavily
on your example) to test this hypothesis:

It *is* a local variable. There are four types of variables in Ruby:

- local variables beginning with a lowercase letter or an underscore
- instance variables beginning with a single "@"
- class variables beginning with "@@"
- global variables beginning with a "$"

All four have completely different features and purposes.

A local variable is a temporary variable for a specific context (mostly
a method). It isn't visible to other contexts, which is why your code
doesn't work.

Instance variables are persistent variables bound to a specific object.
They carry the properties of this object (like the name). You can only
access it from the context of the object -- but of course you can pass
the value to the outside world through getter methods.

A class variable can be accessed from the class it was created by as
well as every subclass and every instance of those classes. You can use
it to store common properties (for example, you may have different
database classes all sharing a single database connection). But most
people avoid using class variables, because they're obviously hard to
control.

Global variables are accessible from any context. You should avoid them
as well.

I think one of the problems is that you are used to circumvent the
limitations of local variables by using instance variables on the top
level. This *does* work, but it's rather a hack and somewhat keeps you
from designing meaningful classes.

I cannot seem to access the accessor methods from within a module.
That's what I'd like to be able to do.

The outer local variable isn't visible for the method. You have to
create the Person object *inside* the method.

However, there may be a misconception about methods: You seem to think
of methods as some kind of static, independend functionalities (like the
mathematical functions in the Math module). But in fact they're the
"abilities" of a certain object.

So asking how to access the name methods from a module really makes no
sense. They don't exist on their own. What you can do is create an
instance of the class, which will then have those methods.

If you actually want to have independend *functions* rather than
methods, you should define them as singleton methods in a module (i. e.
in the module body with "self" as the receiver). But then you're not
supposed to use instance variables, because this would break the whole
idea.

Well, this all probably sounds rather abstract and complicated. If you
write down some actual code, it may be easier for us to give concrete
advice.

···

--
Posted via http://www.ruby-forum.com/\.

Thanks again for another very thorough response.

I'm afraid that I'm going to have to think about all that you have said.
As you probably have recognized, we are pushing the envelope of my
capabilities.

The thing is this: I have this object (i.e., an instance of a class).
This particular instance is used for a multitude of purposes. Among
them is that the instance is needed by several methods contained in a
module. The methods of the module will never require any other
instance. They always require only this particular instance. I guess I
could pass the instance to each of these methods that needs it as an
argument. Since there are so many of the methods of the module that
need the instance, I was hoping that there might be another more
sanitary way of doing it.

From what you have said, I suspect that I am having this problem because
there is a major problem with the underlying structure of my code. I'm
not sure that I am going to be able to figure out what the problem is
and fix it.

Thanks so much for your help.

         ... doug

···

--
Posted via http://www.ruby-forum.com/.

You should stop here.

Your class should generally do one thing and do it well.

Don't use modules to mix in functionality until you understand your underlying design _thoroughly_. Until then, you're just going to shoot yourself in the foot.

···

On Jun 30, 2012, at 18:37 , Doug Jolley wrote:

The thing is this: I have this object (i.e., an instance of a class).
This particular instance is used for a multitude of purposes.

You should stop here.

Your class should generally do one thing and do it well.

You'll get a kick out of this. When I first read your response, I
thought that I can't stop here because there is more that I want to
accomplish. What I think that you really meant was that I should stop
here WITH THAT CLASS and do other things with other classes (and I need
to instantiate them).

When Jan said that I hadn't fully grasped the concept of object
orientation, she wasn't just whistling Dixie. I actually think that I
sort of get it now. (At least that will be the case until the next big
revelation.) I know that my understanding has been expanded greatly.
This
dialog has been extremely enlightening for me. I need to go play with
some code now. I have a whole new prospective. Thanks so much to all
who
contributed.

     ... doug

···

--
Posted via http://www.ruby-forum.com/\.

Doug Jolley wrote in post #1066902:

When Jan said that I hadn't fully grasped the concept of object
orientation, she wasn't just whistling Dixie.

I'm actually a "he". :wink:

I know that my understanding has been expanded greatly.

This
dialog has been extremely enlightening for me. I need to go play with
some code now. I have a whole new prospective. Thanks so much to all
who
contributed.

You're welcome. And I can assure you that you're not the only one
struggling with object orientation. This is really a difficult topic.
Even experienced programmers often get it wrong.

And it's even more difficult to find an appropriate structure for a
program. This is something you need a lot of experience for, you cannot
learn it by reading some books or so.

So keep it up! :slight_smile:

···

--
Posted via http://www.ruby-forum.com/\.

Maybe the OP is going for something like this:

# This module can be mixed in to a class
# that has a :name accessor to give it
# 'extra' functionality
module Named
  def output_name
    puts "Name: #{self.name}"
  end

  def append_to_name(str)
    self.name << str
  end
end

class Person
  include Named
  attr_accessor :name
  def initialize(name)
    @name = name
  end
end

p = Person.new('Doug')

p.output_name
p.append_to_name(" Foo")
p.output_name

The mixin here is silly, but it might give the OP some ideas. In the mixin
methods, you can refer to self, which will be an instance of whatever class
has mixed in the module.

-Doug S

···

On Sun, Jul 1, 2012 at 10:10 AM, Jan E. <lists@ruby-forum.com> wrote:

Doug Jolley wrote in post #1066902:
> When Jan said that I hadn't fully grasped the concept of object
> orientation, she wasn't just whistling Dixie.

I'm actually a "he". :wink:

I know that my understanding has been expanded greatly.
> This
> dialog has been extremely enlightening for me. I need to go play with
> some code now. I have a whole new prospective. Thanks so much to all
> who
> contributed.

You're welcome. And I can assure you that you're not the only one
struggling with object orientation. This is really a difficult topic.
Even experienced programmers often get it wrong.

And it's even more difficult to find an appropriate structure for a
program. This is something you need a lot of experience for, you cannot
learn it by reading some books or so.

So keep it up! :slight_smile:

--
Posted via http://www.ruby-forum.com/\.