I used Wicked gem to create a multistep form. First step is sign up with email name and password, second step would be address for now containing only the street. Here is my address.html.erb
<%= form_for #user, url: wizard_path do |f| %>
<div class="field">
<%= f.label :street %>
<%= f.text_area :street %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
I permitted street and other params in the UsersController:
class UsersController < ApplicationController
def index
#users = User.all
end
def new
#user = User.new
end
def create
#user = User.new(params[:user])
if #user.save
session[:user_id] = #user.id
redirect_to user_steps_path
else
render :new
end
end
private
def user_params
params.require(:user).permit(:email, :password, :password_confirmation, :remember_me, :first_name, :last_name, :street, :house_number, :city, :zip_code)
end
end
I am getting the error mentioned in the title. And these are the params. It basically gets the street, but somhow assignes id to address?
{"utf8"=>"✓",
"_method"=>"patch",
"authenticity_token"=>"ZOkBaqFUdFj47iI8vB0D4PI26ZsgEKasqbzvVM2ry4Z3e+AsYMh0yRSuUoZF5zbJ3SzAkPShI0sjaZOgh0yXRw==",
"user"=>{"street"=>"jef b"},
"commit"=>"Update User",
"id"=>"address"}
What is happening and how to correct it? Here is UserSteprController:
class UserStepsController < ApplicationController
include Wicked::Wizard
steps :address
def show
#user = current_user
render_wizard
end
def update
#user = current_user
#user.attributes = params[:user]
render_wizard #user
end
private
def redirect_to_finish_wizard
new_user_profile_path(current_user.id)
end
end
Second line in the update action is wrong: #user.attributes = params[:user]
Thank you!
The reason you are getting a ActiveModel::ForbiddenAttributesError is that you are passing an unfiltered hash from the params to your model.
#user.attributes = params[:user]
Is pretty much a textbook example of a mass assignment vulnerability which allows a malicious user to assign any attribute they want like for example admin: true. Fortunately Rails has had built in mass-assignment protection since Rails 4 which stopped you from inflicting the vulnerability on your app.
You want to use update or update_attributes instead of the setter and pass it your filtered parameters instead.
#user.update_attributes(user_params)
Related
I'm trying to make a form object work for new User and edit User actions. The form object creates or updates a User through it's save method, but the form object itself is never persisted so Rails always tries to make a POST even though I'm specifying different routes in the simple_form_for url.
Is there any way to make it work for both actions?
UsersController.rb:
class Admin::UsersController < AdminController
def new
#user_form = UserForm.new(account_id: current_account.id)
end
def create
#user_form = UserForm.new(user_form_params)
if #user = #user_form.save
flash[:success] = "User created"
redirect_to admin_user_path(#user)
else
render "new"
end
end
def edit
#user_form = UserForm.new(existing_user: #user, account_id: current_account.id)
end
def update
if #user.update(user_form_params)
flash[:success] = "User saved"
redirect_to admin_user_path(#user)
else
render "edit"
end
end
end
UserForm.rb
class UserForm
include ActiveModel::Model
include ActiveModel::Validations::Callbacks
attr_accessor :fname, :lname, :email
def initialize(params = {})
super(params)
#account = Account.find(account_id)
#user = existing_user || user
end
def user
#user ||= User.new do |user|
user.fname = fname
user.lname = lname
user.email = email
end
end
def save
#user.save
#user
end
end
_form.html.erb
<%= simple_form_for #user_form, url: (#user.present? ? admin_user_path(#user) : admin_users_path) do |f| %>
<%= f.input :fname %>
<%= f.input :lname %>
<%= f.input :email %>
<%= f.submit %>
end
The new/create flow works fine, but editing an existing User returns
No route matches [POST] "/admin/users/69"
class UserForm
# ...
def to_model
#user
end
end
<%= simple_form_for #user_form, url: [:admin, #user_form] do |f| %>
<%= f.input :fname %>
<%= f.input :lname %>
<%= f.input :email %>
<%= f.submit %>
end
When you pass a record to form_for (which SimpleForm wraps), form_with or link_to the polymorphic routing helpers call to_model.model_name.route_key or singular_route_key depending on if the model is persisted?. Passing [:admin, #user_form] will cause the polymorphic route helpers to use admin_users_path instead of just users_path.
On normal models to_model just returns self.
https://api.rubyonrails.org/v6.1.4/classes/ActionDispatch/Routing/PolymorphicRoutes.html
This is my first Rails app and have hit another wall. I have a User model and a Country model. They have a many-to-many relationship, which I join together with a Trip model.
A user can maintain a list of countries that they have been to. On the Country page, I want to have a simple bootstrap button so the current_user can add or remove the country to their list.
I am using a partial that looks like the below to at least render buttons on all the pages.
_add_remove_countries.html.erb
<% if #user.countries.exists?(#country.id) %>
<%= form_for(#user) do |f| %>
<%= f.submit "Remove Country", class: "btn btn-info" %>
<% end %>
<% else %>
<%= form_for(#user) do |f| %>
<%= f.submit "Add Country", class: "btn btn-info" %>
<% end %>
<% end %>
I have tried a few different things, with no luck so I have just reverted to the basic structure. I am currently using a form_for, however that is just what has worked best so far, I am not tied to that solution.
Below are my controllers if needed, I have not set up a Trips controller as I am only using it to join the User and Country Model (maybe I need to set one up?).
users_controller.rb
class UsersController < ApplicationController
def index
#users = User.all
end
def show
#user = User.find(params[:id])
#countries = Country.all
end
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
session[:user_id] = #user.id
redirect_to #user
else
render 'new'
end
end
def update
redirect_to user_path
end
private
def user_params
params.require(:user).permit(:username, :email, :password, :password_confirmation)
end
end
countries_controller.rb
class CountriesController < ApplicationController
before_action :require_user, only: [:index, :show]
def index
#countries = Country.all
#sort = CS.countries.sort_by {|key, value| value}
#sort = #sort.first #sort.size - 2
end
def show
#country = Country.find(params[:id])
#user = User.find(session[:user_id])
end
end
my suggestion using collection_select (and click link in case you would like to know more about collection_select) to add countries to user while editing user, below is sample code to help (using edit method)
user_controller
class UsersController < ApplicationController
def index
#users = User.all
end
def show
#user = User.find(params[:id])
#countries = Country.all
end
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
session[:user_id] = #user.id
redirect_to #user
else
render 'new'
end
end
# ---> here additional code to edit method
def edit
#user = User.find(params[:id])
#countries = Country.all
end
def update
#user = User.find(params[:id])
if #user.update_attributes(user_params)
redirect_to user_path
else
render 'new'
end
end
private
def user_params
params.require(:user).permit(:username,
:email,
:password,
:password_confirmation,
:country_ids => [])
# country_ids is an array that will save data for countries that user have been to
end
end
now this is the fun one, in your views\user\edit.html.erb
<%= form_for #user do |f| %>
<!-- simple -->
<p>Email : </p>
<p><%= f.text_field :email %></p>
<!-- if you using bootstrap -->
<div class="row form-group">
<%= f.label "email", :class => 'control-label col-sm-3' %>
<div class="col-sm-5">
<%= f.text_field :email, :class => 'form-control' %>
</div>
</div>
<!-- other inputs (password / password_confirmation) -->
<%= f.collection_select :country_ids, #countries, :id, :name, {}, { multiple: true, class: 'form-control' } %>
<% end %>
I am trying to render a new view on an already existing user show page. When trying to submit this view, I get param is missing or the value is empty: user. To be clear this is a skill partial being rendered on the user show page. For some reason it is using the strong params in my User Controller.
The code:
show.html.erb for user
<h4>Create a Skill</h4>
<%= render partial: "skills/form" %>
userscontroller.rb
def show
#user = User.find(params[:id])
#skill = Skill.new
#skills = #user.skills.all
end
private
def user_params
params.require(:user).permit(:username, :password, :avatar_url, :email, :about, :cover_letter, :city, :state)
end
end
SkillsController.rb
class SkillsController < ActionController::Base
def new
user = User.find(params[:user_id])
#skill = user.skills.new
end
def create
user = User.find(params[:user_id])
#skill = user.skills.new(skill_params)
if #skill.save
flash[:message] = "#{#skill.name} skill has been created!"
redirect_to user_path(user)
else
redirect_to new_user_skill_path
end
end
private
def skill_params
params.require(:skill).permit(:name, :level)
end
end
Also, I have Namespaced skills within user. No authentication in place yet.
EDIT: #nickm, here are the contents of skills/_form
<%= simple_form_for(Skill.new, :url => { :action => "create" }) do |f| %>
<%= f.input :name, label: 'Skill Name ' %>
<%= f.input :level, label: "Skill Level ", collection: ["Beginner","Proficient", "Intermediate", "Advanced", "Expert"], include_blank: false, include_hidden: false %>
<%= f.submit %>
<% end %>
The problem is that you aren't passing a user_id through the form. You would have to either add a form input:
<%= f.hidden_field :user_id, some_value %>
Then find the user:
user = User.find(params[:skill][:user_id])
and then make skill_params
def skill_params
params.require(:skill).permit(:name, :level, user_id)
end
Or optionally, set the value of user_id in your controller action. Not sure how you're going to pass that value since you haven't built any authentication yet. If you were using something like devise you could do
current_user.skills.new(skills_params)
...in your create action.
I am having problems related to the links given to login and logout.
I am not using devise gem
In my code I have given the following links
<% if current_user %>
<li><%= link_to 'Logout',{:controller=>'sessions', :action=> 'destroy'}%></li>
<% else %>
<li> <%= link_to 'Signup',{:controller =>'users', :action => 'new'} %> </li>
<li> <%= link_to 'Login,{:controller =>'sessions', :action => 'new'} %> </li>
<% end %>
I am using the wicked gem which also has the following steps:
include Wicked::Wizard
steps :business, :login, :payment
If a user enters the form_for values for new method in users_controller and submits it, the user goes to the next step but the link it shows above is "Logout" i.e the user is logged in before signup.
What to do?
Pls, any solution given is appreciated
users_controller.rb:
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
#user.update_attributes(user_params )
session[:user_id]= #user.id
redirect_to user_steps_path
else
render :new
end
end
private
def user_params
params.require(:user).permit( :fname, :lname, :email, :mob, :gender, :country, :state, :suburb, :postal ,:add)
end
end
user_steps_controller.rb
include Wicked::Wizard
steps :business, :login, :payment
def show
#user = current_user
render_wizard
end
def update
#user = current_user
params[:user][:current_step] = step
session[:user_id]= #user.id
#user.update_attributes(user_params )
render_wizard #user
end
private
def redirect_to_finish_wizard(options = nil)
redirect_to root_url
end
def user_params
params.require(:user).permit( :current_step,:cmpyname, :abnacn, :cmpyadd, :cmpydet,:cash, :paypal,:bsb,:usrname,:password, :password_confirmation, :selcat, :protit, :prodes)
end
end
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
helper_method :current_user
private
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
end
Just check in your views if the user is logged in to show your step form:
<% if user_signed_in?%>
instead of
<% if current_user%>
You need to sign out the user after creating it, you can do something like this
if resource.save
sign_out resource # resource = #user
You might need to override devise registrations controller for that if you are using devise!
EDIT:
In your create action you are setting session for newly created user, remove this line from your create action
session[:user_id]= #user.id
Hope this helps!
Instead of checking with current_user you should check <% if session[:user_id].present? %>
It may solve your problem
Just installed Rails 3.0 beta 3 in Windows 7.
And started playing with some easy examples
class SignupController < ApplicationController
def index
#user = User.new(params[:user])
if method.post? and #user.save
redirect_to :root
end
end
end
class User
def initialize(params = {})
#email = params[:email]
#passw = params[:password]
end
def save
end
end
<div align="center">
<% form_for :user do |form| %>
<%= form.label :email %>
<%= form.text_field :email %><br />
<%= form.label :password %>
<%= form.text_field :password %><br />
<%= form.submit :Register! %>
<% end %>
</div>
When I go to /signup I'm getting this error
NoMethodError in
SignupController#index
You have a nil object when you didn't
expect it! You might have expected an
instance of Array. The error occurred
while evaluating nil.[]
Is there a problem with constructor or what's wrong?Please, need your help!
I just won't use ActiveRecord or any other ORM.
You need another action to handle the post, possibly it is called create. This is how I would revise your controller:
def index
#user = User.new
end
def create
#user = User.new(params[:user])
if method.post? and #user.save
redirect_to :root
end
end
The error possibly because when the index is displayed, params variable had no content.