Changing boolean value in Rails 3 app yields ArgumentError - ruby-on-rails-3

In my Rails 3.0.5 app I have columns on my Profile model for privacy. In each User's settings, I want the user to be able to change their settings so they can determine which parts of their profile are shown. In my migration I added four boolean attributes for this. However when I try to update the value (switch between true/false) I get an ArgumentError:
ArgumentError (wrong number of arguments (1 for 2)):
app/controllers/profiles_controller.rb:162:in `edit_settings'
An example of the params passed:
{"utf8"=>"✓",
"authenticity_token"=>"0uySkgRNsIIQSX6PtXl3e0e+MXCTo4yuoU/QjuBxENw=",
"show_hometown"=>"1"}
Here is the boolean in my migration:
t.boolean :show_hometown, :default => true
Here is the controller action edit_settings I'm using to update. There are four if statements to update_attribute, but for length I'm only including two:
def edit_settings
#profile = current_user.profile
if #profile.update_attribute(:show_hometown)
redirect_to settings_path, :notice => 'Updated user information successfully.'
else
redirect_to settings_path, :notice => 'Oops, something went wrong. Please try again.'
end
...
if #profile.update_attribute(:show_current_location)
redirect_to settings_path, :notice => 'Updated user information successfully.'
else
redirect_to settings_path, :notice => 'Oops, something went wrong. Please try again.'
end
end
And the form for updating :show_hometown:
<%= form_tag({:action => "edit_settings", :controller => "profiles"}, :html => {:multipart => true }) do %>
<%= check_box_tag :show_hometown %>
<%= #user.profile.hometown %>
<% end %>
FOLLOW-UP:
If I use validations on :show_hometown and insert the following into my controller I can see the value toggle:
def edit_settings
#profile = current_user.profile
if #profile.show_hometown == true
if #profile.update_attributes(:show_hometown => false)
redirect_to settings_path
else
redirect_to settings_path, :notice => 'Oops, something went wrong. Please try again.'
end
elsif #profile.show_hometown == false
if #profile.update_attributes(:show_hometown => true)
redirect_to settings_path
else
redirect_to settings_path, :notice => 'Oops, something went wrong. Please try again.'
end
end
end
However, since I have four booleans where I'll apply the same code is there a way to handle each update in one controller action? Or do I need to create a separate action for each boolean attribute I want to update?

You have to tell update_attribute the column and the value:
#profile.update_attribute(:show_hometown, params[:show_hometown])
This means set the value of column show_hometown to the value in params[:show_hometown].
Note that with update_attribute validations are skipped. If you want validations to run, you have to use update_attributes:
#profile.update_attributes(:show_hometown => params[:show_hometown])
To answer your follow-up: with update_attributes you can update several columns in one call:
#profile.update_attributes({:show_hometown => !#profile.show_hometown,
:show_birthdate => !#profile.show_birthdate})
There is also a toggle method, but using that would result in 4 database calls instead of 1. It can be written in one line:
%w{show_hometown show_birthdate}.each { |attr| #profile.toggle(attr) }

Related

Unpermitted parameters although controller has them whitelisted when creating user

In migrating a rails application from 3.2 to 4.1, I am hitting some issues with user creation. As there is a need to distinguish the current_user form a local_user. The controller create action
def create
#local_user = User.new(user_params)
respond_to do |format|
if #local_user.save
if params[:user][:avatar].present?
format.html { render :crop }
else
format.html { redirect_to(admin_user_path(#local_user), :notice => 'User was successfully created.') }
end
else
format.html { render :action => "new" }
end
Generates a console error: Unpermitted parameters: name, surname, pen_name[...], yet the User controller defines them:
def user_params
params.require(:user).permit(:name, :surname, :pen_name, [...])
end
The form call is:
<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name), :method => :post, :validate => true) do |f| %>
This is necessarily a devise issue for a user can be created with the scaffolding for the User class with the exact same variables. There is some logic that devise is going through which does not pull all the controller logic ; what am I missing?
Needs a devise specific initializer, as per this demo, where all variables can be entered as an array.

render partial from controller with :layout => !request.xhr? returns missing template?

I cloned and tweak this rails app and I am experiencing some trouble with rendering a partial when on ajax request, in the logs I see that the guilty line is in the registrations_controller.rb (Devise)
class RegistrationsController < Devise::RegistrationsController
def create
build_resource
if resource.save
if resource.active_for_authentication?
sign_in(resource_name, resource)
(render(:partial => 'thankyou', :layout => false) && return) if request.xhr?
respond_with resource, :location => after_sign_up_path_for(resource)
else
resource.update_attribute(:encrypted_password, nil) # make sure there is no password
expire_session_data_after_sign_in!
(render(:partial => 'thankyou', :layout => false) && return) if request.xhr?
respond_with resource, :location => after_inactive_sign_up_path_for(resource)
end
else
clean_up_passwords resource
render :partial => 'email_capture', :action => :new, :layout => !request.xhr?**
end
end
protected
def after_inactive_sign_up_path_for(resource)
'/thankyou.html'
end
def after_sign_up_path_for(resource)
redirect_to root_path
end
end
The error message returned is this:
ActionView::MissingTemplate - Missing partial
with {:locale=>[:en], :formats=>[:html], :handlers=>[:erb, :builder,
:coffee, :haml]}. Searched in: *
"/Users/Davide/Documents/Jobsite/rails-prelaunch-signup-1click/app/views"
* "/Users/Davide/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/devise_invitable-1.1.8/app/views"
* "/Users/Davide/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/devise-2.2.4/app/views"
Interestingly if I remove the :layout => !=request.xhr? parameter the partial gets found but the page refreshes and the loses all the stylesheets and other assets.
Any idea where should I start looking?
Thanks!
I have also cloned and modified that project, and ran into nearly the same issue. For me, the problem would appear after the second failure of saving a bad email address--but the result was identical.
By using context and information from the following question/answer:
form returned from AJAX request is not recognized as 'remote' when submitted
I was able to make a change that resolved my issue. In _email_capture.html.erb, add :remote => true, to the simple_form_for line. In my case, the whole revised line is:
<%= simple_form_for resource, :as => resource_name, :remote => true, :url => registration_path(resource_name) , :html => {:class => 'form-inline'} do |f| %>
Hope this helps you, too!

Show form errors when updating an object from a different controller

I have a form in an account_settings controller that updates a user object through the users_controller. The error messages are not getting passed through. Here's a look at the code.
account_settings/account.html.haml
= form_for #user, :url => { :controller => "users", :action => "update", :id => #user.id } do |f|
- if #user.errors.any?
.alert.alert-error
%h4
Please address the following errors:
%ul
- #user.errors.full_messages.each do |msg|
%li= msg
form stuff...
account_settings_controller
def account
#user = current_user
end
users_controller
def update
#user = User.find(params[:id])
if #user.update_attributes(params[:user])
flash[:notice] = "User was successfully updated."
end
redirect_to :back
end
The form will not update but there are no error messages getting passed back. Any thoughts?
This happens because you are returning a redirecting. The #user object does not persist across the redirect. You should be doing something like:
def update
#user = User.find(params[:id])
if #user.update_attributes(params[:user])
flash[:notice] = "User was successfully updated."
redirect_to :back
else
render :edit
end
end
Here, we redirect when the update is successful, but if not, we render the edit action. Hence we have access to #user, and your errors would be present in #user.errors

Rails 3 render partial from another controller (error: ActionView::MissingTemplate)

I'm trying to include a login (username / password) in the header of my application.html.erb. I am getting this error:
Missing partial /login with {:handlers=>[:rjs, :builder, :rhtml, :erb, :rxml], :locale=>[:en, :en], :formats=>[:html]} in view paths "/app/views"
This is happening when I make this call in my application.html.erb:
<%= render '/login' %>
'/login' is defined in my routes.rb as:
match '/login' => "sessions#new", :as => "login"
UPDATE: here is my sessions controller:
class SessionsController < ApplicationController
def create
if user = User.authenticate(params[:email], params[:password])
session[:user_id] = user.id
user.last_login = Time.now
user.save
redirect_to root_path, :notice => "login successful"
else
flash.now[:alert] = "invalid login / password combination " # don't show pass + params[:password]
#render :action => "new"
redirect_to login_path, :notice => "wrong user pass"
end
end
def destroy
reset_session
redirect_to root_path, :notice => "successfully logged out"
end
end
I have seen in other posts that this can be due to not defining a variable in a controller action, but since this is a session, and it is in the application.html.erb (application_controller.rb), I'm not sure how to do this. Anybody know how to do this? Thanks!
<%= render "sessions/login", :#user => User.new %>
will render login partial of sessions view, i.e. '_login.html.erb' in views/sessions and instantiate #user to new user so that it can be referenced directly in the partial as :
form_for #user, :url => sessions_path do |f|
f.text_field :email
Check your file extension in my case file extension was rhtml, I changed it into html.erb.
Now its working fine.
Note:
This file with rhtml extension was working fine in rails <= 3.0.10. But stopped working in rails 3.1.12. So I changed its extension as mentioned above.

Rails 3 Controller update action works locally but not on heroku, get missing template error

I am experiencing a similar problem as this previous SO question, but the answer doesn't seem to fit me. So I wanted to follow up with my issue.
I have an action in my controller that updates my Profile model using a checkbox. When I click the checkbox with the app running locally I have no problems. However when I deploy to Heroku I get:
ActionView::MissingTemplate (Missing template profiles/show_hometown_settings with {:handlers=>[:erb, :rjs, :builder, :rhtml, :rxml], :formats=>[:html], :locale=>[:en, :en]} in view paths "/app/app/views", "/app/vendor/plugins/rails_log_stdout/app/views", "/app/vendor/plugins/rails3_serve_static_assets/app/views", "/app/vendor/plugins/paperclip/app/views"):
I added both a _show_hometown_settings.html.erb file and a _show_hometown_settings.js.erb file but neither did the trick. Checking my checkbox gave me vanilla code on my screen and the above error.
If I add show_hometown_settings.html.erb I no longer get vanilla code. I see the show_hometown_settings.html.erb template, but I still get the error and I don't get redirected to my settings_path, which is the whole point (check to update, post to db, redirected to make further settings changes). Can anyone help me out with this?
Here is the controller action:
def show_hometown_settings
#profile = current_user.profile
if #profile.show_hometown == true
if #profile.update_attributes(:show_hometown => false)
redirect_to settings_path
else
redirect_to settings_path, :notice => 'Oops, something went wrong. Please try again.'
end
elsif #profile.show_hometown == false
if #profile.update_attributes(:show_hometown => true)
redirect_to settings_path
else
redirect_to settings_path, :notice => 'Oops, something went wrong. Please try again.'
end
end
end
Here's the form I use for the action:
<%= form_tag({:action => "show_hometown_settings", :controller => "profiles"}, :html => {:multipart => true }) do %>
<%= check_box_tag(:show_hometown, 1, #user.profile.show_hometown ? true : false) %>
<%= #user.profile.hometown %>
<% end %>
UPDATE: Here is the part of my routes.rb file that references the action:
match "profiles/show_hometown_settings", :to => "profiles#show_hometown_settings"
UPDATE 2: Following the question below, I got a different error and my logs show a route issue:
This returns
`The page you were looking for doesn't exist (404)`
And in my heroku logs --tail I see
2012-01-19T23:13:47+00:00 app[web.1]: Started POST "/profiles/show_hometown_settings" for 98.218.231.113 at 2012-01-19 23:13:47 +0000
2012-01-19T23:13:47+00:00 app[web.1]:
2012-01-19T23:13:47+00:00 app[web.1]: ActionController::RoutingError (No route matches "/profiles/show_hometown_settings"):
If I change the route from :via => [:put] to :via => [:post] it works. Hopefully that's alright.
You shouldn't need to have that template file at all since you aren't rendering a view, you're only redirecting.
What's probably happening is that your if-elsif logic is breaking down and the code is never reaching those redirects (because neither of the if blocks are evaluating to true). If Rails doesn't get a redirect_to call, then by default it will render the view for the controller action (which is what it is trying to do).
I would change it around to this:
Route:
match "profiles/show_hometown_settings", :to => "profiles#show_hometown_settings", :as => :show_hometown_settings, :via => [:put]
Controller:
def show_hometown_settings
if current_user.profile.update_attributes(:show_hometown => params[:show_hometown])
redirect_to settings_path
else
redirect_to settings_path, :notice => 'Oops, something went wrong. Please try again.'
end
end
Form:
<%= form_tag(show_hometown_settings_path, :method => :put) do %>
<%= check_box_tag(:show_hometown, 1, #user.profile.show_hometown ? true : false) %>
<%= #user.profile.hometown %>
<% end %>