flow 0.1.0 → 0.2.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b7cd9101a0a7a4e63855dc9355576bccb286f2f6500fce4c385cd03563650786
4
- data.tar.gz: bbd6de895ef3c606e7d71df2719fe398c69eb837e7ec736825434de9356da0e1
3
+ metadata.gz: 46f3b0a498f717414773a7c8262c4a045a229007c4066327929c41112ced44d0
4
+ data.tar.gz: 354e5069d30b3fc634c54d3c0170b7b2e4f84682cd2c9f52e84e47651bd1414b
5
5
  SHA512:
6
- metadata.gz: 18acdb36d7196e43166c035131d90ccbc9a73875f3bbb19b40b3a7aa8436d18510ef2b23f476a898c9bced22115d75adc92416f1bd3b34cd137fff6b6f425b4c
7
- data.tar.gz: 3df8fbdb170ce58db95b1386eab961676a4e6bd684116f15da6a30bb982a952c93e0bb3e0d029f71d47a33614adc049a1986ceddb20393971e830b03c88a73de
6
+ metadata.gz: 5d13a497617cf8aa949ca0c77d45d27613321a9417fde96f3f3a8c4a9874954243c29e7766bf0ca94bf3908e70238920e15a9eb4fe739012cfbe74a3e6eaad78
7
+ data.tar.gz: cd3a356d664d7b4b334cdc436cd03f382c48d57b81e9dc3664990d502a64e39d7fd15b9c2804a88dca9a0d3066ea7d593a3b22272886d190c283ccc5fabe4061
data/README.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Flow
2
2
 
