Class: TheHelp::Service

Inherits:
Object
  • Object
show all
Includes:
ProvidesCallbacks
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 NotAuthorizedError,
        "Not authorized to access #{service.name} as #{context.inspect}."
}

Class Method Summary collapse

Instance Method Summary collapse

Methods included from ProvidesCallbacks

included

Constructor Details

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

Returns a new instance of Service.



141
142
143
144
145
146
147
# File 'lib/the_help/service.rb', line 141

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

.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.

Parameters:

  • allow_all (Boolean) (defaults to: false)
  • block (Proc)

    executed in the context of the service instance (and can therefore access all inputs to the service)



116
117
118
119
120
121
122
123
124
# File 'lib/the_help/service.rb', line 116

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

Returns:

  • (Class)

    Returns the receiver



81
82
83
84
# File 'lib/the_help/service.rb', line 81

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

.inherited(other) ⇒ Object

:nodoc:



87
88
89
# File 'lib/the_help/service.rb', line 87

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

.input(name, **options) ⇒ Object



126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/the_help/service.rb', line 126

def input(name, **options)
  attr_accessor name
  if options.key?(:default)
    required_inputs.delete(name)
    define_method(name) do
      instance_variable_get("@#{name}") || options[:default]
    end
  else
    required_inputs << name
  end
  private name, "#{name}="
  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.



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

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



93
94
95
# File 'lib/the_help/service.rb', line 93

def required_inputs
  @required_inputs ||= Set.new
end

Instance Method Details

#callObject



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

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