janela 0.6.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.
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,6 +41,29 @@ module Janela
37
41
  # is for (ADR 021).
38
42
  mattr_accessor :silenced_checks, default: []
39
43
 
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
+
40
67
  # A model that declares a janela block is addressable over HTTP, and so is a
41
68
  # subclass of one, keyed by the route key that appears in pane URLs (orders,
42
69
  # sales_orders). Names are stored rather than classes so a reloaded model
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.6.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
@@ -167,6 +168,9 @@ files:
167
168
  - docs/decisions/029-a-panes-frame-is-identified-by-who-it-is.md
168
169
  - docs/decisions/030-a-panes-src-belongs-to-turbo.md
169
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
170
174
  - docs/decisions/INDEX.md
171
175
  - docs/multi-tenancy.md
172
176
  - docs/naming.md
@@ -190,14 +194,22 @@ metadata:
190
194
  bug_tracker_uri: https://github.com/retail-tasker/janela/issues
191
195
  rubygems_mfa_required: 'true'
192
196
  post_install_message: |
193
- Janela 0.6.0 changes how single table inheritance is handled (ADR 031).
194
- You need to act only if your application has STI subclasses under a model
195
- that declares a janela block. Every named subclass of one is now
196
- registered and addressable on its own route key, and Janela.definitions
197
- returns one entry per subclass where a form offering a choice of model
198
- previously showed one.
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
199
 
200
- Everyone else: nothing to do.
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).
201
213
 
202
214
  Steps: UPGRADING.md in this gem, or
203
215
  https://github.com/retail-tasker/janela/blob/main/UPGRADING.md