3
+ [![Gem Version](https://badge.fury.io/rb/flow.svg)](https://badge.fury.io/rb/flow)
4
+ [![Build Status](https://semaphoreci.com/api/v1/freshly/flow/branches/master/badge.svg)](https://semaphoreci.com/freshly/flow)
5
+ [![Maintainability](https://api.codeclimate.com/v1/badges/02131658005b10c289e0/maintainability)](https://codeclimate.com/github/Freshly/flow/maintainability)
6
+ [![Test Coverage](https://api.codeclimate.com/v1/badges/02131658005b10c289e0/test_coverage)](https://codeclimate.com/github/Freshly/flow/test_coverage)
7
+
3
8
  Business logic is like nuclear fuel.
4
9
 
5
10
  Managed properly, it's incredibly powerful and can do a lot of good.
@@ -1,5 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_model"
4
+
5
+ require "active_support"
6
+ require "active_support/core_ext/class/attribute"
7
+ require "active_support/core_ext/module/delegation"
8
+
9
+ require "technologic"
10
+
1
11
  require "flow/version"
2
12
 
3
- module Flow
4
- # Your code goes here...
5
- end
13
+ require "flow/flow_base"
14
+ require "flow/operation_base"
15
+ require "flow/state_base"
16
+
17
+ module Flow; end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ # A flow is a collection of procedurally executed operations sharing a common state.
4
+ class FlowBase
5
+ class_attribute :_operations, instance_writer: false, default: []
6
+
7
+ class << self
8
+ def state_class
9
+ "#{name.chomp("Flow")}State".constantize
10
+ end
11
+
12
+ def trigger(*arguments)
13
+ new(*arguments).trigger
14
+ end
15
+
16
+ def operations(*operations)
17
+ _operations.concat(operations.flatten)
18
+ end
19
+
20
+ def inherited(base)
21
+ base._operations = _operations.dup
22
+ super
23
+ end
24
+ end
25
+
26
+ attr_reader :state
27
+
28
+ delegate :state_class, :_operations, to: :class
29
+
30
+ def initialize(**input)
31
+ @state = state_class.new(**input)
32
+ end
33
+
34
+ def trigger
35
+ _operations.each { |operation| operation.execute(state) }
36
+ state
37
+ end
38
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Accepts input representing the state.
4
+ module Operation
5
+ module Core
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ attr_reader :state
10
+ end
11
+
12
+ def initialize(state)
13
+ @state = state
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Execute the operation.
4
+ module Operation
5
+ module Execute
6
+ extend ActiveSupport::Concern
7
+
8
+ class_methods do
9
+ def execute(*arguments)
10
+ new(*arguments).execute
11
+ end
12
+ end
13
+
14
+ def execute
15
+ raise NotImplementedError
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "operation/core"
4
+ require_relative "operation/execute"
5
+
6
+ # Operations are service objects which are executed with a state.
7
+ class OperationBase
8
+ include Technologic
9
+ include Operation::Core
10
+ include Operation::Execute
11
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Arguments describe input required to define the initial state.
4
+ module State
5
+ module Arguments
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ class_attribute :_arguments, instance_writer: false, default: []
10
+ set_callback :initialize, :after do
11
+ missing = _arguments.select { |argument| public_send(argument).nil? }
12
+ raise ArgumentError, "Missing #{"argument".pluralize(missing.length)}: #{missing.join(", ")}" if missing.any?
13
+ end
14
+ end
15
+
16
+ class_methods do
17
+ def inherited(base)
18
+ base._arguments = _arguments.dup
19
+ super
20
+ end
21
+
22
+ protected
23
+
24
+ def argument(argument)
25
+ _arguments << argument
26
+ define_attribute argument
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Defines the immutable structure by defining attribute accessors for the state data.
4
+ module State
5
+ module Attributes
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ include ActiveModel::AttributeMethods
10
+
11
+ class_attribute :_attributes, instance_writer: false, default: []
12
+ end
13
+
14
+ class_methods do
15
+ def inherited(base)
16
+ base._attributes = _attributes.dup
17
+ super
18
+ end
19
+
20
+ protected
21
+
22
+ def define_attribute(attribute)
23
+ _attributes << attribute
24
+
25
+ attr_accessor attribute
26
+ define_attribute_methods attribute
27
+ protected "#{attribute}=".to_sym
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ # State callbacks provide an extensible mechanism for composing functionality for a state object.
4
+ module State
5
+ module Callbacks
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ include ActiveSupport::Callbacks
10
+ define_callbacks :initialize
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Accepts input representing the arguments and input which define the initial state.
4
+ module State
5
+ module Core
6
+ extend ActiveSupport::Concern
7
+
8
+ def initialize(**input)
9
+ run_callbacks(:initialize) do
10
+ input.each { |key, value| __send__("#{key}=".to_sym, value) }
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Options describe input which may be provided to define or override the initial state.
4
+ module State
5
+ module Options
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ class_attribute :_options, instance_writer: false, default: {}
10
+
11
+ set_callback :initialize, :after do
12
+ _options.each { |option, info| __send__("#{option}=".to_sym, info.default_value) if public_send(option).nil? }
13
+ end
14
+ end
15
+
16
+ class_methods do
17
+ def inherited(base)
18
+ dup = _options.dup
19
+ base._options = dup.each { |k, v| dup[k] = v.dup }
20
+ super
21
+ end
22
+
23
+ protected
24
+
25
+ def option(option, default: nil, &block)
26
+ _options[option] = Option.new(default: default, &block)
27
+ define_attribute option
28
+ end
29
+ end
30
+
31
+ class Option
32
+ def initialize(default:, &block)
33
+ @default_value = (default.nil? && block_given?) ? block : default
34
+ end
35
+
36
+ def default_value
37
+ return instance_eval(&@default_value) if @default_value.respond_to?(:call)
38
+
39
+ @default_value
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Formats the state as a string.
4
+ module State
5
+ module String
6
+ extend ActiveSupport::Concern
7
+
8
+ delegate :name, to: :class, prefix: true
9
+
10
+ def to_s
11
+ string_for(__method__)
12
+ end
13
+
14
+ def inspect
15
+ string_for(__method__)
16
+ end
17
+
18
+ protected
19
+
20
+ def stringable_attributes
21
+ self.class._attributes
22
+ end
23
+
24
+ private
25
+
26
+ def string_for(method)
27
+ "#<#{class_name} #{attribute_string(method)}>"
28
+ end
29
+
30
+ def attribute_string(method)
31
+ stringable_attribute_values.map { |attribute, value| "#{attribute}=#{value.public_send(method)}" }.join(" ")
32
+ end
33
+
34
+ def stringable_attribute_values
35
+ stringable_attributes.each_with_object({}) { |attribute, result| result[attribute] = public_send(attribute) }
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "state/callbacks"
4
+ require_relative "state/attributes"
5
+ require_relative "state/arguments"
6
+ require_relative "state/options"
7
+ require_relative "state/core"
8
+ require_relative "state/string"
9
+
10
+ # A flow state is the immutable structure of relevant data.
11
+ class StateBase
12
+ include ActiveModel::Model
13
+ include State::Callbacks
14
+ include State::Attributes
15
+ include State::Arguments
16
+ include State::Options
17
+ include State::Core
18
+ include State::String
19
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Flow
2
- VERSION = "0.1.0"
4
+ VERSION = "0.2.0"
3
5
  end
metadata CHANGED
@@ -1,15 +1,57 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: flow
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eric Garside
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-11-29 00:00:00.000000000 Z
11
+ date: 2018-12-02 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 5.2.1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 5.2.1
27
+ - !ruby/object:Gem::Dependency
28
+ name: activemodel
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 5.2.1
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 5.2.1
41
+ - !ruby/object:Gem::Dependency
42
+ name: spicerack
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 0.4.3
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 0.4.3
13
55
  - !ruby/object:Gem::Dependency
14
56
  name: bundler
15
57
  requirement: !ruby/object:Gem::Requirement
@@ -122,8 +164,36 @@ dependencies:
122
164
  - - ">="
123
165
  - !ruby/object:Gem::Version
124
166
  version: 0.11.3
167
+ - !ruby/object:Gem::Dependency
168
+ name: rspice
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - "~>"
172
+ - !ruby/object:Gem::Version
173
+ version: 0.4.3
174
+ type: :development
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - "~>"
179
+ - !ruby/object:Gem::Version
180
+ version: 0.4.3
181
+ - !ruby/object:Gem::Dependency
182
+ name: shoulda-matchers
183
+ requirement: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - '='
186
+ - !ruby/object:Gem::Version
187
+ version: 4.0.0.rc1
188
+ type: :development
189
+ prerelease: false
190
+ version_requirements: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - '='
193
+ - !ruby/object:Gem::Version
194
+ version: 4.0.0.rc1
125
195
  description: Tired of kitchen sink services, god-objects, and fat-everything? So were
126
- we. Get flow.
196
+ we. Get in the flow.
127
197
  email:
128
198
  - eric.garside@freshly.com
129
199
  executables: []
@@ -133,6 +203,17 @@ files:
133
203
  - LICENSE.txt
134
204
  - README.md
135
205
  - lib/flow.rb
206
+ - lib/flow/flow_base.rb
207
+ - lib/flow/operation/core.rb
208
+ - lib/flow/operation/execute.rb
209
+ - lib/flow/operation_base.rb
210
+ - lib/flow/state/arguments.rb
211
+ - lib/flow/state/attributes.rb
212
+ - lib/flow/state/callbacks.rb
213
+ - lib/flow/state/core.rb
214
+ - lib/flow/state/options.rb
215
+ - lib/flow/state/string.rb
216
+ - lib/flow/state_base.rb
136
217
  - lib/flow/version.rb
137
218
  homepage: http://www.freshly.com
138
219
  licenses: