aggro 0.0.1 → 0.0.2
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 +4 -4
- data/.rubocop.yml +8 -0
- data/.travis.yml +15 -0
- data/Gemfile +9 -0
- data/README.md +5 -1
- data/Rakefile +10 -0
- data/aggro.gemspec +8 -1
- data/lib/aggro.rb +191 -7
- data/lib/aggro/abstract_store.rb +12 -0
- data/lib/aggro/aggregate.rb +98 -0
- data/lib/aggro/aggregate_ref.rb +68 -6
- data/lib/aggro/attribute_dsl.rb +96 -0
- data/lib/aggro/binding_dsl.rb +45 -0
- data/lib/aggro/block_helper.rb +14 -0
- data/lib/aggro/channel.rb +37 -0
- data/lib/aggro/client.rb +12 -0
- data/lib/aggro/cluster_config.rb +57 -0
- data/lib/aggro/command.rb +16 -0
- data/lib/aggro/concurrent_actor.rb +26 -0
- data/lib/aggro/event_bus.rb +94 -0
- data/lib/aggro/event_dsl.rb +53 -0
- data/lib/aggro/event_proxy.rb +23 -0
- data/lib/aggro/event_serializer.rb +14 -0
- data/lib/aggro/file_store.rb +97 -0
- data/lib/aggro/file_store/reader.rb +21 -0
- data/lib/aggro/file_store/writer.rb +27 -0
- data/lib/aggro/handler/command.rb +60 -0
- data/lib/aggro/handler/create_aggregate.rb +42 -0
- data/lib/aggro/handler/get_events.rb +30 -0
- data/lib/aggro/handler/query.rb +60 -0
- data/lib/aggro/handler/start_saga.rb +56 -0
- data/lib/aggro/local_node.rb +28 -0
- data/lib/aggro/locator.rb +32 -0
- data/lib/aggro/message/ask.rb +16 -0
- data/lib/aggro/message/command.rb +36 -0
- data/lib/aggro/message/create_aggregate.rb +16 -0
- data/lib/aggro/message/endpoint.rb +16 -0
- data/lib/aggro/message/events.rb +24 -0
- data/lib/aggro/message/get_events.rb +16 -0
- data/lib/aggro/message/heartbeat.rb +16 -0
- data/lib/aggro/message/invalid_target.rb +20 -0
- data/lib/aggro/message/ok.rb +20 -0
- data/lib/aggro/message/publisher_endpoint_inquiry.rb +16 -0
- data/lib/aggro/message/query.rb +36 -0
- data/lib/aggro/message/result.rb +16 -0
- data/lib/aggro/message/start_saga.rb +28 -0
- data/lib/aggro/message/unhandled_operation.rb +20 -0
- data/lib/aggro/message/unknown_operation.rb +20 -0
- data/lib/aggro/message_parser.rb +10 -0
- data/lib/aggro/message_router.rb +26 -0
- data/lib/aggro/nanomsg_transport.rb +31 -0
- data/lib/aggro/nanomsg_transport/client.rb +35 -0
- data/lib/aggro/nanomsg_transport/connection.rb +98 -0
- data/lib/aggro/nanomsg_transport/publish.rb +17 -0
- data/lib/aggro/nanomsg_transport/publisher.rb +37 -0
- data/lib/aggro/nanomsg_transport/raw_reply.rb +18 -0
- data/lib/aggro/nanomsg_transport/raw_request.rb +18 -0
- data/lib/aggro/nanomsg_transport/reply.rb +17 -0
- data/lib/aggro/nanomsg_transport/request.rb +17 -0
- data/lib/aggro/nanomsg_transport/server.rb +84 -0
- data/lib/aggro/nanomsg_transport/socket_error.rb +20 -0
- data/lib/aggro/nanomsg_transport/subscribe.rb +27 -0
- data/lib/aggro/nanomsg_transport/subscriber.rb +82 -0
- data/lib/aggro/node.rb +29 -0
- data/lib/aggro/node_list.rb +39 -0
- data/lib/aggro/projection.rb +13 -0
- data/lib/aggro/query.rb +11 -0
- data/lib/aggro/saga.rb +94 -0
- data/lib/aggro/saga_runner.rb +87 -0
- data/lib/aggro/saga_runner/start_saga.rb +12 -0
- data/lib/aggro/saga_status.rb +29 -0
- data/lib/aggro/server.rb +88 -0
- data/lib/aggro/subscriber.rb +48 -0
- data/lib/aggro/subscription.rb +41 -0
- data/lib/aggro/transform/boolean.rb +16 -0
- data/lib/aggro/transform/email.rb +26 -0
- data/lib/aggro/transform/id.rb +34 -0
- data/lib/aggro/transform/integer.rb +22 -0
- data/lib/aggro/transform/money.rb +22 -0
- data/lib/aggro/transform/noop.rb +16 -0
- data/lib/aggro/transform/string.rb +16 -0
- data/lib/aggro/transform/time_interval.rb +24 -0
- data/lib/aggro/version.rb +1 -1
- data/spec/lib/aggro/abstract_store_spec.rb +15 -0
- data/spec/lib/aggro/aggregate_ref_spec.rb +63 -12
- data/spec/lib/aggro/aggregate_spec.rb +207 -0
- data/spec/lib/aggro/channel_spec.rb +87 -0
- data/spec/lib/aggro/client_spec.rb +26 -0
- data/spec/lib/aggro/cluster_config_spec.rb +33 -0
- data/spec/lib/aggro/command_spec.rb +52 -0
- data/spec/lib/aggro/concurrent_actor_spec.rb +44 -0
- data/spec/lib/aggro/event_bus_spec.rb +20 -0
- data/spec/lib/aggro/event_serializer_spec.rb +28 -0
- data/spec/lib/aggro/file_store/reader_spec.rb +32 -0
- data/spec/lib/aggro/file_store/writer_spec.rb +67 -0
- data/spec/lib/aggro/file_store_spec.rb +51 -0
- data/spec/lib/aggro/handler/command_spec.rb +78 -0
- data/spec/lib/aggro/handler/create_aggregate_spec.rb +64 -0
- data/spec/lib/aggro/handler/get_events_handler_spec.rb +45 -0
- data/spec/lib/aggro/handler/query_spec.rb +78 -0
- data/spec/lib/aggro/handler/start_saga_spec.rb +64 -0
- data/spec/lib/aggro/local_node_spec.rb +52 -0
- data/spec/lib/aggro/locator_spec.rb +61 -0
- data/spec/lib/aggro/message/ask_spec.rb +23 -0
- data/spec/lib/aggro/message/command_spec.rb +50 -0
- data/spec/lib/aggro/message/create_aggregate_spec.rb +28 -0
- data/spec/lib/aggro/message/endpoint_spec.rb +23 -0
- data/spec/lib/aggro/message/events_spec.rb +37 -0
- data/spec/lib/aggro/message/get_events_spec.rb +33 -0
- data/spec/lib/aggro/message/heartbeat_spec.rb +23 -0
- data/spec/lib/aggro/message/invalid_target_spec.rb +28 -0
- data/spec/lib/aggro/message/ok_spec.rb +27 -0
- data/spec/lib/aggro/message/publisher_endpoint_inquiry_spec.rb +23 -0
- data/spec/lib/aggro/message/query_spec.rb +50 -0
- data/spec/lib/aggro/message/start_saga_spec.rb +37 -0
- data/spec/lib/aggro/message/unhandled_operation_spec.rb +28 -0
- data/spec/lib/aggro/message/unknown_operation_spec.rb +28 -0
- data/spec/lib/aggro/message_parser_spec.rb +16 -0
- data/spec/lib/aggro/message_router_spec.rb +35 -0
- data/spec/lib/aggro/nanomsg_transport/socket_error_spec.rb +21 -0
- data/spec/lib/aggro/nanomsg_transport_spec.rb +37 -0
- data/spec/lib/aggro/node_list_spec.rb +38 -0
- data/spec/lib/aggro/node_spec.rb +44 -0
- data/spec/lib/aggro/projection_spec.rb +22 -0
- data/spec/lib/aggro/query_spec.rb +47 -0
- data/spec/lib/aggro/saga_runner_spec.rb +84 -0
- data/spec/lib/aggro/saga_spec.rb +126 -0
- data/spec/lib/aggro/saga_status_spec.rb +56 -0
- data/spec/lib/aggro/server_spec.rb +118 -0
- data/spec/lib/aggro/subscriber_spec.rb +59 -0
- data/spec/lib/aggro/subscription_spec.rb +50 -0
- data/spec/lib/aggro/transform/boolean_spec.rb +23 -0
- data/spec/lib/aggro/transform/email_spec.rb +13 -0
- data/spec/lib/aggro/transform/id_spec.rb +70 -0
- data/spec/lib/aggro/transform/integer_spec.rb +30 -0
- data/spec/lib/aggro/transform/money_spec.rb +34 -0
- data/spec/lib/aggro/transform/string_spec.rb +15 -0
- data/spec/lib/aggro/transform/time_interval_spec.rb +29 -0
- data/spec/lib/aggro_spec.rb +63 -19
- data/spec/spec_helper.rb +21 -2
- metadata +283 -3
@@ -0,0 +1,96 @@
|
|
1
|
+
module Aggro
|
2
|
+
# Public: Adds a DSL defining attributes and validations.
|
3
|
+
module AttributeDSL
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
include ActiveModel::Model
|
7
|
+
include ActiveModel::Validations::Callbacks
|
8
|
+
|
9
|
+
def initialize(attrs = {})
|
10
|
+
if Thread.current[:causation_id] && respond_to?(:causation_id=)
|
11
|
+
attrs.merge! causation_id: Thread.current[:causation_id]
|
12
|
+
end
|
13
|
+
|
14
|
+
if Thread.current[:correlation_id] && respond_to?(:correlation_id=)
|
15
|
+
attrs.merge! correlation_id: Thread.current[:correlation_id]
|
16
|
+
end
|
17
|
+
|
18
|
+
super
|
19
|
+
end
|
20
|
+
|
21
|
+
def attributes
|
22
|
+
self.class.attributes.keys.reduce({}) do |hash, name|
|
23
|
+
hash.merge name => send(name)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def serialized_attributes
|
28
|
+
self.class.attributes.reduce({}) do |hash, (name, transform)|
|
29
|
+
hash.merge name => transform.serialize(send(name))
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class_methods do
|
34
|
+
def attributes
|
35
|
+
Aggro.class_attributes[name]
|
36
|
+
end
|
37
|
+
|
38
|
+
def attribute(name)
|
39
|
+
create_attrs name, Transform::NOOP
|
40
|
+
end
|
41
|
+
|
42
|
+
def boolean(name)
|
43
|
+
create_attrs name, Transform::Boolean
|
44
|
+
end
|
45
|
+
|
46
|
+
def email(name)
|
47
|
+
create_attrs name, Transform::Email
|
48
|
+
end
|
49
|
+
|
50
|
+
def generate_id(name)
|
51
|
+
create_attrs name, Transform::ID.new(generate: true)
|
52
|
+
end
|
53
|
+
|
54
|
+
def id(name)
|
55
|
+
create_attrs name, Transform::ID.new
|
56
|
+
end
|
57
|
+
|
58
|
+
def integer(name)
|
59
|
+
create_attrs name, Transform::Integer
|
60
|
+
end
|
61
|
+
|
62
|
+
def money(name)
|
63
|
+
require 'money'
|
64
|
+
require 'monetize'
|
65
|
+
|
66
|
+
create_attrs name, Transform::Money
|
67
|
+
rescue LoadError
|
68
|
+
puts '`money` and `monetize` gems must be present to use money type'
|
69
|
+
end
|
70
|
+
|
71
|
+
def string(name)
|
72
|
+
create_attrs name, Transform::String
|
73
|
+
end
|
74
|
+
|
75
|
+
def time_interval(name)
|
76
|
+
require 'time-interval'
|
77
|
+
|
78
|
+
create_attrs name, Transform::TimeInterval
|
79
|
+
rescue LoadError
|
80
|
+
puts '`time-interval` gem must be present to use time_interval type'
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
def create_attrs(name, transformer)
|
86
|
+
attr_reader name
|
87
|
+
attributes[name] = transformer
|
88
|
+
|
89
|
+
define_method("#{name}=") do |value|
|
90
|
+
transformed = self.class.attributes[name].deserialize value
|
91
|
+
instance_variable_set "@#{name}", transformed
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Aggro
|
2
|
+
# Public: Adds a DSL creating domain event bindings.
|
3
|
+
module BindingDSL
|
4
|
+
def bind(ref, filters: default_filters, to: nil, &block)
|
5
|
+
if to
|
6
|
+
bindings << Aggro.event_bus.subscribe(ref.id, self, to, filters)
|
7
|
+
else
|
8
|
+
bind_block ref, filters, &block
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def bind_block(ref, filters, namespace = generate_namespace, &block)
|
15
|
+
new_methods = BlockHelper.method_definitions(&block)
|
16
|
+
event_methods[namespace] = Set.new(new_methods)
|
17
|
+
|
18
|
+
class_eval(&block)
|
19
|
+
move_methods_to_namespace(new_methods, namespace)
|
20
|
+
|
21
|
+
bindings << Aggro.event_bus.subscribe(ref.id, self, namespace, filters)
|
22
|
+
end
|
23
|
+
|
24
|
+
def bindings
|
25
|
+
@bindings ||= []
|
26
|
+
end
|
27
|
+
|
28
|
+
def default_filters
|
29
|
+
{}
|
30
|
+
end
|
31
|
+
|
32
|
+
def generate_namespace
|
33
|
+
[*('a'..'z')].sample(8).join
|
34
|
+
end
|
35
|
+
|
36
|
+
def move_methods_to_namespace(method_list, namespace)
|
37
|
+
class_eval do
|
38
|
+
method_list.each do |method|
|
39
|
+
alias_method "#{namespace}_#{method}", method
|
40
|
+
remove_method method
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Aggro
|
2
|
+
# Private: Helper fuction for common operations on blocks.
|
3
|
+
module BlockHelper
|
4
|
+
module_function
|
5
|
+
|
6
|
+
def method_definitions(&block)
|
7
|
+
test_class = Class.new(BasicObject)
|
8
|
+
starting_methods = test_class.instance_methods
|
9
|
+
test_class.class_eval(&block)
|
10
|
+
|
11
|
+
test_class.instance_methods - starting_methods
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Aggro
|
2
|
+
# Private: Provides an interface to communicate with an aggregate or saga.
|
3
|
+
# Only loads the target object when needed.
|
4
|
+
class Channel < Struct.new(:id, :type)
|
5
|
+
def forward_command(command)
|
6
|
+
target << command if handles_command?(command)
|
7
|
+
end
|
8
|
+
|
9
|
+
def handles_command?(command)
|
10
|
+
target_class.allows? command
|
11
|
+
end
|
12
|
+
|
13
|
+
def handles_query?(query)
|
14
|
+
target_class.responds_to? query
|
15
|
+
end
|
16
|
+
|
17
|
+
def run_query(query)
|
18
|
+
target.ask query if handles_query? query
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def target
|
24
|
+
@target ||= begin
|
25
|
+
ConcurrentActor.spawn!(
|
26
|
+
name: id,
|
27
|
+
args: [target_class.new(id)],
|
28
|
+
executor: Concurrent.configuration.global_task_pool
|
29
|
+
)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def target_class
|
34
|
+
@target_class ||= ActiveSupport::Inflector.constantize type
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/aggro/client.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
module Aggro
|
2
|
+
# Public: Makes requests against a given endpoint returning parsed responses.
|
3
|
+
class Client
|
4
|
+
def initialize(endpoint)
|
5
|
+
@transport_client = Aggro.transport.client(endpoint)
|
6
|
+
end
|
7
|
+
|
8
|
+
def post(message)
|
9
|
+
MessageParser.parse @transport_client.post message
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Aggro
|
2
|
+
# Public: Stores the current cluster config. Persists to disk after changes.
|
3
|
+
class ClusterConfig
|
4
|
+
attr_reader :node_name
|
5
|
+
|
6
|
+
def initialize(path)
|
7
|
+
@path = path
|
8
|
+
|
9
|
+
if File.exist? path
|
10
|
+
load_config
|
11
|
+
else
|
12
|
+
initialize_config
|
13
|
+
persist_config
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def add_node(name, server)
|
18
|
+
@nodes = nodes.merge(name => server).freeze
|
19
|
+
persist_config
|
20
|
+
end
|
21
|
+
|
22
|
+
def nodes
|
23
|
+
@nodes ||= {}.freeze
|
24
|
+
end
|
25
|
+
|
26
|
+
def server_node?
|
27
|
+
@is_server_node == true
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def initialize_config
|
33
|
+
@node_name = SecureRandom.uuid
|
34
|
+
@is_server_node = false
|
35
|
+
end
|
36
|
+
|
37
|
+
def load_config
|
38
|
+
YAML.load_file(@path).each do |key, value|
|
39
|
+
instance_variable_set "@#{key}", value.freeze
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def persist_config
|
44
|
+
File.open @path, 'w' do |file|
|
45
|
+
file.write YAML.dump to_h
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def to_h
|
50
|
+
{
|
51
|
+
node_name: node_name,
|
52
|
+
nodes: nodes,
|
53
|
+
is_server_node: server_node?
|
54
|
+
}
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Aggro
|
2
|
+
# Public: Mixin to turn a PORO into an Aggro command.
|
3
|
+
module Command
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
include AttributeDSL
|
6
|
+
|
7
|
+
included do
|
8
|
+
generate_id :causation_id
|
9
|
+
generate_id :correlation_id
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_details
|
13
|
+
{ name: model_name.name, args: serialized_attributes }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Aggro
|
2
|
+
# Private: Wraps a given target in an concurrent actor.
|
3
|
+
class ConcurrentActor < Concurrent::Actor::Context
|
4
|
+
def initialize(target)
|
5
|
+
@target = target
|
6
|
+
end
|
7
|
+
|
8
|
+
def on_message(message)
|
9
|
+
if command? message
|
10
|
+
@target.send :apply_command, message
|
11
|
+
elsif query? message
|
12
|
+
@target.send :run_query, message
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def command?(message)
|
19
|
+
message.class.included_modules.include? Command
|
20
|
+
end
|
21
|
+
|
22
|
+
def query?(message)
|
23
|
+
message.class.included_modules.include? Query
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module Aggro
|
2
|
+
# Public: Publishes events to any subscribed listeners.
|
3
|
+
class EventBus
|
4
|
+
def initialize
|
5
|
+
@remote_publishers = {}
|
6
|
+
end
|
7
|
+
|
8
|
+
def publish(topic, event)
|
9
|
+
Aggro.server.publish Message::Events.new(topic, [event])
|
10
|
+
|
11
|
+
return unless subscriptions.key? topic
|
12
|
+
|
13
|
+
subscriptions[topic].each do |subscription|
|
14
|
+
subscription.handle_event event
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def subscribe(topic, subscriber, event_namespace = nil, filters = {})
|
19
|
+
subscription = Subscription.new(topic, subscriber, event_namespace,
|
20
|
+
filters, 0)
|
21
|
+
|
22
|
+
catchup_subscriber topic, subscription
|
23
|
+
|
24
|
+
subscriptions[topic] ||= []
|
25
|
+
subscriptions[topic] << subscription
|
26
|
+
|
27
|
+
subscribe_bus_to_publisher topic
|
28
|
+
|
29
|
+
subscription
|
30
|
+
end
|
31
|
+
|
32
|
+
def unsubscribe(topic, subscriber)
|
33
|
+
subscriptions[topic].delete subscriber
|
34
|
+
end
|
35
|
+
|
36
|
+
def shutdown
|
37
|
+
remote_publishers.values.each(&:stop)
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
attr_reader :remote_publishers
|
43
|
+
|
44
|
+
def catchup_local(topic, subscription)
|
45
|
+
Aggro.store.read([topic]).first.events.each do |event|
|
46
|
+
subscription.handle_event event
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def catchup_remote(topic, subscription, node)
|
51
|
+
message = Message::GetEvents.new(Aggro.local_node.id, topic, 0)
|
52
|
+
response = node.client.post message
|
53
|
+
|
54
|
+
if response.is_a? Message::Events
|
55
|
+
response.events.each { |event| subscription.handle_event event }
|
56
|
+
else
|
57
|
+
fail 'Could not catchup subscriber'
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def catchup_subscriber(topic, subscription)
|
62
|
+
node = Locator.new(topic).primary_node
|
63
|
+
|
64
|
+
if node.is_a? LocalNode
|
65
|
+
catchup_local(topic, subscription)
|
66
|
+
else
|
67
|
+
catchup_remote(topic, subscription, node)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def handle_events(topic, events)
|
72
|
+
subscriptions[topic].each do |subscription|
|
73
|
+
events.each { |event| subscription.handle_event event }
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def subscribe_bus_to_publisher(topic)
|
78
|
+
node = Locator.new(topic).primary_node
|
79
|
+
|
80
|
+
return if node.is_a? LocalNode
|
81
|
+
|
82
|
+
publisher_endpoint = node.publisher_endpoint
|
83
|
+
remote_publishers[publisher_endpoint] ||= begin
|
84
|
+
Subscriber.new(publisher_endpoint, method(:handle_events)).tap(&:bind)
|
85
|
+
end
|
86
|
+
|
87
|
+
remote_publishers[publisher_endpoint].subscribe_to_topic topic
|
88
|
+
end
|
89
|
+
|
90
|
+
def subscriptions
|
91
|
+
@subscriptions ||= {}
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Aggro
|
2
|
+
# Public: Adds a DSL defining event handlers.
|
3
|
+
module EventDSL
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
def handles_event?(event_name, namespace = nil)
|
7
|
+
self.class.handles_event?(event_name, namespace) || \
|
8
|
+
namespace?(namespace) && event_methods[namespace].include?(event_name)
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def event_methods
|
14
|
+
@event_methods ||= {}
|
15
|
+
end
|
16
|
+
|
17
|
+
def namespace?(namespace)
|
18
|
+
!event_methods[namespace].nil?
|
19
|
+
end
|
20
|
+
|
21
|
+
class_methods do
|
22
|
+
def events(namespace = nil, &block)
|
23
|
+
new_methods = BlockHelper.method_definitions(&block)
|
24
|
+
|
25
|
+
event_methods[namespace] ||= Set.new
|
26
|
+
new_methods.each { |method| event_methods[namespace] << method }
|
27
|
+
|
28
|
+
class_eval(&block)
|
29
|
+
|
30
|
+
class_eval do
|
31
|
+
new_methods.each do |method|
|
32
|
+
alias_method "#{namespace}_#{method}", method
|
33
|
+
remove_method method
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def handles_event?(event_name, namespace = nil)
|
39
|
+
namespace?(namespace) && event_methods[namespace].include?(event_name)
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def event_methods
|
45
|
+
@event_methods ||= {}
|
46
|
+
end
|
47
|
+
|
48
|
+
def namespace?(namespace)
|
49
|
+
!event_methods[namespace].nil?
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|