How can we notify a user when he has a new follower?
create_notification
is suppose to trigger a notification, but I get different errors with different attempts.
relationship.rb
class Relationship < ActiveRecord::Base
after_create :create_notification
has_many :notifications
belongs_to :follower, class_name: "User"
belongs_to :followed, class_name: "User"
validates :follower_id, presence: true
validates :followed_id, presence: true
private
def create_notification # How do we need to rewrite this?
notifications.create(
followed_id: followed_id,
follower_id: follower_id,
user: follower_id.user,
read: false
)
end
end
I used this tutorial as a guide for building my notifications and the Hartl tutorial for building the users and relationships.
relationships_controller.rb
class RelationshipsController < ApplicationController
before_action :logged_in_user
def create # The notification is trigger after create.
@user = User.find(params[:followed_id])
current_user.follow(@user)
respond_to do |format|
format.html { redirect_to @user }
format.js
end
end
def destroy
@user = Relationship.find(params[:id]).followed
current_user.unfollow(@user)
respond_to do |format|
format.html { redirect_to @user }
format.js
end
end
end
Sorry my user model is a bit messy.
class User < ActiveRecord::Base
acts_as_tagger
acts_as_taggable
has_many :notifications
has_many :activities
has_many :activity_likes
has_many :liked_activities, through: :activity_likes, class_name: 'Activity', source: :liked_activity
has_many :liked_comments, through: :comment_likes, class_name: 'Comment', source: :liked_comment
has_many :valuation_likes
has_many :habit_likes
has_many :goal_likes
has_many :quantified_likes
has_many :comment_likes
has_many :authentications
has_many :habits, dependent: :destroy
has_many :levels
has_many :combine_tags
has_many :valuations, dependent: :destroy
has_many :comments
has_many :goals, dependent: :destroy
has_many :quantifieds, dependent: :destroy
has_many :results, through: :quantifieds
has_many :notes
accepts_nested_attributes_for :habits, :reject_if => :all_blank, :allow_destroy => true
accepts_nested_attributes_for :notes, :reject_if => :all_blank, :allow_destroy => true
accepts_nested_attributes_for :quantifieds, :reject_if => :all_blank, :allow_destroy => true
accepts_nested_attributes_for :results, :reject_if => :all_blank, :allow_destroy => true
has_many :active_relationships, class_name: "Relationship",
foreign_key: "follower_id",
dependent: :destroy
has_many :passive_relationships, class_name: "Relationship",
foreign_key: "followed_id",
dependent: :destroy
has_many :following, through: :active_relationships, source: :followed
has_many :followers, through: :passive_relationships, source: :follower
attr_accessor :remember_token, :activation_token, :reset_token
before_save :downcase_email
before_create :create_activation_digest
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, length: { maximum: 255 },
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }, unless: -> { from_omniauth? }
has_secure_password
validates :password, length: { minimum: 6 }
scope :publish, ->{ where(:conceal => false) }
User.tag_counts_on(:tags)
def count_mastered
@res = habits.reduce(0) do |count, habit|
habit.current_level == 6 ? count + 1 : count
end
end
def count_challenged
@challenged_count = habits.count - @res
end
def self.from_omniauth(auth)
where(provider: auth.provider, uid: auth.uid).first_or_initialize.tap do |user|
user.provider = auth.provider
user.image = auth.info.image
user.uid = auth.uid
user.name = auth.info.name
user.oauth_token = auth.credentials.token
user.oauth_expires_at = Time.at(auth.credentials.expires_at)
user.password = (0...8).map { (65 + rand(26)).chr }.join
user.email = (0...8).map { (65 + rand(26)).chr }.join+"@mailinator.com"
user.save!
end
end
def self.koala(auth)
access_token = auth['token']
facebook = Koala::Facebook::API.new(access_token)
facebook.get_object("me?fields=name,picture")
end
# Returns the hash digest of the given string.
def User.digest(string)
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
BCrypt::Engine.cost
BCrypt::Password.create(string, cost: cost)
end
# Returns a random token.
def User.new_token
SecureRandom.urlsafe_base64
end
# Remembers a user in the database for use in persistent sessions.
def remember
self.remember_token = User.new_token
update_attribute(:remember_digest, User.digest(remember_token))
end
# Forgets a user. NOT SURE IF I REMOVE
def forget
update_attribute(:remember_digest, nil)
end
# Returns true if the given token matches the digest.
def authenticated?(attribute, token)
digest = send("#{attribute}_digest")
return false if digest.nil?
BCrypt::Password.new(digest).is_password?(token)
end
# Activates an account.
def activate
update_attribute(:activated, true)
update_attribute(:activated_at, Time.zone.now)
end
# Sends activation email.
def send_activation_email
UserMailer.account_activation(self).deliver_now
end
def create_reset_digest
self.reset_token = User.new_token
update_attribute(:reset_digest, User.digest(reset_token))
update_attribute(:reset_sent_at, Time.zone.now)
end
# Sends password reset email.
def send_password_reset_email
UserMailer.password_reset(self).deliver_now
end
# Returns true if a password reset has expired.
def password_reset_expired?
reset_sent_at < 2.hours.ago
end
def good_results_count
results.good_count
end
# Follows a user.
def follow(other_user)
active_relationships.create(followed_id: other_user.id)
end
# Unfollows a user.
def unfollow(other_user)
active_relationships.find_by(followed_id: other_user.id).destroy
end
# Returns true if the current user is following the other user.
def following?(other_user)
following.include?(other_user)
end
private
def from_omniauth?
provider && uid
end
# Converts email to all lower-case.
def downcase_email
self.email = email.downcase unless from_omniauth?
end
# Creates and assigns the activation token and digest.
def create_activation_digest
self.activation_token = User.new_token
self.activation_digest = User.digest(activation_token)
end
end
users_controller.rb
class UsersController < ApplicationController
before_action :logged_in_user, only: [:index, :edit, :update, :destroy,
:following, :followers]
before_action :correct_user, only: [:edit, :update]
before_action :admin_user, only: :destroy
def tag_cloud
@tags = User.tag_counts_on(:tags)
end
def index
@users = User.paginate(page: params[:page])
end
def show
@user = User.find(params[:id])
if current_user == @user
@habits = @user.habits
@valuations = @user.valuations
@accomplished_goals = @user.goals.accomplished
@unaccomplished_goals = @user.goals.unaccomplished
@averaged_quantifieds = @user.quantifieds.averaged
@instance_quantifieds = @user.quantifieds.instance
else
@habits = @user.habits.publish
@valuations = @user.valuations.publish
@accomplished_goals = @user.goals.accomplished.publish
@unaccomplished_goals = @user.goals.unaccomplished.publish
@averaged_quantifieds = @user.quantifieds.averaged.publish
@instance_quantifieds = @user.quantifieds.instance.publish
end
end
def new
@user = User.new
end
def create
@user = User.new(user_params)
if @user.save
@user.send_activation_email
flash[:info] = "Please check your email to activate your account."
redirect_to root_url
else
render 'new'
end
end
def edit
@user = User.find(params[:id])
end
def update
@user = User.find(params[:id])
if @user.update_attributes(user_params)
flash[:success] = "Profile updated"
redirect_to @user
else
render 'edit'
end
end
def destroy
User.find(params[:id]).destroy
flash[:success] = "User deleted"
redirect_to users_url
end
def following
@title = "Following"
@user = User.find(params[:id])
@users = @user.following.paginate(page: params[:page])
render 'show_follow'
end
def followers
@title = "Followers"
@user = User.find(params[:id])
@users = @user.followers.paginate(page: params[:page])
render 'show_follow'
end
private
def user_params
if params[:conceal] = true
params.require(:user).permit(:name, :email, :tag_list, :password, :conceal, :password_confirmation, valuations_attributes: [:name, :tag_list, :conceal], activities_attributes: [:conceal, :action, :trackable_id, :trackable_type])
else
params[:user][:valuations][:conceal] = false
params.require(:user).permit(:name, :image, :tag_list, :email, :password, :password_confirmation, valuations_attributes: [:name, :tag_list], activities_attributes: [:action, :trackable_id, :trackable_type])
end
end
# Before filters
# Confirms a logged-in user.
def logged_in_user
unless logged_in?
store_location
flash[:danger] = "Please log in."
redirect_to login_url
end
end
# Confirms the correct user.
def correct_user
@user = User.find(params[:id])
redirect_to(root_url) unless current_user?(@user)
end
# Confirms an admin user.
def admin_user
redirect_to(root_url) unless current_user.admin?
end
end
schema.rb
create_table "notifications", force: true do |t|
t.integer "habit_id"
t.integer "quantified_id"
t.integer "valuation_id"
t.integer "goal_id"
t.integer "comment_id"
t.integer "user_id"
t.integer "habit_like_id"
t.integer "quantified_like_id"
t.integer "valuation_like_id"
t.integer "goal_like_id"
t.integer "comment_like_id"
t.integer "likes"
t.integer "followed_id"
t.integer "follower_id"
t.boolean "read"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "relationships", force: true do |t|
t.integer "follower_id"
t.integer "followed_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
Please let me know if you need further explanation or code :-] Keep the dream alive!
You could simply extend your
User#follow
method to something like this:Then add a
new_follower(user)
method to yourUserMailer
in a similar way than the already existingpassword_reset
method.