Ruby - Why is it possible to use Instance variables like everywhere - ruby-on-rails

I'm into Ruby on Rails programming for almost 5 weeks now.
I was wondering why people always use instance variables instead of local ones.
I always thought that you would use instance variables only for classes (so these instance variables are the attributes of the class).
But people also use them not only for being attributes of a class. And this is the part where I am getting confused.
For instance, take a look at these lines of codes:
class Foo
def print_a_hello
puts "Hello World"
end
end
#instance_variable = Foo.new
#instance_variable.print_a_hello
# => "Hello World"
locale_variable = Foo.new
locale_variable.print_a_hello
# => "Hello World"
So, who of you got a great explanation for me?

I was wondering why people always use instance variables instead of locale ones.
I'm not sure how you get that impression. I certainly don't "always" use instance variables. I use instance variables when I need an instance variable, and I use local variables, when I need a local variable, and most code I see does it the same way.
Usually, it doesn't even make sense to interchange them. They have completely different purpose: local variables have static lexical scope, instance variables have dynamic object scope. There's pretty much no way to interchange them, except for the very narrow case of a simple single-file procedural script, where the dynamic scope of the top-level main object and the lexical scope of the script body are identical.
I always thought that you would use instance variables only for classes (so these instance variables are the attributes of the class).
No. Instance variables are attributes of the instance (i.e. object), not the class, that's why they are called "instance variables", after all. Class variables are attributes of the class, but class variables are a different beast and only used in very specific circumstances. (Classes are objects (i.e. instances), too, so they can have instance variables as well; there's generally no need to use class variables, which have some weird and un-intuitive properties, unless you specifically need those weird and un-intuitive properties).
For instance, take a look on this short codelines:
class Foo
def print_a_hello
puts "Hello World"
end
end
#instance_variable = Foo.new
#instance_variable.print_a_hello
# => "Hello World"
locale_variable = Foo.new
locale_variable.print_a_hello
# => "Hello World"
This is the case I mentioned above: in this specific case (and only in this case), the dynamic scope of the top-level main object and the static lexical scope of the script body are identical, so it doesn't matter whether you use a local variable of the script body or an instance variable of the main object.
However, if we make just a tiny change to that, by adding a second script and requireing it from the first, that condition will no longer hold, because we now have two separate script bodies and thus two separate script scopes, but still only one top-level object.
The idiomatic way in your example would definitely be to use a local variable, and nobody I know would do otherwise.

Best use case for instance variables is in Controller's when you want to pass parameter to the view.
Then you use something like
class TestController < ActionController::Base
def show
#usable_in_view = Test.first
not_usable_in_view = Test.first
end
end
In your view you can now use #usable_in_view, but cant use variable not_usable_in_view. Most people always use instance variable in controllers even if they do not need them in view, because they do not understand why they need instance variable

Instance variables are used so that they can be accessed in the view page.
Local variables are not accessible in the view. It has become the habit even I sometimes write instance variables though it is not required in the view.:-)

People probably get in the [bad] habit of using instance variables everywhere since it's common in Rails to use them to get information from the controller to the view.
In my own Ruby code I use instance variables only when I need to, local variables otherwise. That's the proper way to use Ruby.

Related

Setting global instance variable based on domain in Rails

I have a single Rails application that is currently being accessed via lets say https://www.domain1.com and I would like to enable the application to be accessed by a second domain, lets say https://www.domain2.com which would then set a global instance variable.
I would then like to use this instance variable to control certain things on the page such as content, styles and images.
Any ideas how I could achieve this?
E.g.
def my_action
#domain_instance_variable = request.domain
$domain_global_variable = request.domain
end
A global variable and an instance variable are different things. An instance variable is scoped to an instance of a class, and a global variable has a global scope. It's a good rule of thumb to minimize scope.
In the case of Rails code in controllers, views or helpers, you can just call request.domain directly instead of assigning that to a variable.

Redis vs Class variables in Rails

Im using Sidekiq, Redis, Websocket-rails inside of Rails. With sidekiq having server-side Class. Now, when adding more functionality, i cant anymore use instance variables inside my Sidekiq Class's methods, i need to share information between methods. Sidekiq class also inherits from Websocket class, to be available websockets.
Right now, i have 2000/per second data change for objects. Using 95% instance variables, 5% pushing/getting from Redis, to make lower I/O.
Im considering, using class variables or making all 100% on Redis. Im not sure about or it wont overload my background jobs with that big data transfer count, which goes bigger with every new client. I'm using heroku free Dyno, dont want to buy better server yet. But using class variables would be less I/O, and probably unsafe, becouse it inherits from Websocket-rails? Im doing this project partly to show something to employers, that i can program, to get my 1-st IT job. I care how they react to those class variables. What employers would say about class variables? And which one to choose?
You can absolutely use instance variables in your Sidekiq worker. You cannot use class or class-instance variables.
class MyWorker
include Sidekiq::Worker
def perform(a, b)
#a = a # instance variable, no problem!
##a = a # class variable, big problem!
self.set_a(a)
end
def self.set_a(a)
#a = a # class instance variable, big problem!
end
end
Class variables have their places, if they represent a concept that is unchangeable and should live as a definition of something, like a configuration throughout the application, or some fundamental multiplier of a part of the application domain/business.
But, just because things are unchanging, that doesn't mean they have to be a class variable. You can have an instance of a class to be always setup with the same values, and then share the instance. Kind of like a singleton, but not necessarily being a singleton, just being a widely shared variable that is part of the input on the start of a given process.
So instead of using Sidekiq to store data for this unchanging thing, and rather than using class variables, you can achieve a cleaner design by doing something like
class ImportantThing
def initialize(name, other_property)
#name = name
#other_property = other_property
end
# Other methods you wish to define the behavior of the thing
end
then you can
the_important_thing = ImportantThing.new("foobar", 3.46)
do_important_process(the_important_thing)
then as far as the important process you are running is concerned, the important thing is anything that behaves like a important thing, and it doesn't matter if it is a globally setup or ever unchanging.
This makes for a easy to test architecture which is generally a sign of clear and decoupled design.

