Class: LunaPark::UseCases::Scenario

Inherits:
Object
  • Object
show all
Extended by:
Extensions::Callable
Includes:
Extensions::Attributable, Extensions::HasErrors
Defined in:
lib/luna_park/use_cases/scenario.rb

Overview

The main goal of the use case is a high-level description of the business process. This specific implementation is based on the ideas of Ivar Jacobson from his article Ivar Jacobson: Use Case 2.0.

Examples:

Create new user

module Errors
  # To catch the errors, it's should be the error must
  # be inherited from the class LunaPark::Errors::Business
  class UserAlreadyExists < LunaPark::Errors::Business
    message 'Sorry user with this email already created'
    notify: :info
  end
end

class CreateUser < Scenario
  attr_accessor :email, :password

  def perform
    user          = Entities::User.new
    user.email    = email
    user.password = Service::Encode.call(password)

    DB.transaction do
     raise Errors::UserAlreadyExists if Repo::Users.exists?(user)
     Repo::Users.create(user)
    end
  end
end

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Extensions::HasErrors

included

Constructor Details

#initialize(notifier: nil, locale: nil, **attrs) ⇒ Scenario

Initialize new scenario

Examples:

without parameters

class SayHello < Scenario
  attr_accessor :first_name, :last_name

  def perform
    t('hello_my_nme_is', first_name: first_name, last_name: last_name)
  end
end

hello = Scenario.new first_name: 'John', last_name: 'Doe'
hello.notifier    # => Notifiers::Log
hello.locale      # => nil
hello.first_name  # => 'John'
hello.last_name   # => 'Doe'
hello.call!       # => 'Hello my name is John Doe'

with custom parameters

hello = Scenario.new first_name: 'John', last_name: 'Doe', notifier: Notifier::Bugsnag, locale: :ru
hello.notifier    # => Notifiers::Bugsnag
hello.locale      # => :ru
hello.first_name  # => 'John'
hello.last_name   # => 'Doe'
hello.call!       # => 'Добрый день, меня зовут John Doe'

Parameters:

  • notifier (defaults to: nil)
    • custom notifier for the current instance of scenario

  • locale (defaults to: nil)
    • custom locale for the current instance of scenario

  • attrs
    • the parameters that are needed to implement the scenario, usually the request model



153
154
155
156
157
158
159
160
# File 'lib/luna_park/use_cases/scenario.rb', line 153

def initialize(notifier: nil, locale: nil, **attrs)
  set_attributes attrs
  @data     = nil
  @failure  = nil
  @locale   = locale
  @notifier = notifier
  @state    = INIT
end

Instance Attribute Details

#dataObject (readonly)

The result obtained during the execution of the scenario. It’s nil on failure scenario.

Examples:

when work just started

scenario = Scenario.new
scenario.data # => nil

on fail

scenario.call  # Something went wrong
scenario.data  # => nil

on success

class SuccessScenario < Scenario
  def perform
    :result
  end
end

scenario = SuccessScenario.new
scenario.call
scenario.data # => :result


119
120
121
# File 'lib/luna_park/use_cases/scenario.rb', line 119

def data
  @data
end

#failureObject (readonly)

If a failure occurs during the scenario, then this attribute will contain this error Else it’s nil.

Examples:

when work just started

scenario = Scenario.new
scenario.fail # => nil

on fail

class Fail < Errors::Business; end
class FailScenario < Scenario
  def perform
    raise Fail
    :result
  end
end

scenario = FailScenario.new
scenario.call  # Something went wrong
scenario.fail  # => #<Fail: Fail>

on success

scenario.call
scenario.fail # => nil


96
97
98
# File 'lib/luna_park/use_cases/scenario.rb', line 96

def failure
  @failure
end

#localeObject (readonly)

Current locale



122
123
124
# File 'lib/luna_park/use_cases/scenario.rb', line 122

def locale
  @locale
end

#stateObject (readonly)

What status is the process of doing the work under the scenario. It can be :initialized, :success, :failure

Examples:

when work just started

scenario = Scenario.new
scenario.state # => :initialized

on fail

scenario.call  # Something went wrong
scenario.state # => :failure

on success

scenario.call
scenario.state # => :success


