flu-rails 1.2.0
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 +7 -0
- data/CHANGELOG.md +128 -0
- data/MIT-LICENSE +20 -0
- data/README.md +272 -0
- data/lib/flu-rails/action_controller_extender.rb +89 -0
- data/lib/flu-rails/active_record_extender.rb +125 -0
- data/lib/flu-rails/configuration.rb +20 -0
- data/lib/flu-rails/core_ext.rb +30 -0
- data/lib/flu-rails/dummy/in_memory_event_publisher.rb +40 -0
- data/lib/flu-rails/event.rb +106 -0
- data/lib/flu-rails/event_factory.rb +117 -0
- data/lib/flu-rails/event_publisher.rb +51 -0
- data/lib/flu-rails/queue_repository.rb +40 -0
- data/lib/flu-rails/railtie.rb +14 -0
- data/lib/flu-rails/util.rb +71 -0
- data/lib/flu-rails/version.rb +3 -0
- data/lib/flu-rails.rb +88 -0
- metadata +192 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
require_relative "active_record_extender"
|
|
2
|
+
require_relative "action_controller_extender"
|
|
3
|
+
|
|
4
|
+
module Flu
|
|
5
|
+
class CoreExt
|
|
6
|
+
def self.extend_model_classes(event_factory, event_publisher)
|
|
7
|
+
ActiveRecordExtender.extend_models(event_factory, event_publisher)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def self.extend_controller_classes(event_factory, event_publisher, logger)
|
|
11
|
+
ActionControllerExtender.extend_controllers(event_factory, event_publisher, logger)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.flu_tracker_request_id=(value)
|
|
15
|
+
Thread.current[:flu_tracker_request_id] = value
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def self.flu_tracker_request_id
|
|
19
|
+
Thread.current[:flu_tracker_request_id]
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def self.flu_tracker_request_entity_metadata=(value)
|
|
23
|
+
Thread.current[:flu_tracker_request_entity_metadata] = value
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def self.flu_tracker_request_entity_metadata
|
|
27
|
+
Thread.current[:flu_tracker_request_entity_metadata]
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
module Flu
|
|
2
|
+
module Dummy
|
|
3
|
+
class InMemoryEventPublisher < Flu::EventPublisher
|
|
4
|
+
|
|
5
|
+
attr_reader :published_events_by_routing_key, :ordered_published_event_routing_keys
|
|
6
|
+
|
|
7
|
+
def initialize(configuration)
|
|
8
|
+
@logger = configuration.logger
|
|
9
|
+
@configuration = configuration
|
|
10
|
+
@published_events_by_routing_key = {}
|
|
11
|
+
@ordered_published_event_routing_keys = []
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def publish(event, persistent=true)
|
|
15
|
+
routing_key = event.to_routing_key
|
|
16
|
+
published_events_by_routing_key[routing_key] ||= []
|
|
17
|
+
published_events_by_routing_key[routing_key].push(event)
|
|
18
|
+
@ordered_published_event_routing_keys.push(routing_key)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def connect
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def events_count
|
|
25
|
+
@published_events_by_routing_key.map do | key, value |
|
|
26
|
+
value.size
|
|
27
|
+
end.sum
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def fetch_events(routing_key)
|
|
31
|
+
@published_events_by_routing_key[routing_key]
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def clear
|
|
35
|
+
@published_events_by_routing_key = {}
|
|
36
|
+
@ordered_published_event_routing_keys = []
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
require "json"
|
|
2
|
+
|
|
3
|
+
module Flu
|
|
4
|
+
class Event
|
|
5
|
+
|
|
6
|
+
attr_reader :data
|
|
7
|
+
|
|
8
|
+
def initialize(uuid, emitter, kind, name, data)
|
|
9
|
+
raise ArgumentError, "uuid must not be nil" if uuid.nil?
|
|
10
|
+
raise ArgumentError, "emitter must not be nil nor empty" if emitter.nil? || emitter.length == 0
|
|
11
|
+
raise ArgumentError, "kind must not be nil nor empty" if kind.nil? || kind.length == 0
|
|
12
|
+
raise ArgumentError, "name must not be nil nor empty" if name.nil? || name.length == 0
|
|
13
|
+
|
|
14
|
+
@meta = {
|
|
15
|
+
id: uuid,
|
|
16
|
+
name: name,
|
|
17
|
+
emitter: emitter,
|
|
18
|
+
timestamp: Time.now.utc,
|
|
19
|
+
kind: kind,
|
|
20
|
+
status: :new
|
|
21
|
+
}
|
|
22
|
+
@data = data || {}
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def to_routing_key
|
|
26
|
+
"#{@meta[:status]}.#{@meta[:emitter]}.#{@meta[:kind]}.#{@meta[:name]}"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def to_json(options=nil)
|
|
30
|
+
JSON.dump({
|
|
31
|
+
meta: @meta,
|
|
32
|
+
data: map_complex_object(@data)
|
|
33
|
+
})
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def id
|
|
37
|
+
@meta[:id]
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def timestamp=(new_timestamp)
|
|
41
|
+
@meta[:timestamp] = new_timestamp
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def mark_as_replayed
|
|
45
|
+
@meta[:status] = :replayed
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def emitter
|
|
49
|
+
@meta[:emitter]
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def timestamp
|
|
53
|
+
@meta[:timestamp]
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def kind
|
|
57
|
+
@meta[:kind]
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def name
|
|
61
|
+
@meta[:name]
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def status
|
|
65
|
+
@meta[:status]
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
private
|
|
69
|
+
|
|
70
|
+
def map_complex_object(object)
|
|
71
|
+
if object.is_a?(Array)
|
|
72
|
+
map_array(object)
|
|
73
|
+
elsif object.is_a?(Hash)
|
|
74
|
+
map_hash(object)
|
|
75
|
+
elsif object.is_a?(ActionDispatch::Http::UploadedFile)
|
|
76
|
+
map_file(object)
|
|
77
|
+
else
|
|
78
|
+
object
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def map_array(object)
|
|
83
|
+
array = []
|
|
84
|
+
object.each do |value|
|
|
85
|
+
array.push(map_complex_object(value))
|
|
86
|
+
end
|
|
87
|
+
array
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def map_hash(object)
|
|
91
|
+
hash = {}
|
|
92
|
+
object.each do |key, value|
|
|
93
|
+
hash[key] = map_complex_object(value)
|
|
94
|
+
end
|
|
95
|
+
hash
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def map_file(object)
|
|
99
|
+
{
|
|
100
|
+
"file_name": object.original_filename,
|
|
101
|
+
"content_type": object.content_type
|
|
102
|
+
}
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
require "securerandom"
|
|
2
|
+
require_relative "event"
|
|
3
|
+
|
|
4
|
+
module Flu
|
|
5
|
+
class EventFactory
|
|
6
|
+
def initialize(configuration)
|
|
7
|
+
@logger = configuration.logger
|
|
8
|
+
@configuration = configuration
|
|
9
|
+
@emitter = configuration.application_name
|
|
10
|
+
@default_ignored_model_changes = configuration.default_ignored_model_changes.map(&:to_s)
|
|
11
|
+
@default_ignored_request_params = configuration.default_ignored_request_params
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def build_request_event(data)
|
|
15
|
+
raise ArgumentError, "data must not be nil" if data.nil?
|
|
16
|
+
raise ArgumentError, "data must have an action_name" if !data.has_key?(:action_name) || data[:action_name].empty?
|
|
17
|
+
raise ArgumentError, "data must have an controller_name" if !data.has_key?(:controller_name) || data[:controller_name].empty?
|
|
18
|
+
name = "request to #{data[:action_name]} #{data[:controller_name]}"
|
|
19
|
+
event = build_event(name, :request, data)
|
|
20
|
+
@logger.debug("Track action: #{JSON.pretty_generate(event)}")
|
|
21
|
+
event
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def build_entity_change_event(data)
|
|
25
|
+
raise ArgumentError, "data must not be nil" if data.nil?
|
|
26
|
+
raise ArgumentError, "data must have changes" if !data.has_key?(:changes) || data[:changes].empty?
|
|
27
|
+
raise ArgumentError, "data must have an action_name" if !data.has_key?(:action_name) || data[:action_name].empty?
|
|
28
|
+
raise ArgumentError, "data must have an entity_name" if !data.has_key?(:entity_name) || data[:entity_name].empty?
|
|
29
|
+
name = "#{data[:action_name]} #{data[:entity_name]}"
|
|
30
|
+
event = build_event(name, :entity_change, data)
|
|
31
|
+
@logger.debug("Track change: " + JSON.pretty_generate(event))
|
|
32
|
+
event
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def build_manual_event(name, data)
|
|
36
|
+
raise ArgumentError, "data must not be nil" if data.nil?
|
|
37
|
+
raise ArgumentError, "data must be a hash" if !data.is_a?(Hash)
|
|
38
|
+
raise ArgumentError, "name must not be nil or empty" if name.nil? || name.empty?
|
|
39
|
+
event = build_event(name.to_s, :manual, data)
|
|
40
|
+
@logger.debug("Track manual: " + JSON.pretty_generate(event))
|
|
41
|
+
event
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def build_event(name, kind, data)
|
|
45
|
+
original_emitter = @emitter
|
|
46
|
+
overriden_emitter = data[:overriden_emitter]&.strip&.delete(".")
|
|
47
|
+
final_emitter = overriden_emitter.blank? ? original_emitter : overriden_emitter
|
|
48
|
+
Event.new(SecureRandom.uuid, final_emitter, kind, name, deep_camelize(data))
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def create_data_from_entity_changes(action_name, entity, request_id, request_entity_metadata, changes, user_metadata_lambda, association_columns, ignored_model_changes, flu_overriden_emitter_lambda)
|
|
52
|
+
{
|
|
53
|
+
entity_id: entity.id,
|
|
54
|
+
entity_name: entity.class.name.underscore,
|
|
55
|
+
overriden_emitter: flu_overriden_emitter_lambda ? entity.instance_exec(&flu_overriden_emitter_lambda) : nil,
|
|
56
|
+
request_id: request_id,
|
|
57
|
+
request_metadata: request_entity_metadata.nil? ? {} : request_entity_metadata,
|
|
58
|
+
action_name: action_name,
|
|
59
|
+
changes: changes.except(*ignored_model_changes).except(*@default_ignored_model_changes),
|
|
60
|
+
user_metadata: user_metadata_lambda ? entity.instance_exec(&user_metadata_lambda) : {},
|
|
61
|
+
associations: extract_associations_from(entity, association_columns)
|
|
62
|
+
}
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def create_data_from_request(request_id, params, request, response, request_start_time, ignored_request_params)
|
|
66
|
+
{
|
|
67
|
+
request_id: request_id,
|
|
68
|
+
controller_name: params[:controller],
|
|
69
|
+
action_name: params[:action],
|
|
70
|
+
path: request.original_fullpath,
|
|
71
|
+
response_code: response.status,
|
|
72
|
+
user_agent: request.user_agent,
|
|
73
|
+
duration: Time.zone.now - request_start_time,
|
|
74
|
+
params: params.except(*ignored_request_params).except(*@default_ignored_request_params).to_h
|
|
75
|
+
}
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
private
|
|
79
|
+
|
|
80
|
+
def deep_camelize(value)
|
|
81
|
+
case value
|
|
82
|
+
when Array
|
|
83
|
+
value.map { |v| deep_camelize v }
|
|
84
|
+
when Hash
|
|
85
|
+
value.reduce({}) do |camelized_hash, (k,v)|
|
|
86
|
+
camelized_hash[camelize(sanitize(k.to_s), false)] = deep_camelize v
|
|
87
|
+
camelized_hash
|
|
88
|
+
end
|
|
89
|
+
else
|
|
90
|
+
sanitize(value)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def camelize(lower_case_and_underscored_word, first_letter_in_uppercase = true)
|
|
95
|
+
if first_letter_in_uppercase
|
|
96
|
+
lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::" + $1.upcase }.gsub(/(^|_)(.)/) { $2.upcase }
|
|
97
|
+
else
|
|
98
|
+
(lower_case_and_underscored_word[0] || "") + camelize(lower_case_and_underscored_word)[1..-1]
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def extract_associations_from(entity, association_columns)
|
|
103
|
+
association_columns.reduce({}) do |associations, column_name|
|
|
104
|
+
associations[column_name] = entity[column_name]
|
|
105
|
+
associations
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def sanitize(value)
|
|
110
|
+
if value.respond_to?(:encode)
|
|
111
|
+
value.encode("UTF-8", invalid: :replace, undef: :replace).delete("\u0000")
|
|
112
|
+
else
|
|
113
|
+
value
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
require "bunny"
|
|
2
|
+
require_relative "event"
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
module Flu
|
|
6
|
+
class EventPublisher
|
|
7
|
+
def initialize(configuration)
|
|
8
|
+
@logger = configuration.logger
|
|
9
|
+
@configuration = configuration
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def publish(event, persistent=true)
|
|
13
|
+
routing_key = event.to_routing_key
|
|
14
|
+
@logger.debug("Publishing event with id '#{event.id}' with routing key: #{routing_key}")
|
|
15
|
+
@exchange.publish(event.to_json, routing_key: routing_key, persistent: persistent)
|
|
16
|
+
@logger.debug("Event published.")
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def connect
|
|
20
|
+
connected = false
|
|
21
|
+
while !connected
|
|
22
|
+
begin
|
|
23
|
+
connect_to_exchange
|
|
24
|
+
connected = true
|
|
25
|
+
rescue Bunny::TCPConnectionFailedForAllHosts
|
|
26
|
+
@logger.warn("RabbitMQ connection failed, try again in 1 second.")
|
|
27
|
+
sleep 1
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def connect_to_exchange
|
|
35
|
+
options = {
|
|
36
|
+
host: @configuration.rabbitmq_host,
|
|
37
|
+
port: @configuration.rabbitmq_port&.to_i,
|
|
38
|
+
user: @configuration.rabbitmq_user,
|
|
39
|
+
password: @configuration.rabbitmq_password,
|
|
40
|
+
automatically_recover: true
|
|
41
|
+
}.merge(@configuration.bunny_options || {})
|
|
42
|
+
|
|
43
|
+
@connection = Bunny.new(options)
|
|
44
|
+
@connection.start
|
|
45
|
+
@channel = @connection.create_channel
|
|
46
|
+
@exchange = @channel.send(:topic,
|
|
47
|
+
@configuration.rabbitmq_exchange_name,
|
|
48
|
+
durable: @configuration.rabbitmq_exchange_durable)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
require "rabbitmq/http/client"
|
|
2
|
+
|
|
3
|
+
module Flu
|
|
4
|
+
class QueueRepository
|
|
5
|
+
def initialize(configuration)
|
|
6
|
+
@logger = configuration.logger
|
|
7
|
+
@configuration = configuration
|
|
8
|
+
@management_client = create_management_client(configuration)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def find_all
|
|
12
|
+
@management_client.list_queues
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def find_queue(name)
|
|
16
|
+
@management_client.queue_info("/", name)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def find_bindings_for_queue(name)
|
|
20
|
+
@management_client.list_queue_bindings("/", name)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def purge_queue(name)
|
|
24
|
+
@management_client.purge_queue("/", name)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def delete_queue(name)
|
|
28
|
+
@management_client.delete_queue("/", name)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
def create_management_client(configuration)
|
|
34
|
+
rabbitmq_url = "#{configuration.rabbitmq_management_scheme || "http"}://#{configuration.rabbitmq_host}:#{configuration.rabbitmq_management_port}/"
|
|
35
|
+
RabbitMQ::HTTP::Client.new(rabbitmq_url,
|
|
36
|
+
username: configuration.rabbitmq_user,
|
|
37
|
+
password: configuration.rabbitmq_password)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
module Flu
|
|
2
|
+
module Util
|
|
3
|
+
class ExportService
|
|
4
|
+
def export_existing_entities_to_events(event_publisher, event_factory, entity_types=nil)
|
|
5
|
+
entity_types ||= find_all_tracked_entity_types(entity_types)
|
|
6
|
+
check_that_all_entity_types_have_created_at(entity_types)
|
|
7
|
+
|
|
8
|
+
Flu.logger.level = Logger::WARN
|
|
9
|
+
total_number_of_entity_types = entity_types.size
|
|
10
|
+
current_entity_type_index = 0
|
|
11
|
+
entity_types.each do | entity_type |
|
|
12
|
+
entities = entity_type.all
|
|
13
|
+
total_number_of_entities = entities.size
|
|
14
|
+
current_entity_index = 0
|
|
15
|
+
current_entity_type_index += 1
|
|
16
|
+
association_columns = entity_type.flu_association_columns
|
|
17
|
+
user_metadata_lambda = entity_type.flu_user_metadata_lambdas[:create]
|
|
18
|
+
ignored_model_changes = entity_type.flu_ignored_model_changes
|
|
19
|
+
|
|
20
|
+
entities.each do |entity|
|
|
21
|
+
print "\r" unless current_entity_index == 0
|
|
22
|
+
current_entity_index += 1
|
|
23
|
+
print "#{entity_type} (#{current_entity_type_index}/#{total_number_of_entity_types}) : #{current_entity_index}/#{total_number_of_entities}"
|
|
24
|
+
data = extract_data_from(entity, event_factory, user_metadata_lambda, association_columns, ignored_model_changes)
|
|
25
|
+
event = event_factory.build_entity_change_event(data)
|
|
26
|
+
event.timestamp = entity.created_at unless entity.created_at.nil?
|
|
27
|
+
event_publisher.publish(event)
|
|
28
|
+
print "\n" if current_entity_index == total_number_of_entities
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def check_that_all_entity_types_have_created_at(entity_types)
|
|
36
|
+
entity_types_without_timestamp = entity_types.select do | entity_type |
|
|
37
|
+
!entity_type.attribute_names.include?("created_at")
|
|
38
|
+
end
|
|
39
|
+
if (entity_types_without_timestamp.size > 0)
|
|
40
|
+
raise "#{entity_types_without_timestamp.size} entities do not have 'created_at': #{entity_types_without_timestamp.map {|type| type.name}}"
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def find_all_entity_types
|
|
45
|
+
eager_loaded = Rails.application.eager_load!
|
|
46
|
+
Zeitwerk::Loader.eager_load_all if !eager_loaded && defined?(Zeitwerk)
|
|
47
|
+
ActiveRecord::Base.descendants
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def find_all_tracked_entity_types(entity_types)
|
|
51
|
+
(entity_types || find_all_entity_types).select do | entity_type |
|
|
52
|
+
ActiveRecord::Base.connection.table_exists?(entity_type.table_name) && entity_type.flu_is_tracked
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def extract_data_from(entity, event_factory, user_metadata_lambda, association_columns, ignored_model_changes)
|
|
57
|
+
changes = create_changes_from_existing(entity)
|
|
58
|
+
event_factory.create_data_from_entity_changes(:create, entity, nil, nil, changes, user_metadata_lambda, association_columns, ignored_model_changes, nil)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def create_changes_from_existing(entity)
|
|
62
|
+
changes = entity.attribute_names.inject({}) do | result, attribute_name |
|
|
63
|
+
attribute_value = entity.attributes[attribute_name]
|
|
64
|
+
result[attribute_name.to_s] = [nil, attribute_value] unless attribute_value.nil?
|
|
65
|
+
result
|
|
66
|
+
end
|
|
67
|
+
changes.except(:created_at, :updated_at)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
data/lib/flu-rails.rb
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
require "logger"
|
|
2
|
+
require "json"
|
|
3
|
+
require_relative "flu-rails/version"
|
|
4
|
+
require_relative "flu-rails/event"
|
|
5
|
+
require_relative "flu-rails/event_factory"
|
|
6
|
+
require_relative "flu-rails/queue_repository"
|
|
7
|
+
require_relative "flu-rails/configuration"
|
|
8
|
+
require_relative "flu-rails/core_ext"
|
|
9
|
+
require_relative "flu-rails/event_publisher"
|
|
10
|
+
require_relative "flu-rails/util"
|
|
11
|
+
require_relative "flu-rails/dummy/in_memory_event_publisher"
|
|
12
|
+
require_relative "flu-rails/railtie" if defined?(Rails)
|
|
13
|
+
|
|
14
|
+
module Flu
|
|
15
|
+
def self.configure
|
|
16
|
+
yield @configuration ||= Flu::Configuration.new
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.config
|
|
20
|
+
@configuration
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def self.logger
|
|
24
|
+
@logger
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def self.event_factory
|
|
28
|
+
@event_factory
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def self.event_publisher
|
|
32
|
+
@event_publisher
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def self.init
|
|
36
|
+
raise "configuration.application_name must not be nil" if @configuration.application_name.nil?
|
|
37
|
+
@logger = @configuration.logger
|
|
38
|
+
@event_factory = Flu::EventFactory.new(@configuration)
|
|
39
|
+
@event_publisher = create_event_publisher(@configuration)
|
|
40
|
+
extend_models_and_controllers
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def self.create_event_publisher(configuration)
|
|
44
|
+
if is_testing_environment?
|
|
45
|
+
logger.info("Loading Flu with a dummy event publisher (this will not connect any exchange)")
|
|
46
|
+
require_relative "flu-rails/dummy/in_memory_event_publisher"
|
|
47
|
+
Flu::Dummy::InMemoryEventPublisher.new(@configuration)
|
|
48
|
+
else
|
|
49
|
+
Flu::EventPublisher.new(@configuration)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def self.is_testing_environment?
|
|
54
|
+
defined?(Rails) && config.development_environments.include?(Rails.env)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def self.extend_models_and_controllers
|
|
58
|
+
Flu::CoreExt.extend_model_classes(@event_factory, @event_publisher)
|
|
59
|
+
Flu::CoreExt.extend_controller_classes(@event_factory, @event_publisher, @logger)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def self.start
|
|
63
|
+
@event_publisher.connect if config.auto_connect_to_exchange
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def self.load_configuration
|
|
67
|
+
configure do |config|
|
|
68
|
+
config.development_environments = []
|
|
69
|
+
config.rejected_user_agents = []
|
|
70
|
+
config.logger = ::Logger.new(STDOUT)
|
|
71
|
+
config.rabbitmq_host = "localhost"
|
|
72
|
+
config.rabbitmq_port = 5672
|
|
73
|
+
config.rabbitmq_management_scheme = "http"
|
|
74
|
+
config.rabbitmq_management_port = 15672
|
|
75
|
+
config.rabbitmq_user = ""
|
|
76
|
+
config.rabbitmq_password = ""
|
|
77
|
+
config.rabbitmq_exchange_name = "events"
|
|
78
|
+
config.rabbitmq_exchange_durable = true
|
|
79
|
+
config.auto_connect_to_exchange = true
|
|
80
|
+
config.default_ignored_model_changes = [:password, :password_confirmation, :created_at, :updated_at]
|
|
81
|
+
config.default_ignored_request_params = [:password, :password_confirmation, :controller, :action]
|
|
82
|
+
config.application_name = defined?(Rails) ? Rails.application.class.module_parent_name : nil
|
|
83
|
+
config.bunny_options = {}
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
load_configuration
|
|
88
|
+
end
|