ActiveRecord - Find next user in group model - ruby-on-rails

I've four user models: Zone, Product, User, Group
I want to choose what Users can sell a Product in a Zone, this is what Group does, with a many to many relation to User and a foreign key to one Product and one Zone. So I have one group per pair Zone/Product. I will also need to set custom attributes on that many to many relation so I used has_many :through Sell (I was unable to find a better name to describe the relation between Group and User).
So I ended up having 5 models: Zone, Product, User, Group, Sell.
It works fine, but now I'd need to select the next user available in a Group.
I was thinking to exploit Sell.id to find the user assigned to the same group with an higher id, if not present choose the first one again (this allows me to create a ring chain).
It would be useful to have a Group.next_user method.
Unfortunatly I can't figure out how to do this, I'd need help to find the next user available in the group (or the 1st one if there are no more users).
Follows the code for models all the models:
################
# models/group.rb
################
class Group < ActiveRecord::Base
has_many :sells
has_many :users, :through => :sells
belongs_to :zone
belongs_to :product
attr_accessible :priority, :product_id, :user_ids, :zone_id
end
################
# models/zone.rb
################
class Zone < ActiveRecord::Base
belongs_to :location
has_many :cities
has_many :groups
attr_accessible :name, :location_id
validates :location, :presence => true
end
################
# models/user.rb
################
class User < ActiveRecord::Base
after_create :create_calendar
before_destroy :destroy_calendar
belongs_to :location
belongs_to :mall
has_one :event_calendar
has_many :sells
has_many :groups, :through => :sells
# Include default devise modules. Others available are:
# :token_authenticatable, :encryptable, :confirmable, :lockable, :timeoutable and :omniauthable, :registerable,
# :recoverable, :rememberable,
devise :database_authenticatable, :trackable,
:validatable, :authentication_keys => [:username]
# Setup accessible (or protected) attributes for your model
attr_accessible :username, :password, :password_confirmation, :remember_me, :name,
:surname, :role, :location_id
# attr_accessible :title, :body
ROLES = %w[Admin Agente Hostess HostessAdmin]
validates_uniqueness_of :username, :case_sensitive => false
validates :username, :presence => true
validates_presence_of :role, :name, :surname, :location_id
validates :location, :presence => true
validates :role, :inclusion => { :in => ROLES, :message => "%{value} non รจ un ruolo valido." }
def display_name
"#{self.name} #{self.surname}"
end
def has_role?(role)
# convert the role string to a sybmol
self.role.downcase.gsub(/\s+/, "_").to_sym == role
end
private
def create_calendar
if self.has_role? :agente
calendar = EventCalendar.new({:user_id => self.id})
calendar.save()
end
end
def destroy_calendar
if self.has_role? :agente
calendar = EventCalendar.find_by_user_id(self.id)
calendar.destroy()
end
end
def email_required?
false
end
def email_changed?
false
end
end
################
# models/product.rb
################
class Product < ActiveRecord::Base
after_create :create_groups
before_destroy :destroy_groups
attr_accessible :name
def create_groups
for zone in Zone.all
group = Group.new({:zone_id => zone.id, :product_id => self.id})
group.save()
end
end
def destroy_groups
for zone in Zone.all
group = Group.find_by_product_id(self.id)
group.destroy
end
end
end
################
# models/sell.rb
################
class Sell < ActiveRecord::Base
belongs_to :user
belongs_to :group
end
Can you give me some help to get this done? Thanks!

If I get this right then addd this to your User model
scope :next, lambda { |p| {:conditions => ["id > ?", p.id], :limit => 1, :order => "id"} }
and this to your group model
def self.next_user
return User.first if Group.users.blank?
next_user = User.next(Group.users.last).first
return next_user || Group.users.first
end
This should do the trick. I didn't write test for this so you should test it :)

Related

Rails - how to write a method that uses attributes from different models

