rails_admin_next 1.1.2 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2bdbd212982bdb74e9a2288a5c14eba746b74b0a0ed902afbb0671cbdfd5c107
4
- data.tar.gz: 119040f95b74f79a05571d9f099cb98668efa5921ee2a4b43668b3fb320302ec
3
+ metadata.gz: fbd1b1c7b37f7d5d89eb5f9ef44dc086bedf1ecbd47f908c545afbb35e8f8bc1
4
+ data.tar.gz: 9358676100cad7935f07c91a757e27a761fe463e1a2fc6b291f8d8a0dc63df0d
5
5
  SHA512:
6
- metadata.gz: 583c910d3d95569fb8031b30bfda1a71ae61ecc090bdd0e8289d9e0d4ea01ee7cf635e417c912d17a2a22c5d9f1bfe764d057391fb58744c21c1c22099130531
7
- data.tar.gz: f7c7784f080f80977c21d9ae3bc02a822a1b2576aafe07f0dc063c4c7702180f425ea7905f00d8171e0a889a8842311cbff67ffd8268601eee68659b8bfdd032
6
+ metadata.gz: 8b999764cff3b8b068b54f5bba5ac2107d9f4545584a76be7764c63b24cfc7135560ce7372fc953bae2aab8a5ef5e3e09735e3e592125b93d21ee70f6f5662a8
7
+ data.tar.gz: 477419b70e7056875a32b0790422a1310423d93cd43d91bc68007e918dace9b30acd3a6ad55c38eff654df59fbbe11bb87e5a8141c669807849897519a2ad8d0
data/Gemfile CHANGED
@@ -19,6 +19,10 @@ group :test do
19
19
  gem "generator_spec"
20
20
  gem "image_processing"
21
21
  gem "pundit"
22
+ # Pinned within 0.10: Gemfile.lock is not committed here, so CI resolves fresh on every run
23
+ # and no Dependabot PR stages a release first, and the discovery spec drives Hyperdrive's
24
+ # pre-1.0 discovery API, which has renamed keys within 0.x before.
25
+ gem "rails-hyperdrive", "~> 0.10.1", require: false
22
26
  gem "rspec-rails"
23
27
  gem "rspec-retry"
24
28
  gem "ruby-vips"
data/README.md CHANGED
@@ -83,6 +83,22 @@ The install generator mounts the engine, writes
83
83
  `config/initializers/rails_admin_next.rb`, and does nothing else — the engine ships its
84
84
  own importmap and CSS, so there is no `yarn add`, no asset build, and no generated pins.
85
85
 
86
+ ### Agent skill
87
+
88
+ RailsAdminNext is a [Rails Hyperdrive][hyperdrive] companion gem. It ships a
89
+ `rails-admin-next` skill that teaches coding agents this fork's API rather than upstream
90
+ RailsAdmin's. In an app running Hyperdrive, sync installs the skill, and each later
91
+ release updates it:
92
+
93
+ ```bash
94
+ bin/rails hyperdrive:sync
95
+ ```
96
+
97
+ The skill points agents at the `docs/*.md` files packaged in the gem, so its references
98
+ always match the installed version.
99
+
100
+ [hyperdrive]: https://github.com/rails-hyperdrive/rails-hyperdrive
101
+
86
102
  ## Features
87
103
 
88
104
  - CRUD any data with ease
data/Rakefile CHANGED
@@ -8,6 +8,8 @@ Dir["lib/tasks/*.rake"].each { |rake| load rake }
8
8
  require "bundler"
9
9
  Bundler::GemHelper.install_tasks
10
10
 
11
+ require "hyperdrive/skill_tasks"
12
+
11
13
  require "rspec/core/rake_task"
12
14
  RSpec::Core::RakeTask.new(:spec)
13
15
 
