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 User
model 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
How to Match the Last Occurrence of a Pattern
Difference Between Rake Db:Migrate Db:Reset and Db:Schema:Load
What Is the Easiest Way to Duplicate an Activerecord Record
In Ruby on Rails, to Extend the String Class, Where Should the Code Be Put In
Why Does String Interpolation Work in Ruby When There Are No Curly Braces
Aws S3: the Bucket You Are Attempting to Access Must Be Addressed Using the Specified Endpoint
How to Convert a String or Integer to Binary in Ruby
How to Get the Current Absolute Url in Ruby on Rails
Why Is Division in Ruby Returning an Integer Instead of Decimal Value
Ruby: How to Post a File Via Http as Multipart/Form-Data
Ruby Operator Precedence Table
Ruby Style: How to Check Whether a Nested Hash Element Exists
How to Avoid Tripping Over Utf-8 Bom When Reading Files