This my code:
class OrdersController
def create
#order = Order.new(params[:order])
if #order.purchase
work = GATEWAY.store(credit_card, options)
result = work.params['billingid']
current_user.update_attributes(:billing_id => result)
end
end
end
billingid is returned by running GATEWAY.store(credit_card, options)
I am trying to save this returned billingid into :billing_id column in User Model. Is it not possible to update attribute of User model from a that is not UsersController?
Simply put, is it not possible to update an attribute of model #1 from a controller of model #2?
Thanks
UPDATE:
With the help of the men below, I was able to verify two things:
1. result = work.params ['billingid'] returns string
2. That I am able to save into a different model from any controller
However, even though I have attr_accessible :billing_id I am still unable to save the result into billing_id column of User table. I was successful in saving the result in a store_name column of a Store table, so I don't know what it is about User model that is preventing me from saving.
I ran,
#mystore = Store.find(current_user)
#mystore.store_name = result
#mystore.save
and it was successful. But,
#thisuser = User.find(current_user)
#thisuser.billing_id = result
#thisuser.save
This fails even though attr_accessible is set correctly. What else could prevent from saving certain attributes other than attr_accessible? Thanks everyone!
UPDATE 2: User Model
require 'digest'
class User < ActiveRecord::Base
has_one :store
has_many :products
attr_accessor :password
# attr_accessible was commented out completely just to check as well. Neither worked
attr_accessible :name, :email, :password, :password_confirmation, :username, :billing_id
validates :name, :presence => true,
:length => { :maximum => 50 }
validates :email, :presence => true,
:format => { :with => email_regex },
:uniqueness => { :case_sensitive => false }
validates :password, :presence => true,
:confirmation => true,
:length => { :within => 6..40 }
username_regex = /^([a-zA-Z0-9]{1,15})$/
before_save :encrypt_password
def has_password?(submitted_password)
encrypted_password == encrypt(submitted_password)
end
private
def encrypt_password
self.salt = make_salt if new_record?
self.encrypted_password = encrypt(password)
end
def encrypt(string)
secure_hash("#{salt}--#{string}")
end
def make_salt
secure_hash("#{Time.now.utc}--#{password}")
end
def secure_hash(string)
Digest::SHA2.hexdigest(string)
end
end
end
UPDATE FINAL: SOLUTION
using #thisusers.errors, I was able to find out that it was trying to validate the presence of password during this request. Once I commented it out, it saved without an issue. I am unsure why this is happening, but I will take it from here. Thanks everyone esp. dmarkow!
There should be no issue updating any number of models from a controller.
Make sure that work.params['billingid'] actually contains a value.
Your User model may have some attributes marked as attr_accessible (since you have current_user, I assume you have authentication, and this often means needing to protect your model's attributes by default). If this is the case, that means that only those attributes can be changed by mass assignment (e.g. using update_attributes). Either add billing_id to the list of attributes that are attr_accessible, or don't use mass assignment. (Instead, you would just do current_user.billing_id = result and then current_user.save)
Edit: The problem wound up being a validation error on the User model. Always make sure to check the user.errors when user.save returns false.
Related
I'm trying to provide a place to set a single service login for an account, yet not require that the account owner enter the service login credentials every time the rest of the record is updated.
My understanding is that the :reject_if option on accepts_nested_attributes_for is the way to have the nested hash values ignored. Yet, in Rails 4.1, I'm getting a "password can't be blank".
I've traced through the nested_attributes code and it seems to properly ignore the values, yet nothing I do to avoid the update works. I've even deleted the web_service_user_attributes hash from the params passed to update, so I'm wondering if there is something else going on.
Am I understanding :reject_if correctly for a has_one association?
Parent model code:
class Account
has_one :web_service_user
accepts_nested_attributes_for :web_service_user, :allow_destroy => true, :reject_if => :password_not_specified, :update_only => true
def password_not_specified(attributes)
attributes[:password].blank?
end
end
Child model code:
class WebServiceUser
devise :database_authenticatable
belongs_to :account
validates_uniqueness_of :username
validates_presence_of :password, if: Proc.new{|wsu| !username.blank? }
end
Controller code:
def update
respond_to do |format|
if #licensee.update(account_params)
#etc...
end
private
def account_params
params.require(:account).permit(:name, :area_of_business, :address1, :address2, :city, :state_code, :zip, :website_url, :web_service_user_attributes => [:id, :username, :password, :_destroy])
end
Ok, it appears that my primary goof was trying to validate the presence of :password. I really wanted to validate the length of the password if it existed.
class WebServiceUser
devise :database_authenticatable
belongs_to :account
validates_uniqueness_of :username
validates_length_of :password, :minimum => 14, if: Proc.new { |u| !u.password.nil? }
end
I'm trying to mix a custom User authentication mechanism based on SecurePassword with Facebook integration through omniauth-facebook gem.
my app uses Ruby 2.0.0 and Rails 4.0.0.
i tried to follow this guide omniauth and some other articles to came up with something like this for the User and Authentication Models
User model:
class User < ActiveRecord::Base
has_one :user_playlist
has_one :user_info
has_many :band_likes
has_many :song_likes
has_many :band_comments
has_many :song_comments
has_many :authorizations
#many to many relation between User and Band
#todo: make a bands_users migration
has_and_belongs_to_many :bands
has_secure_password
validates :username, presence: true, uniqueness: {case_sensitive: false}, length: {in: 8..64}, format: {with: /\A[a-zA-Z ]+\Z/, message: 'Debe poseer solo letras y espacios.'}
validates :email, presence: true, uniqueness: {case_sensitive: false}, format: {with: /#/, message: 'Dirección de correo inváilda.'}
validates :password, length: {in: 8..24}
validates :password_confirmation, length: {in: 8..24}
def self.create_from_hash!(hash)
create(:email => hash['info']['email'], :username => hash['info']['name'], :password => hash['uid'], :password_confirmation => hash['uid'] )
end
end
Authorization Model:
class Authorization < ActiveRecord::Base
belongs_to :user
validates_presence_of :user_id, :uid, :provider
validates_uniqueness_of :uid, :scope => :provider
def self.find_from_hash(hash)
find_by_provider_and_uid(hash['provider'], hash['uid'])
end
def self.create_from_hash(hash, user = nil)
user ||= User.create_from_hash!(hash)
Authorization.create(:user => user, :uid => hash['uid'], :provider => hash['provider'])
end
end
SessionsController
class SessionsController < ApplicationController
def create
auth = request.env['omniauth.auth']
unless #auth = Authorization.find_from_hash(auth)
# Create a new user or add an auth to existing user, depending on
# whether there is already a user signed in.
#auth = Authorization.create_from_hash(auth, current_user)
end
# Log the authorizing user in.
self.current_user = #auth.user
render :text => "Welcome, #{current_user.username}. <br />User saved = #{current_user.save} .<br/>User valid = #{current_user.valid?}.<br />errors= #{current_user.errors.full_messages}"
end
end
The last render was written to check about the fact that my password does not gets validated, it doesn't matter if i use hash['uid'], hash['info']['name'], or whatever.
The reason why i use this values is just because, i will figure out later how to build a random password for the oauth-ed user, but i don't want blank ones nor disable the validations.
but, no matter what value i use, always get only my name and email:
*Welcome, "My Real Name Here.
User saved = false.
User valid = false.
errors= ["Password is too short (minimum is 8 characters)", "Password confirmation is too short (minimum is 8 characters)"]*
When creating users in Rails Console got no problem, just when OAuth tries to create a User with create_from_hash.
also, if i try to assign a non existing value from hash to password fields, it adds the message that can be blank. so, it isn't blank.
and rendering hash['uid'] in controller shows that it's longer than 8.
I Must warn that i'm new to rails, so if you can, explain me with apples xD
Thanks in advance!
finally i came up with this on User model:
def self.create_from_hash!(hash)
self.where(:email => hash.info.email.to_s).first_or_create do |user|
user.email = hash.info.email
user.username = hash.info.name
user.password = hash.uid
user.password_confirmation = hash.uid
end
end
I don't know why the later doesn't work but at least this one works!
Greetings!
I was adding uniqueness validation to the model in charge of user registration. The username should be uniqueness.
I created a user when testing some days ago, lets call it "example-guy". Then I deleted him from the scaffolding user interface and now when trying to register a new user as "example-guy", it returns that the name has already been taken.
So how can I fix this from the DB without reverting to its "birth-state" and modify the controller to actually destroy the table entry?
Or maybe Rails is keeping track of what was writed to DB, even after destruction?
Im using omniauth-identity, the user model:
class User < ActiveRecord::Base
attr_accessible :name, :provider, :uid, :email
# This is a class method, callable from SessionsController
# hence the "User."
def User.create_with_omniauth(auth)
user = User.new()
user.provider = auth["provider"]
user.uid = auth["uid"]
user.name = auth["info"]["name"]
# ADD EMAIL
user.email = auth["info"]["email"]
user.save
return user
end
end
User controller's destroy function:
# DELETE /users/1
# DELETE /users/1.json
def destroy
#user = User.find(params[:id])
#user.destroy
respond_to do |format|
format.html { redirect_to users_url }
format.json { head :no_content }
end
end
end
Validations are made trought "Identity" model inherited from omniauth's active record:
class Identity < OmniAuth::Identity::Models::ActiveRecord
attr_accessible :email, :name, :password_digest, :password, :password_confirmation
#attr_accessible :email, :name, :password_digest :password, :password confirmation
validates_presence_of :name
validates_uniqueness_of :name
validates_length_of :name, :in => 6..24
validates_format_of :name, :with => /^[a-z]+$/
validate :name_blacklist
validates_uniqueness_of :email, :case_sensitive => false
validates_format_of :email,
:with => /\A([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i,
:message => "doesn't look like a proper email address"
#validates_presence_of :password, on: :create
#validates_presence_of :password_confirmation
validates_length_of :password, :in => 6..24
def name_blacklist
unless self.name.blank? then
case self.name
when "user", "photo", "photos",
"application", "sessions",
"identities", "home"
self.errors.add(:name, "prohibited")
end
end
end
end
Identities controllar manage registration form sent to omniauth and calling new.html.erb:
class IdentitiesController < ApplicationController
def new
#identity = env['omniauth.identity']
end
end
Thanks in advance.
I'm not entirely sure what caused the problem, but I came across a similar problem but with email addresses with a Devise based user.
I was wanting to re-use the same email on a different user. I changed the original user to a different email, but when I then tried to update the second user with the "now unique" email, it failed the unique validation.
A query for users with that email returned nothing.
It seemed to be cache-related to the uniqueness constraint, as restarting the server, after deleting the email address, fixed the problem.
I am extrapolating from a User model given in the Rails tutorial found here to learn more about creating models. I am trying to give a user a confirmation flag, which is initially set false until the user confirms their identity through clicking a link in an automated email sent after registration.
Everything worked before I added the confirmed attribute. I have added a confirmed column to the database through a migration, so it seems to me the error happens somewhere in the before_save :confirmed_false logic.
Can someone help me? The user model is below.
class User < ActiveRecord::Base
attr_accessor :password
attr_accessible :name, :email, :password, :password_confirmation
email_regex = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :name, :presence => true,
:length => { :maximum => 50 }
validates :email, :presence => true,
:format => { :with => email_regex },
:uniqueness => { :case_sensitive => false }
validates :password, :presence => true,
:confirmation => true,
:length => { :within => 6..40 }
before_save :encrypt_password
before_save :confirmed_false
def has_password?(submitted_password)
encrypted_password == encrypt(submitted_password)
end
def self.authenticate(email, submitted_password)
user = find_by_email(email)
return nil if user.nil?
return user if user.has_password?(submitted_password)
end
private
def confirmed_false
self.confirmed = false if new_record?
end
def encrypt_password
self.salt = make_salt if new_record?
self.encrypted_password = encrypt(password)
end
def encrypt(string)
secure_hash("#{salt}--#{string}")
end
def make_salt
secure_hash("#{Time.now.utc}--#{password}")
end
def secure_hash(string)
Digest::SHA2.hexdigest(string)
end
1,1 Top
In your migration, if you set the confirmed column to be a boolean and the default value to be false then you don't need the before_save :confirmed_false callback at all as it will always be false when it's a new record.
Updated
class User < ActiveRecord::Base
# unlike before_save it's only run once (on creation)
before_create :set_registration_date
def set_registration_date
registration_date = Time.now # or Date.today
end
end
Can't really figure out what you're trying to do here. It seems like you want to set the default to be confirmed = false, then change it to confirmed = true if the user clicks on the appropriate link and sends you the correct token, or something like that.
So the flow would be something like this:
A user record is created with confirmed = false
There is no need for a before_filter do do anything yet
A user does some action that permits his confirmed column to be set to true
Still no need for a before_filter
What's the before_filter for? Are you trying to use it to set a default?
I honestly had no idea what to call this any other way (or what to actually look for).
So, In my ActiveRecord (...app/model/s/account.rb/) I have the following:
validates :username, :password, :email, :presence => true
validates :username, :email, :uniqueness => true
validates :username, :length => { :within => 5..30}
validates :password, :length => { :within => 6..128}
validates :password, :confirmation => true
validates :email, :format => { :with => /^([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})$/i }
before_save :encrypt_password
private
def encrypt_password
require "digest"
#account.password = Digest::SHA1.hexdigest(#account.password)
end
And I get the error:
undefined method `password' for nil:NilClass
Why does it report #account as nil? Well, might be because #account doesn't exist outside the View/Controller, but I (honest to god) couldn't think of any other way of doing this.
P.S: I tried putting the encrypt_password inside the controller; it still didn't work (different error, though).
Change the method to read:
def encrypt_password
require 'digest'
self.password = Digest::SHA1.hexdigest(self.password)
end
The instance variable #account, which you use in the controller and view, refers to an instance of the class Account, which is what you're working on in this file. So, presumably, you're doing something like this in the controller:
#account = Account.new(params)
#account.save!
The instance of class Account receives the 'save!' call and fires its before_save callback, where it needs to refer to itself as 'self', not #account (which is the name your controller has for it).
Side note: you should require 'digest' at the top of your class file rather than inside the method - there's no need to load the digest library every time you want to hash a password.
You should try
def self.hash_password(password = '')
self.password = Digest::SHA1.hexdigest(password)
end