Well I decided to try a different approach. Right now I have pages that are only accessible by number ids instead of usernames or emails. I would like it to be accessible by email addreses instead. I tried to override them but it failed in rails 4.0 and I tried the find_by_email command which also failed. This is the error message that I get: ActiveRecord::RecordNotFound in AccountsController#show.
The only method that did work is find(params[:id]) which only works for accounts that have id's attached them and fails completly if it is null.
Is there any other method to solve this?
Rails Controller
def show
#puts "****************************************"
#puts params
if #account.nil?
render "shared/404"
else
#if !current_account.nil?
respond_with #account
#else
# render "shared/403"
#end
end
def load_findaccount
#params[:id] remains fixed but find_by_id changes to username
#account = Account.find(params[:id])
#user_path(user)
Account.rb
class Account < ActiveRecord::Base
#def to_param # overridden
# email
#end
validates :first_name, :presence => true
validates :last_name, :presence => true
validates :email, :presence => true, :uniqueness =>{:case_sensitive => false}
end
You can use this Account.where(:email => "example#pop.com"). This will retrieve the records for that email id which I think will be at max one record in your code as email id is unique in your model.
I decided to resolve this on my own and use just Rails 3.2. So this will end it for this issue.
Related
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.
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.
I'm trying to do a custom validation in my model using valid? method. I need to run all validations of this model, except the password.
Something like this:
#resource = SalesPartner.new(permitted_params.merge(parent: current_sales_partner))
respond_to do |format|
if #resource.valid?(except: :password)
#resource.generate_authentication_token
SalesPartnerMailer.mail_new_sales_partner(#resource.email,
{ auth_token: #resource.authentication_token, id: #resource.id, level: #resource.level }
).deliver
flash[:success] = "#{#resource.level} criado com sucesso."
format.html { render "#{#path}/index" }
else
logger.log_and_alert_save_error(#resource, flash, "Ocorreu um erro ao salvar.")
format.html { render "#{#path}/new" }
end
end
Is that possible?
Thanks everyone!
Any way here is a way that you can do with the help of context. Say you have a User model and you want to validates some fields.
validates_presence_of :name,: email, on: :special_context
validates_presence_of :name,:email, :password
With the above 2 lines, you can now do like below
#user.valid?(:special_context)
The above will validates name and email fields. And if you write now,
#user.valid?
This will perform the presence validations on the name, email and password fields as you wrote.
Read valid?(context = nil) to understand the context.
Runs all the validations within the specified context. If the argument is false (default is nil), the context is set to :create if new_record? is true, and to :update if it is not.
Validations with no :on option will run no matter the context. Validations with some :on option will only run in the specified context.
Check this documentation also as an official example.
Context could really help you, but following maybe not.
validates_presence_of :name,: email, on: :special_context
validates_presence_of :name,:email, :password
This is because when you use validates_presence_of without specifying its context, it will be applied to all context(nil). Please see ref here So, maybe you could try as follow code:
validates_presence_of :name, :email, on: :special_context
validates_presence_of :name, :email, :password, on: [ :create, :update]
Just found one more mechanism to skip some validation on specified context, because specify all other context is not always suitable:
validates_presence_of :name, :email, on: :special_context
validates_presence_of :name, :email, :password, if: -> {Array(validation_context).exclude?(:special_context)}
note: validation_context can be array.
I have a payment api that takes bank account info and user info. I catch the api response and use ajax to send the infomation into my controller where I try to save the information to my user. When I save I get the error Validation failed: Password can't be blank, Password is invalid: Any ideas?
Bank Controller:
def addbank
#user = current_user
#user.phone_number = params[:phone_number]
#user.birth_year = params[:year]
#user.bank_uri = (params['bank_acct_uri'])
#user.save! # <------- ERROR here!
# Code was removed to be more clear
end
User Controller:
def update
# update user controller has been commented out but the error is still there
end
User Model:
class User < ActiveRecord::Base
attr_accessible :email,:password,:password_confirmation,:phone_number,:birth_year
attr_accessor :password
before_save :encrypt_password
before_save { |user| user.email = email.downcase }
VALID_PASSWORD_REGEX = # some reg-expression
VALID_PHONE = # some reg-expression
validates_confirmation_of :password
validates :password, presence: true, format:{ with: VALID_PASSWORD_REGEX }
validates :phone_number, format: { with: VALID_PHONE }, if: :phone_number
end
Edit: Why is saving user not hitting my update user controller?
If you want to avoid the validation of one particular field (password in your case), but you want to do all the other validations (for example phone number), you can do something like this:
attr_accessor :skip_password
validates :password, presence: true, format:{ with: VALID_PASSWORD_REGEX }, unless: :skip_password
Then, in your controller, you could do:
def addbank
#user = current_user
#user.skip_password = true # We don't want to validate this..
#user.phone_number = params[:phone_number]
#user.birth_year = params[:year]
#user.bank_uri = (params['bank_acct_uri'])
#user.save! # <------- ERROR here!
# Code was removed to be more clear
end
Do this at your own risk ~~
Where are you storing the encrypted password?
If you store it in password then it will fail validation every save because it doesn't equal password_confirmation.
I'd recommend putting the password in a separate field.
#from the User Model
attr_accessor :password, :password_confirmation
validates_presence_of :password, :on => :create
validates_confirmation_of :password
def password=(password)
#password = password
self.password_digest = BCrypt::Password.create(#password, :cost => 14)
end
def authenticate(password)
BCrypt::Password.new(self.password_digest) == password
end
This way the password gets hashed and saved to password_digest and won't trigger a validation error on save.
You can try to save without validation:
#user.save(:validate => false)
UPDATE:
if !#user.valid? && #user.errors[:phone_number].any?
#do not save
else
#user.save(:validate => false)
end
I'll post this as I am about 95% certain this is the cause, but I apologize if I'm off.
I believe the cause of this is because the user's password is indeed blank. If you look at your database, you'll see a column probably called encrypted_password, which is never directly accessed via your model, nor is it ever de-crypted into your accessible password and password_confirmation attributes.
In order to update the user, you will have to enter re-enter the password, or use the save(false) method to (potentially dangerously) bypass validations.
I have a Rails app with a user model that contains an admin attribute. It's locked down using attr_accessible. My model looks like this:
attr_accessible :name, :email, :other_email, :plant_id, :password, :password_confirmation
attr_accessible :name, :email, :other_email, :plant_id, :password, :password_confirmation, :admin, :as => :admin
And here's what my update method in my users controller looks like:
def update
#user = User.find(params[:id])
if #user.update_attributes(params[:user], :as => current_user_role.to_sym)
flash[:notice] = "Profile updated"
redirect_to edit_user_url(#user)
else
render 'edit'
end
end
I have a helper method in my application controller that passes back the role as a string:
def current_user_role
#current_user_role ||= current_user.admin? ? "admin" : "default"
end
helper_method :current_user_role
I've also set config.active_record.whitelist_attributes = true in config/application.rb.
I've verified that the current_user_role method is returning the proper value based on the current user's admin status. Rails isn't throwing a mass-assignment error. But when I try to update a user's admin status while logged in as an admin, Rails performs the update and silently ignores the admin attribute. Pulling up the user's record in the Rails console shows that the record hasn't been modified.
I have a feeling there's a Ruby- or Rails-specific issue at play that I'm not aware of. I can't locate any info on making the role dynamic. The best I could find was this.
There was an errant attr_accessor :admin in my model that was left in from a prior attempt at getting this to work. I overlooked it. Removing it fixed it.
So, the upshot is that this is a pretty simple way to get dynamic roles working in Rails 3.2.
Looks like it could be a bug in Rails 3.2
https://github.com/stffn/declarative_authorization/issues/127