Confused with Ruby Accessor Methods

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



Leave a reply



Submit