Skip to content

Instantly share code, notes, and snippets.

@joshuapaling
Created October 27, 2017 12:57
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 joshuapaling/e57b59aab5afd1257ed56232e5016a97 to your computer and use it in GitHub Desktop.
Save joshuapaling/e57b59aab5afd1257ed56232e5016a97 to your computer and use it in GitHub Desktop.
# Here's the code that's a candidate for refactor, I'm just trying
# to get a sense of if dry monads are a good fit or if this isn't really
# complex or nested-ifs enough to make it really worth it.
# I'm more just checking out dry monads for fun (and potential profit!) rather than
# trying to solve an immediate pain point.
#
# Context is: it's an Employee Relationship Management system.
# Employees earn points for doing things. Periodically, the company holds
# a "flash sale" where they can redeem those points for rewards.
#
# An employee clicks "redeem". It:
# a) checks that they can in fact redeem the reward (there's a sale on, etc)
# b) adds the reward
# c) Rewarders::RedeemPointsRewarder basically just adds a transaction record
# to deduct points (rewards points are event sourced)
# d) it returns the employee's updated profile (which will include their new points balance)
# and their recent rewards transactions (which will include the most recent one)
# That last step's a bit clunky - creating a reward and returning different data -
# the reasoning for it is more clear when you see the UI,
# but that's kinda beside the point in this context.
# I'm not expecting you to re-write this or anything, just even a
# "yeah, try refactor that with dry monads"
# or "nah, bad example" would be great.
#
# FYI, I don't really care about all the error messages - if I just return
# the first one that comes up, that's enough.
#
# I've never used Monads before, in any context.
#
# Aside: if you have opinions on if it's OK for a rest-ful-ish API to return
# updated records that aren't the same resource/type as the endpoint itself
# (ie, in this case creating a reward_order and returning a `profile` and `recent_points`, which is the employee's last 5 transactions)
# because it's the easiest thing for the client to get back and save it making
# more API calls, I'd be keen to hear them! Was saving that as a question to
# ask peeps next Ruby meetup.
module API
class RewardOrdersController < API::BaseController
def create
errors = []
@flash_sale = FlashSale.current_sale
if !@flash_sale
errors << 'Cannot order rewards when no sale is on'
end
# Employees under performance review cannot receive points
if !current_employee.no_performance_issues?
errors << 'Cannot order rewards when there are performance issues'
end
# Deboarding / inactive employees can't receive points
if current_employee.deboarding? || current_employee.inactive?
errors << 'Cannot order rewards if deboarding or inactive'
end
employee = Employee.select("id, #{Employee.rewards_points_balance_sql} as reward_points_balance").find(current_employee.id)
reward = Reward.find(reward_order_params[:reward_id])
if employee.reward_points_balance < reward.cost_in_points
errors << 'Cannot order reward - insufficient points'
end
if errors.empty?
order = RewardOrder.new(reward_order_params)
order.employee = current_employee
order.flash_sale = @flash_sale
order.save!
Rewarders::RedeemPointsRewarder.new(current_employee.id).call(order)
# re-fetch with updated points balance
employee = Employee.find_for_profile(current_employee.id)
employee_json = ActionController::Base.new.view_context.render(template: "api/profiles/show.json.jbuilder", locals: { employee: employee })
result = {
recent_points: RewardPointTrx.recent_points(current_employee.id),
profile: JSON.parse(employee_json)
}
render json: result, status: 201
else
render json: errors, status: 422
end
end
private
# Never trust parameters from the scary internet, only allow the white list through.
def reward_order_params
params.permit(:reward_id)
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment