require 'securerandom' require 'action_dispatch' require 'digest' module Services class Base class << self def inherited(subclass) subclass.const_set :Error, Class.new(StandardError) subclass.send :include, Rails.application.routes.url_helpers if defined?(Rails) subclass.send :include, Asyncable if defined?(Asyncable) subclass.send :prepend, CallLogger, ExceptionWrapper, UniquenessChecker end delegate :call, to: :new end def initialize @id = SecureRandom.hex(6) end def call(*args) raise NotImplementedError end private def _split_ids_and_objects(ids_or_objects, klass) ids_or_objects = Array(ids_or_objects) ids, objects = ids_or_objects.grep(Fixnum), ids_or_objects.grep(klass) if ids.size + objects.size < ids_or_objects.size raise "All params must be either #{klass.to_s.pluralize} or Fixnums: #{ids_or_objects.map { |id_or_object| [id_or_object.class, id_or_object.inspect].join(' - ')}}" end [ids, objects] end def find_ids(ids_or_objects, klass = object_class) ids, objects = _split_ids_and_objects(ids_or_objects, klass) ids.concat objects.map(&:id) if objects.any? ids end def find_objects(ids_or_objects, klass = object_class) ids, objects = _split_ids_and_objects(ids_or_objects, klass) if ids.any? find_service = "Services::#{klass.to_s.pluralize}::Find" objects_from_ids = find_service.constantize.call(ids) object_ids = if objects_from_ids.respond_to?(:pluck) objects_from_ids.pluck(:id) else objects_from_ids.map(&:id) end missing_ids = ids - object_ids raise self.class::Error, "#{klass.to_s.pluralize(missing_ids)} #{missing_ids.join(', ')} not found." if missing_ids.size > 0 objects.concat objects_from_ids end objects end %i(object id).each do |type| define_method "find_#{type}" do |*args| send("find_#{type.to_s.pluralize}", *args).tap do |objects_or_ids| raise "Expected exactly one object or ID but found #{objects_or_ids.size}." unless objects_or_ids.size == 1 end.first end end def object_class self.class.to_s[/Services::([^:]+)/, 1].singularize.constantize rescue raise "Could not determine service class from #{self.class}" end def controller @controller ||= begin raise 'Please configure host.' if Services.configuration.host.nil? request = ActionDispatch::TestRequest.new request.host = Services.configuration.host ActionController::Base.new.tap do |controller| controller.instance_variable_set('@_request', request) end end end end end