I'm trying to figure out how to write a method in my user model, that shows the user's full name and the title of the organisation that the user is from, in one line.
In my user model, I have a method called full_name, which works to add the first name and last name attributes together. That part works.
Now, I'm trying to write a method called 'formal_title', which adds the user's full name to an organisation.title.
I have models for User and Organisation. The associations are:
User
belongs_to :organisation
Organisation
has_many :users
My organisation table has an attribute called :title.
My attempt at writing a method in my user model is:
def full_name
if first_name.present?
[*first_name.capitalize, last_name.capitalize].join(" ")
else
test_full_name
end
end
def organisation_title
Organisation.title.where(organisation.id == self.organisation_id).titleize
end
def formal_title
[*self.full_name, self.organisation_title].join(",")
end
When I try this, I get an error in the console that says:
NoMethodError: undefined method `formal_title' for #<User:0x007fea3495fe90>
This error message makes no sense to me because the user that I'm testing this on has a first name, last name and organisation id to test against. the organisation_id in the user table references an organisation that has a title, so I'm lost as to why any part of this method could be missing but also confused about what the console doesn't know about the method.
Can anyone see what I've done wrong?
Entire user model has:
class User < ApplicationRecord
rolify strict: true # strict means you get true only on a role that you manually add
attr_accessor :current_role
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable,
:confirmable, :lockable,
:omniauthable, :omniauth_providers => [ :linkedin, :twitter, :orcid ]
# --------------- associations
belongs_to :organisation, optional: true
has_one :device
has_many :identities, dependent: :destroy
has_one :profile, dependent: :destroy
has_one :setting, dependent: :destroy
has_one :org_request, dependent: :destroy
has_many :proposals, dependent: :destroy
# Conversations
has_many :authored_conversations, class_name: 'Conversation', foreign_key: 'author_id'
has_many :received_conversations, class_name: 'Conversation', foreign_key: 'received_id'
has_many :personal_messages, dependent: :destroy
# End Conversations
# teams
has_many :teams, foreign_key: "team_mate_id"
has_many :team_projects, through: :teams, source: :proposal
has_many :received_invitations, :class_name => "TeamInvitation", :foreign_key => 'recipient_id'
has_many :sent_invitations, :class_name => "TeamInvitation", :foreign_key => 'sender_id'
# --------------- scopes
# --------------- validations
validates :first_name, presence: { message: 'First name cannot be blank.' }
validates :last_name, presence: { message: 'Last name cannot be blank.' }
validates :email, format: { with: EMAIL_REGEX }
validates :email, uniqueness: { message: 'This email is already associated with an account. Try logging in.'}
# validates_format_of :first_name, with: /a-zA-Z/
# validates_format_of :last_name, with: /a-zA-Z/
# validates_length_of :password, within: 8..128
# --------------- class methods
def full_name
if first_name.present?
[*first_name.capitalize, last_name.capitalize].join(" ")
else
"test_full_name"
end
end
def organisation_title
# you don't need to do `Organisation.where...` here because you have defined the `belongs_to` association for `:organisation`. So, it will directly give you the `Organisation` object.
organisation.title.titleize
end
def formal_title
[*self.full_name, self.organisation_title].join(",")
end
# --------------- instance methods
def online?
true
# false
# !Redis.new.get("user_#{self.id}_online").nil?
end
end
I save my changes each time. then I reload! in the console.
Not enough. You should either restart the console or re-query the user.
u = User.find(u.id)
reload! doesn't affect already loaded objects, so the user you had there, it won't see the new method.
Please try to change your code like this and let me know what it gives:
def full_name
if first_name.present?
[first_name.capitalize, last_name.capitalize].join(" ")
else
test_full_name # will work if test_full_name is defined inside this class
end
end
def organisation_title
# you don't need to do `Organisation.where...` here because you have defined the `belongs_to` association for `:organisation`. So, it will directly give you the `Organisation` object.
organisation.title.titleize
end
def formal_title
[self.full_name, self.organisation_title].join(",")
end

NoMethodError: undefined method `encrypted_password devise gem

