Why Use "Self" to Access Activerecord/Rails Model Properties

Why use self to access ActiveRecord/Rails model properties?

Often the use of self is to force Ruby to recognize that as a method call and not mis-interpret it as a variable. Without prior knowledge of a method called day=, then day = "x" looks to Ruby like a variable assignment. self.day = "x" is always a method call.

The reason this is trouble is because the name and name= methods are added dynamically after the User class file has been parsed. The first thing Rails does when using a model is make methods for the associated database fields, but this happens after your user.rb file is parsed.

Is it better practice to use attribute_name or self.attribute_name when accessing within model

Camp 1: Convention over configuration. Also, self.first_name will not work for private accessors.

Camp 2: You know what's what at a glance, whereas you might forget what methods you have without the explicit receiver.

In the end, it's an opinion question, so I'm voting to close. However, food for thought:

bbatsov style guide:

Avoid self where not required. (It is only required when calling a self write accessor.)

GitHub style guide (based on bbatsov style guide):

Avoid explicit use of self as the recipient of internal class or instance messages unless to specify a method shadowed by a variable.

Rails, activerecord: self[:attribute] vs self.attribute

They're both just methods to get to the attribute - they're both just getters. self.attribtue is a more "traditional" getter, whereas self[:attribute] is basically just the [] method. Switching between using either has no ramifications.

I'd recommend using only the self.attribute method because it's syntactically nicer. However, using the self[:attribute] can come in handy when something else overrides the self.attribute method.

For example, suppose you have a User model with a name database column, so you'd get user.name. But let's say you install a gem that adds a #name method to each of your models. To avoid the complication, one option is to use user[:name] to access it directly without going through the compromised method.

What's the difference between using self.attribute and attribute in a model?

The difference in your examples is that the first one works, the second doesn't.

Your second version isn't doing anything (at least nothing meaningful). Writing my_attr = 123 is not equivalent to self.my_attr = 123. Instead it's creating a local variable called my_attr and setting it to 123, and then immediately reaching the end of the method and throwing my_attr away. The whole method is essentially a no-op, and it doesn't affect the model's my_attr value in any way.

class User < ActiveRecord::Base
def do_another_thing!
my_attr = 456

puts self.my_attr # nil (or whatever value it was before)
end
end

Conversely, if you want to access a method defined on an object, you can (and should) omit self:

class User
def name=(value)
@name = value
end

def name
@name
end

def age=(value)
@age = value
end

def age
@age
end

def do_something
self.name = "bob" # self is required
puts name # bob (self.name)

age = 47 # @age is unaffected
age # 47 (local variable), but self.age is nil

end
end

Note that, this isn't a Rails question, it's a Ruby question. There is no Rails-specific code here, this behaviour is part of how Ruby's syntax works.

When to use self in Model?

When you're doing an action on the instance that's calling the method, you use self.

With this code

class SocialData < ActiveRecord::Base
def set_active_flag(val)
active_flag = val
save!
end
end

You are defining a brand new scoped local variable called active_flag, setting it to the passed in value, it's not associated with anything, so it's promptly thrown away when the method ends like it never existed.

self.active_flag = val

However tells the instance to modify its own attribute called active_flag, instead of a brand new variable. That's why it works.

Why isn't self always needed in ruby / rails / activerecord?

This is because attributes/associations are actually methods(getters/setters) and not local variables. When you state "parent = value" Ruby assumes you want to assign the value to the local variable parent.

Somewhere up the stack there's a setter method "def parent=" and to call that you must use "self.parent = " to tell ruby that you actually want to call a setter and not just set a local variable.

When it comes to getters Ruby looks to see if there's a local variable first and if can't find it then it tries to find a method with the same name which is why your getter method works without "self".

In other words it's not the fault of Rails, but it's how Ruby works inherently.

Hope that helps.

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

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

Difference between self.attribute_name and only attribute_name

You can access email from different ways when you are on the User class.

  • self.email when you are in the User scope
  • a_user.email when you have specified a user
  • email when you are in the User class. This is valid for every method in the User class.
  • @email, the variable returned by the email function
  • attributes[:email] the ActiveRecord attribute.

All of this methods are automatically generated by the ActiveRecord model, you can see the doc for more details.

How to access a property from belongs_to model from a scope

Your scope is an instance method rather than scope, since it does not return ActiveRecord::Relation object.

I suggest you to do following:

# person.rb:
def average_grade
review.average(:grade)
end

# controller:
def show
@person = Person.find(params[:id])
@average = @person.average_grade
end


Related Topics



Leave a reply



Submit