Let's say I have a form_tag in a view gathering a view user input . With this input that I get through the params hash, I want to verify some of htis information such as email phone name.
Can I just verify in the controller or is this a bad thing? I don't plan to save the thing I get to a db or anything, it's just getting put into an email and sent off, but I want to verify these things before it's sent.
Thank you.
EDIT:
Found this http://www.viddler.com/explore/rails3/videos/6/
class Test
include ActiveRecord::Validations
validates_presence_of :name, :email
attr_accessor :name, :email
end
You can use the model for whatever you need that is related to the object and don't need to save it. Keeping stuff like this in the model is desirable in order to keep controller tidy. Say you have a user model:
#controller
#user.new params[:user]
#user.check_valid
#user.save # is optional
#user.rb
def check_valid
!email.blank? and !phone.blank?
end
Related
I wanted some advice about how to handle to_param in regards to permalinks
Basically this is what happens.
Create a new company
The company :name is then parameterized and saved as a :permalink in the db
Updating an existing company enables you to change the :permalink
There are validations to ensure user updated :permalink is unique
The problem I'm having is occurring when updating the company's :permalink to something that already exists. The uniqueness validation works which is great, but it changes the params[:id] to the invalid permalink instead of reseting and using the existing params[:id]
When I try to edit the permalink to something else I get a flash validation error of "Name already taken" because it thinks I'm editing the company of the already existing :permalink (company). The URL reflects the change in permalink since my companies_controller.rb is using #company = Company.find_by_permalink[:id])
I wanted to know the best way to handle this issue?
class Companies < ActiveRecord::Base
before_create :set_permalink
before_update :update_permalink
attr_accessible :name, :permalink
validates :name, :permalink, uniqueness: { message: 'already taken' }
def to_param
permalink
end
private
def set_permalink_url
self.permalink = name.parameterize
end
def update_permalink_url
self.permalink = permalink.parameterize
end
end
Apologies if I'm not making too much sense.
Thanks in advance.
you could try to handle this with an after_rollback callback.
after_rollback :restore_permalink
def restore_permalink
self.permalink = permalink_was if permalink_changed?
end
here's how it works : every update / destroy in Rails is wrapped in a transaction. If the save fails, the transaction rollbacks and triggers the callback.
The callback then restores the old value (permalink_was) if it was changed since the record has been loaded.
See ActiveModel::Dirty and ActiveRecord::Transactions for more info.
EDIT
On the other hand, there may be another solution (untested) - just define your accessor like this :
def permalink=( value )
permalink_will_change! unless #permalink == value
#permalink = value
end
This way, the permalink will not be marked as dirty if the new value is identical to the old one, and so AR will not try to update the column.
Explanation:
i don't know on which version of rails it was implemented (it is relatively recent), but here's how "dirtyness" works :
your "standard" (automagically generated) attribute setters basicly call
#{your_attribute}_will_change! before setting the associated
instance variable (even if you set the exact same value than before)
when you call save, ActiveRecords looks for attributes that have changed ("dirty") and builds the SQL UPDATE query using ONLY these attributes (for performance reasons, mostly)
so if you want to avoid your permalink to appear in the query when it is unchanged, i think you have to override the standard setter - or avoid mass-assignment and only set permalink if it has changed
For my app, I have different signup entry points that validate things differently.
So in the main signup, nothing is required except for the email and password field. In an alternative signup field, many more are required. So in the user model I have
validate_presence_of :blah, :lah, :foo, :bah, :if => :flag_detected
def flag_detected
!self.flag.nil?
end
I want to set that flag through the controller. However that flag isn't a database field. I'm just wondering if this is achievable in Rails or there is something wrong with the way that I am thinking about this? If so, what's the best way to achieve this? Thanks.
What you need is attr_accessor
class User < ActiveRecord::Base
attr_accessor :flag
attr_accessible :flag # if you have used attr_accessible or attr_protected else where and you are going to set this field during mass-assignment. If you are going to do user.flag = true in your controller's action, then no need this line
end
basically attr_accessor :flag create the user.flag and user.flag = ... methods for your model.
and attr_accessible is for mass-assignment protection.
Following up on the best practice debate:
Create a method that does what you want. I.e. save_with_additional_validation. This is much more clear and self-documenting code and works the same way. Just call this method instead of save()
It seems like you need to define setter method
class User < ActiveRecord::Base
attr_accessible :flag
def flag=(boolean)
boolean
end
end
In Rails, what is the difference between attr_accessor and attr_accessible? From my understanding, using attr_accessor is used to create getter and setter methods for that variable, so that we can access the variable like Object.variable or Object.variable = some_value.
I read that attr_accessible makes that specific variable accessible to the outside world.
Can someone please tell me whats the difference
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.
Many people on this thread and on google explain very well that attr_accessible specifies a whitelist of attributes that are allowed to be updated in bulk (all the attributes of an object model together at the same time)
This is mainly (and only) to protect your application from "Mass assignment" pirate exploit.
This is explained here on the official Rails doc : Mass Assignment
attr_accessor is a ruby code to (quickly) create setter and getter methods in a Class. That's all.
Now, what is missing as an explanation is that when you create somehow a link between a (Rails) model with a database table, you NEVER, NEVER, NEVER need attr_accessor in your model to create setters and getters in order to be able to modify your table's records.
This is because your model inherits all methods from the ActiveRecord::Base Class, which already defines basic CRUD accessors (Create, Read, Update, Delete) for you.
This is explained on the offical doc here Rails Model and here Overwriting default accessor (scroll down to the chapter "Overwrite default accessor")
Say for instance that: we have a database table called "users" that contains three columns "firstname", "lastname" and "role" :
SQL instructions :
CREATE TABLE users (
firstname string,
lastname string
role string
);
I assumed that you set the option config.active_record.whitelist_attributes = true in your config/environment/production.rb to protect your application from Mass assignment exploit. This is explained here : Mass Assignment
Your Rails model will perfectly work with the Model here below :
class User < ActiveRecord::Base
end
However you will need to update each attribute of user separately in your controller for your form's View to work :
def update
#user = User.find_by_id(params[:id])
#user.firstname = params[:user][:firstname]
#user.lastname = params[:user][:lastname]
if #user.save
# Use of I18 internationalization t method for the flash message
flash[:success] = t('activerecord.successful.messages.updated', :model => User.model_name.human)
end
respond_with(#user)
end
Now to ease your life, you don't want to make a complicated controller for your User model.
So you will use the attr_accessible special method in your Class model :
class User < ActiveRecord::Base
attr_accessible :firstname, :lastname
end
So you can use the "highway" (mass assignment) to update :
def update
#user = User.find_by_id(params[:id])
if #user.update_attributes(params[:user])
# Use of I18 internationlization t method for the flash message
flash[:success] = t('activerecord.successful.messages.updated', :model => User.model_name.human)
end
respond_with(#user)
end
You didn't add the "role" attributes to the attr_accessible list because you don't let your users set their role by themselves (like admin). You do this yourself on another special admin View.
Though your user view doesn't show a "role" field, a pirate could easily send a HTTP POST request that include "role" in the params hash. The missing "role" attribute on the attr_accessible is to protect your application from that.
You can still modify your user.role attribute on its own like below, but not with all attributes together.
#user.role = DEFAULT_ROLE
Why the hell would you use the attr_accessor?
Well, this would be in the case that your user-form shows a field that doesn't exist in your users table as a column.
For instance, say your user view shows a "please-tell-the-admin-that-I'm-in-here" field.
You don't want to store this info in your table. You just want that Rails send you an e-mail warning you that one "crazy" ;-) user has subscribed.
To be able to make use of this info you need to store it temporarily somewhere.
What more easy than recover it in a user.peekaboo attribute ?
So you add this field to your model :
class User < ActiveRecord::Base
attr_accessible :firstname, :lastname
attr_accessor :peekaboo
end
So you will be able to make an educated use of the user.peekaboo attribute somewhere in your controller to send an e-mail or do whatever you want.
ActiveRecord will not save the "peekaboo" attribute in your table when you do a user.save because she don't see any column matching this name in her model.
attr_accessor is a Ruby method that gives you setter and getter methods to an instance variable of the same name. So it is equivalent to
class MyModel
def my_variable
#my_variable
end
def my_variable=(value)
#my_variable = value
end
end
attr_accessible is a Rails method that determines what variables can be set in a mass assignment.
When you submit a form, and you have something like MyModel.new params[:my_model] then you want to have a little bit more control, so that people can't submit things that you don't want them to.
You might do attr_accessible :email so that when someone updates their account, they can change their email address. But you wouldn't do attr_accessible :email, :salary because then a person could set their salary through a form submission. In other words, they could hack their way to a raise.
That kind of information needs to be explicitly handled. Just removing it from the form isn't enough. Someone could go in with firebug and add the element into the form to submit a salary field. They could use the built in curl to submit a new salary to the controller update method, they could create a script that submits a post with that information.
So attr_accessor is about creating methods to store variables, and attr_accessible is about the security of mass assignments.
attr_accessor is ruby code and is used when you do not have a column in your database, but still want to show a field in your forms. The only way to allow this is to attr_accessor :fieldname and you can use this field in your View, or model, if you wanted, but mostly in your View.
Let's consider the following example
class Address
attr_reader :street
attr_writer :street
def initialize
#street = ""
end
end
Here we have used attr_reader (readable attribute) and attr_writer (writable attribute) for accessing purpose. But we can achieve the same functionality using attr_accessor. In short, attr_accessor provides access to both getter and setter methods.
So modified code is as below
class Address
attr_accessor :street
def initialize
#street = ""
end
end
attr_accessible allows you to list all the columns you want to allow Mass Assignment. The opposite of this is attr_protected which means this field I do NOT want anyone to be allowed to Mass Assign to. More than likely it is going to be a field in your database that you don't want anyone monkeying around with. Like a status field, or the like.
In two words:
attr_accessor is getter, setter method.
whereas attr_accessible is to say that particular attribute is accessible or not. that's it.
I wish to add we should use Strong parameter instead of attr_accessible to protect from mass asignment.
Cheers!
A quick & concise difference overview :
attr_accessor is an easy way to create read and write accessors in
your class. It is used when you do not have a column in your database,
but still want to show a field in your forms. This field is a
“virtual attribute” in a Rails model.
virtual attribute – an attribute not corresponding to a column in the database.
attr_accessible is used to identify attributes that are accessible
by your controller methods makes a property available for
mass-assignment.. It will only allow access to the attributes that you
specify, denying the rest.
I need a "I accept terms of service" checkbox on a page, it has to be checked in order for the order to proceed. It seems hence illogical to have a column in the database to match this (whether user has accepted or declined terms).
I am using the form helper like this in my view:
<%= check_box("client", "terms") %>
And in my model:
validates_acceptance_of :terms
At the moment it is not working at all.
This seems like a really common piece of code, yet I can't find it used anywhere without having the terms in the model. Else I could use javascript to validate it, but would prefer to keep it all the in model.
This should work fine, without a database column or attr_accessor:
http://guides.rubyonrails.org/active_record_validations.html#acceptance
I would be inclined to check your params hash is as it should be i.e. that the 'terms' attribute is being passed within the 'client' hash, perhaps try adding raise params.inspect on your controller create action to help you debug?
What about having an attr_accessor :terms in your Client model?
I had this working with these settings:
In the controller, I have added :terms_of_service as a permitted field:
def application_params
params.require(:application).permit(. . . , :terms_of_service)
end
In the model:
attr_accessor :terms_of_service
validates :terms_of_service, :acceptance => true
In the view:
<%= f.check_box("terms_of_service", :checked => false) %>
attr_accessor :terms will do the trick nicely.
Either go with #neutrino's solution, or to reset :terms to "not accepted" if you need to redisplay the form (because validation may fail), use this:
def terms
nil
end
def terms=(val)
# do nothing
end
I have models like this:
class Person
has_many :phones
...
end
class Phone
belongs_to :person
end
I want to forbid changing phones associated to person when some condition is met. Forbidden field is set to disabled in html form. When I added a custom validation to check it, it caused save error even when phone doesn't change. I think it is because a hash with attributes is passed to
#person.update_attributes(params[:person])
and there is some data with phone number (because form include fields for phone). How to update only attributes that changed? Or how to create validation that ignore saves when a field isn't changing? Or maybe I'm doing something wrong?
You might be able to use the
changed # => []
changed? # => true|false
changes # => {}
methods that are provided.
The changed method will return an array of changed attributes which you might be able to do an include?(...) against to build the functionality you are looking for.
Maybe something like
validate :check_for_changes
def check_for_changes
errors.add_to_base("Field is not changed") unless changed.include?("field")
end
def validate
errors.add :phone_number, "can't be updated" if phone_number_changed?
end
-- don't know if this works with associations though
Other way would be to override update_attributes, find values that haven't changed and remove them from params hash and finally call original update_attributes.
Why don't you use before_create, before_save callbacks in model to restrict create/update/save/delete or virtually any such operation. I think hooking up observers to decide whether you want to restrict the create or allow; would be a good approach. Following is a short example.
class Person < ActiveRecord::Base
#These callbacks are run every time a save/create is done.
before_save :ensure_my_condition_is_met
before_create :some_other_condition_check
protected
def some_other_condition_check
#checks here
end
def ensure_my_condition_is_met
# checks here
end
end
More information for callbacks can be obtained here:
http://guides.rubyonrails.org/activerecord_validations_callbacks.html#callbacks-overview
Hope it helps.