glib-web 5.0.4 → 5.0.5

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.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/app/helpers/glib/json_ui/view_builder/fields.rb +6 -2
  3. data/app/models/concerns/glib/enum_symbolization.rb +26 -0
  4. data/app/models/glib/application_record.rb +1 -0
  5. data/app/views/json_ui/garage/test_page/_header.json.jbuilder +31 -28
  6. data/app/views/json_ui/garage/test_page/auth.json.jbuilder +2 -1
  7. data/app/views/json_ui/garage/test_page/auto_validate.json.jbuilder +2 -1
  8. data/app/views/json_ui/garage/test_page/browsers.json.jbuilder +2 -1
  9. data/app/views/json_ui/garage/test_page/calendar.json.jbuilder +2 -1
  10. data/app/views/json_ui/garage/test_page/carousel.json.jbuilder +2 -1
  11. data/app/views/json_ui/garage/test_page/charts.json.jbuilder +2 -1
  12. data/app/views/json_ui/garage/test_page/column.json.jbuilder +2 -1
  13. data/app/views/json_ui/garage/test_page/commands.json.jbuilder +2 -1
  14. data/app/views/json_ui/garage/test_page/components.json.jbuilder +2 -1
  15. data/app/views/json_ui/garage/test_page/cookies.json.jbuilder +2 -1
  16. data/app/views/json_ui/garage/test_page/custom.json.jbuilder +2 -1
  17. data/app/views/json_ui/garage/test_page/dialog.json.jbuilder +2 -1
  18. data/app/views/json_ui/garage/test_page/dirty_state.json.jbuilder +2 -1
  19. data/app/views/json_ui/garage/test_page/fields.json.jbuilder +2 -1
  20. data/app/views/json_ui/garage/test_page/fields_captcha.json.jbuilder +2 -1
  21. data/app/views/json_ui/garage/test_page/fields_creditCard.json.jbuilder +2 -1
  22. data/app/views/json_ui/garage/test_page/fields_date_time.json.jbuilder +2 -1
  23. data/app/views/json_ui/garage/test_page/fields_dynamicSelect.json.jbuilder +2 -1
  24. data/app/views/json_ui/garage/test_page/fields_location.json.jbuilder +2 -1
  25. data/app/views/json_ui/garage/test_page/fields_otp.json.jbuilder +2 -1
  26. data/app/views/json_ui/garage/test_page/fields_phone.json.jbuilder +2 -1
  27. data/app/views/json_ui/garage/test_page/fields_radio.json.jbuilder +293 -0
  28. data/app/views/json_ui/garage/test_page/fields_rating.json.jbuilder +2 -1
  29. data/app/views/json_ui/garage/test_page/fields_richText.json.jbuilder +2 -1
  30. data/app/views/json_ui/garage/test_page/fields_select.json.jbuilder +3 -2
  31. data/app/views/json_ui/garage/test_page/fields_sign.json.jbuilder +2 -1
  32. data/app/views/json_ui/garage/test_page/fields_stripeExternalAccount.json.jbuilder +2 -1
  33. data/app/views/json_ui/garage/test_page/fields_stripeToken.json.jbuilder +2 -1
  34. data/app/views/json_ui/garage/test_page/fields_text.json.jbuilder +312 -0
  35. data/app/views/json_ui/garage/test_page/fields_timer.json.jbuilder +2 -1
  36. data/app/views/json_ui/garage/test_page/fields_upload.json.jbuilder +66 -4
  37. data/app/views/json_ui/garage/test_page/fields_url_fragment.json.jbuilder +2 -1
  38. data/app/views/json_ui/garage/test_page/flow.json.jbuilder +2 -1
  39. data/app/views/json_ui/garage/test_page/form.json.jbuilder +2 -1
  40. data/app/views/json_ui/garage/test_page/form_dynamic.json.jbuilder +2 -1
  41. data/app/views/json_ui/garage/test_page/forms.json.jbuilder +2 -1
  42. data/app/views/json_ui/garage/test_page/grid.json.jbuilder +2 -1
  43. data/app/views/json_ui/garage/test_page/horizontal.json.jbuilder +2 -1
  44. data/app/views/json_ui/garage/test_page/http.json.jbuilder +2 -1
  45. data/app/views/json_ui/garage/test_page/image.json.jbuilder +26 -2
  46. data/app/views/json_ui/garage/test_page/lifecycle.json.jbuilder +2 -1
  47. data/app/views/json_ui/garage/test_page/list.json.jbuilder +2 -1
  48. data/app/views/json_ui/garage/test_page/list_editable.json.jbuilder +2 -1
  49. data/app/views/json_ui/garage/test_page/lists_append.json.jbuilder +2 -1
  50. data/app/views/json_ui/garage/test_page/logics_set.json.jbuilder +0 -1
  51. data/app/views/json_ui/garage/test_page/multimedia_video.json.jbuilder +2 -1
  52. data/app/views/json_ui/garage/test_page/pagination.json.jbuilder +2 -1
  53. data/app/views/json_ui/garage/test_page/panels.json.jbuilder +2 -1
  54. data/app/views/json_ui/garage/test_page/panels_bulkEdit2.json.jbuilder +2 -1
  55. data/app/views/json_ui/garage/test_page/popovers.json.jbuilder +2 -1
  56. data/app/views/json_ui/garage/test_page/progressCircle.json.jbuilder +2 -1
  57. data/app/views/json_ui/garage/test_page/responsive.json.jbuilder +2 -1
  58. data/app/views/json_ui/garage/test_page/scroll.json.jbuilder +2 -1
  59. data/app/views/json_ui/garage/test_page/selectable.json.jbuilder +2 -1
  60. data/app/views/json_ui/garage/test_page/sheets.json.jbuilder +2 -1
  61. data/app/views/json_ui/garage/test_page/snackbars.json.jbuilder +2 -1
  62. data/app/views/json_ui/garage/test_page/split.json.jbuilder +2 -1
  63. data/app/views/json_ui/garage/test_page/storage_items.json.jbuilder +2 -1
  64. data/app/views/json_ui/garage/test_page/switch.json.jbuilder +56 -0
  65. data/app/views/json_ui/garage/test_page/tabBar.json.jbuilder +144 -0
  66. data/app/views/json_ui/garage/test_page/table.json.jbuilder +2 -1
  67. data/app/views/json_ui/garage/test_page/timeline.json.jbuilder +2 -1
  68. data/app/views/json_ui/garage/test_page/timeouts.json.jbuilder +9 -3
  69. data/app/views/json_ui/garage/test_page/ul.json.jbuilder +2 -1
  70. data/app/views/json_ui/garage/test_page/vertical.json.jbuilder +2 -1
  71. data/app/views/json_ui/garage/test_page/web.json.jbuilder +2 -1
  72. data/app/views/json_ui/garage/test_page/window.json.jbuilder +2 -1
  73. data/app/views/json_ui/garage/test_page/windows.json.jbuilder +2 -1
  74. data/lib/glib/json_crawler/router.rb +39 -0
  75. data/lib/glib/test_helpers.rb +68 -2
  76. metadata +6 -1
