A question - or comment - on instance variables. Looking at ':symbols' today, I came across this example: class Test puts :Test.object_id.to_s def test puts :test.object_id.to_s @test = 10 puts :test.object_id.to_s end end t = Test.new t.test So far, my understanding was that both methods and variables used lower case names, and that methods could be written both with and without '()'. To me that then made the t.test line a bit confusing, as it appears to invoke the method test on t. How then could you ever access the instance variable test of t? Am I then also right in assuming that in Ruby there aren't any true visible instance variables, as to make the variable visible, you have to include an accessor - which effectively creates the getter/setter of that variable? So if I changed the above code to have an instance variable of 'value', with an accessor, I could then have: @test = @value = 10 and then access t.value - giving me 10. BUT t.value is then really a call to the 'generated' getter of value - so is really a method call? Do I have that about right? And in the above case, where a 'variable' and method have the same name, is there a way to force the use of a specific 'setter/getter' - ie to have t.test return the value of test, rather than call the method test on t?
on 2012-11-26 18:49
on 2012-11-26 19:21
Hei, > A question - or comment - on instance variables. I can't say I understood all of your post - but here's what might clear things up for you: In Ruby, there's no public access for instance variables. Assume in #initialize you do @foo = 1 To get at the value of @foo, you absolutely must define a getter, either by attr_reader :foo or so def foo return @foo end The only scope @foo is equal to 1 is the instance scope of your class. No external access without some sort of getter. And of course, #instance_variable_get and other meta-methods. k
on 2012-11-26 19:37
On Mon, Nov 26, 2012 at 5:49 PM, Steve T. <email@example.com> wrote: > end > Am I then also right in assuming that in Ruby there aren't any true > and then access t.value - giving me 10. BUT t.value is then really a > call to the 'generated' getter of value - so is really a method call? > > Do I have that about right? > > And in the above case, where a 'variable' and method have the same name, > is there a way to force the use of a specific 'setter/getter' - ie to > have t.test return the value of test, rather than call the method test > on t? As has been answered, you cannot get at the instance variables directly from the outside. It's all calling methods. But I think you have also another confusion, regarding *local* variables and methods: class A def test "test: the method" end def some_method test = "a variable" puts test #=> a variable puts self.test #=> test: the method puts test() #=> test: the method end end If you have a local variable and a method with the same name, the local variable "wins", and you have to use "self." or parenthesis to tell Ruby you want to call the method. Jesus.
on 2012-11-26 19:49
Steve Tu wrote in post #1086523: > > So far, my understanding was that both methods and variables used lower > case names, That's true. > and that methods could be written both with and without > '()'. > That's correct. > To me that then made the t.test line a bit confusing, as it > appears to invoke the method test on t. > It does. > How then could you ever access > the instance variable test of t? > puts t.instance_variable_get(:@test) Or, you can reopen the class and redefine test(): class Test def test @test end end result = t.test puts result --output:-- 10 However, the example you posted is terrible, and no one would ever write code like that. > Am I then also right in assuming that in Ruby there aren't any true > visible instance variables, > Instance variables are "private" in ruby. That means you cannot directly access an instance variable--instead you have to use a method to accesses an instance variable. > as to make the variable visible, you have to > include an accessor - which effectively creates the getter/setter of > that variable? > You can either write an accessor by hand: def test @test end Or, you can tell ruby to create an accessor for you: class Dog attr_reader :test def initialize(t) @test = t end end d = Dog.new(10) puts d.test --output:-- 10 > So if I changed the above code to have an instance variable of 'value', > with an accessor, I could then have: > > > @test = @value = 10 > No. You could write something like: class Dog attr_reader :test attr_accessor :value def initialize(t) @test = t end end d = Dog.new(10) d.value = 'hello' #setter puts d.value #getter > BUT t.value is then really a > call to the 'generated' getter of value - so is really a method call? > Yes. In fact, when you write: x = 1 + 2 "+" is actually the name of an Integer method. And because "+" is the name of a method, you can write that line like this: x = 1.+(2) puts x --output:-- 3 > And in the above case, where a 'variable' and method have the same name, > is there a way to force the use of a specific 'setter/getter' - ie to > have t.test return the value of test, rather than call the method test > on t? You would never write a class that has two methods with the same name--but the second method would overwrite the first method. class Dog def greet puts 'hello' end def greet puts 'goodbye' end end d = Dog.new d.greet --output:-- goodbye It's like if you did this: x = 1 x = 3 and asked if there was a way to retrieve the first value of x.
on 2012-11-27 10:26
I am nearly there! If I have class My_class attr_accessor :test end my_c = My_class.new my_c.test = 10 my_c.test Then I have automatically created two methods in My_class - the reader/writer - both with a method name of 'test'. my_c is then an instance of that class, and to access the variables I simply use my_c.test - BUT although my_c.test looks like it is a simple variable, it is really a call to a method of the name 'test'. This is where I had the problem with the symbols test above - as I wanted to get to the getter of test, not the setter. But it seemed that every time that I used t.test it invoked the setter. I'll look at the example again and try to see why I was getting that effect as I would guess that if I call the test method with an arg passed via the =, then it would invoke the setter, otherwise with no args it would invoke the getter (why doesn't Ruby use ? suffix on the getter - ie my_c.test? or t.test?). Thanks for all of your help.
on 2012-11-27 10:35
Subject: Re: Novice: Understanding instance 'variables' and methods Date: Tue 27 Nov 12 05:26:46PM +0900 Quoting S. Tu (firstname.lastname@example.org): > Then I have automatically created two methods in My_class - the > reader/writer - both with a method name of 'test'. No. There cannot be two methods with the same name in Ruby. One method (the getter) is 'test'. The other (the setter) is called 'test='. You can always have a look at what methods an objects answers to, with the 'methods' method. For your class, p my_c.methods [:test, :test=, :nil?, :===, :=~, :!~, :eql?, :hash, :<=>, :class, :singleton_class, :clone, :dup, :initialize_dup, :initialize_clone, :taint, :tainted?, :untaint, :untrust, :untrusted?, :trust, :freeze, :frozen?, :to_s, :inspect, :methods, :singleton_methods, :protected_methods, :private_methods, :public_methods, :instance_variables, :instance_variable_get, :instance_variable_set, :instance_variable_defined?, :instance_of?, :kind_of?, :is_a?, :tap, :send, :public_send, :respond_to?, :respond_to_missing?, :extend, :display, :method, :public_method, :define_singleton_method, :object_id, :to_enum, :enum_for, :==, :equal?, :!, :!=, :instance_eval, :instance_exec, :__send__, :__id__] The first two in the list are the ones you added with attr_accessor. Carlo
on 2012-11-27 11:40
carlo, Got it. So in the :symbols Test code, the problem was where I added the accessor. The original code just had the method 'test' which in effect was a setter (presumably in Ruby parlance it could have been better called test= ?). I then added the 'attr_accessor :test' which presumably added a test= method, leaving the original 'test' method. So what I in effect ended up with were two setter methods 'test' (the original, which set @test to 10) and a new setter accessor (test=) that would set @test to whatever value I passed. I didn't actualyy have a getter method called 'test'. Now that starts to make sense, as every time I used t.test, I ended up with the 'puts' lines. Didn't take me long eh? Sorry for being a bit thick.
on 2012-11-27 20:23
Steve Tu wrote in post #1086651: > The original code just had the method 'test' which in effect was a > setter (presumably in Ruby parlance it could have been better called > test= ?). Kind-of. There is special syntactic sugar for methods with names like test=. foo.test = 123 is mapped to a method call of the "test=" method, even though this looks like an assignment (and there may be spaes around the equals sign). It's the same as: foo.send(:test=, 123) Furthermore, the return value of the method is forced to be 123, even if it explicitly returns something else. There are other examples: def (k) ... end foo def=(k,v) ... end foo = 456 And even operators like + and - map to methods class Test # binary + def +(other) ... end # unary + def +@ ... end end
on 2012-11-27 21:20
First, I suggest that you learn more than one variable name. Naming everything in your code test/Test/:test is just going to confuse you. > I then added the 'attr_accessor :test' which presumably added a test= > method, leaving the original 'test' method. Writing attr_accessor :name inside a class is equivalent to writing: def name #getter @name end def name=(val) #setter @name = val end And you call the 'name=' method like this: d.name = 10 ruby ignores the space between 'd.name' and the '=' sign. Once again, if you redefine a method it will overwrite an existing method: class Dog attr_accessor :name end d = Dog.new d.name = 'Sam' puts d.name --output:-- Sam But see what happens here: class Dog attr_accessor :name def name 'hello' end end d = Dog.new d.name = 'Sam' puts d.name --output:-- hello The line attr_accessor :name tells ruby to create the methods: def name #getter @name end def name=(val) #setter @name = val end ...which ruby does. Next ruby sees the definition: def name 'hello' end in the Dog class, and that name method overwrites the name method that ruby created. If you reverse the order of the statements: class Dog def name 'hello' end attr_accessor :name end d = Dog.new d.name = 'Sam' puts d.name --output:-- Sam ...then ruby's definition of the name method overwrites your definition of the name method.
on 2012-11-27 23:07
attr methods actually do the creation, probably with convert to string append the '@' sigil in front and run it through eval, of what symbol was given to them. If you need a quick conceptual open up irb and type: puts '@' + :verb.to_s look into define_method, as well as other eval family methods. Getter/Setters are simple syntax sugar as ruby allows the method name to have '=' so we don't have to type get_verb or set_verb( val). attr is a unique piece of automation for the common templating, boiler plate code, you see in object oriented languages. attr_accesor is a method which creates methods. It wouldn't be as obvious if we where able to actually send the sigilled version of what's being created instead of a symbol. attr_writer( @foo, @bar) would be a bit more obvious. But that won't work as @foo is just a variable and one that is not set. It would actually send the value nil and not the name which is why using a symbol or string does.