flow 0.1.0 → 0.2.0

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
  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: