Created
October 9, 2017 16:46
-
-
Save joegaudet/e4d7410d9a3182158de7e1e6538eab4b to your computer and use it in GitHub Desktop.
command-macro
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# example usage | |
class SomeService | |
# lets say Foo and Bar are AR models so they will be injected as classes | |
# I've been cooking up a further idea here to perhaps decorate the service | |
# call with another kind of dependency that would allow you to map the params | |
# to AR models or something... still a WIP though | |
dependency :foo_dao, Foo | |
dependency :bar_dao, Bar | |
dependency :logger, Logger, instance: Rails.logger | |
def call(foo_id, bar_id, some_other_param) | |
foo = foo_dao.find(foo_id) | |
bar = bar_dao.find(bar_id) | |
foo.bars << bar | |
foo.save! | |
logger.info("What's up") | |
foo | |
end | |
end | |
class SomeController < ActionController | |
command :foo, SomeService | |
end | |
# We have support for JR, which you probably don't need | |
module CommandSupport | |
def self.included(base) | |
base.extend(ClassMethods) | |
raise 'CommandSupport can only be included on controllers' unless base < ActionController::Base | |
end | |
private | |
module ClassMethods | |
# defines a command that creates some resource | |
# Defines a new controller command | |
# @param [Symbol] command_name | |
# @param [Class] service_class | |
# @param [Object] options | |
# @!macro [attach] command | |
# @return [$2] the $1 $0 | |
def create_command(command_name, service_class, options = {}) | |
options[:success_status] = :created | |
command(command_name, service_class, options) | |
end | |
# Defines a new controller command | |
# @param [Symbol] command_name | |
# @param [Class] service_class | |
# @param [Object] options | |
# @!macro [attach] command | |
# @return [$2] the $1 $0 | |
def command(command_name, service_class, options = {}) | |
service_instance = options[:service_instance] | |
with_mapping = options[:mapping] | |
ignore_resource = options[:ignore_resource] | |
no_content = options[:ignore_resource] || options[:no_content] | |
success_status = options[:success_status] | |
include = options.fetch(:include, '').underscore.split(',') | |
# extract the call parameters from the service class | |
call_param_names = service_class.instance_method(:call).parameters.flatten.reject {|p| p == :req} | |
# define a command for the controller | |
send :define_method, command_name do | |
# create strong params either with a mapping or with the service interface | |
strong_params = with_mapping.present? ? with_mapping.(params) : params.permit(*call_param_names) | |
# think about service injection via dependency here... | |
service = service_instance || service_class.new | |
# build call arguments out of params here | |
args = call_param_names.map {|param_name| strong_params[param_name]} | |
# call the service on the the args list, since we use the call params above to enforce the requirements | |
# we should have an args list that matches the service interface | |
service_result = service.(*args) | |
# If we are using a JR controller there will be a resource_klass present | |
if no_content | |
head :no_content | |
else | |
if ignore_resource | |
content_type = :json | |
result = service_result | |
else | |
content_type = JSONAPI::MEDIA_TYPE | |
# TODO: support array commands | |
# Break into the resource serializer that is on the controller by default | |
# and add includes | |
resource_serializer.instance_variable_set(:@include_directives, JSONAPI::IncludeDirectives.new(resource_klass, include)) | |
resource = resource_klass.new(service_result, context) | |
result = resource_serializer.serialize_to_hash(resource) | |
end | |
render json: result, status: success_status || :ok, content_type: content_type | |
end | |
end | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment