action_logic 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b6d36552e9a95bd397f6841f5ea609b870885349
4
- data.tar.gz: f2e121ac6007fcd73d09a88988053adc7892fe4a
3
+ metadata.gz: ef74e69789f9c0d6c9503637575ca32222448e5c
4
+ data.tar.gz: d24078b096c3f01fcd52705b37791ec4a62d96e6
5
5
  SHA512:
6
- metadata.gz: 8ae1e86d81b1d5799d95089eb6319402610046880d7c1856ce420fba6865a6029191e606d5093c595e839179773b16e91f10fb53bc4e1489300fd9202474afb0
7
- data.tar.gz: 869e66643e26a901d707f4887539649ebe13e160199fc361190099be4b46dfe0761eb3e75d40805e12f8db72faf2c92f9ae865d375ce2ca66238f3686a008488
6
+ metadata.gz: 128b0c7b91f271fe3057463885811cbf0f21aea68025a93cbd9de4c7613d35b5a8ed5c068eec14287fc6ca71325e3c4dc2f7c6a8ed80f178abb92ac635050f90
7
+ data.tar.gz: 8f3be87913f67e3b22d9caca7391f21bd69188674a49bcb4b5e2f418e0e4ec4c719e696b9ab9dfb646ec4feb7b142ed1b0e5a9b9f6c23ba3c439c337ab7087ca
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- action_logic (0.0.1)
4
+ action_logic (0.0.4)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/README.md CHANGED
@@ -1,6 +1,171 @@
1
1
  # ActionLogic
