I understand (I think) the difference between class variables and instance variables of a class in Ruby.
I'm wondering how one can access the instance variables of a class from OUTSIDE that class.
From within (that is, in a class method instead of an instance method), it can be accessed directly, but from outside, is there a way to do MyClass.class.[#$#]variablename?
I don't have any specific reason for doing this, just learning Ruby and wondering if it is possible.
class MyClass
#my_class_instance_var = "foo"
class << self
attr_accessor :my_class_instance_var
end
end
puts MyClass::my_class_instance_var
The foregoing yields:
>> foo
I believe that Arkku demonstrated how to access class variables (##), not class instance variables (#) from outside the class.
I drew the foregoing from this essay: Seeing Metaclasses Clearly
Ruby has Class, Class Object, and Instance.
A Class variable belongs to a Class. A Class instance variable
belongs to a Class Object
Class variable:
Accessible within the class and its instances.
attr_accessor does not work on class variables.
Class instance variable:
Accessible only through the Class.
attr_accessor works if you define it in the class and not in the class object as below.
class A
#b = 1
class << self
attr_accessor :b
end
end
Defining a getter and setter on the instances for the class instance variable b in:
class A
#b = 1
class << self
attr_accessor :b
end
def b
A.b
end
def b=(value)
A.b=value
end
end
Now the class instance variable b can be accessed via the owner Class and its instances.
As a several days old ruby learner, this is the most I can do.
`irb(main):021:0* class A
irb(main):022:1> #b = 1
irb(main):023:1> class << self
irb(main):024:2> attr_accessor :b
irb(main):025:2> end
irb(main):026:1> def b
irb(main):027:2> A.b
irb(main):028:2> end
irb(main):029:1> def b=(v)
irb(main):030:2> A.b=v
irb(main):031:2> end
irb(main):032:1> end
=> :b=
irb(main):033:0> A.b
=> 1
irb(main):034:0> c = A.new
=> #<A:0x00000003054440>
irb(main):035:0> c.b
=> 1
irb(main):036:0> c.b= 50
=> 50
irb(main):037:0> A.b
=> 50
irb(main):038:0>`
Yes, I'm begining to dislike ruby...iso a better solution.
In ruby you can achieve this in 2 ways
manually defining getter and setters
using attr_* methods
Let me elaborate the above ways for you,
Manually defining getter and setters
class Human
def sex=(gender)
#sex = gender
end
def sex
#sex
end
end
//from outside class
human = Human.new
// getter method call
puts human.sex
// setter method call to explicitly set the instance variable
human.sex = 'female'
puts human.sex
// now this prints female which is set
using attr_* methods
class Human
attr_accessor :sex
end
//from outside
human = Human.new
// getter method call
puts human.sex
// setter method call to explicitly set the instance variable
human.sex = 'female'
puts human.sex
// now this prints female which is set
attr_accessor internally creates setter and getter methods for you, if you want only setter you can just use attr_writer and if you want only getter you can use attr_reader.
I hope, I answered your question
Related
Why can't I access an instance variable directly in ruby without using an accessor method or instance_variable_get?
class Foo
#my_var
end
Why shouldn't we be able to use Foo.#my_var in this example?
The example provided by the OP is a class instance variable. These can only be accessed by class methods.
"regular" attribute accessors won't allow access from outside the class. Here are a couple of ways to create accessors that work:
class A
#class_instance_var = "foo"
class << self
attr_accessor :class_instance_var
end
end
puts A::class_instance_var # Output: foo
OR
class A
#class_instance_var = "foo"
def self.class_instance_var
#class_instance_var
end
end
puts A::class_instance_var # Output: foo
Class instance variables
Class instance variable names also begin with #. However, they are defined at class level, outside any methods. Class instance variables can only be accessed by class methods. They are shared amongst all instances of a class but not its subclasses. In other words, they are not inheritable. If the value of a class instance variable is changed in one instance of the class, all other instances are affected. Earlier we saw how all classes are instances of a built-in class called Class. That is what makes class instance variables possible.
class Vehicle
#count = 0 # This is a class instance variable
def initialize
self.class.increment_count
self.class.show_count
end
def self.increment_count # This is a class method
#count += 1
end
def self.show_count # This is a class method
puts #count
end
end
class Car < Vehicle
#count = 0
end
v1 = Vehicle.new # Output: 1
v2 = Vehicle.new # Output: 2
v3 = Vehicle.new # Output: 3
car1 = Car.new # Output: 1
car2 = Car.new # Output: 2
v3 = Vehicle.new # Output: 4
Let's review the example above. A class instance variable called #count is set in the Vehicle class, with an initial value of 0. Every time the Vehicle class is instantiated, the initialize method calls self.increment_count to increment the value of #count and self.show_count to return the new value. Then, we have the Car class, which is a subclass of Vehicle and inherits all of its methods. However, it does not inherit the #count class instance variable, as this type of variable is not inheritable. That's why the counter works within the Car class, but it has its own count.
Methods prefixed with self., such as self.increment_count and self.show_count, are class methods. That is the only kind of method capable of accessing class instance variables.
That's just not how the language is built. Maybe look at openstruct
require 'ostruct'
obj = OpenStruct.new(my_var: 1)
obj.my_var
# => 1
By the way, you're method of setting up an instance variable is not correct. You should only be setting instance variables inside instance methods or initialize, otherwise use class variables or constants.
An example with constants:
class Foo
MyVar = 1
end
Foo::MyVar
# => 1
You could also make Foo.new.#my_var work with method_missing:
class Foo
def method_missing(m, *args, &block)
self.instance_variable_get(m)
end
def initialize
#my_var = 1
end
end
Foo.new.#my_var
# => 1
class Invoice
def Invoice.generate(order_id, charge_amount, credited_amount = 0.0)
Invoice.new(:order_id => order_id, :amount => charge_amount, :invoice_type => PURCHASE, :credited_amount => credited_amount)
end
end
Why would you create Invoice.generate inside Invoice class rather than self.generate?
self.generate is easier to work with, whereas Invoice.generate is arguably more explicit. Other than that, there's no difference between the two.
Explanation
You can define a method on any instance using this form
def receiver.method(args)
end
Check this out
class Foo
end
def Foo.bar
"bar"
end
Foo.bar # => "bar"
And yes, I mean any instance. It's absolutely possible that one instance has some method while another doesn't
f = Foo.new
def f.quux
'quux'
end
f2 = Foo.new
f.quux # => "quux"
f2.quux # => # ~> -:20:in `<main>': undefined method `quux' for #<Foo:0x007fe4e904a6c0> (NoMethodError)
A reminder: inside of class definition (but outside of method definitions) self points to that class.
class Foo
# self is Foo
end
So, armed with this knowledge, the difference between self.generate and Invoice.generate should be obvious.
Under normal circumstances, it would practically have no difference from def self.generate.
The only edge case I can think of is if you have a nested class with the same name, then the explicit version would apply only to the nested class.
class A
def self.x
name
end
def A.y
name
end
class A
# nested class A::A
end
def self.p
name
end
def A.q
name
end
end
> A.x # => "A"
> A.y # => "A"
> A.p # => "A"
> A.q # => NoMethodError: undefined method `q' for A:Class
> A::A.q # => "A::A"
As you see, after a nested class with the same name is defined, subsequent explicit class method definitions made with the class name refer to the nested class, but explicit definitions made beforehand refer to the original.
Implicit definitions made with self always refer to the base class.
You have 2 ways for defining a class method.
1) You can use the name of the class directly
class Test #Test is now an instance of a built-in class named Class
def Test.class_method
"I'm a class method."
end
end
2) You can use the self variable, which is always pointing to the current object
class Test
def self.class_method
"I'm a class method."
end
end
Once you understand that classes are objects, this use of the self variable to define a class method finally makes sense.
The value of self
Not too surprinsingly, when you are inside a class method, the value of self refers to the object that holds the class structure (the instance of class Class). This means that :
class Test
def self.class_method
self.x
end
end
is equivalent to :
class Test
def self.class_method
Test.x
end
end
When you are inside an instance method, the value of self still refers to the current object. This time however, the current object is an instance of class Test, not an instance of class Class.
More info. : http://www.jimmycuadra.com/posts/self-in-ruby
as the title says,
what is the difference between #var and ##var in a class definition?
Also, what is the difference between self.mymethod and mymethod in defining a method?
##var is a class variable, it is shared between class and all instances of this class. You can access this variable from class methods and from instance methods.
class C
##a = 1
def self.m1 # define class method (this is similar to def C.m1, because self will evaluate to C in this context)
##a
end
def m2 # define instance method
##a
end
end
C.m1 # => 1
C.new.m2 # => 1
#var is a class instance variable. Normally you can get access to this instance variable from the class methods.
class C
#a = 1
def self.m1
#a
end
def m2
# no direct access to #a because in this context #a will refer to regular instance variable, not instance variable of an object that represent class C
end
end
C.m1 # => 1
These variables might be confusing and you should always know the context where you define instance variable #... - it might be defined in the instance of an object that represent a class or might be an instance of regular object.
self always refers to the current object.Check the following Eg:-
class Test
def test
puts "At the instance level, self is #{self}"
end
def self.test
puts "At the class level, self is #{self}"
end
end
Test.test
#=> At the class level, self is Test
Test.new.test
#=> At the instance level, self is #<Test:0x28190>
object variables are so named because they have scope within, and are associated
to, the current object.an object variable, is then accessible from any other method inside that object.
Class variables are particularly useful for storing information relevant to all objects
of a certain class.
In intuitive terms, instance vars are used to keep track of the state of each object. On the other hand, class variables are used to keep track of the state of all instances of the class. E.g. you might use ##count to keep track of the number of this class' objects that have been instantiated, like so:
class User
##count = 0
attr_reader :name
def initialize(name)
##count += 1
#name = name
end
end
User.count gives you the number of users that have been instantiated so far.
user = User.new('Peter') increases User.count by one and user.name returns Peter.
class A
def set(v)
##v = v
end
def put
puts ##v
end
end
class B < A
end
class C < A
end
B.new.set 'b'
B.new.put # => b
C.new.set 'c'
C.new.put # => c
B.new.put # => c
Why? And how should I write this to have 'b' in last B.new.put?
Here is a nice article on the subject - Class and Instance Variables In Ruby.
Basically, what you can do is:
class A
class << self
attr_accessor :class_var
end
def set_class_var(value)
self.class.class_var = value
end
def get_class_var
self.class.class_var
end
end
class B < A; end
A.class_var = 'a'
B.class_var = 'b'
puts A.class_var # => a
puts B.class_var # => b
A.new.set_class_var 'aa'
B.new.set_class_var 'bb'
puts A.new.get_class_var # => aa
puts B.new.get_class_var # => bb
To understand it you should think about A as an instance of Class class (and that's how it is in Ruby). But every object in Ruby has its own singleton class that stores object-specific stuff like methods defined on object itself:
a = A.new
def a.foo
puts 'foo'
end
In that case foo is method defined only for a object and not for every instance of A class. And another way to define method in object's singleton class is like that:
class << a # open a's singleton class
def bar # define method that will be available only on 'a' object
puts 'bar'
end
end
In the first code snippet we use that approach to define class_var attribute accessor in the context of singleton class of our A class (it's a bit tricky, so you need to think about it). As the result class itself has class_var variable as well as its descendant class B. The difference is that every one of them has its own class_var variable that do not interfere.
Another option is to pull out class_inheritable_accessor code from Rails and include its behavior in your classes. See here for a good discussion and the guts of the code.
Perhaps you don't really want a class variable, though.
Assigning a value to a class variable (an ## variable) sets it for EVERY instance of the class. It even "sets" it for instances that "aren't created yet." So, consider this...
B.new.set 'b' # OK, that set ##v for that particular instance of B
B.new.put # Hey, you just created *another* new instance of B!
How can ##v have a value in that one? The second object's value of ##v would be unset, except for the fact that ##v is a class variable, so it has the same value for every instance of the class.
class Person
def name
puts "Dave"
end
end
puts Person.object_id
There are only two ways of accessing methods :
1) Someclass.method in case of class methods. #where Someclass is a class.
2) and Object.method when the method being accessed is a regular method declared inside a class. and Object is an instance of a class.
It follows the pattern Object.method so, does it mean Person class is really an object?
or object_id is a class method? The latter seems unlikely because class methods cannot be inherited into an instance. but when we do something like this :
a = Person.new
a.methods.include?("object_id") # this produces true
a is an instance of Person class so object_id cannot be a class method.
Yes, Ruby classes are objects:
>> String.is_a? Object
=> true
>> String.methods.count
=> 131
>> Fixnum.methods.count
=> 128
Yes, classes in Ruby are instances of class Class. In fact, you can create the same class just with:
Person = Class.new do
define_method :name do
puts 'Dave'
end
end
Then, you can just type Person.new.name and it will work exactly as your class.
Checking that Person is an instance of class Class is as easy as typing in your repl Person.class and you get Class in return.