glib-web 8.0.0 → 8.1.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: dfa432bee9f77048fec3655d894d354986edb1e2eb1750c457b30085c939b7e5
4
- data.tar.gz: fb85b5fbc2e5d766883eaa623e64ad3ca1661dabc2527e56d9a804673e94e9d1
3
+ metadata.gz: 22842c76cc4c92471f28991ff906491e61429a891a9da912aff82af48ac90125
4
+ data.tar.gz: dcb1bfce2a976cae302dc39662c3d696a9c951eb0934fdf6e7d0d2257d5ad48e
5
5
  SHA512:
6
- metadata.gz: d5293ff30003202c2ef89f75ad41b92c3d3ab326f40842d95df3f75d73e9c3aaf57ffbe1fa2ba21ae7b233061a2e6e13f2fc9a5f284d694dda0e69e06aa9e0f6
7
- data.tar.gz: 2d14b253bee2be2015de7c9ced6d76109070568fc4e4018cdc6c24dcc371b5feae5cee4f727bc386696e292bb0dc5d9d8315a86c4aedac4f528503308f9f8944
6
+ metadata.gz: b094093515f7d56a0d4374779dac06b97ef70ee2bd164507668d03c22e5791913264ed68d3ea1ccf33aeeb7ae36cca6b939f78729c2ca94c228007d97024522f
7
+ data.tar.gz: 86a7cc0bc03aeefd9d96d3cbc06e382e0a9b1b6cb851a4b723dc701b825f474d5aac822151505932e320a166299bc246c722dbd9b5e34685b093bfc6826f1e54
@@ -991,6 +991,7 @@ class Glib::JsonUi::ViewBuilder
991
991
  class Carousel < View
992
992
  views :childViews
993
993
  string :distribution
994
+ int :interval
994
995
  end
995
996
 
996
997
  # # TODO: Deprecate in favour of styleClasses
@@ -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
@@ -34,5 +34,12 @@ json_ui_page json do |page|
34
34
  panel.image height: height, url: image_url2
35
35
  end
36
36
  end
37
+
38
+ scroll.spacer height: 20
39
+ scroll.h1 text: 'Carousel with cycle'
40
+ scroll.panels_carousel width: 'matchParent', interval: 3000, childViews: ->(carousel) do
41
+ carousel.image url: image_url1, width: 'matchParent'
42
+ carousel.image url: image_url2, width: 'matchParent'
43
+ end
37
44
  end
38
45
  end
@@ -153,6 +153,41 @@ page.form(
153
153
  res.fields_check name: 'user[tags][]', checkValue: 'b', label: 'Tag B', onChange: set_eq_check
154
154
  res.spacer height: 8
155
155
  res.label id: 'set_eq_status', text: 'PENDING'
156
+
157
+ res.spacer height: 16
158
+ res.h2 text: 'Logics Set - time of day'
159
+ res.spacer height: 8
160
+ # `minutesOfDay` parses a typed time of day exactly as a consuming
161
+ # app's server does and `formatHm` renders the length in explicit
162
+ # units ("4h", "1h 5m"), so a Start/End pair
163
+ # shows its duration live. The `+ 1440` before the `% 1440` keeps
164
+ # json-logic's JS remainder positive when the End is before the
165
+ # Start, which is also when "(next day)" is said. A missing time
166
+ # says a non-breaking space, not an empty string, so the label keeps
167
+ # its height and the page never jumps mid-typing.
168
+ duration_update = ->(action) do
169
+ start_minutes = { 'minutesOfDay' => { 'var' => 'user[start_time]' } }
170
+ end_minutes = { 'minutesOfDay' => { 'var' => 'user[end_time]' } }
171
+ action.logics_set(
172
+ targetId: 'duration_label',
173
+ conditionalData: {
174
+ text: {
175
+ 'if' => [
176
+ { 'or' => [{ '==' => [start_minutes, nil] }, { '==' => [end_minutes, nil] }] }, "\u00a0",
177
+ {
178
+ 'cat' => ['Duration ',
179
+ { 'formatHm' => { '%' => [{ '+' => [{ '-' => [end_minutes, start_minutes] }, 1440] }, 1440] } },
180
+ { 'if' => [{ '<' => [end_minutes, start_minutes] }, ' (next day)', ''] }]
181
+ }
182
+ ]
183
+ }
184
+ }
185
+ )
186
+ end
187
+ res.fields_text label: 'Start', name: 'user[start_time]', width: 300, onChange: duration_update
188
+ res.fields_text label: 'End', name: 'user[end_time]', width: 300, onChange: duration_update
189
+ res.spacer height: 8
190
+ res.label id: 'duration_label', text: 'PENDING'
156
191
  end
157
192
  )
158
193
  end
@@ -0,0 +1,42 @@
1
+ json.title 'Test Page (panels_association)'
2
+
3
+ page = json_ui_page json
4
+ render 'json_ui/garage/test_page/header', json: json, page: page
5
+
6
+ # The panels_association fixture (spec 353, D5): doculocker's submission-form
7
+ # shape on dummy-app models, unpersisted so the page is deterministic against
8
+ # the dummy host's empty database. One un-indexed association (the signature
9
+ # form's singular nested model), two indexed associations each holding an
10
+ # indexed nested association (the qualification form's two-deep shape), a
11
+ # panels_responsive inside one (the signature form nests one), hidden id and
12
+ # visible text props, and a submit.
13
+ page.form \
14
+ url: json_ui_garage_url(path: 'forms/generic_post'),
15
+ model: Post.new(title: 'Fixture post'),
16
+ method: 'post',
17
+ padding: glib_json_padding_body,
18
+ childViews: ->(form) do
19
+ form.fields_text prop: :title, label: 'Post title'
20
+
21
+ form.h3 text: 'Unindexed association'
22
+ form.panels_association model: Comment.new(content: 'Lead comment'), width: 'matchParent', childViews: ->(assoc) do
23
+ assoc.fields_hidden prop: :id
24
+ assoc.panels_responsive width: 'matchParent', childViews: ->(responsive) do
25
+ responsive.fields_text prop: :content, label: 'Lead comment'
26
+ end
27
+ end
28
+
29
+ form.h3 text: 'Indexed associations'
30
+ [['First comment', 'first.pdf'], ['Second comment', 'second.pdf']].each_with_index do |(content, filename), index|
31
+ form.panels_association model: Comment.new(content: content), width: 'matchParent', order_index: index,
32
+ childViews: ->(comment) do
33
+ comment.fields_text prop: :content, label: "Comment #{index}"
34
+ comment.panels_association model: CommentFile.new(filename: filename), width: 'matchParent', order_index: 0,
35
+ childViews: ->(file) do
36
+ file.fields_text prop: :filename, label: "Comment #{index} file"
37
+ end
38
+ end
39
+ end
40
+
41
+ form.fields_submit text: 'Submit'
42
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: glib-web
3
3
  version: !ruby/object:Gem::Version
4
- version: 8.0.0
4
+ version: 8.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - ''
@@ -499,6 +499,7 @@ files:
499
499
  - app/views/json_ui/garage/test_page/multimedia_video.json.jbuilder
500
500
  - app/views/json_ui/garage/test_page/pagination.json.jbuilder
501
501
  - app/views/json_ui/garage/test_page/panels.json.jbuilder
502
+ - app/views/json_ui/garage/test_page/panels_association.json.jbuilder
502
503
  - app/views/json_ui/garage/test_page/panels_bulkEdit3.json.jbuilder
503
504
  - app/views/json_ui/garage/test_page/popovers.json.jbuilder
504
505
  - app/views/json_ui/garage/test_page/progressCircle.json.jbuilder