backstage 0.1.15 → 0.1.16

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: da02200425a97de115bbd7a4ff85a2cc907ab221647c3c9d22bb105ce690ff99
4
- data.tar.gz: 4f53000b38b952e825cb5e82211162a7ce2149834e4efc671cf560595226593b
3
+ metadata.gz: c93addd91e80f76d095d7fd56542ebea748d27be3043cad7affcee189143718e
4
+ data.tar.gz: a9df1684acf09fb155e449e612e1e92c117874b581d4161f4b9c4b3749f610b4
5
5
  SHA512:
6
- metadata.gz: 83f557e7644fa1e07487d3995854645f71454fee792023e9a344f24639fe222cef1d9c8a6bb130e8673dac4f7c4a942df5bb52a7ce06b0628c0a74461e3b1a59
7
- data.tar.gz: dfd0f21995ca2a17e92016e86e6c975633b35b54c40a198e9edd8964116080a574055bd8480aceb589d5391739d87ee51cbdb65fcc600c636b3b8e1d4c31894c
6
+ metadata.gz: 48c81f6f435f100053c573b886ad40f6df175ae871302bbc381c161ade4f99bbe2062505de73133702d8a7c05e679d721e31d0a95f76b6555d3d1923df0f328a
7
+ data.tar.gz: 6653ba7832ea040c05081c5f46e9b4f8504503ed7bf117a49c7d111e95d144c56edbf9262f7864eed7d12094c482574e31ad0505a33323ea6141601e9d13d4e3
data/CHANGELOG.md CHANGED
@@ -7,6 +7,15 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.1.16] — 2026-06-02
11
+
12
+ ### Fixed
13
+
14
+ - Nested table now uses a `<template>` element instead of a static new row, so unsaved blank rows are never submitted unintentionally
15
+ - New-row template includes readonly fields as plain inputs so new records can be fully populated via the Add button
16
+ - Readonly nested fields are now permitted in Strong Parameters, so new records with readonly fields save correctly
17
+ - `belongs_to :assoc, readonly: true` now propagates `readonly:` to the generated FK field
18
+
10
19
  ## [0.1.15] — 2026-05-27
11
20
 
12
21
  ### Added
@@ -99,8 +99,7 @@ module Backstage
99
99
  elsif field.has_many?
100
100
  [{field.name => []}]
101
101
  elsif field.nested?
102
- writable = field.nested_fields - field.nested_readonly_fields
103
- [{"#{field.name}_attributes": [:id, :_destroy, *writable]}]
102
+ [{"#{field.name}_attributes": [:id, :_destroy, *field.nested_fields]}]
104
103
  else
105
104
  [field.name]
106
105
  end
@@ -27,13 +27,29 @@
27
27
  </td>
28
28
  </tr>
29
29
  <% end %>
30
- <%= f.fields_for field.name, records.klass.new, child_index: records.size do |nf| %>
31
- <tr data-nested-new-row>
32
- <% field.nested_fields.reject { |col| field.nested_readonly_fields.include?(col) }.each do |col| %>
33
- <td><%= nf.text_field col %></td>
30
+ <%# Template is not part of the DOM — never submitted until the user clicks Add %>
31
+ <template data-nested-template data-next-index="<%= records.size %>">
32
+ <tr>
33
+ <% field.nested_fields.each do |col| %>
34
+ <td>
35
+ <input type="text"
36
+ name="<%= "#{f.object_name}[#{field.name}_attributes][__INDEX__][#{col}]" %>"
37
+ value="" />
38
+ </td>
34
39
  <% end %>
35
- <td></td>
40
+ <td>
41
+ <input type="hidden"
42
+ name="<%= "#{f.object_name}[#{field.name}_attributes][__INDEX__][_destroy]" %>"
43
+ value="0"
44
+ data-nested-destroy-flag="true" />
45
+ <button type="button" data-nested-destroy aria-label="Remove">&times;</button>
46
+ </td>
36
47
  </tr>
37
- <% end %>
48
+ </template>
49
+ <tr>
50
+ <td colspan="<%= field.nested_fields.size + 1 %>">
51
+ <button type="button" data-nested-add>+ Add</button>
52
+ </td>
53
+ </tr>
38
54
  </tbody>
39
55
  </table>
@@ -26,12 +26,23 @@
26
26
  })
27
27
  })
28
28
  })
29
- document.querySelectorAll("[data-nested-destroy]").forEach(function(btn) {
30
- btn.addEventListener("click", function() {
31
- var row = btn.closest("tr")
32
- row.querySelector("[data-nested-destroy-flag]").value = "1"
29
+ document.addEventListener("click", function(e) {
30
+ var destroyBtn = e.target.closest("[data-nested-destroy]")
31
+ if (destroyBtn) {
32
+ var row = destroyBtn.closest("tr")
33
+ var flag = row.querySelector("[data-nested-destroy-flag]")
34
+ if (flag) flag.value = "1"
33
35
  row.hidden = true
34
- })
36
+ }
37
+ var addBtn = e.target.closest("[data-nested-add]")
38
+ if (addBtn) {
39
+ var table = addBtn.closest("table")
40
+ var tmpl = table.querySelector("[data-nested-template]")
41
+ var index = parseInt(tmpl.dataset.nextIndex, 10)
42
+ tmpl.dataset.nextIndex = index + 1
43
+ var html = tmpl.innerHTML.replace(/__INDEX__/g, index)
44
+ addBtn.closest("tr").insertAdjacentHTML("beforebegin", html)
45
+ }
35
46
  })
36
47
  })
37
48
  </script>
@@ -63,7 +63,7 @@ module Backstage
63
63
  def belongs_to(name, **opts)
64
64
  assoc = AssociationConfig.new(name, :belongs_to, opts)
65
65
  @associations << assoc
66
- fk_field = Field.new(assoc.foreign_key, :belongs_to, association: assoc)
66
+ fk_field = Field.new(assoc.foreign_key, :belongs_to, association: assoc, readonly: opts[:readonly])
67
67
  index_field = Field.new(name.to_sym, :belongs_to, association: assoc)
68
68
  @edit_fields.reject! { |f| f.name == fk_field.name }
69
69
  @index_fields.reject! { |f| f.name == fk_field.name || f.name == index_field.name }
@@ -1,3 +1,3 @@
1
1
  module Backstage
2
- VERSION = "0.1.15"
2
+ VERSION = "0.1.16"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: backstage
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.15
4
+ version: 0.1.16
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gareth James
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-05-27 00:00:00.000000000 Z
11
+ date: 2026-06-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: railties