Skip to content

Instantly share code, notes, and snippets.

@joemasilotti
Created April 27, 2022 03:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save joemasilotti/de2275e3be10b51cf959e68d37694343 to your computer and use it in GitHub Desktop.
Save joemasilotti/de2275e3be10b51cf959e68d37694343 to your computer and use it in GitHub Desktop.
Service layer extraction
# AFTER the refactor
class ColdMessage
class BusinessBlank < StandardError; end
class MissingSubscription < StandardError; end
class ExistingConversation < StandardError
attr_reader :conversation
def initialize(conversation)
super
@conversation = conversation
end
end
Result = Struct.new(:success?, :message)
private attr_reader :options, :user
def initialize(options, developer_id:, user:)
@options = options
@developer_id = developer_id
@user = user
end
def build
message = conversation.messages.new(options.merge(sender: business))
if business.blank?
raise BusinessBlank.new
elsif conversation.persisted?
raise ExistingConversation.new(conversation)
elsif !active_subscription?
raise MissingSubscription.new
elsif !MessagingPolicy.new(user, message).create?
raise Pundit::NotAuthorizedError.new
end
message
end
def send
message = build
if business.blank?
raise BusinessBlank.new
elsif conversation.persisted?
raise ExistingConversation.new(conversation)
elsif !active_subscription?
raise MissingSubscription.new
elsif !MessagingPolicy.new(user, message).create?
raise Pundit::NotAuthorizedError.new
elsif !SubscriptionPolicy.new(user, message).messageable?
raise Pundit::NotAuthorizedError.new
elsif message.save
send_recipient_notification(message)
Result.new(true, message)
else
Result.new(false, message)
end
end
private
def developer
@developer ||= Developer.visible.find(@developer_id)
end
def business
user.business
end
def conversation
@conversation ||= Conversation.find_or_initialize_by(developer:, business:)
end
def active_subscription?
user.active_business_subscription?
end
def send_recipient_notification(message)
NewMessageNotification.with(message:, conversation:).deliver_later(message.recipient.user)
end
end
# AFTER the refactor
class ColdMessagesController < ApplicationController
before_action :authenticate_user!
rescue_from ColdMessage::BusinessBlank, with: :redirect_to_new_business
rescue_from ColdMessage::MissingSubscription, with: :redirect_to_pricing
rescue_from ColdMessage::ExistingConversation, with: :redirect_to_conversation
def new
message = ColdMessage.new({}, developer_id:, user: current_user).build
@context = context(message)
end
def create
result = ColdMessage.new(message_params, developer_id:, user: current_user).send
if result.success?
redirect_to result.message.conversation
else
@context = context(result.message)
render :new, status: :unprocessable_entity
end
end
private
def developer_id
params[:developer_id]
end
def context(message)
ColdMessageContext.new(
message:,
messageable: SubscriptionPolicy.new(current_user, message).messageable?,
show_hiring_fee_terms: current_user.active_full_time_business_subscription?,
tips: MarkdownRenderer.new("cold_messages/tips").render
)
end
def redirect_to_new_business
store_location!
redirect_to new_business_path, notice: I18n.t("errors.business_blank")
end
def redirect_to_conversation(e)
redirect_to e.conversation
end
def redirect_to_pricing
store_location!
redirect_to pricing_path, notice: I18n.t("errors.business_subscription_inactive")
end
def message_params
params.require(:message).permit(:body, :hiring_fee_agreement)
end
end
# BEFORE the refactor
class ColdMessagesController < ApplicationController
before_action :authenticate_user!
before_action :require_business!
before_action :require_new_conversation!
before_action :require_active_subscription!
def new
message = Message.new(conversation:)
@cold_message = cold_message(message)
end
def create
message = Message.new(message_params.merge(conversation:, sender: business))
if message.save
redirect_to message.conversation
else
@cold_message = cold_message(message)
render :new, status: :unprocessable_entity
end
end
private
def cold_message(message)
ColdMessage.new(
message:,
messageable: SubscriptionPolicy.new(current_user, message).messageable?,
show_hiring_fee_terms: current_user.active_full_time_business_subscription?,
tips: MarkdownRenderer.new("cold_messages/tips").render
)
end
def require_business!
unless business.present?
store_location!
redirect_to new_business_path, notice: I18n.t("errors.business_blank")
end
end
def require_new_conversation!
redirect_to conversation unless conversation.new_record?
end
def require_active_subscription!
unless current_user.active_business_subscription?
store_location!
redirect_to pricing_path
end
end
def conversation
@conversation ||= Conversation.find_or_initialize_by(developer:, business:)
end
def developer
@developer ||= Developer.find(params[:developer_id])
end
def business
@business = current_user.business
end
def message_params
params.require(:message).permit(:body, :hiring_fee_agreement)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment