Module: Flu

Defined in:
lib/flu-rails.rb,
lib/flu-rails/util.rb,
lib/flu-rails/event.rb,
lib/flu-rails/errors.rb,
lib/flu-rails/railtie.rb,
lib/flu-rails/version.rb,
lib/flu-rails/core_ext.rb,
lib/flu-rails/configuration.rb,
lib/flu-rails/event_factory.rb,
lib/flu-rails/event_publisher.rb,
lib/flu-rails/queue_repository.rb,
lib/flu-rails/transaction_buffer.rb,
lib/flu-rails/pending_publications.rb,
lib/flu-rails/active_record_extender.rb,
lib/flu-rails/action_controller_extender.rb,
lib/flu-rails/dummy/in_memory_event_publisher.rb

Defined Under Namespace

Modules: Dummy, Util Classes: ActionControllerExtender, ActiveRecordExtender, Configuration, ConnectionLostError, CoreExt, Error, Event, EventFactory, EventPublisher, NotConnectedError, PendingPublications, QueueRepository, Railtie, TransactionBuffer

Constant Summary collapse

VERSION =
"8.0.8"

Class Method Summary collapse

Class Method Details

.configObject



25
26
27
# File 'lib/flu-rails.rb', line 25

def self.config
  @configuration
end

.configure {|@configuration ||= Flu::Configuration.new| ... } ⇒ Object

Yields:



21
22
23
# File 'lib/flu-rails.rb', line 21

def self.configure
  yield @configuration ||= Flu::Configuration.new
end

.create_event_publisher(configuration) ⇒ Object

The railtie re-runs 'init' on every code reload. Building a new publisher on each of them left every reference the application had already taken on the previous one -- a tracked model publishes through the very publisher it was tracked with -- on a connection 'init' had closed and that nothing ever reopens. The publisher outlives the reloads, and only a change of kind replaces it: its connection, its channels and Bunny's heartbeat thread are then closed with it.



94
95
96
97
98
99
100
101
102
103
# File 'lib/flu-rails.rb', line 94

def self.create_event_publisher(configuration)
  publisher_class = event_publisher_class
  return @event_publisher if @event_publisher.instance_of?(publisher_class)

  @event_publisher&.disconnect
  if publisher_class == Flu::Dummy::InMemoryEventPublisher
    logger.info("Loading Flu with a dummy event publisher (this will not connect any exchange)")
  end
  publisher_class.new(configuration)
end

.default_application_nameObject



80
81
82
83
84
85
86
87
# File 'lib/flu-rails.rb', line 80

def self.default_application_name
  if defined?(Rails) && Rails.respond_to?(:application)
    application = Rails.application
    application.nil? ? nil : application.class.module_parent_name
  else
    nil
  end
end

.event_factoryObject



33
34
35
# File 'lib/flu-rails.rb', line 33

def self.event_factory
  @event_factory
end

.event_publisherObject



37
38
39
# File 'lib/flu-rails.rb', line 37

def self.event_publisher
  @event_publisher
end

.event_publisher_classObject



105
106
107
# File 'lib/flu-rails.rb', line 105

def self.event_publisher_class
  is_testing_environment? ? Flu::Dummy::InMemoryEventPublisher : Flu::EventPublisher
end

.extend_models_and_controllersObject



116
117
118
119
# File 'lib/flu-rails.rb', line 116

def self.extend_models_and_controllers
  Flu::CoreExt.extend_model_classes(@event_factory, @event_publisher)
  Flu::CoreExt.extend_controller_classes(@event_factory, @event_publisher, @logger)
end

.initObject



71
72
73
74
75
76
77
78
# File 'lib/flu-rails.rb', line 71

def self.init
  @configuration.application_name ||= default_application_name
  raise "configuration.application_name must not be nil" if @configuration.application_name.nil?
  @logger          = @configuration.logger
  @event_factory   = Flu::EventFactory.new(@configuration)
  @event_publisher = create_event_publisher(@configuration)
  extend_models_and_controllers
end

.is_testing_environment?Boolean

Returns:

  • (Boolean)


109
110
111
112
113
114
# File 'lib/flu-rails.rb', line 109

def self.is_testing_environment?
  # 'defined?(Rails)' alone is not enough: gems such as 'rails-html-sanitizer' define an empty
  # 'Rails' namespace, so the constant can exist without railties ever defining 'Rails.env'.
  return false unless defined?(Rails) && Rails.respond_to?(:env)
  config.development_environments.include?(Rails.env)
end

.load_configurationObject



130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/flu-rails.rb', line 130

def self.load_configuration
  configure do |config|
    config.development_environments       = []
    config.rejected_user_agents           = []
    config.logger                         = ::Logger.new(STDOUT)
    config.rabbitmq_host                  = "localhost"
    config.rabbitmq_vhost                 = "/"
    config.rabbitmq_port                  = 5672
    config.rabbitmq_management_scheme     = "http"
    config.rabbitmq_management_port       = 15672
    config.rabbitmq_user                  = ""
    config.rabbitmq_password              = ""
    config.rabbitmq_exchange_name         = "events"
    config.rabbitmq_exchange_durable      = true
    config.auto_connect_to_exchange       = true
    config.default_ignored_model_changes  = [:password, :password_confirmation, :created_at, :updated_at]
    config.default_ignored_request_params = [:password, :password_confirmation, :controller, :action]
    config.application_name               = nil
    config.bunny_options                  = {}
    config.on_publication_failure         = nil
    config.max_pending_events             = 1000
    config.max_connect_wait               = 30
  end
end

.loggerObject



29
30
31
# File 'lib/flu-rails.rb', line 29

def self.logger
  @logger
end

.publication_failed(event, error, event_publisher) ⇒ Object

Keeps an event whose publication failed for another attempt, a broker being reachable again within seconds. One that could not even be built is reported at once instead.



43
44
45
46
47
48
49
# File 'lib/flu-rails.rb', line 43

def self.publication_failed(event, error, event_publisher)
  if event.nil?
    report_publication_failure(event, error) 
  else
    PendingPublications.current.push(event, event_publisher, error)
  end
end

.report_publication_failure(event, error) ⇒ Object

Parameters:

  • event (Flu::Event, nil)

    nil when the event could not even be built.



60
61
62
63
64
65
66
67
68
69
# File 'lib/flu-rails.rb', line 60

def self.report_publication_failure(event, error)
  handler = config.on_publication_failure
  if handler.nil?
    subject = event.nil? ? "an event it could not build" : "the event '#{event.id}' ('#{event.name}')"
    config.logger&.error("Flu could not publish #{subject}: #{error.class}: #{error.message}. " \
                         "The event is lost unless 'on_publication_failure' is configured to keep it.")
  else
    handler.call(event, error) 
  end
end

.retry_pending_publicationsObject

Publishes again what the last attempt could not. Never raises: its callers are a transaction that has committed and the end of a request, neither of which is a place to fail.



53
54
55
56
57
# File 'lib/flu-rails.rb', line 53

def self.retry_pending_publications
  PendingPublications.current.drain
rescue StandardError => error
  config.logger&.error("Flu could not retry the publications it had kept: #{error.class}: #{error.message}")
end

.startObject

A broker that is not there yet must not keep the application from booting: publishing reopens the connection itself once the broker answers again.



123
124
125
126
127
128
# File 'lib/flu-rails.rb', line 123

def self.start
  return unless config.auto_connect_to_exchange
  @event_publisher.connect
rescue Flu::ConnectionLostError => error
  config.logger&.error("Flu could not connect to RabbitMQ: #{error.message}")
end