71
72
73
# File 'lib/luna_park/use_cases/scenario.rb', line 71

def state
  @state
end

Class Method Details

.default_notifierObject

Returns Default notifier.

Returns:

  • Default notifier



275
276
277
# File 'lib/luna_park/use_cases/scenario.rb', line 275

def default_notifier
  @default_notifier ||= DEFAULT_NOTIFIER
end

.notify_with(notifier) ⇒ Object

Set notifier for this class

Examples:

set notifier

class Foobar < Scenario
  notify_with Notifier::Bugsnag

  def perform
    true
  end
end

Foobar.default_notifier # => Notifier::Bugsnag
Foobar.new.notifier     # => Notifier::Bugsnag


292
293
294
# File 'lib/luna_park/use_cases/scenario.rb', line 292

def notify_with(notifier)
  @default_notifier = notifier
end

Instance Method Details

#callObject

This method is abstract.

You must define this action and describe all business logic here. When you run this method - it run as is, and does not change scenario instance.

Examples:

fail way

class YouDied < Errors::Business; end

class Shot < Scenario
  attr_accessor :lucky_mode

  def perform
    raise YouDied, 'Always something went wrong' unless lucky_mode
    'All good'
  end
end

bad_day = Shot.new lucky_mode: false
bad_day.call         # => #<Shot:0x000055cbee4bc070...>
bad_day.success?     # => false
bad_day.fail?        # => true
bad_day.data         # => nil
bad_day.state        # => :failure
bad_day.fail         # => #<YouDied:0x000055cbee4bc071...>
bad_day.fail_message # => ''

@example main way

good_day = Shot.new lucky_mode: true
good_day.call! # => 'All good'
good_day.state # => :initialized

Russian roulette

class RussianRoulette < Scenario
  def call!
    [true, true, true, true, true, false].shuffle do |bullet|
      Shot.call! lucky_mode: bullet
    end
  end
end


243
244
245
246
# File 'lib/luna_park/use_cases/scenario.rb', line 243

def call
  rescue_exception { @data = call! }
  self
end

#call!Object



162
163
164
# File 'lib/luna_park/use_cases/scenario.rb', line 162

def call!
  perform
end

#fail?Boolean Also known as: failure?, failed?

Returns true if the scenario runs unsuccessfully.

Returns:

  • (Boolean)

    true if the scenario runs unsuccessfully



254
255
256
# File 'lib/luna_park/use_cases/scenario.rb', line 254

def fail?
  state == FAIL
end

#failure_message(locale: nil) ⇒ String

Returns fail message.

Returns:

  • (String)

    fail message



269
270
271
# File 'lib/luna_park/use_cases/scenario.rb', line 269

def failure_message(locale: nil)
  failure&.message(locale: locale || self.locale)
end

#notifierObject

Return notifier



249
250
251
# File 'lib/luna_park/use_cases/scenario.rb', line 249

def notifier
  @notifier ||= self.class.default_notifier
end

#performObject

This method is abstract.

You must define this action and describe all business logic here. When you run this method - it run as is, and does not change scenario instance.

Examples:

Fail way

class Shot < Scenario
  attr_accessor :lucky_mode

  def perform
    raise YouDied, 'Always something went wrong' unless lucky_mode
    'All good'
  end
end

bad_day = Shot.new lucky_mode: false
bad_day.call! # it raise - SomethingWentWrong: Always something went wrong
bad_day.state # => :initialized

Main way

good_day = Shot.new lucky_mode: true
good_day.call! # => 'All good'
good_day.state # => :initialized

Russian roulette

# `.call!` usually use for "scenario in scenario"
class RussianRoulette < Scenario
  def perform
    [true, true, true, true, true, false].shuffle do |bullet|
      Shot.call! lucky_mode: bullet
    end
  end
end

Raises:



199
200
201
# File 'lib/luna_park/use_cases/scenario.rb', line 199

def perform
  raise Errors::AbstractMethod
end

#success?Boolean Also known as: succeed?

Returns true if the scenario runs successfully.

Returns:

  • (Boolean)

    true if the scenario runs successfully



262
263
264
# File 'lib/luna_park/use_cases/scenario.rb', line 262

def success?
  state == SUCCESS
end