Skip to content

Instantly share code, notes, and snippets.

@nogtini
Forked from tomdalling/after.rb
Created July 7, 2019 21:07
Show Gist options
  • Save nogtini/0a8b055f3ab258d7b6441a7c4b58709b to your computer and use it in GitHub Desktop.
Save nogtini/0a8b055f3ab258d7b6441a7c4b58709b to your computer and use it in GitHub Desktop.
Refactoring a controller action
class CommentsController
def create
result = CreateComment.call(params, @user)
if result.ok?
render :partial => "comments/postedreply", :layout => false,
:content_type => "text/html", :locals => { :comment => result.value }
else
case result.error.name
when :story_not_found
render :plain => "can't find story", :status => 400
when :parent_comment_not_found
render :json => { :error => "invalid parent comment", :status => 400 }
when :duplicate_comment
comment = result.error.comment
comment.errors.add(:comment, "^You have already posted a comment here recently.")
render :partial => "commentbox", :layout => false,
:content_type => "text/html", :locals => { :comment => comment }
else #:didnt_save
preview result.error.comment
end
end
end
end
module CreateComment
extend self
extend Resonad::Mixin
Error = Struct.new(:name, :comment)
def call(params, user)
is_preview = params[:preview].present?
find_story(params[:story_id])
.and_then { |story| build_comment(params, story, user) }
.and_then { |comment| prevent_duplicates(comment, is_preview) }
.and_then { |comment| save_comment(comment, is_preview) }
end
private
def find_story(story_id)
story = Story.where(:short_id => story_id).first
if story && !story.is_gone?
Success(story)
else
Failure(Error.new(:story_not_found, nil))
end
end
def build_comment(params, story, user)
comment = story.comments.build
comment.comment = params[:comment].to_s
comment.user = user
if params[:hat_id] && user.hats.where(:id => params[:hat_id])
comment.hat_id = params[:hat_id]
end
if params[:parent_comment_short_id].present?
parent = Comment.where(story_id: story.id, short_id: params[:parent_comment_short_id]).first
if parent
comment.parent_comment = parent
else
return Failure(Error.new(:parent_comment_not_found, comment))
end
end
Success(comment)
end
def prevent_duplicates(comment, is_preview)
unless is_preview
doubled_comment = Comment.where(
story_id: comment.story_id,
user_id: comment.user_id,
parent_comment_id: comment.parent_comment_id
).first
if doubled_comment && (Time.now - doubled_comment.created_at) < 5.minutes
return Failure(Error.new(:duplicate_comment, comment))
end
end
Success(comment)
end
def save_comment(comment, is_preview)
if comment.valid? && is_preview && comment.save
comment.current_vote = { :vote => 1 }
Success(comment)
else
comment.upvotes = 1
comment.current_vote = { :vote => 1 }
Failure(Error.new(:didnt_save, comment))
end
end
end
class CommentsController
def create
if !(story = Story.where(:short_id => params[:story_id]).first) ||
story.is_gone?
return render :plain => "can't find story", :status => 400
end
comment = story.comments.build
comment.comment = params[:comment].to_s
comment.user = @user
if params[:hat_id] && @user.hats.where(:id => params[:hat_id])
comment.hat_id = params[:hat_id]
end
if params[:parent_comment_short_id].present?
if pc = Comment.where(:story_id => story.id, :short_id =>
params[:parent_comment_short_id]).first
comment.parent_comment = pc
else
return render :json => { :error => "invalid parent comment",
:status => 400 }
end
end
# prevent double-clicks of the post button
if params[:preview].blank? &&
(pc = Comment.where(:story_id => story.id, :user_id => @user.id,
:parent_comment_id => comment.parent_comment_id).first)
if (Time.now - pc.created_at) < 5.minutes
comment.errors.add(:comment, "^You have already posted a comment " <<
"here recently.")
return render :partial => "commentbox", :layout => false,
:content_type => "text/html", :locals => { :comment => comment }
end
end
if comment.valid? && params[:preview].blank? && comment.save
comment.current_vote = { :vote => 1 }
render :partial => "comments/postedreply", :layout => false,
:content_type => "text/html", :locals => { :comment => comment }
else
comment.upvotes = 1
comment.current_vote = { :vote => 1 }
preview comment
end
end
end
class CommentsController
def create
begin
comment = CreateComment.call(params, @user)
render :partial => "comments/postedreply", :layout => false,
:content_type => "text/html", :locals => { :comment => comment }
rescue CreateComment::StoryNotFound
render :plain => "can't find story", :status => 400
rescue CreateComment::ParentNotFound
render :json => { :error => "invalid parent comment", :status => 400 }
rescue CreateComment::DuplicateComment => ex
ex.comment.errors.add(:comment, "^You have already posted a comment here recently.")
render :partial => "commentbox", :layout => false,
:content_type => "text/html", :locals => { :comment => ex.comment }
rescue CreateComment::DidntSave => ex
preview ex.comment
end
end
end
class CreateComment
class StoryNotFound < StandardError; end
class ParentNotFound < StandardError; end
class DuplicateComment < StandardError
attr_reader :comment
def initialize(comment)
@comment = comment
super()
end
end
class DidntSave < StandardError
attr_reader :comment
def initialize(comment)
@comment = comment
super()
end
end
def self.call(*args)
new(*args).call
end
def initialize(params, user)
@params = params
@user = user
end
def call
story = find_story!(params[:story_id])
comment = build_comment!(story)
prevent_duplicates!(comment)
save_comment!(comment)
comment
end
private
attr_reader :params, :user
def find_story!(story_id)
story = Story.where(:short_id => story_id).first
if story && !story.is_gone?
story
else
raise StoryNotFound
end
end
def build_comment!(story)
comment = story.comments.build
comment.comment = comment_params[:comment].to_s
comment.user = user
if comment_params[:hat_id] && user.hats.where(:id => comment_params[:hat_id])
comment.hat_id = comment_params[:hat_id]
end
if comment_params[:parent_comment_short_id].present?
parent = Comment.where(story_id: story.id, short_id: comment_params[:parent_comment_short_id]).first
if parent
comment.parent_comment = parent
else
raise ParentNotFound
end
end
comment
end
def prevent_duplicates!(comment)
if !preview? && duplicate?(comment)
raise DuplicateComment.new(comment)
end
end
def save_comment!(comment)
if comment.valid? && preview? && comment.save
comment.current_vote = { :vote => 1 }
else
comment.upvotes = 1
comment.current_vote = { :vote => 1 }
raise DidntSave.new(comment)
end
end
def comment_params
@comment_params ||= params.slice(:comment, :hat_id, :parent_comment_short_id)
end
def preview?
params[:preview].present?
end
def duplicate?(comment)
duplicate = Comment.where(
story_id: comment.story_id,
user_id: comment.user_id,
parent_comment_id: comment.parent_comment_id
).first
duplicate && (Time.now - duplicate.created_at) < 5.minutes
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment