I'm about two months in to learning RoR.
Trying to setup a subscription site using https://monospace-rails.herokuapp.com/ and want to add customer's physical address fields to the form. For some reason I can't find any info on this. I'm probably searching the wrong key words.
This is the user model:
class User < ActiveRecord::Base
attr_accessible :name, :email, :password, :password_confirmation, :stripe_token, :last_4_digits
attr_accessor :password, :stripe_token
before_save :encrypt_password
before_save :update_stripe
validates_confirmation_of :password
validates_presence_of :password, :on => :create
validates_presence_of :name
validates_presence_of :email
validates_uniqueness_of :email
validates_presence_of :last_4_digits
def stripe_description
"#{name}: #{email}"
end
def update_stripe
if stripe_id.nil?
if !stripe_token.present?
raise "We're doing something wrong -- this isn't supposed to happen"
end
customer = Stripe::Customer.create(
:email => email,
:description => stripe_description,
:card => stripe_token
)
self.last_4_digits = customer.active_card.last4
response = customer.update_subscription({:plan => "premium"})
else
customer = Stripe::Customer.retrieve(stripe_id)
if stripe_token.present?
customer.card = stripe_token
end
# in case they've changed
customer.email = email
customer.description = stripe_description
customer.save
self.last_4_digits = customer.active_card.last4
end
self.stripe_id = customer.id
self.stripe_token = nil
end
def self.authenticate(email, password)
user = self.find_by_email(email)
if user && BCrypt::Password.new(user.hashed_password) == password
user
else
nil
end
end
def encrypt_password
if password.present?
self.hashed_password = BCrypt::Password.create(password)
end
end
end
I'm assuming I need to add the address fields to the db, attr_accessible and within the Stripe:: Customer.create add something. I'm a little confused on how to write it and if that is all require to make it work.
Any help or even links in the right direction would be huge.
Related
I am trying to store a password after hashing it but it shows up as NULL in the database.I generated a scaffold for users using password string and name string, and then altered the mysql table to store hashed password instead using this :
ALTER TABLE users CHANGE password hashed_password CHAR(40) NULL;
my model:
class User < ActiveRecord::Base
attr_accessor :password
attr_accessible :name, :password
validates :name, :uniqueness => true
validates :password, :length => { :in => 6..20 }
def before_create
self.hashed_password = User.hash_password(self.password)
end
def after_create
#password = nil
end
private
def self.hash_password(password)
Digest::SHA1.hexdigest(password)
end
end
I am using Rails 3.2.13.
I think you should use
before_create :hash_the_password
after_create :nil_the_password
def hash_the_password
self.hashed_password = User.hash_password(self.password)
end
def nil_the_password
#password = nil
end
and NOT
#Wrong?
def before_create
...
end
so the callbacks can be the problem.
I have the method below which saves data to the users table as well as the user_details table.
When i pass the #newUser variable to the EmailMailer, i can't access the user_details attributes. How can i pass the user_details in the #newUser object without having to re-query the database?
Models
class User < ActiveRecord::Base
has_one :user_details, :dependent => :destroy
accepts_nested_attributes_for :user_details
attr_accessible :email, :password, :password_confirmation, :remember_me, :username, :login, :home_phone, :cell_phone, :work_phone, :birthday, :home_address, :work_address, :position, :company, :user_details_attributes
end
class UserDetails < ActiveRecord::Base
belongs_to :user
attr_accessible :first_name, :last_name, :home_phone, :cell_phone, :work_phone, :birthday, :home_address, :work_address, :position, :company
end
Controller
# POST /users
def create
#newUser = User.new(params[:user], :include =>:user_details)
# create password
require 'securerandom'
password = SecureRandom.urlsafe_base64(8)
#newUser.password = password
respond_to do |format|
if #newUser.save
#newUser.build_user_details
# Tell the UserMailer to send a welcome Email after save
EmailMailer.welcome_email(#newUser).deliver
# To be used in dev only. Just tests if the email was queued for sending.
#assert ActionMailer::Base.deliveries.empty?
format.html {
flash[:success] = "User created successfully"
redirect_to(contacts_path)
}
else
format.html {
flash[:error] = flash[:error].to_a.concat resource.errors.full_messages
redirect_to(contacts_path)
}
end
end
end
Something like this might do what you are after.
class User < ActiveRecord::Base
has_one :user_details
accepts_nested_attributes_for :user_details
after_initialize :build_user_details
...
end
# In controller
def create
#new_user = User.new
#new_user.attributes = params[:user]
if #new_user.save
# do mail thing
else
# other thing
end
end
You need to build the UserDetails association prior to saving #newUser
#newUser.build_user_details
if #newUser.save
#send mailer
else
#do something else
end
Alternatively you could use the create action after the #newuser is saved
if #newUser.save
#newUser.create_user_details
#send mailer
else
#do something else
end
By the way, Ruby/Rails convention is to use snake_case for variables. so #newUser should be #new_user.
I'm having and error undefined methodadd_friend' for #when lauch$ rake db:seeds` .I'am trying to add friend via the rake task , it's define in the User model. But the method is unusable .
Here is the db/seeds.rb file
require 'faker'
require 'populator'
User.destroy_all
10.times do
user = User.new
user.username = Faker::Internet.user_name
user.email = Faker::Internet.email
user.password = "test"
user.password_confirmation = "test"
user.save
end
User.all.each do |user|
Flit.populate(5..10) do |flit|
flit.user_id = user.id
flit.message = Faker::Lorem.sentence
end
3.times do
User.add_friend(User.all[rand(User.count)])
end
end
and there is the user file.
class User < ActiveRecord::Base
# new columns need to be added here to be writable through mass assignment
attr_accessible :username, :email, :password, :password_confirmation
attr_accessor :password
before_save :prepare_password
validates_presence_of :username
validates_uniqueness_of :username, :email, :allow_blank => true
validates_format_of :username, :with => /^[-\w\._#]+$/i, :allow_blank => true, :message => "should only contain letters, numbers, or .-_#"
validates_format_of :email, :with => /^[-a-z0-9_+\.]+\#([-a-z0-9]+\.)+[a-z0-9]{2,4}$/i
validates_presence_of :password, :on => :create
validates_confirmation_of :password
validates_length_of :password, :minimum => 4, :allow_blank => true
has_many :flits, :dependent => :destroy
has_many :friendships
has_many :friends, :through => :friendships
def add_friend(friend)
friendship = friendships.build(:friend_id => friend.id)
if !friendship.save
logger.debug "User '#{friend.email}' already exists in the user's friendship list."
end
end
# login can be either username or email address
def self.authenticate(login, pass)
user = find_by_username(login) || find_by_email(login)
return user if user && user.password_hash == user.encrypt_password(pass)
end
def encrypt_password(pass)
BCrypt::Engine.hash_secret(pass, password_salt)
end
private
def prepare_password
unless password.blank?
self.password_salt = BCrypt::Engine.generate_salt
self.password_hash = encrypt_password(password)
end
end
end
Make add_friend a class method:
def self.add_friend(friend)
friendship = friendships.build(:friend_id => friend.id)
if !friendship.save
logger.debug "User '#{friend.email}' already exists in the user's friendship list."
end
end
or call it as User.new.add_friend(User.all[rand(User.count)]).
I'm building a call-tracking application as a way to learn rails and twilio.
Right now, I have the model scheme plans has_many users has_many phones.
In the plans model, I have a parameter called max_phone_numbers.
What I'd like to do is to limit the number of phones a user has based on the max_phone_numbers the plan gives.
The flow looks something like this :
1) User buys a bunch of phone numbers
2)When User.phones.count = max_phone numbers, then ability to buy more phone numbers is disabled, and a link pops up to the upgrade_path
I'm not quite sure how I would go about doing this though. What are the combinations of things I would need to do in my model, and in my controller?
What would I define in my controller, in such a way that in the view I can warp if/then statements around the buttons?
i.e if limit is reached, than show this, else show button
What would I put in my models to prevent someone from just visiting the link instead?
Any guidance, or resources on doing something like this would be greatly appreciated
Here's my current user model
# == Schema Information
#
# Table name: users
#
# id :integer not null, primary key
# name :string(255)
# email :string(255)
# created_at :datetime not null
# updated_at :datetime not null
# password_digest :string(255)
# remember_token :string(255)
# twilio_account_sid :string(255)
# twilio_auth_token :string(255)
# plan_id :integer
# stripe_customer_token :string(255)
#
# Twilio authentication credentials
class User < ActiveRecord::Base
attr_accessible :name, :email, :password, :password_confirmation, :plan_id, :stripe_card_token
has_secure_password
belongs_to :plan
has_many :phones, dependent: :destroy
before_save { |user| user.email = email.downcase }
before_save :create_remember_token
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
validates :password, presence: true, length: { minimum: 6 }, on: :create
validates :password_confirmation, presence: true, on: :create
validates_presence_of :plan_id
attr_accessor :stripe_card_token
def save_with_payment
if valid?
customer = Stripe::Customer.create(description: email, plan: plan_id, card: stripe_card_token)
self.stripe_customer_token = customer.id
save!
end
rescue Stripe::InvalidRequestError => e
logger.error "Stripe error while creating customer: #{e.message}"
errors.add :base, "There was a problem with your credit card."
false
end
def create_twilio_subaccount
#client = Twilio::REST::Client.new(TWILIO_PARENT_ACCOUNT_SID, TWILIO_PARENT_ACCOUNT_TOKEN)
#subaccount = #client.accounts.create({:FriendlyName => self[:email]})
self.twilio_account_sid = #subaccount.sid
self.twilio_auth_token = #subaccount.auth_token
save!
end
private
def create_remember_token
self.remember_token = SecureRandom.urlsafe_base64
end
end
You could add a custom validation to your Phone model to check if a user has reached their limit. That would prevent any new Phone's from being created if the user has reached their limit.
In your User class
def at_max_phone_limit?
self.phones.count >= self.plan.max_phone_numbers
end
In your Phone class
validate :check_phone_limit, :on => :create
def check_phone_limit
if User.find(self.user_id).at_max_phone_limit?
self.errors[:base] << "Cannot add any more phones"
end
end
In your view/form, you would do something like this
<% if #user.at_max_phone_limit? %>
<%= link_to "Upgrade your Plan", upgrade_plan_path %>
<% else %>
# Render form/widget/control for adding a phone number
<% end %>
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.