The following code prints nothing, I was expecting welcomeBack. Please explain.
class Hello
#instanceHello = "welcomeBack"
def printHello
print(#instanceHello)
end
Hello.new().printHello();
end
I just started to lean ruby, so please forgive if the question looks dumb.
If you could memorize only one thing about defining methods and setting variables, it's this: always ask yourself, what is self at this point?
class Hello
# This ivar belongs to Hello class object, not instance of Hello.
# `self` is Hello class object.
#instanceHello = "welcomeBack"
def printHello
# Hello##instanceHello hasn't been assigned a value. It's nil at this point.
# `self` is an instance of Hello class
print #instanceHello
end
def self.printSelfHello
# Now this is Hello.#instanceHello. This is the var that you assigned value to.
# `self` is Hello class object
print #instanceHello
end
end
Hello.new.printHello # >>
Hello.printSelfHello # >> welcomeBack
If you want to set a default value for an ivar, do it in the constructor:
class Hello
def initialize
#instanceHello = "welcomeBack"
end
def printHello
print #instanceHello
end
end
Hello.new.printHello # >> welcomeBack
In Ruby, instance variables are defined and used in instance methods. So you need to put your assignment in the initialize method:
class Hello
def initialize
#instanceHello = "welcomeBack"
end
def printHello
print(#instanceHello)
end
end
Hello.new.printHello();
Also, note that I moved the printHello call outside of the class definition. This is because the class isn't actually defined until after it is closed the first time; that is, after the last end.
class Hello
#instanceHello = "welcomeBack"
def printHello
puts self.class.instance_variable_get(:#instanceHello)
end
Hello.new.printHello; #=> welcomeBack
end
This is not good programming, but just to illustrate that a class (which is an instance of the class Class) can also have instance variables, like any other instance. They are called "class instance variables" and are preferred to class variables. The following example illustrates how a counter could be defined at the class level :
class Hello
#counter = 0
class << self # access methods for class instance variables must be
# defined in the singleton class
attr_accessor :counter
end
def printHello
self.class.counter += 1
puts "Hello #{self.class.counter}"
end
end
Hello.new.printHello; #=> Hello 1
Hello.new.printHello; #=> Hello 2
p Hello.singleton_methods #=> ["counter=", "counter"]
change #instanceHello to self.class.instance_variable_get(:#instanceHello)
#instanceHello is a class instance variant
the code is this:
class Hello
#instanceHello = "welcomeBack"
##classHello = "greeting!"
def printHello
print(self.class.instance_variable_get(:#instanceHello))
print(self.class.class_variable_get(:##classHello))
end
end
Hello.new().printHello();
Related
I'd like to understand this code. Why is it returning Hello instead of Howdy! ?
class Speaker
#message = "Hello!"
class << self
#message = "Howdy!"
def speak
#message
end
end
end
puts Speaker.speak
First off, your message #message is not an instance variable, or rather not the type of instance variable you may be thinking about: it's a class-level instance var, so an instance variable of Speaker itself, which as an object is an instance of class Class.
Here's a version of the code that does what you're trying to do with a local variable and a closure:
class Speaker
#message = "Hello!"
class << self
message = "Howdy!"
define_method(:speak) { message }
end
end
Speaker.speak
#=> "Howdy!"
And here's some code that illustrates the difference between the class-level instance variable and a "normal" instance variable:
class Speaker
#message = 'Howdy!' # class-level instance variable
def initialize
#message = 'Hello!' # instance variable of Speaker's instances
end
def speak
#message
end
class << self
def speak
#message
end
end
end
Speaker.speak
#=> "Howdy!"
Speaker.new.speak
#=> "Hello!"
Here is your code, except I've defined the class method in the usual way (def self.speak...). As a class method is nothing more than an instance method defined on the class' singleton class, this change is merely a different way of creating the same class method. (If you doubt that, run the code below both ways.) I made that change because I thought it would make my explanation of what is happening clearer. I also added a puts statement.
class Speaker
#message = "Hello!"
def self.speak
puts "self=#{self}"
#message
end
class << self
#message = "Howdy!"
end
end
The first line of the class definition creates a class instance variable #message:
Speaker.instance_variables
#=> [:#message]
Speaker.instance_variable_get(:#message)
#=> "Hello!"
By constrast,
#message = "Howdy!"
creates an instance variable on Speaker's singleton class:
Speaker.singleton_class.instance_variables
#=> [:#message]
Speaker.singleton_class.instance_variable_get(:#message)
#=> "Howdy!"
Now invoke speak on Speaker:
Speaker.speak
# self=Speaker
#=> "Hello!"
As self #=> Speaker, speak is obviously returning the value of the class instance variable.
For speak to return the value of the instance variable defined on Speaker's singleton class we can write the following:
class Speaker
#message = "Hello!"
def self.speak
puts "self=#{self}"
puts "singleton_class = #{singleton_class}"
singleton_class.instance_variable_get :#message
end
class << self
#message = "Howdy!"
end
end
puts Speaker.speak
# self=Speaker
# singleton_class = #<Class:Speaker>
# Howdy!
In the last expression, because self equals Speaker and self is the implied receiver when there is no explicit receiver, "singleton_class is equivalent to Speaker.singleton_class.
The reason this code returns 'Hello' is that it is attempting to change an instance variable in a class << self block.
Class methods are for anything that does not deal with an individual instance of that class - instance variables are tied to individual instances of a class, and we can't change instance variables at a class level.
Instead of using an instance variable in the speak method, we should use a class variable (denoted by ##).
As an example, the following code will return 'Howdy!' -
class Speaker
##message = "Hello!"
class << self
##message = "Howdy!"
def speak
##message
end
end
end
puts Speaker.speak
I have a class A, which I would like to anonymously extend and add a class method to the child class. E.g.:
class A
end
Class.new A do
def self.new_class_method
puts 'I am a class method'
end
end.new_class_method
=> I am a class method
The above example works well, unless you want to access some variables outside of the def self.new_class_method block. E,g,
greeting = 'hello'
Class.new A do
def self.new_class_method
puts greeting + ' I am a class method'
end
end.new_class_method
=> NameError: undefined local variable or method `greeting'
I am using Ruby 1.8.7, which is sad because I believe Ruby 1.9+ contains an analog to define_method which adds a class method. Does anyone have a work around for 1.8.7?
I have tested the below in Ruby 1.8.7 :-
greeting = 'hello'
class A
end
Class.new A do
meta_klass = class << self; self ;end
meta_klass.send(:define_method, :new_class_method) do
puts greeting + ' I am a class method'
end
end.new_class_method
# >> hello I am a class method
As Ruby 1.8.7 doesn't support Object#singleton_class, I used meta_klass = class << self; self ;end. This method is available since 1.9.2, I think.
You can also use extend() to pry open an object's singleton class. Calling extend(module) adds the methods in the module to the calling object's(i.e. the receiver's) singleton class. So if you call extend(module) when self=A, i.e. inside class A, then the module's methods will be inserted into A's singleton class, and the methods in A's singleton class are also known as class methods of A:
class A
end
greeting = "hello"
Class.new(A) do
extend(
Module.new do
define_method(:greet) do
puts greeting
end
end
)
end.greet
--output:--
hello
And you can rewrite that like this (although then it's not as tricky):
class A
end
greeting = "hello"
Class.new(A) do
m = Module.new do
define_method(:greet) do
puts greeting
end
end
extend(m)
end.greet
...which isn't much different than:
class A
end
greeting = "hello"
m = Module.new do
define_method(:greet) do
puts greeting
end
end
Class.new(A) do
extend(m)
end.greet
...which moves the closure out of the class, and doesn't seem very tricky at all because it only opens up two scope gates instead of three.
Also note, extend() is a public method, so it doesn't require the trickery of a private method, i.e. where you can't specify an explicit receiver, so you have to create a context in which self is the object you want to call the private method on. In other words, you can specify an explicit receiver for extend(). How about the class that is returned by Class.new(A)?
class A
end
greeting = "hello"
Class.new(A).extend(
Module.new do
define_method(:greet) do
puts greeting
end
end
).greet
--output:--
hello
Hey, tacking on ".greet" there works! Uh oh, that has the makings of a one liner:
class A
end
greeting = "hello"
Class.new(A).extend(Module.new {define_method(:greet) {puts greeting} }).greet
--output:--
hello
Yeech!
More generally,
class A
end
class Object
def meta_def name, &blk
(class << self; self; end).instance_eval { define_method name, &blk }
end
end
greeting = 'hello'
Class.new A do
meta_def :new_class_method do
puts greeting + ' I am a class method'
end
end.new_class_method
#=> hello I am a class method
If you find this useful, don't thank me, thank some lucky stiff (which I saw mentioned by Jay Fields).
Not sure if this will solve your problem, but changing Greeting to uppercase (making it a constant) would work...
class A
end
Greeting = 'hello'
Class.new A do
def self.new_class_method
puts Greeting + ' I am a class method'
end
end.new_class_method
I have ruby code like this:
module Hello
class Hi
def initialize()
puts self.module.name //Should print "Hello"
end
end
end
How can I get the name of the module the class is included in?
Thanks
You can do this with Module::nesting method:
nesting → array
Returns the list of Modules nested at the point of call.
module M
class C
Module.nesting[1] # => M
end
end
If you want to get this value from the instance methods, you can assign it to class variable:
module Hello
class Hi
##parent = Module.nesting[1]
def initialize()
puts ##parent # => Hello
end
end
end
So I'm using the awesome trollop gem to do option parsing, but I'm having a general problem with the scope of the variables it's setting.
require 'trollop'
class MyClass
opts = Trollop::options do
opt :thing, "does something", default: "blah", type: String
end
def my_method
puts opts[:thing]
end
end
But I get:
undefined local variable or method `opts' for #<MyClass:0x0000010203c840> (NameError)
Any ideas what I'm doing wrong with my scope?
There are about six options here: instance variable, class instance variable, class variable, class constant, global variable, global constant. Which to use depends on your needs.
Instance Variable - each MyClass instance gets its own options:
class MyClass
def initialize
#opts = ...
end
def my_method
puts #opts[:thing]
end
end
Class Instance Variable - single value across the class that can be reassigned:
class MyClass
#opts = ...
class << self
attr_accessor :opts
end
def my_method
puts self.class.opts[:thing]
end
end
Class Variable - each MyClass and all subclasses share the same value (convenient syntax, but rarely a good idea):
class MyClass
##opts = ...
def my_method
puts ##opts[:thing]
end
end
Class Constant - single object that may be mutated, but not re-assigned. Easily accessed from this class, accessible from others via MyClass::OPTS:
class MyClass
OPTS = ...
def my_method
puts OPTS[:thing]
end
end
Global Variable - you can only have one of these in your entire app; often global variables are ill-advised, but perhaps appropriate for a standalone application's options:
$opts = ...
class MyClass
def my_method
puts $opts[:thing]
end
end
Global Constant - accessed from many classes, can't be set to a new value, but may be mutated:
OPTS = ...
class MyClass
def my_method
puts OPTS[:thing]
end
end
Shouldn't you just use instance variable?
require 'trollop'
class MyClass
def initialize
#opts = Trollop::options do
opt :thing, "does something", default: "blah", type: String
end
end
def my_method
puts #opts[:thing]
end
end
You are defining 'opts' as a local variable inside your class. Instances methods (like my_method) will not be able to access it. Is opts supposed to be "global" for the whole class? In that case:
class MyClass
##opts = Trollop::options...
def my_method
puts ##opts[:thing]
end
end
Or is there supposed to be a unique one for each instance of the class?
class MyClass
def initialize
#opts = Trollop::options...
end
def my_method
puts #opts[:thing]
end
end
This might be a good read: http://sporkmonger.com/2007/2/19/instance-variables-class-variables-and-inheritance-in-ruby
You'd want to make it either a class variable or an instance variable, depending on your needs.
How can a class method (inside a module) update an instance variable? Consider the code bellow:
module Test
def self.included(klass)
klass.extend ClassMethods
end
module ClassMethods
def update_instance_variable
#temp = "It won't work, bc we are calling this on the class, not on the instance."
puts "How can I update the instance variable from here??"
end
end
end
class MyClass
include Test
attr_accessor :temp
update_instance_variable
end
m = MyClass.new # => How can I update the instance variable from here??
puts m.temp # => nil
You'd have to pass your object instance to the class method as a parameter, and then return the updated object from the method.
That does nto quite make sense.
You use the initialize method to set default values.
class MyClass
attr_accessor :temp
def initialize
#temp = "initial value"
end
end
The initialize method is automatically run for you when you create a new object.
When your class declaration is run, there are no, and cannot be any, instances of the class yet.
If you want to be able to change the default values later you can do something like this:
class MyClass
attr_accessor :temp
##default_temp = "initial value"
def initialize
#temp = ##default_temp
end
def self.update_temp_default value
##default_temp = value
end
end
a = MyClass.new
puts a.temp
MyClass.update_temp_default "hej"
b = MyClass.new
puts b.temp
prints
initial value
hej
If you also want that to change already created instances' variables you need additional magic. Please explain exactly what you wish to accomplish. You are probably doing it wrong :)