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?
Related
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
I am implementing something of a todo list with a user model and a List model with a date attribute.
On the user show page, I retrieve today's to do list.
How do I go about querying a user todo list for the previous and/or the next day.
All insights are welcome, thanks!
class User < ActiveRecord::Base
before_save { self.email = email.downcase }
before_save { self.username = username.downcase }
has_many :to_do_lists, dependent: :destroy
has_many :tasks, dependent: :destroy
validates_presence_of :first_name, :last_name
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-]+(?:\.[a-z\d\-]+)*\.[a-z]+\z/i
VALID_USERNAME_REGEX = /\A[a-z_0-9]+\z/i
validates :email, presence: true,
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
validates :username, presence: true,
format: { with: VALID_USERNAME_REGEX },
uniqueness: { case_sensitive: false }
def name
[first_name, last_name].compact.join(' ')
end
end
and the list model
class ToDoList < ActiveRecord::Base
belongs_to :user
has_many :tasks, dependent: :destroy
validates_presence_of :user_id
validates :date, presence: true,
uniqueness: {scope: :user_id}
end
Rails adds many helpful methods to Time to make this type of query quite intuitive. Since you validate that a user has only one to do list for each day:
#next_day_list = #user.to_do_lists.find_by_date(Date.today.tomorrow)
#prev_day_list = #user.to_do_lists.find_by_date(Date.today.yesterday)
This simple validation test is failing:
require 'test_helper'
class UserTest < ActiveSupport::TestCase
def setup
#user = User.new(name: "Example User",
email: "user#example.com",
character_attributes: {callsign: "example"},
password: "foobar",
password_confirmation: "foobar"
)
end
test "should be valid" do
assert #user.valid?, "#{#user.errors.messages}"
end
end
...with this message: character.sociable_id"=>["can't be blank"]
I don't understand why the user creation in UserTest is failing to make a valid User.
Each User has_one :character and each Character belongs_to a User.
The User model:
User.rb:
class User < ActiveRecord::Base
attr_accessor :remember_token, :activation_token, :reset_token
has_one :character, as: :sociable, dependent: :destroy
accepts_nested_attributes_for :character
has_secure_password
before_validation do
self.create_character unless character
end
before_save do
self.email.downcase!
end
before_create :create_activation_digest
validates :name, presence: true,
length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-]+(?:\.[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 }, allow_blank: true
validates :character, presence: true
.
.
end
The Character model:
Character.rb:
class Character < ActiveRecord::Base
belongs_to :sociable, polymorphic: true
has_many :posts, dependent: :destroy
before_save do
self.callsign.downcase!
end
validates :sociable_id, presence: true
VALID_CALLSIGN_REGEX = /\A[a-z\d\-.\_]+\z/i
validates :callsign, presence: true,
length: { maximum: 20 },
format: { with: VALID_CALLSIGN_REGEX },
uniqueness: { case_sensitive: false }
end
It should be:-
test "should be valid" do
assert #user.valid? , "#{#user.errors.messages}"
end
I'm working through an API for Rails and have been fumbling how to set up the associations, the spec, and the controller for my Get route. The goal - As a user I want to get all the notes closest to my location that have not been viewed. I know the viewed? logic is off, as is the Query Interface in the Recipients Model and Nearests Controller.
Here's the Error Rspec is giving me:
Failure/Error: note1 = create(:note)
NoMethodError:
undefined method `recipient_id=' for #<Note:0x007fd2a40e1400>
Here's the spec:
describe 'GET /v1/notes/nearests?lat=&lon=&radius=' do
it 'returns the notes within the given radius' do
near_note1 = create(:note, lat: 37.760322, lon: -122.429667)
near_note2 = create(:note, lat: 37.760322, lon: -122.429667)
lat = 37.771098
lon = -122.430782
radius = 10
get "/v1/notes/nearests?lat=#{lat}&lon=#{lon}&radius=#{radius}"
expect(response_json).to eq([
{
'id' => [near_note1.id, near.note2.id],
# 'lat' => near_note1.lat,
# 'lon' => near_note1.lon,
'note_text' => [near_note1.note_text, near_note2.note_text],
'photo_uri' => [near_note1.photo_uri, near_note2.photo.uri],
# 'expiration' => near_note.expiration.as_json,
'viewed' => [near_note1.viewed?, near_note2.viewed?]
},
])
end
end
Here is the controller code:
def index
#notes = Note.near([
params[:recipient_id],
params[:lat],
params[:lon]],
radius: :APP_CONFIG['radius'],
units: :APP_CONFIG['units']
)
end
Here are the Factories - Notes
FactoryGirl.define do
factory :note do |u|
sender_id {FactoryGirl.create(:user).id}
recipient_id {FactoryGirl.create(:user).id}
lat 1.5
lon 1.5
note_text "MyString"
photo_uri "MyString"
expiration Time.zone.now.utc
end
end
My Models:
User Model
class User < ActiveRecord::Base
has_many :notes
validates :first_name, :last_name, :pw, presence: true
validates :email, :username, :devicetoken, presence: true, uniqueness: true
validates :email, length: { minimum: 8 }
end
Note Model
class Note < ActiveRecord::Base
belongs_to :user, foreign_key: 'sender_id', class_name: 'User'
has_many :recipients, foreign_key: 'recipient_id', class_name: 'User'
validates :sender_id, presence: true
validates :lat, presence: true
validates :lon, presence: true
validates :note_text, presence:true
validates :expiration, presence: true
reverse_geocoded_by :lat, :lon
end
Recipients Model
class Recipient < ActiveRecord::Base
belongs_to :note, foreign_key: 'recipient_id', class_name: 'Note'
def get_recipient
Note.find(:all, params[:note_id])
end
def viewed?
end
end
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