layered-resource-rails 0.4.0 → 0.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 +4 -4
- data/.claude/skills/layered-resource-rails/SKILL.md +2 -2
- data/CHANGELOG.md +16 -0
- data/README.md +5 -5
- data/lib/layered/resource/base.rb +45 -18
- data/lib/layered/resource/version.rb +1 -1
- metadata +93 -21
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: dd456a360f63f94c4c38e833251b2d4b22a20c7df76b2b78612d9baf55135e9c
|
|
4
|
+
data.tar.gz: 783f255e36a42959f5565862cf2354d5a57cb1ec5e2d98fbf281477613446c43
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e29f1a82da384c20cf1d2b99793fedd5079d129a3767cd0c18ba81a55b41b4cf7621c9d0528d992239dc75480607ec0702bb6c70f2562660bb5a063d4faa33d6
|
|
7
|
+
data.tar.gz: 6b144d42bb808d4ef5cab32ed5119ec6a976046e4dd82b9d3fac221c2aacfd2015985f0428f1011493a2bd46289e75876b44bce62e1c69b2f22cb42f0c2e1ff7
|
|
@@ -178,7 +178,7 @@ Inference by column type: `enum`/`belongs_to` -> multi-select (`_in`; `multiple:
|
|
|
178
178
|
|
|
179
179
|
**Long option lists switch control.** A select-type filter renders as the plain list (multi-select: checkboxes + Apply; single-choice: instant-apply links) up to `Layered::Resource.filter_combobox_threshold` options (10), and as a type-ahead **combobox** past it — a checkbox list of every user is no way to pick one. A single-choice combobox posts a scalar (no `[]`), so `_eq` gets the value rather than an array. The count is taken per request, after a `collection:` callable resolves, so the control follows the data; the decision lives in `layered_filter_control` (the view helper), not in `resolved_filters`, for exactly that reason. Declaring `as:` pins the control and opts out: `as: :select` keeps the list however long, `as: :combobox` uses the combobox however short.
|
|
180
180
|
|
|
181
|
-
**Remote options.** `url:` fetches the options from an endpoint as the user types instead of rendering a collection — such a filter is always a combobox (nothing to render or count). Give it as a callable (`-> { user_options_path }`) so it resolves per request in the view, where route helpers exist. The endpoint is an ordinary host-app action including `Layered::Ui::ComboboxOptions` and returning `l_ui_combobox_options(scope, label:, search:)` — the gem never routes it, so it's authorised however any index is. Since the browser has no collection to look a label up in, an active remote filter's values are labelled server-side from the records (same `name`/`title`/`label`/`email` fallbacks), so the tag reads "User: Alice" not "User: 12"; a `url:` over a plain column has no records and its values label themselves. The label lookup is confined to records the resource's `scope(controller)` references (via the foreign key), so a hand-edited id outside it - e.g. another tenant's user - labels itself instead of leaking a name; the cost is that an in-scope option with no matching records also shows its raw id. `min_chars:` and `text:` pass through too; the write-side options (`create:`, `create_name:`, `reorder:`) deliberately don't — a filter picks among values that already exist.
|
|
181
|
+
**Remote options.** `url:` fetches the options from an endpoint as the user types instead of rendering a collection — such a filter is always a combobox (nothing to render or count). Give it as a callable (`-> { user_options_path }`) so it resolves per request in the view, where route helpers exist. The endpoint is an ordinary host-app action including `Layered::Ui::ComboboxOptions` and returning `l_ui_combobox_options(scope, label:, search:)` — the gem never routes it, so it's authorised however any index is. It searches outside any resource, so the model needs its own `ransackable_attributes` covering the `search:` fields. Since the browser has no collection to look a label up in, an active remote filter's values are labelled server-side from the records (same `name`/`title`/`label`/`email` fallbacks), so the tag reads "User: Alice" not "User: 12"; a `url:` over a plain column has no records and its values label themselves. The label lookup is confined to records the resource's `scope(controller)` references (via the foreign key), so a hand-edited id outside it - e.g. another tenant's user - labels itself instead of leaking a name; the cost is that an in-scope option with no matching records also shows its raw id. `min_chars:` and `text:` pass through too; the write-side options (`create:`, `create_name:`, `reorder:`) deliberately don't — a filter picks among values that already exist.
|
|
182
182
|
|
|
183
183
|
**The blank a combobox posts.** `l_ui_combobox` emits a leading blank hidden input so clearing every token submits an empty collection rather than omitting the key. Ransack prunes blanks from an `_in` array itself, and `layered_filter_query_params` prunes them too — an array left with nothing becomes an explicit `""`, the same shape the Clear link writes, so the tag reads as inactive *and* a `default:` doesn't immediately re-apply.
|
|
184
184
|
|
|
@@ -444,7 +444,7 @@ columns [
|
|
|
444
444
|
|
|
445
445
|
`as: :badge, rounded: true` gives the rounded count pill; `link:` (optional) makes it link to the children's nested index. If the parent doesn't already carry a counter-cache column, add the migration and `counter_cache: true` rather than reaching for `.size`.
|
|
446
446
|
|
|
447
|
-
To make an association searchable, add a Ransack-walk-shaped entry to `search_fields` - e.g. `search_fields [:title, :user_name]` resolves `:user_name` against `belongs_to :user` + `users.name` and joins into the association. The gem scopes the Ransack allowlists per resource: it only responds when the resource class is the auth object (on both the parent and the associated model), so host-app Ransack config is preserved. A walked search field is also *sortable* (`q[s]=user_name asc` orders by `users.name`) because Ransack derives its sort allowlist from the search allowlist. Cross-model sort/filter on associations *not* declared in `search_fields` remains off; to opt in, define `ransackable_associations` on the parent model yourself and allowlist the attributes on the child model - the gem detects the host-defined override and unions with it. The search box placeholder labels a walk as "<association> <attribute>" via each model's `human_attribute_name` - translate `activerecord.attributes.<model>.<attr>` to rename a half, or set `search_placeholder` on the resource to replace the whole string.
|
|
447
|
+
To make an association searchable, add a Ransack-walk-shaped entry to `search_fields` - e.g. `search_fields [:title, :user_name]` resolves `:user_name` against `belongs_to :user` + `users.name` and joins into the association. The gem scopes the Ransack allowlists per resource: it only responds when the resource class is the auth object (on both the parent and the associated model), so host-app Ransack config is preserved. A host-app `Model.ransack` call outside a resource still needs the model's own allowlist: with none defined, the gem's patch raises a "needs explicitly allowlisted" error, as Ransack does for an unconfigured model, rather than allowlisting everything. A walked search field is also *sortable* (`q[s]=user_name asc` orders by `users.name`) because Ransack derives its sort allowlist from the search allowlist. Cross-model sort/filter on associations *not* declared in `search_fields` remains off; to opt in, define `ransackable_associations` on the parent model yourself and allowlist the attributes on the child model - the gem detects the host-defined override and unions with it. The search box placeholder labels a walk as "<association> <attribute>" via each model's `human_attribute_name` - translate `activerecord.attributes.<model>.<attr>` to rename a half, or set `search_placeholder` on the resource to replace the whole string.
|
|
448
448
|
|
|
449
449
|
## Common issues
|
|
450
450
|
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. This project follows [Semantic Versioning](https://semver.org/).
|
|
4
4
|
|
|
5
|
+
## [0.5.0] - 2026-11-11
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- The "needs explicitly allowlisted" error raised for a configured model with no allowlist of its own now names the class being searched. An STI subclass of a configured model was reported as its parent
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
|
|
13
|
+
- Refresh the development/test bundle to Rails 8.1.4, layered-ui-rails 0.28.1, Pagy 43.8.1, concurrent-ruby 1.3.8, SQLite 2.10.2, and compatible transitive updates.
|
|
14
|
+
- Raise runtime and development dependency minimums to the versions verified by the suite, retaining their current major-version ranges. Ruby >= 3.3 and Rails ~> 8.0 support are unchanged; Rails keeps its existing minimum so Rails 8.0 apps remain supported.
|
|
15
|
+
|
|
16
|
+
### Security
|
|
17
|
+
|
|
18
|
+
- Require Ransack >= 4.4.4 within the existing 4.x range, including the sort-link reflected XSS and open redirect fix for GHSA-58rx-96gf-w92x.
|
|
19
|
+
- A model the gem configures for Ransack no longer becomes fully searchable by the rest of the app. The scoped `ransackable_attributes`/`ransackable_associations` fell through to Ransack's builtin for any other caller. Because the override counted as an explicit definition, Ransack logged a deprecation warning and allowlisted every column and association, including fields like `encrypted_password`. Where the host app hasn't defined its own allowlist, another layered resource now gets an empty list, and any other caller gets the same "needs explicitly allowlisted" error Ransack raises for an unconfigured model. A host app that searched such a model outside a resource, and relied on the accidental widening, now needs to define `ransackable_attributes`/`ransackable_associations` on it
|
|
20
|
+
|
|
5
21
|
## [0.4.0] - 2026-10-04
|
|
6
22
|
|
|
7
23
|
### Added
|
data/README.md
CHANGED
|
@@ -45,9 +45,9 @@ curl -fsSL https://raw.githubusercontent.com/layered-ai-public/layered-resource-
|
|
|
45
45
|
## Requirements
|
|
46
46
|
|
|
47
47
|
- Ruby on Rails >= 8.0
|
|
48
|
-
- [layered-ui-rails](https://github.com/layered-ai-public/layered-ui-rails) ~> 0.
|
|
49
|
-
- Ransack ~> 4.
|
|
50
|
-
- Pagy ~> 43.
|
|
48
|
+
- [layered-ui-rails](https://github.com/layered-ai-public/layered-ui-rails) ~> 0.28, >= 0.28.1
|
|
49
|
+
- Ransack ~> 4.4, >= 4.4.4
|
|
50
|
+
- Pagy ~> 43.8, >= 43.8.1
|
|
51
51
|
|
|
52
52
|
## Getting started
|
|
53
53
|
|
|
@@ -386,7 +386,7 @@ class PostResource < Layered::Resource::Base
|
|
|
386
386
|
end
|
|
387
387
|
```
|
|
388
388
|
|
|
389
|
-
The Ransack allowlists are scoped to the resource: the association and the associated model's attribute are only ransackable when this resource is the one searching, so nothing else gains access to the associated model and any host-app Ransack config is preserved.
|
|
389
|
+
The Ransack allowlists are scoped to the resource: the association and the associated model's attribute are only ransackable when this resource is the one searching, so nothing else gains access to the associated model and any host-app Ransack config is preserved. A model the gem configures isn't opened up for anything else either: other Ransack calls still go through the model's own `ransackable_attributes`/`ransackable_associations`, and if it doesn't define them, the gem raises a "needs explicitly allowlisted" error, as Ransack does for a model with no allowlist.
|
|
390
390
|
|
|
391
391
|
A walked search field is also sortable — `q[s]=user_name asc` orders the index by `users.name` — because Ransack derives its sort allowlist from the search allowlist. Associations not declared in `search_fields` stay unsearchable and unsortable.
|
|
392
392
|
|
|
@@ -562,7 +562,7 @@ class PostResource < Layered::Resource::Base
|
|
|
562
562
|
end
|
|
563
563
|
```
|
|
564
564
|
|
|
565
|
-
Give `url:` as a callable so it resolves per request in the view, where route helpers are available. The endpoint is an ordinary action in your app — the gem never routes it — so it's authorised however any index is:
|
|
565
|
+
Give `url:` as a callable so it resolves per request in the view, where route helpers are available. The endpoint is an ordinary action in your app — the gem never routes it — so it's authorised however any index is. It searches with Ransack outside any resource, so the model it searches needs its own `ransackable_attributes` covering the `search:` fields:
|
|
566
566
|
|
|
567
567
|
```ruby
|
|
568
568
|
class UserOptionsController < ApplicationController
|
|
@@ -401,24 +401,46 @@ module Layered
|
|
|
401
401
|
# `m`. The overrides only answer when the `auth_object` is a layered
|
|
402
402
|
# resource that references `m` — either as its own model, or via an
|
|
403
403
|
# association-walking search field. Every other caller falls through
|
|
404
|
-
# to the
|
|
405
|
-
#
|
|
404
|
+
# to the host's own allowlist when it has one. When it doesn't, a
|
|
405
|
+
# layered resource gets nothing and anyone else gets the error Ransack
|
|
406
|
+
# raises for an unconfigured model. Delegating to Ransack's builtin
|
|
407
|
+
# instead would be deprecated, and since these overrides count as an
|
|
408
|
+
# explicit definition it would allowlist every column — so patching a
|
|
409
|
+
# model must not widen what the rest of the app can search.
|
|
406
410
|
def patch_ransack(m)
|
|
407
411
|
return if m.instance_variable_get(:@_layered_resource_ransack_configured)
|
|
408
412
|
|
|
409
413
|
original_attributes = m.method(:ransackable_attributes)
|
|
410
414
|
original_associations = m.method(:ransackable_associations)
|
|
411
415
|
# Detect a host-defined override anywhere in the model's singleton
|
|
412
|
-
# ancestry
|
|
413
|
-
#
|
|
414
|
-
#
|
|
415
|
-
#
|
|
416
|
-
host_attributes_defined = original_attributes.owner
|
|
417
|
-
host_associations_defined = original_associations.owner
|
|
416
|
+
# ancestry: a `def self.x` in Post or ApplicationRecord, or a module
|
|
417
|
+
# brought in with `extend` or a concern's `class_methods`. Only
|
|
418
|
+
# Ransack's own default (the module it extends ActiveRecord::Base
|
|
419
|
+
# with) counts as "no allowlist".
|
|
420
|
+
host_attributes_defined = original_attributes.owner != ::Ransack::Adapters::ActiveRecord::Base
|
|
421
|
+
host_associations_defined = original_associations.owner != ::Ransack::Adapters::ActiveRecord::Base
|
|
422
|
+
fallback_attributes = ->(klass, auth_object) do
|
|
423
|
+
if host_attributes_defined
|
|
424
|
+
original_attributes.call(auth_object)
|
|
425
|
+
elsif auth_object.is_a?(Class) && auth_object < Layered::Resource::Base
|
|
426
|
+
[]
|
|
427
|
+
else
|
|
428
|
+
Layered::Resource::Base.send(:raise_unconfigured_ransack, klass, :ransackable_attributes)
|
|
429
|
+
end
|
|
430
|
+
end
|
|
431
|
+
fallback_associations = ->(klass, auth_object) do
|
|
432
|
+
if host_associations_defined
|
|
433
|
+
original_associations.call(auth_object)
|
|
434
|
+
elsif auth_object.is_a?(Class) && auth_object < Layered::Resource::Base
|
|
435
|
+
[]
|
|
436
|
+
else
|
|
437
|
+
Layered::Resource::Base.send(:raise_unconfigured_ransack, klass, :ransackable_associations)
|
|
438
|
+
end
|
|
439
|
+
end
|
|
418
440
|
|
|
419
441
|
m.define_singleton_method(:ransackable_attributes) do |auth_object = nil|
|
|
420
442
|
unless auth_object.is_a?(Class) && auth_object < Layered::Resource::Base
|
|
421
|
-
next
|
|
443
|
+
next fallback_attributes.call(self, auth_object)
|
|
422
444
|
end
|
|
423
445
|
|
|
424
446
|
if auth_object.model == self
|
|
@@ -452,12 +474,7 @@ module Layered
|
|
|
452
474
|
declared = auth_object.association_search_fields
|
|
453
475
|
.select { |a| a[:klass] == self }
|
|
454
476
|
.map { |a| a[:attribute] }
|
|
455
|
-
|
|
456
|
-
base = host_attributes_defined ? original_attributes.call(auth_object) : []
|
|
457
|
-
base | declared
|
|
458
|
-
else
|
|
459
|
-
original_attributes.call(auth_object)
|
|
460
|
-
end
|
|
477
|
+
fallback_attributes.call(self, auth_object) | declared
|
|
461
478
|
end
|
|
462
479
|
end
|
|
463
480
|
|
|
@@ -468,16 +485,26 @@ module Layered
|
|
|
468
485
|
# rather than 500ing.
|
|
469
486
|
m.define_singleton_method(:ransackable_associations) do |auth_object = nil|
|
|
470
487
|
if auth_object.is_a?(Class) && auth_object < Layered::Resource::Base && auth_object.model == self
|
|
471
|
-
|
|
472
|
-
base | auth_object.association_search_fields.map { |a| a[:association] }
|
|
488
|
+
fallback_associations.call(self, auth_object) | auth_object.association_search_fields.map { |a| a[:association] }
|
|
473
489
|
else
|
|
474
|
-
|
|
490
|
+
fallback_associations.call(self, auth_object)
|
|
475
491
|
end
|
|
476
492
|
end
|
|
477
493
|
|
|
478
494
|
m.instance_variable_set(:@_layered_resource_ransack_configured, true)
|
|
479
495
|
end
|
|
480
496
|
|
|
497
|
+
# Mirrors Ransack 4's refusal to search a model that hasn't
|
|
498
|
+
# allowlisted anything, for callers outside the gem.
|
|
499
|
+
def raise_unconfigured_ransack(m, method)
|
|
500
|
+
raise <<~MESSAGE.squish
|
|
501
|
+
Ransack needs #{m.name} #{method.to_s.delete_prefix("ransackable_")} explicitly
|
|
502
|
+
allowlisted as searchable. Define `#{m.name}.#{method}(auth_object = nil)`,
|
|
503
|
+
leaving out anything you don't want searchable. layered-resource-rails
|
|
504
|
+
only allowlists for its own resources.
|
|
505
|
+
MESSAGE
|
|
506
|
+
end
|
|
507
|
+
|
|
481
508
|
# Walks the resource class ancestry to find the first ancestor that
|
|
482
509
|
# has the given ivar set. Class-level ivars are not inherited in Ruby,
|
|
483
510
|
# so we explicitly walk to give subclasses access to a parent's
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: layered-resource-rails
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- layered.ai
|
|
@@ -13,44 +13,62 @@ dependencies:
|
|
|
13
13
|
name: concurrent-ruby
|
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|
|
15
15
|
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '1.3'
|
|
16
19
|
- - ">="
|
|
17
20
|
- !ruby/object:Gem::Version
|
|
18
|
-
version:
|
|
21
|
+
version: 1.3.8
|
|
19
22
|
type: :runtime
|
|
20
23
|
prerelease: false
|
|
21
24
|
version_requirements: !ruby/object:Gem::Requirement
|
|
22
25
|
requirements:
|
|
26
|
+
- - "~>"
|
|
27
|
+
- !ruby/object:Gem::Version
|
|
28
|
+
version: '1.3'
|
|
23
29
|
- - ">="
|
|
24
30
|
- !ruby/object:Gem::Version
|
|
25
|
-
version:
|
|
31
|
+
version: 1.3.8
|
|
26
32
|
- !ruby/object:Gem::Dependency
|
|
27
33
|
name: layered-ui-rails
|
|
28
34
|
requirement: !ruby/object:Gem::Requirement
|
|
29
35
|
requirements:
|
|
30
36
|
- - "~>"
|
|
31
37
|
- !ruby/object:Gem::Version
|
|
32
|
-
version: '0.
|
|
38
|
+
version: '0.28'
|
|
39
|
+
- - ">="
|
|
40
|
+
- !ruby/object:Gem::Version
|
|
41
|
+
version: 0.28.1
|
|
33
42
|
type: :runtime
|
|
34
43
|
prerelease: false
|
|
35
44
|
version_requirements: !ruby/object:Gem::Requirement
|
|
36
45
|
requirements:
|
|
37
46
|
- - "~>"
|
|
38
47
|
- !ruby/object:Gem::Version
|
|
39
|
-
version: '0.
|
|
48
|
+
version: '0.28'
|
|
49
|
+
- - ">="
|
|
50
|
+
- !ruby/object:Gem::Version
|
|
51
|
+
version: 0.28.1
|
|
40
52
|
- !ruby/object:Gem::Dependency
|
|
41
53
|
name: pagy
|
|
42
54
|
requirement: !ruby/object:Gem::Requirement
|
|
43
55
|
requirements:
|
|
44
56
|
- - "~>"
|
|
45
57
|
- !ruby/object:Gem::Version
|
|
46
|
-
version: '43.
|
|
58
|
+
version: '43.8'
|
|
59
|
+
- - ">="
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: 43.8.1
|
|
47
62
|
type: :runtime
|
|
48
63
|
prerelease: false
|
|
49
64
|
version_requirements: !ruby/object:Gem::Requirement
|
|
50
65
|
requirements:
|
|
51
66
|
- - "~>"
|
|
52
67
|
- !ruby/object:Gem::Version
|
|
53
|
-
version: '43.
|
|
68
|
+
version: '43.8'
|
|
69
|
+
- - ">="
|
|
70
|
+
- !ruby/object:Gem::Version
|
|
71
|
+
version: 43.8.1
|
|
54
72
|
- !ruby/object:Gem::Dependency
|
|
55
73
|
name: rails
|
|
56
74
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -71,14 +89,20 @@ dependencies:
|
|
|
71
89
|
requirements:
|
|
72
90
|
- - "~>"
|
|
73
91
|
- !ruby/object:Gem::Version
|
|
74
|
-
version: '4.
|
|
92
|
+
version: '4.4'
|
|
93
|
+
- - ">="
|
|
94
|
+
- !ruby/object:Gem::Version
|
|
95
|
+
version: 4.4.4
|
|
75
96
|
type: :runtime
|
|
76
97
|
prerelease: false
|
|
77
98
|
version_requirements: !ruby/object:Gem::Requirement
|
|
78
99
|
requirements:
|
|
79
100
|
- - "~>"
|
|
80
101
|
- !ruby/object:Gem::Version
|
|
81
|
-
version: '4.
|
|
102
|
+
version: '4.4'
|
|
103
|
+
- - ">="
|
|
104
|
+
- !ruby/object:Gem::Version
|
|
105
|
+
version: 4.4.4
|
|
82
106
|
- !ruby/object:Gem::Dependency
|
|
83
107
|
name: devise
|
|
84
108
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -86,6 +110,9 @@ dependencies:
|
|
|
86
110
|
- - "~>"
|
|
87
111
|
- !ruby/object:Gem::Version
|
|
88
112
|
version: '5.0'
|
|
113
|
+
- - ">="
|
|
114
|
+
- !ruby/object:Gem::Version
|
|
115
|
+
version: 5.0.4
|
|
89
116
|
type: :development
|
|
90
117
|
prerelease: false
|
|
91
118
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -93,90 +120,129 @@ dependencies:
|
|
|
93
120
|
- - "~>"
|
|
94
121
|
- !ruby/object:Gem::Version
|
|
95
122
|
version: '5.0'
|
|
123
|
+
- - ">="
|
|
124
|
+
- !ruby/object:Gem::Version
|
|
125
|
+
version: 5.0.4
|
|
96
126
|
- !ruby/object:Gem::Dependency
|
|
97
127
|
name: importmap-rails
|
|
98
128
|
requirement: !ruby/object:Gem::Requirement
|
|
99
129
|
requirements:
|
|
100
130
|
- - "~>"
|
|
101
131
|
- !ruby/object:Gem::Version
|
|
102
|
-
version: '2.
|
|
132
|
+
version: '2.2'
|
|
133
|
+
- - ">="
|
|
134
|
+
- !ruby/object:Gem::Version
|
|
135
|
+
version: 2.2.3
|
|
103
136
|
type: :development
|
|
104
137
|
prerelease: false
|
|
105
138
|
version_requirements: !ruby/object:Gem::Requirement
|
|
106
139
|
requirements:
|
|
107
140
|
- - "~>"
|
|
108
141
|
- !ruby/object:Gem::Version
|
|
109
|
-
version: '2.
|
|
142
|
+
version: '2.2'
|
|
143
|
+
- - ">="
|
|
144
|
+
- !ruby/object:Gem::Version
|
|
145
|
+
version: 2.2.3
|
|
110
146
|
- !ruby/object:Gem::Dependency
|
|
111
147
|
name: propshaft
|
|
112
148
|
requirement: !ruby/object:Gem::Requirement
|
|
113
149
|
requirements:
|
|
114
150
|
- - "~>"
|
|
115
151
|
- !ruby/object:Gem::Version
|
|
116
|
-
version: '1.
|
|
152
|
+
version: '1.3'
|
|
153
|
+
- - ">="
|
|
154
|
+
- !ruby/object:Gem::Version
|
|
155
|
+
version: 1.3.2
|
|
117
156
|
type: :development
|
|
118
157
|
prerelease: false
|
|
119
158
|
version_requirements: !ruby/object:Gem::Requirement
|
|
120
159
|
requirements:
|
|
121
160
|
- - "~>"
|
|
122
161
|
- !ruby/object:Gem::Version
|
|
123
|
-
version: '1.
|
|
162
|
+
version: '1.3'
|
|
163
|
+
- - ">="
|
|
164
|
+
- !ruby/object:Gem::Version
|
|
165
|
+
version: 1.3.2
|
|
124
166
|
- !ruby/object:Gem::Dependency
|
|
125
167
|
name: pundit
|
|
126
168
|
requirement: !ruby/object:Gem::Requirement
|
|
127
169
|
requirements:
|
|
128
170
|
- - "~>"
|
|
129
171
|
- !ruby/object:Gem::Version
|
|
130
|
-
version: '2.
|
|
172
|
+
version: '2.5'
|
|
173
|
+
- - ">="
|
|
174
|
+
- !ruby/object:Gem::Version
|
|
175
|
+
version: 2.5.2
|
|
131
176
|
type: :development
|
|
132
177
|
prerelease: false
|
|
133
178
|
version_requirements: !ruby/object:Gem::Requirement
|
|
134
179
|
requirements:
|
|
135
180
|
- - "~>"
|
|
136
181
|
- !ruby/object:Gem::Version
|
|
137
|
-
version: '2.
|
|
182
|
+
version: '2.5'
|
|
183
|
+
- - ">="
|
|
184
|
+
- !ruby/object:Gem::Version
|
|
185
|
+
version: 2.5.2
|
|
138
186
|
- !ruby/object:Gem::Dependency
|
|
139
187
|
name: puma
|
|
140
188
|
requirement: !ruby/object:Gem::Requirement
|
|
141
189
|
requirements:
|
|
142
190
|
- - "~>"
|
|
143
191
|
- !ruby/object:Gem::Version
|
|
144
|
-
version: '7.
|
|
192
|
+
version: '7.2'
|
|
193
|
+
- - ">="
|
|
194
|
+
- !ruby/object:Gem::Version
|
|
195
|
+
version: 7.2.1
|
|
145
196
|
type: :development
|
|
146
197
|
prerelease: false
|
|
147
198
|
version_requirements: !ruby/object:Gem::Requirement
|
|
148
199
|
requirements:
|
|
149
200
|
- - "~>"
|
|
150
201
|
- !ruby/object:Gem::Version
|
|
151
|
-
version: '7.
|
|
202
|
+
version: '7.2'
|
|
203
|
+
- - ">="
|
|
204
|
+
- !ruby/object:Gem::Version
|
|
205
|
+
version: 7.2.1
|
|
152
206
|
- !ruby/object:Gem::Dependency
|
|
153
207
|
name: sqlite3
|
|
154
208
|
requirement: !ruby/object:Gem::Requirement
|
|
155
209
|
requirements:
|
|
156
210
|
- - "~>"
|
|
157
211
|
- !ruby/object:Gem::Version
|
|
158
|
-
version: '2.
|
|
212
|
+
version: '2.10'
|
|
213
|
+
- - ">="
|
|
214
|
+
- !ruby/object:Gem::Version
|
|
215
|
+
version: 2.10.2
|
|
159
216
|
type: :development
|
|
160
217
|
prerelease: false
|
|
161
218
|
version_requirements: !ruby/object:Gem::Requirement
|
|
162
219
|
requirements:
|
|
163
220
|
- - "~>"
|
|
164
221
|
- !ruby/object:Gem::Version
|
|
165
|
-
version: '2.
|
|
222
|
+
version: '2.10'
|
|
223
|
+
- - ">="
|
|
224
|
+
- !ruby/object:Gem::Version
|
|
225
|
+
version: 2.10.2
|
|
166
226
|
- !ruby/object:Gem::Dependency
|
|
167
227
|
name: stimulus-rails
|
|
168
228
|
requirement: !ruby/object:Gem::Requirement
|
|
169
229
|
requirements:
|
|
170
230
|
- - "~>"
|
|
171
231
|
- !ruby/object:Gem::Version
|
|
172
|
-
version: '1.
|
|
232
|
+
version: '1.3'
|
|
233
|
+
- - ">="
|
|
234
|
+
- !ruby/object:Gem::Version
|
|
235
|
+
version: 1.3.4
|
|
173
236
|
type: :development
|
|
174
237
|
prerelease: false
|
|
175
238
|
version_requirements: !ruby/object:Gem::Requirement
|
|
176
239
|
requirements:
|
|
177
240
|
- - "~>"
|
|
178
241
|
- !ruby/object:Gem::Version
|
|
179
|
-
version: '1.
|
|
242
|
+
version: '1.3'
|
|
243
|
+
- - ">="
|
|
244
|
+
- !ruby/object:Gem::Version
|
|
245
|
+
version: 1.3.4
|
|
180
246
|
- !ruby/object:Gem::Dependency
|
|
181
247
|
name: turbo-rails
|
|
182
248
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -184,6 +250,9 @@ dependencies:
|
|
|
184
250
|
- - "~>"
|
|
185
251
|
- !ruby/object:Gem::Version
|
|
186
252
|
version: '2.0'
|
|
253
|
+
- - ">="
|
|
254
|
+
- !ruby/object:Gem::Version
|
|
255
|
+
version: 2.0.23
|
|
187
256
|
type: :development
|
|
188
257
|
prerelease: false
|
|
189
258
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -191,6 +260,9 @@ dependencies:
|
|
|
191
260
|
- - "~>"
|
|
192
261
|
- !ruby/object:Gem::Version
|
|
193
262
|
version: '2.0'
|
|
263
|
+
- - ">="
|
|
264
|
+
- !ruby/object:Gem::Version
|
|
265
|
+
version: 2.0.23
|
|
194
266
|
description: A convention-over-configuration CRUD engine for Rails 8+, built on the
|
|
195
267
|
layered-ui-rails gem. Declare a resource class, mount it in routes.rb, and get index,
|
|
196
268
|
new/create, edit/update, and destroy actions with clean default views, Ransack-powered
|