Guys, how are you?
Watch this simple code first:
class Foo
@@x = 1
def self.x
@@x
end
def self.x= (y)
@@x = y
end
end
class << Foo
attr_accessor :x
@x = 1
end
I'm developing an aplication, and I'm wondering which way of the both
above I should use, or is recommended to use, I really need your advice,
'cause I'm noob. This is the point: the instances of Foo class almost
will never access the class variable @@x. @@x is just a thing related
with the class itself, which is accessed generally from outside the
class, like...for example:
class Dog
@@all_the_legs = 0
def initialize
@@all_the_legs += 4
end
end
And I just want to take a look sometimes at how many legs are...so:
class Dog
def self.all_the_legs
@@all_the_legs
end
end
This class variable will not be accessed anymore(after the initialize)
by any instance. So I'm wondering if is better to do this instead:
class << Dog
attr_accessor :all_the_legs
@all_the_legs = 0
end
class Dog
def initialize
Dog.all_the_legs += 4
end
end
Because I can see that if I choose the first way, and I want to Inherit
a class from Dog, I will fail:
class Dogo < Dog
@@all_the_legs = 0 #with and without this line: same result for both
def initialize
@@all_the_legs += 4
end
def self.all_the_legs
@@all_the_legs
end
end
Dog.new; Dog.new; Dog.new; Dog.all_the_legs
#=> 12
Dogo.new; Dogo.new; Dogo.new; Dogo.all_the_legs
#=> 24
I have examples like this in my whole source code, so I'm thinking
which way to take. Hope you can view my point(the example could not be
the best): I need your advice about if is good to use class variables if
you almost don't use it for the access of instances, instead you need to
require that information from outside the class(suppose that I want to
maintain in a GUI the amount of legs that share all the dogs). It is
good in this cases to enter the singleton class of a class and define
accessors and instance variables to later be accessed? Thank you all.
on 2012-09-15 20:41
on 2012-09-15 20:59
Hi,
You should definitely use class instance variables. The only situation
where you might use a class variable is when you actually want the
variable to be shared by the whole class family (i. e. all subclasses).
Otherwise it makes no sense and can easily lead to problems (which you
already discovered). Do *not* use it just to share the variable between
the class and the instances. That's not what it's meant for. Many people
even avoid class variables completely, because they're so hard to
control.
By the way, your code does not do what you think it does. When you set a
class instance variable in the singleton class, then this variable
actually belongs to the singleton class and not the original class
(which is the whole point of instance variables). So you have to set the
variable in the Foo/Dog class:
class Foo
@x = 0
class << self
attr_accessor :x
end
end
or shorter
class Foo
@x = 0
singleton_class.send :attr_accessor, :x
end
on 2012-09-15 21:26
Are we aren't talking about the same thing? >class Foo > @x = 0 > class << self > attr_accessor :x > end >end class << Foo attr_accessor :x @x = 1 end Are not the same? I mean(I may be wrong), when you define a class, self is the class itself(Foo in this case), so it's not the same as class << Foo?
on 2012-09-15 21:34
Yeee, I can see that they are not the same object, made this test: irb(main):022:0> class << Foo irb(main):023:1> self.object_id irb(main):024:1> end => 10361628 irb(main):025:0> class Foo irb(main):026:1> self.object_id irb(main):027:1> end => 10361640 But why?
on 2012-09-15 22:38
I don't get what you mean. The singleton class of a class is obviously
not the same as the class itself, so getting different object IDs was to
be expected.
Maybe you mean something different? If you wonder why I open the
singleton class in the original class with "self" instead of outside the
class with the name: That's both the same, but the first approach is
less redundant and (in my opinion) more readable. But you should
actually use "send".
And again: You cannot define a class instance variable for Foo in the
singleton class of Foo. Those are two different variables:
class Foo
@x = 'Foo'
class << self # or class << Foo
@x = 'singleton'
end
end
[Foo, Foo.singleton_class].each do |c|
puts "#{c}: @x == #{c.instance_variable_get :@x}"
end
An instance variable always belongs to a specific object instance (hence
the name). So if you define @x within the body of the singleton class,
it will actually belong to the singleton class and not the original
class.
on 2012-09-15 22:41
Damián M. González wrote in post #1076182: > Yeee, I can see that they are not the same object, made this test: > > irb(main):022:0> class << Foo > irb(main):023:1> self.object_id > irb(main):024:1> end > => 10361628 > irb(main):025:0> class Foo > irb(main):026:1> self.object_id > irb(main):027:1> end > => 10361640 > > But why? > Because ruby has two distinct entities: a class and what's known as a "singleton class". When you write: @some_var = 10 ...that instance variable gets attached to whatever self is. And inside a singleton class, which is opened by writing: class << Foo end self is the "singleton class". Inside a regular class (but outside any method definitions), self is the class. A regular class inherits the methods from its singleton class.
on 2012-09-15 22:56
Jan E. wrote in post #1076183: > I don't get what you mean. The singleton class of a class is obviously > not the same as the class itself, so getting different object IDs was to > be expected. Yes. What I did was stupid, but I realized late to edit it. > Maybe you mean something different? If you wonder why I open the > singleton class in the original class with "self" instead of outside the > class with the name: That's both the same, but the first approach is > less redundant and (in my opinion) more readable. But you should > actually use "send". Understood. > And again: You cannot define a class instance variable for Foo in the > singleton class of Foo. Those are two different variables: > > class Foo > @x = 'Foo' > class << self # or class << Foo > @x = 'singleton' > end > end > > [Foo, Foo.singleton_class].each do |c| > puts "#{c}: @x == #{c.instance_variable_get :@x}" > end > > An instance variable always belongs to a specific object instance (hence > the name). So if you define @x within the body of the singleton class, > it will actually belong to the singleton class and not the original > class. Now I know, the class itself is an object which can have instance variables, and the singleton class of a class is kind of another object that can have their own instance variables. class Foo >> singleton class Foo --------- ------------------- @x = 1 @x = 3 #not the same variables Thanks for your help, I appreciate it.
on 2012-09-15 23:12
Damián M. González wrote in post #1076185: > Now I know, the class itself is an object which can have instance > variables, and the singleton class of a class is kind of another object > that can have their own instance variables. Exactly. And the singleton class can also have its own methods and constants. In fact, the only relation to the original class is that the instance methods of the singleton class are the singleton methods of the original class (and vice versa). Apart from that it's a completely separate class.
on 2012-09-15 23:30
Jan E. wrote in post #1076186: > Exactly. And the singleton class can also have its own methods and > constants. In fact, the only relation to the original class is that the > instance methods of the singleton class are the singleton methods of the > original class (and vice versa). Apart from that it's a completely > separate class. Well this is amazing, I've tried to understand what you say, you mean this?: class Foo; end class << Foo attr_accessor :x @x = 1 def lalala "lalala #{@x}" end end class Foo @x = 2 def self.lelele "lelele #{@x}" end end irb(main):018:0* Foo.lalala => "lalala 2" irb(main):019:0> Foo.lelele => "lelele 2" irb(main):020:0> Foo.singleton_class.instance_variable_get :@x => 1 I'm referencing always to the Foo object when I call for lalala() and lelele() and of course to the @x of the Foo object. Just when I', referencing to the "ghost" Foo, I mean his singleton class I'll get the value 1 which belong to the singleton class. And this should completely show what you mean: irb(main):017:0> Foo.singleton_class.lalala NoMethodError: undefined method `lalala' for #<Class:Foo> from (irb):17 from C:/Ruby193/bin/irb:12:in `<main>' irb(main):018:0> Foo.singleton_class.lelele NoMethodError: undefined method `lelele' for #<Class:Foo> from (irb):18 from C:/Ruby193/bin/irb:12:in `<main>' irb(main):019:0> class << Foo irb(main):020:1> def self.lololo irb(main):021:2> @x irb(main):022:2> end irb(main):023:1> end => nil irb(main):024:0> Foo.singleton_class.lololo => 1
on 2012-09-15 23:56
To clear doubts, is this:
class Foo
@x = 50
def self.x
@x
end
end
the same than this?:
Foo.class_eval do
@x = 50
def self.x
@x
end
end
I think yes. And is the first snippet the same than this?:
class Foo; @x = 50; end
Foo.instance_eval do
def x
@x
end
end
and than this?:
class Foo; @x = 50; end
class << Foo
def x
@x
end
end
and than this?, what you mean in the beginning of this topic:
class Foo
@x = 50
class << self
def x
@x
end
end
end
Of course is better use attr_accessor but is just for understanding
purpose. This is a good topic, newbies will find this usefull.
on 2012-09-16 00:13
Damián M. González wrote in post #1076189: > Of course is better use attr_accessor but is just for understanding > purpose. Yes, those code snippets all do the same (the second one requires the class to already exist, however). So there are obviously many ways to do the same thing in Ruby. :-)
on 2012-09-16 04:15
On Sat, Sep 15, 2012 at 1:41 PM, Damin M. Gonzlez <lists@ruby-forum.com>wrote: > @@x = y > above I should use, or is recommended to use, I really need your advice, > end > end > > > Dog.new; Dog.new; Dog.new; Dog.all_the_legs > maintain in a GUI the amount of legs that share all the dogs). It is > good in this cases to enter the singleton class of a class and define > accessors and instance variables to later be accessed? Thank you all. > > The instance variable solution is better, I advocate never using class variables for the reason that you showed. Singletons tend to force you into a workflow that you will later regret. So these days, I try fairly hard to avoid them. For your example, where it sounds like it is just a trick for debugging / getting information, and it has very little behaviour, I'd probably do what you're doing (I'm assuming we're deleting this code after we decide that it's doing what we think it is). If it were more complex than just incrementing a counter, or if you were planning on keeping it around for a while, I'd probably try to create some other object to handle the behaviour, then just store an instance of that object on this class. Maybe something like this: class Counter attr_reader :count def initialize(increment, count=0) @increment = increment @count = count end def count! @count += @increment end end class Dog def self.leg_count @leg_counter.count end def self.new(*) @leg_counter.count! super end def self.reset! @leg_counter = Counter.new 4 end reset! end Dog.leg_count # => 0 Dog.new Dog.leg_count # => 4 Dog.new Dog.leg_count # => 8 Dog.reset! Dog.leg_count # => 0 In this example, it's of course overkill, but for something with more complex behaviour, try to get it where you can deal with it outside of the constraints of the singleton (e.g. to test it or to use it in a different way than imagined). Also, since I don't know what you're doing with it, let me warn against tracking all the instances in some collection. This will prevent them from being garbage collected, and will prevent you from being able to create instances where that is not the case (e.g. when you subclass pretty much anything in Rails, it tracks that and then does things with it that you may reasonably want it to not do).
on 2012-09-16 09:16
Josh Cheek wrote in post #1076199: > The instance variable solution is better, I advocate never using class > variables for the reason that you showed. Agree with you as with Jan. > Singletons tend to force you into a workflow that you will later regret. > In this example, it's of course overkill, but for something with more > complex behaviour, try to get it where you can deal with it outside of > the constraints of the singleton (e.g. to test it or to use it in a > different way than imagined). Nice to know it. > Also, since I don't know what you're doing with it, let me warn against > tracking all the instances in some collection. This will prevent them > from > being garbage collected, and will prevent you from being able to create > instances where that is not the case (e.g. when you subclass pretty much > anything in Rails, it tracks that and then does things with it that you > may > reasonably want it to not do). Do you mean that is good to track all the instances of a class in some collection? Is not clear..
on 2012-09-16 11:45
On Sun, Sep 16, 2012 at 2:16 AM, Damin M. Gonzlez <lists@ruby-forum.com>wrote: > Do you mean that is good to track all the instances of a class in some > collection? Is not clear.. > > No, I'm saying don't do this. You can count dog legs since that's a single object, but don't implicitly try to track all of the dogs.
on 2012-09-16 17:41
Josh Cheek wrote in post #1076220: > No, I'm saying don't do this. You can count dog legs since that's a > single > object, but don't implicitly try to track all of the dogs. Well but I don't see another way of keep track of the objects which I'll have to work around. I do this in many cases: class Foo @instances = [] #can be a Hash too, whatever class << self attr_accessor :instances end attr_accessor :nickname initialize(nickname) @nickname = nickname Foo.instances = Foo.instances.push(self) end end Do you say that I do not have to do this? How can I keep track of the instances so, if I want to refer them in the future? 'cause later meaby I'll need to do this: Foo.instances.each do |x| puts x.nickname end
on 2012-09-16 23:31
On 17/09/2012, at 3:41 AM, "Damin M. Gonzlez" <lists@ruby-forum.com> wrote: > > Do you say that I do not have to do this? How can I keep track of the > instances so, if I want to refer them in the future? 'cause later meaby > I'll need to do this: foo_collection = [] foo_collection << Foo.new('nickname') foo_collection.each do |x| puts x.nickname end Henry
on 2012-09-17 00:03
On Sun, Sep 16, 2012 at 10:41 AM, Damin M. Gonzlez <lists@ruby-forum.com>wrote: > @instances = [] #can be a Hash too, whatever > Do you say that I do not have to do this? How can I keep track of the > instances so, if I want to refer them in the future? 'cause later meaby > I'll need to do this: > > Foo.instances.each do |x| > puts x.nickname > end > > Yeah, I would not do this. If you want to keep track of them, then explicitly add them to a collection. foo = Foo.new foos << foo Now, when foos gets garbage collected, your instances get garbage collected (As opposed to the example above, where the class is always referenced by the constant, and always references the collection containing the instances, these instances are essentially permanent). Furthermore, it allows the user to decide how to use the code. Do they want to keep it in a collection? Then they can put it in a collection. Do they want to just have one of these instances for a bit and then throw it away? They can do that, too. Lets say you want to do what Rails does with its rail ties, where they need to hook into the system life cycle. Rails does it like this https://github.com/rails/rails/blob/2801786e1a51b7... is basically what it sounds like you're trying to do. This makes decisions for users that Rails should not be making. The hoops I have had to jump through to get away from this kind of invasive decision making (even when it was my own dumb decisions in my own code) would widen your eyes. If you want this kind of global registry of instances, let the instantiator add the class to the registry. Here is how I have handled this in my own code: I have a gem that tests code samples embedded in files like READMEs. To test the code sample, you need to know what strategy to use (i.e. run it through rspec). The tests need to be able to specify what strategy they should be run with, thus I maintain a list of strategies. But I do not auto-add them, I allow whoever is creating the strategies to decide whether they want it added to this list or not. Here is the code that maintains the strategies: https://github.com/JoshCheek/mountain_berry_fields... Here is an example of an actual strategy, note that it explicitly registers itself: https://github.com/JoshCheek/mountain_berry_fields...
on 2012-09-17 03:37
> Yeah, I would not do this. If you want to keep track of them, then > explicitly add them to a collection. > > foo = Foo.new > foos << foo > > Now, when foos gets garbage collected, your instances get garbage > collected > (As opposed to the example above, where the class is always referenced > by > the constant, and always references the collection containing the > instances, these instances are essentially permanent). > > > Furthermore, it allows the user to decide how to use the code. Do they > want > to keep it in a collection? Then they can put it in a collection. Do > they > want to just have one of these instances for a bit and then throw it > away? > They can do that, too. Woah, I had never thought this way... I see what you say kind of dangerous...meaby I have to take a closer look. Also the software that I'm developing is not for programmers or developers or enginners, just for final users. It will have a GUI, and it will save big amounts of information trough time and that information need to be showed to the users almost all the time, so in many ways there's no option for save or not save some information, the software just save it and need to keep it. This is a topic very interesting for me, because I'm desingning the classes now, and soon will be programming so I'm trying to discover which is the best strategy for keep track of this objects, and wich is the easiest way to keep them alive once the software is closed(once I know the first I'll know how to serialize it), then I'll have to restore all the information back when the program start again. >foo_collection = [] > >foo_collection << Foo.new('nickname') > >foo_collection.each do |x| > puts x.nickname >end I see Henry Maddocks advice a little tangled. I can't visualize order in my source code if I take the way he advice, meaby I'm wrong...don't know. Thanks for be helping me.
Please log in before posting. Registration is free and takes only a minute.
Existing account
(Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
Log in with Google account | Log in with Yahoo account
No account? Register here.