glib-web 7.3.0 → 7.4.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: c46931ac3a3686ca63eb928be8cc23f77f6df86bcb2d3f6ad28c25a57f564fd3
4
- data.tar.gz: 2b782f018a4220a56c0512f0603d2a712e4b97d57ffa15a64be1601035fda93f
3
+ metadata.gz: 6bc5c239db343c0d309433661931b7b69546973e9c6e579c8b5c71a508bd4807
4
+ data.tar.gz: b19abc6b0253672008441ba9a904388cf16c8456009cefe9da103b239c3cde89
5
5
  SHA512:
6
- metadata.gz: 8d089424b8a53f6913c60c3cd895434aa168799243536b77ac73c75975ba766202382c52d86701beb6562fe38be03ff869336b353d168f3e4c92a89418ff85d1
7
- data.tar.gz: 1cb4c4c951bdde00b63495407ee19e2c5738e2152107cbb8898bf19ce26ed91c3c82f6de9d922c7118d1754fe31d28def156b964d33edb3bc004980e019cd524
6
+ metadata.gz: '0695a7083c1165bfac025c232124b67812ea3bcc3f9dde2adf794d8cfd3a23a4a0094c3d02b96726875ad864741f116602b63e76c193d5895d802945a5453a55'
7
+ data.tar.gz: 40270171a87c63927d41f92b344ca5cad5b0ce743c7d4fc2ffd995ec02589deecce930c0b254d93d534e001017989b976f0898b45cf532b4b4760a5757bb046d
@@ -31,9 +31,26 @@ module Glib
31
31
  # key and is almost always a bug) and override the reader to return a symbol,
32
32
  # so callers can compare with `record.status == :active` without tracking
33
33
  # which form they're holding (see backend/01a_defensive_programming.md item 7).
34
+ #
35
+ # The same symbolization covers the per-attribute dirty-tracking readers
36
+ # (`status_was`, `status_in_database`, `status_change`, `saved_change_to_status`,
37
+ # ...), which Rails builds from the raw attribute rather than through the
38
+ # reader and would otherwise hand back the enum's string label. The dirty
39
+ # PREDICATES (`status_changed?(from: :draft)`) need no override: Rails casts
40
+ # their from:/to: through the enum type, so symbols already work there.
41
+ # Hash-form readers (`changes`, `saved_changes`, `attributes`) are left as
42
+ # Rails returns them: they are keyed by attribute name rather than written
43
+ # per attribute, and their consumers (serialization, audit trails, update
44
+ # payloads) expect Rails' native form.
34
45
  module EnumSymbolization
35
46
  extend ActiveSupport::Concern
36
47
 
48
+ # Per-attribute dirty readers returning the single value (string or nil).
49
+ VALUE_SUFFIXES = %w[_was _previously_was _before_last_save _in_database].freeze
50
+ # Per-attribute dirty readers returning a [before, after] pair, or nil when
51
+ # unchanged. `saved_change_to_<attr>` is the prefixed member of this set.
52
+ PAIR_SUFFIXES = %w[_change _previous_change _change_to_be_saved].freeze
53
+
37
54
  class_methods do
38
55
  def enum_symbolize(*attrs, **opts)
39
56
  if attrs.size >= 2 && (attrs[1].is_a?(Hash) || attrs[1].is_a?(Array))
@@ -48,7 +65,7 @@ module Glib
48
65
  # remove or move below `enum`.
49
66
  attribute name, Glib::EnumSymbolization.backing_type(values)
50
67
  enum name, values, **opts
51
- glib_symbolize_reader(name)
68
+ glib_symbolize_enum(name)
52
69
  else
53
70
  # Legacy form: enum_symbolize :a[, :b, ...] for enums already declared.
54
71
  if opts.any?
@@ -56,18 +73,38 @@ module Glib
56
73
  "enum_symbolize: pass enum values as a braced hash, " \
57
74
  "e.g. enum_symbolize :#{attrs.first}, { ... }"
58
75
  end
59
- attrs.each { |attr| glib_symbolize_reader(attr) }
76
+ attrs.each { |attr| glib_symbolize_enum(attr) }
60
77
  end
61
78
  end
62
79
 
63
80
  private
64
81
 
65
- def glib_symbolize_reader(attr)
82
+ # Overrides are defined on the model class itself, above the generated
83
+ # attribute-methods module Rails includes, so each `super()` reaches the
84
+ # Rails implementation.
85
+ def glib_symbolize_enum(attr)
66
86
  validates attr, presence: true
67
- define_method(attr) { super()&.to_sym }
87
+ define_method(attr) { Glib::EnumSymbolization.symbolize(super()) }
88
+
89
+ VALUE_SUFFIXES.each do |suffix|
90
+ define_method("#{attr}#{suffix}") { Glib::EnumSymbolization.symbolize(super()) }
91
+ end
92
+
93
+ pair_methods = PAIR_SUFFIXES.map { |suffix| "#{attr}#{suffix}" } + ["saved_change_to_#{attr}"]
94
+ pair_methods.each do |method_name|
95
+ define_method(method_name) { super()&.map { |value| Glib::EnumSymbolization.symbolize(value) } }
96
+ end
68
97
  end
69
98
  end
70
99
 
100
+ # A label becomes a symbol; nil stays nil. Anything else passes through:
101
+ # with `validate: true` Rails casts an unknown raw value (99) to itself
102
+ # instead of raising, and that value must survive to fail validation
103
+ # rather than blow up in `to_sym`.
104
+ def self.symbolize(value)
105
+ value.is_a?(String) ? value.to_sym : value
106
+ end
107
+
71
108
  # Infer the attribute type backing an enum from its declared values, so the
72
109
  # type never depends on the (possibly not-yet-migrated) DB column. A hash