2
+
3
+ [![Codeship Status for rewinfrey/action_logic](https://codeship.com/projects/7737cf40-6808-0133-84a7-460d97cd31f0/status?branch=master)](https://codeship.com/projects/114179)
2
4
  [![Gem Version](https://badge.fury.io/rb/action_logic.svg)](https://badge.fury.io/rb/action_logic)
3
5
  [![Code Climate](https://codeclimate.com/github/rewinfrey/action_logic/badges/gpa.svg)](https://codeclimate.com/github/rewinfrey/action_logic)
4
6
  [![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
7
 
6
- [![Codeship Status for rewinfrey/action_logic](https://codeship.com/projects/7737cf40-6808-0133-84a7-460d97cd31f0/status?branch=master)](https://codeship.com/projects/114179)
8
+ ### Introduction
9
+
10
+ This is a business logic abstraction gem that provides structure to the organization and composition of business logic in a Ruby or Rails application. `ActionLogic` is inspired by similar gems such as [ActiveInteraction](https://github.com/orgsync/active_interaction), [DecentExposure](https://github.com/hashrocket/decent_exposure), [Interactor](https://github.com/collectiveidea/interactor), [Light-Service](https://github.com/adomokos/light-service), [Mutations](https://github.com/cypriss/mutations), [Surrounded](https://github.com/saturnflyer/surrounded), [Trailblazer](https://github.com/apotonick/trailblazer) and [Wisper](https://github.com/krisleech/wisper).
11
+
12
+ Why another business logic abstraction gem? `ActionLogic` seeks to provide teams of varying experience levels to work with a common set of abstractions that help to honor the SOLID principles, make resulting business logic code easy and simple to test and allow teams to spin up or refactor business domains quickly and efficiently.
13
+
14
+ ### Overview
15
+
16
+ There are three levels of abstraction provided by `ActionLogic`:
17
+
18
+ * `ActionTask` (the core unit of work)
19
+ * `ActionUseCase` (contains one or many `ActionTask`s)
20
+ * `ActionCoordinator` (contains two or more `ActionUseCase`s)
21
+
22
+ Each level of abstraction operates with a shared, mutable data structure referred to as a `context` and is an instance of `ActionContext`. This shared `context` is threaded through each `ActionTask`, `ActionUseCase` and / or `ActionCoordinator` until all work in the defined business logic flow are completed and the resulting `context` is returned to the original caller (typically in a Rails application this will be a controller action).
23
+
24
+ ### ActionTask
25
+
26
+ At the core of every `ActionLogic` work flow is an `ActionTask`. These units of work represent where concrete work is performed. All `ActionTask`s conform to the same basic structure and incorporate all the features of `ActionLogic` including validations, error handling and the ability to mutate the shared `context` made available to the `ActionTask`.
27
+
28
+ The following is a simple example of an `ActionTask`:
29
+
30
+ ```ruby
31
+ class ActionTaskExample
32
+ include ActionLogic::ActionTask
33
+
34
+ def call
35
+ context.example_attribute1 = "Example value"
36
+ context.example_attribute2 = 123
37
+ end
38
+ end
39
+ ```
40
+
41
+ To invoke the above `ActionTask`:
42
+
43
+ ```ruby
44
+ result = ActionTaskExample.execute
45
+ result # => <ActionContext :success=true, :example_attribute1="Example value", :example_attribute2=123, :message = "">
46
+ ```
47
+
48
+ This is a simple example, but shows the basic structure of `ActionTask`s and the way they can be invoked by themselves in isolation. However, many of the business logic work flows we find ourselves needing in Rails applications require multiple steps or tasks to achieve the intended result. When we have a business workflow that requires multiple tasks, we can use the `ActionUseCase` abstraction to provide organization and a deterministic order for how the required `ActionTask`s are invoked.
49
+
50
+ ### ActionUseCase
51
+
52
+ Most of the time our business logic work flows can be thought of as use cases of a given domain in our Rails application. Whether that domain is a user, account or notification domain, we can abstract a series of steps that need to be performed from a controller action into a well defined use case that specifies a series of tasks in order to satisfy that use case's goal. `ActionUseCase` represents a layer of abstraction that organizes multiple `ActionTask`s and executes them in a specified order with a shared `context`:
53
+
54
+ ```ruby
55
+ class ActionUseCaseExample
56
+ include ActionLogic::ActionUseCase
57
+
58
+ # The `call` method is invoked prior to invoking any of the ActionTasks defined by the `tasks` method.
59
+ # The purpose of the `call` method allows us to prepare the shared `context` prior to invoking the ActionTasks.
60
+ def call
61
+ context.example_attribute1 = "Example value"
62
+ context.example_attribute2 = 123
63
+ end
64
+
65
+ def tasks
66
+ [ActionTaskExample1,
67
+ ActionTaskExample2,
68
+ ActionTaskExample3]
69
+ end
70
+ end
71
+ ```
72
+
73
+ We see in the above example that an `ActionUseCase` differs from `ActionTask` by adding a `tasks` method. The `tasks` method defines a list of `ActionTask` classes that are invoked in order with the same shared `context` passed from task1 to task2 and so on until all tasks are invoked. Additionally, `ActionUseCase` requires us to define a `call` method that allows us to prepare any necessary attributes and values on the shared `context` prior to beginning the evaluation of the `ActionTask`s defined by the `tasks` method.
74
+
75
+ We can invoke the above `ActionUseCase` in the following way:
76
+
77
+ ```ruby
78
+ ActionUseCaseExample.execute()
79
+ ```
80
+
81
+ ### ActionCoordinator
82
+
83
+ ### ActionContext
84
+
85
+ ### Features
86
+
87
+ `ActionLogic` provides a number of convenience functionality that supports simple to complex business logic work flows while maintaining a simple and easy to understand API:
88
+
89
+ * Validations (`context` is verified to have all necessary attributes, have `presence` and are of the correct type)
90
+ * Custom error handling defined as a callback
91
+ * Prematurely halt or fail a workflow
92
+
93
+ ### Validations
94
+
95
+ Validating that a shared `context` contains the necessary attributes (parameters) becomes increasingly important as your application grows in complexity and `ActionTask` or `ActionUseCase` classes are reused. `ActionLogic` makes it easy to validate your shared `context`s by providing three different validations:
96
+
97
+ * Attribute is defined on a context
98
+ * Attribute has a value (presence)
99
+ * Attribute has the correct type
100
+
101
+ Additionally, validations can be invoked in three ways in any execution context (`ActionTask`, `ActionUseCase` or `ActionCoordinator`):
102
+
103
+ * Before validations are invoked before the execution context is invoked
104
+ * After validations are invoked after the execution context is invoked
105
+ * Aroud validations are invoked before and after the execution context is invoked
106
+
107
+ Validations are defined and made available for all execution contexts with the same methods and format:
108
+
109
+ ```ruby
110
+ class ExampleActionTask
111
+ include ActionLogic::ActionTask
112
+
113
+ validates_before :attribute1 => { :type => :integer, :presence => true },
114
+ :attribute2 => { :type => :string, :presence => true }
115
+
116
+ validates_after :attribute3 => { :type => :boolean, :presence => true },
117
+ :attribute4 => { :type => :string, :presence => true }
118
+
119
+ validates_around :ids => { :type => :array, :presence => ->(ids) { !ids.empty? } }
120
+
121
+ def call
122
+ # set attribute3 on the shared context to satisfy the `validates_after` validations
123
+ context.attribute3 = true
124
+
125
+ # set attribute4 on the shared context to satisfy the `validates_after` validations
126
+ context.attribute4 = "an example string value"
127
+ end
128
+ end
129
+
130
+ # In order to satisfy ExampleActionTask's `validates_before` validations, we must provide an initial
131
+ # hash of attributes and values that satisfy the `validates_before` validations:
132
+ params = {
133
+ :attribute1 => 1,
134
+ :attribute2 => "another example string value"
135
+ }
136
+
137
+ # In order to satisfy ExampleActionTask's `validates_around` validation, we must provide an initial
138
+ # attribute and value that will satisfy the `validates_around` validation:
139
+ params[:ids] = [1, 2, 3, 4]
140
+
141
+ ExampleActionTask.execute(params) # => <ActionContext :success=true, :attribute1=1, :attribute2="another example string value", :attribute3=true, :attribute4="an example string value", :ids=[1,2,3,4] :message="">
142
+ ```
143
+
144
+ ### Supported Types For Validation
145
+
146
+ `ActionLogic` supports the following built in Ruby data types:
147
+
148
+ * :string
149
+ * :boolean (rather than TrueClass or FalseClass)
150
+ * :float
151
+ * :integer (rather than FixNum)
152
+ * :array
153
+ * :hash
154
+ * :nil (rather than NilClass)
155
+
156
+ Additionally, `ActionLogic` allows you to also validate user defined types (custom types):
157
+
158
+ ```ruby
159
+
160
+ class CustomType1
161
+ end
162
+
163
+ class ExampleActionTask
164
+ include ActionLogic::ActionTask
165
+
166
+ :validates_before { :custom_type_attribute => { :type => :customtype1 } }
167
+
168
+ def call
169
+ end
170
+ end
171
+ ```
@@ -17,15 +17,15 @@ module ActionLogic
17
17
  end
18
18
 
19
19
  def get_validates_before
20
- @validates_before
20
+ @validates_before ||= {}
21
21
  end
22
22
 
23
23
  def get_validates_after
24
- @validates_after
24
+ @validates_after ||= {}
25
25
  end
26
26
 
27
27
  def get_validates_around
28
- @validates_around
28
+ @validates_around ||= {}
29
29
  end
30
30
  end
31
31
 
@@ -49,9 +49,9 @@ module ActionLogic
49
49
  end
50
50
 
51
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 || {}
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
55
  end
56
56
 
57
57
  def validate_attributes!(validations)
@@ -1,3 +1,3 @@
1
1
  module ActionLogic
2
- VERSION = '0.0.4'
2
+ VERSION = '0.0.5'
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: action_logic
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rick Winfrey