composable_operations 0.5.1 → 0.6.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
  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
-