carry_out 0.2.8 → 0.2.9

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
  SHA1:
3
- metadata.gz: 48df2d9a646c48893cc2224a4167d522dd2c8e81
4
- data.tar.gz: e928043f8032caccc5af2f111d31d8f94a348b26
3
+ metadata.gz: 15539f051ef8fbb7bde23ab9027e1a309b318eb6
4
+ data.tar.gz: 75df54ddbd00851be278a80faaa350e2483ad0c5
5
5
  SHA512:
6
- metadata.gz: 2de51be35be58f48b5c02269631e6b731a5b61057c6f29b9c7cc3edf260a82aeee71b31a2c999b09ea412de6b125349a2b053d6d9a1c2f651b4536ee37596f8a
7
- data.tar.gz: 86e7ad493a8175f7e426a6bd0b69d919d9f1ba8d633cc3cad86eef9734d7ba3890e3fad5c4cf9614130b091d2a85f2dade390f3fc91e5b1a422791c625f7e86d
6
+ metadata.gz: c9df9545f0b391a1f31471ecbc1b85a4f5257fd9399144c75d55643cb787620ac7486dc46de7ee297877779a975b7b3a1817ac14ad62b31172895f9a851c581c
7
+ data.tar.gz: 33bcc7bc1820b9c2dbd2bd8a084f6a8168d145e854d6ee50ad0fd0ae370ce8a8255ba0a361dadc97201b277353a5b6f1b1d283063d75935f7a3edac6702593dd
data/README.md CHANGED
@@ -221,6 +221,53 @@ plan = CarryOut
221
221
  # or .unless(CarryOut.get(:silenced))
