janela 0.5.0 → 0.7.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 +4 -4
- data/CHANGELOG.md +33 -0
- data/README.md +61 -8
- data/UPGRADING.md +158 -0
- data/app/assets/javascripts/janela/frame_controller.js +38 -3
- data/app/controllers/janela/application_controller.rb +3 -2
- data/app/helpers/janela/frames_helper.rb +18 -5
- data/app/jobs/janela/snapshot_job.rb +47 -6
- data/app/models/janela/snapshot.rb +11 -2
- data/app/views/janela/frames/_frame.html.erb +1 -1
- data/app/views/janela/queries/show.html.erb +6 -1
- data/db/migrate/20260921000001_add_owner_to_janela_snapshots.rb +9 -0
- data/docs/decisions/009-snapshots.md +1 -1
- data/docs/decisions/029-a-panes-frame-is-identified-by-who-it-is.md +112 -0
- data/docs/decisions/030-a-panes-src-belongs-to-turbo.md +93 -0
- data/docs/decisions/031-a-subclass-inherits-the-dashboard.md +110 -0
- data/docs/decisions/032-janela-will-not-read-a-model-it-cannot-scope.md +156 -0
- data/docs/decisions/033-a-snapshot-is-told-who-owns-it.md +151 -0
- data/docs/decisions/034-janela-will-not-freeze-a-scope-the-host-has-not-named.md +238 -0
- data/docs/decisions/INDEX.md +12 -6
- data/docs/multi-tenancy.md +87 -18
- data/lib/janela/definition.rb +11 -1
- data/lib/janela/doctor.rb +58 -7
- data/lib/janela/model.rb +46 -10
- data/lib/janela/version.rb +1 -1
- data/lib/janela.rb +43 -3
- metadata +24 -7
data/lib/janela.rb
CHANGED
|
@@ -20,6 +20,10 @@ module Janela
|
|
|
20
20
|
# Something the request asked for is not allowed here: a renderer, a
|
|
21
21
|
# granularity, a limit or a filter. Rendered as 400.
|
|
22
22
|
class BadRequest < Error; end
|
|
23
|
+
# The host has not said what may be read. Not rendered as anything: it is a
|
|
24
|
+
# setup mistake rather than a data condition, and dressing it as a 404 would
|
|
25
|
+
# hide the one line that fixes it (ADR 032).
|
|
26
|
+
class Unscoped < Error; end
|
|
23
27
|
|
|
24
28
|
# Janela's controllers inherit from the host's, so the host's authentication
|
|
25
29
|
# and authorisation apply to dashboards with no configuration.
|
|
@@ -37,9 +41,33 @@ module Janela
|
|
|
37
41
|
# is for (ADR 021).
|
|
38
42
|
mattr_accessor :silenced_checks, default: []
|
|
39
43
|
|
|
40
|
-
#
|
|
41
|
-
#
|
|
42
|
-
#
|
|
44
|
+
# What the host permits to be read, asked of its controller and never
|
|
45
|
+
# assumed. Janela refuses rather than reading everything, because a
|
|
46
|
+
# dashboard that quietly totals rows its reader may not see is the worst
|
|
47
|
+
# thing this library can do (ADR 032).
|
|
48
|
+
#
|
|
49
|
+
# Asked of the controller and never of self, because a helper runs on the
|
|
50
|
+
# view and a view cannot see a private controller method: when there were
|
|
51
|
+
# two copies of this question they disagreed about whether a host had
|
|
52
|
+
# answered it (#46). One copy, for that reason.
|
|
53
|
+
def self.scope(controller, model)
|
|
54
|
+
unless controller.respond_to?(:policy_scope, true)
|
|
55
|
+
raise Unscoped, "#{controller.class} defines no policy_scope, so Janela has not been " \
|
|
56
|
+
"told what may be read of #{model.name}. Define it on " \
|
|
57
|
+
"#{Janela.parent_controller}, returning the rows this visitor may see:\n\n" \
|
|
58
|
+
" private def policy_scope(model) = model.all\n\n" \
|
|
59
|
+
"That line says every visitor may read every row of every model on a " \
|
|
60
|
+
"dashboard. If that is not true here, return something narrower. " \
|
|
61
|
+
"UPGRADING.md has the steps and docs/multi-tenancy.md has the wiring."
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
controller.send(:policy_scope, model)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# A model that declares a janela block is addressable over HTTP, and so is a
|
|
68
|
+
# subclass of one, keyed by the route key that appears in pane URLs (orders,
|
|
69
|
+
# sales_orders). Names are stored rather than classes so a reloaded model
|
|
70
|
+
# leaves nothing stale behind.
|
|
43
71
|
def self.registry
|
|
44
72
|
@registry ||= {}
|
|
45
73
|
end
|
|
@@ -48,6 +76,18 @@ module Janela
|
|
|
48
76
|
registry[model.model_name.route_key] = model.name
|
|
49
77
|
end
|
|
50
78
|
|
|
79
|
+
# A subclass registers itself as it is created (ADR 031), so unlike a
|
|
80
|
+
# declaration it is not a host writing a line of code. It never takes a
|
|
81
|
+
# route key another class already holds: a host that gives a subclass its
|
|
82
|
+
# parent's model_name, so the two share a route and a form, would otherwise
|
|
83
|
+
# find the parent's URL answering with a subset of its rows.
|
|
84
|
+
def self.register_subclass(model)
|
|
85
|
+
route_key = model.model_name.route_key
|
|
86
|
+
return if registry.key?(route_key) && registry[route_key] != model.name
|
|
87
|
+
|
|
88
|
+
register(model)
|
|
89
|
+
end
|
|
90
|
+
|
|
51
91
|
# Every model that declares a janela block, for a form that offers a choice
|
|
52
92
|
# of them. Eager loading first, because a model nobody has referenced yet has
|
|
53
93
|
# not registered. A name that no longer resolves is left out rather than
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: janela
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.7.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jay Killeen
|
|
@@ -136,6 +136,7 @@ files:
|
|
|
136
136
|
- db/migrate/20260915000001_create_janela_snapshots.rb
|
|
137
137
|
- db/migrate/20260916000001_create_janela_frames.rb
|
|
138
138
|
- db/migrate/20260916000002_create_janela_panes.rb
|
|
139
|
+
- db/migrate/20260921000001_add_owner_to_janela_snapshots.rb
|
|
139
140
|
- docs/decisions/001-built-to-be-forked.md
|
|
140
141
|
- docs/decisions/002-measures-and-dimensions-over-ransack.md
|
|
141
142
|
- docs/decisions/003-cross-filtering-with-turbo-frames.md
|
|
@@ -164,6 +165,12 @@ files:
|
|
|
164
165
|
- docs/decisions/026-a-renderer-is-the-seam-and-html-comes-first.md
|
|
165
166
|
- docs/decisions/027-the-gallery-is-a-host-page.md
|
|
166
167
|
- docs/decisions/028-the-predicate-list-adr-025-named.md
|
|
168
|
+
- docs/decisions/029-a-panes-frame-is-identified-by-who-it-is.md
|
|
169
|
+
- docs/decisions/030-a-panes-src-belongs-to-turbo.md
|
|
170
|
+
- docs/decisions/031-a-subclass-inherits-the-dashboard.md
|
|
171
|
+
- docs/decisions/032-janela-will-not-read-a-model-it-cannot-scope.md
|
|
172
|
+
- docs/decisions/033-a-snapshot-is-told-who-owns-it.md
|
|
173
|
+
- docs/decisions/034-janela-will-not-freeze-a-scope-the-host-has-not-named.md
|
|
167
174
|
- docs/decisions/INDEX.md
|
|
168
175
|
- docs/multi-tenancy.md
|
|
169
176
|
- docs/naming.md
|
|
@@ -187,12 +194,22 @@ metadata:
|
|
|
187
194
|
bug_tracker_uri: https://github.com/retail-tasker/janela/issues
|
|
188
195
|
rubygems_mfa_required: 'true'
|
|
189
196
|
post_install_message: |
|
|
190
|
-
Janela 0.
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
197
|
+
Janela 0.7.0 stops guessing what may be read. Two things now refuse
|
|
198
|
+
rather than defaulting to every row, and both are quick to answer.
|
|
199
|
+
|
|
200
|
+
1. If your ApplicationController defines no policy_scope, every pane
|
|
201
|
+
raises Janela::Unscoped instead of totalling every row. Pundit
|
|
202
|
+
hosts and anyone following docs/multi-tenancy.md: nothing to do.
|
|
203
|
+
Everyone else writes one line (ADR 032).
|
|
204
|
+
|
|
205
|
+
2. If you schedule Janela::SnapshotJob, it now needs to be told what
|
|
206
|
+
rows to freeze: pass scope: :model_default, or subclass it and
|
|
207
|
+
override scope_for. A job already on your queue was serialised
|
|
208
|
+
without that argument and will raise when it performs, so drain it
|
|
209
|
+
or re-enqueue (ADR 034).
|
|
210
|
+
|
|
211
|
+
If you use snapshots, there is also a migration: janela_snapshots
|
|
212
|
+
gains a nullable owner (ADR 033).
|
|
196
213
|
|
|
197
214
|
Steps: UPGRADING.md in this gem, or
|
|
198
215
|
https://github.com/retail-tasker/janela/blob/main/UPGRADING.md
|