composable_operations 0.5.1 → 0.6.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
  SHA1:
3
- metadata.gz: 81ead7b2ae4c840da890083408658b4fd9dcdc04
4
- data.tar.gz: 465aad013d3ccc83c7f3d6796c2e211751224296
3
+ metadata.gz: 658fbfd45d7aed7d7589d0603270cbac1b3fea39
4
+ data.tar.gz: bb95ff34e25b6df75249604c9e606ffe2046c136
5
5
  SHA512:
6
- metadata.gz: e4875e2b1b96e1a3fba4b1dc6543343beb3142c2c66fbaea464f6d33c3966c13e2bc3ed8ba311ed504cf55868731d42d2caaef5e43a11a51cd7e18f3bbbae113
7
- data.tar.gz: a8f1a7f56b581e21fb88728f9bad4565db606d033de28cc17dfea1a6c400ab21ef24fadc473079451f559117d1af87e37eef80c23e3dce6ececb1c8cf871d858
6
+ metadata.gz: 091483e197ccab3c020d27ecfe4e040a9fed7c0b8a0f90a08dbff8d97e75ed80545a642c64b86fab4ea995dea4f1e2d3dee4e9335bfd08b6df85a10a8542332a
7
+ data.tar.gz: 66b9beda48a6df81f21eddee06f83d0bb0c6f3bdb9c60b6954000fcb0ed59402c277eb5f2a7c0e1ac6a8679285d781cf0457e33a523579e9f1deada49426ffcc
data/README.md CHANGED
@@ -217,13 +217,15 @@ to the initializer.
217
217
  ```ruby
218
218
  class Indention < ComposableOperations::Operation
219
219
 
220
+ processes :text
221
+
220
222
  property :indent, default: 2,
221
223
  converts: lambda { |value| value.to_s.to_i },
222
224
  accepts: lambda { |value| value >= 0 },
223
225
  required: true
224
226
 
225
227
  def execute
226
- input.split("\n").map { |line| " " * indent + line }.join("\n")
228
+ text.split("\n").map { |line| " " * indent + line }.join("\n")
227
229
  end
228
230
 
229
231
  end
@@ -22,6 +22,7 @@ multiple of these operations in operation pipelines.}
22
22
  spec.add_dependency "smart_properties", "~> 1.0"
23
23
 
24
24
  spec.add_development_dependency "bundler", "~> 1.3"
25
+ spec.add_development_dependency "pry", "~> 0.9.0"
25
26
  spec.add_development_dependency "rake"
26
27
  spec.add_development_dependency "rspec", "~> 2.13"
27
28
  end
@@ -1,4 +1,3 @@
1
- require_relative 'matcher/fail_to_perform'
2
- require_relative 'matcher/succeed_to_perform'
1
+ require_relative 'matcher/execution'
3
2
  require_relative 'matcher/utilize_operation'
4
3
 
@@ -0,0 +1,166 @@
1
+ module ComposableOperations
2
+ module Matcher
3
+ module Execution
4
+
5
+ class Base
6
+
7
+ def and_return(result)
8
+ @result = result
9
+ self
10
+ end
11
+
12
+ def when_initialized_with(*input)
13
+ @input = input
14
+ self
15
+ end
16
+
17
+ protected
18
+
19
+ attr_reader :operation
20
+ attr_reader :result
21
+ attr_reader :input
22
+
23
+ def operation=(operation)
24
+ operation = operation.new(*input) if operation.kind_of?(Class)
25
+ operation.perform
26
+ @operation = operation
27
+ end
28
+
29
+ def failed?
30
+ operation.failed?
31
+ end
32
+
33
+ def succeeded?
34
+ operation.succeeded?
35
+ end
36
+
37
+ def result_as_expected?
38
+ return true unless result
39
+ operation.result == result
40
+ end
41
+
42
+ def input_as_text
43
+ humanize(*input)
44
+ end
45
+
46
+ def result_as_text
47
+ humanize(result)
48
+ end
49
+
50
+ private
51
+
52
+ def humanize(*args)
53
+ args = args.map(&:inspect)
54
+ last_element = args.pop
55
+ args.length > 0 ? [args.join(", "), last_element].join(" and ") : last_element
56
+ end
57
+
58
+ end
59
+
60
+ class SucceedToPerform < Base
61
+
62
+ def matches?(operation)
63
+ self.operation = operation
64
+ succeeded? && result_as_expected?
65
+ end
66
+
67
+ def description
68
+ description = "succeed to perform"
69
+ description += " when initialized with custom input (#{input_as_text})" if input
70
+ description += " and return the expected result (#{result_as_text})" if result
71
+ description
72
+ end
73
+
74
+ def failure_message
75
+ "the operation failed to perform for the following reason(s):\n#{failure_reasons}"
76
+ end
77
+
78
+ def negative_failure_message
79
+ "the operation succeeded unexpectedly"
80
+ end
81
+
82
+ private
83
+
84
+ def failure_reasons
85
+ reasons = []
86
+ reasons << "it did not succeed at all" unless succeeded?
87
+ unless result_as_expected?
88
+ reasons << [
89
+ "it did not return the expected result",
90
+ "Expected: #{result.inspect}",
91
+ "Got: #{operation.result.inspect}"
92
+ ].join("\n\t ")
93
+ end
94
+ reasons.map { |r| "\t- #{r}" }.join("\n")
95
+ end
96
+
97
+ end
98
+
99
+ class FailToPerform < Base
100
+
101
+ def matches?(operation)
102
+ self.operation = operation
103
+ failed? && result_as_expected? && message_as_expected?
104
+ end
105
+
106
+ def because(message)
107
+ @message = message
108
+ self
109
+ end
110
+
111
+ def description
112
+ description = "fail to perform"
113
+ description += " because #{message}" if message
114
+ description += " when initialized with custom input (#{input_as_text})" if input
115
+ description += " and return the expected result (#{result_as_text})" if result
116
+ description
117
+ end
118
+
119
+ def failure_message
120
+ "the operation did not fail to perform for the following reason(s):\n#{failure_reasons}"
121
+ end
122
+
123
+ def negative_failure_message
124
+ "the operation failed unexpectedly"
125
+ end
126
+
127
+ protected
128
+
129
+ attr_reader :message
130
+
131
+ def message_as_expected?
132
+ return true unless message
133
+ operation.message == message
134
+ end
135
+
136
+ def failure_reasons
137
+ reasons = []
138
+ reasons << "it did not fail at all" unless failed?
139
+ reasons << "its message was not as expected" unless message_as_expected?
140
+ unless result_as_expected?
141
+ reasons << [
142
+ "it did not return the expected result",
143
+ "Expected: #{result.inspect}",
144
+ "Got: #{operation.result.inspect}"
145
+ ].join("\n\t ")
146
+ end
147
+ reasons.map { |r| "\t- #{r}" }.join("\n")
148
+ end
149
+
150
+ end
151
+
152
+ def succeed_to_perform
153
+ SucceedToPerform.new
154
+ end
155
+
156
+ def fail_to_perform
157
+ FailToPerform.new
158
+ end
159
+
160
+ end
161
+ end
162
+ end
163
+
164
+ RSpec.configure do |config|
165
+ config.include ComposableOperations::Matcher::Execution
166
+ end
@@ -5,6 +5,12 @@ module ComposableOperations
5
5
 
6
6
  class << self
7
7
 
8
+ attr_writer :arity
9
+
10
+ def arity
11
+ @arity || 0
12
+ end
13
+
8
14
  def perform(*args)
9
15
  operation = new(*args)
10
16
  operation.perform
@@ -51,11 +57,11 @@ module ComposableOperations
51
57
  end
52
58
 
53
59
  def processes(*names)
60
+ self.arity = names.length
61
+
54
62
  case names.length
55
63
  when 0
56
64
  raise ArgumentError, "#{self}.#{__callee__} expects at least one argument"
57
- when 1
58
- alias_method names[0].to_sym, :input
59
65
  else
60
66
  names.each_with_index do |name, index|
61
67
  define_method(name) { input[index] }
@@ -82,13 +88,12 @@ module ComposableOperations
82
88
  attr_reader :backtrace
83
89
 
84
90
  def initialize(*args)
85
- super(args.last.kind_of?(Hash) ? args.pop : {})
86
- @input = case args.length
87
- when 0 then nil
88
- when 1 then args.first
89
- else
90
- args
91
- end
91
+ named_input_parameters = args.shift(self.class.arity)
92
+ options = args.last.kind_of?(Hash) ? args.pop : {}
93
+ unnamed_input_parameters = args
94
+
95
+ @input = named_input_parameters + unnamed_input_parameters
96
+ super(options)
92
97
  end
93
98
 
94
99
  def failed?
@@ -1,3 +1,3 @@
1
1
  module ComposableOperations
2
- VERSION = "0.5.1"
2
+ VERSION = "0.6.0"
3
3
  end
@@ -16,12 +16,14 @@ describe ComposableOperations::ComposedOperation do
16
16
 
17
17
  let(:string_capitalizer) do
18
18
  Class.new(ComposableOperations::Operation) do
19
+ processes :text
20
+
19
21
  def self.name
20
22
  "StringCapitalizer"
21
23
  end
22
24
 
23
25
  def execute
24
- input.upcase
26
+ text.upcase
25
27
  end
26
28
  end
27
29
  end
@@ -0,0 +1,157 @@
1
+ require 'spec_helper'
2
+
3
+ describe ComposableOperations::Operation, "input processing:" do
4
+
5
+ describe "An operation that takes a Hash as input" do
6
+
7
+ let(:input) { {food: "chunky bacon" } }
8
+
9
+ subject(:operation) do
10
+ Class.new(described_class) do
11
+ processes :some_hash
12
+ def execute
13
+ some_hash
14
+ end
15
+ end
16
+ end
17
+
18
+ it { should succeed_to_perform.when_initialized_with(input).and_return(input) }
19
+
20
+ end
21
+
22
+ describe "An operation that takes no arguments except for a Hash of additional options" do
23
+
24
+ subject(:operation) do
25
+ Class.new(described_class) do
26
+ def execute
27
+ input
28
+ end
29
+ end
30
+ end
31
+
32
+ it { should succeed_to_perform.when_initialized_with(key: :value).and_return([]) }
33
+ it { should succeed_to_perform.when_initialized_with(1, 2, 3, key: :value).and_return([1, 2, 3]) }
34
+
35
+ end
36
+
37
+ describe "An operation that takes a Hash as input and an Hash of additional options" do
38
+
39
+ let(:input) { { food: nil } }
40
+
41
+ subject(:operation) do
42
+ Class.new(described_class) do
43
+ processes :some_hash
44
+ property :default_food, default: "chunky bacon"
45
+ def execute
46
+ some_hash[:food] ||= default_food
47
+ some_hash
48
+ end
49
+ end
50
+ end
51
+
52
+ it { should succeed_to_perform.when_initialized_with(input, default_food: "bananas").and_return(food: "bananas") }
53
+ it { should succeed_to_perform.when_initialized_with(input).and_return(food: "chunky bacon") }
54
+
55
+ end
56
+
57
+ describe "An operation that takes two named arguments as input and sums them up" do
58
+
59
+ subject(:operation) do
60
+ Class.new(described_class) do
61
+ processes :first_operand, :second_operand
62
+ def execute
63
+ first_operand + second_operand
64
+ end
65
+ end
66
+ end
67
+
68
+ it { should succeed_to_perform.when_initialized_with(1, 2).and_return(3) }
69
+
70
+ end
71
+
72
+ describe "An operation that takes two named arguments as input and simply returns all input arguments as output" do
73
+
74
+ subject(:operation) do
75
+ Class.new(described_class) do
76
+ processes :first_operand, :second_operand
77
+ def execute
78
+ input
79
+ end
80
+ end
81
+ end
82
+
83
+ it { should succeed_to_perform.when_initialized_with(1, 2).and_return([1, 2]) }
84
+ it { should succeed_to_perform.when_initialized_with(1, 2, 3).and_return([1, 2, 3]) }
85
+
86
+ end
87
+
88
+ describe "An operation that takes multiple arguments as input where the last of these arguments is a Hash" do
89
+
90
+ subject(:operation) do
91
+ Class.new(described_class) do
92
+ processes :first_operand, :second_operand
93
+ property :operator, default: :+, converts: :to_sym, required: true
94
+ def execute
95
+ first_operand.public_send(operator, second_operand)
96
+ end
97
+ end
98
+ end
99
+
100
+ it { should succeed_to_perform.when_initialized_with(1, 2).and_return(3) }
101
+ it { should succeed_to_perform.when_initialized_with(1, 2, operator: :*).and_return(2) }
102
+
103
+ end
104
+
105
+ describe "An operation that takes multiple arguments as input where the last of these arguments is a Hash, as well as, a Hash of additional options" do
106
+
107
+ subject(:operation) do
108
+ Class.new(described_class) do
109
+ processes :some_value, :yet_another_value, :a_hash
110
+ def execute
111
+ a_hash
112
+ end
113
+ end
114
+ end
115
+
116
+ it { should succeed_to_perform.when_initialized_with(1, 2, {food: "chunky bacon"}, { additional: :options }).and_return(food: "chunky bacon") }
117
+
118
+ end
119
+
120
+ end
121
+
122
+ describe ComposableOperations::ComposedOperation, "input processing:" do
123
+
124
+ describe "A composed operation that consists of a producer and a consumer" do
125
+
126
+ let(:producer) do
127
+ Class.new(ComposableOperations::Operation) do
128
+ def execute
129
+ return 1, 2
130
+ end
131
+ end
132
+ end
133
+
134
+ let(:consumer) do
135
+ Class.new(ComposableOperations::Operation) do
136
+ processes :first_operand, :second_operand
137
+ def execute
138
+ first_operand + second_operand
139
+ end
140
+ end
141
+ end
142
+
143
+ subject(:operation) do
144
+ producer = self.producer
145
+ consumer = self.consumer
146
+
147
+ Class.new(described_class) do
148
+ use producer
149
+ use consumer
150
+ end
151
+ end
152
+
153
+ it { should succeed_to_perform.and_return(3) }
154
+
155
+ end
156
+
157
+ end
@@ -22,7 +22,7 @@ describe ComposableOperations::Operation do
22
22
  let(:halting_operation) do
23
23
  Class.new(described_class) do
24
24
  def execute
25
- halt "Full stop!", input
25
+ halt "Full stop!", input.first
26
26
  end
27
27
  end
28
28
  end
@@ -169,16 +169,12 @@ describe ComposableOperations::Operation do
169
169
 
170
170
  context "when extended with a finalizer that checks that the result is not an empty string" do
171
171
 
172
- let(:simple_operation_with_sanity_check) do
172
+ subject(:simple_operation_with_sanity_check) do
173
173
  Class.new(simple_operation) do
174
174
  after { fail "the operational result is an empty string" if self.result == "" }
175
175
  end
176
176
  end
177
177
 
178
- subject(:simple_operation_with_sanity_check_instance) do
179
- simple_operation_with_sanity_check.new
180
- end
181
-
182
178
  it { should fail_to_perform.because("the operational result is an empty string") }
183
179
 
184
180
  end
@@ -189,21 +185,17 @@ describe ComposableOperations::Operation do
189
185
 
190
186
  subject(:string_multiplier) do
191
187
  Class.new(described_class) do
188
+ processes :text
192
189
  property :multiplier, :default => 3
193
190
 
194
191
  def execute
195
- input.to_s * multiplier
192
+ text.to_s * multiplier
196
193
  end
197
194
  end
198
195
  end
199
196
 
200
- it "should operate according to the specified default value" do
201
- string_multiplier.perform("-").should be == "---"
202
- end
203
-
204
- it "should allow to overwrite default settings" do
205
- string_multiplier.perform("-", :multiplier => 5).should be == "-----"
206
- end
197
+ it { should succeed_to_perform.when_initialized_with("-").and_return("---") }
198
+ it { should succeed_to_perform.when_initialized_with("-", multiplier: 5).and_return("-----") }
207
199
 
208
200
  end
209
201
 
@@ -219,9 +211,7 @@ describe ComposableOperations::Operation do
219
211
  end
220
212
  end
221
213
 
222
- it "should build a string that is multiplier-times long" do
223
- string_multiplier.perform(["-", 3]).should be == "---"
224
- end
214
+ it { should succeed_to_perform.when_initialized_with("-", 3).and_return("---") }
225
215
 
226
216
  end
227
217
 
data/spec/spec_helper.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require 'bundler/setup'
2
2
  require 'rspec'
3
+ require 'pry'
3
4
 
4
5
  require 'composable_operations'
5
6
  require 'composable_operations/matcher'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: composable_operations
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.1
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Konstantin Tennhard
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-08-01 00:00:00.000000000 Z
11
+ date: 2013-08-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: smart_properties
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - ~>
39
39
  - !ruby/object:Gem::Version
40
40
  version: '1.3'
41
+ - !ruby/object:Gem::Dependency
42
+ name: pry
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: 0.9.0
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: 0.9.0
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: rake
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -84,14 +98,14 @@ files:
84
98
  - lib/composable_operations.rb
85
99
  - lib/composable_operations/composed_operation.rb
86
100
  - lib/composable_operations/matcher.rb
87
- - lib/composable_operations/matcher/fail_to_perform.rb
88
- - lib/composable_operations/matcher/succeed_to_perform.rb
101
+ - lib/composable_operations/matcher/execution.rb
89
102
  - lib/composable_operations/matcher/utilize_operation.rb
90
103
  - lib/composable_operations/operation.rb
91
104
  - lib/composable_operations/operation_error.rb
92
105
  - lib/composable_operations/version.rb
93
106
  - spec/composable_operations/composed_operation_spec.rb
94
107
  - spec/composable_operations/control_flow_spec.rb
108
+ - spec/composable_operations/operation_input_processing_spec.rb
95
109
  - spec/composable_operations/operation_spec.rb
96
110
  - spec/spec_helper.rb
97
111
  homepage: http://github.com/t6d/composable_operations
@@ -114,12 +128,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
114
128
  version: '0'
115
129
  requirements: []
116
130
  rubyforge_project:
117
- rubygems_version: 2.0.3
131
+ rubygems_version: 2.0.6
118
132
  signing_key:
119
133
  specification_version: 4
120
134
  summary: Tool set for operation pipelines.
121
135
  test_files:
122
136
  - spec/composable_operations/composed_operation_spec.rb
123
137
  - spec/composable_operations/control_flow_spec.rb
138
+ - spec/composable_operations/operation_input_processing_spec.rb
124
139
  - spec/composable_operations/operation_spec.rb
125
140
  - spec/spec_helper.rb
@@ -1,83 +0,0 @@
1
- module ComposableOperations
2
- module Matcher
3
- module FailToPerform
4
- class Matcher
5
-
6
- def matches?(operation)
7
- self.operation = operation
8
- failed? && result_as_expected? && message_as_expected?
9
- end
10
-
11
- def because(message)
12
- @message = message
13
- self
14
- end
15
-
16
- def and_return(result)
17
- @result = result
18
- self
19
- end
20
-
21
- def description
22
- description = "fail to perform"
23
- description += " because #{message}" if message
24
- description += " and return the expected result" if result
25
- description
26
- end
27
-
28
- def failure_message
29
- "the operation did not fail to perform for the following reason(s):\n#{failure_reasons}"
30
- end
31
-
32
- def negative_failure_message
33
- "the operation failed unexpectedly"
34
- end
35
-
36
- protected
37
-
38
- attr_reader :operation
39
- attr_reader :message
40
- attr_reader :result
41
-
42
- def operation=(operation)
43
- operation.perform
44
- @operation = operation
45
- end
46
-
47
- private
48
-
49
- def failed?
50
- operation.failed?
51
- end
52
-
53
- def message_as_expected?
54
- return true unless message
55
- operation.message == message
56
- end
57
-
58
- def result_as_expected?
59
- return true unless result
60
- operation.result == result
61
- end
62
-
63
- def failure_reasons
64
- reasons = []
65
- reasons << "it did not fail at all" unless failed?
66
- reasons << "its message was not as expected" unless message_as_expected?
67
- reasons << "it did not return the expected result" unless result_as_expected?
68
- reasons.map { |r| "\t- #{r}" }.join("\n")
69
- end
70
-
71
- end
72
-
73
- def fail_to_perform
74
- Matcher.new
75
- end
76
-
77
- end
78
- end
79
- end
80
-
81
- RSpec.configure do |config|
82
- config.include ComposableOperations::Matcher::FailToPerform
83
- end
@@ -1,70 +0,0 @@
1
- module ComposableOperations
2
- module Matcher
3
- module SucceedToPerform
4
- class Matcher
5
-
6
- def matches?(operation)
7
- self.operation = operation
8
- succeeded? && result_as_expected?
9
- end
10
-
11
- def and_return(result)
12
- @result = result
13
- self
14
- end
15
-
16
- def description
17
- description = "succeed to perform"
18
- description += " and return the expected result" if result
19
- description
20
- end
21
-
22
- def failure_message
23
- "the operation failed to perform for the following reason(s):\n#{failure_reasons}"
24
- end
25
-
26
- def negative_failure_message
27
- "the operation succeeded unexpectedly"
28
- end
29
-
30
- protected
31
-
32
- attr_reader :operation
33
- attr_reader :result
34
-
35
- def operation=(operation)
36
- operation.perform
37
- @operation = operation
38
- end
39
-
40
- private
41
-
42
- def succeeded?
43
- operation.succeeded?
44
- end
45
-
46
- def result_as_expected?
47
- return true unless result
48
- operation.result == result
49
- end
50
-
51
- def failure_reasons
52
- reasons = []
53
- reasons << "it did not succeed at all" unless succeeded?
54
- reasons << "it did not return the expected result" unless result_as_expected?
55
- reasons.map { |r| "\t- #{r}" }.join("\n")
56
- end
57
-
58
- end
59
-
60
- def succeed_to_perform
61
- Matcher.new
62
- end
63
- end
64
- end
65
- end
66
-
67
- RSpec.configure do |config|
68
- config.include ComposableOperations::Matcher::SucceedToPerform
69
- end
70
-