thecore_backend_commons 3.4.1 → 3.5.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
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5e57106019f11e4ac721eaee6a0d974f3c640e32bd6d031a462d3356a20f1091
|
|
4
|
+
data.tar.gz: 2ad214f8fe011dafc87a3323d22b8d5f0e69bf0cc8ba61766385f219e14e41b6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: fec2dcd9fe2769f2853a34ec1f8b887a8c9e6f9766e911821ef741df81cfa58399da343745dc589b384b05746ed599f6e61fd3d8ba6d48f928fe2156fcd3b527
|
|
7
|
+
data.tar.gz: f46bf3f1ee9ff55bbabd17b7e5f3614d1a9d04ad7a58113d014f5030c2ed1f1f39d39d11f2a0556876d3540356f9a2565a9d75d5cb0c5fd882343cd8f5c9f6ed
|
data/README.md
CHANGED
|
@@ -126,6 +126,39 @@ PushNotificationChannel.broadcast_to(subscriber, message)
|
|
|
126
126
|
|
|
127
127
|
---
|
|
128
128
|
|
|
129
|
+
## `DefaultModuleRegistry` — shared `ApplicationRecord.inherited` hook
|
|
130
|
+
|
|
131
|
+
Other gems in the ecosystem (`model_driven_api`, `thecore_ui_rails_admin`, ...) need to give
|
|
132
|
+
*every* model some default behavior — a default API serialization shape, a default RailsAdmin
|
|
133
|
+
navigation entry — without requiring a generated per-model concern file for the
|
|
134
|
+
no-customization case. Rather than each gem independently overriding
|
|
135
|
+
`ApplicationRecord.inherited` (and risking one override clobbering another), they all register
|
|
136
|
+
into this one shared registry:
|
|
137
|
+
|
|
138
|
+
```ruby
|
|
139
|
+
ThecoreBackendCommons::DefaultModuleRegistry.register(
|
|
140
|
+
MyDefaultModule, # normally an ActiveSupport::Concern with an `included do ... end` block
|
|
141
|
+
applies_to: ->(klass) { true } # optional; defaults to every ApplicationRecord subclass
|
|
142
|
+
)
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Every registered module is `include`d, in registration order, into every `ApplicationRecord`
|
|
146
|
+
subclass for which `applies_to` returns true — at the moment the subclass is *defined*, not via
|
|
147
|
+
a post-boot scan (which would miss classes not yet autoloaded in development). This is wired up
|
|
148
|
+
by installing an `ApplicationRecord.inherited` hook from `config.to_prepare` (see
|
|
149
|
+
`config/initializers/default_module_registry.rb`), so it's already in place before Rails eager
|
|
150
|
+
loads every model in production.
|
|
151
|
+
|
|
152
|
+
Consumers today: `model_driven_api`'s `ModelDrivenApiDefaultJsonAttrs` (default `json_attrs`)
|
|
153
|
+
and `thecore_ui_rails_admin`'s `ThecoreUiRailsAdminDefaultNavigationConcern` (default
|
|
154
|
+
`navigation_label`/`navigation_icon`) — see their own READMEs for what each one does. A model
|
|
155
|
+
that still needs custom behavior keeps (or adds) an explicit `Api::ModelName`/
|
|
156
|
+
`RailsAdmin::ModelName` concern exactly as before; it simply runs *after* the default and
|
|
157
|
+
overrides it. Full mechanics (idempotency, abstract/STI exclusion, `to_prepare` vs
|
|
158
|
+
`after_initialize` timing) are documented in this gem's `CLAUDE.md`.
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
129
162
|
## Invio email e configurazione SMTP
|
|
130
163
|
|
|
131
164
|
### Configurazione
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
Rails.application.configure do
|
|
2
|
+
# Installed from `to_prepare` (not `after_initialize`) so the hook is in
|
|
3
|
+
# place *before* `eager_load!` runs -- see
|
|
4
|
+
# `ThecoreBackendCommons::DefaultModuleRegistry.install!` for why that
|
|
5
|
+
# ordering matters. `to_prepare` also re-runs on every class reload in
|
|
6
|
+
# development, so `install!` is idempotent.
|
|
7
|
+
config.to_prepare do
|
|
8
|
+
ThecoreBackendCommons::DefaultModuleRegistry.install!(ApplicationRecord)
|
|
9
|
+
end
|
|
10
|
+
end
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
module ThecoreBackendCommons
|
|
2
|
+
# Shared registry other gems use to register "default" modules that get
|
|
3
|
+
# `include`d automatically into every `ApplicationRecord` subclass, as it
|
|
4
|
+
# is defined -- not via a post-boot scan of `ApplicationRecord.subclasses`.
|
|
5
|
+
#
|
|
6
|
+
# This exists so multiple gems (`model_driven_api`, `thecore_ui_rails_admin`,
|
|
7
|
+
# ...) can each contribute default model behavior (API serialization shape,
|
|
8
|
+
# RailsAdmin config, ...) through ONE shared `ApplicationRecord.inherited`
|
|
9
|
+
# hook instead of each gem independently overriding `inherited` itself.
|
|
10
|
+
# See ADR 0001/0002 (`vendor/external/thecore/docs/adr/` in the host app).
|
|
11
|
+
#
|
|
12
|
+
# == Usage
|
|
13
|
+
#
|
|
14
|
+
# ThecoreBackendCommons::DefaultModuleRegistry.register(
|
|
15
|
+
# MyDefaultModule,
|
|
16
|
+
# applies_to: ->(klass) { klass.table_exists? rescue false }
|
|
17
|
+
# )
|
|
18
|
+
#
|
|
19
|
+
# `applies_to` defaults to "every subclass" (`->(_klass) { true }`) when
|
|
20
|
+
# omitted. Registered modules are `include`d, in registration order, into
|
|
21
|
+
# every `ApplicationRecord` subclass for which `applies_to` returns true,
|
|
22
|
+
# at the moment the subclass is defined -- see `InheritedHook` below.
|
|
23
|
+
#
|
|
24
|
+
# `MyDefaultModule` should normally be an `ActiveSupport::Concern` with an
|
|
25
|
+
# `included do ... end` block, so its default methods/callbacks/DSL calls
|
|
26
|
+
# land as *own* behavior on each model class (see ADR 0001's consequences
|
|
27
|
+
# section -- `model_driven_api`'s introspection depends on this).
|
|
28
|
+
#
|
|
29
|
+
# == Idempotency
|
|
30
|
+
#
|
|
31
|
+
# - Calling `.register` twice with the *same* module object is a no-op the
|
|
32
|
+
# second time -- it will not be applied twice to any subclass.
|
|
33
|
+
# - `.apply_to` never re-includes a module a class already has in its
|
|
34
|
+
# ancestor chain (e.g. an STI subclass that already inherited it from its
|
|
35
|
+
# base class), so STI subclasses are never double-included.
|
|
36
|
+
# - `.apply_to` never applies anything to an abstract class
|
|
37
|
+
# (`klass.abstract_class?`) -- most importantly `ApplicationRecord`
|
|
38
|
+
# itself (`primary_abstract_class`), which never receives default
|
|
39
|
+
# modules through this mechanism since `ApplicationRecord.inherited` only
|
|
40
|
+
# fires for classes that inherit *from* `ApplicationRecord`, never for
|
|
41
|
+
# `ApplicationRecord`'s own definition.
|
|
42
|
+
module DefaultModuleRegistry
|
|
43
|
+
Entry = Struct.new(:mod, :applies_to)
|
|
44
|
+
private_constant :Entry
|
|
45
|
+
|
|
46
|
+
# Prepended onto `ApplicationRecord.singleton_class` (see `.install!`)
|
|
47
|
+
# so that every subsequent subclass definition applies the registry.
|
|
48
|
+
#
|
|
49
|
+
# Calls `super` first so it composes correctly with ActiveRecord's own
|
|
50
|
+
# `inherited` (which sets up `base_class`, STI bookkeeping, ...) and with
|
|
51
|
+
# any other gem's pre-existing `inherited` override further up the
|
|
52
|
+
# singleton-class ancestor chain.
|
|
53
|
+
module InheritedHook
|
|
54
|
+
def inherited(subclass)
|
|
55
|
+
super
|
|
56
|
+
ThecoreBackendCommons::DefaultModuleRegistry.apply_to(subclass)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
class << self
|
|
61
|
+
# Registers +mod+ so it gets `include`d into every matching
|
|
62
|
+
# `ApplicationRecord` subclass defined from now on. Returns +mod+.
|
|
63
|
+
#
|
|
64
|
+
# +applies_to+ is a 1-arity callable invoked with the candidate class;
|
|
65
|
+
# only classes for which it returns truthy receive +mod+.
|
|
66
|
+
#
|
|
67
|
+
# Registering the same module object again is a no-op -- the original
|
|
68
|
+
# registration (and its original `applies_to`) is kept.
|
|
69
|
+
def register(mod, applies_to: ->(_klass) { true })
|
|
70
|
+
entries << Entry.new(mod, applies_to) unless registered?(mod)
|
|
71
|
+
mod
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# True if +mod+ has already been registered.
|
|
75
|
+
def registered?(mod)
|
|
76
|
+
entries.any? { |entry| entry.mod.equal?(mod) }
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Applies every registered module whose `applies_to` predicate matches
|
|
80
|
+
# +klass+, in registration order. No-op for abstract classes. Never
|
|
81
|
+
# re-includes a module +klass+ already has in its ancestor chain.
|
|
82
|
+
#
|
|
83
|
+
# Called automatically by `InheritedHook` -- only call directly from
|
|
84
|
+
# application/test code that needs to (re-)apply defaults to a class
|
|
85
|
+
# defined before the registry/hook was set up.
|
|
86
|
+
def apply_to(klass)
|
|
87
|
+
return klass if klass.abstract_class?
|
|
88
|
+
|
|
89
|
+
entries.each do |entry|
|
|
90
|
+
next unless entry.applies_to.call(klass)
|
|
91
|
+
next if klass.include?(entry.mod)
|
|
92
|
+
|
|
93
|
+
klass.include(entry.mod)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
klass
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Installs `InheritedHook` onto +base_class+'s singleton class so every
|
|
100
|
+
# subsequently-defined subclass triggers `.apply_to`. Idempotent --
|
|
101
|
+
# safe to call on every `to_prepare` cycle in development.
|
|
102
|
+
#
|
|
103
|
+
# Must run before any subclass of +base_class+ is defined (i.e. before
|
|
104
|
+
# eager loading), which is why the host initializer installs it from
|
|
105
|
+
# `config.to_prepare` rather than `config.after_initialize` -- Rails
|
|
106
|
+
# runs `to_prepare` callbacks *before* `eager_load!`, while
|
|
107
|
+
# `after_initialize` runs *after* it (see
|
|
108
|
+
# `Rails::Application::Finisher`). Installing from `after_initialize`
|
|
109
|
+
# would miss every subclass already loaded by eager loading in
|
|
110
|
+
# production.
|
|
111
|
+
def install!(base_class)
|
|
112
|
+
return base_class if base_class.singleton_class.ancestors.include?(InheritedHook)
|
|
113
|
+
|
|
114
|
+
base_class.singleton_class.prepend(InheritedHook)
|
|
115
|
+
base_class
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Test helper: clears all registrations. Not intended for production
|
|
119
|
+
# use -- does not uninstall `InheritedHook` from any class it was
|
|
120
|
+
# already installed on, and does not un-include modules already
|
|
121
|
+
# included into existing classes.
|
|
122
|
+
def reset!
|
|
123
|
+
@entries = []
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
private
|
|
127
|
+
|
|
128
|
+
def entries
|
|
129
|
+
@entries ||= []
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
@@ -17,6 +17,7 @@ require "thecore_backend_commons/engine"
|
|
|
17
17
|
require "thecore_backend_commons/smtp_config"
|
|
18
18
|
require "thecore_backend_commons/smtp_tester"
|
|
19
19
|
require "thecore_backend_commons/push_notification_service"
|
|
20
|
+
require "thecore_backend_commons/default_module_registry"
|
|
20
21
|
|
|
21
22
|
module ThecoreBackendCommons
|
|
22
23
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: thecore_backend_commons
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.
|
|
4
|
+
version: 3.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Gabriele Tassoni
|
|
@@ -216,6 +216,7 @@ files:
|
|
|
216
216
|
- config/initializers/concern_integer.rb
|
|
217
217
|
- config/initializers/concern_string.rb
|
|
218
218
|
- config/initializers/concern_user.rb
|
|
219
|
+
- config/initializers/default_module_registry.rb
|
|
219
220
|
- config/initializers/extension_nil.rb
|
|
220
221
|
- config/initializers/extension_string.rb
|
|
221
222
|
- config/locales/en.devise.custom.yml
|
|
@@ -229,6 +230,7 @@ files:
|
|
|
229
230
|
- db/seeds.rb
|
|
230
231
|
- lib/tasks/thecore_backend_commons_tasks.rake
|
|
231
232
|
- lib/thecore_backend_commons.rb
|
|
233
|
+
- lib/thecore_backend_commons/default_module_registry.rb
|
|
232
234
|
- lib/thecore_backend_commons/engine.rb
|
|
233
235
|
- lib/thecore_backend_commons/push_notification_service.rb
|
|
234
236
|
- lib/thecore_backend_commons/smtp_config.rb
|