mini_camel 0.5.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/.rspec +4 -0
  4. data/.ruby-gemset +1 -0
  5. data/.ruby-version +1 -0
  6. data/.travis.yml +4 -0
  7. data/Gemfile +4 -0
  8. data/LICENSE.md +22 -0
  9. data/README.md +300 -0
  10. data/Rakefile +6 -0
  11. data/bin/console +14 -0
  12. data/bin/setup +8 -0
  13. data/examples/hello_world.rb +106 -0
  14. data/lib/mini_camel.rb +63 -0
  15. data/lib/mini_camel/context.rb +5 -0
  16. data/lib/mini_camel/default_error_handler.rb +37 -0
  17. data/lib/mini_camel/dto.rb +55 -0
  18. data/lib/mini_camel/environment.rb +66 -0
  19. data/lib/mini_camel/error.rb +57 -0
  20. data/lib/mini_camel/exchange.rb +80 -0
  21. data/lib/mini_camel/exchange_error.rb +36 -0
  22. data/lib/mini_camel/exchange_result.rb +5 -0
  23. data/lib/mini_camel/interactor.rb +13 -0
  24. data/lib/mini_camel/processor/base.rb +14 -0
  25. data/lib/mini_camel/processor/each.rb +28 -0
  26. data/lib/mini_camel/processor/expose_field.rb +29 -0
  27. data/lib/mini_camel/processor/expose_fields.rb +34 -0
  28. data/lib/mini_camel/processor/extract_result.rb +22 -0
  29. data/lib/mini_camel/processor/mutate.rb +18 -0
  30. data/lib/mini_camel/processor/mutate_each.rb +16 -0
  31. data/lib/mini_camel/processor/pipeline.rb +19 -0
  32. data/lib/mini_camel/processor/process.rb +22 -0
  33. data/lib/mini_camel/processor/process_each.rb +16 -0
  34. data/lib/mini_camel/processor/produce.rb +17 -0
  35. data/lib/mini_camel/processor/to.rb +15 -0
  36. data/lib/mini_camel/processor/transform.rb +15 -0
  37. data/lib/mini_camel/processor/transform_each.rb +15 -0
  38. data/lib/mini_camel/processor/validate.rb +21 -0
  39. data/lib/mini_camel/processor/wrap_in_dto.rb +19 -0
  40. data/lib/mini_camel/processor_definition/base.rb +13 -0
  41. data/lib/mini_camel/processor_definition/each.rb +20 -0
  42. data/lib/mini_camel/processor_definition/expose_field.rb +28 -0
  43. data/lib/mini_camel/processor_definition/expose_fields.rb +19 -0
  44. data/lib/mini_camel/processor_definition/extract_result.rb +17 -0
  45. data/lib/mini_camel/processor_definition/mutate.rb +21 -0
  46. data/lib/mini_camel/processor_definition/mutate_each.rb +15 -0
  47. data/lib/mini_camel/processor_definition/pipeline.rb +17 -0
  48. data/lib/mini_camel/processor_definition/process.rb +19 -0
  49. data/lib/mini_camel/processor_definition/process_each.rb +14 -0
  50. data/lib/mini_camel/processor_definition/produce.rb +20 -0
  51. data/lib/mini_camel/processor_definition/to.rb +17 -0
  52. data/lib/mini_camel/processor_definition/transform.rb +17 -0
  53. data/lib/mini_camel/processor_definition/transform_each.rb +20 -0
  54. data/lib/mini_camel/processor_definition/validate.rb +26 -0
  55. data/lib/mini_camel/processor_definition/wrap_in_dto.rb +21 -0
  56. data/lib/mini_camel/route.rb +21 -0
  57. data/lib/mini_camel/route_builder.rb +24 -0
  58. data/lib/mini_camel/route_collection.rb +16 -0
  59. data/lib/mini_camel/route_definition.rb +146 -0
  60. data/lib/mini_camel/route_dispatcher.rb +37 -0
  61. data/lib/mini_camel/route_finalizer.rb +38 -0
  62. data/lib/mini_camel/route_generator.rb +32 -0
  63. data/lib/mini_camel/strict_dto.rb +36 -0
  64. data/lib/mini_camel/version.rb +3 -0
  65. data/mini_camel.gemspec +30 -0
  66. metadata +237 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4e997221b5d82b7839ee097a982a8e09bd5e356b
