how to update db with private helper method? - ruby-on-rails

New to rails, and I think i found my problem but I'm not sure if this is the case
def additional_info
#user = User.find params[:id]
end
def update
#user = User.find(params[:id])
if #user.update(user_addinfo)
render user_path
else
render action: 'additional_info'
end
end
def user_params
params.require(:user).permit(:name, :email, :password, :password_confirmation)
end
def user_addinfo
params.require(:user).permit(:years_business, :years_relationships, :years_careers, :years_lifeoutlook)
end
end
A user suggested that i change the
if #user.update(user_addinfo) to -> if#user.update!(user_addinfo)
the result was an error page saying my password is too short! I went back reread Michael Hartl's guide and rails api on the params. section and i believe this is causing an issue? I've tried changing to
params.permit(:years_business, :years_relationships, :years_careers, :years_lifeoutlook)
only and it still gives a password too short error.... what am i doing wrong? or am i totally misunderstanding the params?

There's probably a length validation in the User model that checks for the length of the given password.
Open the app/models/user.rb file and look for a line like this:
validates :password, length: { minimum: 4 }
or:
validates_length_of :password

Related

Show information after record creation on ActiveAdmin

I want to let the admin user see a particular message after or before or during he creats a new record.
I need either an alert box after he created the new record, or to change the current confirmation message just for the user model, or to add a small text in the form specifying this.
I can't seem to find any of the ways.
Thank you
You need to use "notice:". In my case, after saving new "admin_user", I am checking for "resource". If it is "valid", then "redirect_to" with a "message". ... This always works for me.
ActiveAdmin.register AdminUser do
....
....
permit_params :first_name, :last_name, :email, :password
def create
#admin_user = AdminUser.new( admin_user_params )
#admin_user.save
if resource.valid?
redirect_to collection_url, notice: 'Creation Success'
else
flash[:alert] = 'Creation Failed'
render :new
end
end
private
def admin_user_params
params.require(:admin_user).permit(:id, :first_name, :last_name, :email, :password)
end
end
you can modify the flash message with an after_create callback for that case, something like this
ActiveAdmin.register User do
permit_params :name, :email, :password
after_create do |user|
flash[:notice] = "User has been created with the default password" if user.valid?
end
end

Proper user of strong parameters, assignments, and validators in Rails 4?

