action_logic 0.0.3 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +72 -0
- data/README.md +6 -0
- data/action_logic.gemspec +24 -0
- data/lib/action_logic/action_context.rb +39 -0
- data/lib/action_logic/action_coordinator.rb +42 -0
- data/lib/action_logic/action_core.rb +45 -0
- data/lib/action_logic/action_task.rb +24 -0
- data/lib/action_logic/action_use_case.rb +28 -0
- data/lib/action_logic/action_validation.rb +111 -0
- data/lib/action_logic/errors.rb +6 -0
- data/lib/action_logic/version.rb +3 -0
- data/spec/action_logic/action_context_spec.rb +75 -0
- data/spec/action_logic/action_coordinator_spec.rb +162 -0
- data/spec/action_logic/action_task_spec.rb +234 -0
- data/spec/action_logic/active_use_case_spec.rb +199 -0
- data/spec/fixtures/constants.rb +46 -0
- data/spec/fixtures/coordinators.rb +564 -0
- data/spec/fixtures/custom_types.rb +5 -0
- data/spec/fixtures/tasks.rb +286 -0
- data/spec/fixtures/use_cases.rb +324 -0
- data/spec/spec_helper.rb +17 -0
- metadata +35 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b6d36552e9a95bd397f6841f5ea609b870885349
|
4
|
+
data.tar.gz: f2e121ac6007fcd73d09a88988053adc7892fe4a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8ae1e86d81b1d5799d95089eb6319402610046880d7c1856ce420fba6865a6029191e606d5093c595e839179773b16e91f10fb53bc4e1489300fd9202474afb0
|
7
|
+
data.tar.gz: 869e66643e26a901d707f4887539649ebe13e160199fc361190099be4b46dfe0761eb3e75d40805e12f8db72faf2c92f9ae865d375ce2ca66238f3686a008488
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
action_logic (0.0.1)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
coderay (1.1.0)
|
10
|
+
coveralls (0.8.3)
|
11
|
+
json (~> 1.8)
|
12
|
+
rest-client (>= 1.6.8, < 2)
|
13
|
+
simplecov (~> 0.10.0)
|
14
|
+
term-ansicolor (~> 1.3)
|
15
|
+
thor (~> 0.19.1)
|
16
|
+
diff-lcs (1.2.5)
|
17
|
+
docile (1.1.5)
|
18
|
+
domain_name (0.5.25)
|
19
|
+
unf (>= 0.0.5, < 1.0.0)
|
20
|
+
http-cookie (1.0.2)
|
21
|
+
domain_name (~> 0.5)
|
22
|
+
json (1.8.3)
|
23
|
+
method_source (0.8.2)
|
24
|
+
mime-types (2.6.2)
|
25
|
+
netrc (0.11.0)
|
26
|
+
pry (0.10.3)
|
27
|
+
coderay (~> 1.1.0)
|
28
|
+
method_source (~> 0.8.1)
|
29
|
+
slop (~> 3.4)
|
30
|
+
rest-client (1.8.0)
|
31
|
+
http-cookie (>= 1.0.2, < 2.0)
|
32
|
+
mime-types (>= 1.16, < 3.0)
|
33
|
+
netrc (~> 0.7)
|
34
|
+
rspec (3.3.0)
|
35
|
+
rspec-core (~> 3.3.0)
|
36
|
+
rspec-expectations (~> 3.3.0)
|
37
|
+
rspec-mocks (~> 3.3.0)
|
38
|
+
rspec-core (3.3.2)
|
39
|
+
rspec-support (~> 3.3.0)
|
40
|
+
rspec-expectations (3.3.1)
|
41
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
42
|
+
rspec-support (~> 3.3.0)
|
43
|
+
rspec-mocks (3.3.2)
|
44
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
45
|
+
rspec-support (~> 3.3.0)
|
46
|
+
rspec-support (3.3.0)
|
47
|
+
simplecov (0.10.0)
|
48
|
+
docile (~> 1.1.0)
|
49
|
+
json (~> 1.8)
|
50
|
+
simplecov-html (~> 0.10.0)
|
51
|
+
simplecov-html (0.10.0)
|
52
|
+
slop (3.6.0)
|
53
|
+
term-ansicolor (1.3.2)
|
54
|
+
tins (~> 1.0)
|
55
|
+
thor (0.19.1)
|
56
|
+
tins (1.6.0)
|
57
|
+
unf (0.1.4)
|
58
|
+
unf_ext
|
59
|
+
unf_ext (0.0.7.1)
|
60
|
+
|
61
|
+
PLATFORMS
|
62
|
+
ruby
|
63
|
+
|
64
|
+
DEPENDENCIES
|
65
|
+
action_logic!
|
66
|
+
coveralls (~> 0.8.3)
|
67
|
+
pry (~> 0.10)
|
68
|
+
rspec (~> 3.3)
|
69
|
+
simplecov (~> 0.10.0)
|
70
|
+
|
71
|
+
BUNDLED WITH
|
72
|
+
1.10.6
|
data/README.md
ADDED
@@ -0,0 +1,6 @@
|
|
1
|
+
# ActionLogic
|
2
|
+
[](https://badge.fury.io/rb/action_logic)
|
3
|
+
[](https://codeclimate.com/github/rewinfrey/action_logic)
|
4
|
+
[](https://coveralls.io/github/rewinfrey/action_logic?branch=master)
|
5
|
+
|
6
|
+
[](https://codeship.com/projects/114179)
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require File.expand_path('../lib/action_logic/version', __FILE__)
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = 'action_logic'
|
5
|
+
s.summary = 'Business logic abstraction'
|
6
|
+
s.homepage = 'https://github.com/rewinfrey/action_logic'
|
7
|
+
s.license = 'MIT'
|
8
|
+
|
9
|
+
s.files = `git ls-files`.split($\)
|
10
|
+
s.executables = s.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
11
|
+
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
12
|
+
s.require_paths = ["lib"]
|
13
|
+
s.version = ActionLogic::VERSION
|
14
|
+
|
15
|
+
s.authors = ["Rick Winfrey"]
|
16
|
+
s.email = 'rick.winfrey@gmail.com'
|
17
|
+
s.date = '2015-11-03'
|
18
|
+
s.description = 'Provides common interfaces for validating and abstracting business logic'
|
19
|
+
|
20
|
+
s.add_development_dependency("rspec", "~> 3.3")
|
21
|
+
s.add_development_dependency("pry", "~> 0.10")
|
22
|
+
s.add_development_dependency("simplecov", "~> 0.10.0")
|
23
|
+
s.add_development_dependency("coveralls", "~> 0.8.3")
|
24
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
|
3
|
+
module ActionLogic
|
4
|
+
class ActionContext < OpenStruct
|
5
|
+
SUCCESS = :success
|
6
|
+
FAILURE = :failure
|
7
|
+
HALTED = :halted
|
8
|
+
|
9
|
+
def initialize(params = {})
|
10
|
+
params[:status] ||= SUCCESS
|
11
|
+
super(params)
|
12
|
+
end
|
13
|
+
|
14
|
+
def update!(status, message)
|
15
|
+
self.status = status
|
16
|
+
self.message = message
|
17
|
+
end
|
18
|
+
|
19
|
+
def fail!(message = "")
|
20
|
+
update!(FAILURE, message)
|
21
|
+
end
|
22
|
+
|
23
|
+
def halt!(message = "")
|
24
|
+
update!(HALTED, message)
|
25
|
+
end
|
26
|
+
|
27
|
+
def success?
|
28
|
+
self.status == SUCCESS
|
29
|
+
end
|
30
|
+
|
31
|
+
def failure?
|
32
|
+
self.status == FAILURE
|
33
|
+
end
|
34
|
+
|
35
|
+
def halted?
|
36
|
+
self.status == HALTED
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'action_logic/action_core'
|
2
|
+
require 'action_logic/action_validation'
|
3
|
+
|
4
|
+
module ActionLogic
|
5
|
+
module ActionCoordinator
|
6
|
+
include ActionLogic::ActionValidation
|
7
|
+
include ActionLogic::ActionCore
|
8
|
+
|
9
|
+
def self.included(klass)
|
10
|
+
klass.extend ClassMethods
|
11
|
+
klass.extend ActionLogic::ActionCore::ClassMethods
|
12
|
+
klass.extend ActionLogic::ActionValidation::ClassMethods
|
13
|
+
end
|
14
|
+
|
15
|
+
module ClassMethods
|
16
|
+
def execute(params = {})
|
17
|
+
around(params) do |execution_context|
|
18
|
+
execution_context.call
|
19
|
+
|
20
|
+
next_execution_context = execution_context.plan.keys.first
|
21
|
+
|
22
|
+
while (next_execution_context) do
|
23
|
+
execution_context.context = next_execution_context.execute(execution_context.context)
|
24
|
+
next_execution_context = execution_context.plan[next_execution_context][execution_context.context.status]
|
25
|
+
|
26
|
+
# From the perspective of the coordinator, the status of the context should be
|
27
|
+
# :success as long as the state transition plan defines the next execution context
|
28
|
+
# for a given current exection context and its resulting context state.
|
29
|
+
# However, because normally a context in a state of :halted or :failure would
|
30
|
+
# be considered a "breaking" state, the status of a context that is :halted or :failure
|
31
|
+
# has to be reset to the default :success status only within the execution context of
|
32
|
+
# the coordinator and only when the next execution context is defined within the
|
33
|
+
# state transition plan. Otherwise, the context is return as is, without mutating its :status.
|
34
|
+
execution_context.context.status = :success if next_execution_context
|
35
|
+
end
|
36
|
+
|
37
|
+
execution_context.context
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module ActionLogic
|
2
|
+
module ActionCore
|
3
|
+
attr_accessor :context
|
4
|
+
|
5
|
+
def initialize(params)
|
6
|
+
self.context = make_context(params)
|
7
|
+
end
|
8
|
+
|
9
|
+
def make_context(params = {})
|
10
|
+
ActionContext.new(params)
|
11
|
+
end
|
12
|
+
|
13
|
+
def break?
|
14
|
+
context.status == :failure ||
|
15
|
+
context.status == :halted
|
16
|
+
end
|
17
|
+
|
18
|
+
module ClassMethods
|
19
|
+
def around(params, &block)
|
20
|
+
execution_context = self.new(params)
|
21
|
+
|
22
|
+
return execution_context.context if execution_context.break?
|
23
|
+
|
24
|
+
execution_context.set_validation_rules
|
25
|
+
execution_context.validations!(:before)
|
26
|
+
execution_context.validations!(:around)
|
27
|
+
|
28
|
+
begin
|
29
|
+
block.call(execution_context)
|
30
|
+
rescue => e
|
31
|
+
if execution_context.respond_to?(:error)
|
32
|
+
execution_context.error(e)
|
33
|
+
else
|
34
|
+
raise e
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
execution_context.validations!(:after)
|
39
|
+
execution_context.validations!(:around)
|
40
|
+
|
41
|
+
execution_context.context
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'action_logic/action_core'
|
2
|
+
require 'action_logic/action_validation'
|
3
|
+
|
4
|
+
module ActionLogic
|
5
|
+
module ActionTask
|
6
|
+
include ActionLogic::ActionValidation
|
7
|
+
include ActionLogic::ActionCore
|
8
|
+
|
9
|
+
def self.included(klass)
|
10
|
+
klass.extend ClassMethods
|
11
|
+
klass.extend ActionLogic::ActionCore::ClassMethods
|
12
|
+
klass.extend ActionLogic::ActionValidation::ClassMethods
|
13
|
+
end
|
14
|
+
|
15
|
+
module ClassMethods
|
16
|
+
def execute(params = {})
|
17
|
+
around(params) do |execution_context|
|
18
|
+
execution_context.call
|
19
|
+
execution_context.context
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'action_logic/action_core'
|
2
|
+
require 'action_logic/action_validation'
|
3
|
+
|
4
|
+
module ActionLogic
|
5
|
+
module ActionUseCase
|
6
|
+
include ActionLogic::ActionValidation
|
7
|
+
include ActionLogic::ActionCore
|
8
|
+
|
9
|
+
def self.included(klass)
|
10
|
+
klass.extend ClassMethods
|
11
|
+
klass.extend ActionLogic::ActionCore::ClassMethods
|
12
|
+
klass.extend ActionLogic::ActionValidation::ClassMethods
|
13
|
+
end
|
14
|
+
|
15
|
+
module ClassMethods
|
16
|
+
def execute(params = {})
|
17
|
+
around(params) do |execution_context|
|
18
|
+
execution_context.call
|
19
|
+
|
20
|
+
execution_context.tasks.reduce(execution_context.context) do |context, task|
|
21
|
+
execution_context.context = task.execute(context)
|
22
|
+
execution_context.context
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'action_logic/errors'
|
2
|
+
|
3
|
+
module ActionLogic
|
4
|
+
module ActionValidation
|
5
|
+
|
6
|
+
module ClassMethods
|
7
|
+
def validates_before(args)
|
8
|
+
@validates_before = args
|
9
|
+
end
|
10
|
+
|
11
|
+
def validates_after(args)
|
12
|
+
@validates_after = args
|
13
|
+
end
|
14
|
+
|
15
|
+
def validates_around(args)
|
16
|
+
@validates_around = args
|
17
|
+
end
|
18
|
+
|
19
|
+
def get_validates_before
|
20
|
+
@validates_before
|
21
|
+
end
|
22
|
+
|
23
|
+
def get_validates_after
|
24
|
+
@validates_after
|
25
|
+
end
|
26
|
+
|
27
|
+
def get_validates_around
|
28
|
+
@validates_around
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def validations
|
33
|
+
[:validate_attributes!,
|
34
|
+
:validate_types!,
|
35
|
+
:validate_presence!]
|
36
|
+
end
|
37
|
+
|
38
|
+
def validate!(validation, validation_rules)
|
39
|
+
return if validation_rules.empty?
|
40
|
+
send(validation, validation_rules)
|
41
|
+
end
|
42
|
+
|
43
|
+
def validations!(validation_order)
|
44
|
+
case validation_order
|
45
|
+
when :before then validations.each { |validation| validate!(validation, @before_validation_rules) }
|
46
|
+
when :after then validations.each { |validation| validate!(validation, @after_validation_rules) }
|
47
|
+
when :around then validations.each { |validation| validate!(validation, @around_validation_rules) }
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def set_validation_rules
|
52
|
+
@before_validation_rules ||= self.class.get_validates_before || {}
|
53
|
+
@after_validation_rules ||= self.class.get_validates_after || {}
|
54
|
+
@around_validation_rules ||= self.class.get_validates_around || {}
|
55
|
+
end
|
56
|
+
|
57
|
+
def validate_attributes!(validations)
|
58
|
+
existing_attributes = context.to_h.keys
|
59
|
+
expected_attributes = validations.keys || []
|
60
|
+
missing_attributes = expected_attributes - existing_attributes
|
61
|
+
|
62
|
+
raise ActionLogic::MissingAttributeError.new(missing_attributes) if missing_attributes.any?
|
63
|
+
end
|
64
|
+
|
65
|
+
def validate_types!(validations)
|
66
|
+
return unless validations.values.find { |expected_validation| expected_validation[:type] }
|
67
|
+
|
68
|
+
type_errors = validations.reduce([]) do |collection, (expected_attribute, expected_validation)|
|
69
|
+
next unless expected_validation[:type]
|
70
|
+
|
71
|
+
if type_to_sym(context.to_h[expected_attribute]) != expected_validation[:type]
|
72
|
+
collection << "Attribute: #{expected_attribute} with value: #{context.to_h[expected_attribute]} was expected to be of type #{expected_validation[:type]} but is #{type_to_sym(context.to_h[expected_attribute])}"
|
73
|
+
end
|
74
|
+
collection
|
75
|
+
end
|
76
|
+
|
77
|
+
raise ActionLogic::AttributeTypeError.new(type_errors) if type_errors.any?
|
78
|
+
end
|
79
|
+
|
80
|
+
def validate_presence!(validations)
|
81
|
+
return unless validations.values.find { |expected_validation| expected_validation[:presence] }
|
82
|
+
|
83
|
+
presence_errors = validations.reduce([]) do |collection, (expected_attribute, expected_validation)|
|
84
|
+
next unless expected_validation[:presence]
|
85
|
+
|
86
|
+
if expected_validation[:presence] == true
|
87
|
+
collection << "Attribute: #{expected_attribute} is missing value in context but presence validation was specified" unless context[expected_attribute]
|
88
|
+
elsif expected_validation[:presence].class == Proc
|
89
|
+
collection << "Attribute: #{expected_attribute} is missing value in context but custom presence validation was specified" unless expected_validation[:presence].call(context[expected_attribute])
|
90
|
+
else
|
91
|
+
raise ActionLogic::UnrecognizablePresenceValidatorError.new("Presence validator: #{expected_validation[:presence]} is not a supported format")
|
92
|
+
end
|
93
|
+
|
94
|
+
collection
|
95
|
+
end || []
|
96
|
+
|
97
|
+
raise ActionLogic::PresenceError.new(presence_errors) if presence_errors.any?
|
98
|
+
end
|
99
|
+
|
100
|
+
def type_to_sym(value)
|
101
|
+
case value.class.name.downcase.to_sym
|
102
|
+
when :fixnum then :integer
|
103
|
+
when :falseclass then :boolean
|
104
|
+
when :trueclass then :boolean
|
105
|
+
when :nilclass then :nil
|
106
|
+
else
|
107
|
+
value.class.name.downcase.to_sym
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'action_logic'
|
3
|
+
require 'fixtures/constants'
|
4
|
+
|
5
|
+
module ActionLogic
|
6
|
+
describe ActionContext do
|
7
|
+
subject { ActionContext.new }
|
8
|
+
|
9
|
+
describe "initialization" do
|
10
|
+
it "sets a default success attribute on the context" do
|
11
|
+
expect(subject.status).to eq(described_class::SUCCESS)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "success?" do
|
16
|
+
it "returns true if the context is successful" do
|
17
|
+
expect(subject.success?).to be_truthy
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "failing a context" do
|
22
|
+
it "sets the context status as failed" do
|
23
|
+
subject.fail!
|
24
|
+
|
25
|
+
expect(subject.status).to eq(:failure)
|
26
|
+
end
|
27
|
+
|
28
|
+
it "does not require a message" do
|
29
|
+
subject.fail!
|
30
|
+
|
31
|
+
expect(subject.message).to be_empty
|
32
|
+
end
|
33
|
+
|
34
|
+
it "allows a custom failure message to be defined" do
|
35
|
+
failure_message = Constants::FAILURE_MESSAGE
|
36
|
+
subject.fail!(failure_message)
|
37
|
+
|
38
|
+
expect(subject.message).to eq(failure_message)
|
39
|
+
end
|
40
|
+
|
41
|
+
it "responds to directly query" do
|
42
|
+
subject.fail!
|
43
|
+
|
44
|
+
expect(subject.failure?).to be_truthy
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe "halting a context" do
|
49
|
+
it "sets the context status as halted" do
|
50
|
+
subject.halt!
|
51
|
+
|
52
|
+
expect(subject.status).to eq(:halted)
|
53
|
+
end
|
54
|
+
|
55
|
+
it "does not require a message" do
|
56
|
+
subject.halt!
|
57
|
+
|
58
|
+
expect(subject.message).to be_empty
|
59
|
+
end
|
60
|
+
|
61
|
+
it "allows a custom halted message to be defined" do
|
62
|
+
halt_message = Constants::HALT_MESSAGE
|
63
|
+
subject.halt!(halt_message)
|
64
|
+
|
65
|
+
expect(subject.message).to eq(halt_message)
|
66
|
+
end
|
67
|
+
|
68
|
+
it "responds to direct query" do
|
69
|
+
subject.halt!
|
70
|
+
|
71
|
+
expect(subject.halted?).to be_truthy
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,162 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'action_logic'
|
3
|
+
require 'fixtures/coordinators'
|
4
|
+
require 'fixtures/custom_types'
|
5
|
+
|
6
|
+
module ActionLogic
|
7
|
+
describe ActionCoordinator do
|
8
|
+
context "no failures and no halts" do
|
9
|
+
it "evaluates all use cases defined by the state transition plan" do
|
10
|
+
result = TestCoordinator1.execute()
|
11
|
+
|
12
|
+
expect(result.test_coordinator1).to be_truthy
|
13
|
+
expect(result.test_use_case1).to be_truthy
|
14
|
+
expect(result.test_task1).to be_truthy
|
15
|
+
expect(result.test_use_case2).to be_truthy
|
16
|
+
expect(result.test_task2).to be_truthy
|
17
|
+
expect(result.test_use_case3).to be_truthy
|
18
|
+
expect(result.test_task3).to be_truthy
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
context "with halts" do
|
23
|
+
it "evaluates all use cases defined by the state transition plan" do
|
24
|
+
result = HaltedTestCoordinator1.execute()
|
25
|
+
|
26
|
+
expect(result.halted_test_coordinator1).to be_truthy
|
27
|
+
expect(result.halted_test_use_case1).to be_truthy
|
28
|
+
expect(result.halted_test_task1).to be_truthy
|
29
|
+
expect(result.test_use_case2).to be_truthy
|
30
|
+
expect(result.test_task2).to be_truthy
|
31
|
+
expect(result.test_use_case3).to be_truthy
|
32
|
+
expect(result.test_task3).to be_truthy
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context "with failures" do
|
37
|
+
it "evaluates all use cases defined by the state transition plan" do
|
38
|
+
result = FailureTestCoordinator1.execute()
|
39
|
+
|
40
|
+
expect(result.failure_test_coordinator1).to be_truthy
|
41
|
+
expect(result.failure_test_use_case1).to be_truthy
|
42
|
+
expect(result.failure_test_task1).to be_truthy
|
43
|
+
expect(result.test_use_case2).to be_truthy
|
44
|
+
expect(result.test_task2).to be_truthy
|
45
|
+
expect(result.test_use_case3).to be_truthy
|
46
|
+
expect(result.test_task3).to be_truthy
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe "before validations" do
|
51
|
+
describe "required attributes and type validation" do
|
52
|
+
it "does not raise error if context has required keys and values are of the correct type" do
|
53
|
+
expect { ValidateBeforeTestCoordinator.execute(Constants::VALID_ATTRIBUTES) }.to_not raise_error
|
54
|
+
end
|
55
|
+
|
56
|
+
it "raises error if context is missing required keys" do
|
57
|
+
expect { ValidateBeforeTestCoordinator.execute() }.to\
|
58
|
+
raise_error(ActionLogic::MissingAttributeError)
|
59
|
+
end
|
60
|
+
|
61
|
+
it "raises error if context has required key but is not of correct type" do
|
62
|
+
expect { ValidateBeforeTestCoordinator.execute(Constants::INVALID_ATTRIBUTES) }.to\
|
63
|
+
raise_error(ActionLogic::AttributeTypeError)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe "custom types" do
|
68
|
+
it "allows validation against custom defined types" do
|
69
|
+
expect { ValidateBeforeCustomTypeTestCoordinator.execute(Constants::CUSTOM_TYPE_ATTRIBUTES1) }.to_not raise_error
|
70
|
+
end
|
71
|
+
|
72
|
+
it "raises error if context has custom type attribute but value is not correct custom type" do
|
73
|
+
expect { ValidateBeforeCustomTypeTestCoordinator.execute(Constants::CUSTOM_TYPE_ATTRIBUTES2) }.to\
|
74
|
+
raise_error(ActionLogic::AttributeTypeError)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe "presence" do
|
79
|
+
it "validates presence if presence is specified" do
|
80
|
+
expect { ValidateBeforePresenceTestCoordinator.execute(:integer_test => 1) }.to_not raise_error
|
81
|
+
end
|
82
|
+
|
83
|
+
it "raises error if context has required key but value is not defined when validation requires presence" do
|
84
|
+
expect { ValidateBeforePresenceTestCoordinator.execute(:integer_test => nil) }.to\
|
85
|
+
raise_error(ActionLogic::PresenceError)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
describe "custom presence" do
|
90
|
+
it "allows custom presence validation if custom presence is defined" do
|
91
|
+
expect { ValidateBeforeCustomPresenceTestCoordinator.execute(:array_test => [1]) }.to_not raise_error
|
92
|
+
end
|
93
|
+
|
94
|
+
it "raises error if custom presence validation is not satisfied" do
|
95
|
+
expect { ValidateBeforeCustomPresenceTestCoordinator.execute(:array_test => []) }.to\
|
96
|
+
raise_error(ActionLogic::PresenceError)
|
97
|
+
end
|
98
|
+
|
99
|
+
it "raises error if custom presence validation is not supported" do
|
100
|
+
expect { ValidateBeforeUnrecognizablePresenceTestCoordinator.execute(:integer_test => 1) }.to\
|
101
|
+
raise_error(ActionLogic::UnrecognizablePresenceValidatorError)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
describe "after validations" do
|
107
|
+
describe "required attributes and type validation" do
|
108
|
+
it "does not raise error if the task sets all required keys and values are of the correct type" do
|
109
|
+
expect { ValidateAfterTestCoordinator.execute() }.to_not raise_error
|
110
|
+
end
|
111
|
+
|
112
|
+
it "raises error if task does not provide the necessary keys" do
|
113
|
+
expect { ValidateAfterMissingAttributesTestCoordinator.execute() }.to\
|
114
|
+
raise_error(ActionLogic::MissingAttributeError)
|
115
|
+
end
|
116
|
+
|
117
|
+
it "raises error if task has required key but is not of correct type" do
|
118
|
+
expect { ValidateAfterInvalidTypeTestCoordinator.execute() }.to\
|
119
|
+
raise_error(ActionLogic::AttributeTypeError)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
describe "custom types" do
|
124
|
+
it "allows validation against custom defined types" do
|
125
|
+
expect { ValidateAfterCustomTypeTestCoordinator.execute() }.to_not raise_error
|
126
|
+
end
|
127
|
+
|
128
|
+
it "raises error if context has custom type attribute but value is not correct custom type" do
|
129
|
+
expect { ValidateAfterInvalidCustomTypeTestCoordinator.execute() }.to\
|
130
|
+
raise_error(ActionLogic::AttributeTypeError)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
describe "presence" do
|
135
|
+
it "validates presence if presence is specified" do
|
136
|
+
expect { ValidateAfterPresenceTestCoordinator.execute() }.to_not raise_error
|
137
|
+
end
|
138
|
+
|
139
|
+
it "raises error if context has required key but value is not defined when validation requires presence" do
|
140
|
+
expect { ValidateAfterInvalidPresenceTestCoordinator.execute() }.to\
|
141
|
+
raise_error(ActionLogic::PresenceError)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
describe "custom presence" do
|
146
|
+
it "allows custom presence validation if custom presence is defined" do
|
147
|
+
expect { ValidateAfterCustomPresenceTestCoordinator.execute() }.to_not raise_error
|
148
|
+
end
|
149
|
+
|
150
|
+
it "raises error if custom presence validation is not satisfied" do
|
151
|
+
expect { ValidateAfterInvalidCustomPresenceTestCoordinator.execute() }.to\
|
152
|
+
raise_error(ActionLogic::PresenceError)
|
153
|
+
end
|
154
|
+
|
155
|
+
it "raises error if custom presence validation is not supported" do
|
156
|
+
expect { ValidateAfterUnrecognizablePresenceTestCoordinator.execute() }.to\
|
157
|
+
raise_error(ActionLogic::UnrecognizablePresenceValidatorError)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|