How to reject blank and empty attributes during sign_up form? - ruby-on-rails

I have
Model
class User < ActiveRecord::Base
attr_accessible :email
validates :email,
presence: true
serialize :data, ActiveRecord::Coders::Hstore
%w[zipcode first_name].each do |key|
attr_accessible key
define_method(key) do
data && data[key]
end
define_method("#{key}=") do |value|
self.data = (data || {}).merge(key => value)
end
end
end
Controller
class UsersController < ApplicationController
def create
#user = User.find_or_initialize_by_email(params[:user][:email])
if #user.update_attributes(params[:user])
redirect_to :back, notice: "Thanks for sign up!"
else
render "pages/home"
end
end
end
View with client side validation
<%= simple_form_for User.new, validate: true do |f| %>
<%= f.input :email %>
<%= f.input :first_name %>
<%= f.input :zipcode %>
<%= f.button :submit, 'Sign up' %>
<% end %>
First question: I would like to update existing user record but not if param is "" or " ", how to achieve this?
Second question: Should I use create action to do that? Maybe update will be more clear. But this form also create an user object.
Third question: Is any chance to add uniqueness validation to email attribute? Right now my client-side validation do not allow do that.

First: To disallow updating a field with empty strings, set allow_blank: false you could also do allow_nil: false. Add these to your validations in your model.
Second: N/A
Third: Simply add uniqueness: true to your email validation in your model.
Read more here

Related

Rails call custom validation before .new or .create

