recourse 7.4.0 → 7.6.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/CHANGELOG.md +22 -0
- data/app/views/layouts/recourses.html.erb +1 -1
- data/app/views/recourses/edit.html.erb +0 -2
- data/app/views/recourses/index.html.erb +0 -2
- data/app/views/recourses/new.html.erb +0 -2
- data/app/views/recourses/show.html.erb +0 -2
- data/lib/recourse/helpers/breadcrumbs.rb +8 -0
- data/lib/recourse/helpers/deletions.rb +19 -57
- data/lib/recourse/helpers/warnings.rb +68 -0
- data/lib/recourse/helpers.rb +3 -2
- data/lib/recourse/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a2a20bbc5915a1d57b72fb203edfdd05c1bd5f994c428b0520f2ef9af73ec3f0
|
|
4
|
+
data.tar.gz: a6ba069086c36351dd7072d09a2909f64bd17a84346d8ef31921fd953592a61d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c98284ac83b4a7b5419f747e69028dff262aa5281745c61bc8fbd01d7dd0eeb07dc4706ffbbb531133c8c8bc1b2478e75b55a12484cb39a6ca2b539df2821ea5
|
|
7
|
+
data.tar.gz: cdede705768f5b68438efd3d1e80f1120827614a16d8bf11b9556aad5107184f4691d18e893a3d2ee8e1223a1ceef92f74c9026c60d278c6e3d904aa332edae9
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,28 @@ For more information about changelogs, check [Keep a Changelog](http://keepachan
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## 7.6.0 - 2026-09-20
|
|
11
|
+
|
|
12
|
+
* [CHANGE] The layout titles the page, so a host template cannot lose it
|
|
13
|
+
|
|
14
|
+
The four templates each set `content_for :title`, so a host writing one of its own —
|
|
15
|
+
which is the whole point of the gem drawing the rest — left the tab reading `Recourse`.
|
|
16
|
+
The title is chrome like the trail beside it, and the layout works it out from the same
|
|
17
|
+
place: what the trail ends with, the resource it is of where the record names nothing,
|
|
18
|
+
and the app's own name where neither answers. `content_for :title` still wins where a
|
|
19
|
+
host sets one.
|
|
20
|
+
|
|
21
|
+
## 7.5.0 - 2026-09-20
|
|
22
|
+
|
|
23
|
+
* [FEATURE] A model may word its own deletion
|
|
24
|
+
|
|
25
|
+
Deleting is not what every model does when a row goes: an account is disconnected, a
|
|
26
|
+
note is withdrawn. Both the button and the heading over the dialog now read
|
|
27
|
+
`recourse.models.<model>.delete` and `recourse.models.<model>.deletion_title` where a
|
|
28
|
+
host wrote them, and the one word the gem has where nobody did. Keyed off the record's
|
|
29
|
+
own class, so a subclass words it apart from its siblings — and a slash in an i18n key
|
|
30
|
+
is a nesting, so `integration/jobber` sits under `integration` in the locale.
|
|
31
|
+
|
|
10
32
|
## 7.4.0 - 2026-09-20
|
|
11
33
|
|
|
12
34
|
* [CHANGE] A singular resource that has its record reads it rather than offering a form
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<html lang='en'>
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset='utf-8'>
|
|
5
|
-
<title><%= content_for(:title) ||
|
|
5
|
+
<title><%= content_for(:title) || page_title %></title>
|
|
6
6
|
<meta name='viewport' content='width=device-width, initial-scale=1'>
|
|
7
7
|
<meta name='color-scheme' content='light dark'>
|
|
8
8
|
<%# A refresh Turbo is sent morphs the page in place and keeps the scroll; without these it
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
<% content_for :title, resource_record_label %>
|
|
2
|
-
|
|
3
1
|
<%# Nothing to read out where the host found no record: a singular resource is reached
|
|
4
2
|
with no id, so the one it stands for is one it may not have yet, and the page says so
|
|
5
3
|
rather than reading attributes off nothing. -%>
|
|
@@ -46,6 +46,14 @@ module Recourse
|
|
|
46
46
|
safe_join [tag.i(class: "bi bi-#{icon}"), word], ' '
|
|
47
47
|
end
|
|
48
48
|
|
|
49
|
+
# What the tab calls this page: what the trail ends with, the resource it is of
|
|
50
|
+
# where the record names nothing, and the app's own name where neither answers.
|
|
51
|
+
def page_title
|
|
52
|
+
breadcrumb_leaf.presence || breadcrumb_name.presence || app_name
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def app_name = Rails.application.class.module_parent_name
|
|
56
|
+
|
|
49
57
|
# Only a page beneath the index names itself, and names what it is showing.
|
|
50
58
|
def breadcrumb_leaf
|
|
51
59
|
case controller.action_name
|
|
@@ -3,13 +3,6 @@ module Recourse
|
|
|
3
3
|
# The button that deletes the record a form is showing, and the warning it puts
|
|
4
4
|
# in front of whoever clicked it.
|
|
5
5
|
module Deletions
|
|
6
|
-
# `dependent:` values that take the children with the parent.
|
|
7
|
-
DESTROYED = %i[destroy destroy_async].freeze
|
|
8
|
-
|
|
9
|
-
# And the one that keeps them, holding the key open. Anything else — a bare
|
|
10
|
-
# `has_many`, a `:restrict` — is left unsaid rather than guessed at.
|
|
11
|
-
NULLIFIED = %i[nullify].freeze
|
|
12
|
-
|
|
13
6
|
private
|
|
14
7
|
|
|
15
8
|
# The record a page may delete: the one it is about, on its own look or its own
|
|
@@ -26,68 +19,37 @@ module Recourse
|
|
|
26
19
|
path = record && destroy_resource_path(record)
|
|
27
20
|
return unless path
|
|
28
21
|
|
|
29
|
-
confirm_button_to
|
|
22
|
+
confirm_button_to destroy_label(record), path,
|
|
30
23
|
confirm: destroy_warning(record), method: :delete,
|
|
31
24
|
class: 'btn btn-sm btn-solid theme-danger ms-3',
|
|
32
25
|
form_class: 'd-inline-block'
|
|
33
26
|
end
|
|
34
27
|
|
|
35
|
-
#
|
|
36
|
-
#
|
|
37
|
-
#
|
|
38
|
-
#
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
def destroy_resource_path(record)
|
|
46
|
-
return unless routed_action? 'destroy'
|
|
47
|
-
|
|
48
|
-
url_for action: :destroy, id: record
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
# The middle of the warning, in the order it reads: what goes, what stays, and
|
|
52
|
-
# only then what is under what goes — the levels this stops short of counting.
|
|
53
|
-
def dependent_lines(record)
|
|
54
|
-
going = dependents record, DESTROYED
|
|
55
|
-
staying = dependents record, NULLIFIED
|
|
56
|
-
lines = []
|
|
57
|
-
lines << t('recourse.deletion.going', list: going.to_sentence) if going.any?
|
|
58
|
-
lines << staying_line(staying) if staying.any?
|
|
59
|
-
lines << t('recourse.deletion.under') if going.any?
|
|
60
|
-
|
|
61
|
-
lines
|
|
28
|
+
# The word on the button, and below it the word over the dialog. A model that
|
|
29
|
+
# undoes something rather than deleting it says so under its own name, and every
|
|
30
|
+
# other model falls through to the one word the gem has. Keyed off the record's
|
|
31
|
+
# class rather than the resource's, so a subclass words it apart from its
|
|
32
|
+
# siblings: an integration a provider authorized is disconnected where the one an
|
|
33
|
+
# admin picked is given up.
|
|
34
|
+
def destroy_label(record)
|
|
35
|
+
t worded(record, :delete), model: resource_name, default: :'recourse.delete'
|
|
62
36
|
end
|
|
63
37
|
|
|
64
|
-
def
|
|
65
|
-
t
|
|
38
|
+
def destroy_heading(record)
|
|
39
|
+
t worded(record, :deletion_title), record: destroy_title(record),
|
|
40
|
+
default: :'recourse.deletion.title'
|
|
66
41
|
end
|
|
67
42
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
dependent_count record, association
|
|
73
|
-
end
|
|
43
|
+
# A slash in the key is a nesting to i18n, which is what lets a subclass sit
|
|
44
|
+
# under the model it inherits from rather than beside it.
|
|
45
|
+
def worded(record, name)
|
|
46
|
+
:"recourse.models.#{record.model_name.i18n_key}.#{name}"
|
|
74
47
|
end
|
|
75
48
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
def dependent_count(record, association)
|
|
79
|
-
count = record.association(association.name).reader.count
|
|
80
|
-
return if count.zero?
|
|
81
|
-
|
|
82
|
-
name = Recourse.downcase association.klass.model_name.human
|
|
83
|
-
|
|
84
|
-
"#{number_with_delimiter count} #{name.pluralize count}"
|
|
85
|
-
end
|
|
49
|
+
def destroy_resource_path(record)
|
|
50
|
+
return unless routed_action? 'destroy'
|
|
86
51
|
|
|
87
|
-
|
|
88
|
-
def destroy_title(record)
|
|
89
|
-
record.attributes[record.class.recourse_label.to_s].presence ||
|
|
90
|
-
record.class.model_name.human
|
|
52
|
+
url_for action: :destroy, id: record
|
|
91
53
|
end
|
|
92
54
|
end
|
|
93
55
|
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
module Recourse
|
|
2
|
+
module Helpers
|
|
3
|
+
# What a deletion is said to cost before it is made: the heading over the dialog,
|
|
4
|
+
# and what the row takes with it or leaves behind.
|
|
5
|
+
module Warnings
|
|
6
|
+
# `dependent:` values that take the children with the parent.
|
|
7
|
+
DESTROYED = %i[destroy destroy_async].freeze
|
|
8
|
+
|
|
9
|
+
# And the one that keeps them, holding the key open. Anything else — a bare
|
|
10
|
+
# `has_many`, a `:restrict` — is left unsaid rather than guessed at.
|
|
11
|
+
NULLIFIED = %i[nullify].freeze
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
# @api private
|
|
16
|
+
# What deleting this record takes with it, counted a level down and no further:
|
|
17
|
+
# a state reaches counties, then ZIPs, then locations, and counting that far
|
|
18
|
+
# would join 40,965 rows to draw one page.
|
|
19
|
+
def destroy_warning(record)
|
|
20
|
+
lines = [destroy_heading(record), nil]
|
|
21
|
+
|
|
22
|
+
[*lines, *dependent_lines(record), nil, t('recourse.deletion.undone')].join "\n"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# The middle of the warning, in the order it reads: what goes, what stays, and
|
|
26
|
+
# only then what is under what goes — the levels this stops short of counting.
|
|
27
|
+
def dependent_lines(record)
|
|
28
|
+
going = dependents record, DESTROYED
|
|
29
|
+
staying = dependents record, NULLIFIED
|
|
30
|
+
lines = []
|
|
31
|
+
lines << t('recourse.deletion.going', list: going.to_sentence) if going.any?
|
|
32
|
+
lines << staying_line(staying) if staying.any?
|
|
33
|
+
lines << t('recourse.deletion.under') if going.any?
|
|
34
|
+
|
|
35
|
+
lines
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def staying_line(staying)
|
|
39
|
+
t 'recourse.deletion.staying', list: staying.to_sentence, model: resource_name
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def dependents(record, kinds)
|
|
43
|
+
record.class.reflect_on_all_associations(:has_many).filter_map do |association|
|
|
44
|
+
next if association.through_reflection || kinds.exclude?(association.options[:dependent])
|
|
45
|
+
|
|
46
|
+
dependent_count record, association
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# `association.reader` rather than a method named at runtime, and `count` rather
|
|
51
|
+
# than loading them: the warning needs how many, never which.
|
|
52
|
+
def dependent_count(record, association)
|
|
53
|
+
count = record.association(association.name).reader.count
|
|
54
|
+
return if count.zero?
|
|
55
|
+
|
|
56
|
+
name = Recourse.downcase association.klass.model_name.human
|
|
57
|
+
|
|
58
|
+
"#{number_with_delimiter count} #{name.pluralize count}"
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# What the record is called, or what it is, for one that answers to no label.
|
|
62
|
+
def destroy_title(record)
|
|
63
|
+
record.attributes[record.class.recourse_label.to_s].presence ||
|
|
64
|
+
record.class.model_name.human
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
data/lib/recourse/helpers.rb
CHANGED
|
@@ -47,6 +47,7 @@ require_relative 'helpers/spans'
|
|
|
47
47
|
require_relative 'helpers/tabs'
|
|
48
48
|
require_relative 'helpers/times'
|
|
49
49
|
require_relative 'helpers/values'
|
|
50
|
+
require_relative 'helpers/warnings'
|
|
50
51
|
require_relative 'helpers/weeks'
|
|
51
52
|
require_relative 'helpers/zones'
|
|
52
53
|
|
|
@@ -58,8 +59,8 @@ module Recourse
|
|
|
58
59
|
Deletions, Densities, Details, Events, Examples, Fields, Filters, Flashes,
|
|
59
60
|
Formats, Inputs, Kinds, Limits, Links, Maps, Names, Navigation, Parents,
|
|
60
61
|
Pictures, Previews, References, Refreshes, Routing, Resources, Rows, Searches,
|
|
61
|
-
Shapes, Shortcuts, Sidebars, Sorts, Spans, Tabs, Themes, Times, Values,
|
|
62
|
-
Zones
|
|
62
|
+
Shapes, Shortcuts, Sidebars, Sorts, Spans, Tabs, Themes, Times, Values,
|
|
63
|
+
Warnings, Weeks, Zones
|
|
63
64
|
|
|
64
65
|
# The grid a record's own two pages lay an attribute out in: two columns on a large
|
|
65
66
|
# viewport, and the same padding on both, so a value and the field that edits it sit
|
data/lib/recourse/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: recourse
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 7.
|
|
4
|
+
version: 7.6.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- claudiob
|
|
@@ -240,6 +240,7 @@ files:
|
|
|
240
240
|
- lib/recourse/helpers/themes.rb
|
|
241
241
|
- lib/recourse/helpers/times.rb
|
|
242
242
|
- lib/recourse/helpers/values.rb
|
|
243
|
+
- lib/recourse/helpers/warnings.rb
|
|
243
244
|
- lib/recourse/helpers/weeks.rb
|
|
244
245
|
- lib/recourse/helpers/zones.rb
|
|
245
246
|
- lib/recourse/icons.rb
|