What is the proper way to set strong parameters in a Rails controller and using validators with them? I've seen several examples of doing this several different ways.
This is what a typical controller def looks like for me:
# User model
class User < ActiveRecord::Base
validates :first_name, length: { in: 2..50 }, format: { with: /[a-zA-Z0-9\s\-\']*/ }
validates :last_name, length: { in: 2..50 }, format: { with: /[a-zA-Z0-9\s\-\']*/ }
validates :email, presence: true, length: { 5..100 }, format: { with: /**email regex**/ }, uniqueness: { case_sensitive: false }
end
# Controller def
def valid_email
# Strong parameters defined here? When is an error thrown for unmatched requires/permits?
params.require(:user)
params[:user].permit(:email)
# How to prevent blank params[:user][:email] from making unnecessary database call if it's blank?
#user = User.find_by(email: params[:user][:email])
unless #user.nil?
# Should work because in permit whitelist?
#user.assign_attributes(email: params[:user][:email])
# Should not work because not in permit whitelist?
#user.assign_attributes(first_name: params[:user][:first_name])
# Should work just sending to private def
#user.assign_attributes(savable_params)
# Validate entire model
if #user.valid?
#user.save
end
end
rescue => e
log_error(e)
render text: "Something bad happened. Contact support.", status: :unprocessed_entity
end
private def savable_params
params.require(:user).permit(:email)
end
How I understand it, is the params.require and params.permit allow for whitelisting of data passed to the controller/action, but doesn't keep "other" data from being seen in the params list (if it's passed).
So, leads into a few questions:
Proper way of whitelisting in a similar scenario a top?
How would you, say for instance, make sure params[:user][:email] isn't blank ("") before trying to do a User.find_by since we know that a blank email isn't valid anyway and do not want to make an unnecessary database call?
User.valid? will validate the entire model, even though I'm interested in only the :email attribute be valid before doing a User.find_by?
If you have the uniqueness validator on email, for instance, how would you keep uniqueness from hitting the database, if params[:user][:email] is blank and we're only interested in that email isn't blank, fits within a certain length, and matches the format?
Thanks again.
So this is updating an existing user?
Just do - assuming for some reason you are not sending a user id as part of your submission:
not_found if params[:user].blank? or params[:user][:email].blank?
#user = User.find_by email: params[:user][:email] or not_found
#user.update savable_params
if #user.valid?
flash.notice = "All good"
redirect_to #user
else
# this has not been saved
flash.now.alert = #user.errors.full_messages.to_sentence
render :(whatever you call to display the form in the first place
end
and add to your application_controller:
def not_found
raise ActionController::RoutingError.new('Not Found')
end
to your questions:
as per above. Validators run on your model, they have the logic, keep your controllers simple.
I added a line for that - should work, but normally you're sending up and id of the user, so it's not something I've done. I don't bother testing it.
The validation happens on the update,. it won't hit the db unless it passes, it's a few cycles, not something I've ever worried about. You could put another pre-validation in there, to save on the db roundtrip, but if your going to do that - why not put it on the html field, and save a server round trip.
Put uniqueness constraint on the model and the db (the model should get it, but for race conditions, the db is your backstop). Nothing will hit the db unless it passes your validations first, so it should be rare.
oh, and I think you'll need:
params.require(:user).permit(:email, :first_name)
to get the other field to update.

user.save returns false in console, but true when done via the UI

I'm a programming/rails beginner, and have encountered a bug I cannot wrap my head around.
I'm using/learning about the "has_secure_password" method. When I try and create a user in my console with a mismatched password/confirm_password, the console returns false and the error is "Password confirmation doesn't match Password". But, when I try and do the same thing within the UI given the below code (+ a view), it saves just fine! Now, notice that in my "user_params" method, I accidentally forgot to permit ":password_confirmation" which is how I noticed this issue in the first place. With that ":password_confirmation" added, the view throws an error but that's not the point. Why even without this is the new User record being successfully created with a mismatched password and password confirmation, even though it doesn't save in the console?
Here is my User model:
class User < ActiveRecord::Base
has_secure_password
validates :name, presence: true
validates :email, presence: true, format: /\A\S+#\S+\z/, uniqueness: {case_sensitive: false}
validates :password, length: {minimum: 4, allow_blank: true}
end
And my User controller:
class UsersController < ApplicationController
def index
#users = User.all
end
def show
#user = User.find(params[:id])
end
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
redirect_to #user, notice: "Thanks for signing up!"
else
render :new
end
end
private
def user_params
params.require(:user).permit(:name, :email, :password)
end
end
This is happening because the password_confirmation attribute is optional. When it isn't supplied to the model that has_secure_password, the model simply accepts the password.
When your password confirmation attribute isn't whitelisted in your controller via user_params, it isn't being passed to the model at all, which is why mismatches appears not to throw an error. In truth the validation isn't taking place at all.
This works in your console because it creates a user without involving a controller or strong parameter whitelisting.

Updating Strong Params Only

First of all, I believe there must be some people, who already asked this question before but I don't know how can I google this problem. So, if it is duplicate I am sorry.
I am working on a social media site. I have user model, which I use to register users to the site. It validates, name, email, and password when registering.
I use the same model to make users edit their informations, like username.
This is what I have in my update controller:
def update
# Find an existing object using form parameters
#profile = User.find_by_id(current_user.id)
# Update the object
if #profile.update_attributes!(settings_profile_params)
# If save succeeds, redirect to itself
redirect_to request.referrer
else
# If save fails, redisplay the form so user can fix the problems
render('edit')
end
end
private # user_params is not an action, that is why it is private.
def settings_profile_params
params.require(:user).permit(:first_name, :last_name, :username, :school, :program, :website, :information)
end
The problem is, I only want to update strong parameters that I defined there. But I am getting an exception because of password validation. I don't know why am I getting this exception. How can I set up system to update the values in strong parameter only.
Thank you.
You can achieve this by changing you password validation. You need to add a condition on password validation.
# Password
validates :password,
:presence => {:message => 'Password cannot be blank'},
:length => {:within => 8..99, :message => 'Password length should be within 8 and 99 characters'}
:if => Proc.new { new_record? || !password.nil? }
By calling update_attributes you are implicitly invoking the same range of validations as an other update and save. You need to update on the specific params you're targeting (e.g. omitting :password).
Here, we can store that list of permitted keys in a variable that is reusable. Then we call update_attribute on each of those keys — doing so within a reduce that gives the same true/false for the switch to edit or display.
def update
# Find an existing object using form parameters
#profile = User.find_by_id(current_user.id)
# Update the object
if PERMITTED_KEYS.reduce(true) {|bool, key| bool &&= #profile.update_attribute(key, #profile.send(key)) }
# If save succeeds, redirect to itself
redirect_to request.referrer
else
# If save fails, redisplay the form so user can fix the problems
render('edit')
end
end
private
PERMITTED_KEYS = [:first_name, :last_name, :username, :school, :program, :website, :information]
# user_params is not an action, that is why it is private.
def settings_profile_params
params.require(:user).permit(PERMITTED_KEYS)
end
Having not used strong_parameters gem before, I think this would be more idiomatic to the use of the gem:
def update
# Find an existing object using form parameters
#profile = User.find_by_id(current_user.id)
# Update the object
if settings_profile_params.keys.reduce(true) {|bool, key| bool &&= #profile.update_attribute(key, #profile.send(key)) }
# If save succeeds, redirect to itself
redirect_to request.referrer
else
# If save fails, redisplay the form so user can fix the problems
render('edit')
end
end
private
# user_params is not an action, that is why it is private.
def settings_profile_params
params.require(:user).permit(
:first_name, :last_name, :username,
:school, :program,
:website, :information
)
end
Though, I still think this is a duplicate question, since it regard how to update model data without all of the defined validation. I've answered in case the update_attributes loop is felt to be a sufficiently unique solution to warrant non-duplication.
Okay, now I found the problem. First of all, #Muntasim figured out a way to solve this problem. But I actually don't need to use this solution, because there is another easy way to fix this.
In this situation, when I let users to update their profiles, rails should not validate my password or any other column in user model, if I don't ask it to. But why was it validating? Because I have validates :password in user model. Instead it has to be validates :digest_password. Because I am using bcrypt.
I don't know why :password was working fine when I register even though I used bcrypt.

ruby on rails - undefined method valid?

im following ryan bates screen cast on how http://railscasts.com/episodes/219-active-model on how to validate a form without a database
but i keep getting an undefined method valid?
heres my controller
def create
#contacts = FreshDeskApiWrapper.new().post_tickets(params[:contacts])
if #contacts.valid?
redirect_to new_contact_path
else
flash[:notice] = "OOps"
render action: 'new'
end
end
I can seem to call
$ FreshDeskApiWrapper.new().valid?
just fine in the console but it does not seem to like it when i tack on the
$ FreshDeskApiWrapper.new().post_tickets(params[email: 'user#example.com']).valid?
i get an undefined method valid?
There is something im not understanding about this
heres my fresh_desk_api_wrapper.rb file i created in my models folder
class FreshDeskApiWrapper
include ActiveModel::Validations
include ActiveModel::Conversion
extend ActiveModel::Naming
attr_accessor :config, :client, :subject, :email, :custom_field_phone_number_50754, :custom_field_company_50754, :description
validates :subject, presence: true
validates :email, presence: true
validates_format_of :email, :with => /^[-a-z0-9_+\.]+\#([-a-z0-9]+\.)+[a-z0-9]{2,4}$/i
def initialize(attributes = {})
attributes.each do |name, value|
send("#{name}=", value)
end
self.config = YAML.load_file("#{Rails.root}/config/fresh_desk.yml")[Rails.env]
self.client = Freshdesk.new(config[:url], config[:api_key], config[:password])
end
def post_tickets(params)
client.post_tickets(params)
end
def persisted?
false
end
end
post_tickets is something im defining in there
You can call valid? on an single instance of an object, not multiple. #contacts would imply that your post_tickets method is returning multiple objects.
try something like this:
#contacts = FreshDeskApiWrapper.new(post_tickets(params[:contacts])
what seems to be the problem is that the method you are adding dosnt return a active record object, so the method valid? is not available
Edit:
maybe this:
#contacts = FreshDeskApiWrapper.new(FreshDeskApiWrapper.post_tickets(params[:contacts])
omg im so dumb so what i did was
def create
#contacts = FreshDeskApiWrapper.new(params[:contacts])
#contacts.post_tickets(params[:contacts])
if #contacts.valid?
redirect_to new_contact_path
else
flash[:notice] = "OOps"
render action: 'new'
end
end
and it works!
Im still struggling to learn all this.....thanks for your guy's guidance it really helped

Resources