Created
December 3, 2013 19:46
-
-
Save elskwid/7776245 to your computer and use it in GitHub Desktop.
Service Object WIP
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
# An attempt to codify how we're using Service Objects now | |
# | |
# Most follow this pattern: | |
# | |
# class SomeService | |
# | |
# def self.call(a, b, &block) | |
# new(a, b).call(&block) | |
# end | |
# | |
# attr_reader :a, :b | |
# | |
# def initialize(a, b) | |
# @a = a | |
# @b = b | |
# end | |
# | |
# def call(&block) | |
# # do work here | |
# # maybe call block | |
# end | |
# | |
# end | |
# | |
# These two modules provide this pattern: | |
# | |
# Service - | |
# creates the initializer, readers, and a `collaborators` class | |
# method to make it clearer. | |
# | |
# CallableService - | |
# provides the default `.call` class method and a default noop | |
# `#call` method while also mixing in the Service module. | |
# | |
# | |
# Note: It doesn't use AS::Concern so it's usable outside of Rails. | |
# | |
# To use it: | |
# | |
# class ExampleService | |
# include CallableService | |
# | |
# collaborators :a, :b, :c | |
# end | |
# | |
# ex = ExampleService.new("A", "B", "C") | |
# | |
# ex.a # => "A" | |
# ex.b # => "B" | |
# ex.c # => "C" | |
# | |
# ex.call | |
# => #<ExampleService: ...> | |
# | |
# ExampleService.call("A", "B", "C") | |
# => #<ExampleService:...> | |
# | |
module Service | |
def self.included(base) | |
base.send(:extend, ClassMethods) | |
super | |
end | |
# Instance level access to the collaborators | |
def collaborators | |
self.class.collaborators | |
end | |
module ClassMethods | |
# Provides a collaborators class method to set them up in your service. | |
# | |
# i.e. | |
# collaborators :event, :user, :payment | |
# | |
def collaborators(*collabs) | |
return @collaborators if collabs.empty? | |
@collaborators = collabs | |
create_readers | |
create_initializer | |
end | |
private | |
# Creates an attr_reader for each collaborator | |
def create_readers | |
collaborators.each { |c| puts c.inspect; attr_reader c.to_sym } | |
end | |
# Also adds a simple initializer that takes each argument, maps it to | |
# one of the declared collaborators, and sets the ivar for that value. | |
# | |
# i.e. | |
# collaborators :event, :user, :payment | |
# | |
# s = Service.new(some_event, a_user, the_payment) | |
# | |
# s.event # => some_event | |
# s.user # => a_user | |
# s.payment # => the_payment | |
# | |
def create_initializer | |
initializer = %Q{ | |
def initialize(#{collaborators.join(", ")}) | |
#{collaborators.map{|c| "@#{c} = #{c}\n"}.join("")} | |
end | |
} | |
module_eval initializer | |
end | |
end # ClassMethods | |
end | |
module CallableService | |
def self.included(base) | |
base.send(:include, Service) | |
base.send(:extend, ClassMethods) | |
super | |
end | |
# Default call method, returns self | |
def call | |
self | |
end | |
module ClassMethods | |
# Default call class method that passes args to the initializer | |
# and the block to the call method. | |
def call(*args, &block) | |
new(*args).call(&block) | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment