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.
Related
I want to skip the default image that I assign to paperclip, only when remote_avatar is not null, that is, if the user starts a session with facebook that uses remote_avatar and in the opposite case that paperclip assigns a default image to my attachment :avatar
user.rb
# == Schema Information
#
# Table name: users
#
# id :integer not null, primary key
# email :string default(""), not null
# encrypted_password :string default(""), not null
# reset_password_token :string
# reset_password_sent_at :datetime
# remember_created_at :datetime
# sign_in_count :integer default(0), not null
# current_sign_in_at :datetime
# last_sign_in_at :datetime
# current_sign_in_ip :string
# last_sign_in_ip :string
# confirmation_token :string
# confirmed_at :datetime
# confirmation_sent_at :datetime
# username :string
# avatar_file_name :string
# avatar_content_type :string
# avatar_file_size :integer
# avatar_updated_at :datetime
# bio :text
# birthdate :date
# nationality :string
# gender :string
# created_at :datetime not null
# updated_at :datetime not null
# roles_mask :integer
# uid :string
# provider :string
# doctor_id :integer
# facebook :string
# remote_avatar :string
#
# Indexes
#
# index_users_on_confirmation_token (confirmation_token) UNIQUE
# index_users_on_doctor_id (doctor_id)
# index_users_on_email (email) UNIQUE
# index_users_on_reset_password_token (reset_password_token) UNIQUE
# index_users_on_username (username) UNIQUE
#
class User < ApplicationRecord
...
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable, :confirmable,
:recoverable, :rememberable, :trackable, :validatable, :omniauthable, :omniauth_providers => [:facebook]
validates :username, uniqueness: {message: "The username has already been taken."}
has_attached_file :avatar, styles:{thumb:"42x42",medium:"300x300"}, default_url:"/images/:style/user.png"
validates_attachment_content_type :avatar, content_type: /\Aimage\/.*\Z/
def self.from_omniauth(auth)
where(provider: auth[:provider], uid: auth[:uid]).first_or_create do |user|
if auth[:info]
...
user.facebook = auth[:extra][:raw_info][:link]
if auth[:info][:image]
user.remote_avatar = auth[:info][:image]
user.avatar = nil
end
...
end
end
end
end
Looks like you can make it easier:
<%= current_user.avatar? ? current_user.avatar.url(:medium) : '/images/medium/user.png' %>
Or you can to use helper, like:
def self.fetch_avatar(user)
return '/images/medium/user.png' unless user.avatar?
user.avatar.url(:medium)
end
ERB:
<%= SomethingHelper.fetch_avatar(current_user) %>
I am trying to add a user role according to Rolify gem in my devise User model.
What I basically want to achieve is that, if the user has selected that he is a student or a teacher in the registration page itself, after the creation of the user, it should add the required roles to the user.
Please note that I am not storing the 'role' in the User table. I am just using attr_accessor to send me an initial value to compare.
This is my User model code :
# == Schema Information
#
# Table name: users
#
# id :integer 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 default(0), not null
# current_sign_in_at :datetime
# last_sign_in_at :datetime
# current_sign_in_ip :string(255)
# last_sign_in_ip :string(255)
# created_at :datetime not null
# updated_at :datetime not null
# avatar :string(255)
# username :string(255)
#
class User < ActiveRecord::Base
# This is for the user roles.
rolify
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
attr_accessor :role
# Adding the carrierwave uploader
mount_uploader :avatar, AvatarUploader
before_create :add_specified_role
def add_specified_role
if self.role == 0
# I am sure I am messing it up here and this is not the way.. :/
after_create :add_student_role
elsif self.role == 1
after_create :add_teacher_role
end
end
def add_student_role
self.add_role(:student) if self.roles.blank?
end
def add_teacher_role
self.add_role(:teacher) if self.roles.blank?
end
end
However, it does not seem to be working as when I check the roles, the role has not been added and I am sure I am doing something wrong.
What is the correct way to achieve the above task?
callback should be
after_create :add_specified_role , if: proc { |a| a.roles.blank? }
instead of
before_create :add_specified_role
And method should
def add_specified_role
if role == 0
add_role(:student)
elsif role == 1
add_role(:teacher)
end
end
When I authenticate through my twitter sign in button, I'm getting this browser error
The action 'new' could not be found for RegistrationsController
I created a registrations_controller.rb to override the devise controller for some methods according to this guide http://www.orhancanceylan.com/rails-twitter-and-facebook-authentications-with-omniauth-and-devise/#comment-1375
Sorry to be verbose about the code. The the main part is in the authentications_controller.rb in the twitter method, in the else portion of the method.
That portion of the code governs when a user does not have other 3rd party signin authentications previously created AND not signed in via devise. Thus an authentication record and a user record must be created in that portion of the code. But I'm getting the error.
There is an authentications table so that I can add other omniauth strategies.
If unclear, or more information is needed, please let me know how I can improve the question.
routes.rb
devise_for :users, :path => '',
:path_names => { :sign_in => "login",
:sign_out => "logout",
:sign_up => "sign-up",
:account_update => "account-settings" },
:controllers => { omniauth_callbacks: "authentications", registrations: "registrations" }
get "/auth/:provider/callback", to: "authentications#:provider"
authentications_controller.rb
class AuthenticationsController < Devise::OmniauthCallbacksController
def twitter
omni = request.env["omniauth.auth"]
authentication = Authentication.find_by_provider_and_uid(omni['provider'], omni['uid'])
if authentication
# If already registered previously
flash[:notice] = "Logged in successfully."
user = User.find(authentication.user_id)
sign_in_and_redirect user
elsif current_user
# If signed in via any other strategy, including devise.
current_user.authentications.create!(:provider => omni['provider'],
:uid => omni['uid'],
:token => token = omni['credentials']['token'],
:token_secret => omni['credentials']['secret'])
flash[:notice] = "Authentication successful."
sign_in_and_redirect current_user
else # this is the conditional that gets executed.
# If brand new to the site.
user = User.new
user.apply_omniauth(omni)
if user.save
flash[:notice] = "Login successful."
sign_in_and_redirect User.find(user.id)
else
session[:omniauth] = omni.except('extra')
redirect_to new_user_registration_path
end
end
end
# Overrides
def create
super
session[:omniauth] = nil unless #user.new_record?
end
end
authentication.rb
# == Schema Information
#
# Table name: authentications
#
# id :integer not null, primary key
# user_id :integer
# provider :string(255)
# uid :string(255)
# token :string(255)
# token_secret :string(255)
# profile_page :string(255)
# created_at :datetime
# updated_at :datetime
#
class Authentication < ActiveRecord::Base
belongs_to :user
end
#
user.rb
# == Schema Information
#
# Table name: users
#
# id :integer not null, primary key
# created_at :datetime
# updated_at :datetime
# 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 default(0), not null
# current_sign_in_at :datetime
# last_sign_in_at :datetime
# current_sign_in_ip :inet
# last_sign_in_ip :inet
# username :string(255)
# admin :boolean default(FALSE)
# image :string(255)
# points :integer default(0), not null
# hacks_count :integer default(0), not null
# comment_threads_count :integer default(0), not null
#
# Indexes
#
# index_users_on_email (email) UNIQUE
# index_users_on_reset_password_token (reset_password_token) UNIQUE
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable,
:omniauthable, omniauth_providers: [ :twitter ]
has_many :hacks
has_many :comments
acts_as_voter
has_many :authentications
validates :username,
presence: true,
:uniqueness => { case_sensitive: false },
length: { in: 4..20 }
#
SOCIALS = {
twitter: 'Twitter',
facebook: 'Facebook'
}
#
def apply_omniauth(omni)
authentications.build(:provider => omni['provider'],
:uid => omni['uid'],
:token => token = omni['credentials']['token'],
:token_secret => omni['credentials']['secret'])
end
# Overrides
def update_with_password(params, *options)
if encrypted_password.blank?
update_attributes(params, *options)
else
super
end
end
def password_required?
super && (authentications.empty? || !password.blank?)
end
end
I was inheriting registrations_controller from the wrong controller.
It should be
class RegistrationsController < Devise::RegistrationsController
not
class RegistrationsController < ApplicationController
watch out, guys =]
This question already has an answer here:
Closed 10 years ago.
Possible Duplicate:
undefined method `to_f' for #<ActiveRecord::Relation:0x472d0a0>
I'm trying to make a call tracking application to learn twilio and rails.
Right now, I would like to make a graph that shows a user how many phone calls a particular phone number gets per day.
The schema is user has_many phones has_many calls.
I try to make the graph by creating an instance method that counts the number of phones on a particular day, but when I try executing the code, I get the error :
SQLite3::SQLException: no such column: calls.placed_at: SELECT COUNT(*) FROM "calls" WHERE "calls"."phone_id" = 44 AND ("calls"."placed_at" BETWEEN '2012-09-15 00:00:00.000000' AND '2012-09-15 23:59:59.999999')
I don't quite understand the code I'm using for the instance method, and it's probably calling the wrong column. Your help on this would be greatly appreciated.
Here's the important part of my call model:
def total_on(date)
calls.where(placed_at: date.beginning_of_day..date.end_of_day).count
end
Here's how I'm counting the phone calls in my show view
<%= (1.month.ago.to_date..Date.today).map { |date| #phone.total_on(date).to_f}.inspect %>
Here's how I define the #phone variable
#phone = Phone.find_by_id(params[:id])
Here's my complete phone model (for schema reference)
# == Schema Information
#
# Table name: phones
#
# id :integer not null, primary key
# name :string(255)
# twilio_number :integer
# original_number :integer
# user_id :integer
# created_at :datetime not null
# updated_at :datetime not null
#
class Phone < ActiveRecord::Base
attr_accessible :original_number, :user_id, :name, :twilio_number
belongs_to :user
has_many :calls, dependent: :destroy
validates :name, presence: true
validates :twilio_number, presence: true
validates :original_number, presence: true
validates :user_id, presence: true
default_scope order: 'phones.created_at DESC'
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
def original_number=(value)
num = value.to_s.gsub(/[^0-9+]/, "")
write_attribute(:original_number, num.to_i)
end
def total_on(date)
calls.where(placed_at: date.beginning_of_day..date.end_of_day).count
end
end
Here's my complete call model
# == Schema Information
#
# Table name: calls
#
# id :integer not null, primary key
# AccountSid :string(255)
# From :string(255)
# To :string(255)
# CallStatus :string(255)
# ApiVersion :string(255)
# Direction :string(255)
# FromCity :string(255)
# FromState :string(255)
# FromZip :string(255)
# FromCountry :string(255)
# ToCity :string(255)
# ToState :string(255)
# ToZip :string(255)
# ToCountry :string(255)
# CallSid :string(255)
# DialCallSid :string(255)
# DialCallDuration :string(255)
# DialCallStatus :string(255)
# RecordingUrl :string(255)
# phone_id :integer
# DialCallMinutes :integer
# created_at :datetime
# updated_at :datetime
#
class Call < ActiveRecord::Base
attr_accessible :AccountSid, :From, :To, :CallStatus, :ApiVersion, :Direction, :FromCity, :FromState, :FromZip, :FromCountry, :ToCity, :ToState, :ToZip, :ToCountry, :CallSid, :DialCallSid, :DialCallDuration, :DialCallStatus, :RecordingUrl, :DialCallMinutes
belongs_to :phone
def self.create_from_incoming_call(params)
user_phone = Phone.find_by_twilio_number(params['To']) #Finds the phone number in the database based on what phone Twilio is calling
twilio_request_params = {
:CallSid => params['CallSid'],
:AccountSid => params['AccountSid'],
:From => params['From'],
:To => params['To'],
:CallStatus => params['CallStatus'],
:ApiVersion => params['ApiVersion'],
:Direction => params['Direction'],
:FromCity => params['FromCity'],
:FromState => params['FromState'],
:FromZip => params['FromZip'],
:FromCountry => params['FromCountry'],
:ToCity => params['ToCity'],
:ToState => params['ToState'],
:ToZip => params['ToZip'],
:ToCountry => params['ToCountry']
:phone_id => user_phone.phone_id
}
call = Call.new(twilio_request_params)
call.save
return call
end
def Call.update_dial_call(params)
twilio_request_params = {
:DialCallSid => params['DialCallSid'],
:DialCallDuration => params['DialCallDuration'],
:DialCallStatus => params['DialCallStatus'],
:RecordingUrl => params['RecordingUrl'],
:DialCallMinutes => (params['DialCallDuration'].to_f/60.to_f).ceil
}
call = Call.where( :CallSid => params['CallSid'] ).first
call.update_attributes twilio_request_params
call.save
end
end
I've been stuck on this for a while; any help would be greatly appreciated!
Your call model uses the standard rails created_at, yet your query was using placed_at, which doesn't exist.
What I would like to happen is every single time a new file is uploaded, user.space_used is updated.
But that doesn't happen right now. In order to get an update, I have to manually run user.update_space at the console to update a specific user, or use an array to cycle through all the users and update it like that.
How do I get it to do it at the right time - also, it would be nice if I could verify that the space_used column on the User model has the total sum of the filesizes of all the files uploaded for that user on the Upload model.
My user model looks like this:
# == Schema Information
# Schema version: 20110412170916
#
# Table name: users
#
# id :integer not null, primary key
# email :string(255)
# encrypted_password :string(128)
# password_salt :string(255)
# reset_password_token :string(255)
# remember_token :string(255)
# remember_created_at :datetime
# sign_in_count :integer
# current_sign_in_at :datetime
# last_sign_in_at :datetime
# current_sign_in_ip :string(255)
# last_sign_in_ip :string(255)
# username :string(255)
# first_name :string(255)
# last_name :string(255)
# created_at :datetime
# updated_at :datetime
# invitation_token :string(60)
# invitation_sent_at :datetime
# plan_id :integer
# current_state :string(255)
# confirmation_token :string(255)
# confirmed_at :datetime
# confirmation_sent_at :datetime
# space_used :integer default(0), not null
# failed_attempts :integer default(0)
# unlock_token :string(255)
# locked_at :datetime
# trial_end_date :date
# active_subscription :boolean
#
class User < ActiveRecord::Base
acts_as_voter
devise :database_authenticatable, :confirmable, :registerable, :timeoutable,
:recoverable, :rememberable, :trackable, :validatable, :invitable, :lockable
attr_accessible :email, :password, :password_confirmation, :remember_me, :username, :first_name, :last_name, :plan_id
after_save :update_space
def role_symbols
roles.map do |role|
role.name.underscore.to_sym
end
end
def update_space
total_size = 0
if self.uploads.count > 0
self.uploads.each do |upload|
total_size += upload[:image_file_size]
end
end
self.space_used = total_size
end
def space_threshold_reached?
self.plan.storage == self.space_used
end
def space_left
(self.plan.storage * 1024 * 1024 * 1024) - self.space_used.to_f
end
end
My Upload model looks like this:
# == Schema Information
# Schema version: 20110330215959
#
# Table name: uploads
#
# id :integer not null, primary key
# name :string(255)
# description :string(255)
# image_file_name :string(255)
# image_content_type :string(255)
# image_file_size :integer
# image_updated_at :datetime
# stage_id :integer
# user_id :integer
# created_at :datetime
# updated_at :datetime
class Upload < ActiveRecord::Base
acts_as_voteable
has_attached_file :image, :styles => {
:thumb => "64x64" },
:storage => :s3,
:path => "/:style/:id/:filename"
validates_attachment_presence :image
validates_attachment_size :image, :less_than => 10.megabytes
validates_attachment_content_type :image, :content_type => ['image/jpeg', 'image/png', 'image/gif', 'image/jpg', 'image/JPG']
after_post_process :update_users_space_used
def self.total_used
total_size = 0
all.each do |upload|
total_size += upload.image_file_size
end
return total_size
end
def update_users_space_used
Authorization.current_user.space_used += self.image_file_size
end
end
Thanks.
Edit1: Btw, I am using paperclip to manage the uploads.
Edit2: In the Upload.rb model, I changed the before_save to the after_post_process callback for paperclip and it still doesn't work.
Seems like Paperclip's after_post_process callback would be what you want.