73
110
  # whose values are strings backs a :string column; integer-keyed hashes and
@@ -136,36 +136,36 @@ Try @mentioning a teammate here.
136
136
  form.h2 text: 'Actions/events'
137
137
  form.spacer height: 8
138
138
  form.label id: 'rich_text_status', text: 'Status: idle'
139
- # form.spacer height: 8
140
- # form.panels_flow(
141
- # innerPadding: { bottom: 0 },
142
- # width: 'matchParent',
143
- # childViews: ->(flow) do
144
- # flow.button(
145
- # text: 'Insert template',
146
- # onClick: ->(action) do
147
- # action.runMultiple(
148
- # childActions: ->(multiple) do
149
- # multiple.logics_set targetId: 'rich_text_main', data: { value: "# Quick update\n\n- Item one\n- Item two\n\nThanks!" }
150
- # multiple.logics_set targetId: 'rich_text_status', data: { text: 'Status: template inserted' }
151
- # end
152
- # )
153
- # end
154
- # )
155
- # # flow.spacer width: 8
156
- # # flow.button(
157
- # # text: 'Clear content',
158
- # # onClick: ->(action) do
159
- # # action.runMultiple(
160
- # # childActions: ->(multiple) do
161
- # # multiple.logics_set targetId: 'rich_text_main', data: { value: '' }
162
- # # multiple.logics_set targetId: 'rich_text_status', data: { text: 'Status: cleared' }
163
- # # end
164
- # # )
165
- # # end
166
- # # )
167
- # end
168
- # )
139
+ form.spacer height: 8
140
+ form.panels_flow(
141
+ innerPadding: { bottom: 0 },
142
+ width: 'matchParent',
143
+ childViews: ->(flow) do
144
+ flow.button(
145
+ text: 'Insert template',
146
+ onClick: ->(action) do
147
+ action.runMultiple(
148
+ childActions: ->(multiple) do
149
+ multiple.logics_set targetId: 'rich_text_main', data: { value: "# Quick update\n\n- Item one\n- Item two\n\nThanks!" }
150
+ multiple.logics_set targetId: 'rich_text_status', data: { text: 'Status: template inserted' }
151
+ end
152
+ )
153
+ end
154
+ )
155
+ flow.spacer width: 8
156
+ flow.button(
157
+ text: 'Clear content',
158
+ onClick: ->(action) do
159
+ action.runMultiple(
160
+ childActions: ->(multiple) do
161
+ multiple.logics_set targetId: 'rich_text_main', data: { value: '' }
162
+ multiple.logics_set targetId: 'rich_text_status', data: { text: 'Status: cleared' }
163
+ end
164
+ )
165
+ end
166
+ )
167
+ end
168
+ )
169
169
 
170
170
  form.spacer height: 12
171
171
  form.hr width: 'matchParent'
@@ -2,23 +2,31 @@
2
2
  <html lang="<%= I18n.locale %>">
3
3
 
4
4
  <head>
5
- <meta charset="utf-8" />
6
- <meta content="width=device-width, initial-scale=1, shrink-to-fit=no" name="viewport" />
5
+ <meta charset="utf-8">
6
+ <meta content="width=device-width, initial-scale=1, shrink-to-fit=no" name="viewport">
7
7
 
8
- <title><%= yield(:title) || 'App'%></title>
8
+ <title><%= yield(:title) || 'App' %></title>
9
9
  <%= csrf_meta_tags %>
10
-
11
-
12
10
  <%# TODO: Remove this, for dev only %>
13
- <%# <script src="https://cdn.tailwindcss.com"></script> %>
14
- <link href="//fonts.googleapis.com/css?family=Material+Icons" rel="stylesheet" />
15
- <%# Without this, checkboxes and radio buttons will not display. %>
16
- <%# <link href="//cdn.materialdesignicons.com/2.1.99/css/materialdesignicons.min.css" rel="stylesheet" /> %>
17
- <%= yield :head %>
18
- <%= vite_javascript_tag 'vue_renderer', defer: true %>
19
- <% if @__glib_head_code %>
11
+ <%# <script src="https://cdn.tailwindcss.com"></script> %>
12
+ <%# The Material Icons link below reaches two third-party origins: the
13
+ stylesheet itself and the font file it references. The stylesheet is
14
+ render-blocking, so its DNS/TLS handshake sits on the critical path;
15
+ preconnecting lets both handshakes overlap the app's own asset loading.
16
+ `crossorigin` is required on the font origin because fonts are fetched
17
+ CORS-anonymously, and without it the preconnected socket is discarded.
18
+ Note the icons themselves are held blank until this font resolves -- see
19
+ Dom.hideIconsUntilFontReady in the npm package. %>
20
+ <link href="//fonts.googleapis.com" rel="preconnect">
21
+ <link href="//fonts.gstatic.com" rel="preconnect" crossorigin>
22
+ <link href="//fonts.googleapis.com/css?family=Material+Icons" rel="stylesheet">
23
+ <%# Without this, checkboxes and radio buttons will not display. %>
24
+ <%# <link href="//cdn.materialdesignicons.com/2.1.99/css/materialdesignicons.min.css" rel="stylesheet" /> %>
25
+ <%= yield :head %>
26
+ <%= vite_javascript_tag 'vue_renderer', defer: true %>
27
+ <% if @__glib_head_code %>
20
28
  <%= @__glib_head_code.html_safe %>
21
- <% end %>
29
+ <% end %>
22
30
  </head>
23
31
  <body>
24
32
  <%= yield :header %>
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: glib-web
3
3
  version: !ruby/object:Gem::Version
4
- version: 7.3.0
4
+ version: 7.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - ''
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-10-17 00:00:00.000000000 Z
11
+ date: 2026-10-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activestorage