validator :on is not working (ruby-on-rails) - ruby-on-rails-3

In my User model i have the following validations:
validates :password, :presence => true, :length => { minimum: 8 }, :on => :update_settings
if i don't use the :on validator it works work across the board. However with the :on I don't get any validation when executing the specified actions.
The form is going to: action="/users/1/update_settings"
The route is set up: user_update_settings PUT /users/:user_id/update_settings(.:format) users#update_settings
I have checked the guidehere. The same validation works on standard actions like :create but not on my own actions.
Can you see what else am I missing to make this validation valid? Thanks!

I think you're mixed up about what the :on option is for. It is not for specifying actions on the controller, it is for specifying actions on the record. It would go totally against the MVC principle to tie controller actions to model validations in the way you're trying to do here.
Check the docs:
The :on option lets you specify when the validation should happen. The default behavior for all the built-in validation helpers is to be run on save (both when you’re creating a new record and when you’re updating it). If you want to change it, you can use :on => :create to run the validation only when a new record is created or :on => :update to run the validation only when a record is updated.
So the on option takes one of only three possible values: :save (the default), :update or :create.
Given the name of your action (update_settings), I'm going to assume that in that action you are calling update_attributes (or update_attribute) on the model, in which case you should just use :on => :update in your validator. Note that this means that it will also apply the validation in any other controller actions that update the record, since the condition is not specific to any particular controller action.

Related

validates_inclusion_of Rails 3 with a query not working as expected

I am newish to Rails and i am facing a prblem. I have
Models
Model1
id: integer
account_id: integer
Account
id: integer
I have a validation in the Model1 as follows,
Model Code:
validates :account_id, :inclusion => { :in => Account.find(:all).collect(&:id)}
I use a dropdown for
View Code:
<%= f.select :account_id, #accounts.collect {|acc| [acc.name, acc.id]}, {:prompt => 'Select Account' }, { :selected => #defaultaccount, :class => 'selectwidth' } %>
I am using client side validations to validate the fields before submitting the form. I am using Heroku to deploy my app.
I create an account first and then from the form i try to create a new model1. The drop down is populated with all the accounts that i have. Then i select an account that i created from the dropdown, the client side validations comes into effect and says "is not valid" which is the error message for validates_inclusion_of as if the account never exists. Not sure what ishappening and i checked the database and the account is created which is why the dropdown is loaded with accounts in the first place.
I change the validates_inclusion_of to rails 3 validates :account_id, :inclusion => { :in => {} } syntax and redeploy. Now i try to select that account in the new model1 form, The drop down does not show an error for the account that was previously created.
So then i created a new account and tried to create a new model1 for the new account the validation failure resurfaces.
So its like a cache issue ? Is it a client side validations issue ? I did everything i can to try and find what the issues is but i am out of ideas on what is happening.
So everytime i create an account it expects me redeploy which kills what the application does. How do i get rid of this problem ? Not really sure what is happening.
Config,
Rails 3
Deployed in Heroku
uses Asset pipeline
Gems:
Client Side Validations
Kaminari
It's not working as you want because the :in => Account.find(:all).collect(&:id) it's evaluated just one time in production mode, when Rails loads the class. I confess I don't understand the reason behind such a validation. Anyway, you have to use a custom validator if you really want to achieve that.
Currently, the statement gets executed once the server is started (as you correctly realized). You'll need to wrap it in a lambda, so it gets executed everytime the validation is done.
1.8.7 Syntax:
validates :account_id, :inclusion => { :in => lambda {Account.find(:all).collect(&:id)}}
1.9.2 Syntax:
validates :account_id, :inclusion => { :in => ->{Account.find(:all).collect(&:id)]}

Questions about rails3 routes

I'm upgrading my app to rails 3, and I am a bit confused about some of the routes. The resourceful ones are easy enough, but how can I set a generic rule for all actions in a specific controller. I tried something like this:
get 'custom/:action/' => {:controller => :custom}
But that didn't work. It seems the new format is "controller#action", but how can I specify the action to be variable?
Also, other than using named routes or resources, is it possible to do shorthand notation to name routes in a specific controller?
i.e. rather than:
get '/tasks', :controller => :home, :action => :tasks, :as => 'tasks_home'
get '/accounts', :controller => :home, :action => :accounts, :as => 'accounts_home'
is it possible to do something a little cleaner, like:
controller => :home do
get :tasks
get :accounts
end
And that would automatically created the named routes?
You can use action as a variable like this:
resource :custom do
match ':action'
end
This will generate
/custom/:action(.:format) customs#:action
custom POST /custom(.:format) customs#create
new_custom GET /custom/new(.:format) customs#new
edit_custom GET /custom/edit(.:format) customs#edit
GET /custom(.:format) customs#show
PUT /custom(.:format) customs#update
DELETE /custom(.:format) customs#destroy
So it will handle your action as a variable URL-s and will add some default CRUD actions as well.
Note that the controller name here is in plural. If you would like to use a route for a controller which name is in singular, use resources instead of resource.
The answer to the second question is almost identical to the first one, use resource:
resource :home do
get :tasks
get :accounts
end
generates:
tasks_home GET /home/tasks(.:format) homes#tasks
accounts_home GET /home/accounts(.:format) homes#accounts
home POST /home(.:format) homes#create
new_home GET /home/new(.:format) homes#new
edit_home GET /home/edit(.:format) homes#edit
GET /home(.:format) homes#show
PUT /home(.:format) homes#update
DELETE /home(.:format) homes#destroy
Note that the matched controller names are in plural again, because of the convention.
Looks like this is related to the persisted field being set to false on nested ActiveResource objects: https://github.com/rails/rails/pull/3107

