Create data in other tables when creating user - ruby-on-rails

Good day.
I have 2 tables in my database (PostgeSQL): "Users" and "Folders". Folders are belongs_to :user and user has_many :folders. I need user, when creating his account automatically create one folder in folders table with name: "Default", id: 1, user_id: current_user.id. As I understand correctly, I need to edit create method in users_controller
This is my 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
# password_digest :string(255)
# remember_token :string(255)
# admin :boolean default(FALSE)
#
class User < ActiveRecord::Base
attr_accessible :name, :email, :password, :password_confirmation
has_secure_password
has_many :folders, dependent: :destroy
before_save { 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: { case_sensitive: false }
validates :password, length: { minimum: 6 }
validates :password_confirmation, presence: true
private
def create_remember_token
self.remember_token = SecureRandom.urlsafe_base64
end
end
This is folder.rb
# == Schema Information
#
# Table name: folders
#
# id :integer not null, primary key
# name :string(255)
# user_id :integer
# created_at :datetime not null
# updated_at :datetime not null
#
class Folder < ActiveRecord::Base
attr_accessible :name
belongs_to :user
has_many :bookmarks, dependent: :destroy
validates :user_id, presence: true
validates :name, presence: true
end
And this is the create method from users_controller.rb
def create
#user = User.new(params[:user])
if #user.save
sign_in #user
redirect_to #user
flash[:success] = "Welcome to Timio Bookmarks!"
else
if signed_in?
redirect_to root_path
else
render 'new'
end
end
end
Thanks for your help!

Try something like:
#user.folders << Folder.new
before you do the save on the #user.save

Related

Saving model after updating an attribute

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.

Ruby on Rails - Does the scope of SessionsHelper reach models?

In my Organisations module, I try to assign a value to a field before creating the Organisation record.
I use a before_create filter in the model, which usually works fine.
But when I try to assign a value coming from an attribute of the current_user method defined in the session, I get an Undefined method 'current_user' error message.
As doing this works fine in a controller, I wonder why it does not work in the model?
Here is my code for the model:
# == Schema Information
#
# Table name: organisations
#
# id :integer not null, primary key
# playground_id :integer
# code :string(255)
# name :string(255)
# description :text
# parent_organisation_id :integer
# organisation_level :integer
# hierarchy :string(255)
# created_by :string(255)
# updated_by :string(255)
# created_at :datetime not null
# updated_at :datetime not null
#
class Organisation < ActiveRecord::Base
### before filter
before_create :set_code
before_create :set_hierarchy
validates :code, presence: true, uniqueness: true
validates :name, presence: true, uniqueness: true
validates :organisation_level, presence: true
validates :created_by , presence: true
validates :updated_by, presence: true
# validates :owner_id, presence: true
# validates :status_id, presence: true
validates :playground_id, presence: true
# belongs_to :owner, :class_name => "User", :foreign_key => "owner_id" # helps retrieving the owner name
# belongs_to :status, :class_name => "Parameter", :foreign_key => "status_id" # helps retrieving the status name
belongs_to :organisation
has_many :organisations
### private functions definitions
private
### before filters
def set_code
if Organisation.count > 0
self.code = self.organisation.code + '-' + code
end
end
def set_hierarchy
if Organisation.count == 0
self.hierarchy = current_user.current_playground_id.to_s + '.001'
else
last_one = Organisation.maximum("hierarchy")
self.hierarchy = last_one.next
end
end
end
Here is my code for the SessionsHelper (inspired from Rails Tutorial):
module SessionsHelper
def sign_in(user)
remember_token = User.new_remember_token
cookies.permanent[:remember_token] = remember_token
user.update_attribute(:remember_token, User.encrypt(remember_token))
self.current_user = user
end
def current_user=(user)
#current_user = user
end
def current_user
remember_token = User.encrypt(cookies[:remember_token])
#current_user ||= User.find_by(remember_token: remember_token)
end
def signed_in?
!current_user.nil?
end
def sign_out
self.current_user = nil
cookies.delete(:remember_token)
end
end
Here is an example from a controller where the assignment works:
#business_flow.playground_id = current_user.current_playground_id
I'd be glad to understand why it does not work in the model.
The SessionsHelper module gets included into your controller class, so you can use its methods from there. It does not get included in the model class.
In fact it probably wouldn't work even if you included it by hand, because the model would need access to the cookies method for the current_user method to work. cookies comes from an ActionController method.
To achieve what you want, try making the change in your Organisation object from the controller that's creating that object, passing in the user information that the controller has access to.

Limit the number of rows of a column a user has based on parameter

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 %>

Rails Adding Twilio Sub Accounts : Uninitialized constant User::Twilio

*********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!

newbie: tips on how to cleanup , improve, arrange, shorten model?

I find my models getting more and more clumped and messy. below is my actual user model, any suggestions on cleaning things up and arranging things for better readability?
Im getting frustrated by the unreadability of this all, so any general thoughts on this code are welcome. I really like code that does the same to be positioned together but like I have now is just one big bulk unreadable mess.
# == Schema Information
#
# Table name: users
#
# id :integer(4) not null, primary key
# email :string(255) default(""), not null
# encrypted_password :string(255) default(""), not null
# reset_password_token :string(255)
# reset_password_sent_at :datetime
# remember_created_at :datetime
# sign_in_count :integer(4) default(0)
# current_sign_in_at :datetime
# last_sign_in_at :datetime
# current_sign_in_ip :string(255)
# last_sign_in_ip :string(255)
# password_salt :string(255)
# confirmation_token :string(255)
# confirmed_at :datetime
# confirmation_sent_at :datetime
# unconfirmed_email :string(255)
# failed_attempts :integer(4) default(0)
# unlock_token :string(255)
# locked_at :datetime
# authentication_token :string(255)
# username :string(255)
# is_blocked :boolean(1)
# is_deleted :boolean(1)
# role :string(255)
# slug :string(255)
# created_at :datetime not null
# updated_at :datetime not null
# last_seen :datetime
# credits :integer(4)
#
class User < ActiveRecord::Base
devise :database_authenticatable,
:registerable,
:recoverable,
:rememberable,
:trackable,
:validatable,
:token_authenticatable,
:encryptable,
:confirmable,
:lockable,
:timeoutable,
:lastseenable
#:omniauthable
attr_accessible :username,
:login,
:email,
:password,
:password_confirmation,
:remember_me,
:profile_attributes,
:is_moderated,
:is_blocked,
:is_deleted,
:credits,
:role,
:confirmed_at,
:last_seen,
:invite_code
attr_accessor :login
#attr_accessor :invite_code
has_one :profile
has_one :account
accepts_nested_attributes_for :profile
accepts_nested_attributes_for :account
extend FriendlyId
friendly_id :username, use: :slugged
before_create :default_values
# AFTER CREATE -------------------------------------------------------------------------------------------------------
after_create :add_account
def add_account
self.create_account
end
def default_values
self.credits = -1
self.invite_code = invite_code
#self.reset_authentication_token!
beta = Beta.where(:code => invite_code).first
beta.used = 1
beta.save
end
# ROLES --------------------------------------------------------------------------------------------------------------
before_create :setup_default_role_for_new_users
ROLES = %w[admin default vip]
# VALIDATIONS --------------------------------------------------------------------------------------------------------
before_validation { |u| u.username.downcase! }
before_validation { |u| u.email.downcase! }
validates_uniqueness_of :username,
:email,
:case_sensitive => false
validates_presence_of :email,
:username,
:invite_code
validates :username,
:exclusion => {:in => ["admin", "root", "administrator", "superuser", "myhost", "support", "contact", "chat", "boo"],
:message => "is reserved"}
validate :check_email, :on => :create
validate :check_invite_code, :on => :create
def check_invite_code
errors.add(:invite_code, "Invalid code") unless Beta.where(:code => invite_code, :used => 0).first
end
# Devise
def active_for_authentication?
super && !is_deleted
end
# Devise
def confirm!
#welcome_message
#super
end
# Devise
def soft_delete
update_attribute(:deleted_at, Time.current)
end
def is_moderated?
return self.is_moderated
end
def is_online?
if self.last_seen < 10.minutes.ago
return true
else
return false
end
end
private
def setup_default_role_for_new_users
if self.role.blank?
self.role = "default"
end
end
def welcome_message
#::Devise.mailer.welcome_instructions(self).deliver
::Devise.mailer.welcome_instructions(self).deliver
end
def check_email
host = email.split("#").last
username = email.split("#").first
reserved_word_filters = %w(admin hostmaster root support )
if /.*(#{reserved_word_filters.join("|")}).*\#/.match(email)
errors.add(:email, "Invalid username in email")
end
if email.include? 'myhost'
errors.add(:email, "Invalid email")
end
end
# DEVISE: --------------------------------------------------------------------------------------------------
protected
def self.find_for_database_authentication(warden_conditions)
conditions = warden_conditions.dup
login = conditions.delete(:login)
where(conditions).where(["lower(username) = :value OR lower(email) = :value", {:value => login.strip.downcase}]).first
end
end
The only thing I would do to clean up models is to modularize them a bit. DHH himself posted a great example gist showing how to clean up a model that had gotten too large. I don't think yours is particularly too big, but if you wanted to move all the devise stuff into its own module, it certainly would make your model a bit more tidy.
You could also get in the habit of treating booleans as first class expressions. For example,
def is_online?
if self.last_seen < 10.minutes.ago
return true
else
return false
end
end
is more clearly written as:
def is_online?
last_seen < 10.minutes.ago
end
1.
You have return statements all over your code. Unless you are returning something prematurely in a method, the return of the last statement in a method is what Ruby returns automatically. Like so:
def square(x)
val = x**2
return val
end
can be shortened to:
def square(x)
x**2
end
It's a contrived example, but there it is.
2.
Many of the selfs are redundant. In an model instance's scope, when setting an attribute to a value or calling a method, you do not need to prepend self, since that method/variable is already being called from that same scope.

Resources