Instance variables inside of model - ruby-on-rails

Let's say I have model Foo with bar column.
I'm quite used to define instance methods of model like this:
class Foo < ActiveRecord::Base
def do_smth
self.bar.capitalize!
end
end
But this is calling getter method on self, which should be just longer way of #bar.capitalize!.
As in Rails some things are hidden from eye (such as initializing Base class) I'm not sure. Will it work?

There is quite a bit of confusion going on here so lets start from the top:
Ruby is all about message passing - when you call a method on an object the implicit recipient is the object itself:
def do_smth
self.bar.capitalize!
end
is equivalent to:
def do_smth
bar.capitalize!
en
So lets take the following example:
class Thing
def initialize
#foo = 1
end
def test
foo # NameError: undefined local variable or method `foo'
end
def test2
#bar # nil
end
end
So, why does this fail? Because in Ruby instance variables are scoped to the instance and only accessible by using the sigil #. Ruby does not automatically map #foo to Thing#foo. Instance variables Ruby even lets you access uninitialized ivars (#bar).
That is unless we create an accessor:
class Thing
def foo
#foo
end
def test
foo # nil
end
end
Which can be done with the shorthand macros attr_reader and attr_accessor.
class Thing
attr_reader :foo # read only
attr_accessor :foo # rw
def test
foo # nil
end
end
This can be quite confusing if you start with Rails without learning how Ruby does OOP since Active Record reads your database tables and creates getters and setters "magically" for all your model attributes.
One thing that you need to remember in that case is that using #some_column will not call any getter/setter methods. Which can cause issues with the Rails dirty state tracking and in cases like date columns which typecast in the setter.

Related

"transaction do" without explicit receiver

According to the docs, in Rails models, transactions are made on either classes descending from ActiveRecord::Base or any instance of such a class. Some blog posts on the subject list the following alternatives:
ActiveRecord::Base.transaction do
User.transaction doUser descends from ActiveRecord::Base
#user.transaction do#user is an instance of User
self.class.transaction dowithin any model.rb where self is e.g. User
self.transaction dowithin any model.rb where self is e.g. an instance of User)
However, I'm missing another variation on this list: How about just transaction do within model.rb?
class User < ApplicationRecord
def foo
transaction do
# do stuff
end
end
end
Am I right to assume, that the self. is not really necessary since in the scope of a model instance method, self is always the model instance by default?
In Ruby the implicit recipient in an instance method is always the instance:
class Thing
def foo
inspect
end
end
irb(main):026:0> Thing.new.foo
=> "#<Thing:0x007fde95955828>"
Am I right to assume, that the self. is not really necessary since in
the scope of a model instance method, self is always the model
instance by default?
There is always an implicit recipient (self) in ruby. What it is depends on the context. In class methods its the class. Its "main" (the global object) if you are not in a class or module.
class Foo
inspect
end
# => "Foo"
def foo
inspect
end
# => "main"
You only have to explicitly use self where there is a need to disambiguate (like for example self.class). But there are many situations where it can help the readability of your code.
All of the options above are pretty much functionally equivalent:
ActiveRecord::Base.transaction connects to the DB for that environment from database.yml or ENV["DATABASE_URL"].
User.transaction or class.transaction can be used if you want to have different connections per model class or just don't like typing. All it really does is connection.transaction(options, &block).
The instance level self.transaction or #user.transaction just proxies to the class method. All it really does is self.class.transaction(options,&block);

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.

If self points to the klass object in ruby, why is it used as assignment in ActiveRecord?

