eventhub-processor2 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +5 -0
- data/.rspec +2 -0
- data/.travis.yml +36 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +76 -0
- data/LICENSE.txt +22 -0
- data/README.md +38 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/docker/Dockerfile +6 -0
- data/docker/README.md +14 -0
- data/docker/definitions.json +87 -0
- data/docker/rabbitmq.config +16 -0
- data/eventhub-processor2.gemspec +34 -0
- data/example/README.md +19 -0
- data/example/config/example.json +19 -0
- data/example/crasher.rb +57 -0
- data/example/example.rb +25 -0
- data/example/publisher.rb +95 -0
- data/lib/eventhub/actor_heartbeat.rb +121 -0
- data/lib/eventhub/actor_listener.rb +161 -0
- data/lib/eventhub/actor_watchdog.rb +41 -0
- data/lib/eventhub/configuration.rb +114 -0
- data/lib/eventhub/constant.rb +35 -0
- data/lib/eventhub/consumer.rb +9 -0
- data/lib/eventhub/hash_extensions.rb +49 -0
- data/lib/eventhub/helper.rb +55 -0
- data/lib/eventhub/logger.rb +14 -0
- data/lib/eventhub/message.rb +158 -0
- data/lib/eventhub/processor2.rb +121 -0
- data/lib/eventhub/statistics.rb +53 -0
- data/lib/eventhub/version.rb +3 -0
- data/lib/eventhub.rb +25 -0
- metadata +190 -0
@@ -0,0 +1,121 @@
|
|
1
|
+
# EventHub module
|
2
|
+
module EventHub
|
3
|
+
# Heartbeat class
|
4
|
+
class ActorHeartbeat
|
5
|
+
include Celluloid
|
6
|
+
include Helper
|
7
|
+
finalizer :cleanup
|
8
|
+
|
9
|
+
def initialize(processor_instance)
|
10
|
+
@processor_instance = processor_instance
|
11
|
+
async.start
|
12
|
+
end
|
13
|
+
|
14
|
+
def start
|
15
|
+
EventHub.logger.info('Heartbeat is starting...')
|
16
|
+
|
17
|
+
every(60) { EventHub.logger.info("Running actors: #{Celluloid::Actor.all.size}: #{Celluloid::Actor.all.map{ |a| a.class }.join(', ') }") }
|
18
|
+
|
19
|
+
publish(heartbeat(action: 'started'))
|
20
|
+
loop do
|
21
|
+
sleep Configuration.processor[:heartbeat_cycle_in_s]
|
22
|
+
EventHub.logger.info('Running heartbeat...')
|
23
|
+
publish(heartbeat)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def cleanup
|
28
|
+
EventHub.logger.info('Heartbeat is cleanig up...')
|
29
|
+
publish(heartbeat(action: 'stopped'))
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def publish(message)
|
35
|
+
connection = Bunny.new(bunny_connection_properties)
|
36
|
+
connection.start
|
37
|
+
channel = connection.create_channel
|
38
|
+
channel.confirm_select
|
39
|
+
exchange = channel.direct(EventHub::EH_X_INBOUND, durable: true)
|
40
|
+
exchange.publish(message, persistent: true)
|
41
|
+
success = channel.wait_for_confirms
|
42
|
+
|
43
|
+
unless success
|
44
|
+
raise 'Published heartbeat message has '\
|
45
|
+
'not been confirmed by the server'
|
46
|
+
end
|
47
|
+
ensure
|
48
|
+
connection.close if connection
|
49
|
+
end
|
50
|
+
|
51
|
+
def heartbeat(args = { action: 'running' })
|
52
|
+
message = EventHub::Message.new
|
53
|
+
message.origin_module_id = EventHub::Configuration.name
|
54
|
+
message.origin_type = 'processor'
|
55
|
+
message.origin_site_id = 'global'
|
56
|
+
|
57
|
+
message.process_name = 'event_hub.heartbeat'
|
58
|
+
|
59
|
+
now = Time.now
|
60
|
+
|
61
|
+
# message structure needs more changes
|
62
|
+
message.body = {
|
63
|
+
version: @processor_instance.send(:version),
|
64
|
+
action: args[:action],
|
65
|
+
pid: Process.pid,
|
66
|
+
process_name: 'event_hub.heartbeat',
|
67
|
+
heartbeat: {
|
68
|
+
started: now_stamp(started_at),
|
69
|
+
stamp_last_beat: now_stamp(now),
|
70
|
+
uptime_in_ms: (now - started_at) * 1000,
|
71
|
+
heartbeat_cycle_in_ms: Configuration.processor[:heartbeat_cycle_in_s] * 1000,
|
72
|
+
queues_consuming_from: EventHub::Configuration.processor[:listener_queues],
|
73
|
+
queues_publishing_to: [EventHub::EH_X_INBOUND], # needs more dynamic in the future
|
74
|
+
host: Socket.gethostname,
|
75
|
+
addresses: addresses,
|
76
|
+
messages: messages_statistics
|
77
|
+
}
|
78
|
+
}
|
79
|
+
message.to_json
|
80
|
+
end
|
81
|
+
|
82
|
+
def started_at
|
83
|
+
@processor_instance.started_at
|
84
|
+
end
|
85
|
+
|
86
|
+
def statistics
|
87
|
+
@processor_instance.statistics
|
88
|
+
end
|
89
|
+
|
90
|
+
def addresses
|
91
|
+
interfaces = Socket.getifaddrs.select do |interface|
|
92
|
+
!interface.addr.ipv4_loopback? && !interface.addr.ipv6_loopback?
|
93
|
+
end
|
94
|
+
|
95
|
+
interfaces.map do |interface|
|
96
|
+
begin
|
97
|
+
{
|
98
|
+
interface: interface.name,
|
99
|
+
host_name: Socket.gethostname,
|
100
|
+
ip_address: interface.addr.ip_address
|
101
|
+
}
|
102
|
+
rescue
|
103
|
+
nil # will be ignored
|
104
|
+
end
|
105
|
+
end.compact
|
106
|
+
end
|
107
|
+
|
108
|
+
def messages_statistics
|
109
|
+
{
|
110
|
+
total: statistics.messages_total,
|
111
|
+
successful: statistics.messages_successful,
|
112
|
+
unsuccessful: statistics.messages_unsuccessful,
|
113
|
+
average_size: statistics.messages_average_size,
|
114
|
+
average_process_time_in_ms:
|
115
|
+
statistics.messages_average_process_time * 1000,
|
116
|
+
total_process_time_in_ms:
|
117
|
+
statistics.messages_total_process_time * 1000
|
118
|
+
}
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,161 @@
|
|
1
|
+
# EventHub module
|
2
|
+
module EventHub
|
3
|
+
# Listner Class
|
4
|
+
class ActorListener
|
5
|
+
include Celluloid
|
6
|
+
include Helper
|
7
|
+
finalizer :cleanup
|
8
|
+
|
9
|
+
def initialize(processor_instance)
|
10
|
+
@actor_watchdog = ActorWatchdog.new_link
|
11
|
+
@connections= {}
|
12
|
+
@processor_instance = processor_instance
|
13
|
+
start
|
14
|
+
end
|
15
|
+
|
16
|
+
def start
|
17
|
+
EventHub.logger.info('Listener is starting...')
|
18
|
+
EventHub::Configuration.processor[:listener_queues].each_with_index do |queue_name, index|
|
19
|
+
async.listen(queue_name: queue_name, index: index)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def restart
|
24
|
+
raise 'Listener is restarting...'
|
25
|
+
end
|
26
|
+
|
27
|
+
def listen(args = {})
|
28
|
+
with_listen(args) do |connection, channel, consumer, queue, queue_name|
|
29
|
+
EventHub.logger.info("Listening to queue [#{queue_name}]")
|
30
|
+
consumer.on_delivery do |delivery_info, metadata, payload|
|
31
|
+
EventHub.logger.info("#{queue_name}: [#{delivery_info.delivery_tag}]"\
|
32
|
+
' delivery')
|
33
|
+
|
34
|
+
# EventHub::logger.debug("delivery_info: #{delivery_info.inspect}")
|
35
|
+
# EventHub::logger.debug("metadata: #{metadata.inspect}")
|
36
|
+
|
37
|
+
@processor_instance.statistics.measure(payload.size) do
|
38
|
+
handle_payload(payload: payload,
|
39
|
+
connection: connection,
|
40
|
+
queue_name: queue_name,
|
41
|
+
content_type: metadata[:content_type],
|
42
|
+
priority: metadata[:priority],
|
43
|
+
delivery_tag: delivery_info.delivery_tag
|
44
|
+
)
|
45
|
+
channel.acknowledge(delivery_info.delivery_tag, false)
|
46
|
+
end
|
47
|
+
|
48
|
+
EventHub.logger.info("#{queue_name}: [#{delivery_info.delivery_tag}]"\
|
49
|
+
' acknowledged')
|
50
|
+
end
|
51
|
+
queue.subscribe_with(consumer, block: false)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def with_listen(args = {}, &block)
|
56
|
+
connection = Bunny.new(bunny_connection_properties)
|
57
|
+
connection.start
|
58
|
+
queue_name = args[:queue_name]
|
59
|
+
@connections[queue_name] = connection
|
60
|
+
channel = connection.create_channel
|
61
|
+
channel.prefetch(1)
|
62
|
+
queue = channel.queue(queue_name, durable: true)
|
63
|
+
consumer = EventHub::Consumer.new(channel,
|
64
|
+
queue,
|
65
|
+
EventHub::Configuration.name +
|
66
|
+
'-' +
|
67
|
+
args[:index].to_s,
|
68
|
+
false)
|
69
|
+
yield connection, channel, consumer, queue, queue_name
|
70
|
+
end
|
71
|
+
|
72
|
+
def handle_payload(args = {})
|
73
|
+
response_messages = []
|
74
|
+
connection = args[:connection]
|
75
|
+
|
76
|
+
# convert to EventHub message
|
77
|
+
message = EventHub::Message.from_json(args[:payload])
|
78
|
+
|
79
|
+
# append to execution history
|
80
|
+
message.append_to_execution_history(EventHub::Configuration.name)
|
81
|
+
|
82
|
+
# return invalid messages to dispatcher
|
83
|
+
if message.invalid?
|
84
|
+
response_messages << message
|
85
|
+
EventHub.logger.info("-> #{message.to_s} => return invalid to dispatcher")
|
86
|
+
else
|
87
|
+
begin
|
88
|
+
response_messages = @processor_instance.send(:handle_message,
|
89
|
+
message,
|
90
|
+
pass_arguments(args))
|
91
|
+
rescue => exception
|
92
|
+
# this catches unexpected exceptions in handle message method
|
93
|
+
# deadletter the message via dispatcher
|
94
|
+
message.status_code = EventHub::STATUS_DEADLETTER
|
95
|
+
message.status_message = exception
|
96
|
+
EventHub.logger.info("-> #{message.to_s} => return exception to diaptcher")
|
97
|
+
response_messages << message
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
Array(response_messages).each do |message|
|
102
|
+
publish(message: message.to_json, connection: connection)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def pass_arguments(args = {})
|
107
|
+
keys_to_pass = [:queue_name, :content_type, :priority, :delivery_tag]
|
108
|
+
args.select{ |key| keys_to_pass.include?(key) }
|
109
|
+
end
|
110
|
+
|
111
|
+
def publish(args = {})
|
112
|
+
with_publish(args) do |connection, exchange_name, message|
|
113
|
+
begin
|
114
|
+
channel = connection.create_channel
|
115
|
+
channel.confirm_select
|
116
|
+
exchange = channel.direct(exchange_name, durable: true)
|
117
|
+
exchange.publish(message, persistent: true)
|
118
|
+
|
119
|
+
success = channel.wait_for_confirms
|
120
|
+
|
121
|
+
unless success
|
122
|
+
raise 'Published message from Listener actor '\
|
123
|
+
'has not been confirmed by the server'
|
124
|
+
end
|
125
|
+
ensure
|
126
|
+
channel.close if channel
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
|
132
|
+
def with_publish(args = {}, &block)
|
133
|
+
message = args[:message]
|
134
|
+
return if message.nil?
|
135
|
+
|
136
|
+
need_to_close = false
|
137
|
+
connection = args[:connection]
|
138
|
+
if connection.nil?
|
139
|
+
connection = Bunny.new(bunny_connection_properties)
|
140
|
+
connection.start
|
141
|
+
need_to_close = true
|
142
|
+
end
|
143
|
+
|
144
|
+
exchange_name = args[:exchange_name] || EH_X_INBOUND
|
145
|
+
|
146
|
+
yield connection, exchange_name, message
|
147
|
+
ensure
|
148
|
+
connection.close if connection && need_to_close
|
149
|
+
end
|
150
|
+
|
151
|
+
|
152
|
+
def cleanup
|
153
|
+
EventHub.logger.info('Listener is cleanig up...')
|
154
|
+
# close all open connections
|
155
|
+
@connections.values.each do |connection|
|
156
|
+
connection.close if connection
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
end
|
161
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# EventHub module
|
2
|
+
module EventHub
|
3
|
+
# Watchdog class
|
4
|
+
class ActorWatchdog
|
5
|
+
include Celluloid
|
6
|
+
include Helper
|
7
|
+
finalizer :cleanup
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
async.start
|
11
|
+
end
|
12
|
+
|
13
|
+
def start
|
14
|
+
loop do
|
15
|
+
EventHub.logger.info('Running watchdog...')
|
16
|
+
watch
|
17
|
+
sleep Configuration.processor[:watchdog_cycle_in_s]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def cleanup
|
22
|
+
EventHub.logger.info('Watchdog is cleanig up...')
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def watch
|
28
|
+
connection = Bunny.new(bunny_connection_properties)
|
29
|
+
connection.start
|
30
|
+
|
31
|
+
EventHub::Configuration.processor[:listener_queues].each do |queue_name|
|
32
|
+
unless connection.queue_exists?(queue_name)
|
33
|
+
EventHub.logger.warn("Queue [#{queue_name}] is missing")
|
34
|
+
raise "Queue [#{queue_name}] is missing"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
ensure
|
38
|
+
connection.close if connection
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
# EventHub module
|
2
|
+
module EventHub
|
3
|
+
# Configuraiton module
|
4
|
+
module Configuration
|
5
|
+
# it's a singleton (don't allow to instantiate this class)
|
6
|
+
extend self
|
7
|
+
|
8
|
+
attr_reader :name # name of processor
|
9
|
+
attr_reader :environment # environment the processor is running
|
10
|
+
attr_reader :detached # run processor run as a daemon
|
11
|
+
attr_reader :config_file # name of configuration file
|
12
|
+
attr_reader :config_data # data from configuration file
|
13
|
+
|
14
|
+
@name = 'undefined'
|
15
|
+
@environment = 'development'
|
16
|
+
@detached = false
|
17
|
+
@config_file = File.join(Dir.getwd, 'config', "#{@name}.json")
|
18
|
+
@config_data = {}
|
19
|
+
|
20
|
+
# set name of processor
|
21
|
+
def name=(value)
|
22
|
+
@name = value
|
23
|
+
end
|
24
|
+
|
25
|
+
def reset
|
26
|
+
@name = 'undefined'
|
27
|
+
@environment = 'development'
|
28
|
+
@detached = false
|
29
|
+
@config_file = File.join(Dir.getwd, 'config', "#{@name}.json")
|
30
|
+
@config_data = {}
|
31
|
+
end
|
32
|
+
|
33
|
+
# parse options from argument list
|
34
|
+
def parse_options(argv = ARGV)
|
35
|
+
@config_file = File.join(Dir.getwd, 'config', "#{@name}.json")
|
36
|
+
|
37
|
+
OptionParser.new do |opts|
|
38
|
+
note = 'Define environment'
|
39
|
+
opts.on('-e', '--environment ENVIRONMENT', note) do |environment|
|
40
|
+
@environment = environment
|
41
|
+
end
|
42
|
+
|
43
|
+
opts.on('-d', '--detached', 'Run processor detached as a daemon') do
|
44
|
+
@detached = true
|
45
|
+
end
|
46
|
+
|
47
|
+
note = 'Define configuration file'
|
48
|
+
opts.on('-c', '--config CONFIG', note) do |config|
|
49
|
+
@config_file = config
|
50
|
+
end
|
51
|
+
end.parse!(argv)
|
52
|
+
|
53
|
+
true
|
54
|
+
rescue OptionParser::InvalidOption => e
|
55
|
+
EventHub.logger.warn("Argument Parsing: #{e}")
|
56
|
+
false
|
57
|
+
rescue OptionParser::MissingArgument => e
|
58
|
+
EventHub.logger.warn("Argument Parsing: #{e}")
|
59
|
+
false
|
60
|
+
end
|
61
|
+
|
62
|
+
# load configuration from file
|
63
|
+
def load!(args = {})
|
64
|
+
# for better rspec testing
|
65
|
+
@config_file = args[:config_file] if args[:config_file]
|
66
|
+
@environment = args[:environment] if args[:environment]
|
67
|
+
|
68
|
+
new_data = {}
|
69
|
+
begin
|
70
|
+
new_data = JSON.parse(File.read(@config_file), symbolize_names: true)
|
71
|
+
rescue => e
|
72
|
+
EventHub.logger.warn("Exception while loading configuration file: #{e}")
|
73
|
+
EventHub.logger.info('Using default configuration values')
|
74
|
+
end
|
75
|
+
|
76
|
+
deep_merge!(@config_data, default_configuration)
|
77
|
+
new_data = new_data[@environment.to_sym]
|
78
|
+
deep_merge!(@config_data, new_data)
|
79
|
+
end
|
80
|
+
|
81
|
+
# Deep merging of hashes
|
82
|
+
# deep_merge by Stefan Rusterholz, see http://www.ruby-forum.com/topic/142809
|
83
|
+
def deep_merge!(target, data)
|
84
|
+
return if data.nil?
|
85
|
+
merger = proc do |_, v1, v2|
|
86
|
+
v1.is_a?(Hash) && v2.is_a?(Hash) ? v1.merge(v2, &merger) : v2
|
87
|
+
end
|
88
|
+
target.merge! data, &merger
|
89
|
+
end
|
90
|
+
|
91
|
+
def method_missing(name, *_args, &_block)
|
92
|
+
@config_data[name.to_sym] ||
|
93
|
+
fail(NoMethodError, "unknown configuration [#{name}]", caller)
|
94
|
+
end
|
95
|
+
|
96
|
+
def default_configuration
|
97
|
+
{
|
98
|
+
server: {
|
99
|
+
user: 'guest',
|
100
|
+
password: 'guest',
|
101
|
+
host: 'localhost',
|
102
|
+
vhost: 'event_hub',
|
103
|
+
port: 5672,
|
104
|
+
tls: false
|
105
|
+
},
|
106
|
+
processor: {
|
107
|
+
heartbeat_cycle_in_s: 300,
|
108
|
+
watchdog_cycle_in_s: 15,
|
109
|
+
listener_queues: [@name]
|
110
|
+
}
|
111
|
+
}
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module EventHub
|
2
|
+
EH_X_INBOUND = 'event_hub.inbound'
|
3
|
+
|
4
|
+
STATUS_INITIAL = 0 # To be set when dispatcher needs to dispatch to first process step.
|
5
|
+
|
6
|
+
STATUS_SUCCESS = 200 # To be set to indicate successful processed message. Dispatcher will routes message to the next step.
|
7
|
+
|
8
|
+
STATUS_RETRY = 300 # To be set to trigger retry cycle controlled by the dispatcher
|
9
|
+
STATUS_RETRY_PENDING = 301 # Set and used by the dispatcher only.
|
10
|
+
# Set before putting the message into a retry queue.
|
11
|
+
# Once message has been retried it will sent do the same step with status.code = STATUS_SUCCESS
|
12
|
+
|
13
|
+
STATUS_INVALID = 400 # To be set to indicate invalid message (not json, invalid Event Hub Message).
|
14
|
+
# Dispatcher will publish message to the invalid queue.
|
15
|
+
|
16
|
+
STATUS_DEADLETTER = 500 # To be set by dispatcher, processor or channel adapters to indicate
|
17
|
+
# that message needs to be dead-lettered. Rejected messages could miss the
|
18
|
+
# status.code = STATUS_DEADLETTER due to the RabbitMQ deadletter exchange mechanism.
|
19
|
+
|
20
|
+
STATUS_SCHEDULE = 600 # To be set to trigger scheduler based on schedule block, proceses next process step
|
21
|
+
STATUS_SCHEDULE_RETRY = 601 # To be set to trigger scheduler based on schedule block, retry actual process step
|
22
|
+
STATUS_SCHEDULE_PENDING = 602 # Set and used by the dispatcher only. Set before putting the scheduled message to the schedule queue.
|
23
|
+
|
24
|
+
STATUS_CODE_TRANSLATION = {
|
25
|
+
STATUS_INITIAL => 'STATUS_INITIAL',
|
26
|
+
STATUS_SUCCESS => 'STATUS_SUCCESS',
|
27
|
+
STATUS_RETRY => 'STATUS_RETRY',
|
28
|
+
STATUS_RETRY_PENDING => 'STATUS_RETRY_PENDING',
|
29
|
+
STATUS_INVALID => 'STATUS_INVALID',
|
30
|
+
STATUS_DEADLETTER => 'STATUS_DEADLETTER',
|
31
|
+
STATUS_SCHEDULE => 'STATUS_SCHEDULE',
|
32
|
+
STATUS_SCHEDULE_RETRY => 'STATUS_SCHEDULE_RETRY',
|
33
|
+
STATUS_SCHEDULE_PENDING =>'STATUS_SCHEDULE_PENDING',
|
34
|
+
}
|
35
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# HashExtensions module
|
2
|
+
module HashExtensions
|
3
|
+
# ClassMethods module
|
4
|
+
module ClassMethods
|
5
|
+
end
|
6
|
+
|
7
|
+
# InstanceMethods module
|
8
|
+
module InstanceMethods
|
9
|
+
# get value from provided key path
|
10
|
+
# e.g. hash.get(%w(event_hub plate.queue1 retry_s))
|
11
|
+
# "a" => { "b" => { "c" => { "value"}}}
|
12
|
+
def get(arg)
|
13
|
+
path = arg.is_a?(String) ? arg.split('.') : arg
|
14
|
+
path.inject(self, :[])
|
15
|
+
end
|
16
|
+
|
17
|
+
# set value from provided key path, e.h. hash.set('a.b.c','new value')
|
18
|
+
# if overwrite is false, value will be set if it was nil previously
|
19
|
+
def set(arg, value, overwrite = true)
|
20
|
+
*key_path, last = arg.is_a?(String) ? arg.split('.') : arg
|
21
|
+
if overwrite
|
22
|
+
key_path.inject(self) { |h, key| h.key?(key) ? h[key] : h[key] = {} } [last] = value
|
23
|
+
else
|
24
|
+
key_path.inject(self) { |h, key| h.key?(key) ? h[key] : h[key] = {} } [last] ||= value
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# get all keys with path,
|
29
|
+
# { 'a' => 'v1', 'b' => { 'c' => 'v2'}}.all_keys_with_path => ['a','b.c']
|
30
|
+
def all_keys_with_path(parent = nil)
|
31
|
+
a = []
|
32
|
+
each do |k, v|
|
33
|
+
if v.is_a?(Hash)
|
34
|
+
a << v.all_keys_with_path([parent, k].compact.join('.'))
|
35
|
+
else
|
36
|
+
a << [parent, k].compact.join('.').to_s
|
37
|
+
end
|
38
|
+
end
|
39
|
+
a.flatten
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.included(receiver)
|
44
|
+
receiver.extend ClassMethods
|
45
|
+
receiver.send :include, InstanceMethods
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
HashExtensions.included(Hash)
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
|
3
|
+
# EventHub module
|
4
|
+
module EventHub
|
5
|
+
# Helper module
|
6
|
+
module Helper
|
7
|
+
# Extracts processor name from given class instance.
|
8
|
+
# Removes 'EventHub' module from name.
|
9
|
+
|
10
|
+
# Examples:
|
11
|
+
# EventHub::Namespace::Demo => namespace.demo
|
12
|
+
# EventHub::NameSpace::Demo => name_space.demo
|
13
|
+
# EventHub::NameSpace::DemoProcessor => name_space.demo_processor
|
14
|
+
# NameSpace::Demo => name_space.demo
|
15
|
+
def get_name_from_class(instance)
|
16
|
+
instance.class.to_s.split('::').map do |element|
|
17
|
+
next if element == 'EventHub'
|
18
|
+
element.split(/(?=[A-Z])/).join('_').downcase
|
19
|
+
end.compact.join('.')
|
20
|
+
end
|
21
|
+
|
22
|
+
def bunny_connection_properties
|
23
|
+
server = EventHub::Configuration.server
|
24
|
+
|
25
|
+
if Configuration.server[:tls]
|
26
|
+
{
|
27
|
+
user: server[:user],
|
28
|
+
password: server[:password],
|
29
|
+
host: server[:host],
|
30
|
+
vhost: server[:vhost],
|
31
|
+
port: server[:port],
|
32
|
+
tls: server[:tls],
|
33
|
+
logger: Logger.new('/dev/null'), # logs from Bunny not required
|
34
|
+
network_recovery_interval: 15
|
35
|
+
}
|
36
|
+
else
|
37
|
+
{
|
38
|
+
user: server[:user],
|
39
|
+
password: server[:password],
|
40
|
+
host: server[:host],
|
41
|
+
vhost: server[:vhost],
|
42
|
+
port: server[:port],
|
43
|
+
logger: Logger.new('/dev/null'), # logs from Bunny not required
|
44
|
+
network_recovery_interval: 15
|
45
|
+
}
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Formats stamp into UTC format
|
50
|
+
def now_stamp(now=nil)
|
51
|
+
now ||= Time.now
|
52
|
+
now.utc.strftime("%Y-%m-%dT%H:%M:%S.%6NZ")
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# EventHub module
|
2
|
+
module EventHub
|
3
|
+
def self.logger
|
4
|
+
unless @logger
|
5
|
+
@logger = ::EventHub::Components::MultiLogger.new
|
6
|
+
@logger.add_device(Logger.new(STDOUT))
|
7
|
+
@logger.add_device(
|
8
|
+
EventHub::Components::Logger.logstash(Configuration.name,
|
9
|
+
Configuration.environment)
|
10
|
+
)
|
11
|
+
end
|
12
|
+
@logger
|
13
|
+
end
|
14
|
+
end
|