Rails 3: client_side_validations gem and devise password validations - ruby-on-rails-3

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| %>

Related

Rails syntax Passing POST parameters from a form to a controller

I'm new to Rails (and fairly new to programming in general) and I am building a web app for myself as a way to learn. Right now I am modifying scaffolded forms and such.
My question is with the "create" method in one of my controllers. There are two entities I am concerned with: the User table and the Habit table. I created a dropdown box in the _form partial for the Habit views to allow a person to select a user from a list of all available when creating a habit as below
<%= collection_select :user, :id, #users, :id, :first_name %>
The habit controller, of course, has
def new
#users = User.all
...
end
This works fine, and when the form submits it posts two hashes of parameters :habit and :user. Now, when I want to process the form input in the create method, I'm not sure how to use the syntax correctly and assign the user_id to the newly create habit. What I WANT to do is something like this
def create
#habit = Habit.new(params[:habit], params[:user])
end
This, of course, is improper syntax.
def create
#habit = Habit.new(params[:habit])
end
assigns the params from the :habit hash correctly, but then the user_id is left unset.
What works is the following, but the code is very lengthy, assigning each value manually.
def create
#habit = Habit.new(:user_id => params[:user][:id],
:description => params[:habit][:description],
:habit_method => params[:habit][:habit_method],
:time_reqd => params[:habit][:time_reqd],
:will_reqd => params[:habit][:will_reqd],
:active => params[:habit][:active])
end
So my question is, when dealing with a form that posts data in multiple hashes, what is the proper way to pass those parameters into some method in a controller?
So my question is, when dealing with a form that posts data in multiple hashes, what is the proper way to pass those parameters into some method in a controller?
Instead of saying Habit.new( <lots of stuff> ), just use Habit.new(params[:habit]). Rails will try to assign each key in the hash (in this case, the params[:habit] hash's keys) to a matching value on the object.
Thus, if params[:habit] has a :description key, it will be assigned to a field called description on your model. This is called mass assignment and is quite handy.
Now you can just do:
#habit = Habit.new(params[:habit])
#habit.user_id = params[:user][:id]
You may want to read the RoR Getting Started Guide, like this section, for more similarly handy features of Rails.
Change
<%= collection_select  :user, :id, #users, :id, :first_name %>
To
<%= collection_select  :habit, :user_id, #users, :id, :first_name %>
The existing scaffold code should just work after that
Alternate
<%= f.select :user_id, #users, :id, :first_name %>

How to implement multiple selection on Rails 3

I have an index where I'm showing a list of documents. I would like to implement a multiple select in order to do different actions to the documents the user has selected
I have created a
<%= check_box_tag 'id', 'document.id %>
for each document, inside a form_tag
But if I select multiple checkboxes, the params that are passed to the action are overwrited and I'm just receiving the id of the last checkbox I've selected in the id param.
¿Anyone knows how to implement multiple select?¿Any other approach?
I'm running Rails 3 and Ruby 1.8.7
Thanks in advance
You need to set :multiple => true
<%= check_box_tag 'id', document.id, :multitple => true %>
This will give you results in form of an array in params[:id]
Minor correction (plural):
<%= check_box_tag 'ids[]', document.id %>
ensure your model is properly set for attr_accessible something like :document_ids

How do I make a settings configuration page for the rails-settings gem?

I just discovered the rails-settings gem and now I need to make an admin page that lets me edit the setting values. How would I make a settings controller with an edit view that can change these dynamic app wide settings?
I haven't used this gem but it seems like it should be fairly straight forward. Since it uses a database backed model, you would simply create a controller as normal:
rails g controller Settings
From here you would define your index action to gather all your individual settings for display in the view:
def index
#settings = Settings.all
end
Then in the view you can setup a loop to display them:
<% #settings.each do |setting| %>
<%= setting.var %> = <%= setting.value %>
<% end %>
As far as editing ... this might be a bit tricky since by default rails would expect you to submit only one setting at a time to edit. You could do it this way but unless you implement the edit with ajax it might be tedious and non-intuitive.
Another way would be to set up your update method to accept all the individual settings at once, loop through and update each one with new values. It might look something like this:
// The /settings route would need to be setup manually since it is without an id (the default)
<%= form_tag("/settings", :method => "put") do %>
<% #settings.each do |setting| %>
<%= label_tag(setting.var, setting.var) %>
<%= text_field_tag(setting.var, :value => setting.value) %>
<% end %>
<%= submit_tag("Save Changes") %>
<% end %>
This should output all of the settings (given they have been assigned to the #settings variable) with the var name as the label and the current value as the text field value. Assuming that the routing is setup, when you submit this form the action that receives it should all the new settings in the params variable. Then you can do something like this in the action:
def update
params.each_pair do |setting, value|
eval("Settings.#{setting} = #{value}")
end
redirect_to settings_path, :notice => 'Settings updated' # Redirect to the settings index
end
This may not be the best way depending on how often you edit the settings and how many settings you have...but this is a possible solution.
I was looking for some suggestions for this and found another answer to this that is very simple and elegant, for anyone looking for this later. It just sets up dynamic accessors in your model, allowing your form to have settings fields just like your normal attributes. An example can be found in the original answer:
How to create a form for the rails-settings plugin

Accept terms of use rails

What is the best way to add a check for accepting terms of use in a rails app?
I can't seem to get validates_acceptance_of working quite right. I added a bool to my user model (was that necessary?). And then have a checkbox that returns either true/false.
I feel like I'm just making a silly little mistake. Any ideas?
In your model,
validates_acceptance_of :terms
If you're using attr_accessible in your model then make sure you also add,
attr_accessible :terms
In your view,
<%= form_for #user do |f| %>
...
<%= f.check_box :terms %>
...
<% end %>
There is no need for an extra column in the users table unless you plan on denying access to users who have not accepted the terms of service, which won't exist since they can't complete registration in the first place.
This is a working Rails 4 solution:
Terms of service doesn't need to be a column in the database
Form
= f.check_box :terms_of_service
models/user.rb
validates :terms_of_service, acceptance: true
And most important, devise will sanitize your parameters and terms of service will be removed from the submitted params. So:
registrations_controller.rb
class RegistrationsController < Devise::RegistrationsController
before_filter :configure_permitted_parameters
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) do |u|
u.permit(:full_name,
:email, :password, :password_confirmation, :terms_of_service)
end
end
end
This is a working solution for Rails-6.1 (I18n) + Devise-4.8.0 + SimpleForm. No need to add a column in the "users" table in DB.
View
<% label_str = t('read_html', mylink: link_to(t('terms'), '/a/b/c')) %>
<%= f.input :terms_of_service, label: label_str, as: :boolean, checked: false %>
Or, if you use Indo-European languages only, you can make it a little more simple, like:
label_str = (t('agree_html')+link_to(t('terms'), '/a/b/c')+'.').html_safe
/app/models/user.rb
attr_accessor :terms_of_service
validates_acceptance_of :terms_of_service, on: :create
validates_presence_of :terms_of_service, on: :create
/app/controllers/application_controller.rb
Devise::ParameterSanitizer::DEFAULT_PERMITTED_ATTRIBUTES[:sign_up] << :terms_of_service
# see /vendor/bundle/ruby/*/gems/devise-*/lib/devise/parameter_sanitizer.rb
Explanation
In the User model, on: create guarantees it is read only in creation. If you need to reevaluate the condition in updating, too, specify it accordingly, like on: %i(create update).
In the User model, I add validates_presence_of to play safe. The reason is, validates_acceptance_of will not be executed when the parameter terms_of_service is nil, in which case validates_presence_of will catch it and set an error. Admittedly, if the data are always submitted via the web-interface you have built AND your implementation is working perfectly, the value should be always either true or false and never be nil. So, validates_presence_of should not be necessary in this sense. It does no harm, though (except you'd need to be a little careful in manual user creation, bypassing the web-interface, such as from the Console).
The last one is neccesary for use with Devise for the same reason as in the answer by #vladCovaliov; that is, to prevent Devise from sanitizing your custom parameter, which is not a column in the database table. The one-liner in the example above can be stated in any files as long as you are sure it is read at the run-time and after Devise Ruby code. application_controller.rb is one of the sure places (though I guess there is a better-fitting place). Make sure the sentence is put out of the class ApplicationController block.

Rails: Integration test data

I'm new to Rails testing and I'm confuse with the nature of rails integration test data:
test "should register new user and login" do
..
# create user
post users_path, :user => {:username => "newuser", :email => "newuser#gmail.com",
:password => "secret",
:password_confirmation => "secret"}
assert assigns(:user).valid?
..
end
test "another should register new user and login" do
..
# create user
post users_path, :user => {:username => "newuser", :email => "newuser#gmail.com",
:password => "secret",
:password_confirmation => "secret"}
assert assigns(:user).valid?
..
end
In the User model i have a validation to ensure that :username and :email are unique. But how come the test was able to post both data with no complain/error/invalid? Any help is appreciated, I thought there's a single DB only for the test and the test data should've clashed. Thanks.
Good question. Before the test framework (Test::Unit) runs each test it resets the database to its original state. So before each test you are guaranteed that the DB only has the fixture data and nothing else.
This is helpful. You don't want the data from one test still to be there when you start the next test. If it were, the DB state would be inconsistent if you did something like run a test by itself (instead of the whole suite) or run tests in a different order.
The solution: you can write a test that specifically verifies that the :username and :email are unique. Before I give an example, I should mention that it looks like you wrote a functional test for this validation. This is tempting because you're verifying the behavior the user will see, but the place to test validation is in the unit tests because validation rules in Rails get pushed down to the models. (To keep validation DRY. You don't have to duplicate your validation rules across a bunch of controllers)
So here's one way you could write the unit test:
test "should validate unique email" do
attributes = { :username => "newuser", :email => "newuser#gmail.com", :password => "secret", :password_confirmation => "secret" }
# create user
user1 = User.create(attributes)
assert_nil user1.errors.on(:username)
assert_nil user1.errors.on(:email)
# create another user with the same name and email
user2 = User.create(attributes)
assert_not_nil user2.errors.on(:username)
assert_not_nil user2.errors.on(:email)
end
For a good article about this, see John Nunemaker's blog post from two years ago. Ignore the accidental HTML tags in his code samples. Nunemaker is an awesome Rails blogger.
There are some alternate testing libraries like Shoulda that have built-in functions to validate that your model validates uniqueness. But since you're new to Rails I'd recommend sticking with the default test framework for now. (In fact the folks at 37Signals who invented Rails still use the default too, so that says a lot for it. I also prefer to stick with the default.)
By the way, you can format code in a StackOverflow question by indenting it with four spaces.