this is my user model:
class User < Account
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
attr_accessible :email, :password, :password_confirmation, :remember_me
devise :database_authenticatable,
:ga_otp_authenticatable,
:yk_otp_authenticatable,
:registerable,
:confirmable,
:recoverable,
:trackable,
:validatable,
:lockable,
:timeoutable
# Setup accessible (or protected) attributes for your model
attr_accessible :password, :password_confirmation, :remember_me, :time_zone, :require_ga_otp, :require_yk_otp, :full_name, :address
attr_accessor :captcha,
:skip_captcha,
:new_password,
:new_password_confirmation,
:current_password
before_validation :generate_name,
:on => :create
has_many :trade_orders,
:dependent => :destroy
has_many :purchase_trades,
:class_name => "Trade",
:foreign_key => "buyer_id"
has_many :sale_trades,
:class_name => "Trade",
:foreign_key => "seller_id"
has_many :invoices,
:dependent => :destroy
has_many :yubikeys,
:dependent => :destroy
has_many :bank_accounts,
:dependent => :destroy
has_many :tickets,
:dependent => :destroy
#has_many :tickets,
# :dependent => :destroy
validates :email,
:uniqueness => true,
:presence => true
validate :captcha do
if captcha.nil? and new_record?
unless skip_captcha
errors[:captcha] << I18n.t("errors.answer_incorrect")
end
end
end
def captcha_checked!
self.captcha = true
end
def bitcoin_address
super or (generate_new_address && super)
end
def qr_code
if #qrcode.nil?
#qrcode = RQRCode::QRCode.new(bitcoin_address, :size => 6)
end
#qrcode
end
def confirm!
super
UserMailer.registration_confirmation(self).deliver
end
protected
def self.find_for_database_authentication(warden_conditions)
conditions = warden_conditions.dup
name = conditions.delete(:name)
where(conditions).where(["name = :value OR email = :value", { :value => name }]).first
end
def generate_name
self.name = "BF-U#{"%06d" % (rand * 10 ** 6).to_i}"
end
end
This is my account model:
class Account < ActiveRecord::Base
has_many :account_operations
has_many :used_currencies,
:dependent => :destroy
belongs_to :parent,
:class_name => 'Account'
validates :name,
:presence => true,
:uniqueness => true
# BigDecimal returned here
def balance(currency, options = {} )
account_operations.with_currency(currency).with_confirmations(options[:unconfirmed]).with_processed_active_deposits_only.map(&:amount).sum.round(5).abs
end
# Generates a new receiving address if it hasn't already been refreshed during the last hour
def generate_new_address
unless last_address_refresh && last_address_refresh > DateTime.now.advance(:hours => -1)
self.last_address_refresh = DateTime.now
self.bitcoin_address = Bitcoin::Client.instance.get_new_address(id.to_s)
save
end
end
def max_withdraw_for(currency)
Transfer.round_amount(self.balance(currency), currency)
end
def self.storage_account_for(currency)
account_name = "storage_for_#{currency.to_s.downcase}"
account = find_by_name(account_name)
if account
account
else
Account.create! do |a|
a.name = account_name
end
end
end
end
My problem is I am trying to input the password but it is not accepting password field. It is giving error that password cant be blank.
I have tried to create user manually. here it is giving error undefined method encrypted password.
how to solve this problem?
Try out this.....
Do you have the encrypted_password column on your model?
Yes
attr_accessible :email, :password, :password_confirmation, :remember_me, :encrypted_password
Just because it's attr_accessable doesn't mean the attribute exists, just means you can access it (if it did exist). Go into your rails console and run:
User.new.respond_to?(:encrypted_password=)
If that returns true you have the column in your model, if not you need to make sure you run the correct migrations.
User.new.respond_to?(:encrypted_password=) => false
I run rake db:migrate:reset and it work!!!

Cannot map Employee back to User ID correctly

