Class: ServiceObjects::Base

Inherits:
Object
  • Object
show all
Includes:
Attestor::Validations, AttrCoerced, Virtus::Model::Core, Wisper::Publisher
Defined in:
lib/service_objects/base.rb

Overview

Base class for service objects

Examples:

class ChangeFoo < ServiceObjects::Base

  # Attributes definition
  attribute    :id,   Integer
  attribute    :name
  attr_coerced :name, String

  # Object validation rules
  validate { invalid :blank_id   unless id }
  validate { invalid :blank_name unless name }

  # Injectable dependencies
  dependency :get_foo, default: GetFoo

  private

  # The main algorithm
  def run!
    validate
    get_foo
    change_foo
  end

  attr_accessor :foo

  def get_foo
    run_service get_foo.new(id: id), Listener.new(self)
  end

  def change_foo
    foo.save! name: name
    publish :changed, foo, message(:success, :changed, name: name)
  rescue => error
    publish :error, message(:error, error.message)
  end

  class Listener < ServiceObjects::Listener
    def on_found(foo, *)
      self.foo = foo
    end

    def otherwise
      publish :not_found, message(:error, :not_found, id: id)
    end
  end
end

See Also:

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.dependency(name, default: nil) ⇒ undefined

Declares the dependency from another class

Parameters:

  • name (#to_sym)
  • [Class] (Hash)

    a customizable set of options

Returns:

  • (undefined)


82
83
84
85
86
# File 'lib/service_objects/base.rb', line 82

def self.dependency(name, default: nil)
  attr_accessor(name)
  return unless default
  define_method(name) { instance_eval "@#{ name } ||= #{ default }" }
end

Instance Method Details

#message(type, key, options) ⇒ ServiceObjects::Message #message(type, key) ⇒ ServiceObjects::Message

Creates the message of given type

Overloads:

  • #message(type, key, options) ⇒ ServiceObjects::Message

    Translates the symbolic key

    Examples:

    # config/locales/en.yml
    en:
      service_objects:
        get_foo:
          info:
            found: "Item with id %{id} has been found"
    result = GetFoo.new.message(:info, :found, id: 1)
    
    result.class # => ServiceObjects::Message
    result.type  # => :info
    result.text  # => "Item with id 1 has been found"

    Parameters:

    • type (Symbol)
    • key (Symbol)
    • options (Hash)

    Returns:

  • #message(type, key) ⇒ ServiceObjects::Message

    Stringifies non-symbolic key

    Examples:

    result = GetFoo.new.message(:info, 1)
    
    result.class # => ServiceObjects::Message
    result.type  # => :info
    result.text  # => "1"

    Parameters:

    • type (Symbol)
    • key (#to_s)

    Returns:



173
174
175
# File 'lib/service_objects/base.rb', line 173

def message(type, *args)
  Message.new type, translate(type, *args)
end

#publish(notification, *args) ⇒ undefined

Notifies subscribers and then throws :published

Parameters:

  • notification (#to_sym)
  • args (Array)

Returns:

  • (undefined)

Raises:

  • (UncaughtThrowError)

    :published

See Also:



188
189
190
191
# File 'lib/service_objects/base.rb', line 188

def publish(*)
  super
  throw :published
end

#runundefined

Runs the service by calling #run! and catching its :published throws

Returns:

  • (undefined)

Raises:

  • (NotImplementedError)

    if #run! method not implemented yet



103
104
105
# File 'lib/service_objects/base.rb', line 103

def run
  catch(:published) { run! }
end

#run_service(service, listener) ⇒ undefined

Runs the service and receives its notifications to listener

Parameters:

Returns:

  • (undefined)


126
127
128
129
130
# File 'lib/service_objects/base.rb', line 126

def run_service(service, listener)
  service.subscribe listener, prefix: :on
  service.run
  listener.finalize
end

#subscribe(listener, prefix: nil) ⇒ undefined

Subscribes a listener for the service object’s notifications

Parameters:

  • listener (Object)

Returns:

  • (undefined)

See Also:



# File 'lib/service_objects/base.rb', line 88

#validate(context) ⇒ undefined

Validates the object in given context

Publishes error if validation fails

Parameters:

  • context (#to_sym)

Returns:

  • (undefined)


114
115
116
117
118
# File 'lib/service_objects/base.rb', line 114

def validate(context)
  validate!(context)
rescue Attestor::InvalidError => error
  publish :error, error.messages.map { |text| Message.new :error, text }
end