flu-rails 1.2.0 → 8.0.1
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/CHANGELOG.md +60 -0
- data/MIT-LICENSE +18 -17
- data/README.md +54 -14
- data/flu-rails.gemspec +39 -0
- data/lib/flu-rails/action_controller_extender.rb +34 -27
- data/lib/flu-rails/active_record_extender.rb +4 -6
- data/lib/flu-rails/event_factory.rb +12 -2
- data/lib/flu-rails/util.rb +13 -5
- data/lib/flu-rails/version.rb +1 -1
- data/lib/flu-rails.rb +16 -2
- metadata +51 -23
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 721522defefd5f7c30f0cd24dd1773b6191cf93bc8162d791e349883dd91698f
|
|
4
|
+
data.tar.gz: 3d5e0c52ddbffbdf5c021647a76460acde5726ca1201bda1022d21fdb3095a05
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9965aef8635a94a1f2e8843253e3c8817b2b6b0f61f13b1f0137a1d555eb638c6e12d4f620c004e47f3365cec9fc9da57a2076e629f53ea11df045c21a0c0a27
|
|
7
|
+
data.tar.gz: 5818a4c90e2b819a12483f9683a75119c33e6573b57e0952916a3cbd258dd6d0e536be85105efbb1b1415fafa105c5de9de478ae3976a06f12ab2f28a86203a8
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,66 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
### [Unreleased]
|
|
9
|
+
|
|
10
|
+
**Fixed**
|
|
11
|
+
|
|
12
|
+
* Honour `configuration.logger` when rejecting a request on its user agent.
|
|
13
|
+
* Stop defining those helpers on `ActionController::Base` and `ActionController::API` themselves.
|
|
14
|
+
* Apply the model's `emitter:` override when exporting existing entities. `Util::ExportService`
|
|
15
|
+
passed `nil` instead of the model's lambda, so a replayed event carried the global
|
|
16
|
+
`application_name` while the live event for the same entity carried the overriden one — putting
|
|
17
|
+
the two on different routing keys. Unnoticed since `1.0.4`, where the option was introduced.
|
|
18
|
+
* Do not assume that a defined `Rails` constant means railties is loaded.
|
|
19
|
+
|
|
20
|
+
**Changed**
|
|
21
|
+
|
|
22
|
+
* Stop publishing `overriden_emitter` as part of the event payload.
|
|
23
|
+
|
|
24
|
+
**Tests**
|
|
25
|
+
|
|
26
|
+
* Enable the three controller examples that were disabled with `xit`, and run them through the real
|
|
27
|
+
`ActionController` dispatch, which is what triggers the callbacks registered by `track_requests`.
|
|
28
|
+
This is the code path that hid the `ActionController::UnfilteredParameters` bug fixed in `8.0.1`.
|
|
29
|
+
* Cover `Util::ExportService`, which had no test at all — and which is how the `emitter:` bug above
|
|
30
|
+
surfaced.
|
|
31
|
+
* Make the spec setup idempotent
|
|
32
|
+
* Integration tests with RabbitMQ
|
|
33
|
+
|
|
34
|
+
### [8.0.1] - 2026-07-31
|
|
35
|
+
|
|
36
|
+
**Fixed**
|
|
37
|
+
|
|
38
|
+
* Fix tracking a controller request raising `ActionController::UnfilteredParameters`. Controller params
|
|
39
|
+
reach `EventFactory` as unpermitted `ActionController::Parameters`, on which `to_h` raises
|
|
40
|
+
(Rails >= 5.1). The unsafe hash is now used, which is what a read-only tracker needs. This broke
|
|
41
|
+
`track_requests` entirely, and went unnoticed because its specs are disabled (`xit`).
|
|
42
|
+
* Fix `Flu.init` raising `configuration.application_name must not be nil` on a stock Rails application.
|
|
43
|
+
|
|
44
|
+
**Changed**
|
|
45
|
+
|
|
46
|
+
* **Breaking**: `flu-rails` now supports Rails 8 exclusively.
|
|
47
|
+
* Drop the pre-Rails-8 compatibility shims that this makes dead code:
|
|
48
|
+
* `flu_changes_depending_on_active_record_version`
|
|
49
|
+
* the `Zeitwerk::Loader.eager_load_all` fallback in `Util::ExportService`
|
|
50
|
+
* Restore `activesupport` as a runtime dependency
|
|
51
|
+
* Require the ActiveSupport core extensions that are actually used
|
|
52
|
+
|
|
53
|
+
**Publishing**
|
|
54
|
+
|
|
55
|
+
* Add a `Rakefile` (`bundler/gem_tasks` + an `rspec` task), so that `rake release` builds, tags and publishes the gem. `rake` becomes a development dependency.
|
|
56
|
+
* Add `.github/workflows/release.yml`: pushing a `v*` tag checks that the tag matches `Flu::VERSION`,
|
|
57
|
+
runs the specs, then builds and pushes the gem through RubyGems' Trusted Publishing. No RubyGems
|
|
58
|
+
API key is stored in the repository — a short-lived GitHub OIDC token is exchanged for a scoped
|
|
59
|
+
RubyGems credential.
|
|
60
|
+
|
|
61
|
+
**Verified**
|
|
62
|
+
|
|
63
|
+
* Verified against Rails 8.1 on a real application: railtie boot, `ActiveRecord` and
|
|
64
|
+
`ActionController::Base`/`API` tracking through the full Rack stack, request id propagation from a
|
|
65
|
+
controller into its entity-change events, uploaded file mapping and user-agent rejection — with
|
|
66
|
+
every framework deprecator set to `:raise`. No deprecation is triggered.
|
|
67
|
+
|
|
8
68
|
### [1.2.0] - 2026-07-31
|
|
9
69
|
|
|
10
70
|
* Declare `required_ruby_version` as `>= 3.2`
|
data/MIT-LICENSE
CHANGED
|
@@ -1,20 +1,21 @@
|
|
|
1
|
-
|
|
1
|
+
MIT License
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
a copy of this software and associated documentation files (the
|
|
5
|
-
"Software"), to deal in the Software without restriction, including
|
|
6
|
-
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
-
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
-
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
-
the following conditions:
|
|
3
|
+
Copyright (c) 2026 Commuty SA
|
|
10
4
|
|
|
11
|
-
|
|
12
|
-
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
13
11
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
CHANGED
|
@@ -16,7 +16,7 @@ For now, events are generated from:
|
|
|
16
16
|
Add the gem to your project's Gemfile:
|
|
17
17
|
|
|
18
18
|
```ruby
|
|
19
|
-
gem "flu-rails"
|
|
19
|
+
gem "flu-rails"
|
|
20
20
|
```
|
|
21
21
|
|
|
22
22
|
Then, create an initializer into your Rails app (`config/initializers/flu-rails.rb`)
|
|
@@ -38,20 +38,18 @@ Each configuration is detailed below.
|
|
|
38
38
|
## Requirements
|
|
39
39
|
|
|
40
40
|
* Ruby >= 3.2 (tested against 3.2, 3.3 and 3.4)
|
|
41
|
-
*
|
|
42
|
-
|
|
41
|
+
* Rails 8 only (`activerecord`, `actionpack` and `activesupport` are runtime dependencies,
|
|
42
|
+
constrained to `~> 8.0`). `flu-rails` extends `ActiveRecord::Base` and `ActionController::Base`, so it requires Rails.
|
|
43
|
+
* RabbitMQ: no specific version is required by `flu-rails` itself. The broker is reached through
|
|
44
|
+
`bunny` 3.x and `rabbitmq_http_api_client` 3.x, which target the
|
|
45
|
+
[currently supported RabbitMQ release series](https://www.rabbitmq.com/release-information).
|
|
43
46
|
|
|
44
47
|
## Usage
|
|
45
48
|
|
|
46
49
|
### Start up
|
|
47
50
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
Flu.init
|
|
51
|
-
Flu.start
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
`flu-rails` startup waits until its RabbitMQ exchange is connected.
|
|
51
|
+
`flu-rails` starts automatically through its `Railtie`: there is nothing to call by hand.
|
|
52
|
+
Its startup waits until its RabbitMQ exchange is connected.
|
|
55
53
|
|
|
56
54
|
### Track changes on an ActiveRecord model
|
|
57
55
|
|
|
@@ -90,7 +88,7 @@ For instance:
|
|
|
90
88
|
|
|
91
89
|
### Track requests to a Rails Controller action
|
|
92
90
|
|
|
93
|
-
All subclasses of `ActionController::Base` or `ActionController::API`
|
|
91
|
+
All subclasses of `ActionController::Base` or `ActionController::API` that call `track_requests` are "tracked". _E.g._:
|
|
94
92
|
|
|
95
93
|
```ruby
|
|
96
94
|
class ApplicationController < ActionController::Base
|
|
@@ -167,7 +165,7 @@ All options have a default value. However, all of them can be changed in your in
|
|
|
167
165
|
| `auto_connect_to_exchange` | `true`| Boolean | Optional | Thanks to `Railties`, `flu-rails` starts automatically when the Rails app boots. However, this can be useful to not connect RabbitMQ at start up. To do so, set `auto_connect_to_exchange` to `false`. | `false` |
|
|
168
166
|
| `default_ignored_model_changes` | `[:password, :password_confirmation, :created_at, :updated_at]` | Boolean | Optional | By default, all these attributes will be ignored from model changes when creating an event. For instance, this means that timestamp fields (`created_at` and `updated_at`) are not monitored when they change. | `[]` |
|
|
169
167
|
| `default_ignored_request_params` | `[:password, :password_confirmation, :controller, :action]` | Boolean | Optional | By default, all these parameters will be ignored from controller request's `params` when creating an event. | `false` |
|
|
170
|
-
| `application_name` | `Rails.application.class.
|
|
168
|
+
| `application_name` | `Rails.application.class.module_parent_name`, resolved on startup | String | Required | Is used as `emitter` for each event created by `flu-rails`, if not overriden by the `track_met`. | `my_app` |
|
|
171
169
|
| `bunny_options` | `{}` | Hash of symbols | Optional | Additional options to add when connecting the RabbitMQ broker. This overrides the existing options with the same name. | `{ verify_peer: true }` |
|
|
172
170
|
|
|
173
171
|
## How to execute tests
|
|
@@ -175,10 +173,32 @@ All options have a default value. However, all of them can be changed in your in
|
|
|
175
173
|
From the `flu-rails` directory:
|
|
176
174
|
|
|
177
175
|
```
|
|
178
|
-
$ docker
|
|
179
|
-
$ docker run -v `pwd`:/usr/src/app/ flu:test rspec spec
|
|
176
|
+
$ docker compose run --rm specs
|
|
180
177
|
```
|
|
181
178
|
|
|
179
|
+
This starts a RabbitMQ broker, waits for it, and runs the whole suite against it.
|
|
180
|
+
|
|
181
|
+
Part of the suite publishes to a real broker and drives its HTTP management API: nothing there is
|
|
182
|
+
stubbed, since a stub cannot tell whether the gem actually speaks AMQP. To run the specs directly on
|
|
183
|
+
your machine, start a broker first:
|
|
184
|
+
|
|
185
|
+
```
|
|
186
|
+
$ docker compose up -d rabbitmq
|
|
187
|
+
$ bundle exec rspec
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Without one, those examples are reported as skipped and the rest of the suite still runs. Set
|
|
191
|
+
`FLU_REQUIRE_RABBITMQ` to turn that skip into a failure — this is what the CI workflow does, so that
|
|
192
|
+
a build cannot report success without having reached RabbitMQ:
|
|
193
|
+
|
|
194
|
+
```
|
|
195
|
+
$ FLU_REQUIRE_RABBITMQ=true bundle exec rspec
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
A broker of your own can be used instead through `FLU_RABBITMQ_HOST`, `FLU_RABBITMQ_PORT`,
|
|
199
|
+
`FLU_RABBITMQ_MANAGEMENT_PORT`, `FLU_RABBITMQ_USER` and `FLU_RABBITMQ_PASSWORD`. Every example
|
|
200
|
+
declares an exchange and queues of its own, and deletes them afterwards.
|
|
201
|
+
|
|
182
202
|
## Example of Events
|
|
183
203
|
|
|
184
204
|
### Structure of an Active Record change
|
|
@@ -270,3 +290,23 @@ For instance, calling the action `destroy` of `CountryController` will emit this
|
|
|
270
290
|
## Side-notes
|
|
271
291
|
|
|
272
292
|
* `assocations` node contains `belongs_to` associations only.
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
## How to release a new version
|
|
296
|
+
|
|
297
|
+
Releases are published to [RubyGems](https://rubygems.org/gems/flu-rails) by GitHub Actions
|
|
298
|
+
(`.github/workflows/release.yml`), through [Trusted Publishing](https://guides.rubygems.org/trusted-publishing/):
|
|
299
|
+
no API key is stored in this repository, the workflow exchanges a short-lived GitHub OIDC token for a
|
|
300
|
+
scoped RubyGems credential.
|
|
301
|
+
|
|
302
|
+
1. Update `Flu::VERSION` in `lib/flu-rails/version.rb` and the `CHANGELOG.md`
|
|
303
|
+
2. Commit and push these changes to `main`
|
|
304
|
+
3. Tag the commit and push the tag:
|
|
305
|
+
|
|
306
|
+
```
|
|
307
|
+
$ git tag -a v8.0.1 -m "Version 8.0.1"
|
|
308
|
+
$ git push origin v8.0.1
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
The workflow then checks that the tag matches `Flu::VERSION`, runs the tests, builds the gem
|
|
312
|
+
and pushes it. It only publishes tags starting with `v`.
|
data/flu-rails.gemspec
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
require_relative "lib/flu-rails/version"
|
|
2
|
+
|
|
3
|
+
Gem::Specification.new do |spec|
|
|
4
|
+
spec.name = "flu-rails"
|
|
5
|
+
spec.version = Flu::VERSION
|
|
6
|
+
spec.authors = ["Loïc Vigneron", "Lorent Lempereur", "Thibault Poncelet", "Logan Clément"]
|
|
7
|
+
spec.email = ["support@commuty.net"]
|
|
8
|
+
spec.summary = "Track your application events and publish them to RabbitMQ."
|
|
9
|
+
spec.description = "Seamlessly emit events from an existing Rails application, without changing its " \
|
|
10
|
+
"codebase, and publish them to RabbitMQ. Events are generated from CRUD operations " \
|
|
11
|
+
"on ActiveRecord models and from requests on Rails controller actions."
|
|
12
|
+
spec.homepage = "https://github.com/crepesourcing/flu-rails"
|
|
13
|
+
spec.license = "MIT"
|
|
14
|
+
|
|
15
|
+
spec.required_ruby_version = ">= 3.2"
|
|
16
|
+
|
|
17
|
+
spec.metadata = {
|
|
18
|
+
"source_code_uri" => spec.homepage,
|
|
19
|
+
"changelog_uri" => "#{spec.homepage}/blob/main/CHANGELOG.md",
|
|
20
|
+
"bug_tracker_uri" => "#{spec.homepage}/issues",
|
|
21
|
+
"rubygems_mfa_required" => "true"
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
spec.files = Dir.glob(["lib/**/*.rb", "CHANGELOG.md", "MIT-LICENSE", "README.md", "flu-rails.gemspec"])
|
|
25
|
+
spec.require_paths = ["lib"]
|
|
26
|
+
|
|
27
|
+
spec.add_dependency "actionpack", "~> 8.0"
|
|
28
|
+
spec.add_dependency "activerecord", "~> 8.0"
|
|
29
|
+
spec.add_dependency "activesupport", "~> 8.0"
|
|
30
|
+
spec.add_dependency "bunny", "~> 3.1"
|
|
31
|
+
spec.add_dependency "logger", "~> 1.7"
|
|
32
|
+
spec.add_dependency "rabbitmq_http_api_client", "~> 3.2"
|
|
33
|
+
|
|
34
|
+
spec.add_development_dependency "byebug", "~> 13.0"
|
|
35
|
+
spec.add_development_dependency "ostruct", "~> 0.6"
|
|
36
|
+
spec.add_development_dependency "rake", ">= 13.0"
|
|
37
|
+
spec.add_development_dependency "rspec", "~> 3.13"
|
|
38
|
+
spec.add_development_dependency "sqlite3", "~> 2.9"
|
|
39
|
+
end
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
require "active_support/core_ext/string/inflections"
|
|
2
|
+
require "active_support/core_ext/time/zones"
|
|
3
|
+
|
|
1
4
|
module Flu
|
|
2
5
|
class ActionControllerExtender
|
|
3
6
|
def self.extend_controllers(event_factory, event_publisher, logger)
|
|
@@ -18,49 +21,38 @@ module Flu
|
|
|
18
21
|
ignored_request_params = options.fetch(:ignored_request_params, []).map(&:to_sym)
|
|
19
22
|
emitter_lambda = options[:emitter]
|
|
20
23
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
prepend_after_action do
|
|
29
|
-
track_requests(event_factory, event_publisher, user_metadata_lambda, ignored_request_params, emitter_lambda)
|
|
30
|
-
remove_request_entity_metadata_lambda
|
|
31
|
-
end
|
|
32
|
-
after_action do
|
|
33
|
-
remove_request_id
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
def define_request_id
|
|
24
|
+
# These helpers are defined with 'define_method' rather than 'def' for two reasons. A bare
|
|
25
|
+
# 'def' opens a new scope, so it captures neither 'logger' nor the tracking options: the
|
|
26
|
+
# former silently resolved to ActionController::Base#logger, which ignored 'Flu.config.logger'
|
|
27
|
+
# altogether. And its default definee is the class 'class_eval' was called on, so the helpers
|
|
28
|
+
# landed on ActionController::Base itself, leaking onto every controller of the host
|
|
29
|
+
# application. Here 'self' is the class calling 'track_requests', which is where they belong.
|
|
30
|
+
define_method(:flu_define_request_id) do
|
|
37
31
|
request_id = SecureRandom.uuid
|
|
38
32
|
@flu_request_id = request_id
|
|
39
33
|
Flu::CoreExt.flu_tracker_request_id = request_id
|
|
40
34
|
end
|
|
41
35
|
|
|
42
|
-
|
|
36
|
+
define_method(:flu_remove_request_id) do
|
|
43
37
|
Flu::CoreExt.flu_tracker_request_id = nil
|
|
44
38
|
end
|
|
45
39
|
|
|
46
|
-
|
|
40
|
+
define_method(:flu_define_request_entity_metadata) do
|
|
47
41
|
Flu::CoreExt.flu_tracker_request_entity_metadata = instance_exec(&entity_metadata_lambda) if entity_metadata_lambda
|
|
48
42
|
end
|
|
49
43
|
|
|
50
|
-
|
|
44
|
+
define_method(:flu_remove_request_entity_metadata) do
|
|
51
45
|
Flu::CoreExt.flu_tracker_request_entity_metadata = nil
|
|
52
46
|
end
|
|
53
47
|
|
|
54
|
-
|
|
55
|
-
rejected_user_agents
|
|
56
|
-
|
|
57
|
-
matching_user_agents = user_agent&.match(rejected_user_agents)
|
|
58
|
-
!matching_user_agents.nil?
|
|
48
|
+
define_method(:flu_rejected_origin?) do
|
|
49
|
+
rejected_user_agents = Regexp.union(Flu.config.rejected_user_agents)
|
|
50
|
+
!request.user_agent&.match(rejected_user_agents).nil?
|
|
59
51
|
end
|
|
60
52
|
|
|
61
|
-
|
|
62
|
-
if
|
|
63
|
-
logger.warn
|
|
53
|
+
define_method(:flu_track_request) do
|
|
54
|
+
if flu_rejected_origin?
|
|
55
|
+
logger.warn("Origin user agent rejected: #{request.user_agent}")
|
|
64
56
|
else
|
|
65
57
|
tracked_request = event_factory.create_data_from_request(@flu_request_id,
|
|
66
58
|
params,
|
|
@@ -74,6 +66,21 @@ module Flu
|
|
|
74
66
|
event_publisher.publish(event)
|
|
75
67
|
end
|
|
76
68
|
end
|
|
69
|
+
|
|
70
|
+
before_action do
|
|
71
|
+
flu_define_request_id
|
|
72
|
+
@request_start_time = Time.zone.now
|
|
73
|
+
end
|
|
74
|
+
prepend_before_action do
|
|
75
|
+
flu_define_request_entity_metadata
|
|
76
|
+
end
|
|
77
|
+
prepend_after_action do
|
|
78
|
+
flu_track_request
|
|
79
|
+
flu_remove_request_entity_metadata
|
|
80
|
+
end
|
|
81
|
+
after_action do
|
|
82
|
+
flu_remove_request_id
|
|
83
|
+
end
|
|
77
84
|
end
|
|
78
85
|
end
|
|
79
86
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
require "active_support/core_ext/object/try"
|
|
2
|
+
|
|
1
3
|
module Flu
|
|
2
4
|
class ActiveRecordExtender
|
|
3
5
|
def self.extend_models(event_factory, event_publisher)
|
|
@@ -8,8 +10,8 @@ module Flu
|
|
|
8
10
|
self.flu_ignored_model_changes = options.fetch(:ignored_model_changes, []).map(&:to_s)
|
|
9
11
|
self.flu_overriden_emitter_lambda = options.fetch(:emitter, nil)
|
|
10
12
|
|
|
11
|
-
after_create { flu_track_entity_change(:create,
|
|
12
|
-
after_update { flu_track_entity_change(:update,
|
|
13
|
+
after_create { flu_track_entity_change(:create, saved_changes, event_factory) }
|
|
14
|
+
after_update { flu_track_entity_change(:update, saved_changes, event_factory) }
|
|
13
15
|
after_destroy { flu_track_entity_change(:destroy, { "id" => [id, nil] }, event_factory) }
|
|
14
16
|
after_commit { flu_commit_changes(event_factory, event_publisher) }
|
|
15
17
|
after_rollback { flu_rollback_changes }
|
|
@@ -55,10 +57,6 @@ module Flu
|
|
|
55
57
|
end
|
|
56
58
|
end
|
|
57
59
|
|
|
58
|
-
def flu_changes_depending_on_active_record_version
|
|
59
|
-
respond_to?(:saved_changes) ? saved_changes : changes
|
|
60
|
-
end
|
|
61
|
-
|
|
62
60
|
def flu_changes
|
|
63
61
|
@flu_changes ||= []
|
|
64
62
|
end
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
require "securerandom"
|
|
2
|
+
require "active_support/core_ext/object/blank"
|
|
3
|
+
require "active_support/core_ext/string/inflections"
|
|
4
|
+
require "active_support/core_ext/time/zones"
|
|
2
5
|
require_relative "event"
|
|
3
6
|
|
|
4
7
|
module Flu
|
|
@@ -45,7 +48,9 @@ module Flu
|
|
|
45
48
|
original_emitter = @emitter
|
|
46
49
|
overriden_emitter = data[:overriden_emitter]&.strip&.delete(".")
|
|
47
50
|
final_emitter = overriden_emitter.blank? ? original_emitter : overriden_emitter
|
|
48
|
-
|
|
51
|
+
# 'overriden_emitter' selects the emitter of the event, it is not part of what is tracked:
|
|
52
|
+
# it is dropped from the payload instead of being published as an 'overridenEmitter' key.
|
|
53
|
+
Event.new(SecureRandom.uuid, final_emitter, kind, name, deep_camelize(data.except(:overriden_emitter)))
|
|
49
54
|
end
|
|
50
55
|
|
|
51
56
|
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)
|
|
@@ -71,12 +76,17 @@ module Flu
|
|
|
71
76
|
response_code: response.status,
|
|
72
77
|
user_agent: request.user_agent,
|
|
73
78
|
duration: Time.zone.now - request_start_time,
|
|
74
|
-
params: params
|
|
79
|
+
params: extract_params(params, ignored_request_params)
|
|
75
80
|
}
|
|
76
81
|
end
|
|
77
82
|
|
|
78
83
|
private
|
|
79
84
|
|
|
85
|
+
def extract_params(params, ignored_request_params)
|
|
86
|
+
remaining = params.except(*ignored_request_params).except(*@default_ignored_request_params)
|
|
87
|
+
remaining.respond_to?(:to_unsafe_h) ? remaining.to_unsafe_h.to_h : remaining.to_h
|
|
88
|
+
end
|
|
89
|
+
|
|
80
90
|
def deep_camelize(value)
|
|
81
91
|
case value
|
|
82
92
|
when Array
|
data/lib/flu-rails/util.rb
CHANGED
|
@@ -16,12 +16,13 @@ module Flu
|
|
|
16
16
|
association_columns = entity_type.flu_association_columns
|
|
17
17
|
user_metadata_lambda = entity_type.flu_user_metadata_lambdas[:create]
|
|
18
18
|
ignored_model_changes = entity_type.flu_ignored_model_changes
|
|
19
|
+
overriden_emitter_lambda = entity_type.flu_overriden_emitter_lambda
|
|
19
20
|
|
|
20
21
|
entities.each do |entity|
|
|
21
22
|
print "\r" unless current_entity_index == 0
|
|
22
23
|
current_entity_index += 1
|
|
23
24
|
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
|
+
data = extract_data_from(entity, event_factory, user_metadata_lambda, association_columns, ignored_model_changes, overriden_emitter_lambda)
|
|
25
26
|
event = event_factory.build_entity_change_event(data)
|
|
26
27
|
event.timestamp = entity.created_at unless entity.created_at.nil?
|
|
27
28
|
event_publisher.publish(event)
|
|
@@ -42,8 +43,7 @@ module Flu
|
|
|
42
43
|
end
|
|
43
44
|
|
|
44
45
|
def find_all_entity_types
|
|
45
|
-
|
|
46
|
-
Zeitwerk::Loader.eager_load_all if !eager_loaded && defined?(Zeitwerk)
|
|
46
|
+
Rails.application.eager_load!
|
|
47
47
|
ActiveRecord::Base.descendants
|
|
48
48
|
end
|
|
49
49
|
|
|
@@ -53,9 +53,17 @@ module Flu
|
|
|
53
53
|
end
|
|
54
54
|
end
|
|
55
55
|
|
|
56
|
-
def extract_data_from(entity, event_factory, user_metadata_lambda, association_columns, ignored_model_changes)
|
|
56
|
+
def extract_data_from(entity, event_factory, user_metadata_lambda, association_columns, ignored_model_changes, overriden_emitter_lambda)
|
|
57
57
|
changes = create_changes_from_existing(entity)
|
|
58
|
-
event_factory.create_data_from_entity_changes(:create,
|
|
58
|
+
event_factory.create_data_from_entity_changes(:create,
|
|
59
|
+
entity,
|
|
60
|
+
nil,
|
|
61
|
+
nil,
|
|
62
|
+
changes,
|
|
63
|
+
user_metadata_lambda,
|
|
64
|
+
association_columns,
|
|
65
|
+
ignored_model_changes,
|
|
66
|
+
overriden_emitter_lambda)
|
|
59
67
|
end
|
|
60
68
|
|
|
61
69
|
def create_changes_from_existing(entity)
|
data/lib/flu-rails/version.rb
CHANGED
data/lib/flu-rails.rb
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
require "logger"
|
|
2
2
|
require "json"
|
|
3
|
+
require "active_support/core_ext/module/introspection"
|
|
3
4
|
require_relative "flu-rails/version"
|
|
4
5
|
require_relative "flu-rails/event"
|
|
5
6
|
require_relative "flu-rails/event_factory"
|
|
@@ -33,6 +34,7 @@ module Flu
|
|
|
33
34
|
end
|
|
34
35
|
|
|
35
36
|
def self.init
|
|
37
|
+
@configuration.application_name ||= default_application_name
|
|
36
38
|
raise "configuration.application_name must not be nil" if @configuration.application_name.nil?
|
|
37
39
|
@logger = @configuration.logger
|
|
38
40
|
@event_factory = Flu::EventFactory.new(@configuration)
|
|
@@ -40,6 +42,15 @@ module Flu
|
|
|
40
42
|
extend_models_and_controllers
|
|
41
43
|
end
|
|
42
44
|
|
|
45
|
+
def self.default_application_name
|
|
46
|
+
if defined?(Rails) && Rails.respond_to?(:application)
|
|
47
|
+
application = Rails.application
|
|
48
|
+
application.nil? ? nil : application.class.module_parent_name
|
|
49
|
+
else
|
|
50
|
+
nil
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
43
54
|
def self.create_event_publisher(configuration)
|
|
44
55
|
if is_testing_environment?
|
|
45
56
|
logger.info("Loading Flu with a dummy event publisher (this will not connect any exchange)")
|
|
@@ -51,7 +62,10 @@ module Flu
|
|
|
51
62
|
end
|
|
52
63
|
|
|
53
64
|
def self.is_testing_environment?
|
|
54
|
-
defined?(Rails)
|
|
65
|
+
# 'defined?(Rails)' alone is not enough: gems such as 'rails-html-sanitizer' define an empty
|
|
66
|
+
# 'Rails' namespace, so the constant can exist without railties ever defining 'Rails.env'.
|
|
67
|
+
return false unless defined?(Rails) && Rails.respond_to?(:env)
|
|
68
|
+
config.development_environments.include?(Rails.env)
|
|
55
69
|
end
|
|
56
70
|
|
|
57
71
|
def self.extend_models_and_controllers
|
|
@@ -79,7 +93,7 @@ module Flu
|
|
|
79
93
|
config.auto_connect_to_exchange = true
|
|
80
94
|
config.default_ignored_model_changes = [:password, :password_confirmation, :created_at, :updated_at]
|
|
81
95
|
config.default_ignored_request_params = [:password, :password_confirmation, :controller, :action]
|
|
82
|
-
config.application_name =
|
|
96
|
+
config.application_name = nil
|
|
83
97
|
config.bunny_options = {}
|
|
84
98
|
end
|
|
85
99
|
end
|
metadata
CHANGED
|
@@ -1,86 +1,101 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: flu-rails
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 8.0.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Loïc Vigneron
|
|
8
8
|
- Lorent Lempereur
|
|
9
9
|
- Thibault Poncelet
|
|
10
|
+
- Logan Clément
|
|
10
11
|
bindir: bin
|
|
11
12
|
cert_chain: []
|
|
12
13
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
13
14
|
dependencies:
|
|
14
15
|
- !ruby/object:Gem::Dependency
|
|
15
|
-
name:
|
|
16
|
+
name: actionpack
|
|
16
17
|
requirement: !ruby/object:Gem::Requirement
|
|
17
18
|
requirements:
|
|
18
19
|
- - "~>"
|
|
19
20
|
- !ruby/object:Gem::Version
|
|
20
|
-
version: '
|
|
21
|
+
version: '8.0'
|
|
21
22
|
type: :runtime
|
|
22
23
|
prerelease: false
|
|
23
24
|
version_requirements: !ruby/object:Gem::Requirement
|
|
24
25
|
requirements:
|
|
25
26
|
- - "~>"
|
|
26
27
|
- !ruby/object:Gem::Version
|
|
27
|
-
version: '
|
|
28
|
+
version: '8.0'
|
|
28
29
|
- !ruby/object:Gem::Dependency
|
|
29
|
-
name:
|
|
30
|
+
name: activerecord
|
|
30
31
|
requirement: !ruby/object:Gem::Requirement
|
|
31
32
|
requirements:
|
|
32
33
|
- - "~>"
|
|
33
34
|
- !ruby/object:Gem::Version
|
|
34
|
-
version: '
|
|
35
|
+
version: '8.0'
|
|
35
36
|
type: :runtime
|
|
36
37
|
prerelease: false
|
|
37
38
|
version_requirements: !ruby/object:Gem::Requirement
|
|
38
39
|
requirements:
|
|
39
40
|
- - "~>"
|
|
40
41
|
- !ruby/object:Gem::Version
|
|
41
|
-
version: '
|
|
42
|
+
version: '8.0'
|
|
42
43
|
- !ruby/object:Gem::Dependency
|
|
43
|
-
name:
|
|
44
|
+
name: activesupport
|
|
44
45
|
requirement: !ruby/object:Gem::Requirement
|
|
45
46
|
requirements:
|
|
46
47
|
- - "~>"
|
|
47
48
|
- !ruby/object:Gem::Version
|
|
48
|
-
version: '
|
|
49
|
+
version: '8.0'
|
|
49
50
|
type: :runtime
|
|
50
51
|
prerelease: false
|
|
51
52
|
version_requirements: !ruby/object:Gem::Requirement
|
|
52
53
|
requirements:
|
|
53
54
|
- - "~>"
|
|
54
55
|
- !ruby/object:Gem::Version
|
|
55
|
-
version: '
|
|
56
|
+
version: '8.0'
|
|
56
57
|
- !ruby/object:Gem::Dependency
|
|
57
|
-
name:
|
|
58
|
+
name: bunny
|
|
58
59
|
requirement: !ruby/object:Gem::Requirement
|
|
59
60
|
requirements:
|
|
60
61
|
- - "~>"
|
|
61
62
|
- !ruby/object:Gem::Version
|
|
62
|
-
version: '
|
|
63
|
-
type: :
|
|
63
|
+
version: '3.1'
|
|
64
|
+
type: :runtime
|
|
64
65
|
prerelease: false
|
|
65
66
|
version_requirements: !ruby/object:Gem::Requirement
|
|
66
67
|
requirements:
|
|
67
68
|
- - "~>"
|
|
68
69
|
- !ruby/object:Gem::Version
|
|
69
|
-
version: '
|
|
70
|
+
version: '3.1'
|
|
70
71
|
- !ruby/object:Gem::Dependency
|
|
71
|
-
name:
|
|
72
|
+
name: logger
|
|
72
73
|
requirement: !ruby/object:Gem::Requirement
|
|
73
74
|
requirements:
|
|
74
75
|
- - "~>"
|
|
75
76
|
- !ruby/object:Gem::Version
|
|
76
|
-
version: '
|
|
77
|
-
type: :
|
|
77
|
+
version: '1.7'
|
|
78
|
+
type: :runtime
|
|
78
79
|
prerelease: false
|
|
79
80
|
version_requirements: !ruby/object:Gem::Requirement
|
|
80
81
|
requirements:
|
|
81
82
|
- - "~>"
|
|
82
83
|
- !ruby/object:Gem::Version
|
|
83
|
-
version: '
|
|
84
|
+
version: '1.7'
|
|
85
|
+
- !ruby/object:Gem::Dependency
|
|
86
|
+
name: rabbitmq_http_api_client
|
|
87
|
+
requirement: !ruby/object:Gem::Requirement
|
|
88
|
+
requirements:
|
|
89
|
+
- - "~>"
|
|
90
|
+
- !ruby/object:Gem::Version
|
|
91
|
+
version: '3.2'
|
|
92
|
+
type: :runtime
|
|
93
|
+
prerelease: false
|
|
94
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
95
|
+
requirements:
|
|
96
|
+
- - "~>"
|
|
97
|
+
- !ruby/object:Gem::Version
|
|
98
|
+
version: '3.2'
|
|
84
99
|
- !ruby/object:Gem::Dependency
|
|
85
100
|
name: byebug
|
|
86
101
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -109,6 +124,20 @@ dependencies:
|
|
|
109
124
|
- - "~>"
|
|
110
125
|
- !ruby/object:Gem::Version
|
|
111
126
|
version: '0.6'
|
|
127
|
+
- !ruby/object:Gem::Dependency
|
|
128
|
+
name: rake
|
|
129
|
+
requirement: !ruby/object:Gem::Requirement
|
|
130
|
+
requirements:
|
|
131
|
+
- - ">="
|
|
132
|
+
- !ruby/object:Gem::Version
|
|
133
|
+
version: '13.0'
|
|
134
|
+
type: :development
|
|
135
|
+
prerelease: false
|
|
136
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
137
|
+
requirements:
|
|
138
|
+
- - ">="
|
|
139
|
+
- !ruby/object:Gem::Version
|
|
140
|
+
version: '13.0'
|
|
112
141
|
- !ruby/object:Gem::Dependency
|
|
113
142
|
name: rspec
|
|
114
143
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -141,8 +170,7 @@ description: Seamlessly emit events from an existing Rails application, without
|
|
|
141
170
|
its codebase, and publish them to RabbitMQ. Events are generated from CRUD operations
|
|
142
171
|
on ActiveRecord models and from requests on Rails controller actions.
|
|
143
172
|
email:
|
|
144
|
-
-
|
|
145
|
-
- info@commuty.net
|
|
173
|
+
- support@commuty.net
|
|
146
174
|
executables: []
|
|
147
175
|
extensions: []
|
|
148
176
|
extra_rdoc_files: []
|
|
@@ -150,6 +178,7 @@ files:
|
|
|
150
178
|
- CHANGELOG.md
|
|
151
179
|
- MIT-LICENSE
|
|
152
180
|
- README.md
|
|
181
|
+
- flu-rails.gemspec
|
|
153
182
|
- lib/flu-rails.rb
|
|
154
183
|
- lib/flu-rails/action_controller_extender.rb
|
|
155
184
|
- lib/flu-rails/active_record_extender.rb
|
|
@@ -167,9 +196,8 @@ homepage: https://github.com/crepesourcing/flu-rails
|
|
|
167
196
|
licenses:
|
|
168
197
|
- MIT
|
|
169
198
|
metadata:
|
|
170
|
-
homepage_uri: https://github.com/crepesourcing/flu-rails
|
|
171
199
|
source_code_uri: https://github.com/crepesourcing/flu-rails
|
|
172
|
-
changelog_uri: https://github.com/crepesourcing/flu-rails/blob/
|
|
200
|
+
changelog_uri: https://github.com/crepesourcing/flu-rails/blob/main/CHANGELOG.md
|
|
173
201
|
bug_tracker_uri: https://github.com/crepesourcing/flu-rails/issues
|
|
174
202
|
rubygems_mfa_required: 'true'
|
|
175
203
|
rdoc_options: []
|
|
@@ -186,7 +214,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
186
214
|
- !ruby/object:Gem::Version
|
|
187
215
|
version: '0'
|
|
188
216
|
requirements: []
|
|
189
|
-
rubygems_version:
|
|
217
|
+
rubygems_version: 3.6.9
|
|
190
218
|
specification_version: 4
|
|
191
219
|
summary: Track your application events and publish them to RabbitMQ.
|
|
192
220
|
test_files: []
|