My problem is: I have a User model , Employee , Student, Parent. I don't know how to map each of Employee , Student, Parent back to User ID correctly. because in the current state for example I have differentiating by a normal user and an employee (since the employee is a user as well ) , How can I do so ? For example I will paste Employee and User and Ticket so you can help me to map it correctly because in my system Ticket model is not dealing with the Employee as a User . please I am stuck on this two days ago :(
In those models , user_id in the ticket model is refering to creator of tickets and employee id is refering to assigned employee and thats the error i need to map Employee back to User ID correctly
User.rb
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :token_authenticatable, :encryptable, :validatable,:confirmable and :omniauthable
devise :database_authenticatable, :registerable, :recoverable, :rememberable,
:trackable, :lockable, :timeoutable
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :user_name, :first_name, :last_name, :password, :password_confirmation, :remember_me,
:role_ids, :current_password, :user_type_id
attr_accessor :current_password
# attr_accessible :title, :body
has_many :assignments
has_many :roles, :through => :assignments
has_many :articles
has_many :comments
has_many :students
has_many :guardians
has_many :employees
has_many :tickets
has_many :permissions
belongs_to :user_type
accepts_nested_attributes_for :tickets
def has_role?(role_sym)
roles.any? { |r| r.role_name.underscore.to_sym == role_sym }
end
end
Employee.rb
class Employee < ActiveRecord::Base
# attr_accessible :title, :body
after_create :add_to_users
attr_accessible :employee_number, :joining_date, :first_name, :middle_name, :last_name,
:gender, :job_title, :employee_department_id, :qualification, :experience_detail,
:experience_year, :experience_month, :status_description, :date_of_birth, :marital_status,
:children_count, :father_name, :mother_name, :husband_name, :blood_group, :nationality_id,
:home_address_line1, :home_address_line2, :home_city, :home_state, :home_pin_code,
:office_address_line1, :office_address_line2, :office_city, :office_state, :office_pin_code,
:office_phone1, :office_phone2, :mobile_phone, :home_phone, :email, :fax, :user_id, :school_id,
:employee_category_id, :employee_position_id, :reporting_manager_id, :employee_grade_id,
:office_country_id, :home_country_id
belongs_to :employee_department
belongs_to :employee_category
belongs_to :employee_position
belongs_to :employee_grade
belongs_to :nationality, class_name: 'Country'
belongs_to :reporting_manager, class_name: "Employee"
belongs_to :school
belongs_to :user
has_many :tickets
def add_to_users
new_user = User.new
new_user.user_name = self.first_name
new_user.first_name = self.first_name
new_user.last_name = self.last_name
new_user.email = self.email
new_user.password = "123456"
new_user.password_confirmation = "123456"
new_user.user_type_id = 2
new_user.save
t = Employee.find(self.id)
t.user_id = new_user.id
t.save
end
def to_label
full_name = first_name + " " + last_name
end
def full_name
full_name = first_name + " " + last_name
end
end
Ticket.rb
class Ticket < ActiveRecord::Base
before_save :default_values
after_commit :close_solved
after_commit :close_canceled
before_create :assign_state
attr_accessible :description, :title, :employee_department_id, :user_id, :first_name,
:last_name , :email, :state_id, :employee_id, :ticket_state, :assign_state
belongs_to :employee_department
belongs_to :user
belongs_to :state
belongs_to :employee
has_many :replies
def default_values
self.state_id = 3 if self.state_id.nil?
end
def to_label
ticket_state.to_s
end
def close_solved
if self.ticket_state == "solved"
self.update_column(:ticket_state, "closed (solved)")
self.save!
end
end
def close_canceled
if self.ticket_state == "canceled"
self.update_column(:ticket_state, "closed (canceled)")
self.save!
end
end
def assign_state
if self.employee_id.nil?
self.assign_state = "un-assigned"
else
self.assign_state = "assigned"
end
end
Ticket.all.each do |ticket|
if ticket.ticket_state.blank?
ticket.ticket_state = 'open'
end
ticket.save
end
end

What's an efficient way of associating Post, Comment, User and Vote models in Rails?

Right now, I have three models Post, Comment and User (using Devise) associated as follows:
post.rb:
class Post < ActiveRecord::Base
attr_accessible :title, :content, :total_votes
validates :title, :presence => true,
:length => { :maximum => 30 },
:uniqueness => true
validates :content, :presence => true,
:uniqueness => true
belongs_to :user
has_many :comments, :dependent => :destroy
end
comment.rb:
class Comment < ActiveRecord::Base
attr_accessible :content, :user_id
belongs_to :post, :counter_cache => true
belongs_to :user
end
user.rb:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :token_authenticatable, :encryptable, :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable,
:omniauthable
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation, :remember_me, :username
validates_presence_of :username
has_many :posts, :dependent => :destroy
has_many :comments, :dependent => :destroy
def self.find_for_facebook_oauth(access_token, signed_in_resource=nil)
data = access_token.extra.raw_info
if user = User.where(:email => data.email).first
user
else # Create a user with a stub password.
User.create!(:email => data.email, :password => Devise.friendly_token[0,20])
end
end
end
I want to add a fourth model called Vote with the following conditions:
Both posts and comments can be voted (up and down) and show the total/sum.
Each post will have many votes (up and down) and show the total/sum.
Each comment will have many votes
The ID of the user should be stored each time he or she votes so I can restrict one vote per user and show the ID/name of the users who voted (not sure where to store it)
Now, I'm not sure if this is a good occasion to use polymorphic associations and/or counter cache.
What's an efficient way of associating these Post, Comment, User and Voting models?
(If possible, I would like to see how the migration would look like)
This is a perfect textbook example of where a polymorphic association would be useful.
Your votes table migration would look like this:
create_table :votes do |t|
t.references :votable, :polymorphic => true
t.references :user
t.integer :polarity
t.integer :total
end
This would create a table with this schema:
id INTEGER
votable_id INTEGER
votable_type VARCHAR
user_id INTEGER
polarity INTEGER
total INTEGER
Here, user_id would be the person who cast the vote, polarity would be either '1' for an upvote or '-1' for a downvote (this lets you just sum the polarities to get upvotes and downvotes to cancel), votable_type would contain what the vote is for (Post or Comment), votable_id would contain the id of the thing the vote is for, and total would keep a running total of the vote sum (for efficiency).
Then your models would look like this:
class Vote < ActiveRecord::Base
belongs_to :votable, :polymorphic => true
belongs_to :user
before_create :update_total
protected
def update_total
self.total ||= 0
self.total += self.polarity
end
end
class Post < ActiveRecord::Base
has_many :votes, :as => :votable
end
class Comment < ActiveRecord::Base
has_many :votes, :as => :votable
end
class User < ActiveRecord::Base
has_many :votes
end

