glib-web 7.0.0.beta1 → 7.0.1.beta1

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.
@@ -0,0 +1,128 @@
1
+ json.title 'Test Page (Fields Change Trigger)'
2
+
3
+ page = json_ui_page json
4
+
5
+ render 'json_ui/garage/test_page/header', json: json, page: page
6
+
7
+ page.body(
8
+ childViews: ->(body) do
9
+
10
+ body.panels_responsive(
11
+ padding: glib_json_padding_body,
12
+ childViews: ->(res) do
13
+ res.h2 text: 'Fields Change Trigger'
14
+ res.spacer height: 8
15
+ res.label text: 'changeOn / changeDelay control WHEN a typed-input field reports its change to onChange.'
16
+ res.spacer height: 12
17
+
18
+ res.panels_form(
19
+ url: json_ui_garage_url(path: 'forms/generic_post'),
20
+ method: 'post',
21
+ childViews: ->(form) do
22
+
23
+ # ── Default: changeOn 'input', 300ms ──────────────────────────────
24
+ form.h4 text: 'Default (changeOn: input)'
25
+ form.spacer height: 8
26
+ form.label text: 'No options set: reports once the user pauses for the default 300ms.'
27
+ form.spacer height: 8
28
+
29
+ form.fields_text(
30
+ name: 'user[default_value]',
31
+ label: 'Default',
32
+ placeholder: 'default',
33
+ width: 'matchParent',
34
+ onChange: ->(action) do
35
+ action.logics_set(
36
+ targetId: 'status_default',
37
+ conditionalData: { text: { 'printf': ['Default: {0}', { 'var': 'user[default_value]' }] } }
38
+ )
39
+ end
40
+ )
41
+ form.spacer height: 8
42
+ form.label id: 'status_default', text: 'Default: idle'
43
+
44
+ form.spacer height: 12
45
+ form.hr width: 'matchParent'
46
+ form.spacer height: 12
47
+
48
+ # ── changeDelay ───────────────────────────────────────────────────
49
+ form.h4 text: 'changeDelay: 1500'
50
+ form.spacer height: 8
51
+ form.label text: 'Same input mode, longer pause. Leaving the field still reports right away, rather than waiting out the remaining delay.'
52
+ form.spacer height: 8
53
+
54
+ form.fields_text(
55
+ name: 'user[slow]',
56
+ label: 'Slow',
57
+ placeholder: 'slow',
58
+ width: 'matchParent',
59
+ changeDelay: 1500,
60
+ onChange: ->(action) do
61
+ action.logics_set(
62
+ targetId: 'status_slow',
63
+ conditionalData: { text: { 'printf': ['Slow: {0}', { 'var': 'user[slow]' }] } }
64
+ )
65
+ end
66
+ )
67
+ form.spacer height: 8
68
+ form.label id: 'status_slow', text: 'Slow: idle'
69
+
70
+ form.spacer height: 12
71
+ form.hr width: 'matchParent'
72
+ form.spacer height: 12
73
+
74
+ # ── changeOn: 'blur' ──────────────────────────────────────────────
75
+ form.h4 text: "changeOn: 'blur'"
76
+ form.spacer height: 8
77
+ form.label text: 'Stays idle no matter how long the user pauses; reports only once focus leaves.'
78
+ form.spacer height: 8
79
+
80
+ form.fields_text(
81
+ name: 'user[on_blur]',
82
+ label: 'Reports on blur',
83
+ placeholder: 'on blur',
84
+ width: 'matchParent',
85
+ changeOn: 'blur',
86
+ onChange: ->(action) do
87
+ action.logics_set(
88
+ targetId: 'status_on_blur',
89
+ conditionalData: { text: { 'printf': ['Blur: {0}', { 'var': 'user[on_blur]' }] } }
90
+ )
91
+ end
92
+ )
93
+ form.spacer height: 8
94
+ form.label id: 'status_on_blur', text: 'Blur: idle'
95
+
96
+ form.spacer height: 12
97
+ form.hr width: 'matchParent'
98
+ form.spacer height: 12
99
+
100
+ # ── Textarea on blur ──────────────────────────────────────────────
101
+ form.h4 text: "fields_textarea with changeOn: 'blur'"
102
+ form.spacer height: 8
103
+
104
+ form.fields_textarea(
105
+ name: 'user[notes]',
106
+ label: 'Notes',
107
+ placeholder: 'notes',
108
+ width: 'matchParent',
109
+ rows: 3,
110
+ changeOn: 'blur',
111
+ onChange: ->(action) do
112
+ action.logics_set(
113
+ targetId: 'status_notes',
114
+ conditionalData: { text: { 'printf': ['Notes: {0}', { 'var': 'user[notes]' }] } }
115
+ )
116
+ end
117
+ )
118
+ form.spacer height: 8
119
+ form.label id: 'status_notes', text: 'Notes: idle'
120
+
121
+ form.spacer height: 16
122
+ form.fields_submit text: 'Submit'
123
+ end
124
+ )
125
+ end
126
+ )
127
+ end
128
+ )
@@ -1,5 +1,8 @@
1
1
  json.title 'Test Page (Fields Sign)'
