action_logic 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Gem Version](https://badge.fury.io/rb/action_logic.svg)](https://badge.fury.io/rb/action_logic)
|
3
|
+
[![Code Climate](https://codeclimate.com/github/rewinfrey/action_logic/badges/gpa.svg)](https://codeclimate.com/github/rewinfrey/action_logic)
|
4
|
+
[![Coverage Status](https://coveralls.io/repos/rewinfrey/action_logic/badge.svg?branch=master&service=github)](https://coveralls.io/github/rewinfrey/action_logic?branch=master)
|
5
|
+
|
6
|
+
[![Codeship Status for rewinfrey/action_logic](https://codeship.com/projects/7737cf40-6808-0133-84a7-460d97cd31f0/status?branch=master)](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
|