Reputation: 2866
If I make a comment on my own thing I shouldn't get a notification about it. How can we remove this "feature"?
class Comment < ActiveRecord::Base
after_save :create_notification
has_many :notifications
has_many :comment_likes
has_many :likers, through: :comment_likes, class_name: 'User', source: :liker
belongs_to :habit
belongs_to :stat
belongs_to :valuation
belongs_to :goal
belongs_to :user
validates :user, presence: true
default_scope { order('created_at') }
private
def create_notification
author =
if goal
goal.user
#elsif comment_like
# comment_like.user
elsif habit
habit.user
elsif stat
stat.user
elsif valuation
valuation.user
end
notifications.create(
comment: self,
likes: likes,
habit: habit,
stat: stat,
goal: goal,
valuation: valuation,
user: author,
read: false
)
end
end
Comments can be made on goals, valuations, stats, & habits.
class NotificationsController < ApplicationController
before_action :correct_user, only: [:destroy]
def index
@notifications = current_user.notifications
@notifications.each do |notification|
notification.update_attribute(:read, true)
end
end
def destroy
@notification = Notification.find(params[:id])
@notification.destroy
redirect_to :back
end
private
def correct_user
@notification = current_user.notifications.find_by(id: params[:id])
redirect_to root_url, notice: "Not authorized to edit this notification" if @notification.nil?
end
end
CommentsController
class CommentsController < ApplicationController
before_action :set_commentable, only: [:index, :new, :create]
before_action :set_comment, only: [:edit, :update, :destroy]
before_action :correct_user, only: [:edit, :update, :destroy]
def index
@comments = @commentable.comments
end
def new
@comment = @commentable.comments.new
end
def create
@comment = @commentable.comments.new(comment_params)
if @comment.persisted?
redirect_to @commentable, notice: "Comment created."
else
render :new
end
end
def edit
end
def update
if @comment.update_attributes(comment_params)
redirect_to :back, notice: "Comment was updated."
else
render :edit
end
end
def destroy
@comment.destroy
redirect_to @comment.commentable, notice: "Comment destroyed."
end
def like
@comment = Comment.find(params[:id])
@comment_like = current_user.comment_likes.build(comment: @comment)
if @comment_like.save
@comment.increment!(:likes)
flash[:success] = 'Thanks for liking!'
else
flash[:error] = 'Too many likes'
end
redirect_to(:back)
end
private
def set_commentable
@commentable = find_commentable
end
def set_comment
@comment = current_user.comments.find(params[:id])
end
def correct_user
@comment = current_user.comments.find_by(id: params[:id])
redirect_to root_url, notice: "Not authorized to edit this comment" if @comment.nil?
end
def find_commentable
if params[:goal_id]
Goal.find(params[:goal_id])
elsif params[:habit_id]
Habit.find(params[:habit_id])
elsif params[:valuation_id]
Valuation.find(params[:valuation_id])
elsif params[:stat_id]
Stat.find(params[:stat_id])
end
end
def comment_params
params[:comment][:user_id] = current_user.id
params.require(:comment).permit(:content, :commentable, :user_id, :like)
end
end
Please let me know if you need further code or explanation. Live long & prosper :]
Upvotes: 1
Views: 164
Reputation: 176462
Ideally, you should not rely on callbacks. Especially if, like in this case, your callback must access data which is not available outside the context of the model.
The solution is simple. Rather than creating the comment calling ActiveRecord methods directly in your controller, define a custom method in the Comment itself.
class Comment
# ...
def self.create_comment(user, attributes)
transaction do
comment = new(attributes)
if comment.save
create_notification(user)
# other after creation methods here
end
comment
end
end
end
Now the create_notification
has access to the current user. Then, in your controller:
def create
comment = Comment.create(current_user, params[:comments])
if comment.persisted?
# saved
else
# not saved
# comment.errors
end
end
There are several variations. You can also work always with an instance.
class Comment
# ...
def create_comment(user, attributes)
transaction do
comment.attributes = attributes
if result = comment.save
create_notification(user)
# other after creation methods here
end
result
end
end
end
def create
comment = Comment.new
if comment.create_comment(current_user, params[:comments])
# saved
else
# not saved
# comment.errors
end
end
Whatever is your implementation, the point is that callbacks should not be abused. In particular, they should be used to guarantee consistency within the same model, and you should not start introducing explicit dependencies between models or your code will quickly become slow and unmaintainable.
Upvotes: 2