222
222
  ```
223
223
 
224
+ ### Magic Directives (Experimental)
225
+
226
+ *This feature is highly experimental. It has not been thoroughly tested in larger application environments like Rails and is not yet guaranteed to remain part of this gem.*
227
+
228
+ CarryOut provides some magic methods that can improve the readability of a plan. These rely on a search strategy to find classes by name. A very limited strategy is provided out-of-the-box. This strategy accepts an array of modules and will only find classes that are direct children of any of the provided modules. The first match gets priority.
229
+
230
+ ```ruby
231
+ CarryOut.defaults = {
232
+ search: [ MyModule1 ]
233
+ }
234
+ ```
235
+
236
+ If the default strategy is insufficient (and it most likely will be), a custom strategy can be provided as a lambda/Proc. For example, a strategy that works in Rails is to put the following in an initializer:
237
+
238
+ ```ruby
239
+ CarryOut.defaults = {
240
+ search: -> (name) { name.constantize }
241
+ }
242
+ ```
243
+
244
+ #### Magic will\_, then\_, and within\_
245
+
246
+ The magic versions of `will`, `then`, and `within` will use the configured search strategy to convert the remaning portion of the directive into a class reference.
247
+
248
+ Using the default strategy as configured above:
249
+ ```ruby
250
+ module MyModule1
251
+ class SayHello
252
+ def execute; puts "Hello!"; end
253
+ end
254
+ end
255
+
256
+ plan = CarryOut.will_say_hello
257
+ ```
258
+
259
+ #### Magic returning\_as\_
260
+
261
+ The magic `returning_as_` directive is an alternative to passing the `as:` option to a `will`/`then` directive. The remainder of the directive becomes the key symbol into which the unit's return value will be stored.
262
+
263
+ ```ruby
264
+ plan = CarryOut
265
+ .will_receive_message
266
+ .returning_as_message
267
+ .then_log
268
+ .message(CarryOut.get(:message))
269
+ ```
270
+
224
271
  ## Motivation
225
272
 
226
273
  I've been trying to keep my Rails controllers clean, but I prefer to avoid shoving inter-model business logic inside database models. The recommendation I most frequently run into is to move that kind of logic into something akin to service objects. I like that idea, but I want to keep my services small and composable, and I want to separate the "what" from the "how" of my logic.
data/carry_out.gemspec CHANGED
@@ -24,10 +24,14 @@ Gem::Specification.new do |spec|
24
24
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
25
25
  spec.require_paths = ["lib"]
26
26
 
27
- spec.required_ruby_version = '~> 2.0'
27
+ spec.required_ruby_version = '>= 1.9.3'
28
28
 
29
29
  spec.add_development_dependency "bundler", "~> 1.12"
30
30
  spec.add_development_dependency "rake", "~> 10.0"
31
31
  spec.add_development_dependency "minitest", "~> 5.0"
32
- spec.add_development_dependency "coveralls"
32
+ spec.add_development_dependency "coveralls", "~> 0.8"
33
+
34
+ if RUBY_VERSION < '2.0'
35
+ spec.add_development_dependency "tins", "~> 1.6.0"
36
+ end
33
37
  end
@@ -1,15 +1,17 @@
1
1
  module CarryOut
2
2
  class Plan
3
-
3
+ MATCH_CONTINUATION_METHOD = /^(?:will|then)_(.+)/
4
+ MATCH_CONTEXT_METHOD = /^within_(.+)/
5
+ MATCH_RETURNING_METHOD = /^returning_as_(.+)/
6
+
4
7
  def initialize(unit = nil, options = {})
5
8
  @nodes = {}
6
9
  @node_meta = {}
7
10
  @previously_added_node = nil
8
11
  @wrapper = options[:within]
12
+ @search = options[:search] || []
9
13
 
10
- unless unit.nil?
11
- self.then(unit, options)
12
- end
14
+ self.then(unit, options) unless unit.nil?
13
15
  end
14
16
 
15
17
  def execute(context = nil, &block)
@@ -41,8 +43,8 @@ module CarryOut
41
43
  self.then(*args)
42
44
  end
43
45
 
44
- def then(unit, options = {})
45
- add_node(PlanNode.new(unit), options[:as])
46
+ def then(unit = nil, options = {})
47
+ add_node(PlanNode.new(unit), options[:as]) unless unit.nil?
46
48
  self
47
49
  end
48
50
 
@@ -53,8 +55,21 @@ module CarryOut
53
55
  end
54
56
 
55
57
  def method_missing(method, *args, &block)
56
- if @previously_added_node
57
- @previously_added_node.send(method, *args, &block)
58
+ if MATCH_CONTINUATION_METHOD =~ method
59
+ obj = find_object($1)
60
+ return super if obj.nil?
61
+ self.then(obj, *args, &block)
62
+ elsif MATCH_CONTEXT_METHOD =~ method
63
+ obj = find_object($1)
64
+ return super if obj.nil?
65
+ @wrapper = obj.new
66
+ self
67
+ elsif @previously_added_node
68
+ if MATCH_RETURNING_METHOD =~ method
69
+ node_meta(@previously_added_node)[:as] = $1.to_sym
70
+ else
71
+ @previously_added_node.send(method, *args, &block)
72
+ end
58
73
  self
59
74
  else
60
75
  super
@@ -106,7 +121,18 @@ module CarryOut
106
121
  node_result = node.execute(result.artifacts)
107
122
  result.add(publish_to, node_result) unless publish_to.nil?
108
123
  rescue UnitError => error
109
- result.add(publish_to || id, CarryOut::Error.new(error.error.message, error.error))
124
+ result.add(publish_to || key_for_node(node), CarryOut::Error.new(error.error.message, error.error))
125
+ end
126
+ end
127
+
128
+ def find_object(name)
129
+ constant_name = name.to_s.split('_').map { |w| w.capitalize }.join('')
130
+
131
+ if @search.respond_to?(:call)
132
+ @search.call(constant_name)
133
+ else
134
+ containing_module = @search.find { |m| m.const_get(constant_name) rescue nil }
135
+ containing_module.const_get(constant_name) unless containing_module.nil?
110
136
  end
111
137
  end
112
138
 
@@ -121,8 +147,12 @@ module CarryOut
121
147
  guards.nil? || guards.map { |guard| guard.call(artifacts) }.all?
122
148
  end
123
149
 
150
+ def key_for_node(node)
151
+ @nodes.key(node)
152
+ end
153
+
124
154
  def node_meta(node)
125
- @node_meta[@nodes.key(node)]
155
+ @node_meta[key_for_node(node)]
126
156
  end
127
157
  end
128
158
  end
@@ -1,3 +1,3 @@
1
1
  module CarryOut
2
- VERSION = "0.2.8"
2
+ VERSION = "0.2.9"
3
3
  end
data/lib/carry_out.rb CHANGED
@@ -9,15 +9,61 @@ require "carry_out/unit"
9
9
  require "carry_out/unit_error"
10
10
 
11
11
  module CarryOut
12
- def self.get(*args)
13
- Reference.new(*args)
12
+ MATCH_CONTINUATION_METHOD = /^will_/
13
+ MATCH_WITHIN_METHOD = /^within_/
14
+
15
+ class ConfiguredCarryOut
16
+ def initialize(options = {})
17
+ @config = options
18
+ end
19
+
20
+ def get(*args)
21
+ Reference.new(*args)
22
+ end
23
+
24
+ def method_missing(method, *args, &block)
25
+ if MATCH_CONTINUATION_METHOD =~ method
26
+ create_plan.send(method, *args, &block)
27
+ elsif MATCH_WITHIN_METHOD =~ method
28
+ create_plan.send(method, *args, &block)
29
+ else
30
+ super
31
+ end
32
+ end
33
+
34
+ def will(*args)
35
+ create_plan.will(*args)
36
+ end
37
+
38
+ def within(wrapper = nil, &block)
39
+ create_plan(within: wrapper || block)
40
+ end
41
+
42
+ private
43
+ def create_plan(options = {})
44
+ Plan.new(nil, @config.merge(options))
45
+ end
46
+ end
47
+
48
+ def self.configured_with(options = {})
49
+ ConfiguredCarryOut.new(options)
14
50
  end
15
51
 
16
- def self.will(*args)
17
- Plan.new(*args)
52
+ def self.defaults=(options = {})
53
+ @default_options = options
54
+ @default_carry_out = nil
18
55
  end
19
56
 
20
- def self.within(unit = nil, &block)
21
- Plan.new(nil, within: unit || block)
57
+ def self.method_missing(method, *args, &block)
58
+ default_carry_out.send(method, *args, &block)
22
59
  end
60
+
61
+ private
62
+ def self.default_options
63
+ @default_options ||= Hash.new
64
+ end
65
+
66
+ def self.default_carry_out
67
+ @default_carry_out ||= ConfiguredCarryOut.new(default_options)
68
+ end
23
69
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: carry_out
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.8
4
+ version: 0.2.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryan Fields
@@ -56,16 +56,16 @@ dependencies:
56
56
  name: coveralls
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - ">="
59
+ - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '0'
61
+ version: '0.8'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - ">="
66
+ - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '0'
68
+ version: '0.8'
69
69
  description: |2
70
70
  CarryOut connects units of logic into workflows. Each unit can extend
71
71
  the DSL with parameter methods. Artifacts and errors are collected as
@@ -106,9 +106,9 @@ require_paths:
106
106
  - lib
107
107
  required_ruby_version: !ruby/object:Gem::Requirement
108
108
  requirements:
109
- - - "~>"
109
+ - - ">="
110
110
  - !ruby/object:Gem::Version
111
- version: '2.0'
111
+ version: 1.9.3
112
112
  required_rubygems_version: !ruby/object:Gem::Requirement
113
113
  requirements:
114
114
  - - ">="