Module: Simple::Service

Defined in:
lib/simple/service.rb,
lib/simple/service/action.rb,
lib/simple/service/action.rb,
lib/simple/service/errors.rb,
lib/simple/service/context.rb,
lib/simple/service/context.rb,
lib/simple/service/version.rb

Overview

The Simple::Service interface

This module implements the main API of the Simple::Service ruby gem.

  1. Marking a service module: To turn a target module as a service module one must include Simple::Service into the target. This serves as a marker that this module is actually intended to provide one or more services. Example:

    module GodMode
      include Simple::Service
    
      # Build a universe.
      #
      # This comment will become part of the full description of the
      # "build_universe" service
      def build_universe(name, c: , pi: 3.14, e: 2.781)
        # at this point I realize that *I* am not God.
    
        42 # Best try approach
      end
    end
    
  2. Discover services: To discover services in a service module use the #actions method. This returns a Hash of actions.

    Simple::Service.actions(GodMode)
    => {:build_universe=>#<Simple::Service::Action...>, ...}
    

TODO: why a Hash? It feels much better if Simple::Service.actions returns an array of names.

  1. Invoke a service: run Simple::Service.invoke3 or Simple::Service.invoke. You must set a context first.

    Simple::Service.with_context do
      Simple::Service.invoke3(GodMode, :build_universe, "TestWorld", c: 1e9)
    end
    => 42
    

Defined Under Namespace

Modules: ClassMethods, GemHelper, ServiceExpectations Classes: Action, ArgumentError, Context, ContextMissingError, ContextReadOnlyError, ExtraArguments, MissingArguments, NoSuchAction

Constant Summary collapse

VERSION =
GemHelper.version "simple-service"

Class Method Summary collapse

Class Method Details

.action(service, name) ⇒ Object

returns the action with the given name.



96
97
98
99
100
101
# File 'lib/simple/service.rb', line 96

def self.action(service, name)
  actions = self.actions(service)
  actions[name] || begin
    raise ::Simple::Service::NoSuchAction.new(service, name)
  end
end

.actions(service) ⇒ Object

returns a Hash with all actions in the service module



89
90
91
92
93
# File 'lib/simple/service.rb', line 89

def self.actions(service)
  verify_service!(service)

  service.__simple_service_actions__
end

.contextObject

Returns the current context.



3
4
5
# File 'lib/simple/service/context.rb', line 3

def self.context
  Thread.current[:"Simple::Service.context"]
end

.included(klass) ⇒ Object



59
60
61
62
# File 'lib/simple/service.rb', line 59

def self.included(klass) # @private
  klass.extend ClassMethods
  klass.include ServiceExpectations
end

.invoke(service, name, args: {}, flags: {}) ⇒ Object

Note: You cannot call this method if the context is not set.



159
160
161
162
163
164
165
166
167
# File 'lib/simple/service.rb', line 159

def self.invoke(service, name, args: {}, flags: {})
  raise ContextMissingError, "Need to set context before calling ::Simple::Service.invoke3" unless context

  expect! args => [Hash, Array], flags: Hash
  args.keys.each { |key| expect! key => String } if args.is_a?(Hash)
  flags.keys.each { |key| expect! key => String }

  action(service, name).invoke(args: args, flags: flags)
end

.invoke3(service, name, *args, **flags) ⇒ Object

invokes an action with a given name in a service with args and flags.

This is a helper method which one can use to easily call an action from ruby source code.

As the main purpose of this module is to call services with outside data, the .invoke action is usually preferred.

Note: You cannot call this method if the context is not set.



112
113
114
115
# File 'lib/simple/service.rb', line 112

def self.invoke3(service, name, *args, **flags)
  flags = flags.each_with_object({}) { |(k, v), hsh| hsh[k.to_s] = v }
  invoke service, name, args: args, flags: flags
end

.loggerObject



64
65
66
# File 'lib/simple/service.rb', line 64

def self.logger
  @logger ||= ::Logger.new(STDOUT)
end

.logger=(logger) ⇒ Object



68
69
70
# File 'lib/simple/service.rb', line 68

def self.logger=(logger)
  @logger = logger
end

.service?(service) ⇒ Boolean

returns true if the passed in object is a service module.

A service must be a module, and it must include the Simple::Service module.

Returns:

  • (Boolean)


75
76
77
78
79
80
# File 'lib/simple/service.rb', line 75

def self.service?(service)
  verify_service! service
  true
rescue ::ArgumentError
  false
end

.verify_service!(service) ⇒ Object

Raises an error if the passed in object is not a service

Raises:

  • (::ArgumentError)


83
84
85
86
# File 'lib/simple/service.rb', line 83

def self.verify_service!(service) # @private
  raise ::ArgumentError, "#{service.inspect} must be a Simple::Service, but is not even a Module" unless service.is_a?(Module)
  raise ::ArgumentError, "#{service.inspect} must be a Simple::Service, did you 'include Simple::Service'" unless service.include?(self)
end

.with_context(ctx = nil, &block) ⇒ Object

yields a block with a given context, and restores the previous context object afterwards.



9
10
11
12
13
14
15
16
17
18
# File 'lib/simple/service/context.rb', line 9

def self.with_context(ctx = nil, &block)
  old_ctx = Thread.current[:"Simple::Service.context"]
  new_ctx = old_ctx ? old_ctx.merge(ctx) : Context.new(ctx)

  Thread.current[:"Simple::Service.context"] = new_ctx

  block.call
ensure
  Thread.current[:"Simple::Service.context"] = old_ctx
end