@@ -0,0 +1,144 @@
1
+ json.title 'Test Page (tabBar)'
2
+
3
+ page = json_ui_page json
4
+
5
+ render 'json_ui/garage/test_page/header', json: json, page: page
6
+
7
+ active_index = params[:tab].to_i
8
+
9
+ page.body(
10
+ childViews: ->(body) do
11
+ body.panels_responsive(
12
+ padding: glib_json_padding_body,
13
+ childViews: ->(res) do
14
+ res.h2 text: 'Overview'
15
+ res.p(
16
+ text: 'tabBar renders a row of icon/text buttons used for primary navigation. ' \
17
+ 'It supports badges, active-tab highlighting, style variants, and custom onClick actions.'
18
+ )
19
+ res.spacer height: 12
20
+ res.hr width: 'matchParent'
21
+ res.h2 text: 'Variants and Props'
22
+ res.spacer height: 8
23
+ res.label text: 'With badges'
24
+ res.spacer height: 6
25
+ res.tabBar(
26
+ activeIndex: 0,
27
+ buttons: ->(menu) do
28
+ [
29
+ { text: 'Inbox', icon: 'inbox', badge: { text: 5, backgroundColor: '#272551' } },
30
+ { text: 'Alerts', icon: 'notifications', badge: { text: '!', backgroundColor: '#cc0000' } },
31
+ { text: 'Messages', icon: 'chat', badge: { text: nil, backgroundColor: nil } }
32
+ ].each do |tab|
33
+ menu.button(
34
+ icon: tab[:icon],
35
+ text: tab[:text],
36
+ badge: tab[:badge],
37
+ disabled: false,
38
+ onClick: ->(action) do
39
+ action.snackbars_alert message: "Tapped #{tab[:text]}"
40
+ end
41
+ )
42
+ end
43
+ end
44
+ )
45
+ res.spacer height: 12
46
+ res.label text: "Style: 'full-width-divider'"
47
+ res.spacer height: 6
48
+ res.tabBar(
49
+ activeIndex: 0,
50
+ styleClass: 'full-width-divider',
51
+ buttons: ->(menu) do
52
+ 4.times do |index|
53
+ menu.button(
54
+ text: "Tab #{index}",
55
+ disabled: index == 0,
56
+ onClick: ->(action) do
57
+ action.snackbars_alert message: "Tapped tab #{index}"
58
+ end
59
+ )
60
+ end
61
+ end
62
+ )
63
+ res.spacer height: 12
64
+ res.label text: "Style: 'no-grow'"
65
+ res.spacer height: 6
66
+ res.tabBar(
67
+ activeIndex: 0,
68
+ styleClass: 'no-grow',
69
+ buttons: ->(menu) do
70
+ 3.times do |index|
71
+ menu.button(
72
+ text: "Tab #{index}",
73
+ disabled: index == 0,
74
+ onClick: ->(action) do
75
+ action.snackbars_alert message: "Tapped tab #{index}"
76
+ end
77
+ )
78
+ end
79
+ end
80
+ )
81
+
82
+ res.spacer height: 12
83
+ res.hr width: 'matchParent'
84
+ res.h2 text: 'Edge and Advanced'
85
+ res.spacer height: 8
86
+ res.label text: 'All tabs disabled'
87
+ res.spacer height: 6
88
+ res.tabBar(
89
+ activeIndex: -1,
90
+ buttons: ->(menu) do
91
+ ['One', 'Two', 'Three'].each do |label|
92
+ menu.button(
93
+ text: label,
94
+ disabled: true,
95
+ onClick: ->(action) do
96
+ action.snackbars_alert message: "Tapped #{label}"
97
+ end
98
+ )
99
+ end
100
+ end
101
+ )
102
+ res.spacer height: 12
103
+ res.label text: 'Many tabs (overflow)'
104
+ res.spacer height: 6
105
+ res.tabBar(
106
+ activeIndex: 0,
107
+ buttons: ->(menu) do
108
+ 8.times do |index|
109
+ menu.button(
110
+ text: "Tab #{index}",
111
+ disabled: index == 0,
112
+ onClick: ->(action) do
113
+ action.snackbars_alert message: "Tapped tab #{index}"
114
+ end
115
+ )
116
+ end
117
+ end
118
+ )
119
+ res.spacer height: 12
120
+ res.label text: 'Long badge text'
121
+ res.spacer height: 6
122
+ res.tabBar(
123
+ activeIndex: 0,
124
+ buttons: ->(menu) do
125
+ menu.button(
126
+ icon: 'star',
127
+ text: 'Stars',
128
+ disabled: true,
129
+ badge: { text: '⭐⭐⭐', backgroundColor: '#008000' },
130
+ onClick: ->(action) { action.snackbars_alert message: 'Stars' }
131
+ )
132
+ menu.button(
133
+ icon: 'inbox',
134
+ text: 'Inbox',
135
+ disabled: false,
136
+ badge: { text: 999, backgroundColor: '#272551' },
137
+ onClick: ->(action) { action.snackbars_alert message: 'Inbox' }
138
+ )
139
+ end
140
+ )
141
+ end
142
+ )
143
+ end
144
+ )
@@ -2,9 +2,10 @@ json.title 'Test Page (Table)'
2
2
 