4
+ data.tar.gz: f3a9f0018276a72737ae7f6c47f9e97828545618
5
+ SHA512:
6
+ metadata.gz: d1ef696eb2bbaba77ac605386d431b032da4aad1addd75e79cc720ee0b3e3a1b94ead11b07fef807d0daf38619901b04e78ede27f2baaf1fc2e1e420f586a243
7
+ data.tar.gz: c9b9bbba74a702031b344365e6a414583fc1d57bdc002083adc85f734cec7b783d3f6f032c843d7f59ccf6442a8933bfd0aff044141fc68b3f00dfec917df2c5
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,4 @@
1
+ --format documentation
2
+ --color
3
+ --order random
4
+ --require spec_helper
@@ -0,0 +1 @@
1
+ mini_camel
@@ -0,0 +1 @@
1
+ ruby-2.3.0
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.3.0
4
+ before_install: gem install bundler -v 1.11.2
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in mini_camel.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2016 Spas Poptchev
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,300 @@
1
+ # MiniCamel
2
+
3
+ MiniCamel allows you to perform simple data transformations and combine these transformations to reusable business processes. It is inspired by his big brother [Apache Camel](http://camel.apache.org/index.html "Apache Camel").
4
+
5
+ ## Basic Usage
6
+
7
+ The idea behind MiniCamel is to define a route (or multiple) to transform an input presentation to an output presentation.
8
+
9
+ ```ruby
10
+ # Checkout the 'examples' folder for the full example
11
+ class HelloWorldRoutes < MiniCamel::RouteBuilder
12
+ def configure
13
+ from(:hello_world).
14
+ desc("Transforms 'Hello' to 'Hello World'").
15
+ to(:write_text).
16
+ transform(:input, to: :output, with_class: TransformHello).
17
+ extract_result(from: :output)
18
+
19
+ # reusable route
20
+ from(:write_text).
21
+ desc("Writes some text to stdout").
22
+ process(:input, with_class: WriteInput)
23
+ end
24
+ end
25
+ ```
26
+
27
+ In each route you can define custom interactor classes that will transform or process specific parameters.
28
+
29
+ ```ruby
30
+ # transform(:input, to: :output, with_class: TransformHello)
31
+
32
+ class TransformHello < MiniCamel::Interactor
33
+
34
+ attribute :input, String
35
+ validates :input, presence: true
36
+
37
+ def run
38
+ {text: "#{input} World"}
39
+ end
40
+
41
+ end
42
+ ```
43
+
44
+ This interactor will process the input string and transform it to a `Hash`. The return value of the interactor will be written into `output`.
45
+
46
+ After you have defined all your routes you can generate your MiniCamel environment.
47
+
48
+ ```ruby
49
+ environment = MiniCamel::Environment.new
50
+ environment.register_route_builder(HelloWorldRoutes.new)
51
+ environment.finalize
52
+ ```
53
+
54
+ And dispatch one of the routes.
55
+
56
+ ```ruby
57
+ exchange = environment.dispatch_route(:hello_world, with_data: {input: "Hello"})
58
+ ```
59
+
60
+ The result of the `dispatch_route` call will be a `MiniCamel::Exchange`. An exchange is an object that is transfered between each processor of a route. It exposes two callbacks
61
+
62
+ ```ruby
63
+ exchange.on(:success) do |result|
64
+ puts result
65
+ end.on(:failure) do |error|
66
+ puts error
67
+ end
68
+ ```
69
+
70
+ The `on(:success)` callback will yield the exchange result if the exchange was processed successfully otherwise it will yield an error in the `on(:failure)` callback.
71
+
72
+ ## The internals
73
+
74
+ ### Context
75
+
76
+ The context is a part of the exchange and holds the data of the exchange.
77
+
78
+ ### Processors
79
+
80
+ Each route can call a number of processors. A processor is a simple logical unit that changes the context or uses its data to trigger specific business processes.
81
+
82
+ #### ExposeField
83
+
84
+ The `ExposeField` processor exposes a field from an object or a hash to the context so the value can be used by other processors.
85
+
86
+ ```ruby
87
+ # context: {test: {hello: "world"}}
88
+ expose_field(:hello, from: :test)
89
+ # new context: {test: {hello: "world"}, hello: "world"}
90
+ ```
91
+
92
+ Further more you can change the name of the exposed field.
93
+
94
+ ```ruby
95
+ # context: {test: {hello: "world"}}
96
+ expose_field(:hello, from: :test, as: :bye)
97
+ # new context: {bye: "world", test: {hello: "world"}}
98
+ ```
99
+
100
+ #### ExposeFields
101
+
102
+ The `ExposeFields` processor works like _ExposeField_ but it allows to expose multiple fields at once. Unlike ExposeField it does not allow to change the name of the exposed fields.
103
+
104
+ ```ruby
105
+ # context: {test: {hello: "world", foo: "bar"}}
106
+ expose_fields(:hello, :foo, from: :test)
107
+ # new context: {hello: "world", foo: "bar", test: {hello: "world"}}
108
+ ```
109
+
110
+ #### ExtractResult
111
+
112
+ The `ExtractResult` processor extracts an object from the context. This object will be used as the result of the exchange. Please be aware that the result object has to be of the type `Hash` or `MiniCamel::Dto`.
113
+
114
+ ```ruby
115
+ from(:extract_result_example).
116
+ desc("Extracts a result from ':test'.").
117
+ extract_result(from: :test)
118
+
119
+ exchange = env.dispatch_route(:extract_result_example, with_data: {test: {hello: "world"})
120
+ exchange.on(:success) do |result|
121
+ puts result # -> {hello: "world"}
122
+ end
123
+ ```
124
+
125
+ #### Mutate
126
+
127
+ The `Mutate` processor transforms a value with an interactor and writes the return value back to the original field. If you want to transform multiple values than you should use the `TransformProcessor`.
128
+
129
+ ```ruby
130
+ # context: {test: {hello: "world"}}
131
+ mutate(:test, with_class: MutateTestInteractor)
132
+ # new context: {test: {foo: "bar"}} (the MutateTestInteractor class returns {foo: "bar"})
133
+ ```
134
+
135
+ #### MutateEach
136
+
137
+ Like the `Mutate` processor the `MutateEach` processor transforms a value with an interactor. But instead of a single value it transforms the array under the given field name. You may also provide `additional_fields` for each step.
138
+
139
+ ```ruby
140
+ # context: {values: [1, 2, 3]}
141
+ mutate_each(:value, in: :values, with_class: MultiplyBy2Interactor)
142
+ # new context: {values: [2, 4, 6]}
143
+ ```
144
+
145
+ #### To
146
+
147
+ `To` allows you to route the current exchange to a sub route and to reuse predifined processes.
148
+
149
+ ```ruby
150
+ from(:hello_world).
151
+ desc("Prints 'Hello'").
152
+ to(:print_text)
153
+
154
+ from(:foo_bar).
155
+ desc("Prints 'Foo'").
156
+ to(:print_text)
157
+
158
+ from(:print_text).
159
+ desc("Prints some text to stdout").
160
+ process(:input, with_class: WriteInput)
161
+
162
+ env.dispatch_route(:hello_world, with_data: {input: 'Hello'})
163
+ # -> "Hello"
164
+
165
+ env.dispatch_route(:foo_bar, with_data: {input: 'Foo'})
166
+ # -> "Foo"
167
+ ```
168
+
169
+ #### Pipeline
170
+
171
+ `Pipeline` allows you to call multiple sub routes in one call.
172
+
173
+ ```ruby
174
+ from(:ask_question).
175
+ desc("Prints some user input").
176
+ pipeline(:ask_for_input, :print_text)
177
+
178
+ from(:print_text).
179
+ desc("Prints some text to stdout").
180
+ process(:input, with_class: WriteInput)
181
+
182
+ from(:ask_for_input).
183
+ desc("Asks the user for input").
184
+ produce(:input, with_class: AskUserForInputInteractor)
185
+
186
+ env.dispatch_route(:ask_question, with_data: {})
187
+ # -> depends on user input
188
+ ```
189
+
190
+ #### Process
191
+
192
+ `Process` allows you to execute some business logic. `Process` does not modify the data you pass into it.
193
+
194
+ ```ruby
195
+ from(:print_text).
196
+ desc("Prints some text to stdout").
197
+ process(:input, with_class: WriteInput)
198
+
199
+ env.dispatch_route(:print_text, with_data: {input: "foobar"})
200
+ # -> "foobar"
201
+ ```
202
+
203
+ #### ProcessEach
204
+
205
+ `ProcessEach` allows you to process each value in an array. Just like `Process` it does not modify the array values you pass into it. You may also provide `additional_fields` for each step.
206
+
207
+ ```ruby
208
+ from(:greet).
209
+ desc("Prints each value of an array").
210
+ process(:input, in: :inputs, with_class: WriteInput)
211
+
212
+ env.dispatch_route(:greet, with_data: {inputs: ["hello", "world"]})
213
+ # -> "hello"
214
+ # -> "world"
215
+ ```
216
+
217
+ #### Transform
218
+
219
+ The `Transform` processor allows you to transform multiple values into one result value with an interactor.
220
+
221
+ ```ruby
222
+ from(:concat_foobar).
223
+ desc("Concats foo bar to foobar").
224
+ transform(:foo, :bar, to: :foobar, with_class: ConcatFoobarInteractor).
225
+ process(:foobar, with_class: PrintInput)
226
+
227
+ env.dispatch_route(:concat_foobar, with_data: {foo: "foo", bar: "bar"})
228
+ # -> "foobar"
229
+ ```
230
+
231
+ #### TransformEach
232
+
233
+ The `TransformEach` processor allows you to transform array values and put them into a new array with an interactor. You may also provide `additional_fields` for each step.
234
+
235
+ ```ruby
236
+ from(:multiply_by_two).
237
+ desc("Multiplies each array entry by two and puts it into a new numbers_multiplied_by_two array.").
238
+ transform(:number, in: :numbers, to: :numbers_multiplied_by_two, with_class: MultiplyByTwoInteractor).
239
+ process(:numbers_multiplied_by_two, with_class: PrintInput)
240
+
241
+ env.dispatch_route(:multiply_by_two, with_data: {numbers: [1,2,3]})
242
+ # -> [2,4,6]
243
+ ```
244
+
245
+ #### Validate
246
+
247
+ The `Validate` processor will validate a field you pass into it. The value of the field has to respond to `invalid?`.
248
+
249
+ ```ruby
250
+ class InvalidValue
251
+
252
+ def invalid?
253
+ true
254
+ end
255
+
256
+ end
257
+
258
+ class InvalidValueError < StandardError; end
259
+
260
+ from(:check_value).
261
+ desc("Raises a InvalidValueError").
262
+ validate(:value, raise_error: InvalidValueError)
263
+
264
+ env.dispatch_route(:check_value, with_data: {value: InvalidValue.new})
265
+ # -> raises InvalidValueError
266
+ ```
267
+
268
+ ## TODO
269
+
270
+ * Improve README
271
+ * Check for unknown routes at route finalization
272
+ * Create a plugin system to easily add new processors
273
+ * Improve exception handling
274
+
275
+
276
+ ## License
277
+
278
+ Copyright (c) 2016 Spas Poptchev
279
+
280
+ MIT License
281
+
282
+ Permission is hereby granted, free of charge, to any person obtaining
283
+ a copy of this software and associated documentation files (the
284
+ "Software"), to deal in the Software without restriction, including
285
+ without limitation the rights to use, copy, modify, merge, publish,
286
+ distribute, sublicense, and/or sell copies of the Software, and to
287
+ permit persons to whom the Software is furnished to do so, subject to
288
+ the following conditions:
289
+
290
+ The above copyright notice and this permission notice shall be
291
+ included in all copies or substantial portions of the Software.
292
+
293
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
294
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
295
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
296
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
297
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
298
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
299
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
300
+
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "mini_camel"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,106 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH.push File.expand_path("../../lib", __FILE__)
4
+ require 'mini_camel'
5
+
6
+ # Define the interaction classes
7
+ class TransformHello < MiniCamel::Interactor
8
+
9
+ attribute :input, String
10
+ validates :input, presence: true
11
+
12
+ def run
13
+ {text: "#{input} World"}
14
+ end
15
+
16
+ end
17
+
18
+ class TransformHappy < MiniCamel::Interactor
19
+
20
+ attribute :input, String
21
+ validates :input, presence: true
22
+
23
+ def run
24
+ {text: "The world is #{input}"}
25
+ end
26
+
27
+ end
28
+
29
+ class WriteInput < MiniCamel::Interactor
30
+
31
+ attribute :input, String
32
+ validates :input, presence: true
33
+
34
+ def run
35
+ puts input
36
+ end
37
+ end
38
+
39
+ # Define the routes
40
+ class HelloWorldRoutes < MiniCamel::RouteBuilder
41
+ def configure
42
+ from(:hello_world).
43
+ desc("Transforms 'Hello' to 'Hello World'").
44
+ to(:write_text).
45
+ transform(:input, to: :output, with_class: TransformHello).
46
+ extract_result(from: :output)
47
+
48
+ from(:happy_world).
49
+ desc("Transforms 'Happy' to 'The world is happy'").
50
+ to(:write_text).
51
+ transform(:input, to: :output, with_class: TransformHappy).
52
+ extract_result(from: :output)
53
+
54
+ # reusable route
55
+ from(:write_text).
56
+ desc("Writes some text to stdout").
57
+ process(:input, with_class: WriteInput)
58
+ end
59
+ end
60
+
61
+ class HelloWorldFacade
62
+ include Singleton
63
+
64
+ def hello_world
65
+ environment.dispatch_route(:hello_world, with_data: {input: "Hello"})
66
+ end
67
+
68
+ def happy_world
69
+ environment.dispatch_route(:happy_world, with_data: {input: "happy"})
70
+ end
71
+
72
+ private
73
+
74
+ def environment
75
+ @environment ||= build_environment
76
+ end
77
+
78
+ # Build and finalize the environment
79
+ def build_environment
80
+ env = MiniCamel::Environment.new
81
+ env.register_route_builder(HelloWorldRoutes.new)
82
+ env.finalize
83
+ end
84
+
85
+ end
86
+
87
+ client = HelloWorldFacade.instance
88
+
89
+ # Dispatch the hello world route
90
+ exchange = client.hello_world
91
+
92
+ exchange.on(:success) do |result|
93
+ puts result
94
+ end.on(:failure) do |error|
95
+ puts error
96
+ end
97
+
98
+ # Dispatch the happy world route
99
+ exchange = client.happy_world
100
+
101
+ exchange.on(:success) do |result|
102
+ puts result
103
+ end.on(:failure) do |error|
104
+ puts error
105
+ end
106
+