data/docs/UPGRADING.md ADDED
@@ -0,0 +1,463 @@
1
+ # Upgrading from rails_admin to rails_admin_next
2
+
3
+ `rails_admin_next` is the modernized fork of `rails_admin`: Rails 8.1 / Ruby 4.0 floor,
4
+ ActiveRecord only, an importmap + Propshaft asset pipeline, a Stimulus + Turbo
5
+ frontend (no jQuery, jQuery-UI, Bootstrap JS, or `@rails/ujs`), vanilla CSS with a
6
+ latest-Chrome/Safari browser floor, and a hardened security posture. This guide covers
7
+ the one-time changes a host app needs.
8
+
9
+ The Ruby namespace is renamed **`RailsAdmin` → `RailsAdminNext`** (and `rails_admin` →
10
+ `rails_admin_next`), so your initializer, engine mount, model macros, and route helpers
11
+ need a mechanical rename. The **authorization contract is deliberately unchanged** — the
12
+ `:access, :rails_admin` subject and the `admin.*` I18n namespace stay exactly as they were,
13
+ so your CanCanCan abilities, Pundit policies, and translations need no edits.
14
+
15
+ ## 1. Gemfile
16
+
17
+ Replace the upstream `rails_admin` gem with the fork (distributed via `git:`):
18
+
19
+ ```ruby
20
+ # Gemfile
21
+ gem 'rails_admin_next', git: 'https://github.com/6temes/rails_admin_next'
22
+ ```
23
+
24
+ Remove the old `gem 'rails_admin'` line, then `bundle install`.
25
+
26
+ ## 2. Application code — rename the namespace
27
+
28
+ Rename `RailsAdmin` → `RailsAdminNext` and `rails_admin` → `rails_admin_next` in your
29
+ initializer, mount, per-model blocks, and route-helper calls:
30
+
31
+ ```ruby
32
+ # config/initializers/rails_admin_next.rb (renamed from config/initializers/rails_admin.rb)
33
+ RailsAdminNext.config do |config|
34
+ config.model User do
35
+ # …
36
+ end
37
+
38
+ # DELETE this line if your old initializer had it — the option was removed
39
+ # (there is a single importmap + Propshaft pipeline now; leaving it in place
40
+ # only logs a deprecation warning):
41
+ # config.asset_source = :importmap
42
+ end
43
+
44
+ # config/routes.rb
45
+ mount RailsAdminNext::Engine => '/admin', as: 'rails_admin_next'
46
+
47
+ # per-model blocks
48
+ class Team < ApplicationRecord
49
+ rails_admin_next do
50
+ field :name
51
+ end
52
+ end
53
+
54
+ # route helpers — these also hide in mailers and regular views
55
+ # (`rails_admin_url(...)` → `rails_admin_next_url(...)`) and inside field blocks
56
+ # (`bindings[:view].rails_admin.show_path(...)` → `bindings[:view].rails_admin_next.show_path(...)`)
57
+ rails_admin_next.dashboard_path
58
+ rails_admin_next.edit_path(model_name: 'team', id: team.id)
59
+ ```
60
+
61
+ **Keep the mount alias `rails_admin_next`.** The engine's own views resolve routes through
62
+ the `rails_admin_next` route proxy, so the mount must expose that helper name — either
63
+ `as: 'rails_admin_next'` as above, or no `as:` at all (it defaults to the engine name).
64
+ Aliasing the mount to something else to preserve old helper names (e.g. `as: 'rails_admin'`)
65
+ breaks every engine page at render time with
66
+ `undefined local variable or method 'rails_admin_next'`.
67
+
68
+ Find every host-side call site that needs the rename with:
69
+
70
+ ```bash
71
+ grep -rnE 'rails_admin_url|rails_admin_path|\.rails_admin\.' app config
72
+ ```
73
+
74
+ Running `rails g rails_admin_next:install` writes the new initializer and mount for you.
75
+
76
+ ## 3. Icons (`link_icon` / `navigation_icon`)
77
+
78
+ Font Awesome is **no longer shipped** (no icon font, no `font-awesome.css`). Icons now
79
+ render as inline SVG, and the icon options take a **logical icon name** (a Symbol or
80
+ String) instead of a Font Awesome class string:
81
+
82
+ ```ruby
83
+ # Before
84
+ RailsAdminNext.config do |config|
85
+ config.model Team do
86
+ navigation_icon 'fas fa-trophy'
87
+ end
88
+ config.actions do
89
+ # …
90
+ my_custom_action { link_icon 'fas fa-rocket' }
91
+ end
92
+ end
93
+
94
+ # After
95
+ RailsAdminNext.config do |config|
96
+ config.model Team do
97
+ navigation_icon :list # a logical name (see the list below)
98
+ end
99
+ config.actions do
100
+ my_custom_action { link_icon :show_in_app }
101
+ end
102
+ end
103
+ ```
104
+
105
+ Available logical names (see `RailsAdminNext::Icons::GLYPHS`):
106
+
107
+ `:dashboard` (alias `:home`), `:list`, `:show` (alias `:info`), `:new` (alias `:add`/`:plus`),
108
+ `:edit` (alias `:pencil`), `:delete` (aliases `:cancel`/`:times`), `:export`,
109
+ `:history` (alias `:book`), `:show_in_app` (alias `:eye`), `:bulk_delete` (alias `:question`),
110
+ `:collapse`, `:expand`, `:calendar`, `:check`, `:minus`, `:refresh` (alias `:sync`),
111
+ `:trash`, `:move_right`, `:move_left`, `:move_up`, `:move_down`.
112
+
113
+ **Legacy Font Awesome strings still given to `link_icon`/`navigation_icon` are handled
114
+ gracefully**: the glyph is best-effort mapped to the nearest logical name and a deprecation
115
+ warning is emitted (`RailsAdminNext.deprecator`); a value that can't be mapped renders no
116
+ icon rather than raising. Migrate to logical names to silence the warning. If you relied on
117
+ Font Awesome classes elsewhere in your own admin views, vendor Font Awesome yourself — the
118
+ engine no longer provides it.
119
+
120
+ ## Authorization (no change needed)
121
+
122
+ The authorization contract is held stable — **do not touch your abilities or policies**:
123
+
124
+ - The access subject is `:rails_admin`.
125
+ - Every action's authorization key (`:dashboard`, `:index`, `:show`, `:new`, `:edit`,
126
+ `:export`, `:destroy`, `:history`, `:show_in_app`) is unchanged.
127
+
128
+ ```ruby
129
+ # This CanCanCan ability keeps working unchanged:
130
+ can :access, :rails_admin
131
+ ```
132
+
133
+ The access subject is deliberately frozen and locked by a spec in the gem — a renamed
134
+ subject can fail *open* (a `cannot` rule that no longer matches silently grants access).
135
+
136
+ ## Asset prerequisites
137
+
138
+ RailsAdminNext ships its frontend over **importmap + Propshaft** only — there is no
139
+ build step and no Sprockets/Webpacker/Webpack/Vite support. The host app must use:
140
+
141
+ - **Propshaft** (`config.assets.*` via `propshaft`), and
142
+ - **importmap-rails**.
143
+
144
+ Optional:
145
+
146
+ - **Thruster** in front of the app for edge caching of the fingerprinted assets.
147
+ - Set `config.assets.integrity_hash_algorithm` if you want Subresource Integrity on the
148
+ engine's self-hosted modules (off by default; same-origin hosting makes SRI low-value).
149
+
150
+ ## Browser support
151
+
152
+ The frontend targets the **latest stable Chrome and Safari only**. This fork is an internal
153
+ tool for teams that control their admin users' browsers; the old "Baseline 2023 /
154
+ Firefox ≥ 111" contract is retired deliberately, and no legacy-browser fallbacks ship. The
155
+ engine's CSS and JS assume `:has()`, `<dialog>`, popovers, CSS nesting, `oklch()`,
156
+ `color-mix()`, `light-dark()`, `@starting-style`, and CSS anchor positioning.
157
+
158
+ Two features are newer still: `closedby="any"` (backdrop light-dismissal of the inline-edit
159
+ dialog) needs Chromium 134+ / Safari 26, and CSS anchor positioning needs Chromium 128+ /
160
+ Safari 26 (dropdown menus are positioned by anchor CSS alone — no JS fallback ships).
161
+ `field-sizing: content` (auto-growing textareas) is a Chromium-only enhancement — other
162
+ browsers keep fixed-height textareas.
163
+
164
+ ## Styling: cascade layers and design tokens
165
+
166
+ Bootstrap's SCSS is gone. Styling is hand-maintained vanilla CSS delivered through three
167
+ cascade layers, declared in `rails_admin.css` (the layer names are a documented contract):
168
+
169
+ - `ra.tokens` — design tokens (CSS custom properties)
170
+ - `ra.framework` — the trimmed ex-Bootstrap component CSS
171
+ - `ra.skin` — the engine's own widgets, icons, and admin skin
172
+
173
+ Because all engine CSS is layered, **any unlayered host rule beats it** — a plain rule in
174
+ your own stylesheet overrides the admin without `!important` or specificity games:
175
+
176
+ ```css
177
+ /* host stylesheet — no layer, so it wins over everything the engine ships */
178
+ .badge {
179
+ border-radius: 7px;
180
+ }
181
+ ```
182
+
183
+ ### Supported override tokens (public API)
184
+
185
+ Hosts may override these tokens (unlayered, e.g. `:root { --ra-primary: #663399; }`) and
186
+ every derived color — button hover/active states, focus rings, alert tints, table tints,
187
+ links — follows automatically:
188
+
189
+ | Token | Role |
190
+ | --- | --- |
191
+ | `--ra-primary`, `--ra-secondary`, `--ra-success`, `--ra-info`, `--ra-warning`, `--ra-danger`, `--ra-light`, `--ra-dark` | brand and status colors |
192
+ | `--ra-body-bg`, `--ra-body-color` | body surface and text |
193
+ | `--ra-link-color` | link color (defaults to a tint of `--ra-primary`) |
194
+
195
+ A single-color override applies to both light and dark schemes; hosts that want a tuned dark
196
+ mode should override with their own pair:
197
+
198
+ ```css
199
+ :root {
200
+ --ra-primary: light-dark(#663399, #a97fd6);
201
+ }
202
+ ```
203
+
204
+ **Every other `--ra-*` name is internal and may change without notice.**
205
+
206
+ ### Dark mode and the `color_scheme` knob
207
+
208
+ The admin now follows the OS color-scheme preference automatically: every color token is a
209
+ `light-dark()` pair, resolved against a `<meta name="color-scheme">` the layout renders from
210
+ the new config knob:
211
+
212
+ ```ruby
213
+ RailsAdminNext.config do |config|
214
+ config.color_scheme = :auto # default; :light or :dark pins the scheme
215
+ end
216
+ ```
217
+
218
+ Pin `:light` to keep the old always-light rendering. Two deliberate exceptions: the navbar is
219
+ scheme-locked (`navbar_css_classes` express host intent — `navbar-dark bg-primary` stays a
220
+ dark navbar under both schemes), and the Trix (ActionText) editor stays light-only for now.
221
+
222
+ ### Token mapping: `--bs-*` → `--ra-*`
223
+
224
+ The Bootstrap `--bs-*` custom properties are gone. Renamed with the same meaning — read or
225
+ override the new name:
226
+
227
+ | Old | New | Note |
228
+ | --- | --- | --- |
229
+ | `--bs-body-bg` | `--ra-body-bg` | |
230
+ | `--bs-body-color` | `--ra-body-color` | |
231
+ | `--bs-body-font-family` | `--ra-body-font-family` | |
232
+ | `--bs-body-font-size` | `--ra-body-font-size` | |
233
+ | `--bs-body-font-weight` | `--ra-body-font-weight` | |
234
+ | `--bs-body-line-height` | `--ra-body-line-height` | |
235
+ | `--bs-body-text-align` | `--ra-body-text-align` | still undeclared by default; set it to opt in, exactly like Bootstrap's knob |
236
+ | `--bs-breadcrumb-divider` | `--ra-breadcrumb-divider` | |
237
+ | `--bs-font-monospace` | `--ra-font-monospace` | |
238
+ | `--bs-font-sans-serif` | `--ra-font-sans-serif` | |
239
+ | `--bs-gradient` | `--ra-gradient` | |
240
+ | `--bs-gutter-x` / `--bs-gutter-y` | `--ra-gutter-x` / `--ra-gutter-y` | grid-scoped |
241
+ | `--bs-table-bg` | `--ra-table-bg` | table-scoped; likewise `-accent-bg`, `-striped-bg`, `-striped-color`, `-active-bg`, `-active-color`, `-hover-bg`, `-hover-color` |
242
+
243
+ Renamed with a value-space change (number → percentage):
244
+
245
+ | Old | New |
246
+ | --- | --- |
247
+ | `--bs-bg-opacity: 0.5` | `--ra-bg-opacity: 50%` |
248
+ | `--bs-text-opacity: 0.5` | `--ra-text-opacity: 50%` |
249
+
250
+ Previously declared but consumed by nothing — now live semantic tokens (overriding the
251
+ `--bs-*` name never did anything; the `--ra-*` name works): `--bs-primary` →
252
+ `--ra-primary`, `--bs-secondary` → `--ra-secondary`, `--bs-success` → `--ra-success`,
253
+ `--bs-info` → `--ra-info`, `--bs-warning` → `--ra-warning`, `--bs-danger` → `--ra-danger`,
254
+ `--bs-light` → `--ra-light`, `--bs-dark` → `--ra-dark`.
255
+
256
+ Removed — replaced by derivation (never override these; override the base token instead):
257
+ the whole `-rgb` family (`--bs-primary-rgb` … `--bs-dark-rgb`, `--bs-white-rgb`,
258
+ `--bs-black-rgb`, `--bs-body-color-rgb`, `--bs-body-bg-rgb`) is gone; utilities now derive
259
+ translucency via `color-mix(in srgb, var(--ra-*) <pct>, transparent)`.
260
+
261
+ Removed without replacement (declared but never consumed): the raw color ladder
262
+ (`--bs-blue`, `--bs-indigo`, `--bs-purple`, `--bs-pink`, `--bs-red`, `--bs-orange`,
263
+ `--bs-yellow`, `--bs-green`, `--bs-teal`, `--bs-cyan` — the code-pink role of `--bs-pink`
264
+ lives on as the internal `--ra-code-color`), `--bs-white`, `--bs-gray`, `--bs-gray-dark`,
265
+ `--bs-gray-100` … `--bs-gray-900` (the grays the CSS actually uses exist as internal
266
+ `--ra-gray-*` tokens), and `--bs-position` (set by `.dropdown-menu-end` for Bootstrap's
267
+ dropdown JS, which this engine does not ship).
268
+
269
+ ### Removed component CSS
270
+
271
+ The flattened Bootstrap CSS was trimmed to what the engine actually renders. These component
272
+ families were deleted as unreachable:
273
+
274
+ | Removed family | If you relied on it |
275
+ | --- | --- |
276
+ | Carousel | No engine view renders one; ship your own carousel CSS/JS in custom actions. |
277
+ | Tooltip (`.tooltip`, `.bs-tooltip-*`) | Bootstrap tooltips needed Bootstrap JS, which the engine does not ship; use the native `title` attribute or your own library. |
278
+ | Popover component (`.popover`, `.bs-popover-*`) | Same as tooltips. (The native `popover` *attribute* is what the engine now uses for its dropdown menus.) |
279
+ | Toasts | Engine flash messages render as alerts; `alert-*` stays supported. |
280
+ | Offcanvas | Never emitted by engine views. |
281
+ | Accordion | Sidebar groups are native `<details>`/`<summary>` now. |
282
+ | Spinners (`.spinner-border`, `.spinner-grow`) | The engine's loading indicator is the `#loading` badge. |
283
+ | Placeholders (loading skeletons) | Never emitted by engine views. |
284
+ | Floating labels (`.form-floating`) | Engine forms use stacked labels. |
285
+ | `.form-range` | No engine form renders a range input. |
286
+ | Form validation (`.is-valid` / `.is-invalid` / `*-feedback` / `*-tooltip`) | Engine forms render server-side errors through their own `.error` + `.help-inline.text-danger` markup, which is kept. |
287
+ | `.form-check` / `.form-switch` wrapper styling | Checkboxes/radios are native, tinted via `accent-color`; `.form-check-input` itself is still styled. |
288
+ | jQuery-UI chrome | jQuery-UI is gone. |
289
+ | wysihtml5 editor chrome | The editor was dropped; use ActionText/Trix. |
290
+ | `.collapsing` | Bootstrap JS's collapse-transition class; sidebar collapse is a native `<details>`. |
291
+ | `[data-bs-popper]` rules | Popper.js is not shipped. |
292
+ | `.dropup` / `.dropend` / `.dropstart` | Engine menus only drop down (and flip above the toggle automatically near the viewport edge). |
293
+ | Responsive tiers `sm` / `lg` / `xl` / `xxl` | The `.col-sm-*` grid columns engine forms emit survive; the responsive display/spacing/text utilities and `container-*` tiers are gone (a couple of `md` utilities remain). |
294
+ | Print utilities (`.d-print-*`) | Gone; add your own print CSS if you print admin pages. |
295
+
296
+ ### The styled vocabulary (keep-list)
297
+
298
+ The stylesheet keeps a deliberate keep-list even where no engine view currently emits the
299
+ class: vocabularies that are host-facing configuration API remain styled and supported —
300
+
301
+ - `.navbar-light` / `.navbar-dark` — the `navbar_css_classes` value space,
302
+ - `alert-*` — the `flash_alert_class` value space,
303
+ - `bg-*` utilities — badges, indicators, and the host navbar,
304
+ - `.badge` colors,
305
+ - `table-*` contextual rows — `row_css_class` conventions.
306
+
307
+ Class names the stylesheet does not style are out of contract and may stay unstyled.
308
+
309
+ ## Markup changes
310
+
311
+ Three widgets moved from Bootstrap markup to native platform primitives. Hand-rolled host CSS
312
+ or JS that targeted the old DOM needs updating — `.modal.fade`, `.dropdown .show`, and the
313
+ sidebar's `.collapse` no longer appear:
314
+
315
+ | Widget | Was | Now |
316
+ | --- | --- | --- |
317
+ | Inline-edit modal | `.modal.fade` + `.modal-dialog` divs | native `<dialog id="modal" closedby="any">` |
318
+ | Dropdown menus (filters, bulk actions) | `<a role="button">` toggles + `.dropdown .show` | `<button popovertarget>` toggles + `popover="auto"` menus, positioned with CSS anchor positioning |
319
+ | Sidebar navigation groups | `.collapse` + Bootstrap collapse classes | native `<details>`/`<summary>` disclosures |
320
+
321
+ ## Required `belongs_to` associations
322
+
323
+ A `belongs_to` your model actually requires is now rendered as required. Previously only a
324
+ presence or numericality validator on the association or its foreign key was detected, and
325
+ Rails registers the validator it adds for a required `belongs_to` with an `if:` condition that
326
+ the detection skipped.
327
+
328
+ `belongs_to_required_by_default` has been on since `load_defaults 5.0`. On `load_defaults 7.1`
329
+ or later Rails also sets `belongs_to_required_validates_foreign_key = false`, which is what makes
330
+ the presence validator conditional and invisible to the old detection — so this affects every
331
+ plain `belongs_to` whose foreign key carries no validator of its own. For those fields:
332
+
333
+ - the help text reads "Required." instead of "Optional."
334
+ - a plain association select carries `required`, so the browser blocks submission while it is
335
+ empty; polymorphic association fields change help text and filter operators only, because
336
+ their partial does not apply the field's HTML attributes
337
+ - the association select no longer offers its clear-selection entry
338
+ - the index filter loses the "is present" and "is blank" operators
339
+
340
+ An association declared with `default:` is left optional, since Rails fills it in a
341
+ `before_validation` callback. If a model fills the association server-side some other way, the
342
+ browser will now block a form the server would have accepted. Mark the field optional:
343
+
344
+ ```ruby
345
+ RailsAdminNext.config Order do
346
+ edit do
347
+ field :customer do
348
+ optional true
349
+ end
350
+ end
351
+ end
352
+ ```
353
+
354
+ ## Nested forms hide the link back to the parent
355
+
356
+ A nested subform no longer renders the child's association back to the record being edited.
357
+ Editing a `FieldTest` with `has_one :comment, as: :commentable` used to ask, from inside that
358
+ form, which record the comment belonged to — a choice the parent had already made and that the
359
+ nested save overwrote anyway.
360
+
361
+ This widened along with the association's notion of an inverse. `inverse_of` now answers with
362
+ the declared `inverse_of:`, otherwise the inverse ActiveRecord resolves for the reflection,
363
+ otherwise a polymorphic `as:`. It previously answered only the declared option, so a pair that
364
+ declared nothing on either side — the conventional case Rails resolves on its own — reported no
365
+ inverse and the back-reference stayed on screen. Combined with required `belongs_to` detection
366
+ above, that field could be marked required while being one only the nested save can fill.
367
+
368
+ Two consequences worth checking in a host app:
369
+
370
+ - A subform for a pair that relies on Rails' automatic inverse detection now hides one more
371
+ field than before, and its key is no longer permitted inside `<assoc>_attributes[...]` either.
372
+ Only at nested depth: editing the child on its own still permits its own association, which is
373
+ a legitimate way to set it.
374
+ - `field.inverse_of` in your own configuration blocks now returns a symbol for associations
375
+ that previously returned `nil`.
376
+
377
+ **Narrowing the allowlist is a security fix, for collections.** Saving a singular association re-applies
378
+ the owner to the child, so a submitted back-reference never survived there. A collection does not:
379
+ `assign_nested_attributes_for_collection_association` assigns straight onto an already-associated
380
+ record, so a crafted back-reference inside `<assoc>_attributes[n]` moved that child onto a
381
+ different parent — one the submitter may have had no rights over. Dropping the key from the
382
+ allowlist closes it, because it never reaches the assignment.
383
+
384
+ `inverse_of: false` on the association is honoured, as in Rails. To keep the back-reference
385
+ visible — and permitted — for one field without touching the model, disable the parent field's
386
+ inverse. On a collection that re-opens the re-parenting described above, so scope it to the one
387
+ field that needs it:
388
+
389
+ ```ruby
390
+ RailsAdminNext.config FieldTest do
391
+ edit do
392
+ field :comment do
393
+ inverse_of { false }
394
+ end
395
+ end
396
+ end
397
+ ```
398
+
399
+ Note the block: a bare `inverse_of nil` reads the option rather than setting it.
400
+
401
+ ## The show action answers HTML by default
402
+
403
+ `show` used to declare its JSON format before its HTML one, so a request carrying no
404
+ `Accept` header — `curl`, a bare `fetch()`, most HTTP client libraries — arrived as
405
+ `*/*`, matched every registered type, and got JSON back. Every other action answered
406
+ HTML for the same request.
407
+
408
+ It now declares HTML first. A client that explicitly sends `Accept: application/json`,
409
+ or requests the `.json` extension, is unaffected.
410
+
411
+ If you have a script relying on the unheadered JSON response, send the header or use
412
+ `.json`:
413
+
414
+ ```bash
415
+ curl -H 'Accept: application/json' https://example.com/admin/player/1
416
+ curl https://example.com/admin/player/1.json
417
+ ```
418
+
419
+ Note that the JSON branch renders the whole record, including columns the model config
420
+ hides from the show view.
421
+ ## Removed: options deprecated before RailsAdmin 3.0
422
+
423
+ These six were deprecated upstream between 2019-07 and 2022-01 and had no effect for
424
+ years. They are gone rather than carried into this gem's 1.0:
425
+
426
+ | Removed | What to do |
427
+ | --- | --- |
428
+ | `config.total_columns_width=` | Nothing. It had no effect. |
429
+ | `config.sidescroll=` | Nothing. Horizontal scroll with frozen columns is always on. |
430
+ | `list.sidescroll` | Nothing. Same as above. The field-level `sticky?` still controls which columns freeze. |
431
+ | `list.sort_reverse` | Nothing. It had no effect. The field-level `sort_reverse?` is unrelated and unaffected. |
432
+ | `momentjs_format` | Nothing. Date/time fields are native HTML5 inputs and render per the browser locale. |
433
+ | `eager_load?` | Rename to `eager_load`. |
434
+ | `delete_key` (`:multiple_file_upload`) | Rename to `delete_value`. |
435
+
436
+ `register_deprecated_instance_option` itself stays — extensions and host apps use it for
437
+ their own options.
438
+
439
+ ## Dropped support
440
+
441
+ These are intentionally gone — migrate off them before upgrading:
442
+
443
+ - **Mongoid** — RailsAdminNext is ActiveRecord only.
444
+ - **Sprockets / Webpacker / Webpack / Vite** — importmap + Propshaft only.
445
+ - **Standalone WYSIWYG/code editor field types** (`bootstrap-wysihtml5`, `froala`, and the
446
+ cdnjs-backed `:ck_editor` / `:code_mirror` / `:simple_mde`) — they loaded JS/CSS from cdnjs.
447
+ Migrate those fields to `:action_text` (self-hosted Trix rich text) or a plain `:text` textarea
448
+ instead. The engine now has **zero external CDN** usage, default and opt-in.
449
+ - **Non-ActiveStorage attachment libraries** (CarrierWave, Paperclip, Shrine, Dragonfly,
450
+ Refile) — RailsAdminNext detects **ActiveStorage** (and ActionText/Trix) attachments only.
451
+ Migrate file/image columns to ActiveStorage before upgrading. Image variants are processed
452
+ with **libvips** via `image_processing` + `ruby-vips` (set
453
+ `config.active_storage.variant_processor = :vips`); install the libvips system library
454
+ (`brew install vips` / `apt-get install libvips-dev`) — ImageMagick is no longer required.
455
+ - **Configurable app branding (`main_app_name`)** — the navbar brand and page `<title>` are
456
+ hardcoded to "Rails AdminNext"; the config option is gone. Fork
457
+ `app/views/layouts/rails_admin_next/_navigation.html.erb` and `content.html.erb` in your
458
+ host app if you need custom branding.
459
+
460
+ ## History / audit table
461
+
462
+ The gem owns **no** history table. Auditing remains host/PaperTrail territory, so there is
463
+ no gem migration to run — your existing `rails_admin_histories` table (if any) is unaffected.
data/docs/security.md ADDED
@@ -0,0 +1,67 @@
1
+ # Security posture
2
+
3
+ The admin is a privileged surface. This engine ships the request-forgery, framing, and
4
+ destructive-action controls itself; a few things are left for the host to opt into.
5
+
6
+ ## Always on
7
+
8
+ - **CSRF.** `protect_from_forgery(with: :exception)` is hardcoded. A non-GET request without a
9
+ valid token is rejected (`ActionController::InvalidAuthenticityToken` → HTTP 422). There is no
10
+ host-configurable forgery option to weaken it.
11
+ - **Clickjacking.** Every admin response carries `X-Frame-Options: SAMEORIGIN`.
12
+ - **Export.** The CSV/JSON/XML data stream requires a non-GET, CSRF-protected request; `GET` only
13
+ renders the export form, so the table can't be exfiltrated via a crafted link.
14
+ - **Destructive actions.** `delete` and `bulk_delete` render a confirmation page and additionally
15
+ carry `data-turbo-confirm` on the destructive submit (Turbo's confirmation, not `@rails/ujs`).
16
+ - **Mass assignment.** Submitted attributes are sliced to the model config's `visible_fields`
17
+ before `permit!`, at the top level and at every nested depth, regardless of the submitted
18
+ nested-attributes cardinality (hash or array). At nested depth the slice also drops the child's
19
+ link back to the record being edited — the field the subform does not draw — so a crafted
20
+ back-reference cannot move a nested record onto a different parent.
21
+
22
+ ## Content Security Policy (opt-in)
23
+
24
+ The engine enforces **no** CSP by default. When you opt in, it applies the policy per-request to
25
+ admin responses only (never to the host app) and threads a per-request nonce onto the tags it
26
+ emits — the importmap JSON, the import entry point and its module preloads, the index
27
+ column-width `<style>`, and the engine's stylesheet and style-preload links — so a `:self` +
28
+ nonce policy does not block the admin's own assets.
29
+
30
+ ```ruby
31
+ # config/initializers/rails_admin_next.rb
32
+ RailsAdminNext.config do |config|
33
+ config.content_security_policy do |policy|
34
+ policy.default_src :self
35
+ policy.script_src :self
36
+ policy.style_src :self, :unsafe_inline # see note below
37
+ policy.img_src :self, :data
38
+ policy.font_src :self
39
+ policy.connect_src :self
40
+ end
41
+ end
42
+ ```
43
+
44
+ Tune it without breaking pages first by reporting only:
45
+
46
+ ```ruby
47
+ RailsAdminNext.config do |config|
48
+ config.content_security_policy(report_only: true) do |policy|
49
+ policy.default_src :self
50
+ end
51
+ end
52
+ ```
53
+
54
+ > **Note on `style-src`.** The admin still renders five inline `style="…"` attributes, which a CSP
55
+ > nonce cannot cover (nonces apply to `<script>`/`<style>` elements, not style attributes), so a
56
+ > `style-src` directive needs `:unsafe_inline` for now. The engine's own `<style>` element (the
57
+ > index column-width block) is nonced. The remaining attributes:
58
+ >
59
+ > - the `#loading` badge's positioning in the layout, and the delete-notice `<li>` spacing —
60
+ > static values, removable by moving them into the stylesheet;
61
+ > - the file-upload delete toggle's initial `display:none` — conditional initial state, removable
62
+ > by switching to the `hidden` attribute;
63
+ > - the dashboard's `.progress` margin (static) and each progress bar's `width: N%` (a per-row
64
+ > dynamic value — the one that genuinely resists removal; it would need the nonced-`<style>`
65
+ > route or a native `<progress>` element).
66
+ >
67
+ > Once those five are reauthored, `:unsafe_inline` can be dropped.
@@ -0,0 +1,101 @@
1
+ # Upstream parity
2
+
3
+ Assessed through upstream `897ffc13` (2026-09-13). Fork point: `d8e0809e` (2025-09-14).
4
+
5
+ Divergence from the fork point: 787 files, +20,899 / −73,565 lines.
6
+
7
+ ## How this fork tracks upstream
8
+
9
+ RailsAdminNext is a hard fork of [railsadminteam/rails_admin](https://github.com/railsadminteam/rails_admin). It is **never merged or rebased from upstream**, and it cannot be: the fork deleted whole subsystems upstream still ships — the Mongoid adapter, the npm/webpack build, jQuery and `@rails/ujs`, Bootstrap's JavaScript, Appraisal, CarrierWave. Replaying this fork's history onto a moving upstream would conflict with every commit touching any of them, by construction.
10
+
11
+ Instead, upstream work is absorbed by **reimplementation**: read the upstream commit, judge whether the underlying problem exists here, and write the fix in this codebase's terms. A port therefore need not — and often should not — match upstream's diff. Deliberate divergences are recorded below so they are not "corrected" back later.
12
+
13
+ This means `git rev-list --count upstream/master...master` is not a progress measure. It counts commits never merged, so it does not fall when work is absorbed and it grows every time upstream moves. **This ledger is the state.** The watermark above records what has been assessed; the table records the verdict for each commit, including the ones deliberately skipped.
14
+
15
+ ## Verdicts
16
+
17
+ Commits are listed newest first, as `git log` reports them.
18
+
19
+ | upstream | subject | verdict | ours |
20
+ |---|---|---|---|
21
+ | `897ffc13` | Fix _discard leaking into SQL for multi-select enum filters | adapted — server half only; the client half was already stronger here (disabled options), so upstream's JS was deliberately not ported | [#36](https://github.com/6temes/rails_admin_next/pull/36) |
22
+ | `11f214f6` | Remove config options deprecated before RailsAdmin 3.0 | adapted — the deprecator half already shipped; this removed the six options | [#40](https://github.com/6temes/rails_admin_next/pull/40) |
23
+ | `cbea80b1` | Match GET in http_methods case-insensitively | adapted — `Actions::Base#linkable?` | [#38](https://github.com/6temes/rails_admin_next/pull/38) |
24
+ | `9336ec71` | Drop Proc bounds from a field's length validation | adapted — widened beyond upstream to Symbol bounds, which crash identically | [#35](https://github.com/6temes/rails_admin_next/pull/35) |
25
+ | `331841ea` | Remove duplicate global assignment of window.jQuery | n/a — jQuery removed | — |
26
+ | `71852cd8` | Fix malformed rubocop directive comment | n/a — measured: standardrb clean, and no `has_option?` directive here to malform | — |
27
+ | `cfc17e0d` | Pin json to < 3 for the test suite | n/a — dependency churn, Dependabot's job | — |
28
+ | `316c299d` | Change default response format of the show action to HTML | adapted | [#39](https://github.com/6temes/rails_admin_next/pull/39) |
29
+ | `0f4f765e` | Use the same widget which works for nullable booleans also for non nullable | declined — cosmetic consistency change; alters every boolean field in every host app and fixes no defect | — |
30
+ | `495cc864` | Filter ferrum 0.18's console stack-frame log lines | adapted — landed before the cuprite bump that would trigger it | [#37](https://github.com/6temes/rails_admin_next/pull/37) |
31
+ | `2eed28e1` | Fully specify all ESM imports with an extension | n/a — measured: every relative import in `src/` already carries `.js`; browser-native ESM requires it | — |
32
+ | `e8dec57a` | Upgrade @hotwired/turbo-rails to resolve security warning | n/a — turbo comes from the `turbo-rails` gem (2.0.23), which Dependabot reads | — |
33
+ | `07b2066a` | Replace `asset-url` with `url` in Sass files | n/a — no Sass; one hand-owned CSS file | — |
34
+ | `a0946615` | Drop support for Rails 6.x and Ruby 2.5/2.6 | n/a — this fork is Rails ~> 8.1 / Ruby >= 4.0.5 only | — |
35
+ | `0543cb3f` | Revert npm package.json version ahead of actual publish | n/a — no npm package | — |
36
+ | `cf7b112e` | Run Prettier on migrated docs | n/a — upstream's vendored wiki | — |
37
+ | `1c2b92a3` | Rewrite wiki cross-links to relative docs/ links | n/a — upstream's vendored wiki | — |
38
+ | `70ca765e` | Merge wiki history into docs/ | n/a — upstream vendored its GitHub wiki; this fork ships its own docs | — |
39
+ | `0690a5e6` | Start development for 4.0.0 | n/a — upstream release chore | — |
40
+ | `50fbbcd9` | Remove @babel/runtime as a direct dependency | n/a — no npm build here | — |
41
+ | `e4ad87b7` | Replace deprecated jQuery functions with native ones | n/a — jQuery removed | — |
42
+ | `77e5d0bc` | Improve intermittent autocomplete test failures due to stale element | n/a — measured: the widget stamps and aborts its own requests, and each query renders once | [#18](https://github.com/6temes/rails_admin_next/issues/18) |
43
+ | `26bb8763` | Declare JavaScript package is a module | n/a — no npm package | — |
44
+ | `336845f4` | Remove string mutation | adapted | [#16](https://github.com/6temes/rails_admin_next/pull/16) |
45
+ | `b72badeb` | Add a "sass" entry point to package.json | n/a — no npm/webpack build | — |
46
+ | `9b21e3ba` | Update rails/ujs NPM package | n/a — UJS removed, Turbo replaces it | — |
47
+ | `ca2d0954` | Use a non-beta trix package in dummy_app | n/a — trix is self-hosted from `action_text-trix`, not an npm pin | — |
48
+ | `1f681b48` | Add nonce to css/js tags | adapted — `csp_meta_tag` deliberately left out, see below | [#22](https://github.com/6temes/rails_admin_next/pull/22) |
49
+ | `54a23401` | Add belongs_to optional/required support to `required?` | adapted | [#13](https://github.com/6temes/rails_admin_next/pull/13) |
50
+ | `c18592a1` | Draw routes before the suite goes multi-threaded | adapted — premise corrected, see PR | [#17](https://github.com/6temes/rails_admin_next/pull/17) |
51
+ | `91e9cca8` | Resolve the inverse of a polymorphic association | adapted — 3 divergences, see below | [#14](https://github.com/6temes/rails_admin_next/pull/14) |
52
+ | `8bffeb61` | Pin vite-plugin-ruby below 5.2 | n/a — no vite | — |
53
+ | `9f03412d` | Fix RuboCop offense in main_controller_spec | n/a — linting is Standard Ruby | — |
54
+ | `17b64251` | Avoid carrierwave versions that pull in mimemagic | n/a — CarrierWave removed | — |
55
+ | `703e4894` | Add Rails 8.1 support | n/a — this fork is Rails ~> 8.1 only | — |
56
+ | `9881e254` | Add test for editing a nested one widget item | adapted — driven through this fork's markup | [#15](https://github.com/6temes/rails_admin_next/pull/15) |
57
+ | `47d913fe` | Fix flaky locale spec for Datetimepicker widget | n/a — native HTML5 date/time inputs replaced the widget | — |
58
+ | `ec685453` | Don't auto-require the appraisal gem | n/a — Appraisal removed | — |
59
+
60
+ ## Standing divergences
61
+
62
+ Where a port deliberately differs from upstream's own patch. Each is correct *for this fork*; changing one to match upstream would reintroduce a bug.
63
+
64
+ **`Association#inverse_of`** (from `91e9cca8`) resolves the declared `inverse_of:`, then the inverse ActiveRecord resolves for the reflection, then a polymorphic `as:` — and honours `inverse_of: false`. Upstream reads only the declared option and `as`.
65
+
66
+ - The middle step exists because this fork is ActiveRecord-only. Upstream's adapter also serves Mongoid, which resolves no inverse, so `as` is the only source available to both. Without the middle step, a conventional pair that declares nothing on either side reports no inverse and keeps rendering its back-reference.
67
+ - Honouring `inverse_of: false` matters because `false.try(:to_sym)` is `nil`, so upstream's `|| as` silently reinstates an inverse the host explicitly declined.
68
+ - The order is load-bearing. Reading the declared option first is what keeps a polymorphic `belongs_to` that declares an inverse from raising inside Rails' reflection, where the class cannot be computed.
69
+
70
+ **`FormBuilder#nested_field_association?`** (from the same commit) limits its hoisted name-match branch to association fields. Upstream's hoist is unconstrained. A polymorphic `as:` is never validated against the child class, so without the limit a plain column sharing that name can be suppressed from a subform.
71
+
72
+ **`sanitize_params_for!`** drops the parent's inverse from the nested allowlist, which upstream does not do at all — its permitted keys are derived purely from the visible fields. Hiding the back-reference without narrowing the allowlist left a collection subform re-parentable through a crafted `<assoc>_attributes[n]`, since `assign_nested_attributes_for_collection_association` assigns straight onto an already-associated record ([#24](https://github.com/6temes/rails_admin_next/pull/24)).
73
+
74
+ **`Field::Base#valid_length` drops Symbol bounds as well as Proc ones** (from `9336ec71`), and drops them only from the three keys that get compared (`LENGTH_BOUNDS`). Upstream drops every Proc-valued entry and no Symbols at all.
75
+
76
+ - ActiveModel sanctions four bound types and resolves Proc and Symbol against the record at validation time, so neither can be compared against the column limit. Measured here: `minimum: :some_method` raised the same `ArgumentError` upstream's fix addresses for Procs, and a surviving `maximum: :some_method` rendered "Length up to max_len" as help text.
77
+ - The narrowing to three keys exists because `valid_length` is a `register_instance_option` that host apps read. Rejecting every Proc, as upstream does, would also strip `if:`/`unless:` conditions from the hash.
78
+ - `:in` and `:within` need no entry: ActiveModel collapses them to `:minimum`/`:maximum` at validator construction, and a Proc passed as `:in` raises at declaration time.
79
+
80
+ **`StatementBuilder#to_statement` excludes `between` from the `_discard` strip** (from `897ffc13`), and strips only when the array actually contained the sentinel. Upstream strips unconditionally.
81
+
82
+ - `between` reads its value array by index, so removing an element shifts the range ends. Measured: `["ignored", "_discard", "20"]` became a `>= 20` filter where it had been `<= 20`.
83
+ - An array that arrived empty is not an array the strip emptied. A date filter legitimately submits `{v: [], o: "today"}`, carrying its meaning in the operator alone; returning early on any empty array breaks it.
84
+ - Upstream's client-side half is deliberately not ported. `filter_box_controller` here sets `disabled` on the sentinel options in multi-select mode, not merely `hidden`, and a disabled `<option>` is excluded from form submission — a stronger guarantee than upstream's deselect.
85
+
86
+ **The engine's stylesheet and style-preload tags carry the CSP nonce** (from `1f681b48`), but `csp_meta_tag` does not ship. Turbo reads that meta to re-nonce scripts it re-activates, and this engine renders no body script for it to re-activate; the nonce generator here is also per-request random, which a cached Turbo snapshot would carry stale. Adding the meta wants that generator decision alongside it.
87
+
88
+ ## Assessing the next batch
89
+
90
+ ```bash
91
+ git fetch upstream
92
+ git log --oneline --first-parent 897ffc13..upstream/master
93
+ ```
94
+
95
+ `--first-parent` is load-bearing. Upstream folds whole histories in from time to time — `70ca765e` merged its GitHub wiki into `docs/`, and a plain `git log` across it turns an 18-commit batch into 1301, the extra 1283 being someone else's markdown. Check anything that is a merge (`git log -1 --format='%P %s' <sha>`) for what it actually brought in: vendored docs are one row, a merged feature branch needs its commits triaged individually.
96
+
97
+ Triage each commit by the **underlying problem**, not by the API, selector, or library its diff happens to touch. State the problem without naming any of them; if that sentence describes something possible here, the commit is relevant even when its diff is entirely in code this fork deleted.
98
+
99
+ `77e5d0bc` above is the cautionary case, and it cuts both ways. It was first dismissed as jQuery-UI-only, which was the wrong reason — the race it describes is stated in terms of a selector, and dismissing it on that vocabulary is exactly the mistake the rule above exists to prevent. But the CI failure that seemed to confirm the re-triage turned out to be an unrelated bug: an example reading the database before the server had answered ([#23](https://github.com/6temes/rails_admin_next/pull/23)). The widget itself stamps and aborts its requests, and instrumenting it showed one render per query, so the upstream race has no counterpart here.
100
+
101
+ **A coincident failure is not confirmation.** Look past a commit's vocabulary to decide whether it *could* apply, then measure this fork's own code before concluding that it does.
@@ -6,8 +6,8 @@ module RailsAdminNext
6
6
  # the tag's version over these constants at build time, so the in-repo values are only
7
7
  # the floor a local `gem build` produces.
8
8
  MAJOR = 1
9
- MINOR = 1
10
- PATCH = 2
9
+ MINOR = 2
10
+ PATCH = 0
11
11
  PRE = nil
12
12
 
13
13
  class << self
@@ -0,0 +1,103 @@
1
+ ---
2
+ name: rails-admin-next
3
+ description: Configure or extend the rails_admin_next admin in a host Rails app (models, fields, custom actions and field types, CanCanCan/Pundit authorization, PaperTrail history, theming, CSP). Use instead of upstream RailsAdmin knowledge, which is wrong for this fork.
4
+ ---
5
+
6
+ # rails_admin_next
7
+
8
+ rails_admin_next is a hard fork of RailsAdmin: ActiveRecord only, Rails 8.1, importmap + Propshaft, Turbo + Stimulus, no build step. Most of the per-model DSL is unchanged from upstream, but the namespace, the asset stack and the frontend are not. Code written from upstream RailsAdmin memory is usually wrong here.
9
+
10
+ ## Read the installed gem first
11
+
12
+ Every `<gem>/` path below is relative to the directory this prints:
13
+
14
+ ```bash
15
+ bundle info --path rails_admin_next
16
+ ```
17
+
18
+ Read the version the app actually bundles before answering:
19
+
20
+ - `<gem>/docs/UPGRADING.md` lists every difference from upstream RailsAdmin.
21
+ - `<gem>/docs/security.md` covers what the engine enforces and what the host opts into.
22
+ - `<gem>/docs/upstream-parity.md` has a "Standing divergences" section on behavior that deliberately differs from upstream patches.
23
+ - `<gem>/lib/rails_admin_next/config/` is the DSL itself. When this skill and the source disagree, the source wins.
24
+
25
+ The upstream RailsAdmin wiki is still accurate for model, group and field options that the fork leaves unchanged. Check it against the source above before relying on it.
26
+
27
+ ## Naming: what was renamed, what was not
28
+
29
+ Renamed (write the new form):
30
+
31
+ - Ruby namespace `RailsAdminNext`, configured with `RailsAdminNext.config do |config| ... end` in config/initializers/rails_admin_next.rb.
32
+ - Mount: `mount RailsAdminNext::Engine => "/admin", as: "rails_admin_next"`. Keep the `rails_admin_next` alias, because the engine's views call that route proxy.
33
+ - Per-model macro `rails_admin_next do ... end`.
34
+ - Route helpers `rails_admin_next.dashboard_path`, `rails_admin_next_url`, and `bindings[:view].rails_admin_next` inside field blocks.
35
+
36
+ Not renamed (leave as is):
37
+
38
+ - The authorization subject is still `:rails_admin`. CanCanCan abilities grant `can :access, :rails_admin`. Never rewrite it to `:rails_admin_next`, which fails authorization.
39
+ - Action authorization keys (`:dashboard`, `:index`, `:show`, `:new`, `:edit`, `:export`, `:destroy`, `:history`, `:show_in_app`).
40
+ - The `admin.*` I18n namespace.
41
+
42
+ `bin/rails g rails_admin_next:install` writes the initializer and the mount.
43
+
44
+ ## Models, sections and fields
45
+
46
+ Configure a model either inline with the `rails_admin_next do ... end` macro or in the initializer with `config.model "Team" do ... end`. Both take the upstream DSL: `list`, `show`, `edit`, `create`, `update`, `export`, `nested` and `modal` sections, plus `field`, `configure`, `exclude_fields`, `group` and `navigation_icon`. Global options live on `config`, for example `included_models`, `excluded_models`, `default_items_per_page` and `label_methods`.
47
+
48
+ Field types live in `<gem>/lib/rails_admin_next/config/fields/types/`. A custom type subclasses one of them and registers with `RailsAdminNext::Config::Fields::Types.register(MyType)`. Register it under a name of your own, or the registration replaces the built-in type.
49
+
50
+ Read the "Required `belongs_to` associations" and "Nested forms hide the link back to the parent" sections of `<gem>/docs/UPGRADING.md` before changing association fields. Both behave differently from upstream.
51
+
52
+ ## Actions
53
+
54
+ Pick and order actions in the initializer:
55
+
56
+ ```ruby
57
+ RailsAdminNext.config do |config|
58
+ config.actions do
59
+ dashboard
60
+ index
61
+ new
62
+ export
63
+ bulk_delete
64
+ show
65
+ edit
66
+ delete
67
+ show_in_app
68
+ end
69
+ end
70
+ ```
71
+
72
+ A custom action subclasses `RailsAdminNext::Config::Actions::Base`, registers with `RailsAdminNext::Config::Actions.register(self)`, and then appears by name inside `config.actions`. Its options (`member`, `collection`, `http_methods`, `controller`, `link_icon`, `visible?`, `authorization_key`, `template_name` and the rest) are declared in `<gem>/lib/rails_admin_next/config/actions/base.rb`. `<gem>/lib/rails_admin_next/config/actions/show_in_app.rb` is a short example. The action renders the host view app/views/rails_admin_next/main/<template_name>.html.erb.
73
+
74
+ Icons take logical names, never Font Awesome classes: `link_icon :show_in_app` or `navigation_icon :list`. The list is in the "Icons" section of `<gem>/docs/UPGRADING.md`.
75
+
76
+ ## Authorization and history
77
+
78
+ - `config.authorize_with :cancancan` uses the app's `Ability`. Keep the `:rails_admin` subject (see Naming).
79
+ - `config.authorize_with :pundit` calls the record's policy with `<action>?` methods (`index?`, `edit?`, `dashboard?`) and scopes lists with `policy_scope`.
80
+ - `config.authorize_with do ... end` runs a custom block as a before filter.
81
+ - `config.authenticate_with do ... end` and `config.current_user_method do ... end` wire in the app's authentication, such as Devise or Warden.
82
+ - `config.audit_with :paper_trail` turns on the history action. Read the "History / audit table" section of `<gem>/docs/UPGRADING.md` first.
83
+
84
+ ## Frontend
85
+
86
+ - The host app must use Propshaft and importmap-rails. There is no Sprockets, Webpacker, Webpack, Vite or build step, and there is no `config.asset_source` option.
87
+ - The engine runs its own Stimulus application, exposed as `window.RailsAdminNext.application`. Register custom controllers on that instance, not on the host's Stimulus app.
88
+ - Add JavaScript to the admin through a host config/importmap.rails_admin.rb. The engine appends it after its own map, so it can add pins or override the entrypoint pin. The engine's pin names are in `<gem>/config/importmap.rails_admin.rb`.
89
+ - There is no jQuery, jQuery UI, Bootstrap JS or `@rails/ujs`. Confirmations use Turbo's `data-turbo-confirm`.
90
+ - Date and time fields are native HTML5 inputs, with no flatpickr. Rich text is ActionText/Trix. There are no CDN editors such as CKEditor, CodeMirror or froala.
91
+ - The admin supports only the latest Chrome and Safari, so modern CSS and DOM APIs are fair game.
92
+
93
+ ## Theming
94
+
95
+ Engine CSS sits in the cascade layers `ra.tokens`, `ra.framework` and `ra.skin`, so any unlayered host rule wins without specificity tricks. Rebrand by overriding `--ra-*` tokens, for example `:root { --ra-primary: #663399; }`. Upstream `--bs-*` variables do nothing here. The supported tokens and the `--bs-*` to `--ra-*` mapping are in the "Styling" section of `<gem>/docs/UPGRADING.md`. Dark mode follows the OS. `config.color_scheme = :light` (or `:dark`, or `:auto`) pins it.
96
+
97
+ ## Security
98
+
99
+ The engine always enforces these, and there is no option to weaken them: CSRF, `X-Frame-Options: SAMEORIGIN`, non-GET export, confirmation on delete, and visible-field slicing of submitted attributes. A Content Security Policy is opt-in through `config.content_security_policy do |policy| ... end`. Pass `report_only: true` while tuning it. Read `<gem>/docs/security.md` before writing the policy. It lists the inline styles that still need `:unsafe_inline` in `style-src`.
100
+
101
+ ## Never suggest
102
+
103
+ Mongoid, `RailsAdmin.config`, `rails_admin do` blocks, Font Awesome icon classes, jQuery plugins, Sprockets or Webpacker setup, or a migration from upstream RailsAdmin. The migration is a one-time rename, and `<gem>/docs/UPGRADING.md` covers it.
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails_admin_next
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.2
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel López Prat
@@ -189,6 +189,9 @@ files:
189
189
  - config/initializers/active_record_extensions.rb
190
190
  - config/locales/rails_admin_next.en.yml
191
191
  - config/routes.rb
192
+ - docs/UPGRADING.md
193
+ - docs/security.md
194
+ - docs/upstream-parity.md
192
195
  - lib/generators/rails_admin_next/install_generator.rb
193
196
  - lib/generators/rails_admin_next/templates/initializer.erb
194
197
  - lib/generators/rails_admin_next/utils.rb
@@ -297,6 +300,7 @@ files:
297
300
  - lib/rails_admin_next/support/paginated_collection.rb
298
301
  - lib/rails_admin_next/version.rb
299
302
  - lib/tasks/rails_admin.rake
303
+ - skills/rails-admin-next/SKILL.md
300
304
  - src/rails_admin/application.js
301
305
  - src/rails_admin/base.js
302
306
  - src/rails_admin/controllers/alert_controller.js
@@ -324,6 +328,7 @@ licenses:
324
328
  metadata:
325
329
  bug_tracker_uri: https://github.com/6temes/rails_admin_next/issues
326
330
  changelog_uri: https://github.com/6temes/rails_admin_next/releases
331
+ hyperdrive_targets: rails_admin_next
327
332
  rubygems_mfa_required: 'true'
328
333
  source_code_uri: https://github.com/6temes/rails_admin_next
329
334
  rdoc_options: []