recourse 7.11.2 → 7.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +20 -0
- data/app/controllers/concerns/recourse/parent_naming.rb +1 -1
- data/app/controllers/concerns/recourse/parent_resolution.rb +1 -1
- data/app/controllers/concerns/recourse/resource_resolution.rb +1 -1
- data/lib/recourse/helpers/bookmarks.rb +1 -1
- data/lib/recourse/helpers/resources.rb +2 -2
- data/lib/recourse/icons.rb +1 -1
- data/lib/recourse/maps.rb +1 -1
- data/lib/recourse/recoursive.rb +1 -1
- data/lib/recourse/routes/nested.rb +1 -1
- data/lib/recourse/titles.rb +1 -1
- data/lib/recourse/version.rb +1 -1
- data/lib/recourse.rb +48 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c3f3474232bc6f128f093102e6695f23e9579ad323bc60bba64fc5bfeb47c5c1
|
|
4
|
+
data.tar.gz: 0ae245a7fd65040853c0ce90112241b19c8a47d053a86d48a513a229e35191ec
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1087e317accf95b332f2e97f7aee06e22275a0129a2d6d7e8d4b3e35a0cbd84a059ce69e9f265559f6ce2d0ade6bd79b344ecb6dfe1feb86c45710da62c6372d
|
|
7
|
+
data.tar.gz: 55486b544450eaf334cd99d00e81fd493139870780fe78c9d2df90299b44d9a9cd2896a3a8c3e4115a4fb8c3a3929481a7390f876dcd8ea4369f9ffcdf5227c3
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,26 @@ For more information about changelogs, check [Keep a Changelog](http://keepachan
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## 7.12.1 - 2026-10-02
|
|
11
|
+
|
|
12
|
+
* [FIX] A model kept in a module is bookmarked and nested under by its route's name
|
|
13
|
+
|
|
14
|
+
The id a bookmark posts and a nested page reads its parent by is named after the route,
|
|
15
|
+
`user_id`, and no longer after the model's whole name, `admin_user_id`, which no route draws.
|
|
16
|
+
A model's default icon and the name its parent is assigned to (`@user`) drop the module too.
|
|
17
|
+
For a model at the top level, all of these read as they did.
|
|
18
|
+
|
|
19
|
+
## 7.12.0 - 2026-10-02
|
|
20
|
+
|
|
21
|
+
* [FEATURE] A route drawn in a module lists the model kept in that module
|
|
22
|
+
|
|
23
|
+
`scope module: :admin { recourses :users }` lists an `Admin::User`, and so does a page
|
|
24
|
+
nested under a post, `admin/posts/users`. Only an Active Record model in the module counts;
|
|
25
|
+
where there is none, the route lists the top-level class of its last word, as before, so
|
|
26
|
+
`admin/comments` still lists a `Comment`. A host keeping models in a module will want
|
|
27
|
+
`def self.use_relative_model_naming? = true` on it, so their routes and params say `user`
|
|
28
|
+
rather than `admin_user`.
|
|
29
|
+
|
|
10
30
|
## 7.11.2 - 2026-09-26
|
|
11
31
|
|
|
12
32
|
* [FIX] The count in a tab is delimited
|
|
@@ -18,7 +18,7 @@ module Recourse
|
|
|
18
18
|
model = path && Recourse.model?(path)
|
|
19
19
|
return unless model && @recourse_parent.is_a?(model)
|
|
20
20
|
|
|
21
|
-
instance_variable_set "@#{model.model_name.
|
|
21
|
+
instance_variable_set "@#{model.model_name.element}", @recourse_parent
|
|
22
22
|
end
|
|
23
23
|
end
|
|
24
24
|
end
|
|
@@ -27,7 +27,7 @@ module Recourse
|
|
|
27
27
|
model = parent && Recourse.model?(parent)
|
|
28
28
|
return unless model
|
|
29
29
|
|
|
30
|
-
id = request.path_parameters[:"#{model.model_name.
|
|
30
|
+
id = request.path_parameters[:"#{model.model_name.singular_route_key}_id"]
|
|
31
31
|
model.find_by id: id if id
|
|
32
32
|
end
|
|
33
33
|
|
|
@@ -78,7 +78,7 @@ module Recourse
|
|
|
78
78
|
# to match it. Private, the way `recourse_relation` beside it is: naming a model
|
|
79
79
|
# adds no action.
|
|
80
80
|
def recourse_model
|
|
81
|
-
Recourse.model
|
|
81
|
+
Recourse.model controller_path
|
|
82
82
|
end
|
|
83
83
|
|
|
84
84
|
# What the page calls one of its rows: the model's own word, or the route's for a
|
|
@@ -83,7 +83,7 @@ module Recourse
|
|
|
83
83
|
|
|
84
84
|
def bookmark_url(record)
|
|
85
85
|
url_for controller: "/#{resource_controller_path}/bookmarks", action: :create,
|
|
86
|
-
"#{resource_model.model_name.
|
|
86
|
+
"#{resource_model.model_name.singular_route_key}_id": record.id
|
|
87
87
|
end
|
|
88
88
|
end
|
|
89
89
|
end
|
|
@@ -15,7 +15,7 @@ module Recourse
|
|
|
15
15
|
def resources_name
|
|
16
16
|
return controller.controller_name.humanize if blob_resource?
|
|
17
17
|
|
|
18
|
-
Recourse.known_title controller.
|
|
18
|
+
Recourse.known_title controller.controller_path
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
# Singular, lowercase name of the resource, e.g. 'contact' — and 'photo' on a page
|
|
@@ -72,7 +72,7 @@ module Recourse
|
|
|
72
72
|
# Resolved by the controller, which is the one that knows whether the name is a
|
|
73
73
|
# model of this app's, something a record has attached, or a model a host named.
|
|
74
74
|
def resource_model
|
|
75
|
-
controller_assign('recourse_model') || Recourse.model(controller.
|
|
75
|
+
controller_assign('recourse_model') || Recourse.model(controller.controller_path)
|
|
76
76
|
end
|
|
77
77
|
end
|
|
78
78
|
end
|
data/lib/recourse/icons.rb
CHANGED
|
@@ -8,7 +8,7 @@ module Recourse
|
|
|
8
8
|
# `Message` may be a host's own class for gathering rows rather than a table of them,
|
|
9
9
|
# and asking it would raise rather than draw nothing.
|
|
10
10
|
def self.known_icon(name)
|
|
11
|
-
model = name
|
|
11
|
+
model = model? name
|
|
12
12
|
|
|
13
13
|
known_model_icon model if model.respond_to? :recourse_icon
|
|
14
14
|
end
|
data/lib/recourse/maps.rb
CHANGED
|
@@ -18,7 +18,7 @@ module Recourse
|
|
|
18
18
|
def self.mappable?(model) = placed?(model) || (POINT_COLUMNS - model.column_names).empty?
|
|
19
19
|
|
|
20
20
|
# The layer a model's places are drawn on, or nil for a model that is no geography.
|
|
21
|
-
def self.boundary(model) = BOUNDARIES[model.model_name.
|
|
21
|
+
def self.boundary(model) = BOUNDARIES[model.model_name.element.to_sym]
|
|
22
22
|
|
|
23
23
|
# Whether a model's rows keep a place ID, which an area or a pin at a place is drawn from.
|
|
24
24
|
def self.placed?(model) = model.column_names.include? PLACE_COLUMN
|
data/lib/recourse/recoursive.rb
CHANGED
|
@@ -10,7 +10,7 @@ module Recourse
|
|
|
10
10
|
# The concept a resource is drawn with, which Unicon names in each icon set it
|
|
11
11
|
# knows. A model's own name by default — `contact` draws a rolodex, `job` a hammer
|
|
12
12
|
# — and Unicon answers with a circle for a name it has never heard of.
|
|
13
|
-
def recourse_icon = model_name.
|
|
13
|
+
def recourse_icon = model_name.element.to_sym
|
|
14
14
|
|
|
15
15
|
# Columns the model keeps off its screens — the table, the show page, the
|
|
16
16
|
# form and the search box — none by default. One name or a list:
|
|
@@ -25,7 +25,7 @@ module Recourse
|
|
|
25
25
|
# `/placeholders/:placeholder_id/bookmark`, where nothing linked to it and
|
|
26
26
|
# anything reaching it raised. Asked of the constant rather than the class:
|
|
27
27
|
# loading a model while the routes draw is what Rails 8.2 warns about.
|
|
28
|
-
def addressable_rows? =
|
|
28
|
+
def addressable_rows? = Recourse.model_defined? declared_path(parent_resource.name)
|
|
29
29
|
|
|
30
30
|
# The place a row of a positioned table holds, at `/teams/5/position`. Recorded
|
|
31
31
|
# nowhere, for the reason the bookmark gives: a tab and a bare-action button both
|
data/lib/recourse/titles.rb
CHANGED
|
@@ -33,7 +33,7 @@ module Recourse
|
|
|
33
33
|
# than a table of them, and asking it would raise rather than read.
|
|
34
34
|
def known_title(name)
|
|
35
35
|
segment = name.to_s.split('/').last
|
|
36
|
-
model =
|
|
36
|
+
model = model? name
|
|
37
37
|
|
|
38
38
|
model.respond_to?(:model_name) ? model_title(model) : segment.humanize
|
|
39
39
|
end
|
data/lib/recourse/version.rb
CHANGED
data/lib/recourse.rb
CHANGED
|
@@ -71,11 +71,58 @@ module Recourse
|
|
|
71
71
|
# action is a verb — `recourse :sweep, only: :create` — and the gem labels its button
|
|
72
72
|
# from the path alone, so whether a name has a model behind it has to be a question
|
|
73
73
|
# and not an accusation.
|
|
74
|
-
def self.model?(name) = model_name(name).safe_constantize
|
|
74
|
+
def self.model?(name) = namespaced_model(name) || model_name(name).safe_constantize
|
|
75
75
|
|
|
76
76
|
# A namespaced resource is `admin/sources`, and the model it lists is a Source.
|
|
77
77
|
def self.model_name(name) = name.to_s.split('/').last.classify
|
|
78
78
|
|
|
79
|
+
# Whether a model answers the name, asked of the constants rather than the classes:
|
|
80
|
+
# the routes ask while they draw, and loading a model then is what Rails 8.2 warns
|
|
81
|
+
# about.
|
|
82
|
+
def self.model_defined?(name)
|
|
83
|
+
namespaced_names(name).any? { |namespaced| constant namespaced, load: false } ||
|
|
84
|
+
Object.const_defined?(model_name(name))
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# A model the app keeps in the route's own module, where there is one: `admin/users`
|
|
88
|
+
# lists an Admin::User, and so does `admin/posts/users`, nested under a post.
|
|
89
|
+
# Only an Active Record model counts, so a concern or a host's own class that shares
|
|
90
|
+
# the name never takes a page over; and where there is none, `admin/comments` lists a
|
|
91
|
+
# Comment as it always has.
|
|
92
|
+
private_class_method def self.namespaced_model(name)
|
|
93
|
+
namespaced_names(name).each do |namespaced|
|
|
94
|
+
model = constant namespaced
|
|
95
|
+
return model if model.is_a?(Class) && model < ActiveRecord::Base
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
nil
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# The names a model in the route's modules goes by, innermost module first. The
|
|
102
|
+
# segment a nested route adds for its parent is a module no model lives in, so the
|
|
103
|
+
# module around it is asked next.
|
|
104
|
+
private_class_method def self.namespaced_names(name)
|
|
105
|
+
*modules, last = name.to_s.split '/'
|
|
106
|
+
modules.size.downto(1).map { |depth| [*modules[0, depth], last].join('/').classify }
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# The constant a name gives, looked up one module at a time and never through Object:
|
|
110
|
+
# `safe_constantize` answers `Admin::Comment` with the top-level Comment, since a module
|
|
111
|
+
# looks its constants up there too. `load: false` stops short of the last one,
|
|
112
|
+
# answering only whether it is there.
|
|
113
|
+
private_class_method def self.constant(name, load: true)
|
|
114
|
+
*modules, last = name.split '::'
|
|
115
|
+
scope = modules.inject Object do |outer, part|
|
|
116
|
+
break unless outer.const_defined? part, false
|
|
117
|
+
|
|
118
|
+
outer.const_get part, false
|
|
119
|
+
end
|
|
120
|
+
return unless scope.is_a? Module
|
|
121
|
+
return unless scope.const_defined? last, false
|
|
122
|
+
|
|
123
|
+
load ? scope.const_get(last, false) : true
|
|
124
|
+
end
|
|
125
|
+
|
|
79
126
|
# Raised for every failure the gem reports, so hosts can rescue one type.
|
|
80
127
|
class Error < StandardError; end
|
|
81
128
|
end
|