This is user.rb:
# == 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
class User < ActiveRecord::Base
attr_accessible :name, :email, :password, :password_confirmation, :remember_token
has_secure_password
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 => { case_sensitive: false }
validates :password, length: { minimum: 6}
private
def create_remember_token
self.remember_token = SecureRandom.urlsafe_base64
end
end
I have defined the function create_remember_token, but I noticed that in this file I cannot call it. In Rails' console it didn't work either:
1.9.2-p290 :002 > User.first.create_remember_token
User Load (0.4ms) SELECT "users".* FROM "users" LIMIT 1
NoMethodError: private method `create_remember_token' called for #<User:0x0000010289cda0>
from /Users/luke/.rvm/gems/ruby-1.9.2-p290/gems/activemodel-3.2.0/lib/active_model/attribute_methods.rb:404:in `method_missing'
from /Users/luke/.rvm/gems/ruby-1.9.2-p290/gems/activerecord-3.2.0/lib/active_record/attribute_methods.rb:129:in `method_missing'
from (irb):2
from /Users/luke/.rvm/gems/ruby-1.9.2-p290/gems/railties-3.2.0/lib/rails/commands/console.rb:47:in `start'
from /Users/luke/.rvm/gems/ruby-1.9.2-p290/gems/railties-3.2.0/lib/rails/commands/console.rb:8:in `start'
from /Users/luke/.rvm/gems/ruby-1.9.2-p290/gems/railties-3.2.0/lib/rails/commands.rb:41:in `<top (required)>'
from script/rails:6:in `require'
from script/rails:6:in `<main>
What am I doing wrong?
Notice the error you're getting: NoMethodError: private method. Move the method above private and you'll be able to access it.
You can call private methods only in the class itself, not from outside the class. Move your method above private:
def create_remember_token
self.remember_token = SecureRandom.urlsafe_base64
end
private
# Your private methods here
If your issue is calling this method in the console, then yes removing it from private will solve the issue. But, I think you definitely want to keep this method private. Is there some other underlying issue? A failing test? The method should work fine as written.
Related
I have a variable in my model, remember_token, that is assigned a value and after that the model is successfully saved.
But with two other variables -- perishable_token and verified -- save doesn't pass database checks (I got rollbacks). So I used update_attribute.
I just tested assigning a value to remember_token and saving the model in rails console. save doesn't work here either. So I think the difference in before_save filters.
user.remember_token = "asdfa"
user.save
....
ROLLBACK
Although everything is working nice, I would be grateful if you could show me the reason behind this. Maybe there are other (better) ways?
Thanks in advance!
The following is my User model.
(I modeled email verification after AuthLogic examples, but not exactly. Also, credits to Mr Hartl's tutorial.)
# == 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)
# surname :string(255)
# remember_token :string(255)
# role :string(255)
# perishable_token :string(255)
# verified :boolean default(FALSE)
#
class User < ActiveRecord::Base
attr_accessible :email, :name, :surname, :password, :password_confirmation
# attr_reader :perishable_token
attr_protected :role #look at ROLES
has_secure_password
ROLES = %w[admin moderator editor author banned] << nil
has_many :courses
before_save { |user| user.email = email.downcase }
before_save :create_remember_token
# before_save :generate_perishable_token
validates :name, presence: true, length: { maximum: 50 }
validates :surname, 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: { case_sensitive: false }
validates :password, length: { minimum: 6 } #, presence:true >>because there is password_digest
validates :password_confirmation, presence: true
validates :role, inclusion: { in: ROLES }
default_scope order: 'users.surname ASC'
def deliver_verification_instructions!
generate_perishable_token
Notifier.verify_email(self).deliver
end
def self.find_using_perishable_token(token,
age = KarvonSaroy::Application.config.PERISHABLE_TOKEN_VALID_FOR)
return if token.blank?
age = age.to_i
conditions_sql = "perishable_token = ?"
conditions_subs = [token]
if column_names.include?("updated_at") && age > 0
conditions_sql += " and updated_at > ?"
conditions_subs << age.seconds.ago
end
where(conditions_sql, *conditions_subs).first
end
def verify!
self.update_attribute(:verified, true)
# self.verified = true
# self.save
end
#used for sessions
private
def create_remember_token
self.remember_token = SecureRandom.urlsafe_base64
end
def generate_perishable_token
# self.perishable_token = SecureRandom.urlsafe_base64
self.update_attribute(:perishable_token, SecureRandom.urlsafe_base64)
end
end
I would guess that the reason this is not saving properly is that this model you have created in the rails console fails the validations you have set.
The suggestion by gotva that you use save! is also a helpful one as this would show you whether this is the cause of the problem.
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 %>
*********UPDATE : I just tried restarting the Rails server, and it seemed to have worked!
I've built a basic authentication system following Michael Hartl's tutorial on Rails, and now what I would like to do is to use Twilio's API to create a Twilio Sub Account when a user registers.
https://www.twilio.com/docs/api/rest/subaccounts
My thoughts on how to create it were to use a before_save in the User Model, and have twilio create the Auth Token and Account Sid for the sub account. The problem is, that when I hit submit, I get --
NameError in UsersController#create
uninitialized constant User::Twilio
Rails.root: C:/Sites/dentist
Application Trace | Framework Trace | Full Trace
app/models/user.rb:45:in `create_twilio_subaccount'
app/controllers/users_controller.rb:13:in `create'
Here's my Current User Model :
#Twilio authentication credentials
ACCOUNT_SID = '####removed for stackoverflow#####'
ACCOUNT_TOKEN = '####removed for stackoverflow#####'
# == 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)
#
class User < ActiveRecord::Base
attr_accessible :name, :email, :password, :password_confirmation
has_secure_password
before_save { |user| user.email = email.downcase }
before_save :create_remember_token
before_save :create_twilio_subaccount
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 }
validates :password_confirmation, presence: true
private
def create_remember_token
self.remember_token = SecureRandom.urlsafe_base64
end
def create_twilio_subaccount
#client = Twilio::REST::Client.new(ACCOUNT_SID, ACCOUNT_TOKEN)
#subaccount = #client.accounts.create({:FriendlyName => self[:email]})
self.twilio_account_sid = #subaccount.sid
self.twilio_auth_token = #subaccount.auth_token
end
end
Any help on what I should do inside create_twilio_subaccount would be greatly appreciated it. This is just my guess at how to do it, based on how the remember_token worked. Let me know if I'm doing something completely wacky!
I just tried restarting the Rails server, and it seemed to have worked!
According to the rails-cast #237, dynamic attributes were to be easily implemented. Although I have run into some errors when trying to create an object in the rails console. Please advise.
The error I am getting is as follows :
ruby-1.9.3-p0 :005 > User.new :username => "johnsmith", :email => "johnsmith#gmail.com", :password => "changethis"
ArgumentError: wrong number of arguments (1 for 0)
from /Volumes/Terra-Nova/jwaldrip/Sites/theirksome/config/initializers/accessible_attributes.rb:6:in `mass_assignment_authorizer'
from /Volumes/Terra-Nova/jwaldrip/.rvm/gems/ruby-1.9.3-p0/gems/activemodel-3.1.3/lib/active_model/mass_assignment_security.rb:209:in `sanitize_for_mass_assignment'
from /Volumes/Terra-Nova/jwaldrip/.rvm/gems/ruby-1.9.3-p0/gems/activerecord-3.1.3/lib/active_record/base.rb:1744:in `assign_attributes'
from /Volumes/Terra-Nova/jwaldrip/.rvm/gems/ruby-1.9.3-p0/gems/activerecord-3.1.3/lib/active_record/base.rb:1567:in `initialize'
from (irb):5:in `new'
from (irb):5
from /Volumes/Terra-Nova/jwaldrip/.rvm/gems/ruby-1.9.3-p0/gems/railties-3.1.3/lib/rails/commands/console.rb:45:in `start'
from /Volumes/Terra-Nova/jwaldrip/.rvm/gems/ruby-1.9.3-p0/gems/railties-3.1.3/lib/rails/commands/console.rb:8:in `start'
from /Volumes/Terra-Nova/jwaldrip/.rvm/gems/ruby-1.9.3-p0/gems/railties-3.1.3/lib/rails/commands.rb:40:in `<top (required)>'
from script/rails:6:in `require'
from script/rails:6:in `<main>'
/models/user.rb :
class User < ActiveRecord::Base
# Attributes
attr_accessible :username, :email, :password, :password_confirmation, :is_admin
attr_accessor :password
# Callbacks
before_save :encrypt_password
# Relationships
has_many :irks
# Validation
validates_confirmation_of :password
validates_presence_of :password, on: :create
validates :password, presence: true, length: { in: 3..20 }
validates :username, presence: true, uniqueness: true, length: { in: 3..20 }
validates :email, presence: true, email: true, uniqueness: true
# User Authentication
def self.authenticate(email, password)
user = find_by_email(email)
if user && user.password_hash == BCrypt::Engine.hash_secret(password, user.password_salt)
user
else
nil
end
end
# Password Encryption
def encrypt_password
if password.present?
self.password_salt = BCrypt::Engine.generate_salt
self.password_hash = BCrypt::Engine.hash_secret(password, password_salt)
end
end
end
/config/initializers/accessible_attributes.rb :
class ActiveRecord::Base
attr_accessible
attr_accessor :accessible
private
def mass_assignment_authorizer
if accessible == :all
self.class.protected_attributes
else
super + (accessible || [])
end
end
end
Not entirely sure exactly what it is you're trying to do or what the purpose of this mass_assignment_authorizer would be. Seems like there are easier ways to protect against mass assignment. That being said, I read the last couple paragraphs of the railscast, and it appears as though once you have this initializer, you can't pass any arguments into the initializer when creating an object. Even if you could, it wouldn't set the attributes...
In the controller we also need to apply the accessible option to the create action. If we just apply it like this then it will not work.
#article = Article.new(params[:article])
#article.accessible = :all if admin?
The reason that this doesn’t work is that the mass assignment happens in the new call so by the time we’ve set accessible it’s too late. We need to separate creating a new Article from assigning its attributes and slip the call to accessible in between the two.
So it looks to me like in order to set the attributes for one of your models now you need to first create it, then set accessible to be :all on the class, then manually assign the attributes you want, like such:
u = User.create
u.accessible = :all if current_user.is_admin? # or whatever the conditional is for the admin user
u.update_attributes(:username => "johnsmith", :email => "johnsmith#gmail.com", :password => "changethis")
Depending on how many attributes you need to have accessible based on permissions, you may be better off skipping this module since it is a little bit of extra work to implement. If it's only a few attributes on one or two models you may be better off just implementing this functionality by hand with your own methods and attr_accessible. Try reading this article about ruby accessors to see if you can get the desired result without this plugin perhaps?
In Michael Hartl's Ruby on Rails Tutorial, ch 7.2.3, rspec is returns the following errors:
Failures:
1) User has_password? method should be true if the passwords match
Failure/Error: #user.has_password?(#attr[:password].should be_true)
NoMethodError:
undefined method `has_password?' for nil:NilClass
# ./spec/models/user_spec.rb:132:in `block (3 levels) in <top (required)>'
2) User has_password? method should be false if the passwords don't match
Failure/Error: #user.has_password?("invalid").should be_false
NoMethodError:
undefined method `has_password?' for nil:NilClass
# ./spec/models/user_spec.rb:136:in `block (3 levels) in <top (required)>'
Finished in 0.23931 seconds
18 examples, 2 failures
Failed examples:
rspec ./spec/models/user_spec.rb:131 # User has_password? method should be true if the passwords match
rspec ./spec/models/user_spec.rb:135 # User has_password? method should be false if the passwords don't match
In the console I'm also getting an undefined local variable error on 'password confirmation'
I've thoroughly checked my code and can't find the discrepancy but I'm obviously doing it wrong.
here's my users model:
require 'digest'
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 }
# Automatically create the virtual attribute 'password_confirmation'.
validates :password, :presence => true,
:confirmation => true,
:length => { :within => 6..40 }
before_save :encrypt_password
def has_password?(submitted_password)
self.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
Make sure this bit is in your spec, sounds like it's missing
before(:each) do
#user = User.create!(#attr)
end