evil_events 0.3.1 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.gitignore +1 -0
- data/.rubocop.yml +10 -1
- data/.travis.yml +4 -5
- data/CHANGELOG.md +73 -0
- data/evil_events.gemspec +6 -3
- data/lib/evil_events.rb +3 -0
- data/lib/evil_events/core/broadcasting.rb +1 -0
- data/lib/evil_events/core/broadcasting/emitter.rb +19 -13
- data/lib/evil_events/core/broadcasting/emitter/adapter_proxy.rb +46 -0
- data/lib/evil_events/core/config.rb +37 -40
- data/lib/evil_events/core/events.rb +34 -3
- data/lib/evil_events/core/events/abstract_event.rb +4 -6
- data/lib/evil_events/core/events/event_extensions/class_signature.rb +13 -0
- data/lib/evil_events/core/events/event_extensions/class_signature/equalizer.rb +0 -2
- data/lib/evil_events/core/events/event_extensions/dispatchable.rb +74 -0
- data/lib/evil_events/core/events/event_extensions/observable.rb +2 -0
- data/lib/evil_events/core/events/event_extensions/serializable.rb +22 -2
- data/lib/evil_events/core/events/manager.rb +1 -1
- data/lib/evil_events/core/events/manager_registry.rb +16 -0
- data/lib/evil_events/core/events/manager_registry/scoped_event_type_matcher.rb +177 -0
- data/lib/evil_events/core/events/notifier/sequential.rb +2 -2
- data/lib/evil_events/core/events/serializers.rb +13 -2
- data/lib/evil_events/core/events/serializers/base.rb +56 -13
- data/lib/evil_events/core/events/serializers/base/abstract_engine.rb +47 -0
- data/lib/evil_events/core/events/serializers/base/abstract_factory.rb +60 -0
- data/lib/evil_events/core/events/serializers/base/data_transformer.rb +57 -0
- data/lib/evil_events/core/events/serializers/base/event_serialization_state.rb +80 -0
- data/lib/evil_events/core/events/serializers/base/generic_config.rb +7 -0
- data/lib/evil_events/core/events/serializers/hash.rb +3 -73
- data/lib/evil_events/core/events/serializers/hash/config.rb +11 -0
- data/lib/evil_events/core/events/serializers/hash/engines.rb +10 -0
- data/lib/evil_events/core/events/serializers/hash/engines/native.rb +60 -0
- data/lib/evil_events/core/events/serializers/hash/factory.rb +65 -0
- data/lib/evil_events/core/events/serializers/hash/packer.rb +26 -0
- data/lib/evil_events/core/events/serializers/hash/unpacker.rb +24 -0
- data/lib/evil_events/core/events/serializers/json.rb +3 -59
- data/lib/evil_events/core/events/serializers/json/config.rb +11 -0
- data/lib/evil_events/core/events/serializers/json/engines.rb +10 -0
- data/lib/evil_events/core/events/serializers/json/engines/native.rb +43 -0
- data/lib/evil_events/core/events/serializers/json/factory.rb +65 -0
- data/lib/evil_events/core/events/serializers/json/packer.rb +26 -0
- data/lib/evil_events/core/events/serializers/json/unpacker.rb +31 -0
- data/lib/evil_events/core/events/serializers/message_pack.rb +7 -0
- data/lib/evil_events/core/events/serializers/message_pack/config.rb +17 -0
- data/lib/evil_events/core/events/serializers/message_pack/engines.rb +10 -0
- data/lib/evil_events/core/events/serializers/message_pack/engines/mpacker.rb +81 -0
- data/lib/evil_events/core/events/serializers/message_pack/factory.rb +65 -0
- data/lib/evil_events/core/events/serializers/message_pack/packer.rb +26 -0
- data/lib/evil_events/core/events/serializers/message_pack/unpacker.rb +24 -0
- data/lib/evil_events/core/events/serializers/xml.rb +7 -0
- data/lib/evil_events/core/events/serializers/xml/config.rb +11 -0
- data/lib/evil_events/core/events/serializers/xml/engines.rb +10 -0
- data/lib/evil_events/core/events/serializers/xml/engines/ox.rb +31 -0
- data/lib/evil_events/core/events/serializers/xml/factory.rb +65 -0
- data/lib/evil_events/core/events/serializers/xml/packer.rb +26 -0
- data/lib/evil_events/core/events/serializers/xml/unpacker.rb +24 -0
- data/lib/evil_events/core/events/subscriber/mixin.rb +26 -3
- data/lib/evil_events/core/system.rb +28 -2
- data/lib/evil_events/core/system/broadcaster.rb +11 -5
- data/lib/evil_events/core/system/event_builder.rb +98 -34
- data/lib/evil_events/core/system/event_manager.rb +12 -0
- data/lib/evil_events/core/system/mock.rb +30 -2
- data/lib/evil_events/emitter.rb +4 -2
- data/lib/evil_events/error.rb +21 -1
- data/lib/evil_events/serializer.rb +14 -0
- data/lib/evil_events/shared.rb +1 -3
- data/lib/evil_events/shared/any_config.rb +60 -0
- data/lib/evil_events/version.rb +2 -2
- metadata +85 -14
- data/.jrubyrc +0 -1
- data/lib/evil_events/core/events/event_extensions/adapter_customizable.rb +0 -49
- data/lib/evil_events/core/events/event_extensions/emittable.rb +0 -41
- data/lib/evil_events/shared/combined_context.rb +0 -124
- data/lib/evil_events/shared/combined_context/mixin.rb +0 -43
- data/lib/evil_events/shared/configurable.rb +0 -7
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class EvilEvents::Core::Events::Serializers
|
4
|
+
class Hash
|
5
|
+
# @api private
|
6
|
+
# @since 0.4.0
|
7
|
+
class Packer < Base::DataTransformer
|
8
|
+
# @param event [EvilEvents::Core::Events::AbstractEvent]
|
9
|
+
# @raise [EvilEvents::HashSerializationError]
|
10
|
+
# @return [String]
|
11
|
+
#
|
12
|
+
# @see Base::DataTransformer
|
13
|
+
#
|
14
|
+
# @api private
|
15
|
+
# @since 0.4.0
|
16
|
+
def call(event)
|
17
|
+
unless event.is_a?(EvilEvents::Core::Events::AbstractEvent)
|
18
|
+
raise EvilEvents::HashSerializationError
|
19
|
+
end
|
20
|
+
|
21
|
+
serialization_state = build_serialization_state(event)
|
22
|
+
engine.dump(serialization_state)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class EvilEvents::Core::Events::Serializers
|
4
|
+
class Hash
|
5
|
+
# @api private
|
6
|
+
# @since 0.4.0
|
7
|
+
class Unpacker < Base::DataTransformer
|
8
|
+
# @param serialized_event [::Hash]
|
9
|
+
# @raise [EvilEvents::HashDeserializationError]
|
10
|
+
# @return [EvilEvents::Core::Events::AbstractEvent]
|
11
|
+
#
|
12
|
+
# @see Base::DataTransformer
|
13
|
+
#
|
14
|
+
# @api private
|
15
|
+
# @since 0.4.0
|
16
|
+
def call(serialized_event)
|
17
|
+
raise EvilEvents::HashDeserializationError unless serialized_event.is_a?(::Hash)
|
18
|
+
serialization_state = engine.load(serialized_event)
|
19
|
+
raise EvilEvents::HashDeserializationError unless serialization_state.valid?
|
20
|
+
restore_event_instance(serialization_state)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -1,63 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class EvilEvents::Core::Events::Serializers
|
4
|
-
# @api
|
5
|
-
# @since 0.
|
6
|
-
|
7
|
-
# @since 0.1.1
|
8
|
-
extend Base
|
9
|
-
|
10
|
-
class << self
|
11
|
-
# @param event [EvilEvents::Core::Events::AbstractEvent]
|
12
|
-
# @raise [EvilEvents::SerializationError]
|
13
|
-
# @return [::Hash]
|
14
|
-
#
|
15
|
-
# @since 0.1.0
|
16
|
-
def serialize(event)
|
17
|
-
unless event.is_a?(EvilEvents::Core::Events::AbstractEvent)
|
18
|
-
raise EvilEvents::SerializationError
|
19
|
-
end
|
20
|
-
|
21
|
-
::JSON.generate(
|
22
|
-
id: event.id,
|
23
|
-
type: event.type,
|
24
|
-
payload: event.payload,
|
25
|
-
metadata: event.metadata
|
26
|
-
)
|
27
|
-
end
|
28
|
-
|
29
|
-
# @param json [String]
|
30
|
-
# @raise [EvilEvents::DeserializationError]
|
31
|
-
# @return [EvilEvents::Core::Events::AbstractEvent]
|
32
|
-
#
|
33
|
-
# @since 0.1.0
|
34
|
-
def deserialize(json)
|
35
|
-
raise EvilEvents::DeserializationError unless json.is_a?(String)
|
36
|
-
|
37
|
-
begin
|
38
|
-
json_hash = ::JSON.parse(json, symbolize_names: true)
|
39
|
-
event_id = json_hash[:id]
|
40
|
-
event_type = json_hash[:type]
|
41
|
-
event_payload = json_hash[:payload]
|
42
|
-
event_metadata = json_hash[:metadata]
|
43
|
-
|
44
|
-
unless event_type && event_payload && event_metadata
|
45
|
-
raise EvilEvents::DeserializationError
|
46
|
-
end
|
47
|
-
rescue ::JSON::ParserError
|
48
|
-
raise EvilEvents::DeserializationError
|
49
|
-
end
|
50
|
-
|
51
|
-
restore_event_instance(
|
52
|
-
type: event_type,
|
53
|
-
id: event_id,
|
54
|
-
payload: event_payload,
|
55
|
-
metadata: event_metadata
|
56
|
-
)
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
# @since 0.1.0
|
62
|
-
register(:json) { JSON }
|
4
|
+
# @api private
|
5
|
+
# @since 0.4.0
|
6
|
+
JSON = Class.new(Base)
|
63
7
|
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class EvilEvents::Core::Events::Serializers::JSON::Engines
|
4
|
+
# @api private
|
5
|
+
# @since 0.4.0
|
6
|
+
class Native < EvilEvents::Core::Events::Serializers::Base::AbstractEngine
|
7
|
+
# @param serialization_state [Base::EventSerializationState]
|
8
|
+
# @return [String]
|
9
|
+
#
|
10
|
+
# @since 0.4.0
|
11
|
+
# @api private
|
12
|
+
def dump(serialization_state)
|
13
|
+
::JSON.generate(
|
14
|
+
id: serialization_state.id,
|
15
|
+
type: serialization_state.type,
|
16
|
+
payload: serialization_state.payload,
|
17
|
+
metadata: serialization_state.metadata
|
18
|
+
)
|
19
|
+
end
|
20
|
+
|
21
|
+
# @param json_string [String]
|
22
|
+
# @raise [EvilEvents::SerializationEngineError]
|
23
|
+
# @return [EventSerializationState]
|
24
|
+
#
|
25
|
+
# @since 0.4.0
|
26
|
+
# @api private
|
27
|
+
def load(json_string)
|
28
|
+
json = ::JSON.parse(json_string, symbolize_names: true)
|
29
|
+
|
30
|
+
restore_serialization_state(
|
31
|
+
id: json[:id],
|
32
|
+
type: json[:type],
|
33
|
+
payload: json[:payload],
|
34
|
+
metadata: json[:metadata]
|
35
|
+
)
|
36
|
+
rescue ::JSON::ParserError, TypeError
|
37
|
+
raise EvilEvents::SerializationEngineError
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# @since 0.4.0
|
42
|
+
register(:native, Native)
|
43
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class EvilEvents::Core::Events::Serializers
|
4
|
+
class JSON
|
5
|
+
# @api private
|
6
|
+
# @since 0.4.0
|
7
|
+
class Factory < AbstractFactory
|
8
|
+
# @return [JSON::Config]
|
9
|
+
#
|
10
|
+
# @api private
|
11
|
+
# @since 0.4.0
|
12
|
+
def build_config
|
13
|
+
settings = EvilEvents::Core::Bootstrap[:config].serializers.json
|
14
|
+
|
15
|
+
Config.new do |config|
|
16
|
+
config.engine = settings.engine
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# @param config [JSON::Config]
|
21
|
+
# @raise [EvilEvents::UnrecognizedSerializationEngineError]
|
22
|
+
# @return [Base::AbstractEngine]
|
23
|
+
#
|
24
|
+
# @api private
|
25
|
+
# @since 0.4.0
|
26
|
+
def build_engine(config)
|
27
|
+
Engines.resolve(config.engine).new(config)
|
28
|
+
rescue Dry::Container::Error
|
29
|
+
raise EvilEvents::UnrecognizedSerializationEngineError
|
30
|
+
end
|
31
|
+
|
32
|
+
# @param engine [Base::AbstractEngine]
|
33
|
+
# @param config [JSON::Config]
|
34
|
+
# @return [JSON::Packer]
|
35
|
+
#
|
36
|
+
# @api private
|
37
|
+
# @since 0.4.0
|
38
|
+
def build_packer(engine, _config)
|
39
|
+
Packer.new(engine)
|
40
|
+
end
|
41
|
+
|
42
|
+
# @param engine [Base::AbstractEngine]
|
43
|
+
# @param config [JSON::Config]
|
44
|
+
# @return [JSON::Unpacker]
|
45
|
+
#
|
46
|
+
# @api private
|
47
|
+
# @since 0.4.0
|
48
|
+
def build_unpacker(engine, _config)
|
49
|
+
Unpacker.new(engine)
|
50
|
+
end
|
51
|
+
|
52
|
+
# @param engine [Base::AbstractEngine]
|
53
|
+
# @param config [JSON::Config]
|
54
|
+
# @param packer [JSON::Packer]
|
55
|
+
# @param unpacker [JSON::Unpacker]
|
56
|
+
# @return [JSON]
|
57
|
+
#
|
58
|
+
# @api private
|
59
|
+
# @since 0.4.0
|
60
|
+
def create_adapter(engine, config, packer, unpacker)
|
61
|
+
JSON.new(engine, config, packer, unpacker)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class EvilEvents::Core::Events::Serializers
|
4
|
+
class JSON
|
5
|
+
# @api private
|
6
|
+
# @since 0.4.0
|
7
|
+
class Packer < Base::DataTransformer
|
8
|
+
# @param event [EvilEvents::Core::Events::AbstractEvent]
|
9
|
+
# @raise [EvilEvents::JSONSerializationError]
|
10
|
+
# @return [String]
|
11
|
+
#
|
12
|
+
# @see Base::DataTransformer
|
13
|
+
#
|
14
|
+
# @api private
|
15
|
+
# @since 0.4.0
|
16
|
+
def call(event)
|
17
|
+
unless event.is_a?(EvilEvents::Core::Events::AbstractEvent)
|
18
|
+
raise EvilEvents::JSONSerializationError
|
19
|
+
end
|
20
|
+
|
21
|
+
serialization_state = build_serialization_state(event)
|
22
|
+
engine.dump(serialization_state)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class EvilEvents::Core::Events::Serializers
|
4
|
+
class JSON
|
5
|
+
# @api private
|
6
|
+
# @since 0.4.0
|
7
|
+
class Unpacker < Base::DataTransformer
|
8
|
+
# @param serialized_event [String]
|
9
|
+
# @raise [EvilEvents::JSONDeserializationError]
|
10
|
+
# @return [EvilEvents::Core::Events::AbstractEvent]
|
11
|
+
#
|
12
|
+
# @see Base::DataTransformer
|
13
|
+
#
|
14
|
+
# @api private
|
15
|
+
# @since 0.4.0
|
16
|
+
def call(serialized_event)
|
17
|
+
raise EvilEvents::JSONDeserializationError unless serialized_event.is_a?(String)
|
18
|
+
|
19
|
+
begin
|
20
|
+
serialization_state = engine.load(serialized_event)
|
21
|
+
rescue EvilEvents::SerializationEngineError
|
22
|
+
raise EvilEvents::JSONDeserializationError
|
23
|
+
end
|
24
|
+
|
25
|
+
raise EvilEvents::JSONDeserializationError unless serialization_state.valid?
|
26
|
+
|
27
|
+
restore_event_instance(serialization_state)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class EvilEvents::Core::Events::Serializers
|
4
|
+
class MessagePack
|
5
|
+
# @api private
|
6
|
+
# @since 0.4.0
|
7
|
+
class Config < Base::GenericConfig
|
8
|
+
configure do
|
9
|
+
setting :engine, reader: true
|
10
|
+
|
11
|
+
setting :mpacker, reader: true do
|
12
|
+
setting :configurator, ->(engine) {}
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class EvilEvents::Core::Events::Serializers::MessagePack::Engines
|
4
|
+
# @api private
|
5
|
+
# @since 0.4.0
|
6
|
+
class Mpacker < EvilEvents::Core::Events::Serializers::Base::AbstractEngine
|
7
|
+
# @param config [EvilEvents::Core::Events::Serializers::MessagePack::Config]
|
8
|
+
# @raise [EvilEvents::SerializationEngineError]
|
9
|
+
#
|
10
|
+
# @api private
|
11
|
+
# @since 0.4.0
|
12
|
+
def initialize(config)
|
13
|
+
configurator = config.mpacker.configurator
|
14
|
+
raise EvilEvents::SerializationEngineError unless configurator.is_a?(Proc)
|
15
|
+
@factory = ::MessagePack::Factory.new.tap { |factory| configurator.call(factory) }
|
16
|
+
end
|
17
|
+
|
18
|
+
# @param serialization_state [Base::EventSerializationState]
|
19
|
+
# @return [String]
|
20
|
+
#
|
21
|
+
# @since 0.4.0
|
22
|
+
# @api private
|
23
|
+
def dump(serialization_state)
|
24
|
+
packer.pack(
|
25
|
+
id: serialization_state.id,
|
26
|
+
type: serialization_state.type,
|
27
|
+
payload: serialization_state.payload,
|
28
|
+
metadata: serialization_state.metadata
|
29
|
+
).to_str
|
30
|
+
end
|
31
|
+
|
32
|
+
# @param message [String]
|
33
|
+
# @raise [EvilEvents::SerializationEngineError]
|
34
|
+
# @return [EventSerializationState]
|
35
|
+
#
|
36
|
+
# @since 0.4.0
|
37
|
+
# @api private
|
38
|
+
def load(message)
|
39
|
+
begin
|
40
|
+
event_data = unpacker.feed(message).unpack
|
41
|
+
raise EvilEvents::SerializationEngineError unless event_data.is_a?(::Hash)
|
42
|
+
rescue ::MessagePack::UnpackError, ArgumentError, TypeError
|
43
|
+
raise EvilEvents::SerializationEngineError
|
44
|
+
end
|
45
|
+
|
46
|
+
restore_serialization_state(
|
47
|
+
id: event_data[:id],
|
48
|
+
type: event_data[:type],
|
49
|
+
payload: event_data[:payload],
|
50
|
+
metadata: event_data[:metadata]
|
51
|
+
)
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
# @return [::MessagePack::Factory]
|
57
|
+
#
|
58
|
+
# @api private
|
59
|
+
# @since 0.4.0
|
60
|
+
attr_reader :factory
|
61
|
+
|
62
|
+
# @return [::MessagePack::Packer]
|
63
|
+
#
|
64
|
+
# @api private
|
65
|
+
# @since 0.4.0
|
66
|
+
def packer
|
67
|
+
factory.packer
|
68
|
+
end
|
69
|
+
|
70
|
+
# @return [::MessagePack::Unpacker]
|
71
|
+
#
|
72
|
+
# @api private
|
73
|
+
# @since 0.4.0
|
74
|
+
def unpacker
|
75
|
+
factory.unpacker(symbolize_keys: true)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# @since 0.4.0
|
80
|
+
register(:mpacker, Mpacker)
|
81
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class EvilEvents::Core::Events::Serializers
|
4
|
+
class MessagePack
|
5
|
+
# @api private
|
6
|
+
# @since 0.4.0
|
7
|
+
class Factory < AbstractFactory
|
8
|
+
# @return [MessagePack::Config]
|
9
|
+
#
|
10
|
+
# @api private
|
11
|
+
# @since 0.4.0
|
12
|
+
def build_config
|
13
|
+
settings = EvilEvents::Core::Bootstrap[:config].serializers.msgpack
|
14
|
+
|
15
|
+
Config.new do |config|
|
16
|
+
config.engine = settings.engine
|
17
|
+
config.mpacker.configurator = settings.mpacker.configurator
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# @param config [MessagePack::Config]
|
22
|
+
# @raise [EvilEvents::UnrecognizedSerializationEngineError]
|
23
|
+
# @return [Base::AbstractEngine]
|
24
|
+
#
|
25
|
+
# @api private
|
26
|
+
# @since 0.4.0
|
27
|
+
def build_engine(config)
|
28
|
+
Engines.resolve(config.engine).new(config)
|
29
|
+
rescue Dry::Container::Error
|
30
|
+
raise EvilEvents::UnrecognizedSerializationEngineError
|
31
|
+
end
|
32
|
+
|
33
|
+
# @param engine [Base::AbstractEngine]
|
34
|
+
# @param config [MessagePack::Config]
|
35
|
+
# @return [MessagePack::Packer]
|
36
|
+
#
|
37
|
+
# @api private
|
38
|
+
# @since 0.4.0
|
39
|
+
def build_packer(engine, _config)
|
40
|
+
Packer.new(engine)
|
41
|
+
end
|
42
|
+
|
43
|
+
# @param engine [Base::AbstractEngine]
|
44
|
+
# @param config [MessagePack::Config]
|
45
|
+
# @return [MessagePack::Unpacker]
|
46
|
+
#
|
47
|
+
# @api private
|
48
|
+
# @since 0.4.0
|
49
|
+
def build_unpacker(engine, _config)
|
50
|
+
Unpacker.new(engine)
|
51
|
+
end
|
52
|
+
|
53
|
+
# @param engine [Base::AbstractEngine]
|
54
|
+
# @param config [MessagePack::Config]
|
55
|
+
# @param packer [MessagePack::Packer]
|
56
|
+
# @param unpacker [MessagePack::Unpacker]
|
57
|
+
#
|
58
|
+
# @api private
|
59
|
+
# @since 0.4.0
|
60
|
+
def create_adapter(engine, config, packer, unpacker)
|
61
|
+
MessagePack.new(engine, config, packer, unpacker)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|