Difference Between Attr_Accessor and Attr_Accessible

Difference between attr_accessor and attr_accessible

attr_accessor is a Ruby method that makes a getter and a setter. attr_accessible is a Rails method that allows you to pass in values to a mass assignment: new(attrs) or update_attributes(attrs).

Here's a mass assignment:

Order.new({ :type => 'Corn', :quantity => 6 })

You can imagine that the order might also have a discount code, say :price_off. If you don't tag :price_off as attr_accessible you stop malicious code from being able to do like so:

Order.new({ :type => 'Corn', :quantity => 6, :price_off => 30 })

Even if your form doesn't have a field for :price_off, if it's in your model it's available by default. This means a crafted POST could still set it. Using attr_accessible white lists those things that can be mass assigned.

Difference between attr_accessible and strong parameters

attr_accessible has been deprecated in Rails 4 in favor of Strong Parameters.

Both are different approaches to the mass assignment problem but Strong Parameters is more flexible.

In example, you have an Usermodel with the attributes email:string and is_admin:boolean. You wish to allow the users to modify their email through a form but not the is_admin field.

In Rails 3 you should do:

attr_accesible :email

With this approach it's not possible for an user to modify is_admin because that attribute is protected.

One of the good things of Strong Parameters is that you could do the following in your controller:

def user_params
if current_user.admin?
params.require(:user).permit(:email, :is_admin)
else
params.require(:user).permit(:email)
end
end

This way one admin user will be able to modify is_admin while a normal user won't.

This is just one example and not the best way to grant administrative permissions to an user but it's quite illustrative.

The main advantage of Strong Parameters is that they are defined in the controller and can be dynamically assigned in run time. attr_accessible was a more static and monolithic way to whitelist attributes.

On the other hand attr_accessor is completely different thing and still can be used in Rails 4, in example, if you need one attribute in your model that it's not necessary to persist or to be written into the database but you require it in a form. Think about:

attr_accessor :has_accepted_legal_terms

It's a Ruby method that can be used to declare an attribute of your model that is not related to the database, an attribute of a Class or PORO (plain old ruby object).

Rails: difference between attr_accessor and attr_accessible

attr_accessor creates the getter method.attribute and setter method.attribute= for the specified attributes.

attr_accessible is from ActiveRecord::Base and "Specifies a white list of model attributes that can be set via mass-assignment." See documentation and example here.

EDIT:

As for your second question, I don't know. I tried this dummy code and it worked:

class Test
attr_accessor :base_path, :fa_file
def cvitSetup()
self.base_path = "blast_cvit/"
self.fa_file = "input.fa"
end
end
t = Test.new
t.cvitSetup
p t.base_path
#=> "blast_cvit/"

Are you sure that you properly instantiated your class?

Using attr_accessor and attr_accessible on the same field

Thanks everyone for quick answers!
Your answers combined gave me the pieces I needed to understand this puzzle, I think.

(In a related problem, I was getting a lot of nil errors like "Object doesn’t support #inspect", and "undefined method ‘keys’ for nil:NilClass". I managed to solve it now, by removing the att_accessor field altogether.)

By experimenting with this particular case, this is what I've found out:

Actually, the :name field won't be persisted to the database.

user = User.new(:name=>"somename")

Will only set the attribute on the object, but not persist the :name column to the database. Like the following 'rails console' output shows:

> user
=> <User id: nil, created_at: nil, updated_at: nil>
> user.save
=> true
> user
=> <User id:1, created_at: 2011-01-19 12:37:21, updated_at: 2011-01-19 12:37:21>

I assume this is because *the setter made by attr_accessor will override ActiveRecord's setter* (which takes care of the database persistence). You can still retrieve the value from the :name field from the object though, like this:

> user.name
=> "somename"

So, in conclusion, I've learnt that using attr_accessor on fields might lead to them not being persisted to the database. And while I thought attr_accessible describes fields in the database that should be accessible from the outside, it doesn't seem to make a difference in this case.

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

In model, when to use attr_accessor and when to use attr_accessible?

attr_accessible specifies a white list of model attributes that can be set via mass-assignment. (source)

attr_accessor defines a named attribute for this module, where the name is symbol.id2name, creating an instance variable (@name) and a corresponding access method to read it. Also creates a method called name= to set the attribute. (source)

So, basically, if you need a non-database-backed attribute, use attr_accessible. If you need to mass assign an attribute, whether or not it is backed by the db, use attr_accessible. If you need to mass assign a non-database-backed attibute, you would use both.

This all makes perfect sense in the context of your updated question. In a migration, the password_hash field is added as a db-backed attribute to the model. Then in the code, password (and its confirmation) are added as non-database backed attributes. The line before_save :encrypt_password calls the encrypt_password method before the model is saved. In that method, the database-backed attribute is derived from the non-database backed attributes. The reason you don't need attr_accessor :password_hash is because it is never mass-assigned (like password), but rather explicitly set. Make sense?

What is the difference between attr_accessible(*attributes) & attr_protected(*attributes)?

attr_accessible (documentation) says "the specified attributes are accessible and all others are protected" (think of it as whitelisting.)

whereas

attr_protected (documentation) says "the specified attributes are protected and all others are accessible" (think of it as blacklisting.)

A protected attribute is one that can only be modified explicitly (e.g. via attribute=) and can't be updated via mass assignment (e.g. using model.update_attributes or by passing attributes to new). The behaviour upon an attempt to update a protected attribute via mass assignment depends on the mass_assignment_sanitizer setting (see the update below).

The classic example would be if a User model had an is_admin attribute you could protect that attribute to prevent form submissions that would allow any user to be set as an administrator.

example:

class User < ActiveRecord::Base
# explicitly protect is_admin, any new attributes added to the model
# in future will be unprotected so we need to remember to come back
# and add any other sensitive attributes here in the future
attr_protected :is_admin
end

compared with:

class User < ActiveRecord::Base
# explicitly unprotect name and bio, any new attributes added to the model
# in the future will need to be listed here if we want them to be accessible
attr_accessible :name, :bio
end

Now, assuming is_admin attribute is protected:

> u = User.find_by_name('mikej')
> u.is_admin?
false
> u.update_attributes(:name => 'new name', :is_admin => true)
> u.is_admin?
false
> u.name
"new name"
> u.is_admin = true # setting it explicitly
> u.save
> u.is_admin?
true

Update: Later versions of Rails introduced the concept of a mass assignment sanitizer to control the behaviour upon attempts to update protected attributes via mass assignment. In Rails 3.2 and later this can be controlled by setting mass_assignment_sanitizer in config. The default is to just log the attempts and allow code execution to continue, but the standard environment config for development sets this to :strict which raises as exception on an attempt to update a protected attribute.

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