mini_camel 0.5.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.rspec +4 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +4 -0
- data/Gemfile +4 -0
- data/LICENSE.md +22 -0
- data/README.md +300 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/examples/hello_world.rb +106 -0
- data/lib/mini_camel.rb +63 -0
- data/lib/mini_camel/context.rb +5 -0
- data/lib/mini_camel/default_error_handler.rb +37 -0
- data/lib/mini_camel/dto.rb +55 -0
- data/lib/mini_camel/environment.rb +66 -0
- data/lib/mini_camel/error.rb +57 -0
- data/lib/mini_camel/exchange.rb +80 -0
- data/lib/mini_camel/exchange_error.rb +36 -0
- data/lib/mini_camel/exchange_result.rb +5 -0
- data/lib/mini_camel/interactor.rb +13 -0
- data/lib/mini_camel/processor/base.rb +14 -0
- data/lib/mini_camel/processor/each.rb +28 -0
- data/lib/mini_camel/processor/expose_field.rb +29 -0
- data/lib/mini_camel/processor/expose_fields.rb +34 -0
- data/lib/mini_camel/processor/extract_result.rb +22 -0
- data/lib/mini_camel/processor/mutate.rb +18 -0
- data/lib/mini_camel/processor/mutate_each.rb +16 -0
- data/lib/mini_camel/processor/pipeline.rb +19 -0
- data/lib/mini_camel/processor/process.rb +22 -0
- data/lib/mini_camel/processor/process_each.rb +16 -0
- data/lib/mini_camel/processor/produce.rb +17 -0
- data/lib/mini_camel/processor/to.rb +15 -0
- data/lib/mini_camel/processor/transform.rb +15 -0
- data/lib/mini_camel/processor/transform_each.rb +15 -0
- data/lib/mini_camel/processor/validate.rb +21 -0
- data/lib/mini_camel/processor/wrap_in_dto.rb +19 -0
- data/lib/mini_camel/processor_definition/base.rb +13 -0
- data/lib/mini_camel/processor_definition/each.rb +20 -0
- data/lib/mini_camel/processor_definition/expose_field.rb +28 -0
- data/lib/mini_camel/processor_definition/expose_fields.rb +19 -0
- data/lib/mini_camel/processor_definition/extract_result.rb +17 -0
- data/lib/mini_camel/processor_definition/mutate.rb +21 -0
- data/lib/mini_camel/processor_definition/mutate_each.rb +15 -0
- data/lib/mini_camel/processor_definition/pipeline.rb +17 -0
- data/lib/mini_camel/processor_definition/process.rb +19 -0
- data/lib/mini_camel/processor_definition/process_each.rb +14 -0
- data/lib/mini_camel/processor_definition/produce.rb +20 -0
- data/lib/mini_camel/processor_definition/to.rb +17 -0
- data/lib/mini_camel/processor_definition/transform.rb +17 -0
- data/lib/mini_camel/processor_definition/transform_each.rb +20 -0
- data/lib/mini_camel/processor_definition/validate.rb +26 -0
- data/lib/mini_camel/processor_definition/wrap_in_dto.rb +21 -0
- data/lib/mini_camel/route.rb +21 -0
- data/lib/mini_camel/route_builder.rb +24 -0
- data/lib/mini_camel/route_collection.rb +16 -0
- data/lib/mini_camel/route_definition.rb +146 -0
- data/lib/mini_camel/route_dispatcher.rb +37 -0
- data/lib/mini_camel/route_finalizer.rb +38 -0
- data/lib/mini_camel/route_generator.rb +32 -0
- data/lib/mini_camel/strict_dto.rb +36 -0
- data/lib/mini_camel/version.rb +3 -0
- data/mini_camel.gemspec +30 -0
- metadata +237 -0
data/lib/mini_camel.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
require "active_support"
|
2
|
+
require "active_support/core_ext"
|
3
|
+
require "active_model"
|
4
|
+
require "virtus"
|
5
|
+
require "hashie/mash"
|
6
|
+
require 'multi_json'
|
7
|
+
|
8
|
+
require "mini_camel/version"
|
9
|
+
require "mini_camel/dto"
|
10
|
+
require "mini_camel/strict_dto"
|
11
|
+
require "mini_camel/context"
|
12
|
+
require "mini_camel/exchange_error"
|
13
|
+
require "mini_camel/exchange_result"
|
14
|
+
require "mini_camel/exchange"
|
15
|
+
require "mini_camel/environment"
|
16
|
+
require "mini_camel/error"
|
17
|
+
require "mini_camel/default_error_handler"
|
18
|
+
require "mini_camel/route"
|
19
|
+
require "mini_camel/route_builder"
|
20
|
+
require "mini_camel/route_collection"
|
21
|
+
require "mini_camel/route_definition"
|
22
|
+
require "mini_camel/route_dispatcher"
|
23
|
+
require "mini_camel/route_finalizer"
|
24
|
+
require "mini_camel/route_generator"
|
25
|
+
require "mini_camel/interactor"
|
26
|
+
|
27
|
+
require 'mini_camel/processor/base'
|
28
|
+
require 'mini_camel/processor/each'
|
29
|
+
require 'mini_camel/processor/process'
|
30
|
+
require 'mini_camel/processor/process_each'
|
31
|
+
require 'mini_camel/processor/expose_field'
|
32
|
+
require 'mini_camel/processor/expose_fields'
|
33
|
+
require 'mini_camel/processor/extract_result'
|
34
|
+
require 'mini_camel/processor/pipeline'
|
35
|
+
require 'mini_camel/processor/to'
|
36
|
+
require 'mini_camel/processor/transform'
|
37
|
+
require 'mini_camel/processor/transform_each'
|
38
|
+
require 'mini_camel/processor/mutate'
|
39
|
+
require 'mini_camel/processor/mutate_each'
|
40
|
+
require 'mini_camel/processor/produce'
|
41
|
+
require 'mini_camel/processor/wrap_in_dto'
|
42
|
+
require 'mini_camel/processor/validate'
|
43
|
+
|
44
|
+
require 'mini_camel/processor_definition/base'
|
45
|
+
require 'mini_camel/processor_definition/each'
|
46
|
+
require 'mini_camel/processor_definition/process'
|
47
|
+
require 'mini_camel/processor_definition/process_each'
|
48
|
+
require 'mini_camel/processor_definition/expose_field'
|
49
|
+
require 'mini_camel/processor_definition/expose_fields'
|
50
|
+
require 'mini_camel/processor_definition/extract_result'
|
51
|
+
require 'mini_camel/processor_definition/pipeline'
|
52
|
+
require 'mini_camel/processor_definition/to'
|
53
|
+
require 'mini_camel/processor_definition/transform'
|
54
|
+
require 'mini_camel/processor_definition/transform_each'
|
55
|
+
require 'mini_camel/processor_definition/mutate'
|
56
|
+
require 'mini_camel/processor_definition/mutate_each'
|
57
|
+
require 'mini_camel/processor_definition/produce'
|
58
|
+
require 'mini_camel/processor_definition/wrap_in_dto'
|
59
|
+
require 'mini_camel/processor_definition/validate'
|
60
|
+
|
61
|
+
module MiniCamel
|
62
|
+
|
63
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module MiniCamel
|
2
|
+
class DefaultErrorHandler
|
3
|
+
include Virtus.value_object
|
4
|
+
|
5
|
+
values do
|
6
|
+
attribute :registered_errors, Array, default: []
|
7
|
+
end
|
8
|
+
|
9
|
+
def call(error, exchange)
|
10
|
+
raise error unless error.class.in? registered_errors
|
11
|
+
|
12
|
+
exchange.set_error(assemble_exchange_error(error))
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def assemble_exchange_error(error)
|
18
|
+
ExchangeError.new(
|
19
|
+
error_class: error.class,
|
20
|
+
message: error.message,
|
21
|
+
details: Dto.new(details(error)),
|
22
|
+
backtrace: error.backtrace || []
|
23
|
+
)
|
24
|
+
end
|
25
|
+
|
26
|
+
def details(error)
|
27
|
+
if error.respond_to?(:internal_errors)
|
28
|
+
error.internal_errors
|
29
|
+
else
|
30
|
+
{}
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module MiniCamel
|
2
|
+
|
3
|
+
class Dto < OpenStruct
|
4
|
+
|
5
|
+
delegate :to_json, :as_json, :to_hash, :keys, :to_ary, to: :table
|
6
|
+
|
7
|
+
def self.empty
|
8
|
+
new
|
9
|
+
end
|
10
|
+
|
11
|
+
# ensures that every hash like object is a dto
|
12
|
+
def self.build(hash_object)
|
13
|
+
hash_object = MultiJson.load(MultiJson.dump(self.new(hash_object)))
|
14
|
+
deep_build(hash_object)
|
15
|
+
end
|
16
|
+
|
17
|
+
# @private
|
18
|
+
def self.deep_build(hash_object)
|
19
|
+
case hash_object
|
20
|
+
when Hash
|
21
|
+
hash_object.each do |k, v|
|
22
|
+
hash_object[k] = deep_build(v)
|
23
|
+
end
|
24
|
+
self.new(hash_object)
|
25
|
+
when Array
|
26
|
+
hash_object.map{|item| deep_build(item)}
|
27
|
+
else
|
28
|
+
hash_object
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def initialize(hash = {})
|
33
|
+
super
|
34
|
+
@table = Hashie::Mash.new(hash)
|
35
|
+
end
|
36
|
+
|
37
|
+
def update(data = {})
|
38
|
+
modifiable.update(data)
|
39
|
+
end
|
40
|
+
|
41
|
+
def [](key)
|
42
|
+
fetch(key)
|
43
|
+
end
|
44
|
+
|
45
|
+
def fetch(key, default: nil)
|
46
|
+
if @table.has_key?(key)
|
47
|
+
@table.fetch(key, default)
|
48
|
+
else
|
49
|
+
return default unless default.nil?
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module MiniCamel
|
2
|
+
class Environment
|
3
|
+
include Virtus.value_object
|
4
|
+
|
5
|
+
values do
|
6
|
+
attribute :route_builders, Array, default: []
|
7
|
+
attribute :routes, Array, default: []
|
8
|
+
attribute :finalized, Boolean, default: false
|
9
|
+
|
10
|
+
attribute :finalizer_class, Class
|
11
|
+
attribute :error_handler, Object
|
12
|
+
end
|
13
|
+
|
14
|
+
def dispatch_route(route_name, options = {})
|
15
|
+
with_data = options[:with_data] || Dto.new
|
16
|
+
RouteDispatcher.new(route: route_name, with_data: with_data, env: self).dispatch
|
17
|
+
end
|
18
|
+
|
19
|
+
def register_route_builder(*builders)
|
20
|
+
@route_builders += builders
|
21
|
+
self
|
22
|
+
end
|
23
|
+
|
24
|
+
def register_error_handler(handler)
|
25
|
+
@error_handler = handler
|
26
|
+
self
|
27
|
+
end
|
28
|
+
|
29
|
+
def add_route(route)
|
30
|
+
routes << route
|
31
|
+
end
|
32
|
+
|
33
|
+
def finalize
|
34
|
+
finalizer_class.new(env: self).finalize
|
35
|
+
self
|
36
|
+
end
|
37
|
+
|
38
|
+
def finalized!
|
39
|
+
@finalized = true
|
40
|
+
end
|
41
|
+
|
42
|
+
# @internal
|
43
|
+
def call_route(route_name, exchange)
|
44
|
+
if route = routes_by_name[route_name]
|
45
|
+
route.call(exchange)
|
46
|
+
else
|
47
|
+
raise UnknownRouteError, route_name
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def finalizer_class
|
52
|
+
@finalizer_class ||= RouteFinalizer
|
53
|
+
end
|
54
|
+
|
55
|
+
def error_handler
|
56
|
+
@error_handler ||= DefaultErrorHandler.new
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def routes_by_name
|
62
|
+
@routes_by_name ||= routes.index_by(&:route_name)
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module MiniCamel
|
2
|
+
|
3
|
+
class CamelError < StandardError; end
|
4
|
+
|
5
|
+
class EnvironmentNotFinalized < CamelError; end
|
6
|
+
|
7
|
+
class InvalidInteractor < CamelError; end
|
8
|
+
|
9
|
+
class InvalidRouteDefinition < CamelError
|
10
|
+
|
11
|
+
attr_reader :route_definition
|
12
|
+
|
13
|
+
def initialize(message, route_definition)
|
14
|
+
@route_definition = route_definition
|
15
|
+
super("#{message}: #{route_definition.class} #{route_definition.errors.full_messages}")
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
class InvalidProcessorDefinition < CamelError
|
21
|
+
|
22
|
+
attr_reader :processor_definition
|
23
|
+
|
24
|
+
def initialize(message, processor_definition)
|
25
|
+
@processor_definition = processor_definition
|
26
|
+
super("#{message}: #{processor_definition.class} #{processor_definition.errors.full_messages}")
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
class ExchangeFailure < CamelError
|
32
|
+
attr_reader :exchange_error
|
33
|
+
|
34
|
+
def initialize(message, exchange_error)
|
35
|
+
@exchange_error = exchange_error
|
36
|
+
super(message)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class ReRaisedError < CamelError
|
41
|
+
attr_reader :original_error_class
|
42
|
+
|
43
|
+
def initialize(message, original_error_class)
|
44
|
+
@original_error_class = original_error_class
|
45
|
+
super("[#{original_error_class}]: #{message}")
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class UnknownRouteError < CamelError
|
50
|
+
attr_reader :route_name
|
51
|
+
|
52
|
+
def initialize(route_name)
|
53
|
+
super("Route not found: '#{route_name}'")
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module MiniCamel
|
2
|
+
class Exchange
|
3
|
+
include Virtus.value_object
|
4
|
+
|
5
|
+
attribute :context, Context, reader: :private
|
6
|
+
|
7
|
+
values do
|
8
|
+
attribute :result, ExchangeResult, reader: :private
|
9
|
+
attribute :error, ExchangeError, reader: :private
|
10
|
+
end
|
11
|
+
|
12
|
+
def success?
|
13
|
+
error.nil?
|
14
|
+
end
|
15
|
+
|
16
|
+
def failure?
|
17
|
+
!success?
|
18
|
+
end
|
19
|
+
|
20
|
+
def on(modifier)
|
21
|
+
raise ArgumentError, "Unkown modifier '#{modifier}'" unless modifier.in? [:success, :failure]
|
22
|
+
|
23
|
+
yield result if modifier == :success && success?
|
24
|
+
yield error if modifier == :failure && failure?
|
25
|
+
|
26
|
+
self
|
27
|
+
end
|
28
|
+
|
29
|
+
def result_or_else(fallback)
|
30
|
+
if success?
|
31
|
+
result
|
32
|
+
else
|
33
|
+
ExchangeResult.new(fallback)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def result_or_else_nil
|
38
|
+
result if success?
|
39
|
+
end
|
40
|
+
|
41
|
+
def result_or_else_fail!
|
42
|
+
if success?
|
43
|
+
result
|
44
|
+
else
|
45
|
+
raise ExchangeFailure.new("Exchange failed!", error)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def result_or_else_try
|
50
|
+
if success?
|
51
|
+
result
|
52
|
+
else
|
53
|
+
yield error
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def update_context(params = {})
|
58
|
+
context.update(params)
|
59
|
+
end
|
60
|
+
|
61
|
+
def context_fetch(field_name, default: nil)
|
62
|
+
context.fetch(field_name, default: default)
|
63
|
+
end
|
64
|
+
|
65
|
+
def set_result(exchange_result)
|
66
|
+
self.result = exchange_result
|
67
|
+
end
|
68
|
+
|
69
|
+
def set_error(exchange_error)
|
70
|
+
self.error = exchange_error
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
def context
|
76
|
+
@context ||= Context.empty
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module MiniCamel
|
2
|
+
class ExchangeError
|
3
|
+
include Virtus.value_object
|
4
|
+
|
5
|
+
values do
|
6
|
+
attribute :error_class, Class, reader: :private
|
7
|
+
attribute :message, String, reader: :private
|
8
|
+
attribute :details, Dto, reader: :private
|
9
|
+
end
|
10
|
+
|
11
|
+
attribute :backtrace, Array, default: []
|
12
|
+
|
13
|
+
def on(check_error_class)
|
14
|
+
yield message, details if check_error_class == error_class
|
15
|
+
|
16
|
+
self
|
17
|
+
end
|
18
|
+
|
19
|
+
# This is just a helper method.
|
20
|
+
# Do not use it in production code!
|
21
|
+
def raise!
|
22
|
+
raise ReRaisedError.new(message, error_class)
|
23
|
+
end
|
24
|
+
|
25
|
+
def as_json(options = {})
|
26
|
+
{error_class: error_class.name, message: message, details: details}.as_json(options)
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def details
|
32
|
+
@details ||= Dto.empty
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module MiniCamel
|
2
|
+
class Interactor
|
3
|
+
include Virtus.value_object
|
4
|
+
include ActiveModel::Validations
|
5
|
+
|
6
|
+
def call
|
7
|
+
raise InvalidInteractor, "Invalid interactor '#{self.class.name}': #{self.errors.full_messages}." if invalid?
|
8
|
+
|
9
|
+
run
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module MiniCamel
|
2
|
+
module Processor
|
3
|
+
class Each < Base
|
4
|
+
|
5
|
+
values do
|
6
|
+
attribute :field, Symbol
|
7
|
+
attribute :in_field, Symbol
|
8
|
+
attribute :additional_fields, [Symbol]
|
9
|
+
attribute :with_class, Class
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def process(entry, additional_data)
|
15
|
+
with_class.new(additional_data.merge(field => entry)).call
|
16
|
+
end
|
17
|
+
|
18
|
+
def fetch_additional_data(exchange)
|
19
|
+
additional_fields.map{|f| {f => exchange.context_fetch(f)}}.reduce(&:merge) || {}
|
20
|
+
end
|
21
|
+
|
22
|
+
def fetch_collection(exchange)
|
23
|
+
exchange.context_fetch(in_field)
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|