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::AttributeMethods
and 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
Error Installing Any Ruby Version with Rvm on Osx
Rails 3: Generate Unique Codes (Coupons)
Is There a Good Admin Generator for Ruby on Rails
Better Way to Turn a Ruby Class into a Module Than Using Refinements
Ruby: Get All Keys in a Hash (Including Sub Keys)
Rails: Your User Account Isn't Allowed to Install to the System Rubygems
What Are the Things You Would Like Improved in the Ruby Language
Installing Ruby 2.0.0 Using Rvm
Ruby Koans: Why Convert List of Symbols to Strings
How to Check If a Number Is Included in a Range (In One Statement)
What Alternatives to Irb Are There
Sorting a Two-Dimensional Array by Second Value
All Possible Permutations of a Given String
Switching Between Web and Touch Interfaces on Facebook Login Using Omniauth and Rails 3
Restarting Unicorn with Usr2 Doesn't Seem to Reload Production.Rb Settings