I make objects in controller's loop.
I need to check pet_name array before loop starts.
(because i got undefined method 'each' for nil:NilClass when
params[:pet_name].each do |pid|) runs)
But my validation always called after User.new or User.create.
I want to change to validate as when i push submit button and check validation, and redirects back when pet_name array is nil.
Ho can i change my code?
Controller
def create
user_name = params[:user_name]
params[:pet_name].each do |pid|
#user = User.new
#user.name = user_name
#user.pet_name = pid
render :new unless #user.save
end
redirect_to users_path
end
User.rb
class User < ApplicationRecord
has_many :pet
validates :name, presence: true
validates :pet_name, presence: true
validate :check_pet
def check_pet
if pet_name.nil?
errors.add(:pet_name, 'at least one pet id')
end
end
end
Prams structure
{ name: 'blabla', pet_name: ['blabla', 'blablabla', 'bla'] }
Sorry but that isn't even close to how you approach the problem in Rails.
If you want a user to have many pets and accept input for the pets when creating users you need to create a working assocation to a Pet model and have the User accept nested attributes:
class User < ApplicationRecord
has_many :pets # has_many assocations should always be plural!
validates :name, presence: true
validates :pets, presence: true
accepts_nested_attributes_for :pets
end
# rails g model pet name user:reference
class Pet < ApplicationRecord
belongs_to :user
validates :name, presence: true
end
class UsersController < ApplicationController
def new
#user = User.new(user_params)
3.times { #user.pets.new } # seeds the form with blank inputs
end
def create
#user = User.new(user_params)
if #user.save
redirect_to #user,
success: 'User created',
status: :created
else
render :new,
status: :unprocessable_entity
end
end
private
def user_params
params.require(:user)
.permit(:name, pets_attributes: [:name])
end
end
<%= form_with(model: #user) do |form| %>
<div class="field">
<%= form.label :name %>
<%= form.text_input :name %>
</div>
<fieldset>
<legend>Pets</legend>
<%= form.fields_for(:pets) do |pet_fields| %>
<div class="nested-fieldset">
<div class="field">
<%= pet_fields.label :name %>
<%= pet_fields.text_input :name %>
</div>
</div>
<% end %>
</fieldset>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
This is a pretty advanced topic and you should have your Rails CRUD basics well figured out before you attempt it. You should also consider if you instead want to use a separate controller to create the pets one by one as a nested resource after creating the user.

Simple_form submit button its not working

Im new on rails. Im trying to validate the input value in a simple_form. If the values correct then redirect to a external url.
But the submit button its not working.
My model:
class Course < ApplicationRecord
validates :keyword, presence: true
validates :keyword, inclusion: { in: %w(marketing pepe),
message: "%{value} its not valid " }
end
My controller
def new
#course = Course.new
end
def create
#course = Course.new(params[:keyword])
if #course.keyword == "marketing"
#course.save
redirect_to ('https://res.cloudinary.com/...pdf')
end
end
my view
<h2>Do you have a code?</h2>
<%= simple_form_for #course do |f| %>
<%= f.input :keyword, label: 'Your keyword please', placeholder: "puts here" %>
<%= f.submit 'take it' %>
<% end %>
Any one can help me?

Updating a single attribute for a user

I have a user table, after the user is created I want to edit one attribute using the below code. This is my user edit view:
<h1>Please select below</h1>
<%= form_for #user do |f| %>
<div class="form-group">
<%= f.label :extra_activity %>
<%= f.select(:extra_activity, [['P_Act', 1],['Ph_Act', 2], ['C_Act', 3]], class: 'form-control', required: true) %></br>
</div>
<%= f.submit 'Submit', class: 'btn btn-primary btn-lg' %>
<% end %>
In my user controller I have the following methods for edit and update :
def edit
#user = User.find(params[:id])
#users = User.all
#user_id = params[:user_id]
end
def update
#user = User.find(params[:id])
if #user.update_attributes(user_params)
redirect_to new_user_activity_path(#user)
else
redirect_to home_path
end
end
user_params are listed below:
def user_params
params.require(:user).permit(:first_name, :surname, :previous_award, :chosen_award, :email, :password, :password_confirmation, :extra_activity)
end
When i initially create the user, :extra_activity is set to 0. When the user clicks submit on the edit form, nothing happens, the user is redirected to home_path. I just need to update the user's :extra_activity attribute to whatever they select in the form. Can anybody suggest where I am going wrong here? Not sure how to save the selected number, as the updated value for :extra_activity
UPDATE:
class User < ActiveRecord::Base
has_many :activities, dependent: :destroy
has_many :weeks, dependent: :destroy
authenticates_with_sorcery!
validates :password, length: { minimum: 3 }
validates :password, confirmation: true
validates :email, uniqueness: true, email_format: { message: 'has invalid format' }
end
After reading your comments and checking the update, i guess the solution is to add:
validates :password, length: { minimum: 3 }, if: :password
The password is most likely stored in hashed form and not in password column. So if you reload the user the attribute is not set.
The validation should be: IF the password is set, then make sure that it is at least of length 3 (which is pretty short).
You should make sure that the password that is stored is not changed when you update the user through this controller.

Rails Sorcery update attributes of model without password

I use sorcery for user authentication in a rails 4.1 application. Everything works fine. But when I try to update specific attributes of the user model (which is authenticated by sorcery), I get an error that the password is blank and is too short.
Here's a snippet from the console
> user = User.last
=> # I get the user
> user.update(about_me: "I'm a user")
=> false
> user.update(about_me: "I'm a user", password: "secret")
=> true
Here's my model code
app/models/user.rb
class User < ActiveRecord::Base
authenticates_with_sorcery!
validates :password, presence: true, length: { minimum: 6 }
.....
end
My controller code
app/controllers/users_controller.rb
class UsersController < ApplicationController
.....
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
if #user.update(user_params)
redirect_to #user
flash[:notice] = "Profile successfully updated"
else
render 'edit'
end
end
private
def user_params
params.require(:user).permit(:username, :name, :email, :password, :about_me)
end
end
And my update form
app/views/users/edit.html.erb
<%= form_for #user, method: :put do |f| %>
<% if #user.errors.any? %>
<div class="alert">
<p><%= pluralize(#user.errors.count, 'error') %></p>
<ul>
<% #user.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<%= f.text_field :username, placeholder: 'Username' %>
<%= f.text_field :name, placeholder: 'Name' %>
<%= f.email_field :email, placeholder: 'Email' %>
<%= f.text_area :about_me, placeholder: 'About me' %>
<%= f.password_field :password, placeholder: 'Password' %>
<%= f.submit 'Save Changes', class: 'button' %>
<% end %>
If I remove the password field from the form, I get errors about the password being blank and about it's length.
Is this something to do with sorcery or is it something I'm missing with rails itself?
Is there a better way to update let's say only the email field without affecting anything else?
class User < ActiveRecord::Base
authenticates_with_sorcery!
validates :password, presence: true, length: { minimum: 6 }, if: :new_user?
private
def new_user?
new_record?
end
end
The validation will be checked only if it's a new_record, for which we have added our own private validation method new_user?. This function will return true during your normal signups/registrations. Hence, at those signups only the password validation will be needed.
During the edit, off course the user will be an existing user / new_record? will return false. Hence the validation for password will be skipped.
2nd way:
class User < ActiveRecord::Base
attr_accessor :skip_password
validates :password, presence: true, length: { minimum: 6 }, unless: :skip_password
end
#users_controller.rb
def update
#user = User.find(params[:id])
#user.skip_password = true
if #user.update(user_params)
redirect_to #user
else
render 'edit'
end
end
Here we have added our own custom attr_accessor skip_password. If the skip_password value is set to true, then during edit/update the password validation will be skipped.
I hope both of those ways will help you :)
If someone looks for this topic in future, it is possible to use changes map of ActiveRecord model:
class User < ActiveRecord::Base
authenticates_with_sorcery!
validates :password, presence: true, length: { minimum: 6 }, if: -> {new_record? || changes[:crypted_password]}
.....
end
where :crypted_password is the value of sorcery_config.crypted_password_attribute_name.
Also currently such condition of validates pointed in Simple Password Authentication sorcery wiki article.

ActiveModel::ForbiddenAttributesError using update_attributes having created params hash myself

I’m trying to edit/update a model record using simple_form, but the form is not going to directly change a model field. Instead, I offer a couple of check_box_tag fields that tell update what fields need changed. As a result, update is not receiving a params[:device] hash that I can use to update the attributes. I am attempting to create this hash, but am getting ForbiddenAttributesError when I issue the #device.update_attributes(params[:device]).
I believe my strong parameters list is correct. If I allow one model field (name) to be processed in the edit view, I receive the expected params[:device] hash and everything works. If I disable that field, because I don’t want it to be changed, then I need to create that hash myself and I receive the error. When I look at the hash I created, it looks to me as equivalent to the one passed by the view. I don’t understand why it is failing.
Environment is Ruby 2.0.0, Rails 4.1 on Windows 8.1 with RubyMine 6.3.
The form is: <... needs correct formatting once working ...>
<%= simple_form_for #device do |f| %>
<legend><%= controller.action_name.capitalize %> Device:</legend>
<%= f.input :name, disabled: true %>
<%= check_box_tag(:is_admin, 0, #device.admin?) %>
<%= label_tag(:is_admin, "Make admin?") %>
<%= check_box_tag(:chg_pwd) %>
<%= label_tag(:chg_pwd, "Change password?") %>
<%= f.button :submit %>
<% end %>
The params[:device] that I receive when I sent f.input :name, disabled: false and allow the view to generate params[:device] is:
ActionController::Parameters (3 element(s))
"{"name"=>"D105", "password"=>"D105Dvgr", "password_confirmation"=>"D105Dvgr"}"
And, everything works.
The params[:device] that I create is:
ActionController::Parameters (3 element(s))
"{"name"=>"D106", "password"=>"D106VdAd", "password_confirmation"=>"D106VdAd"}"
And, I receive Forbidden Attribute Error, even though I see no difference between the two.
The update is: <... Code needs refactored, once it is working...>
class DevicesController < ApplicationController
before_filter :authenticate_device!
... other methods removed here ...
def edit
#device = Device.find(params[:id])
# my_page = render_to_string controller: 'devices', action: 'edit', layout: "application"
end
def update
authorize! :update, #device, :message => 'Not authorized as an administrator.'
#device = Device.find(params[:id])
pwd_msg = ""
if params[:chg_pwd]
pwd_gen = #device.device + SecureRandom.urlsafe_base64(15).tr('lIO0=_\-', 'sxyzEUM').first(4)
params[:device] = {name: #device.name} if params[:device].nil?
params[:device][:password] = pwd_gen
params[:device][:password_confirmation] = pwd_gen
pwd_msg = ", new password is #{pwd_gen}"
end
if #device.update_attributes(params[:device])
params[:is_admin] ? #device.add_role(:admin) : #device.remove_role(:admin)
flash[:notice] = ["Device updated" + pwd_msg]
redirect_to devices_path
else
#device.errors.messages.each do |key, value|
flash[:alert] = ["Unable to update device"]
#device.errors.messages.each do |key, value|
flash[:alert] << key.to_s.capitalize + " " + value[0]
end
end
redirect_to devices_path
end
end
private
def device_params
params.require(:device).permit(:device, :name, :email, :password, :password_confirmation, :encrypted_password, :salt, :role_ids, :is_admin, :chg_pwd) # TODO minimize when update is working
end
end
The model is:
class Device < ActiveRecord::Base
rolify
devise :database_authenticatable, :rememberable, :trackable, :validatable
validates :device,
presence: true,
length: {minimum: 4 },
uniqueness: {case_sensitive: false }
validates :name,
presence: true
def remember_me
true unless self.admin?
end
def admin
self.add_role :admin
end
def not_admin
self.remove_role :admin
end
def admin?
self.has_role? :admin
end
def device?
self.has_role? :device
end
def vip?
self.has_role? :vip
end
def login=(login)
#login = login
end
def login
#login || self.device || self.email
end
def self.find_first_by_auth_conditions(warden_conditions)
conditions = warden_conditions.dup
if login = conditions.delete(:login) # Note one equal sign. Strange but true.
where(conditions).where(["lower(device) = :value OR lower(email) = :value", { :value => login.downcase }]).first
else
where(conditions).first
end
end
end
NEW INFORMATION: I neglected to provide information I have in the ApplicationController. This fix from Anton Trapp handles strong parameters for gems that aren't yet fully Rails 4 compatible:
before_filter do
resource = controller_name.singularize.to_sym
method = "#{resource}_params"
params[resource] &&= send(method) if respond_to?(method, true)
end
I have found that using the proposed solution of:
#device.update_attributes(device_params)
does not work if a model field is updated. The result is "param not found: device". It does work if no model field is update. So, the whole issue begs the question of what is truly wrong.
In DevicesController#update action, change
#device.update_attributes(params[:device])
To
#device.update_attributes(device_params)
As you are using Rails 4.1, you need to whitelist the attributes which you would like to be inserted/updated in database. As you passed the attributes directly to update_attributes method without permitting them you received ActiveModel::ForbiddenAttributesError
UPDATE
To resolve param not found: device:
def device_params
if params[:device]
params.require(:device).permit(:device, :name, :email, :password, :password_confirmation, :encrypted_password, :salt, :role_ids, :is_admin, :chg_pwd) # TODO minimize when update is working
end
end
The fix was to add the fields as attr_accessor to the model, but not the database, so that it could be used correctly within the form.
attr_accessor :is_admin, :chg_pwd
And then modify the view to:
<%= simple_form_for #device do |f| %>
<legend><%= controller.action_name.capitalize %> Device:</legend>
<%= f.input :name, disabled: true %>
<%= f.input :is_admin, as: :boolean, checked_value: true, unchecked_value: false %>
<%= f.input :chg_pwd, as: :boolean, checked_value: true, unchecked_value: false %>
<%= f.button :submit %>
<% end %>
Then, due to the Application Controller code from Anton Trapp:
before_filter do
resource = controller_name.singularize.to_sym
method = "#{resource}_params"
params[resource] &&= send(method) if respond_to?(method, true)
end
I was able to update the fields in Device Controller as follows:
#device.update_attributes(params[:device])

Resources