Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Instance Variables in Class Methods - Ruby

I have a class something like below, and I used instance variables (array) to avoid using lots of method parameters.

It works as I expected but is that a good practice? Actually I wouldn't expect that worked, but I guess class methods are not working as static methods in other languages.

class DummyClass   def self.dummy_method1     @arr = []     # Play with that array   end    def self.dummy_method2     # use @arr for something else   end end 
like image 730
tackleberry Avatar asked Feb 16 '12 12:02

tackleberry


People also ask

Can class methods access instance variables Ruby?

class . There are no "static methods" in the C# sense in Ruby because every method is defined on (or inherited into) some instance and invoked on some instance. Accordingly, they can access whatever instance variables happen to be available on the callee.

Can we use instance variable in method?

No. 1. Variables declared within a method are local variables. An instance variable is declared inside a class but outside of any method or block.

How do I use an instance variable in Ruby?

An instance variable in ruby has a name starting with @ symbol, and its content is restricted to whatever the object itself refers to. Two separate objects, even though they belong to the same class, are allowed to have different values for their instance variables.

Can you call an instance method in a class method Ruby?

In Ruby, a method provides functionality to an Object. A class method provides functionality to a class itself, while an instance method provides functionality to one instance of a class. We cannot call an instance method on the class itself, and we cannot directly call a class method on an instance.


2 Answers

The reason instance variables work on classes in Ruby is that Ruby classes are instances themselves (instances of class Class). Try it for yourself by inspecting DummyClass.class. There are no "static methods" in the C# sense in Ruby because every method is defined on (or inherited into) some instance and invoked on some instance. Accordingly, they can access whatever instance variables happen to be available on the callee.

Since DummyClass is an instance, it can have its own instance variables just fine. You can even access those instance variables so long as you have a reference to the class (which should be always because class names are constants). At any point, you would be able to call ::DummyClass.instance_variable_get(:@arr) and get the current value of that instance variable.

As for whether it's a good thing to do, it depends on the methods.

If @arr is logically the "state" of the instance/class DummyClass, then store it in instance variable. If @arr is only being used in dummy_method2 as an operational shortcut, then pass it as an argument. To give an example where the instance variable approach is used, consider ActiveRecord in Rails. It allows you to do this:

u = User.new u.name = "foobar" u.save 

Here, the name that has been assigned to the user is data that is legitimately on the user. If, before the #save call, one were to ask "what is the name of the user at this point", you would answer "foobar". If you dig far enough into the internals (you'll dig very far and into a lot of metaprogramming, you'll find that they use instance variables for exactly this).

The example I've used contains two separate public invocations. To see a case where instance variables are still used despite only one call being made, look at the ActiveRecord implementation of #update_attributes. The method body is simply load(attributes, false) && save. Why does #save not get passed any arguments (like the new name) even though it is going to be in the body of save where something like UPDATE users SET name='foobar' WHERE id=1;? It's because stuff like the name is information that belongs on the instance.

Conversely, we can look at a case where instance variables would make no sense to use. Look at the implementation of #link_to_if, a method that accepts a boolean-ish argument (usually an expression in the source code) alongside arguments that are ordinarily accepted by #link_to such as the URL to link to. When the boolean condition is truthy, it needs to pass the rest of the arguments to #link_to and invoke it. It wouldn't make much sense to assign instance variables here because you would not say that the invoking context here (the renderer) contains that information in the instance. The renderer itself does not have a "URL to link to", and consequently, it should not be buried in an instance variable.

like image 103
Steven Avatar answered Oct 20 '22 05:10

Steven


Those are class instance variables and are a perfectly legitimate things in ruby: classes are objects too (instances of Class) and so have instance variables.

One thing to look out for is that each subclass will have its own set of class instance variables (after all these are different objects): If you subclassed DummyClass, class methods on the subclass would not be able to see @arr.

Class variables (@@foo) are of course the other way round: the entire class hierarchy shares the same class variables.

like image 34
Frederick Cheung Avatar answered Oct 20 '22 04:10

Frederick Cheung