Why are # variables used in Ruby class models? - ruby-on-rails

Why are #variables needed in classes? What value do they add? I couldn't find anything online for this but maybe I'm searching for the wrong terms. Is there a resource online I can look this up? Thanks!
car.rb
class Car
attr_accessor :make, :model
def initialize(make = '')
#make = ''
#model = ''
end
end

These variables are called instance variables. Every instance of the class has it's own copy of these variables.
In your example, you would like every instance of the class Car to have it's own make and model.
Note the following example
car1 = Car.new("Toyota", "Carola")
car2 = Car.new("Mitsubishi", "Lancer")
Both car1 and car2 each have their own private make and model. The way to tell the Ruby interpreter to do this is to use #.

Instance variables are available in all areas of each instance of a class.
Each time you create an instance of car the variable is specific to that particular one.
e.g.
car1 = Car.new('Ford', 'Falcon')
car2 = Car.new('Toyota', 'Camry')
Now car1 and car2 have different instances of #make and #model.
If you declare the variable as a class variable using ##make, then every Car has access to it and every time it is changed, it is changed for everyone.
Basically class variables allow you to put some 'walls' around your data.
The attr_accessor creates two methods in your call
def make=(value)
#make = value
end
def make
#make
end
This allows you to call the instance variable within and from outside your class without the #
e.g.
car1.make
returns
'Ford'
www.codecademy.com has some great free courses in basic Ruby that will teach you this stuff really well.

That's the syntax for defining instance variables in ruby.

Objects are said to have behaviours and properties, much like real world object.
The properties of objects are defined by instance variables. And # prefix is
ruby's way of telling particular variable is an instance variable/characteristic.
What value they add ?
They help you to have a variable that is accessible across all your instance methods.
Let us disect your example
class Car
attr_accessor :make, :model #attr_accessor methods creates a getter method. car_instance.make and
#a setter method car_instance.make=(val), which otherwise you would have to do explicitly.
def initialize(make = '') #constructor
#make = '' #initializing your instance variables. I guess you meant #make = make here
#model = ''
end
end
I couldn't find anything online for this but maybe I'm searching for the wrong terms.
Yup you were.
Is there a resource online I can look this up?
Yup, Ruby User Guide By Matz
Bit more details
In ruby, the scope of a variable inside a class is identified by the compiler by first two characters of the
varaible name.
##variable_name -> Two '#' s. Class variables/ Scope across all instances of the class. Eg: If you want to have a count on total number of cars you made.
#variable -> One '#' Instance varaibles/ Scope across an instance.
variable -> No '#' Local variables/ Scope to the block they belong.