How do I pull a list of models (events) through a relationship with another model (Users)?

This is a bit complicated and I'm not sure how to implement it. I have a User model and a Relationship model. Users are able to "follow" each other (just like twitter). The relationship model is all setup properly and works great.
Next, I have an Event model. Each user has_and_belongs_to_many events (many to many association between users and events). Users "attend" events.
What I would like to do is pull a list of all events that are
being attended by the current_user
are being attended by users that current_user is following.
If possible, I would like to have this list accessible via the User model so I can say current_user.event_feed and it will list all events as mentioned above.
Here are my models:
class Event < ActiveRecord::Base
attr_accessible :name,
:description,
:event_date,
:location,
:owner_id,
:category,
:photo
CATEGORIES = ['Music', 'Outdoors', 'Party']
has_and_belongs_to_many :users
and relationship model:
class Relationship < ActiveRecord::Base
attr_accessible :followed_id
belongs_to :follower, :class_name => "User"
belongs_to :followed, :class_name => "User"
validates :follower_id, :presence => true
validates :followed_id, :presence => true
end
and user model:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :token_authenticatable, :encryptable, :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
attr_accessible :email, :password, :password_confirmation, :remember_me
attr_accessor :password
attr_accessible :name, :email, :password, :password_confirmation, :time_zone
has_and_belongs_to_many :events
has_many :relationships, :dependent => :destroy,
:foreign_key => "follower_id"
has_many :reverse_relationships, :dependent => :destroy,
:foreign_key => "followed_id",
:class_name => "Relationship"
has_many :following, :through => :relationships,
:source => :followed
has_many :followers, :through => :reverse_relationships,
:source => :follower
Thanks!
This is rails 3 only, but quite elegant (untested, hopefully my memory of habtm relationships is ok).
class User < ActiveRecord::Base
# ...
def event_feed
ids = self.followers.collect(&:id) << self.id
Event.includes(:users).where(["`users`.id IN (#{ids.join(',')})"])
end
# ...
end
Event Model:
scope :attended, where("event_date < #{Date.today}")
User Model:
# Returns collection of events that are attended by user and users she follows
def attended events
attended_events = []
attended_events << events.attended
followers.each do |follower|
attended_events << follower.events.attended
end
attended_events
end
1) being attended by the current_user and
This can be achieved simply by calling current_user.events
2) are being attended by users that current_user is following.
This is a little trickier. You want to end up with a flattened list of other user's events: current_user.following.collect { |friend| friend.events }.flatten #=> returns an array of followers' events
Since you want to display all events in a single list (from what I could gather), I think a presenter class would be useful:
class EventFeed
attr_accessor :event, :display_name
def initialize(event, name)
self.event = event
self.name = name
end
end
And now, adding them together to get to current_user.event_feed
class User
def event_feed; []; end
end
And gluing it all together:
current_user.events.each { |e| current_user.event_feed << EventFeed.new(e, 'YOU') }
current_user.following.each do |friend|
friend.events.each { |e| current_user.event_feed << EventFeed.new(e, friend.name) }
end
current_user.event_feed #=> an array of EventFeed objects where you can display "You are going to #{event.name}"
Of course this is pseudo code, but it should get you on the right track

Resources