confused with Ruby accessor methods
Setters are special in Ruby.
In fact, defining a method name ending in an equals sign makes that name eligible to appear on the left-hand side of an assignment.
from http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_classes.html
Assignments are defined in Ruby as:
An assignment statement sets the variable or attribute on its left side (the lvalue) to refer to the value on the right (the rvalue).
from http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_expressions.html
So n.name=
is calling the setter name=
directly.
n.name =
is using this special treatment of setters by the fact that it ends in an =
, to make it so that you can use it as the lvalue (that is, it can appear on the left side) in an assignment.
attr_accessor or custom methods duplicate method names confusion
t.number = 13
is just a shorthand for t.number=(13)
, they are effectively the same statement in Ruby.
attr_accessor :b
creates the equivalent of the following two methods:
def b
@b
end
def b=(new_val)
@b = new_val
end
So in your code example, you could replace the two methods #number
and #number=
with attr_accessor :number
Defining non-standard accessor methods using 'attr_accessor'
Use attr_accessor
in conjunction with alias_method
. For example:
class Foo
attr_accessor :bar
alias_method :baz, :bar
alias_method :baz=, :bar=
def initialize
end
end
Then verify it works as expected:
foo = Foo.new
=> #<Foo:0x00007fabb12229a0>
foo.bar = 'foobar'
=> 'foobar'
foo.baz
=> 'foobar'
foo.baz = 'barfoo'
=> 'barfoo'
foo.bar
=> 'barfoo'
Confused by Ruby's class constructors and virtual accessors
1 attr_accessor :isbn, :price # is this part of the class constructor?
This line is to keep concept of access private member through method only philosophy. It's in effect equivalent to declare two private members and their getter and setter methods, has nothing to do with constructor.
2 def initialize(isbn, price) # and is the initialize method part of a constructor or just a regular method?
To put it in the easy way, this is THE constructor. The method get called upon 'new' keyword
3 def price_in_cents=(cents) # this is a 'virtual accessor' method, trough which I am able to update the price down the road...?
It's just an alternative of price= method, which takes argument in different format, the price= method is the setter method automatically generated by your question line 1.
4 book.price_in_cents = 1234 # here I am updating the value thanks to the 'virtual accessor' declared earlier, as I understand it
Keep in mind this feels like assigning a variable but really is accessing a setter method, in consistent with the concept reflected in your question line 1 and 3.
What is attr_accessor in Ruby?
Let's say you have a class Person
.
class Person
end
person = Person.new
person.name # => no method error
Obviously we never defined method name
. Let's do that.
class Person
def name
@name # simply returning an instance variable @name
end
end
person = Person.new
person.name # => nil
person.name = "Dennis" # => no method error
Aha, we can read the name, but that doesn't mean we can assign the name. Those are two different methods. The former is called reader and latter is called writer. We didn't create the writer yet so let's do that.
class Person
def name
@name
end
def name=(str)
@name = str
end
end
person = Person.new
person.name = 'Dennis'
person.name # => "Dennis"
Awesome. Now we can write and read instance variable @name
using reader and writer methods. Except, this is done so frequently, why waste time writing these methods every time? We can do it easier.
class Person
attr_reader :name
attr_writer :name
end
Even this can get repetitive. When you want both reader and writer just use accessor!
class Person
attr_accessor :name
end
person = Person.new
person.name = "Dennis"
person.name # => "Dennis"
Works the same way! And guess what: the instance variable @name
in our person object will be set just like when we did it manually, so you can use it in other methods.
class Person
attr_accessor :name
def greeting
"Hello #{@name}"
end
end
person = Person.new
person.name = "Dennis"
person.greeting # => "Hello Dennis"
That's it. In order to understand how attr_reader
, attr_writer
, and attr_accessor
methods actually generate methods for you, read other answers, books, ruby docs.
Confused about attr_accessor and attr_accessible in rails
attr_accessor and attr_accessible, despite almost identical spelling, are absolutely different methods.
attr_accessor, a native Ruby method which defines a getter and a setter method for the instance of the class:
class User
attr_accessor :password
end
u = User.new
u.password = "secret"
u.password # => "secret"
attr_accessible is a method brought by Rails and it is meant to "whitelist" already existing attributes of a model. Attributes enumerated in attr_accessible can be later changed via mass-assignment of model attributes (while other attributes will be blacklisted and not changeable):
class Account < ActiveRecord::Base
# First, you define 2 attributes: "password" and "created_at"
attr_accessor :password
attr_accessor :created_at
# Now you say that you want "password" attribute
# to be changeable via mass-assignment, while making
# "created_at" to be non-changeable via mass-assignment
attr_accessible :password
end
a = Account.new
# Perform mass-assignment (which is usually done when you update
# your model using the attributes submitted via a web form)
a.update_attributes(:password => "secret", :created_at => Time.now)
a.password # => "secret"
# "password" is changed
a.created_at # => nil
# "created_at" remains not changed
You use attr_accessible to prevent meddling with some attributes of your models by "outsiders" (e.g. you wouldn't want your "Account.superadmin" attribute to be changeable via a simple form submission, which would be a bad security issue).
Note, that you can change the attributes individually, regardless of their "whitelisting/blacklisting" status:
a.created_at = Time.now
a.created_at # => 2012-09-16 10:03:14
Setting with accessors in initialize results in nil properties
attr_accessor :thing1
defines an attribute that accesses the instance variable @thing1
. Instance variables are always prefixed with an @
.
Assigning thing1 = 15
creates a local variable within the initialize
method. It does not call the attribute writer, so the value of the instance variable @thing1
is not set. Because @thing1
has not been assigned, the attribute reader tc.thing1
will return nil
.
Assignment expressions such as thing1 = 15
that do not have an explicit receiver (tc.thing1 = 15
) and that aren't to instance or class variables (@thing1 = 15
, or @@thing1 = 15
) in Ruby will always define and/or assign a local variable. To call the attribute writer (and have it set the instance variable), you need to explicitly specify the receiver, e.g. self.thing1 = 15
.
The reason for is because in Ruby you don't declare variables before assigning them. An expression like thing1 = 15
would be ambiguous, so Ruby chooses to make it always refer to the local variable.
There is no problem calling methods that are not attribute writers (methods with a name not ending in =
) without an explicit receiver because there is no ambiguity. The expression some_method
in initialize
will therefore call the method some_method
The attribute defined by attr_accessor
is available for use immediately. The same behaviour would be encountered in any method, not just initialize
.
Related Topics
Rails 404 Error for Stylesheet or JavaScript Files
Ruby Multiple Background Threads
Twitter::Error::Unauthorized in Postscontroller#Create - Invalid or Expired Token
Uploading a File to a S3 Presigned Url
Return True Only If All Values Evaluate to True in Ruby
Gem Install JSON Fails with Redifinition of Struct Timezone/Timespec
Rails 3: Belongs_To, Has_One and Migrations
Ruby Sequel: Array Returned by Query Is Being Returned as a String Object, Not an Array Object
Convert String to Datetime Ruby on Rails
Exclude Some Ids from Result in Rails Activerecord
Elegant Way to Only Show Records If They Exist in Rails Erb
Rational - Original Numbers in Ruby
Sublime Text Can't Understand Gets.Chomp
Rails: Merit Gem Badge Not Registering or Displaying
Rubocop, How to Disable/Enable Cops on Blocks of Code
Ruby Block Taking Array or Multiple Parameters
Ssl_Connect Syscall Returned=5 Errno=0 State=Sslv3 Read Server Hello a (Openssl::Ssl::Sslerror)