How to properly set an instance variable with instance_variable_set?

I was looking through the docs for instance_variable_set and saw that the sample code given does this:
obj.instance_variable_set(:#instnc_var, "value for the instance variable")
which then allows you to access the variable as #instnc_var in any of the class's instance methods.
I'm wondering why there needs to be a colon : before the #instnc_var. What does the colon do?
My first instinct is to tell you not to use instance_variable_set unless you really know what you are using it for. It's essentially a tool for metaprogramming or a hack to bypass visibility of the instance variable.
That is, if there is no setter for that variable, you can use instance_variable_set to set it anyway. It's far better if you control the code to just create a setter.
Try looking at accessors for the vast majority of your instance variable setting needs. They are an idiomatic way to have getters and setters for your instance variables "for free" without you having to write those functions.
If you really do need instance_variable_set, it allows the first argument to be a symbol which is the name of the instance variable to set. A colon is part of the Ruby language that is like a "symbol literal": when you type :foo, you've created a symbol with value foo, just like when you type "bar" directly into your code, you create a string literal with value "bar".

Ruby instance variables versus ActiveRecord attributes

I've read that ruby objects are just places where we can store instance variables (and class pointers). So:
class Person
def initialize(age)
#age = age
end
end
Now if we run:
p = Person.new(101)
Then we get:
#<Person:0x86cf5b8 #age=101>
Great, the property age is stored as an instance variable, as expected. But things work a little differently if we convert the model to inherit from ActiveRecord. Now, after instantiating an new Person, we see this:
# timestamps removed
#<Person id: 1, age: 101 >
The age property no longer appears to be an instance variable. So what is really going on here?
I know that we can access the #attributes instance variable, which contains a hash of all the properties and values, so I'm wondering if ActiveRecord is possibly modifying the console output to present the objects attributes in this way.
Is it possible to instantiate a Ruby object where properties are held as attributes and not instance variables, without using ActiveRecord?
Yes, you can extend a ruby class with include ActiveModel::AttributeMethods to expose your instance variables as ActiveModel-like attributes.
See the docs for more information.
as you see in your code 'storing' properties as instance vars was your own doing, so if you wanna hold them any other way, is also up to you. ruby gives you convenience class methods to define getter and setter methods like attr_accessor.
also worth noting, that if you inherit from ActiveRecord::Base, you should not override initialize.
I'm wondering if ActiveRecord is possibly modifying the console output to present the objects attributes in this way.
Well, kind of. The method responsible for that is inspect, and it's implemented by Object in a way that (emplasis mine):
...shows the object's class name, an encoding of the object id, and a list of the instance variables and their values (by calling inspect on each of them).
There's more right after:
User defined classes should override this method to provide a better representation of obj.
This is exactly what ActiveRecord does and the reason why you're seeing this output. The overridden method does not list instance variables, but displays AR attributes.
So just because you aren't seeing the instance variable in the console doesn't mean it isn't there!

initializing a class with config (yaml), and setting a variable that should be a single instance

I am getting confused as to how to properly set variables in a initializer, I want these to be class level variables, not instance.
And I also want to then create a single instance of another object (it is a connection object, which already has connection pooling built in, so I just need a single reference to it).
My initializer /initializers/my_class.rb
yml = YAML.load_file("#{Rails.root}/config/my_class.yml")
MYMODULE::MyClass.init(yml)
And here is my my_class.rb:
module MYMODULE
class MyClass
def self.init(yml)
#post_url = yml["defaults"]["post_url"]
end
def self.post_url
#post_url
end
# this should be a single instance
def connection_pool
# ???
end
end
end
These class level variables, how can I access them from both class methods and instance methods?
I'm getting wierd behaviour, and I'm confused as to how to reference the post_url from inside of either class methods and instance methods.
I have seen the following ways, unsure which is correct:
self.class.post_url
MyClass.post_url
#post_url
post_url
self.post_url
self.class.post_url or MyClass.post_url will work. The difference is how they work for subclasses (in the former case, subclasses will use their own version of this variable automatically, in the latter, they would share the variable in MyClass).
There is no way to directly access class instance variables from an instance: you have to call a class method which returns (or sets) them. See also: cattr_accessor.
That said, if this is really a singleton, it seems a little strange to me that you would configure part of it on the class, and then reference that info in the (single) instance. Wouldn't it make more sense just to configure this stuff on the instance? Or use a module as a singleton and not create an instance at all?

Resources