3
3
  page = json_ui_page json
4
4
 
5
+ render 'json_ui/garage/test_page/header', json: json, page: page
6
+
5
7
  page.body(
6
8
  childViews: ->(body) do
7
- render 'json_ui/garage/test_page/header', view: body
8
9
  body.panels_responsive(
9
10
  padding: glib_json_padding_body,
10
11
  childViews: ->(res) do
@@ -2,9 +2,10 @@ json.title 'Test Page (Timeline)'
2
2
 
3
3
  page = json_ui_page json
4
4
 
5
+ render 'json_ui/garage/test_page/header', json: json, page: page
6
+
5
7
  page.body(
6
8
  childViews: ->(body) do
7
- render 'json_ui/garage/test_page/header', view: body
8
9
  body.panels_responsive(
9
10
  padding: glib_json_padding_body,
10
11
  childViews: ->(res) do
@@ -2,9 +2,10 @@ json.title 'Test Page (Timeouts)'
2
2
 
3
3
  page = json_ui_page json
4
4
 
5
+ render 'json_ui/garage/test_page/header', json: json, page: page
6
+
5
7
  page.body(
6
8
  childViews: ->(body) do
7
- render 'json_ui/garage/test_page/header', view: body
8
9
  body.panels_responsive(
9
10
  padding: glib_json_padding_body,
10
11
  childViews: ->(res) do
@@ -58,8 +59,13 @@ page.body(
58
59
  interval: 2000,
59
60
  repeat: true,
60
61
  onTimeout: ->(subaction) do
61
- subaction.logics_set targetId: 'timeout_repeat_status', data: { text: 'Last tick fired' }
62
- subaction.snackbars_alert message: 'Repeat timeout elapsed'
62
+ subaction.logics_set(
63
+ targetId: 'timeout_repeat_status',
64
+ data: { text: 'Last tick fired' },
65
+ onSet: ->(ssaction) do
66
+ ssaction.snackbars_alert message: 'Repeat timeout elapsed'
67
+ end
68
+ )
63
69
  end
64
70
  )
65
71
  end
@@ -2,8 +2,9 @@ json.title 'Test Page (Ul)'
2
2
 
3
3
  page = json_ui_page json
4
4
 
5
+ render 'json_ui/garage/test_page/header', json: json, page: page
6
+
5
7
  page.body childViews: ->(body) do
6
- render 'json_ui/garage/test_page/header', view: body
7
8
 
8
9
  body.panels_responsive padding: glib_json_padding_body, childViews: ->(res) do
9
10
  res.h2 text: 'Overview'
@@ -2,8 +2,9 @@ json.title 'Test Page (Vertical)'
2
2
 
3
3
  page = json_ui_page json
4
4
 
5
+ render 'json_ui/garage/test_page/header', json: json, page: page
6
+
5
7
  page.body childViews: ->(body) do
6
- render 'json_ui/garage/test_page/header', view: body
7
8
 
8
9
  body.panels_responsive padding: glib_json_padding_body, childViews: ->(res) do
9
10
  res.h2 text: 'Overview'
@@ -2,8 +2,9 @@ json.title 'Test Page (Web)'
2
2
 
3
3
  page = json_ui_page json
4
4
 
5
+ render 'json_ui/garage/test_page/header', json: json, page: page
6
+
5
7
  page.body childViews: ->(body) do
6
- render 'json_ui/garage/test_page/header', view: body
7
8
 
8
9
  body.panels_responsive padding: glib_json_padding_body, childViews: ->(res) do
9
10
  res.h2 text: 'Overview'
@@ -3,9 +3,10 @@ json.title 'Test Page (Form)'
3
3
 
4
4
  page = json_ui_page json
5
5
 
6
+ render 'json_ui/garage/test_page/header', json: json, page: page
7
+
6
8
  page.body(
7
9
  childViews: ->(body) do
8
- render 'json_ui/garage/test_page/header', view: body
9
10
  body.panels_responsive(
10
11
  padding: glib_json_padding_body,
11
12
  childViews: ->(res) do
@@ -2,9 +2,10 @@ json.title 'Test Page (Windows)'
2
2
 
3
3
  page = json_ui_page json
4
4
 
5
+ render 'json_ui/garage/test_page/header', json: json, page: page
6
+
5
7
  page.body(
6
8
  childViews: ->(body) do
7
- render 'json_ui/garage/test_page/header', view: body
8
9
  body.panels_responsive(
9
10
  padding: glib_json_padding_body,
10
11
  childViews: ->(res) do
@@ -74,6 +74,16 @@ module Glib
74
74
  JsonCrawler::FormsSubmit.new(http, args)
75
75
  @depth -= 1
76
76
  return
77
+ when 'panels/web-v1', 'panels/web'
78
+ # A panels/web embeds content by URL in an inline viewer -- a file
79
+ # preview (PDF/image), an inline HTML preview, etc. There's no onClick
80
+ # action to catch here, so when the URL is one of our own (same-host)
81
+ # endpoints we record it directly: the client fetches it to render the
82
+ # panel, so its authorization should be exercised by the permission test
83
+ # (it replays each per user and snapshots the response). We only record
84
+ # (no fetch); external embeds are filtered out by internal_url?.
85
+ url = args['url']
86
+ http_actions.add(['panels/web-v1', url]) if url.present? && internal_url?(url)
77
87
  end
78
88
 
79
89
  if args.is_a?(Hash) && args['rel'] != 'nofollow'
@@ -111,6 +121,24 @@ module Glib
111
121
  http_actions.add([action, params['url']])
112
122
  JsonCrawler::WindowsOpen.new(http, params, action)
113
123
  else
124
+ # IMPORTANT — do not drop the `http_actions.add` below.
125
+ #
126
+ # This `else` is reached by BOTH genuinely external links AND by
127
+ # same-host file/download endpoints that `allowed?` rejected for the
128
+ # file-extension rule (e.g. *.pdf). Recording the same-host ones is the
129
+ # ONLY way their file authorization ever gets exercised: the permission
130
+ # test replays each recorded action per user and snapshots the response.
131
+ # Remove this line and every file/download endpoint silently falls out
132
+ # of permission coverage -- a wrong authorization on one would then ship
133
+ # unnoticed (exactly the gap this was added to close).
134
+ #
135
+ # We do NOT traverse/download them (no WindowsOpen crawler is created
136
+ # here), and the permission replay runs with inspect_http:false so it
137
+ # never follows the storage redirect. External links are filtered out by
138
+ # `internal_url?`. Behaviour is guarded by
139
+ # test/dummy-app/test/json_crawler/router_test.rb -- if you delete the
140
+ # line below, that test goes red.
141
+ http_actions.add([action, params['url']]) if internal_url?(params['url'])
114
142
  self.log action, params['url']
115
143
  end
116
144
  when 'dialogs/show-v1', 'dialogs/show', 'popovers/show-v1', 'popovers/show'
@@ -227,6 +255,17 @@ module Glib
227
255
  end
228
256
 
229
257
  private
258
+ # True when `url` is one of our OWN (same-host) endpoints, used to tell an
259
+ # internal endpoint we want recorded for the permission test from a
260
+ # genuinely external link we ignore (in the openWeb `else`-branch and the
261
+ # panels/web case). Anchored at the start and required to end at a path
262
+ # boundary (`/`, `?`, `#`, or end-of-string), so a host that merely appears
263
+ # INSIDE an external URL's path or query is NOT treated as internal --
264
+ # unlike a bare substring match.
265
+ def internal_url?(url)
266
+ %r{\Ahttps?://#{Regexp.escape(host)}(?:[/?#]|\z)}.match?(url.to_s)
267
+ end
268
+
230
269
  def execute_crawler_action(http, action, url, params)
231
270
  params = JSON.parse(params) if params.is_a?(String)
232
271
  params ||= {}
@@ -66,6 +66,44 @@ module Glib
66
66
  File.join(file_dir || __crawler_log_dir, filename)
67
67
  end
68
68
 
69
+ # Asserts that every model with ActiveStorage attachments is actually
70
+ # *exercised* by the crawler: at least one of its file endpoints must appear
71
+ # in the recorded crawler output. A structural "is this type authorized?"
72
+ # check can only prove a type is handled, not that its file authorization is
73
+ # covered end-to-end. This proves the latter -- the crawler recorded the
74
+ # endpoint, so the permission test replays it per user -- which is what
75
+ # catches a *wrong* authorization on a newly-added attachable type (or a
76
+ # fixture that was never made reachable in the crawl).
77
+ #
78
+ # attachable_types: model classes (or names) that must be covered.
79
+ # router_dir: dir holding the crawler router CSVs (the dump_path).
80
+ # signed_id_from: ->(url) returning the blob signed_id for a file URL, or
81
+ # nil for non-file URLs. Route-specific, e.g.
82
+ # ->(u) { u[%r{/blobs/([^/]+)/}, 1] }.
83
+ # exempt: type names whose files are served outside the crawl.
84
+ # pending: type names known-uncovered -- an explicit burn-down
85
+ # list, kept honest by the stale-pending check below.
86
+ def assert_attachable_types_crawl_covered(attachable_types:, router_dir:, signed_id_from:, exempt: [], pending: [])
87
+ required = attachable_types.map(&:to_s) - exempt.map(&:to_s) - pending.map(&:to_s)
88
+ covered = __crawler_covered_record_types(router_dir, signed_id_from)
89
+
90
+ uncovered = (required - covered).sort
91
+ assert_empty(
92
+ uncovered,
93
+ 'No crawled file endpoint covers these attachable types, so the permission test ' \
94
+ 'never exercises their file authorization (a wrong policy on them would ship ' \
95
+ 'silently). Give each a fixture whose file renders in a crawled page and regenerate ' \
96
+ "the crawler snapshots, or list it in `exempt:`/`pending:` -- #{uncovered.join(', ')}"
97
+ )
98
+
99
+ graduated = (pending.map(&:to_s) & covered).sort
100
+ assert_empty(
101
+ graduated,
102
+ 'These `pending:` types are now crawl-covered -- remove them from the burn-down ' \
103
+ "list so it keeps reflecting the real gap: #{graduated.join(', ')}"
104
+ )
105
+ end
106
+
69
107
  def retrace_json_pages(user, past_actions:)
70
108
  __execute_crawler(user, inspect_http: false) do |router, http|
71
109
  # router.follow(http, guiding_routers)
@@ -92,7 +130,7 @@ module Glib
92
130
  # @param method [Symbol, nil] Optional HTTP method override
93
131
  #
94
132
  # @example
95
- # get new_chat_url(checklist_id: @checklist.id), params: json_params
133
+ # get new_chat_url(post_id: @post.id), params: json_params
96
134
  # submit_form(response.body, { chat: { message_body: 'Hello' } })
97
135
  #
98
136
  def submit_form(page_payload, data, url: nil, method: nil)
@@ -211,7 +249,7 @@ module Glib
211
249
 
212
250
  block.call(view)
213
251
 
214
- ['childViews', 'header', 'body', 'footer', 'left', 'right', 'panels'].each do |key|
252
+ ['childViews', 'header', 'body', 'footer', 'left', 'right', 'panels', 'fullPageForm'].each do |key|
215
253
  child = view[key]
216
254
  if child.is_a?(Array)
217
255
  __traverse_form_views(child, &block)
@@ -223,6 +261,34 @@ module Glib
223
261
 
224
262
  # --- Crawler helpers ---
225
263
 
264
+ # Reads the crawler router CSVs and returns the distinct ActiveStorage
265
+ # `record_type`s reachable through the recorded file endpoints. Each row's
266
+ # URL is mapped to a blob via the caller-supplied `signed_id_from` (the
267
+ # only route-specific bit), then blob -> attachment -> record_type.
268
+ def __crawler_covered_record_types(router_dir, signed_id_from)
269
+ require 'csv'
270
+
271
+ Dir.glob(File.join(router_dir, '*.csv')).flat_map do |path|
272
+ CSV.read(path).filter_map do |row|
273
+ url = row[1]
274
+ next if url.blank?
275
+
276
+ signed_id = signed_id_from.call(url)
277
+ next if signed_id.blank?
278
+
279
+ blob =
280
+ begin
281
+ ActiveStorage::Blob.find_signed(signed_id)
282
+ rescue StandardError
283
+ nil
284
+ end
285
+ next if blob.nil?
286
+
287
+ ActiveStorage::Attachment.find_by(blob: blob)&.record_type
288
+ end
289
+ end.compact.uniq
290
+ end
291
+
226
292
  def __execute_crawler(user, inspect_http:, log_file: nil, &block)
227
293
  auth_token = login user
228
294
  user[:token] = auth_token
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: 5.0.4
4
+ version: 5.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - ''
@@ -220,6 +220,7 @@ files:
220
220
  - app/helpers/glib/json_ui/view_builder/panels.rb
221
221
  - app/helpers/glib/urls_helper.rb
222
222
  - app/models/concerns/glib/enum_humanization.rb
223
+ - app/models/concerns/glib/enum_symbolization.rb
223
224
  - app/models/concerns/glib/nilify_blanks.rb
224
225
  - app/models/concerns/glib/soft_deletable.rb
225
226
  - app/models/glib/active_storage/attachment.rb
@@ -405,12 +406,14 @@ files:
405
406
  - app/views/json_ui/garage/test_page/fields_location.json.jbuilder
406
407
  - app/views/json_ui/garage/test_page/fields_otp.json.jbuilder
407
408
  - app/views/json_ui/garage/test_page/fields_phone.json.jbuilder
409
+ - app/views/json_ui/garage/test_page/fields_radio.json.jbuilder
408
410
  - app/views/json_ui/garage/test_page/fields_rating.json.jbuilder
409
411
  - app/views/json_ui/garage/test_page/fields_richText.json.jbuilder
410
412
  - app/views/json_ui/garage/test_page/fields_select.json.jbuilder
411
413
  - app/views/json_ui/garage/test_page/fields_sign.json.jbuilder
412
414
  - app/views/json_ui/garage/test_page/fields_stripeExternalAccount.json.jbuilder
413
415
  - app/views/json_ui/garage/test_page/fields_stripeToken.json.jbuilder
416
+ - app/views/json_ui/garage/test_page/fields_text.json.jbuilder
414
417
  - app/views/json_ui/garage/test_page/fields_timer.json.jbuilder
415
418
  - app/views/json_ui/garage/test_page/fields_upload.json.jbuilder
416
419
  - app/views/json_ui/garage/test_page/fields_url_fragment.json.jbuilder
@@ -440,6 +443,8 @@ files:
440
443
  - app/views/json_ui/garage/test_page/snackbars.json.jbuilder
441
444
  - app/views/json_ui/garage/test_page/split.json.jbuilder
442
445
  - app/views/json_ui/garage/test_page/storage_items.json.jbuilder
446
+ - app/views/json_ui/garage/test_page/switch.json.jbuilder
447
+ - app/views/json_ui/garage/test_page/tabBar.json.jbuilder
443
448
  - app/views/json_ui/garage/test_page/table.json.jbuilder
444
449
  - app/views/json_ui/garage/test_page/timeline.json.jbuilder
445
450
  - app/views/json_ui/garage/test_page/timeouts.json.jbuilder