2
2
 
3
+ # 60x20 ink stand-in used by the pre-filled examples.
4
+ preview_data_uri = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAAUCAYAAADRA14pAAAAoklEQVR4nO2XUQrAIAxDcwjvf9WNfQzG1NFqUt1mwL8a8hBtxfYzYXSAaC1glVJK2RohOXAJdCS4FNgCGw0tA7YAjQCXAHsBoqAPbzpwa3A19OkLpjkjsAL66gmWOTOo0gtvOJXe/VcP9JpH3DvmewBLkdeUKfaLD09xqSaid7bkqilrS57pKHJCYmWq9uFZQD2ZLLkeB49ZQO/qybX+w1/XDujJvn2bufeCAAAAAElFTkSuQmCC'
5
+
3
6
  page = json_ui_page json
4
7
 
5
8
  render 'json_ui/garage/test_page/header', json: json, page: page
@@ -25,6 +28,11 @@ page.body(
25
28
  form.fields_sign(
26
29
  id: 'signature_main',
27
30
  name: 'user[signature]',
31
+ label: 'Your signature',
32
+ # Single-param contract: the typed name is submitted through the
33
+ # field's own param, so typedName carries only the input's
34
+ # decoration and pre-filled value.
35
+ typedName: { label: 'Type your full name', placeholder: 'Full name' },
28
36
  directUploadUrl: glib_direct_uploads_url,
29
37
  width: 320,
30
38
  height: 160,
@@ -137,7 +145,7 @@ page.body(
137
145
 
138
146
  form.h2 text: 'Edge/advanced'
139
147
  form.spacer height: 8
140
- form.label text: 'Use a small canvas to test tighter layouts.'
148
+ form.label text: 'A small, draw-only slot (no typedName, so the dialog has no tabs).'
141
149
  form.spacer height: 8
142
150
  form.fields_sign(
143
151
  id: 'signature_compact',
@@ -146,6 +154,46 @@ page.body(
146
154
  width: 220,
147
155
  height: 100
148
156
  )
157
+
158
+ form.spacer height: 12
159
+ form.hr width: 'matchParent'
160
+ form.spacer height: 12
161
+
162
+ form.h2 text: 'Pre-filled (re-edit)'
163
+ form.spacer height: 8
164
+ form.label text: 'A saved signature: value carries the signed id, fileUrl renders the preview.'
165
+ form.spacer height: 8
166
+ form.fields_sign(
167
+ id: 'signature_prefilled',
168
+ name: 'user[signature_prefilled]',
169
+ # A stand-in for the signed id the Sign builder derives from
170
+ # `prop:` on a re-edit (see Sign#determine_value).
171
+ value: 'prefilled-signed-id',
172
+ fileUrl: preview_data_uri,
173
+ typedName: { label: 'Type your full name', placeholder: 'Full name' },
174
+ directUploadUrl: glib_direct_uploads_url,
175
+ width: 320,
176
+ height: 160
177
+ )
178
+
179
+ form.spacer height: 12
180
+ form.hr width: 'matchParent'
181
+ form.spacer height: 12
182
+
183
+ form.h2 text: 'Read-only (pre-filled)'
184
+ form.spacer height: 8
185
+ form.label text: 'A saved signature on a read-only field: the preview shows, the edit overlay does not.'
186
+ form.spacer height: 8
187
+ form.fields_sign(
188
+ id: 'signature_readonly',
189
+ name: 'user[signature_readonly]',
190
+ value: 'readonly-signed-id',
191
+ fileUrl: preview_data_uri,
192
+ directUploadUrl: glib_direct_uploads_url,
193
+ width: 320,
194
+ height: 160,
195
+ readOnly: true
196
+ )
149
197
  end
150
198
  )
151
199
  end
@@ -0,0 +1,54 @@
1
+ module Glib
2
+ module Generators
3
+ # Installs the Glib::SnapshotV2 database prerequisites. Two opt-in pieces, each produced
4
+ # only when the corresponding flag is passed:
5
+ #
6
+ # `rails g glib:snapshot_v2` # adds V2 columns only (required for V2)
7
+ # `rails g glib:snapshot_v2 --with-blob-references` # also creates the blob-reference table
8
+ #
9
+ # Deliberately separate from `glib:install`: that generator overwrites `config/database.yml`
10
+ # and writes dynamic-text migrations to the non-standard `db/dynamic_text_migrate/` path,
11
+ # neither of which a snapshot adopter wants re-run on their app. Snapshot tables live in the
12
+ # primary DB and use the standard `db/migrate/` path.
13
+ #
14
+ # The V2 columns migration adds `action` / `version` / `diff` (jsonb) / `source` plus the
15
+ # two composite indexes Glib::SnapshotV2's queries rely on. It does NOT backfill — adopters
16
+ # who need to migrate legacy V1 snapshots (stored under metadata) own that one-way data
17
+ # conversion themselves. See issue #960.
18
+ class SnapshotV2Generator < Rails::Generators::Base
19
+ include Rails::Generators::Migration
20
+
21
+ source_root File.expand_path('../templates/snapshot_v2', __dir__)
22
+
23
+ class_option :with_blob_references, type: :boolean, default: false,
24
+ desc: 'Also create the snapshot_blob_references table (required to version attached files)'
25
+
26
+ def copy_v2_columns_migration
27
+ migration_template 'add_snapshot_v2_columns.rb',
28
+ 'db/migrate/add_snapshot_v2_columns.rb'
29
+ end
30
+
31
+ def copy_blob_references_migration
32
+ return unless options[:with_blob_references]
33
+
34
+ migration_template 'create_snapshot_blob_references.rb',
35
+ 'db/migrate/create_snapshot_blob_references.rb'
36
+ end
37
+
38
+ private
39
+ # Rails::Generators::Migration requires this class method. Returns the UTC timestamp for
40
+ # the next migration filename. The mutex + @last_number pair guarantees the second template
41
+ # (create_snapshot_blob_references) lands strictly AFTER the first (add_snapshot_v2_columns)
42
+ # within a single invocation — otherwise both share the same timestamp and load order is
43
+ # non-deterministic.
44
+ @@migration_mutex = Mutex.new
45
+
46
+ def self.next_migration_number(_dirname)
47
+ @@migration_mutex.synchronize do
48
+ @last_snapshot_v2_number ||= (Time.now.utc - 1.second).strftime('%Y%m%d%H%M%S')
49
+ @last_snapshot_v2_number = (@last_snapshot_v2_number.to_i + 1).to_s
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,18 @@
1
+ class AddSnapshotV2Columns < ActiveRecord::Migration[8.0]
2
+ def change
3
+ # PostgreSQL has jsonb (binary, indexable); SQLite (used by glib-web's dummy-app) and MySQL
4
+ # only have json. Pick the richest type the adapter supports so consumers on PG get indexable
5
+ # diffs while greenfield adopters on SQLite/MySQL still work without edits.
6
+ diff_type = connection.adapter_name.match?(/PostgreSQL/i) ? :jsonb : :json
7
+
8
+ change_table :snapshots, bulk: true do |t|
9
+ t.string :action
10
+ t.integer :version
11
+ t.column :diff, diff_type
12
+ t.string :source
13
+ end
14
+
15
+ add_index :snapshots, [:action, :item_type, :item_id]
16
+ add_index :snapshots, [:item_type, :item_id, :version]
17
+ end
18
+ end
@@ -0,0 +1,10 @@
1
+ class CreateSnapshotBlobReferences < ActiveRecord::Migration[8.0]
2
+ def change
3
+ create_table :snapshot_blob_references do |t|
4
+ t.belongs_to :snapshot, null: false, foreign_key: { to_table: :snapshots }
5
+ t.belongs_to :blob, null: false, foreign_key: { to_table: :active_storage_blobs }
6
+ t.timestamps
7
+ end
8
+ add_index :snapshot_blob_references, [:snapshot_id, :blob_id], unique: true
9
+ end
10
+ end
data/lib/glib/engine.rb CHANGED
@@ -22,6 +22,32 @@ module Glib
22
22
  scope :created_desc, -> { order(created_at: :desc, id: :desc) }
23
23
  end
24
24
 
25
+ # Cascade-destroy blob references when a snapshot is destroyed (e.g. by max_snapshots
26
+ # retention in Glib::SnapshotV2#remove_old_snapshot). Without this, the FK on
27
+ # snapshot_blob_references would raise PG::ForeignKeyViolation.
28
+ #
29
+ # Opt-in: only consumers who run the gem's `create_snapshot_blob_references` migration
30
+ # have the table. The connection+table_exists? guard (wrapped in a rescue for boot
31
+ # sequences with no DB reachable — `rails db:create`, parallel test setup, and DB-less
32
+ # build containers like docker's `assets:precompile`) makes this a no-op for everyone
33
+ # else, so a bare `include Glib::SnapshotV2` never crashes a boot.
34
+ unless respond_to?(:snapshot_blob_references)
35
+ begin
36
+ if connection.table_exists?(:snapshot_blob_references)
37
+ has_many :snapshot_blob_references, dependent: :destroy
38
+ end
39
+ rescue ActiveRecord::NoDatabaseError, ActiveRecord::StatementInvalid,
40
+ ActiveRecord::ConnectionNotEstablished => e
41
+ # NoDatabaseError/StatementInvalid: the server exists but the database doesn't yet
42
+ # (e.g. `rails db:create`). ConnectionNotEstablished: no server reachable at all —
43
+ # e.g. docker build's assets:precompile, where DATABASE_HOST is unset and pg falls
44
+ # back to the local socket. The association gets re-registered on the next
45
+ # successful boot that finds the table. Warn (stderr, not the app log) so the
46
+ # breadcrumb lands in build/boot logs regardless of the configured log level.
47
+ warn "[glib-web] snapshot_blob_references association not registered (#{e.class}) — no DB reachable at boot"
48
+ end
49
+ end
50
+
25
51
  # Parses the stored diff into a flat hash of Change structs:
26
52
  # { 'item' => [Change, ...], 'association_name' => [Change, ...], ... }
27
53
  # Returns an empty structure if no diff exists (e.g. the first snapshot).
@@ -93,7 +119,13 @@ module Glib
93
119
 
94
120
  private
95
121
  # Item-level change: from/to are scalar column values. Cast each to the column's real type.
122
+ #
123
+ # `index` is normalized to a Symbol so consumers compare against symbol literals
124
+ # (matching AR attribute keys, e.g. `change.index == :title`). Association-level
125
+ # changes are NOT symbolized — their index is a collection position from a path like
126
+ # "[0]", a positional digit, not a name.
96
127
  def cast_item_change(change, root)
128
+ change.index = change.index.to_sym
97
129
  change.from = cast_column_value(root, change.index, change.from)
98
130
  change.to = cast_column_value(root, change.index, change.to)
99
131
  change
@@ -0,0 +1,38 @@
1
+ require 'active_support/core_ext/numeric/time'
2
+
3
+ module Glib
4
+ module PurgeUnattached
5
+ # Global settings for Glib::PurgeUnattachedJob.
6
+ #
7
+ # `min_age` is how long an unattached ActiveStorage blob must exist before
8
+ # the purge may remove it. The 8-hour default leaves room for multi-step
9
+ # upload flows (direct upload -> form submit -> attach) to finish before
10
+ # their blob becomes eligible; apps with slower flows raise it:
11
+ #
12
+ # # config/initializers/glib.rb
13
+ # Glib::PurgeUnattached::Config.min_age = 10.hours
14
+ #
15
+ # Deliberately NOT a setting on the job class: the job is autoloaded from
16
+ # app/, so an initializer-assigned value would be wiped the first time the
17
+ # class is reloaded. This file lives in lib/ and is required eagerly by
18
+ # glib-web.rb, which is what makes the setting survive reloads.
19
+ class Config
20
+ # The shipped default. Apps opt into a different window via the setter.
21
+ DEFAULT_MIN_AGE = 8.hours
22
+
23
+ @@min_age = DEFAULT_MIN_AGE
24
+
25
+ def self.min_age
26
+ @@min_age
27
+ end
28
+
29
+ def self.min_age=(value)
30
+ unless value.is_a?(ActiveSupport::Duration) && value.positive?
31
+ raise ArgumentError, "Invalid min_age: #{value.inspect}. Expected a positive ActiveSupport::Duration (e.g. 10.hours)."
32
+ end
33
+
34
+ @@min_age = value
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'English'
4
+
5
+ module Glib
6
+ module Test
7
+ # Suite-wide RuboCop auto-remediation, lifted from the per-project
8
+ # `test/linters/rubocop_test.rb` convention (originated in neowork-str) so
9
+ # projects get it from the gem via a CONSCIOUS opt-in: include
10
+ # Glib::Test::RubocopGuards once in the test setup. Never auto-require
11
+ # this from Glib::TestHelpers — that would enable a file-rewriting test
12
+ # in every project without a decision. It lints the WHOLE repo, so files
13
+ # nobody touches cannot silently accumulate offenses.
14
+ #
15
+ # Runs `rubocop -a` (safe autocorrect), which REWRITES files on disk to
16
+ # fix offenses, then asserts nothing remains AND nothing was rewritten.
17
+ # The mutation is deliberate (the repo's auto-remediation convention —
18
+ # i18n-tasks normalize runs the same way): a run that dirties a file is
19
+ # reported as a FAILURE so the rewrite cannot go unnoticed — that is the
20
+ # drift signal. Do NOT strip the `-a`; review the diff, keep the corrected
21
+ # output, and re-run. If a long-passing test suddenly breaks, investigate
22
+ # WHY first: usually a non-canonical file was committed, or a rubocop
23
+ # version bump changed a cop's rules.
24
+ #
25
+ # It self-registers with Minitest, so it runs on EVERY `rails test`
26
+ # invocation, single-file runs included. That is cheap on purpose: RuboCop
27
+ # caches per-file results, so a full-repo run on an unchanged tree costs
28
+ # about a second on a mid-size app.
29
+ #
30
+ # Deliberately does NOT skip under ENV['CI']: the local rails-test
31
+ # wrapper sets CI=true, and skipping here would silence this layer
32
+ # exactly where it runs. Opt out per-run with SKIP_FULL_SUITE_RUBOCOP=1.
33
+ class FullSuiteRubocop < ::ActiveSupport::TestCase
34
+ # `--format simple` offense lines: "C: 12: 3: [Correctable] Cop/Name: message"
35
+ # (line/column right-aligned to width 3, so the padding is 0-2 spaces).
36
+ OFFENSE_LINE = /^[A-Z]: *\d+: *\d+: /
37
+
38
+ def test_rubocop_autocorrect_then_clean
39
+ skip 'opted out via SKIP_FULL_SUITE_RUBOCOP' if ENV['SKIP_FULL_SUITE_RUBOCOP'] == '1'
40
+
41
+ # `scrub`: stderr is merged in, and one invalid byte would turn the
42
+ # scans below into an ArgumentError instead of a lint failure.
43
+ output = `bundle exec rubocop -a --format simple 2>&1`.scrub
44
+ clean = $CHILD_STATUS.success?
45
+
46
+ # Three distinct outcomes hide in one output, and each needs a
47
+ # different action — the message must say which, or an agent told
48
+ # only "offenses detected" reaches for a `rubocop:disable`:
49
+ # [Corrected] -a already rewrote it on disk (exit 0 if that's all)
50
+ # [Correctable] survived -a because only the UNSAFE -A can fix it
51
+ # neither no autocorrect exists — semantic, read the cop
52
+ # Counted on the offense LINES only, so a cop message that happens to
53
+ # contain a marker cannot shift a bucket.
54
+ offenses = output.lines.grep(OFFENSE_LINE)
55
+ corrected = offenses.count { |line| line.include?('[Corrected]') }
56
+ unsafe = offenses.count { |line| line.include?('[Correctable]') }
57
+ semantic = offenses.size - corrected - unsafe
58
+
59
+ # A Proc message is built only on failure (Minitest calls it lazily).
60
+ assert(clean && corrected.zero?, -> { failure_message(output, corrected: corrected, unsafe: unsafe, semantic: semantic) })
61
+ end
62
+
63
+ private
64
+ def failure_message(output, corrected:, unsafe:, semantic:)
65
+ parts = []
66
+ if corrected.positive?
67
+ parts << "`rubocop -a` REWROTE #{corrected} offense(s) on disk (the [Corrected] lines below). That is the " \
68
+ 'drift signal, not a problem with this test: review `git diff`, keep the corrected output, ' \
69
+ 'and re-run — do not strip the -a.'
70
+ end
71
+ if unsafe.positive?
72
+ parts << "#{unsafe} offense(s) marked [Correctable] survived `-a` because their fix is UNSAFE: apply it " \
73
+ 'with `rubocop -A <file>` only after checking what it changes, or fix by hand.'
74
+ end
75
+ if semantic.positive?
76
+ parts << "#{semantic} offense(s) have NO autocorrect, so they are semantic, not mechanical: fix each the " \
77
+ "way the cop's own message prescribes — DevDoc cops name their prescribed alternative in the " \
78
+ "offense itself (full docs: the dev-doc repo's docs/cops/). A `rubocop:disable` is only for " \
79
+ "the case a cop's own doc calls genuinely unavoidable; disables already present in a file are " \
80
+ 'narrow exceptions, not precedent.'
81
+ end
82
+ parts << 'RuboCop did not finish cleanly (see output below).' if parts.empty?
83
+ "#{parts.join("\n\n")}\n\n#{output}"
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Glib
4
+ module Test
5
+ # Conscious opt-in for the suite-time RuboCop guard. Including this module
6
+ # ONCE in the project's test setup IS the project's explicit decision to
7
+ # run it. It must stay a separate include precisely because
8
+ # Glib::TestHelpers is mixed in for unrelated reasons (crawlers, travel
9
+ # helpers): auto-requiring the guard from there would enable it without
10
+ # any conscious decision, and FullSuiteRubocop runs `rubocop -a`, which
11
+ # REWRITES files on disk.
12
+ #
13
+ # class ActiveSupport::TestCase
14
+ # include Glib::Test::RubocopGuards # opts in: full-suite `rubocop -a`
15
+ # end
16
+ #
17
+ # This module is defined as soon as the gem loads (glib/test_helpers
18
+ # requires it at file level), so the include works on its own — in
19
+ # ActiveSupport::TestCase or in a single test class — with no ordering
20
+ # dependency on Glib::TestHelpers.
21
+ #
22
+ # What the include switches on: Glib::Test::FullSuiteRubocop — `rubocop -a`
23
+ # over the whole repo on every `rails test` invocation, failing when
24
+ # anything was rewritten or remains (runs under CI too).
25
+ #
26
+ # MIGRATION NOTE: in the published releases before this change (6.9.3 and
27
+ # earlier as released), Glib::TestHelpers auto-required a report-only
28
+ # ChangedFilesRubocop guard (working-tree edits only, skipped under CI),
29
+ # so every project had it without asking. That guard is retired:
30
+ # FullSuiteRubocop is a superset, and the local rails-test wrapper sets
31
+ # CI=true, where the old guard never ran anyway. A project bumping to the
32
+ # first release that contains this change must add the include line above
33
+ # or it silently has no suite-time RuboCop guard at all.
34
+ module RubocopGuards
35
+ extend ActiveSupport::Concern
36
+
37
+ included do
38
+ require 'glib/test/full_suite_rubocop'
39
+ end
40
+ end
41
+ end
42
+ end
@@ -1,15 +1,18 @@
1
+ # The suite-time RuboCop guard (FullSuiteRubocop) is NOT auto-loaded by this
2
+ # module: it runs `rubocop -a` and rewrites files, so switching it on is a
3
+ # conscious per-project opt-in (`include Glib::Test::RubocopGuards`). Requiring
4
+ # rubocop_guards here only DEFINES that inert module — nothing runs until a
5
+ # project explicitly includes it — and doing so at file level (this file is
6
+ # required when the gem loads) means the include works anywhere, with no
7
+ # dependency on Glib::TestHelpers being included first.
8
+ require 'glib/test/rubocop_guards'
9
+
1
10
  module Glib
2
11
  module TestHelpers
3
12
  extend ActiveSupport::Concern
4
13
 
5
14
  included do
6
15
  extend ClassMethods
7
-
8
- # Auto-load ChangedFilesRubocop only when this module is mixed into a
9
- # test class. AllHelpers also includes Glib::TestHelpers into view
10
- # helper modules in production controllers; in that context
11
- # ActiveSupport::TestCase is not loaded, so we skip the require.
12
- require 'glib/test/changed_files_rubocop' if defined?(::ActiveSupport::TestCase)
13
16
  end
14
17
 
15
18
  module ClassMethods
data/lib/glib-web.rb CHANGED
@@ -6,10 +6,12 @@ if defined?(::Rails)
6
6
  end
7
7
  require 'glib/value'
8
8
  # Loaded eagerly (not autoloaded from app/) so an app initializer can set
9
- # `Glib::JsonUi::Config.name_separator` without tripping Rails' ban on
10
- # autoloading during initialization -- and so the setting survives a reload.
9
+ # `Glib::JsonUi::Config.name_separator` or `Glib::PurgeUnattached::Config.min_age`
10
+ # without tripping Rails' ban on autoloading during initialization -- and so the
11
+ # setting survives a reload.
11
12
  require 'glib/json_ui/config'
12
13
  require 'glib/json_ui/name'
14
+ require 'glib/purge_unattached/config'
13
15
  require 'glib/json_crawler'
14
16
 
15
17
  require 'glib/dynamic_text'
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: 7.0.0.beta1
4
+ version: 7.0.1.beta1
5
5
  platform: ruby
6
6
  authors:
7
7
  - ''
@@ -23,6 +23,20 @@ dependencies:
23
23
  - - "~>"
24
24
  - !ruby/object:Gem::Version
25
25
  version: '8.0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: activejob
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '8.0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '8.0'
26
40
  - !ruby/object:Gem::Dependency
27
41
  name: pundit
28
42
  requirement: !ruby/object:Gem::Requirement
@@ -189,6 +203,7 @@ files:
189
203
  - app/controllers/concerns/glib/analytics/funnel.rb
190
204
  - app/controllers/concerns/glib/auth/policy.rb
191
205
  - app/controllers/concerns/glib/auth/response.rb
206
+ - app/controllers/concerns/glib/auth/token_authenticatable.rb
192
207
  - app/controllers/concerns/glib/form_stepper/controller.rb
193
208
  - app/controllers/concerns/glib/form_stepper/step.rb
194
209
  - app/controllers/concerns/glib/json/dynamic_text.rb
@@ -199,6 +214,7 @@ files:
199
214
  - app/controllers/concerns/glib/json/ui.rb
200
215
  - app/controllers/concerns/glib/json/validation.rb
201
216
  - app/controllers/concerns/glib/params/allowlist.rb
217
+ - app/controllers/glib/api_controller.rb
202
218
  - app/controllers/glib/blob_url_generators_controller.rb
203
219
  - app/controllers/glib/errors_controller.rb
204
220
  - app/controllers/glib/glib_direct_uploads_controller.rb
@@ -249,9 +265,11 @@ files:
249
265
  - app/helpers/glib/json_ui/view_builder/multimedia.rb
250
266
  - app/helpers/glib/json_ui/view_builder/panels.rb
251
267
  - app/helpers/glib/urls_helper.rb
268
+ - app/jobs/glib/purge_unattached_job.rb
252
269
  - app/models/concerns/glib/enum_humanization.rb
253
270
  - app/models/concerns/glib/enum_symbolization.rb
254
271
  - app/models/concerns/glib/nilify_blanks.rb
272
+ - app/models/concerns/glib/snapshot_v2.rb
255
273
  - app/models/concerns/glib/soft_deletable.rb
256
274
  - app/models/glib/active_storage/attachment.rb
257
275
  - app/models/glib/active_storage/blob.rb
@@ -259,8 +277,11 @@ files:
259
277
  - app/models/glib/country_list.rb
260
278
  - app/models/glib/dummy_job_application.rb
261
279
  - app/models/glib/dynamic_text_record.rb
280
+ - app/models/glib/model.rb
281
+ - app/models/glib/plain_model.rb
262
282
  - app/models/glib/state_list.rb
263
283
  - app/models/glib/text.rb
284
+ - app/models/snapshot_blob_reference.rb
264
285
  - app/policies/glib/application_policy.rb
265
286
  - app/validators/email_typo_validator.rb
266
287
  - app/validators/email_validator.rb
@@ -433,6 +454,7 @@ files:
433
454
  - app/views/json_ui/garage/test_page/duplicate_names.json.jbuilder
434
455
  - app/views/json_ui/garage/test_page/fields.json.jbuilder
435
456
  - app/views/json_ui/garage/test_page/fields_captcha.json.jbuilder
457
+ - app/views/json_ui/garage/test_page/fields_change_trigger.json.jbuilder
436
458
  - app/views/json_ui/garage/test_page/fields_creditCard.json.jbuilder
437
459
  - app/views/json_ui/garage/test_page/fields_date_time.json.jbuilder
438
460
  - app/views/json_ui/garage/test_page/fields_dynamicSelect.json.jbuilder
@@ -532,12 +554,15 @@ files:
532
554
  - config/routes.rb
533
555
  - lib/active_storage/service/glib_s3_service.rb
534
556
  - lib/generators/glib/install_generator.rb
557
+ - lib/generators/glib/snapshot_v2_generator.rb
535
558
  - lib/generators/templates/20191017062519_create_texts.rb
536
559
  - lib/generators/templates/20191024063257_add_scope_to_texts.rb
537
560
  - lib/generators/templates/20191112095018_add_lang_to_texts.rb
538
561
  - lib/generators/templates/20191126071051_create_active_storage_tables.active_storage.rb
539
562
  - lib/generators/templates/database.yml
540
563
  - lib/generators/templates/dynamic_text.rb
564
+ - lib/generators/templates/snapshot_v2/add_snapshot_v2_columns.rb
565
+ - lib/generators/templates/snapshot_v2/create_snapshot_blob_references.rb
541
566
  - lib/glib-web.rb
542
567
  - lib/glib/all_helpers.rb
543
568
  - lib/glib/crypt/utils.rb
@@ -564,6 +589,7 @@ files:
564
589
  - lib/glib/last_resort_unit_test.rb
565
590
  - lib/glib/mailer_tester.rb
566
591
  - lib/glib/non_http_integration_test.rb
592
+ - lib/glib/purge_unattached/config.rb
567
593
  - lib/glib/rubocop.rb
568
594
  - lib/glib/rubocop/cops/json_ui/base_nested_parameter.rb
569
595
  - lib/glib/rubocop/cops/json_ui/nested_action_parameter.rb
@@ -571,8 +597,9 @@ files:
571
597
  - lib/glib/rubocop/cops/localize.rb
572
598
  - lib/glib/rubocop/cops/test_name_parentheses.rb
573
599
  - lib/glib/snapshot.rb
574
- - lib/glib/test/changed_files_rubocop.rb
600
+ - lib/glib/test/full_suite_rubocop.rb
575
601
  - lib/glib/test/parallel_coverage.rb
602
+ - lib/glib/test/rubocop_guards.rb
576
603
  - lib/glib/test_helpers.rb
577
604
  - lib/glib/time_freezable_mailer.rb
578
605
  - lib/glib/time_returning_mailer.rb