Look at the below code,which I changed a bit for understanding:
class Car
attr_accessor :make, :model
def initialize(make = '')
#make = make
#model = ''
end
end
car1 = Car.new("Toyota")
car2 = Car.new("Roxin")
p car1.instance_variables #<~~ A
p car2.instance_variables #<~~ B
p car1 #<~~C
p car2#<~~D
Output:
[:#make, :#model]
[:#make, :#model]
#<Car:0x1bc89e8 #make="Toyota", #model="">
#<Car:0x1bc8940 #make="Roxin", #model="">
Explanation: Here in A and B you might think of that both car1,car2 are using the same copy of instance variables. But Not is the case. See when I am printing car1 and car2, in C and D,outputs shows that car1 and car2 are holding different values for the instance variable #make. This fact objects has their own unique instance variables.
Hope this helps.
Cheers!!

Related

Ruby variables visibility inside modules

TL;DR;
I need to make some ##vars of a static method (extends) in one module visible to a instance method in another module(includes).
How to accomplish that once only setting ##var=value was not enough to make it visible?
Maybe you can just read my capitalized comment bellow and jump to question 4.
Hi, I would like to add an method to my models to index some data in a mysql table with some full text search fields.
In order to accomplish that, I created the following module:
module ElasticFakeIndexing
module IndexingTarget
#instance method to be called on model to get data to save
def build_index_data
{
entity_id: self.id,
entity_type: self.class.name,
#UNABLE TO ACCESS IF SET ONLY WITH ##var=value. Why?
#AND ALMOST SURE THAT USING class_variable_set IS THE CAUSE OF CONFIGURATION OF ONE MODULE MESSING UP WITH ANOTHER'S
title: ##title_fields.collect{|prop| self.send(prop.to_sym)}.join(" || "),
description: ##description_fields.collect{|prop| self.send(prop.to_sym)}.join(" || "),
}
end
def self.included(base)
base.extend ClassMethods
end
module ClassMethods
#class method to declare/call at a given model
def elastic_fake(options = {})
#Make sure we always get an array so we can use 'join'
title_arg = Array(options[:title])
ElasticFakeIndexing::IndexingTarget.class_variable_set(:##title_fields, title_arg)
description_arg = Array(options[:description])
ElasticFakeIndexing::IndexingTarget.class_variable_set(:##description_fields, description_arg)
extra_arg = Array(options[:extra])
ElasticFakeIndexing::IndexingTarget.class_variable_set(:##extra_args, extra_arg)
end
end
end
end
And I use it this way at my models:
class SomeModel < ApplicationRecord
#includes the module
include ElasticFakeIndexing::IndexingTarget
...
# 'static' method call to configure to all classes of this model
elastic_fake(title: "prop_a", description: ["prop_b", "prop_c", "prop_d"])
end
And at some point of my code something like this will be called:
index_data = some_model_instance.build_index_data
save_on_mysql_text_search_fields(index_data)
But I got some problems. And have some questions:
when I use/include my module in a second model, looks like the configuration of one model is being visible to the other. And I got 'invalid fields' like errors. I guess it happens because of this, for example:
ElasticFakeIndexing::IndexingTarget.class_variable_set(:##title_fields, title_arg)
But I got to this this because only set ##title_fields wasn't enough to make title_fields visible at build_index_data instance method. Why?
Why using only #title_fields isn't enough too to make it visible at build_index_data?
How to design it in a way that the set of fields are set in a 'static' variable for each model, and visible inside the instance method build_index_data? Or as a possible solution, the fields could live in a instance variable and be visible. But I think it should live in a 'static' variable because the fields will not change from one instance of the model to another...
Any thoughts? What am I missing about the variables scopes/visibility?
Thank you
Read the following articles on Ruby variables:
Ruby Variable Scope
Understanding Scope in Ruby
quick reminder: ##title_fields, class variable, must be initialized at creation time, while #title_fields, instance variable, hasn't such requirement.
Instead of relying on class variables I recommend using class side instance variables. Class variables will easily be overwritten between individual models including the module. Class side instance variables however are save.
Using some of the syntactic sugar (namely concern and class_attribute) rails offers you could write something like
module ElasticFakeIndexing
extend ActiveSupport::Concern
included do
class_attribute :title_fields,
:description_fields,
:extra_args
end
class_methods do
def elastic_fake(options = {})
...
self.title_fields = Array(options[:title])
...
end
end
def build_index_data
...
title: self.class.title_fields ...
...
end
end

Explanation of the # variables inside Rails models

I've worked through a few Rails applications which are using # attribute variables (which are already attr_accessible) in the models. I was having a tough time getting information on this but from what I gather name and #name are the same thing in a model but im probably incorrect on this.
How do these # variables work and why would one ever use the # symbol variables?
To add to the current answers, #instance_variables are from object oriented programming...
In object-oriented programming with classes, an instance variable is a variable defined in a class (i.e. a member variable), for which each instantiated object of the class has a separate copy, or instance.
OOP
"Normal" programming has variables as pieces of data -- strings, integers etc. These are independent & can only interact as part of other functions, depending on their scope.
Object Oriented programming (OOP) can treat variables as pieces of editable data (known as classes). These classes can be invoked, edited and most importantly interacted...
#app/models/product.rb
class Product < ActiveRecord::Base
#-> class
end
def new
#product = Product.new #-> creating a new instance of the class
end
Ruby/Rails is object oriented; it works by giving you a series of objects to load & interact with. The most notable example is with game programming:
The way object orienting works is to invoke/initialize an instance of a class (in our case Product), allowing you to manipulate it.
Class instances hold the object in memory, allowing you to perform actions on the class itself. To do this, you'd store the instance of the class in a variable, allowing you to interact with the variable itself:
#product = Product.new
#product.save
--
Instance variables are only valid within the context of the class:
# app/controllers/products_controller.rb
class ProductsController < ApplicationController
def show
#product = Product.new #-> #product only available within ProductsController
end
end
The controllers in Rails are classes, invoked through a rack request:
Request > Rack > Routes > ProductsController.new(request).show > Response
If you want your #instance_variable available in all methods of the class, it has to be at instance level...
# app/controllers/products_controller.rb
class ProductsController < ApplicationController
def show
#product = Product.new
product_save
end
private
def product_save
#product.save #-> #product available in instance of ProductsController class
end
end
The most common use for #instance_variables are to store / manipulate instance-centric data. A good example (for our Product example) could be the stock level:
#app/models/product.rb
class Product < ActiveRecord::Base
def stock
#qty / #orders
end
end
Because you can use getter/setter methods within Ruby, you can define the instance values of a class, accessing them through other instance data:
#product = Product.find x
#product.stock #-> outputs stock value for that product
"#" in front of the name signifies that it is an instance variable of the model class, which is different than a simple ruby variable name. Instance (#) variables can be used in the front-end (views).
For example, you can instantiate a variable #book which is a specific instance of the Book class (which is the model). So when you use #book, you are referring to that specific book you created, rather than the overall class.
There is also a difference in the methods you can call on instance versus class variables.
Variables beginning with # are instance variables, in short they allow other methods of the same class to use them. For example,
class Something
def a_method(name)
#i_variable = name
normal_variable = "hey"
end
def another_method
#i_variable.reverse #can be seen here and used
normal_variable #cannot be seen here and cannot be used, throws an error
end
end
In Rails, I believe instance variables are important because they are visible across the Model, View, Controller parts of the application.
Variables that start with # are instance variables. This means that belong to an instance of a class. So if I had the following class:
class Dog
def initialize(name, breed)
#name = name
#breed = breed
end
end
Everytime I create a Dog, I need to give it a name and a breed.
fido = Dog.new("Fido", "Dalmation")
Because I created instance variables from the name and breed I can get this information from fido.
fido.name => "Fido"
fido.breed => "Dalmation"
If you do this:
def foo
thing = "value"
end
foo
puts thing
You will get
NameError: undefined local variable or method `thing' for main:Object
If you do this:
def foo
#thing = "value"
end
foo
puts #thing
You will get "value"
You see, #variables are visible to the whole class you are operating on, in this case the model.
The #variables are also used a lot in controllers, like this:
def hello
#current_user_name = User.find(params[:user_id]).name
end
# and in the view:
Hello, <%= #current_user_name %>
Because if you didn't use #, you would have made a local variable and that is not accessible by the view.
A var with # is an instance private variable. A var without # is a local method variable.
A variable with # can be accessed on an object's level if the object has a method returning a value of or setting a value to it being attr_accessor, attr_reader or def.

Ruby/Rails: Understanding ruby getter-setter methods and instances

I am new to ruby and programming in general and am trying to grasp a few key concepts.
Given I have a class Dog, with the below characteristics.
class Dog
attr_accessor :type, :popularity, :total
def initialize(type = nil)
#type = type
end
def total_dogs
Dog.count
end
def total
Dog.where(:type => self.type).size
end
def popularity
total.to_f/total_dogs
end
end
What I am trying to understand, is how ruby persists attributes to an instance via getter/setter methods. Its clear to me that if I instantiate a new instance and then save attributes to that instance, those attributes are tied to that instance because if I look at the object the attributes appear as such:
#dog = Dog.new
#dog
=> #<Dog:0x007fa8689ea238 #type=nil>
Its easy for me to understand that as I pass the #dog object around its always going to have the #type attribute as nil.
However, the situation I am having trouble understanding is if I pass this #dog object to another class. Like if I did:
Owner.new(#dog)
When I am in the owner class and I call #dog.popularity how does it know the value of popularity for that instance? At runtime are all methods processed and then that instance just always is tied to the value at the time? Apologies if this makes no sense or I am way off.
When you do
#dog = Dog.new
You do two spearate things
1) Create an instance variable #dog for whatever object your code is currently inside
2) Instantiate a new instance of Dog (with all its methods and attributes) and assign a reference to it to #dog
#dog is a variable, that just happens to point at the Dog instance ("instance of class" generally same meaning as "object") you created at that point. You can set other variables to point to the same instance, and in Ruby this is generally how you pass data around. Objects contain instance variables, and those instance variables point to yet more objects.
Using the assignment operator (i.e "=") you can point a variable at any other object.
To answer your questions in turn:
When I am in the owner class and I call #dog.popularity how does it
know the value of popularity for that instance?
You have to be careful in Ruby (and OO languages in general) to differentiate between class and object in your descriptions and questions. Ruby I'm assuming you are referring to a line of code in the Owner class, and that you intend it to work with an owner object. I'd also assume that #dog is an attribute you have added to Owner.
In which case, Ruby knows because #dog points to the Dog object that you added to owner. Each Dog object has its own copy of all of Dog's instance variables. You do need to take care in Ruby though, because variables point to objects, that you aren't simply passing in the same Dog object to all the owners (i.e. they all effectively share a single dog). So you need to understand when you are creating new instances (via new) and when you are simply handling existing references.
At runtime are all methods processed and then that instance just
always is tied to the value at the time?
No. At runtime, basic Ruby will only perform the assignments that you have coded. Instance variables may not even exist until the code that assigns them has been run. If you use attr_reader etc methods, then the variables will at least exist (but will be nil unless you assign something during initialize)
Niel has a great answer for this, I just want to add something to it.
Counting Dogs :)
You need a class variable to do this..
class Dog
##count = 0 # this is a class variable; all objects created by this class share it
def initialize
##count += 1 # when we create a new Dog, we increment the count
end
def total
##count
end
end
There is another way to do this with "instance variables of the Class object" but that's a bit of an advanced topic.
Accessing Instance Variables
In Ruby, variables are really just references to objects / instances.
> x = 1
=> 1
> x.class
=> Fixnum
> 1.instance_variables
=> []
x is a reference to the object '1', which is an instance of class Fixnum.
The '1' object is an instance of Fixnum which does not contain any instance variables.
It is not different in any way from a reference to a new "Dog" instance.
Similarly, you can say x = Dog.new , then x is a reference to an instance of class Dog.
class Dog
attr_accessor :legs # this defines the 'legs' and 'legs=' methods!
end
x = Dog.new
x.instance_variables
=> [] # if you would assign legs=4 during "initialize", then it would show up here
x.legs = 4 # this is really a method call(!) to the 'legs' method
x.instance_variables # get created when they are first assigned a value
=> [:legs]
It does not matter if you pass such a reference to a method call, or to another class or just evaluate it by itself - Ruby knows it's an object reference, and looks inside the object and it's inheritance chain on how to resolve things.
Resolving Method Names
That was only the partial truth :) When interpreting x.legs , Ruby checks if there is a method in the class-inheritance chain of the object, which responds to that name 'legs'.
It is not magically accessing the instance variable with the same name!
We can define a method 'legs' by doing "attr_reader :legs" or "attr_accessor :legs", or by defining the method ourselves.
class Dog
def legs
4 # most dogs have 4 legs, we don't need a variable for that
end
end
x.legs # this is a method call! it is not directly accessing a :legs instance variable!
=> 4
x.instance_variables
=> [] # there is no instance variable with name ":legs"
and if we try to implement it as a method and an instance variable, this happens: :)
class Dog
attr_accessor :legs # this creates "def legs" and "def legs=" methods behind the scenes
def legs # here we explicitly override the "def legs" method from the line above.
4
end
end
x = Dog.new
x.legs # that's the method call we implemented explicitly
=> 4
x.legs = 3 # we can still assign something to the instance_variable via legs=
=> 3
x.legs # the last definition of a method overrides previous definitions
# e.g. it overrides the automatically generated "legs" method
=> 4
attr_accessor :legs is just a short hand notation for doing this:
class Dog
def legs
#legs
end
def legs=(value)
#legs = value
end
end
there is no magic way instance variable get automatically accessed. They are always accessed through a method, which can be overridden later.
I hope that makes sense to you
When you create an object, you don't need to use the # symbol. The variable is the object. So if you have multiple dogs, you'd do:
myDog = Dog.new(brown)
yourDog = Dog.new(white)
From there, you can say:
yourDog.type #white
myDog.type #brown
What you would NOT do is:
#dog = Dog.new #myDog
#dog = Dog.new #yourDog
If you need multiple versions of an object, you just give them different names. So if you create multiple dogs and pass them to other objects, they will work. For example:
Say your owner class is:
Class Owner
def initialize(pet)
puts "my pet is #{pet.type}"
end
Then using the instance variable would be:
me = Owner.new(myDog) #my pet is brown
you = Owner.new(yourDog) #my pet is white
Both "type" and "popularity" are methods on "dog" instances. Their definition is as follows:
class Dog
# getter
def type
#type
end
def popularity
total.to_f/total_dogs
end
end
This is roughly equivalent to:
class Dog
attr_accessor :type
def popularity
total.to_f/total_dogs
end
end
Note that attr_accessor is just a shortcut for defining a getter method. If you define a method yourself it is pointless to use attr_accessor:
class Dog
attr_accessor :popularity
# this will override getter defined by attr_accessor
def popularity
total.to_f/total_dogs
end
end
Back to your question: #dog.type invokes type method on #dog which returns its instance variable; #dog.popularity invokes popularity method on #dog which does calculations (as defined by you) on the fly and returns the result. No magic here!

Rails AntiPatterns book - Doubts on composition

I'm reading the Rails AntiPatterns book, which I'm enjoying a lot. At one point, the author talks about the goodness of composition and it gives an example where an Order class gives the responsibility of conversion (to other formats) to another class, called OrderConverter. The classes are defined as:
class Order < ActiveRecord::Base
def converter
OrderConverter.new(self)
end
end
class OrderConverter
attr_reader :order
def initialize(order)
#order = order
end
def to_xml
# ...
end
def to_json
# ...
end
...
end
And then the author says: "In this way, you give the conversion methods their own home, inside a separate and easily testable class. Exporting the PDF version of an order is now just a matter of call-ing the following:"
#order.converter.to_pdf
Regarding to this, my questions are:
Why do you think that order object is preceded by an #? Shouldn't it be created as:
order = Order.new
And then convert by doing:
order.converter.to_pdf
Why is the attr_reader :order line needed in the OrderConverter? It's so we can access the order from an OrderConverter object? Is it needed to be able to do
order.converter.to_pdf ? We could do that without that attr_reader right?
An instance of Order is passed to the initialize method and stored as an instance variable (using the # syntax : #order). This way, this variable can be accessed from other methods in the converter (the variable has the instance scope) :
class OrderConverter
def to_pdf
#order.items.each do |item|
# Write the order items to the PDF
end
end
end
The attr_reader is not strictly required, but is a convenient way to access the Order object from other methods :
class OrderConverter
def to_pdf
order.items.each do |item|
# Write the order items to the PDF
end
end
end
It will also allow you to get the reference to the order out of any converter instance :
converter.order
The # on the front of the variable makes it an instance variable. If it wasn't there the variable would just be a local variable. I'm guessing that since this is a book about Rails, it's assuming that this code would be in a controller. Variables that controllers want to share across methods or expose in their views need to be instance variables. If this is the case #order was probably created either via parameters from a request or with values pulled from the database.
This probably isn't that significant though, both his example and your example work - I think the author was just showing how a call to the OrderConverter would look, and ignored how the Order object got created.
attr_reader :order creates a "getter" method for the #order instance variable in OrderConverter - it's not needed for to_pdf - it would be used to get the Order back out of the OrderConverter via converter.order. I don't see any need to have this in the code you've given so far, but maybe there's some need for it later.

rails has_many and access inside of class

I have a rails model class
class Model < ActiveRecord::Base
has_many :object_collection
def add_object(object)
object_collection.push object // works
#object_collection.push object // does not work
self.object_collection.push object // works
end
end
I was wondering if someone can please explain to me why the # does not work yet self does i thought these two meant the same
cheers
They are not the same. Consider the following Ruby code:
class Person
attr_accessor :employer
end
john = Person.new
john.employer = "ACME"
john.employer # equals "ACME"
The method attr_accessor conveniently generates an attribute reader and writer for you (employer= and employer). You can use these methods to read and write an attribute, which is stored in the instance variable #employer.
Now, we can rewrite the above to the following, which is functionally identical to the code above:
class Person
def employer=(new_employer)
#works_for = new_employer
end
def employer
#works_for
end
end
john = Person.new
john.employer = "ACME"
john.employer # equals "ACME"
Now, the instance variable #employer is no longer used. We chose to write the accessors manually, and have the freedom to pick a different name for the instance variable. In this particular example, the name of the instance variable is different than the name of the attribute accessors. There is nothing that prevents you from doing that.
This is similar to how ActiveRecord stores its attributes internally. They are not stored in instance variables of the same name, that is why your push call to #object_collection does not work.
As you may understand, attribute readers and writers offer a certain abstraction that can hide the implementation details from you. Reading and writing instance variables directly in subclasses is therefore generally considered bad practice.
#foo identifies an instance variable called #foo. foo identified a method called foo.
By default, instance variables in Ruby are private. It means you cannot access the value of an instance variable unless you have some public method that exposes the value.
Those methods are called setters and getters. By convenction, setter and getter have the same name of the instance variable, but this is not a requirement.
class MyClass
def initialize
#foo
end
def foo=(value)
#foo = foo
end
def foo
#foo
end
def an_other_foo=(value)
#foo = foo
end
def an_other_foo
#foo
end
end
Though methods and instance variables can have similar names, thery are different elements.
If this topic is not clear to you, you probably need to stop playing with Rails and go back studying how Ruby works.
In your specific case, object_collection doesn't exist as an instance variable because it's an association method.
They do not mean the same thing. One is an instance variable, the other is a method.
The #foo means "the value of the instance variable foo", where as self.foo means "the value of a call to the method foo on myself".
It is typical for a method foo= to set the #foo instance variable, so I can see how someone new to the language might be confused. I'd encourage you to pick up a book on the ruby language. There's one specifically for people who have done some rails but never learned ruby proper. You often can hack rails without understanding the language or what these statements mean, but you'll be far less productive than someone who spends the small amount of time it takes to learn the ruby language itself.
As a general rule, use the self.foo form whenever you can, as this is less sensitive to changes in the classes definition.

Resources