Note: This was the best title I could think of that wouldn't make this question seem like a dup. Please feel free to edit if it does not capture the point of the question
So I've been reading Advanced Rails and I had a question that wasn't fully answered by these posts:
When to use `self.foo` instead of `foo` in Ruby methods
In Ruby. How do I refer to the class from within class << self definition?
Why use "self" to access ActiveRecord/Rails model properties?
In my understanding, within a class definition, self refers to the object referenced by the pointer klass, so for the example of an eigenclass:
class A
class << self
def to.s
"Woot"
end
end
end
is the exact same as:
class << A
def to.s
"Woot"
end
end
This is because as you open up the class, ruby is creating a Class:A (virtual) class and assigning A's klass pointer to it. To my understanding this means that within the context of this class definition self == A
My question is (assuming my understanding as I've described it above is accurate) why then in an ActiveRecord::Base subclass in Rails does the use of self seem to be merely a disambiguation of the instance and not the class itself?
For example, if I have a class: A < ActiveRecord::Base with an attribute name, I cannot call A.name = "blah" outside the class definition. However, I CAN use ( and indeed MUST use ) self in assignment within the class definition like so: self.name = "blah".
If the use of self in Ruby refers to the Class object and not the instance, why does this work in ActiveRecord and how does Rails make the distinction?
Everything is a method
Anything you do with an object is a method. When you have a class like this:
class A
attr_accessor :foo
end
obj = A.new
obj.foo = 3
obj.foo #=> 3
It might feel weird, but you didn't make any assignment here. obj.foo = 3 is only a synthatic sugar for obj.send(:foo=, 3) so in fact, it executes foo= method. obj.foo is a method as well, it simply returns a value. This is because all the instance variables are private, and there is no other way to interact with them than inside the method. attr_accessor :foo is just a shortcut for:
def foo=(value)
#foo = value
end
def foo
#foo
end
Now, when ruby see any identifier like foo, it needs to find out what it is. Firstly it assumes it is a local variable, and if not, it tries to execute it as method on current self (see below). This means that:
class A
attr_accessor :foo
def bar
foo = 'bar'
end
end
a = A.new
a.bar #=> 'bar'
a.foo #=> nil
What happened? Interpretor first uses foo as an instance variable, it is not defined. But there is an assignment next to it, so it defines a new instance variable and make an assignment. If we want to use our setter we need to tell interpreter that it is not an instance varible:
class A
attr_accessor :foo
def bar
self.foo = 'bar'
end
end
a = A.new
a.bar #=> 'bar'
a.foo #=> 'bar'
I lied. Everything is an object
about self
self in ruby is very similar to this in javascript - it means different things in different context, and it can be easily changed at any point (obviously, not recommended).
First thing you need to know is that in Ruby every class is an object, namely it is an instance of class Class. When you do
class A
it is (almost, method below uses block and has access to external scope) equivalent to:
A = Class.new
self within a context of a class is always a class itself.
class A
self #=> A
def self.bar #=> this is class method
self #=> A
end
def foo #=> this is instance method
# We are no longer in context of class, but in context of the instance, hence
self #=> instance of A
end
end
Now, for any mutable object you can defined a singleton class. This is sort of weird concept - it is an extra subclass of the original class which only has a single instance. Since all the methods come from the class, this allows you to define extra methods on a particular instance of the method without affecting other object. In most of the cases, instance class is not needed and it is created when you access it for the first time. There are couple of ways to open it:
object.singleton_class do
self #=> instance class
end
class << object
self #=> instance class
end
Any method you defined within instance class is accessible only on that particular instance, hence this:
class A
self #=> A
class << A
# we are now in the instance class of A (which is an instance of Class class)
def foo
# So this is an instance method, however our instance is a class A
self #=> A
end
end

Pass a symbol to a method and call the corresponding method

In a Rails controller you can pass a symbol to the layout method that corresponds to a method in you controller that will return the layout name like this:
layout :my_method
def my_method
'layout_1'
end
I want to have a similar functionality to likewise pass a symbol to my classes method and that class should call the corresponding function and use its return value, like this
myClass.foo :my_method
def my_method
'layout_1'
end
I've read posts[1] that tell me I need to pass
myClass.foo(method(:my_method))
which I find ugly and inconvenient. How is rails here different allowing to pass just the symbol without any wrapper? Can this be achieved like Rails does it?
[1] How to implement a "callback" in Ruby?
If you want to only pass a :symbol into the method, then you have to make assumptions about which method named :symbol is the one you want called for you. Probably it's either defined in the class of the caller, or some outer scope. Using the binding_of_caller gem, we can snag that information easily and evaluate the code in that context.
This surely has security implications, but those issues are up to you! :)
require 'binding_of_caller'
class Test
def foo(sym)
binding.of_caller(1).eval("method(:#{sym})").call
end
end
class Other
def blork
t = Test.new
p t.foo(:bar)
p t.foo(:quxx)
end
def bar
'baz'
end
end
def quxx
'quxx'
end
o = Other.new
o.blork
> "baz"
> "quxx"
I still don't understand, what is author asking about. He's saying about "callbacks", but only wrote how he wants to pass parameter to some method. What that method(foo) should do - i have no idea.
So I tried to predict it's implementation. On class initialising it gets the name of method and create private method, that should be called somewhere under the hood. It possible not to create new method, but store method name in class variable and then call it somewhere.
module Foo
extend ActiveSupport::Concern
module ClassMethods
def foo(method_name)
define_method :_foo do
send method_name
end
end
end
end
class BaseClass
include Foo
end
class MyClass < BaseClass
foo :my_method
private
def my_method
"Hello world"
end
end
MyClass.new.send(:_foo)
#=> "Hello world"
And really, everything is much clearer when you're not just wondering how it works in rails, but viewing the source code: layout.rb

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