SyntaxError in UsersController#create -- #user.save Ruby on Rails - ruby-on-rails

I'm new to RoR and am experimenting after completing the rails for zombies course by trying to make a running version on my own. I am currently following this tutorial (http://railscasts.com/episodes/250-authentication-from-scratch) to try and make an authentication system from scratch. I know it is done in rails 3 so I have had some fun changing some things to rails 4 friendly.
However, when trying to create a user I get the error "SyntaxError in UsersController#create" pointing to line 8 of my UsersController (if #user.save)
** EDIT ** /usr/local/rvm/gems/ruby-2.0.0-p353#global/gems/activesupport-4.0.2/lib/active_support/callbacks.rb:377: syntax error, unexpected '=', expecting ')'
The error is saying expecting a ')' but I had an '=' sign
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
redirect_to root_url, :notice => "Signed up!"
else
render "new"
end
end
private
def user_params
params.require(:user).permit(:username, :email, :password, :salt, :encrypted_password)
end
end
class User < ActiveRecord::Base
attr_accessor :password
validates_presence_of :password, :on =>create # needed to move line up from below to. Cannot encrypt password without validating password
before_save :encrypt_password
validates_confirmation_of :password
validates_presence_of :email
validates_uniqueness_of :email
def encrypt_password
if password.present?
self.password_salt = BCrypt::Engine.generate_salt
self.password_hash = BCrypt::Engine.hash_secret(password, password_salt)
end
end
end

Are you certain that the problem isn't really inside your User model? This:
validates_presence_of :password, :on =>create
is syntactically valid but it doesn't make any sense. You do have a create class method so
:on => create
is fine as far as Ruby is concerned but I doubt validates_presence_of's :on option knows what to do with what User.create will return. There could be something going on inside validates_presence_of that is trying to treat the :on value as a method name or similar, that sort of thing could easily trigger your confusing and strange looking error from inside active_support/callbacks.rb. Once you start using instance_eval, module_eval, and friends, your error messages suddenly stop making sense and Rails makes heavy use of such things.
Your validation should look like this:
:on => :create
# or
on: :create
so fix that first and see what happens.

Related

ActiveModel::ForbiddenAttributesError rspec on request post [duplicate]

I have this model in Ruby but it throws a ActiveModel::ForbiddenAttributesError
class User < ActiveRecord::Base
attr_accessor :password
validates :username, :presence => true, :uniqueness => true, :length => {:in => 3..20}
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, :uniqueness => true, format: { with: VALID_EMAIL_REGEX }
validates :password, :confirmation => true
validates_length_of :password, :in => 6..20, :on => :create
before_save :encrypt_password
after_save :clear_password
def encrypt_password
if password.present?
self.salt = BCrypt::Engine.generate_salt
self.encrypted_password= BCrypt::Engine.hash_secret(password, salt)
end
end
def clear_password
self.password = nil
end
end
when I run this action
def create
#user = User.new(params[:user])
if #user.save
flash[:notice] = "You Signed up successfully"
flash[:color]= "valid"
else
flash[:notice] = "Form is invalid"
flash[:color]= "invalid"
end
render "new"
end
on ruby 1.9.3p194 (2012-04-20 revision 35410) [x86_64-linux].
Can you please tell me how to get rid of this error or establish a proper user registration form?
I guess you are using Rails 4. If so, the needed parameters must be marked as required.
You might want to do it like this:
class UsersController < ApplicationController
def create
#user = User.new(user_params)
# ...
end
private
def user_params
params.require(:user).permit(:username, :email, :password, :salt, :encrypted_password)
end
end
For those using CanCan. People might be experiencing this if they use CanCan with Rails 4+. Try AntonTrapps's rather clean workaround solution here until CanCan gets updated:
In the ApplicationController:
before_filter do
resource = controller_name.singularize.to_sym
method = "#{resource}_params"
params[resource] &&= send(method) if respond_to?(method, true)
end
and in the resource controller (for example NoteController):
private
def note_params
params.require(:note).permit(:what, :ever)
end
Update:
Here's a continuation project for CanCan called CanCanCan, which looks promising:
CanCanCan
For those using CanCanCan:
You will get this error if CanCanCan cannot find the correct params method.
For the :create action, CanCan will try to initialize a new instance with sanitized input by seeing if your controller will respond to the following methods (in order):
create_params
<model_name>_params such as article_params (this is
the default convention in rails for naming your param method)
resource_params (a generically named method you could specify in
each controller)
Additionally, load_and_authorize_resource can now take a param_method option to specify a custom method in the controller to run to sanitize input.
You can associate the param_method option with a symbol corresponding to the name of a method that will get called:
class ArticlesController < ApplicationController
load_and_authorize_resource param_method: :my_sanitizer
def create
if #article.save
# hurray
else
render :new
end
end
private
def my_sanitizer
params.require(:article).permit(:name)
end
end
source:
https://github.com/CanCanCommunity/cancancan#33-strong-parameters
There is an easier way to avoid the Strong Parameters at all, you just need to convert the parameters to a regular hash, as:
unlocked_params = ActiveSupport::HashWithIndifferentAccess.new(params)
model.create!(unlocked_params)
This defeats the purpose of strong parameters of course, but if you are in a situation like mine (I'm doing my own management of allowed params in another part of my system) this will get the job done.
If using ActiveAdmin don't forget that there is also a permit_params in the model register block:
ActiveAdmin.register Api::V1::Person do
permit_params :name, :address, :etc
end
These need to be set along with those in the controller:
def api_v1_person_params
params.require(:api_v1_person).permit(:name, :address, :etc)
end
Otherwise you will get the error:
ActiveModel::ForbiddenAttributesError
Alternatively you can use the Protected Attributes gem, however this defeats the purpose of requiring strong params. However if you're upgrading an older app, Protected Attributes does provide an easy pathway to upgrade until such time that you can refactor the attr_accessible to strong params.
One more reason is that you override permitted_params by a method. For example
def permitted_params
params.permit(:email, :password)
end
If you are on Rails 4 and you get this error, it could happen if you are using enum on the model if you've defined with symbols like this:
class User
enum preferred_phone: [:home_phone, :mobile_phone, :work_phone]
end
The form will pass say a radio selector as a string param. That's what happened in my case. The simple fix is to change enum to strings instead of symbols
enum preferred_phone: %w[home_phone mobile_phone work_phone]
# or more verbose
enum preferred_phone: ['home_phone', 'mobile_phone', 'work_phone']

How can I implement strong parameters in rails 4?

My User model is as follows (user.rb)
class User < ActiveRecord::Base
validates :password, presence: true, length: { minimum: 6 }
validates :password_confirmation, presence: true
has_secure_password
validates(:name, presence: true, length: {maximum: 50})
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates(:email, presence: true,format: { with: VALID_EMAIL_REGEX }, uniqueness: true)
end
My UsersController is as follows (users_controller.rb)
class UsersController < ApplicationController
def new
end
private
def user_params
params.require(:user).permit(:name)
end
end
So I should only be able to mass-update(mass-assign) the name attribute only.
But when I logon to rails console and type the following command
user=User.find(1)
user.update_attributes(name: "ck",email: "ck#gmail.com", password: "ckck9090", password_confirmation: "ckck9090")
user.save
I am still able to update email.
I didn't mention the :email attributes in the strong parameter .permit(). So how can I still mass-update the email attribute?
Am I missing something?
The key to your answer is here:
def user_params
params.require(:user).permit(:name)
end
You are testing through the Rails console and not using the params hash. Rails has no clue where your arguments come from and will use them unless otherwise specified.
If you truly want to see your strong params in action, I suggest you do this:
Make a create method in your controller and verify you have the correct routes to trigger it
Create a user,
like this:
#user = User.new user_params
#user.save
Finally you have to add a breakpoint so you can see the magic. I suggest adding byebug to your Gemfile, that way you can stop the execution by adding byebug or debugger anywhere in your code. So all together, this would look something
like this:
class UsersController < ApplicationController
def new
end
def create
#user = User.new user_params
byebug
#user.save
end
private
def user_params
params.require(:user).permit(:name)
end
end
This is not very idiomatic but should work to illustrate. From there, you can type in #user in the REPL to see what was put in there. You should be able to see the filtering taking place.
As far as I know strong_parameters does not prohibit using mass-assignment altogether, but simply prohibits the use of params as an argument in mass-assignment. Therefore you can still do that manually.

How to implement strong parameters in Rails 4

I'm having trouble implementing strong parameters, receiving the Undefined Method attr_accessible error locally.
Could anyone explain what exactly I have done wrong here.
users_controller.rb:
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
redirect_to root_url, :notice => "Signed up!"
else
render "new"
end
end
def user_params
params.require(:user).permit(:username, :email, :password, :password_confirmation)
end
end
And in user.rb:
class User < ActiveRecord::Base
attr_accessible :email, :password, :password_confirmation
has_secure_password
validates_presence_of :password, :on => :create
end
And perhaps a foolproof fix for this...I've tried a number of attempts but I just can't seem to get this right.
strong_params are usually done in the controller, not in the model. it's also described like this in the api. so, there's also no need for you to set attr_accesible. this way different controllers can also set different fields on a model, e.g. a backend users controller could be allowed to set an admin flag, while the users controller on the frontend is not allowed to do that.
so, your user_params method belongs in your UsersController, and the create and update action use user_params to filter out the params you don't allow to be set. e.g.:
#user = User.new(user_params)
Rails 4 uses strong params by default, and you don't need attr_accessible. Also in rails 4 you permit params in the controller instead of the model.
How is attr_accessible used in Rails 4?

Beta (Whitelisted) Email List for Registration on Devise

I'm Using Devise and i'm trying to build a requirement that only emails that are included in my white list can actually Sign Up.
Over Time emails will be added to that list. Meaning that Today there are 10 emails, tomorrow another 20+.
But i don't know quite yet how to achieve this.
I know that i have to Create my own Registrations Controller, and for the Validation i think i need something similar to this:
before_validation :whitelisted?
def whitelisted?
unless WhiteList.exists?(:email => email)
errors.add :email, "is not on our beta list"
end
end
However, i am clueless on how to start or continue this. I don't even know if that's the best Practice.
How do i add emails to that whitelist and where is even that whitelist?
If someone could be noob-friendly enough to explain this to me.
Try the following i think this could help you.
create new registration controller
class RegistrationsController < Devise::RegistrationsController
def create
unless WhiteList.exists?(:email => params[:user][:email])
errors.add :email, "is not on our beta list"
else
super
end
end
end
and in routes file replace existing with following
devise_for :users, controllers: { registrations: "registrations" }
Create new model WhiteList using following
rails g model whitelist email:string
and run rake db:migrate command.
after this start Rails console add email's using following command.
Whitelist.create(email: "test#user.com")
I found #Amit Sharma's answer useful, but it doesn't work straight out of the box. Here's what I came up with:
class RegistrationsController < Devise::RegistrationsController
def create
if WhiteList.exists?(:email => params[:user][:email].downcase)
super
else
flash[:error] = "Your email is not on our beta list."
redirect_to new_user_registration_path
end
end
end
class WhiteList < ActiveRecord::Base
before_save :downcase_email
validates :email, presence: true
def downcase_email
self.email = email.downcase
end
end
This solves for case sensitivities when whitelisting an email and produces a flash error message when a Whitelisted email isn't matched.

ActiveModel::ForbiddenAttributesError when creating new user

I have this model in Ruby but it throws a ActiveModel::ForbiddenAttributesError
class User < ActiveRecord::Base
attr_accessor :password
validates :username, :presence => true, :uniqueness => true, :length => {:in => 3..20}
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, :uniqueness => true, format: { with: VALID_EMAIL_REGEX }
validates :password, :confirmation => true
validates_length_of :password, :in => 6..20, :on => :create
before_save :encrypt_password
after_save :clear_password
def encrypt_password
if password.present?
self.salt = BCrypt::Engine.generate_salt
self.encrypted_password= BCrypt::Engine.hash_secret(password, salt)
end
end
def clear_password
self.password = nil
end
end
when I run this action
def create
#user = User.new(params[:user])
if #user.save
flash[:notice] = "You Signed up successfully"
flash[:color]= "valid"
else
flash[:notice] = "Form is invalid"
flash[:color]= "invalid"
end
render "new"
end
on ruby 1.9.3p194 (2012-04-20 revision 35410) [x86_64-linux].
Can you please tell me how to get rid of this error or establish a proper user registration form?
I guess you are using Rails 4. If so, the needed parameters must be marked as required.
You might want to do it like this:
class UsersController < ApplicationController
def create
#user = User.new(user_params)
# ...
end
private
def user_params
params.require(:user).permit(:username, :email, :password, :salt, :encrypted_password)
end
end
For those using CanCan. People might be experiencing this if they use CanCan with Rails 4+. Try AntonTrapps's rather clean workaround solution here until CanCan gets updated:
In the ApplicationController:
before_filter do
resource = controller_name.singularize.to_sym
method = "#{resource}_params"
params[resource] &&= send(method) if respond_to?(method, true)
end
and in the resource controller (for example NoteController):
private
def note_params
params.require(:note).permit(:what, :ever)
end
Update:
Here's a continuation project for CanCan called CanCanCan, which looks promising:
CanCanCan
For those using CanCanCan:
You will get this error if CanCanCan cannot find the correct params method.
For the :create action, CanCan will try to initialize a new instance with sanitized input by seeing if your controller will respond to the following methods (in order):
create_params
<model_name>_params such as article_params (this is
the default convention in rails for naming your param method)
resource_params (a generically named method you could specify in
each controller)
Additionally, load_and_authorize_resource can now take a param_method option to specify a custom method in the controller to run to sanitize input.
You can associate the param_method option with a symbol corresponding to the name of a method that will get called:
class ArticlesController < ApplicationController
load_and_authorize_resource param_method: :my_sanitizer
def create
if #article.save
# hurray
else
render :new
end
end
private
def my_sanitizer
params.require(:article).permit(:name)
end
end
source:
https://github.com/CanCanCommunity/cancancan#33-strong-parameters
There is an easier way to avoid the Strong Parameters at all, you just need to convert the parameters to a regular hash, as:
unlocked_params = ActiveSupport::HashWithIndifferentAccess.new(params)
model.create!(unlocked_params)
This defeats the purpose of strong parameters of course, but if you are in a situation like mine (I'm doing my own management of allowed params in another part of my system) this will get the job done.
If using ActiveAdmin don't forget that there is also a permit_params in the model register block:
ActiveAdmin.register Api::V1::Person do
permit_params :name, :address, :etc
end
These need to be set along with those in the controller:
def api_v1_person_params
params.require(:api_v1_person).permit(:name, :address, :etc)
end
Otherwise you will get the error:
ActiveModel::ForbiddenAttributesError
Alternatively you can use the Protected Attributes gem, however this defeats the purpose of requiring strong params. However if you're upgrading an older app, Protected Attributes does provide an easy pathway to upgrade until such time that you can refactor the attr_accessible to strong params.
One more reason is that you override permitted_params by a method. For example
def permitted_params
params.permit(:email, :password)
end
If you are on Rails 4 and you get this error, it could happen if you are using enum on the model if you've defined with symbols like this:
class User
enum preferred_phone: [:home_phone, :mobile_phone, :work_phone]
end
The form will pass say a radio selector as a string param. That's what happened in my case. The simple fix is to change enum to strings instead of symbols
enum preferred_phone: %w[home_phone mobile_phone work_phone]
# or more verbose
enum preferred_phone: ['home_phone', 'mobile_phone', 'work_phone']

Resources