recourse 4.7.0 → 4.8.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: ba211bbbb2cad39fb435db7af8c982733056778da14b2617ed01616d7b471643
4
- data.tar.gz: 3b847c74cb62a88425a882a839fc6d264581ab3f7e8aed661f53e03b184a12ed
3
+ metadata.gz: 2de6346d2ca14ff89cc09ff75c23b72b6cc7b7502d9dcfebcb29253a53209c8c
4
+ data.tar.gz: aadab8161994fb71579e46a1ab1343f929f11fdf7bec5b113768f06c16887bc8
5
5
  SHA512:
6
- metadata.gz: 7ba31e2730714820fa039e61ba649fc91e3b944b01703ded5c4a006b52068b0b5dc8997f667481994adb4d4fb4370660b3d2eed5fa0bb882d8307a44b0bc82ab
7
- data.tar.gz: 99f83b13ef7fb727d140dcb6fbd44a3abb160fd10b2bd21d441fa4cb609e985463b66e044047503acdb69375645c8dc408ab2559d4faa00d62416a8dea95a9a9
6
+ metadata.gz: 655ce30df22d4cccd72dd6a48e559eba1e8d14720d14f2a032de9f811a510f81f5a99dbf1be8082599371eff60c24906932b682e910099cd6bc72a6dbb12bd29
7
+ data.tar.gz: cb32000306a3b2f20e27f6898aed2c6d3ffb78bbf32411779f85a6e28bb4c61e80d327fddf2fbca0cf82163d9e0f198997eb0b22cea5f211fc23d2d4f7b30822
data/CHANGELOG.md CHANGED
@@ -5,6 +5,26 @@ All notable changes to this project will be documented in this file.
5
5
  For more information about changelogs, check [Keep a Changelog](http://keepachangelog.com) and
6
6
  [Vandamme](http://tech-angels.github.io/vandamme).
7
7
 
8
+ ## 4.8.0 - 2026-09-13
9
+
10
+ * [Feature] A table is redrawn when a record its rows draw changes, so a `touch: true` kept for that alone can go
11
+
12
+ A cell naming a key draws the record it points at — a booking's row reading its
13
+ contact's phone — and the relation's own version, `COUNT(*)` and `MAX(updated_at)`
14
+ over the page, sees only the rows themselves. The table kept the number the contact
15
+ had when the fragment was written, until somebody wrote to the booking. The key now
16
+ carries the newest `updated_at` among everything `recourse_includes` names, which
17
+ the index eager-loads already — so it is read off the records in memory and costs no
18
+ query.
19
+
20
+ A host that declared `belongs_to ..., touch: true` to expire such a table can drop
21
+ it: a touch is an UPDATE per parent per child write, it contends on the parent row,
22
+ and it makes every child write look like a parent update to every `after_commit` the
23
+ parent has. One that says a domain fact — a parent genuinely updated by its child —
24
+ stays. Where a row of a host's own reads through a second key, name that in
25
+ `recourse_includes` as a hash, the shape `includes` already takes, and the version
26
+ follows it there.
27
+
8
28
  ## 4.7.0 - 2026-09-11
9
29
 
10
30
  * [Feature] The toast after a create or an update names the record and links it to its page
@@ -1,8 +1,11 @@
1
1
  <%# locals: (recourses:, pagy:) %>
2
2
  <%# A sorted or filtered table is drawn live: two requests can ask for one relation
3
3
  and want different headings, since only one of them clicked a heading to get it. -%>
4
- <%# The key carries three riders. The row partial's digest: a host's `_row` is
5
- resolved at render, which the fragment's own template digest never follows.
4
+ <%# The key carries what the rows draw besides their own columns: `MAX(updated_at)`
5
+ over the relation sees only the rows themselves, so a name or a ZIP read off the
6
+ record a row points at would keep whatever it said when the fragment was written.
7
+ And the row partial's digest: a host's `_row` is resolved at render, which the
8
+ fragment's own template digest never follows.
6
9
  And the column list: which columns a table shows is decided in Ruby —
7
10
  `recourse_hidden`, timestamps, counters — that no digest covers either. And the
8
11
  bookmarks, which are the viewer's rather than the relation's — so this is what
@@ -10,7 +13,8 @@
10
13
  the fragment when a square is clicked. And the zone, for the same reason again:
11
14
  a timestamp is drawn against the reader's own clock, so without it the first
12
15
  person to load a table would settle what hour everybody else read. -%>
13
- <% key = [recourses, row_digest, resource_columns, bookmark_digest, Time.zone.name] -%>
16
+ <% key = [recourses, rows_version(recourses), row_digest, resource_columns,
17
+ bookmark_digest, Time.zone.name] -%>
14
18
  <% cache_if cacheable_table?, key do %>
15
19
  <% actions = resource_actions %>
16
20
  <div class='table-responsive'>
@@ -17,6 +17,36 @@ module Recourse
17
17
  ActionView::Digestor.digest name: row.virtual_path, format: :html, finder: lookup_context
18
18
  end
19
19
 
20
+ # What the rows draw besides their own columns. A cell naming a `*_id` reads the
21
+ # record it points at, and `MAX(updated_at)` over the relation only ever sees the
22
+ # rows themselves -- so without this a table keeps a name, a phone or a ZIP that
23
+ # the record it belongs to has since changed.
24
+ #
25
+ # Read off the objects rather than the database: the index eager-loads exactly
26
+ # these, so every one of them is already in memory and this costs no query. And
27
+ # written out to the microsecond, which is what `cache_key_with_version` keeps
28
+ # too: a key expands a time by `to_s`, and two writes inside one second would
29
+ # otherwise be one version.
30
+ def rows_version(rows)
31
+ drawn = reached rows, resource_model.recourse_includes
32
+
33
+ drawn.filter_map { |one| one.try :updated_at }.max&.utc&.to_fs :usec
34
+ end
35
+
36
+ # Every record the rows reach along `includes`, in any shape `includes` accepts:
37
+ # a name, a list of them, or a hash naming what to follow from there.
38
+ def reached(rows, names)
39
+ Array.wrap(names).flat_map do |name|
40
+ next along rows, name unless name.is_a? Hash
41
+
42
+ name.flat_map { |one, nested| followed along(rows, one), nested }
43
+ end
44
+ end
45
+
46
+ def followed(rows, nested) = rows + reached(rows, nested)
47
+
48
+ def along(rows, name) = rows.flat_map { |row| Array.wrap row.public_send(name) }
49
+
20
50
  # Whether the table may be kept at all. A sorted or filtered one never is: two
21
51
  # requests can ask for one relation and want different rows, and only one of them
22
52
  # clicked a heading to say so.
@@ -1,4 +1,4 @@
1
1
  module Recourse
2
2
  # Version of the gem, read by the gemspec and by hosts checking compatibility.
3
- VERSION = '4.7.0'
3
+ VERSION = '4.8.0'
4
4
  end
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: 4.7.0
4
+ version: 4.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - claudiob