Class: TheHelp::Service

Inherits:
Object
  • Object
show all
Includes:
ProvidesCallbacks, ServiceCaller
Defined in:
lib/the_help/service.rb

Overview

An Abstract Service Class with Authorization and Logging

Define subclasses of Service to build out the service layer of your application.

class CreateNewUserAccount < TheHelp::Service

input :user
input :send_welcome_message, default: true

authorization_policy do
  authorized = false
  call_service(Authorize, permission: :admin_users,
               allowed: ->() { authorized = true })
  authorized
end

main do
  # do something to create the user account
  if send_welcome_message
    call_service(SendWelcomeMessage, user: user,
                 success: callback(:message_sent))
  end
end

callback(:message_sent) do
  # do something really important, I'm sure
end

end

class Authorize < TheHelp::Service

input :permission
input :allowed

authorization_policy allow_all: true

main do
  if user_has_permission?
    allowed.call
  end
end

end

class SendWelcomeMessage < TheHelp::Service

input :user
input :success, default: ->() { }

main do
  # whatever
  success.call
end

end

CreateNewUserAccount.(context: current_user, user: new_user_object)

Constant Summary collapse

CB_NOT_AUTHORIZED =

The default :not_authorized callback

It will raise a TheHelp::NotAuthorizedError when the context is not authorized to perform the service.

->(service:, context:) {
  raise TheHelp::NotAuthorizedError,
        "Not authorized to access #{service.name} as #{context.inspect}."
}

Class Method Summary collapse

Instance Method Summary collapse

Methods included from ServiceCaller

#call_service

Methods included from ProvidesCallbacks

included

Constructor Details

#initialize(context:, logger: Logger.new($stdout), not_authorized: CB_NOT_AUTHORIZED, **inputs) ⇒ Service



149
150
151
152
153
154
155
# File 'lib/the_help/service.rb', line 149

def initialize(context:, logger: Logger.new($stdout),
               not_authorized: CB_NOT_AUTHORIZED, **inputs)
  self.context = context
  self.logger = logger
  self.not_authorized = not_authorized
  self.inputs = inputs
end

Class Method Details

.attr_accessor(*names, make_private: false, private_reader: false, private_writer: false) ⇒ Object

Defines attr_accessors with scoping options



76
77
78
79
80
81
82
83
# File 'lib/the_help/service.rb', line 76

def attr_accessor(*names, make_private: false, private_reader: false,
                  private_writer: false)
  super(*names)
  names.each do |name|
    private name if make_private || private_reader
    private "#{name}=" if make_private || private_writer
  end
end

.authorization_policy(allow_all: false, &block) ⇒ Object

Defines the service authorization policy

If allow_all is set to true, or if the provided block (executed in the context of the service object) returns true, then the service will be run when called. Otherwise, the not_authorized callback will be invoked.



125
126
127
128
129
130
131
132
133
# File 'lib/the_help/service.rb', line 125

def authorization_policy(allow_all: false, &block)
  if allow_all
    define_method(:authorized?) { true }
  else
    define_method(:authorized?, &block)
  end
  private :authorized?
  self
end

.call(*args) ⇒ Class

Convenience method to instantiate the service and immediately call it

Any arguments are passed to #initialize



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

def call(*args)
  new(*args).call
  self
end

.inherited(other) ⇒ Object

:nodoc:



96
97
98
# File 'lib/the_help/service.rb', line 96

def inherited(other)
  other.instance_variable_set(:@required_inputs, required_inputs.dup)
end

.input(name, **options) ⇒ Object



135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/the_help/service.rb', line 135

def input(name, **options)
  attr_accessor name, make_private: true
  if options.key?(:default)
    required_inputs.delete(name)
    define_method(name) do
      instance_variable_get("@#{name}") || options[:default]
    end
  else
    required_inputs << name
  end
  self
end

.main(&block) ⇒ Object

Defines the primary routine of the service

The code that will be run when the service is called, assuming it is unauthorized.



110
111
112
113
114
# File 'lib/the_help/service.rb', line 110

def main(&block)
  define_method(:main, &block)
  private :main
  self
end

.required_inputsObject

:nodoc: instances need access to this, otherwise it would be made private



102
103
104
# File 'lib/the_help/service.rb', line 102

def required_inputs
  @required_inputs ||= Set.new
end

Instance Method Details

#callObject



157
158
159
160
161
162
163
164
165
# File 'lib/the_help/service.rb', line 157

def call
  validate_service_definition
  catch(:stop) do
    authorize
    log_service_call
    main
  end
  self
end