cancan: the difference between "manage" and the combination of "read, create, update and destroy"?

In trying to debug use of cancan i found that if use the following i can get past the accessdenied message:
can :manage, Model
When i changed it to the following I am denied access:
can :read, Model
can :create, Model
can :update, Model
can :destroy, Model
What does manage include that the combination of read, create, update and destroy do not?
Thanks.
By default CanCan maps :read, :create etc. to the relevant controller actions e.g.:
def default_alias_actions
{
:read => [:index, :show],
:create => [:new],
:update => [:edit],
}
end
But, of course you're not restricted to having just those actions in your controller, ultimately a controller action can have any name. By the same token you're not restricted to having just :read, :create, :update, :detroy in CanCan. You can alias any symbol to any controller action. Let us say you have an action on your controller called do_cool_things, you can then alias any symbol to that action to be used by CanCan e.g.:
alias_action :do_cool_things, :to => :coolify
You would then be able to do this:
can :coolify, Neighborhood
Which means the current user would have access to the :do_cool_things method of the NeighborhoodsController. However if you had used :manage you wouldn't need to define this separate action since :manage is a catch-all. So if you had done:
can :manage, Neighborhood
The current user would still have had access to the :do_cool_things method of the controller.
So, :manage lets you do anything, but :read, :create, :update and :destroy are only 4 of an infinite number of CanCan actions that you can define and map to any controller action you choose.
You can define custom actions (When you define a user's abilities for a given model, you are not restricted to the 7 RESTful actions (create, update, destroy, etc.), you can create your own.) If you have manage all, you wold be able to access those custom actions too.

Rails 3: client_side_validations gem and devise password validations

I've got the client_side_validations gem working really great on my forms. I'm using Devise for my users model and when I go to the edit_user_registration form, validations are thrown up for everything except the :current_password, :password, and :password_confirmation fields.
Fort instance is I leave the email blank, the validation pops up right when I tab out of the field. However, if I leave the current_password field blank and tab out of it nothing happen, then when I submit the form I get the error "1 error prohibited this user from being saved: password can't be blank"
Thanks for any help
http://pastie.org/1907464
Currently ClientSideValidations will filter out any conditional validators. Devise sets some of the validators as conditional: https://github.com/plataformatec/devise/blob/master/lib/devise/models/validatable.rb#L24-32
The reason I did this is because there is no good way for the client to determine the true value of that conditional. I could do it at the time the form is generated but what if that conditional relied upon a value that could be changed on the form? So I opted to filter them and let things fall back to the server.
That was the idea but clearly it has imposed unfair limitations on some things. This being the most obvious (and popular).
So I plan on releasing a new version very soon that will allow you to explicitly override the conditional filters. It will work like this:
<%= f.text_field :password, :validate => { :presence => true, :confirmation => true } %>
or
<%= f.text_field :password, :validate => true %>
In the first case you can choose which validators to turn the filter off. In the 2nd case it will turn the filter off for all validators on that attribute. The conditional will be evaluated at the time the form is generated and if it passes it will add the validator to the input element for use on the client.
The master branch now supports this format. Point your Gemfile to it and you should be good
It’s simple! The gem extends the Rails default form builder, and all you have to do is set a :validate => true option on any form_for (or simple_form_for for simple form users) tag that you want the inline validations for. The form builder uses some rails reflections on your model validations to generate some json that gets included in a script tag after your form. The json is then used by the gem’s Javascript framework to perform the validations that need to be performed.
<%= form_for(#user, :url => registration_path(resource_name), :validate => true) do |f| %>

:any option for rails 3 routes

In rails 2 you can use the :any option to define a custom route that responds to any request method e.g.
map.resources :items, :member => {:erase => :any}
rails 3 doesn't seem to support the :any option
resources :items do
get :erase, :on => :member # works
any :erase, :on => :member # doesn't work
end
does anyone know if this option has been removed or just renamed?
From digging around and seeing what the get, post, put, and delete actions actually do in ActionDispatch, I think all you need to do is match. So:
resources :items do
get :erase, :on => :member
match :erase, :on => :member
end
I don't think that syntax for match is actually documented, but the routes it constructs are, atleast for me, what you'd expect from an all method
Good question.
Looking at the Edge Rails routing guide and the Rails 3 source it doesn't look like it's supported. You could raise a ticket in the Rails Lighthouse (I couldn't find an existing one for this).
Match will work, but not inside a resources definition unfortunately. I rather wish they'd bring back a way to define get/post at least together..