NoMethodError undefined method `messages' - ruby-on-rails

I have sidekiq running and I am getting a NoMethodError undefined method `messages' in my log for the worker. I have everything defined so I am not sure what I am doing wrong.
class UserMessageWorker
include Sidekiq::Worker
def perform(message_id, recipient_id)
message = Message.find(message_id)
user = User.find(recipient_id)
message.read_at = Time.now
old_msg_count = user.messages.count
usermessages = user.received_messages.where("read_at IS NOT NULL").count + user.sent_messages.where("read_at IS NOT NULL").count
if message.save
msg_response_time = message.read_at - message.created_at
readmessages = []
usermessages.each do |um|
if um.read_at != nil
readmessages << um
end
end
response_rate = usermessages/(old_msg_count + 1)
response_time = ((user.average_response_time * old_msg_count)+msg_response_time)/(old_msg_count + 1)
user.update_attributes(:response_rate => response_rate, :average_response_time => average_response_time )
end
end
end
Messages controller:
def show
#reply_message = Message.new
#message = Message.find(params[:id])
if #message.recipient == current_user
UserMessageWorker.perform_async(#message.id, current_user.id)
end
#message.readingmessage if #message.recipient == current_user
end
Messages model:
attr_accessible :subject, :conversation_id, :body, :parent_id, :sender_id, :recipient_id, :read_at,:sender_deleted,:recipient_deleted
validates_presence_of :subject, :message => "Please enter message title"
has_many :notifications, as: :event
belongs_to :conversation, inverse_of: :messages
belongs_to :user
scope :unread, -> {where('read_at IS NULL')}
scope :not_deleted_by_recipient, where('messages.recipient_deleted IS NULL OR messages.recipient_deleted = ?', false)
scope :not_deleted_by_sender, where('messages.sender_deleted IS NULL OR messages.sender_deleted = ?', false)
belongs_to :sender,
:class_name => 'User',
:foreign_key => 'sender_id'
belongs_to :recipient,
:class_name => 'User',
:foreign_key => 'recipient_id'
def reply
new_message.reply_from_user_id = self.id #save the user id of original repost, to keep track of where it originally came from
end
def self.by_date
order("created_at DESC")
end
# marks a message as deleted by either the sender or the recipient, which ever the user that was passed is.
# When both sender and recipient marks it deleted, it is destroyed.
def mark_message_deleted(id,user_id)
self.sender_deleted = true if self.sender_id == user_id
self.recipient_deleted = user_id if self.recipient_id == user_id
(self.sender_deleted > 0 && self.recipient_deleted > 0) ? self.destroy : self.save!
(self.sender_deleted != 0 && self.recipient_deleted != 0)
end
# Read message and if it is read by recipient then mark it is read
def readingmessage
self.read_at ||= Time.now
save
end
# Based on if a message has been read by it's recipient returns true or false.
def read?
self.read_at.nil? ? false : true
end
def self.received_by(user)
where(:recipient_id => user.id)
end
def self.not_recipient_deleted
where("recipient_deleted = ?", false)
end
def self.sent_by(user)
Message.where(:sender_id => user.id)
end
def next(same_recipient = true)
collection = Message.where('id <> ? AND created_at > ?', self.id, self.created_at).order('created_at ASC')
collection.where(recipient_id: self.recipient_id) if same_recipient
collection.first
end
def previous(same_recipient = true)
collection = Message.where('id <> ? AND created_at < ?', self.id, self.created_at).order('created_at DESC')
collection.where(recipient_id: self.recipient_id) if same_recipient
collection.first
end
end
private
def send_notification(message)
message.notifications.create(user: message.recipient)
end
User model:
has_secure_password
attr_accessible :role, :name, :time_zone, :code, :lat, :lon, :city, :age, :age_end, :password_confirmation, :about_me, :feet, :inches, :password, :birthday, :career, :children, :education, :email, :ethnicity, :gender, :height, :name, :password_digest, :politics, :religion, :sexuality, :user_drink, :user_smoke, :username, :zip_code
# this prevented user from registering as I don't have timezone select on user reg form
# validates_inclusion_of :time_zone, in: ActiveSupport::TimeZone.zones_map(&:name)
has_many :photos
has_many :letsgos, dependent: :destroy
belongs_to :default_photo, :class_name => "Photo"
has_many :notifications
has_many :questions
belongs_to :location
belongs_to :zip
has_many :messages
belongs_to :avatar, class_name: 'Photo'
has_many :received_messages, class_name: 'Message', foreign_key: 'recipient_id'
has_many :sent_messages, class_name: 'Message', foreign_key: 'sender_id'
has_many :users, dependent: :destroy
has_many :relationships, foreign_key: "follower_id", dependent: :destroy
has_many :followed_users, through: :relationships, source: :followed
has_many :reverse_relationships, foreign_key: "followed_id", class_name: "Relationship", dependent: :destroy
has_many :followers, through: :reverse_relationships, source: :follower
validates_format_of :zip_code,
with: /\A\d{5}-\d{4}|\A\d{5}\z/,
message: "should be 12345 or 12345-1234"
validates_uniqueness_of :email
validates_format_of :email, :with => /\A([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i, :on => :create
validates_uniqueness_of :username
validates_presence_of :username
validates_format_of :username, :with => /\A[a-zA-Z0-9]+\Z/, :message => "should only contain letters or numbers"
validates :password, :presence => true,
:confirmation => true,
:length => {:within => 6..40},
:on => :create
before_create { generate_token(:auth_token) }
ROLES = %w[admin user guest banned]
def received_messages
Message.received_by(self)
end
def unread_messages?
unread_message_count > 0 ? true : false
end
def unread_messages
received_messages.where('read_at IS NULL')
end
def sent_messages
Message.sent_by(self)
end
def deleted_messages
Message.where(recipient_deleted: self)
end
# Returns the number of unread messages for this user
def unread_message_count
eval 'messages.count(:conditions => ["recipient_id = ? AND read_at IS NULL", self.user_id])'
end
Log:
2014-02-05T20:46:24Z 36507 TID-107jr10 WARN: {"retry"=>true, "queue"=>"default", "class"=>"UserMessageWorker", "args"=>[152, 1], "jid"=>"68109564031c679162dda497", "enqueued_at"=>1391632954.347344, "error_message"=>"Mysql2::Error: Unknown column 'messages.user_id' in 'where clause': SELECT COUNT(*) FROM `messages` WHERE `messages`.`user_id` = 1", "error_class"=>"ActiveRecord::StatementInvalid", "failed_at"=>"2014-02-05T20:42:34Z", "retry_count"=>3, "retried_at"=>2014-02-05 20:46:24 UTC}
2014-02-05T20:46:24Z 36507 TID-107jr10 WARN: Mysql2::Error: Unknown column 'messages.user_id' in 'where clause': SELECT COUNT(*) FROM `messages` WHERE `messages`.`user_id` = 1

What does you User model looks like?
EDIT:
You might need to add
usermessages = user.received_messages + user.sent_messages
to you worker and edit the response_rate line to this
readmessages = []
usermessages.each do |um|
if um.read_at != nil
readmessages << um
end
end
response_rate = (readmessages.count)/(old_msg_count + 1)
Let me know if this help. You might need to tweak it a little to work correctly but that should fix it
EDIT:
Or even better i think you can do this
usermessages = user.received_messages.where("read_at IS NOT NULL").count + user.sent_messages.where("read_at IS NOT NULL").count
and edit this line:
response_rate = usermessages/(old_msg_count + 1)
FULL WORKING CLASS:
class UserMessageWorker
include Sidekiq::Worker
def perform(message_id, recipient_id)
message = Message.find(message_id)
user = User.find(recipient_id)
message.read_at = Time.now
old_msg_count = user.received_messages.count + user.sent_messages.count
usermessages = user.received_messages.where("read_at IS NOT NULL").count + user.sent_messages.where("read_at IS NOT NULL").count
if message.save
msg_response_time = message.read_at - message.created_at
response_rate = usermessages/(old_msg_count + 1)
response_time = ((user.average_response_time * old_msg_count)+msg_response_time)/(old_msg_count + 1)
user.update_attributes(:response_rate => response_rate, :average_response_time => average_response_time )
end
end
end

Related

How to order a child of has_one relationship by attributes inside a controller using Rails 5?

I have 2 models as a child from has_one relationship like this:
class HotelThai < ApplicationRecord
belongs_to :hotel
#validates :hotel_id, presence: true
validates :hotel_name, presence: true
validates :description, presence: true
end
class HotelEng < ApplicationRecord
belongs_to :hotel
validates :hotel_name, presence: true
validates :description, presence: true
end
And this is my Parent Model:
class Hotel < ApplicationRecord
mount_uploader :logo, PromotionImageUploader
mount_uploader :image_url, PromotionImageUploader
mount_uploader :banner, PromotionImageUploader
validates :logo, presence: true
validates :image_url, presence: true
validates :banner, presence: true
validates :hotel_link, presence: true
has_one :hotel_thai ,dependent: :destroy
has_one :hotel_eng ,dependent: :destroy
validates_associated :hotel_thai
validates_associated :hotel_eng
end
Then on the controller i need to get a hotel data order by child hotel_thai or hotel_eng (depending on current language) with hotel_name attribute.
I tried it with this:
def index
#normal_hotel = nil
if I18n.locale == :en
#normal_hotel = Hotel.where(is_highlight: false).order('hotel_eng.hotel_name ASC').paginate(:page => params[:page], :per_page => 10)
elsif I18n.locale == :th
#normal_hotel = Hotel.where(is_highlight: false).order('hotel_thai.hotel_name ASC').paginate(:page => params[:page], :per_page => 10)
else
#normal_hotel = Hotel.where(is_highlight: false).order('hotel_eng.hotel_name ASC').paginate(:page => params[:page], :per_page => 10)
end
normal_hotel_group = #normal_hotel.in_groups(2)
#left_normal_hotel = normal_hotel_group[0]
#right_normal_hotel = normal_hotel_group[1]
#highlight_hotel = Hotel.where(is_highlight: true)
highlight_hotel_group = #highlight_hotel.in_groups(2)
#left_highlight_hotel = highlight_hotel_group[0]
#right_highlight_hotel = highlight_hotel_group[1]
end
But this is what I got:
PG::UndefinedTable: ERROR: missing FROM-clause entry for table "hotel_eng" LINE 1: ...tels" WHERE "hotels"."is_highlight" = $1 ORDER BY hotel_eng.... ^ : SELECT "hotels".* FROM "hotels" WHERE "hotels"."is_highlight" = $1 ORDER BY hotel_eng.hotel_name ASC LIMIT $2 OFFSET $3
So does anyone know how to fix this, or another way to make this work?
You need to join related tables if you want to use order by theirs columns. Use joins or includes methods for this
def index
hotel = I18n.locale == :th ? :hotel_thai : :hotel_eng
#normal_hotel = Hotel.where(is_highlight: false).
joins(hotel).order("#{hotel.to_s.pluralize}.hotel_name ASC").
paginate(page: params[:page], per_page: 10)
normal_hotel_group = #normal_hotel.in_groups(2)
...
end

undefined method `last_comment_at=' for #<BlogPost:0x0000000adb9110>

I create new post:
def create
params[:blog_post][:draft] = params[:draft].present?
#post = current_user.blog_posts.new(blog_post_params)
#post.save!
redirect_to #post, notice: (#post.draft? ? 'Черновик сохранен' : 'Пост опубликован')
rescue ActiveRecord::RecordInvalid
flash[:error] = ":("
render :new
end
When i create new post, displaying error:
undefined method `last_comment_at=' for #<BlogPost:0x0000000adb9110>
app/models/blog_post.rb:86:in `set_last_comment_at'
app/controllers/blog_posts_controller.rb:25:in `create'
config/initializers/flash_session_cookie_middleware.rb:19:in `call'
Model BlogPost:
class BlogPost < ActiveRecord::Base
# attr_accessible :subject, :body, :tag_list, :commentable_by, :visible_by, :attachments_attributes
include ActiveModel::ForbiddenAttributesProtection
belongs_to :user
has_many :comments, as: :owner, dependent: :destroy
has_many :attachments, as: :owner, dependent: :destroy
has_many :photos, through: :attachments, source: :asset, source_type: 'Photo'
has_many :videos, through: :attachments, source: :asset, source_type: 'Video'
belongs_to :article
has_many :blog_post_subscriptions, dependent: :destroy
has_many :subscribers, through: :blog_post_subscriptions, class_name: 'User', source: :user
has_one :poll, as: :owner, dependent: :destroy
has_many :poll_items, dependent: :destroy
accepts_nested_attributes_for :attachments, :allow_destroy => true
accepts_nested_attributes_for :poll, allow_destroy: true,
reject_if: proc { |attributes| attributes['question'].blank? }
validates :user, :subject, :presence => true
validates :body, presence: true, if: :body_required?
validates :body, length: { maximum: 65000 }
validate :validate_duplicate, on: :create
def body_required?
!article.present?
end
# after_create :bonus_for_blog_post
# after_create :notify_subscribers
before_create :check_paid_attributes
after_create :set_last_comment_at
def notify_subscribers!
unless draft?
Resque.enqueue BlogPostNotifier, self.id
end
end
after_save :set_published_at
def set_published_at
if published_at.nil? and !draft?
update_column :published_at, Time.now
bonus_for_blog_post!
notify_subscribers!
end
end
define_index do
indexes subject
indexes body
indexes tags_line
indexes user.username, as: :blog_post_author
has created_at
has published_at
where "draft=0"
group_by 'subject, body, tags_line, blog_posts.published_at, users.username'
set_property delta: ThinkingSphinx::Deltas::ResqueDelta
end
def to_s
subject || "[без заголовка]"
end
scope :drafts, where(draft: true)
scope :public, lambda { where(draft: false).where(:published_at.lte => Time.now) }
scope :with_privacy, lambda { |u|
unless u.moderator?
friend_ids = u.friend_ids + [u.id]
public.where(' blog_posts.visible_by IS NULL OR visible_by = "all" OR ' +
'(blog_posts.visible_by = "me" AND user_id = ?) OR' +
'(blog_posts.visible_by = "friends" AND user_id IN (?)) OR ' +
'(blog_posts.visible_by = "fof" AND EXISTS ' +
'(SELECT id FROM friendships WHERE friendships.user_id = blog_posts.user_id AND ' +
'friendships.friend_id IN (?) LIMIT 1)) OR ' +
'(blog_posts.visible_by = "bl" AND NOT EXISTS ' +
'(SELECT id FROM black_list_items WHERE black_list_items.owner_type="User" AND black_list_items.owner_id=blog_posts.user_id AND black_list_items.blocked_user_id=?))', u.id, friend_ids, friend_ids, u.id)
end
}
def set_last_comment_at
self.last_comment_at = created_at
save
end
acts_as_taggable
scope :tagged, lambda {|tag| tagged_with(tag) if tag }
before_save :set_tags_line
def set_tags_line
self.tags_line = tag_list.join(', ')
end
def user_can_edit?(user)
self.user.id == user.id or user.moderator?
end
def user_can_comment?(u)
u.can_comment_blog_post? self
end
protected
def bonus_for_blog_post!
unless draft?
user.bonus(:blog_post_bonus)
end
end
def check_paid_attributes
unless user.paid?
self.commentable_by = self.visible_by = 'all'
end
end
def validate_duplicate
errors.add(:base, :duplicate) unless user.blog_posts.where(body: body, article_id: article_id, :created_at.gt => 1.minute.ago).empty?
end
private
after_update :expire_cache
def expire_cache
expire_fragment "#{dom_id}_body"
expire_fragment "#{dom_id}_body_short"
expire_fragment "#{dom_id}_attachments"
Rails.cache.delete "#{dom_id}_tags"
end
before_save :emoji
def emoji
self.body = Rumoji.encode self.body
end
end
I think, problem is here:
def set_last_comment_at
self.last_comment_at = created_at
save
end
But why can't it find the method last_comment_at?
I fixed issue, i run migrate and added new column last_comment_at to table BlogPost

Showing all followers

How to show up all followers that I have?
I have made following possible, but I don't know how to show users that are following. Also I don't know how will I show posts from all the people "I'm following".
#/app/controllers/followers_controller.rb
class FollowersController < ApplicationController
def index
#user = current_user
#followers = Follower.where(following_id: params[:user_id])
end
def create
if current_user
#user = User.find(params[:user_id])
#follower = Follower.new(follower_params)
#follower.user_id = current_user.id
#follower.following_id = #user.id
if #user != current_user
#follower.save
end
redirect_to root_url
end
end
def follower_params
params.require(:follower).permit(:user_id, :following_id)
end
end
#/app/views/followers/index.html.erb
<% #followers.each do |f| %>
<b><%= f.user_id %></b>
<br/>
<% end %>
#/app/models/follower.rb
class Follower < ActiveRecord::Base
validates_uniqueness_of :following_id, scope: :user_id
belongs_to :user
end
#/app/models/user.rb
class User < ActiveRecord::Base
attr_accessor :password
before_save :encrypt_password
before_save { self.username = username.downcase }
before_save { self.email = email.downcase }
validates_confirmation_of :password
validates_presence_of :password, on: :create, length: { minimum: 8 }
validates :email, presence: true, uniqueness: { case_sensitive: false }, length: { maximum: 255 }
validates :username, presence: true, length: { minimum: 6, maximum: 30 }, uniqueness: { case_sensitive: false }
validates :bio, length: { maximum: 140 }
has_many :posts, dependent: :destroy
has_many :comments, dependent: :destroy
has_many :likes, dependent: :destroy
has_many :followers, dependent: :destroy
has_attached_file :avatar,
styles: {
thumb: '75x75#',
small: '150x150#'
}
validates_attachment_content_type :avatar, content_type: /\Aimage\/.*\Z/
has_attached_file :banner,
styles: {
thumb: '75x75>',
small: '150x150>'
}
validates_attachment_content_type :banner, content_type: /\Aimage\/.*\Z/
def self.authenticate(username, password)
user = find_by_username(username)
if user && user.password_hash == BCrypt::Engine.hash_secret(password, user.password_salt)
user
end
end
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
So, there is a little adjustment to be made to your model's setup.
Since a follower is also a user, It means that your Follower(which I will rename to Following should be a join table.)
class Following < ActiveRecord::Base
belongs_to :leader, class_name: 'User'
belongs_to :follower, class_name: 'User'
end
and your user model:
class User < ActiveRecord::Base
has_many :followings, foreign_key: :follower_id,
dependent: :destroy
has_many :leaders, through: :followings
has_many :reverse_followings, foreign_key: :leader_id,
class_name: 'Following',
dependent: :destroy
has_many :followers, through: :reverse_followings
end
Note that you will have to facilitate this by changing your migration to include the required columns.
Then you should be able to call user.followers
and run migrations.
Hope this helps?

rails 3 custom validation error messages in a join table, how?

How can I return errors messages from a cross reference table with multiple records when I trying to create those? I'm trying this:
## activity_set.rb
class ActivitySet < ActiveRecord::Base
has_many :activity_set_lessons
has_many :lessons, :through => :activity_set_lessons
validates :name, :presence => true
def activity_set_lessons=(data)
data.each_with_index do |v, i|
activity_set_lessons.build(
:lesson_id => v[:lesson_id],
:sort_order => i,
:weight_percentage => v[:weight_percentage]
)
end
end
end
## activity_set_lesson.rb
class ActivitySetLesson < ActiveRecord::Base
belongs_to :activity_set
belongs_to :lesson
validates :lesson_id, :presence => true
validates_each :weight_percentage do |record, attr, value|
record.errors.add :base, "woot" if value.blank?
end
end
This is the request data:
## params[:activity_set]
"activity_set" => {
"name" => "hshshshs",
"keywords" => "",
"activity_set_lessons" => [
{"weight_percentage" => "", "lesson_id"=>"4"},
{"weight_percentage" => "", "lesson_id"=>"5"}
]
}
Error messages from #activity_set when I do #save:
{
"errors":{
"activity_set_lessons":["is invalid","is invalid"]
},
"full_messages":[
"Activity set lessons is invalid","Activity set lessons is invalid"
]
}
I always got the same error message even if I'm adding a custom one in the join table. How can I return a message like: "woot 1 is wrong" or something like that, per validation?.
Thanks.
make use of accepts_nested_attributes_for
## activity_set.rb
class ActivitySet < ActiveRecord::Base
has_many :activity_set_lessons
has_many :lessons, :through => :activity_set_lessons
validates :name, :presence => true
accepts_nested_attributes_for :activity_set_lessons
end
view will look like
= form_for #activity_set do |f|
[activity_set form fields ]
= f.fields_for :activity_set_lessons do |p|
= p.select :lession_id
= p.select :weight_percentage

Rails 3 error: Object doesn't support #inspect on includes (left outer join)

I'm using Rails 3.
I have 3 models:
class Deal < ActiveRecord::Base
has_many :wishes, :class_name => "Wish"
has_many :wishers, :through => :wishes, :source => :user
end
class User < ActiveRecord::Base
has_many :wishes, :class_name => "Wish", :conditions => { 'wishes.wished' => true }
has_many :wished_deals, :through => :wishes, :source => :deal
end
class Wish < ActiveRecord::Base
belongs_to :user
belongs_to :deal
end
And i'm trying to create the following scope in the Deal model:
scope :not_wished_by_user, lambda { |user| includes(:wishes).where('wishes.wished' != true, 'wishes.user_id' => user) }
What i want is all the Deals, except those that are marked as 'wished' by the given user in the block. But whenever i do that includes, i get the following error:
ruby-1.9.2-head > u = User.all.first
ruby-1.9.2-head > Deal.not_wished_by_user(u)
(Object doesn't support #inspect)
=>
Also, placing it in a function doesn't work. Any idea what this could be?
Thanks!
EDIT: These are Wishes table migration
class CreateWish < ActiveRecord::Migration
def self.up
create_table :wishes do |t|
t.integer :deal_id
t.integer :user_id
t.boolean :wished, :default => true
t.boolean :collected, :default => false
t.datetime :collected_date
t.timestamps
end
add_index :wishes, [:deal_id, :user_id], :uniq => true
end
end
See Update below vv
Old answer
You are not using any Deal attributes for selects so try to move code into Wish class:
class Wish < ActiveRecord::Base
belongs_to :user
belongs_to :deal
scope :'wished?', lambda{ |f| where('wished = ?', f) }
scope :not_wished_by_user, lambda{|user| wished?(false).where('user_id = ?', user)}
end
Usage exmple and output:
ruby-1.9.2-p180 :023 > Wish.not_wished_by_user(User.first).to_sql
=> "SELECT \"wishes\".* FROM \"wishes\" WHERE (wished = 't') AND (user_id = 1)"
Is this correct result for you?
PS:
In the Deal you can leave proxy-method like:
class Deal < ActiveRecord::Base
has_many :wishes, :class_name => "Wish"
has_many :wishers, :through => :wishes, :source => :user
def self.not_wished_by_user(user)
Wish.not_wished_by_user(user)
end
end
Update1 (subquery)
class Deal < ActiveRecord::Base
has_many :wishes, :class_name => "Wish"
has_many :wishers, :through => :wishes, :source => :user
scope :deal_ids_not_wished_by_user, lambda { |user|
joins(:wishes).where('wishes.user_id = ?', user).where('wishes.wished = ?', false).select('deals.id')
}
scope :wished_by_user, lambda { |user|
where("id not in (#{Deal.deal_ids_not_wished_by_user(user).to_sql})")
}
end
Usage example and output:
ruby-1.9.2-p180 :023 > Deal.wished_by_user(User.first).to_sql
=> "SELECT \"deals\".* FROM \"deals\" WHERE (id not in (SELECT deals.id FROM \"deals\" INNER JOIN \"wishes\" ON \"wishes\".\"deal_id\" = \"deals\".\"id\" WHERE (wishes.user_id = 1) AND (wishes.wished = 'f')))"
UPD2 (railish outer join)
Deal class:
class Deal < ActiveRecord::Base
has_many :wishes, :class_name => "Wish"
has_many :wishers, :through => :wishes, :source => :user
scope :not_wished_excluded, lambda { |user|
joins('LEFT OUTER JOIN wishes on wishes.deal_id = deals.id').
where('wishes.user_id = ? OR wishes.user_id is null', user).
where('wishes.wished = ? OR wishes.wished is null', true)
}
end
Usage:
ruby-1.9.2-p180 :096 > Deal.not_wished_excluded(User.first).to_sql
=> "SELECT \"deals\".* FROM \"deals\" LEFT OUTER JOIN wishes on wishes.deal_id = deals.id WHERE (wishes.user_id = 1 OR wishes.user_id is null) AND (wishes.wished = 't' OR wishes.wished is null)"

Resources