Skip to content

Instantly share code, notes, and snippets.

@rpbaltazar
Last active February 24, 2018 07:43
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 rpbaltazar/141bc48f89b6355a9351b9e179c3f7ea to your computer and use it in GitHub Desktop.
Save rpbaltazar/141bc48f89b6355a9351b9e179c3f7ea to your computer and use it in GitHub Desktop.
Endpoint proposal
require 'dry-matcher'
module FirstTouch
# This class assumes that the project is api only and its rendering
# JSON. For other options (e.g render a view) this needs to be thought
class Endpoint
Matcher = Dry::Matcher.new(
success: Dry::Matcher::Case.new(
match: ->(result:, **) { result.success? },
resolve: ->(result:, representer:, **) do
{
'data': representer.new(result['models']),
'status': :ok
}
end
),
created: Dry::Matcher::Case.new(
match: ->(result:, **) do
result.success? && result['model.action'] == :new
end,
resolve: ->(result:, representer:, **) do
{
'data': representer.new(result['model']),
'status': :created
}
end
),
unauthenticated: Dry::Matcher::Case.new(
match: ->(result:, **) { result.policy_error? },
resolve: ->(_result:, **) do
{
'data': { errors: ['unauthorized'] },
'status': :unauthorized
}
end
),
not_found: Dry::Matcher::Case.new(
match: ->(result:, **) do
result.failure? && result['result.model']&.failure?
end,
resolve: ->(result:, **) do
{
'data': { errors: result['result.model.errors'] },
'status': :unprocessable_entity
}
end
),
invalid: Dry::Matcher::Case.new(
match: ->(result:, **) do
result.failure? && result['result.contract.default']&.failure?
end,
resolve: ->(result:, **) do
{
'data': { errors: result['contract.default']&.errors&.full_messages },
'status': :unprocessable_entity
}
end
)
)
# options expects a TRB Operation result
# it might have a representer, else will assume the default name
def self.call(operation_class, options = {})
result = operation_class.(*options[:args])
endpoint_opts = { result: result, representer: options[:representer] }
new.(endpoint_opts)
end
def call(options)
matcher.(options) do |m|
m.created { |v| v }
m.success { |v| v }
m.not_found { |v| v }
m.unauthenticated { |v| v }
m.invalid { |v| v }
end
end
def matcher
Matcher
end
end
end
module OPL
class Endpoint
DEFAULT_MATCHERS = {
created: {
rule: ->(result) { result.success? && result["model.action"] == :new },
resolve: lambda do |result, representer|
{ "data": representer.new(result["model"]),
"status": :created }
end
},
deleted: {
rule: ->(result) { result.success? && result["model.operation"] == :destroy },
resolve: lambda do |result, _representer|
{ "data": { id: result["model"].id },
"status": :ok }
end
},
success: {
rule: ->(result) { result.success? },
resolve: lambda do |result, representer|
data = if representer
representer.new(result["results"])
else
result["results"]
end
{ "data": data, "status": :ok }
end
},
unauthenticated: {
rule: ->(result) { result.policy_error? },
resolve: ->(_result, _representer) { { "data": {}, "status": :unauthorized } }
},
invalid: {
rule: ->(result) { result.failure? },
resolve: ->(_result, _representer) { { "data": {}, "status": :unprocessable_entity } }
},
not_found: {
rule: ->(result) { result.failure? && result["result.model"]&.failure? },
resolve: lambda do |result, _representer|
{ "data": { errors: result["result.model.errors"] },
"status": :unprocessable_entity }
end
},
fallback: {
rule: ->(_result) { true },
resolve: lambda do |_result, _representer|
{ "data": { errors: "Can't process the result" },
"status": :unprocessable_entity }
end
}
}.freeze
# NOTE: options expects a TRB Operation result
# it might have a representer, else will assume the default name
def self.call(operation_result, representer_class = nil, overrides = {})
endpoint_opts = { result: operation_result, representer: representer_class }
new.(endpoint_opts, overrides)
end
def call(options, overrides)
overrides.each do |rule_key, rule_description|
rule = rule_description[:rule] || DEFAULT_MATCHERS[rule_key][:rule]
resolve = rule_description[:resolve] || DEFAULT_MATCHERS[rule_key][:resolve]
if rule.nil? || resolve.nil?
puts "Matcher is not properly set. #{rule_key} will be ignored"
next
end
return resolve.(options[:result], options[:representer]) if rule.(options[:result])
end
matching_rules(overrides).each do |_rule_key, rule_description|
if rule_description[:rule].(options[:result])
return rule_description[:resolve].(options[:result], options[:representer])
end
end
end
def matching_rules(overrides)
DEFAULT_MATCHERS.except(*overrides.keys)
end
end
end
module Api
module V3
class TreeCategoryController < Api::V3::BaseController
def create
result = ::V3::TreeNode::Create.(params, current_user: current_user)
response = OPL::Endpoint.(result, ::V3::TreeNode::Representer::Full)
render json: response[:data], status: response[:status]
end
def update
op_result = ::V3::TreeNode::Update.(params, current_user: current_user)
success_resolve = ->(result, representer) {
{ "data": representer.new(result["model"]),
"status": :ok }
}
response = OPL::Endpoint.(op_result, ::V3::TreeNode::Representer::Full, success: { resolve: success_resolve } )
render json: response[:data], status: response[:status]
end
def bulk_update
result = ::V3::TreeNode::BulkUpdate.(params, current_user: current_user)
failure_proc = ->(res, _representer){
{ "data": res["response"], "status": :unprocessable_entity }
}
response = OPL::Endpoint.(result, nil, invalid: { resolve: failure_proc })
render json: response[:data], status: response[:status]
end
def destroy
result = ::V3::TreeNode::Delete.(params, current_user: current_user)
response = OPL::Endpoint.(result, nil)
render json: response[:data], status: response[:status]
end
def show
op_result = ::V3::TreeNode::Show.(params, current_user: current_user)
success_resolve = ->(result, representer) {
{ "data": representer.new(result["model"]),
"status": :ok }
}
response = OPL::Endpoint.(op_result, ::V3::TreeNode::Representer::Full, success: { resolve: success_resolve })
render json: response[:data], status: response[:status]
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment