Differencebetween Methods and Attributes in Ruby

What is the difference between Methods and Attributes in Ruby?

Attributes are specific properties of an object. Methods are capabilities of an object.

In Ruby all instance variables (attributes) are private by default. It means you don't have access to them outside the scope of the instance itself.
The only way to access the attribute is using an accessor method.

class Foo
def initialize(color)
@color = color
end
end

class Bar
def initialize(color)
@color = color
end

def color
@color
end
end

class Baz
def initialize(color)
@color = color
end

def color
@color
end

def color=(value)
@color = value
end
end

f = Foo.new("red")
f.color # NoMethodError: undefined method ‘color’

b = Bar.new("red")
b.color # => "red"
b.color = "yellow" # NoMethodError: undefined method `color='

z = Baz.new("red")
z.color # => "red"
z.color = "yellow"
z.color # => "yellow"

Because this is a really commmon behavior, Ruby provides some convenient method to define accessor methods: attr_accessor, attr_writer and attr_reader.

Methods vs Attributes Rails

To answer the part I think I understand: yes, an attribute on a model is quite different than a method.

Take, for example, a User model, a Users controller, and one of its corresponding views. Let's pretend the User model itself contains first_name, last_name, and email properties:

class User < ActiveRecord::Base
#Ensure presence of the following before saving model
validates_presence_of :first_name, :last_name, :email

#Verify unique email
validates_uniqueness_of :email

#Here's our lone User method...poor guy
def admin?
self.company_members.where('? = ANY (company_members.roles)', Role::ADMIN).any?
end
end

When you get around to working with an instance of that model in your controller, it'll look something like this:

class UsersController < ApplicationController
def index
#Let's just grab a random user
@user = User.find(1)
end
end

Finally, in your view, your properties (attributes) can be accessed in the following way:

####views/users/index.html.haml####

.random-class
= "You're logged in as #{@user.first_name}!"

But we can also call User methods in the view as well. Continuing off our last example, we could add something like this:

####views/users/index.html.haml####

.random-class
= "You're logged in as #{@user.first_name}!"
- if @user.admin?
= 'Oh my gosh, our freaking User method worked!'

So in short, you're correct in that properties and methods are very different, although they look similar at times.

Lastly, it's worth pointing out that instance methods are just that: methods called on instances of a class, whereas class methods are called on an actual model object, and not a singular instance of that model object. Here's a great link to learn more about the differences.

Hope this helps!

attributes() method in Ruby

Hi attributes is a method provided by ActiveRecord. If you click on source you will notice how it really just exposes the instance variable @attributes which is a hash (as it can be nil it is enforced to a hash through .to_hash).

class ActiveRecord::Base
def attributes
@attributes.to_hash
end
end

We'll call this method parent as we will extend its behaviour in our class. This is possible through inheritance:

class Person < ActiveRecord::Base
def attributes
super.merge('foo' => self.foo)
end
end

attributes is now calling the parent's method [Activerecord::Base].attributes and is adding a new key to the hash.
This code is roughly equivalent and should be easier to read.

class Person < ActiveRecord::Base
def attributes
attrs = super # eg. { name: "x", created_at: [...], [...] }
attrs[:foo] = self.foo # eg. { name: "x", foo: [...], ... }
attrs
end
end

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.

Differences between *, self.* and @* when referencing associations/attributes in Ruby/Rails Models/Controllers

self.x/self.x=y are always method calls.

(self.x is just sugar for self.__send__(:x) and self.x = y is really just sugar for self.__send__(:x=, y))

@x, on the other hand, only refers to an instance variable.

Using @x will not work with AR associations as AR only defines x/x= (which are methods) for its magical operation. (AR essentially just "captures" intent access through these methods and routes through its own internal data structures which are unrelated to any similar-named instance variables.)

attr_accessor allows "accessing both ways" because and only because it uses the same-named instance variable as it's backing (it has to store the value somewhere). Consider that attr_accessor :x is equivalent to:

def x; @x; end
def x= (y); @x = y; end

Happy coding.

What's the difference between attribute.present? and attribute?

present? returns the opposite of blank?. They're Rails extensions of Ruby's core Object. .present? will return false if the value being testing is nil, "", {}, false, [], etc... based on the type (Array, Hash, String, etc...) extending Object.

Methods like name? which are based on the attributes of your model like :name are actually passed through the method_missing method in ActiveRecord::AttributeMethods which is then processed by ActiveModel::AttributeMethodsand eventually processed by this class.

So, yes they are mostly the same (for certain column types blank? is actually called; the same blank? in present? above), but they're not identical, as the ? suffix methods do some additional checking based on the column type.

understanding ruby class attributes, using accessor macros and self

attr_accessor :some_hash defines reader and writer methods for the given attribute. It is equivalent to this:

class User
def some_hash
@some_hash
end

def some_hash=(some_hash)
@some_hash = some_hash
end
end

@some_hash refers to an instance variable of the object, while some_hash and some_hash= are methods. The former returns the value of the variable, the latter sets it.

The idiom self.some_hash ||= {} is equivalent to self.some_hash || self.some_hash = {}.

Boolean operators in Ruby short circuit, which means the second expression (self.some_hash = {}) will not be executed at all if the first expression (self.some_hash) returns a true value.

The method:

def some_hash
self.some_hash ||= {}
end

Is actually recursive, since it expands to some_hash || self.some_hash = {}. It will keep calling itself until you get a stack overflow. Use the second form:

def some_hash
@some_hash ||= {}
end

Since it sets the instance variable directly, you will not have problems with recursion, nor will you have to call a writer method. some_hash can never return nil, because if @some_hash is nil, it will be assigned an empty hash before the method returns.

By the way, this is also called lazy initialization, because the variable is initialized when it is first accessed, not when the User instance is created.

Difference between @instance_variable and attr_accessor

An instance variable is not visible outside the object it is in; but when you create an attr_accessor, it creates an instance variable and also makes it visible (and editable) outside the object.

Example with instance variable (not attr_accessor)

class MyClass
def initialize
@greeting = "hello"
end
end

m = MyClass.new
m.greeting #results in the following error:
#NoMethodError: undefined method `greeting' for #<MyClass:0x007f9e5109c058 @greeting="hello">

Example using attr_accessor:

class MyClass
attr_accessor :greeting

def initialize
@greeting = "hello"
end
end

m2 = MyClass.new
m2.greeting = "bonjour" # <-- set the @greeting variable from outside the object
m2.greeting #=> "bonjour" <-- didn't blow up as attr_accessor makes the variable accessible from outside the object

Hope that makes it clear.



Related Topics



Leave a reply



Submit