rhino-rails 4.7.2 → 4.7.3

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: 632861555ee3e2508a90ec546b6cb0412368bc141a7d387b2f92698531605b86
4
- data.tar.gz: 91b3ad79b1c0b0b492f1f3003e906ce11e9f3fa62e81b067ecfb9c0b7740a025
3
+ metadata.gz: c7f50b9ced877e5d2fc1b304798f7c2c6372e89754ea1c7c9efd296c8f478c33
4
+ data.tar.gz: cca1fbd5f280df7e94a1fa9a69c1ca338fdfff6b76d35158afdc16fd07ef4309
5
5
  SHA512:
6
- metadata.gz: 2c5fee208f22ae88b0531f4075e6183d6d0a17137c43423e7f491dbf341848764ce16418feca131b13c45c4295f6b4a3507eebb52e1e30b2f887dcbcfdd624c1
7
- data.tar.gz: 83468b5afe671a2b0313778e57c2f053747129156d643bb85d5543513ea2d02aa6c2b63c4b70bb5f04613cb79bbb21544ba142bb4f127ebf56247581aab4c079
6
+ metadata.gz: 43e7ae4d3e346c984ab8452c8dbc2f3ceb7199f35f8e3eba3f971931aed8539e752f9744393f2429125cb94b6d092358c070d719b634fa95b0cd9e314ea362e1
7
+ data.tar.gz: d03e920444bfcdfc0c42ddfa0d494ac180aae4fc499acd852aafc1ca6486aeccaecdff54decf4e64f65e21e5c7cb0f078ea81b3426518d7495b7e8bc3a4bc7de
data/lib/rhino/context.rb CHANGED
@@ -30,20 +30,24 @@ module Rhino
30
30
  end
31
31
  end
32
32
 
33
- # The route group serving the current request, as stashed into RequestStore
34
- # by Rhino's own controllers and by Rhino::RouteGroupContext in custom ones.
35
- # Nil outside a request or when the route carries no group.
33
+ # The active route group: the explicit override if one is in effect (set by
34
+ # Rhino.in_route_group), else the group stashed into RequestStore by Rhino's
35
+ # own controllers and by Rhino::RouteGroupContext in custom ones.
36
+ #
37
+ # Unlike user/organization, an override never *erases* the request's group —
38
+ # +with+ only installs a non-nil one — so Rhino.for_user(u).query(M) inside a
39
+ # non-tenant request keeps that request's group.
36
40
  def route_group
37
- return nil unless defined?(RequestStore)
41
+ value = store[:rhino_route_group]
42
+ value = RequestStore.store[:rhino_route_group] if value.nil? && defined?(RequestStore)
38
43
 
39
- value = RequestStore.store[:rhino_route_group]
40
- value.nil? || value.to_s.empty? ? nil : value
44
+ value.nil? || value.to_s.empty? ? nil : value.to_s
41
45
  end
42
46
 
43
47
  # Run +block+ with the given user/organization installed into RequestStore.
44
48
  # Snapshots the prior RequestStore user+org, sets the new ones, yields, and
45
49
  # restores the snapshot in an ensure. Returns the block's value.
46
- def with(user:, organization:)
50
+ def with(user:, organization:, route_group: nil)
47
51
  return yield unless defined?(RequestStore)
48
52
 
49
53
  had_user = RequestStore.store.key?(:rhino_current_user)
@@ -51,6 +55,13 @@ module Rhino
51
55
  prev_user = RequestStore.store[:rhino_current_user]
52
56
  prev_org = RequestStore.store[:rhino_organization]
53
57
 
58
+ # A route group is only ever ADDED, never cleared: with no explicit group
59
+ # the request's own one (if any) stays in effect.
60
+ had_group = RequestStore.store.key?(:rhino_route_group)
61
+ prev_group = RequestStore.store[:rhino_route_group]
62
+ had_override_group = store.key?(:rhino_route_group)
63
+ prev_override_group = store[:rhino_route_group]
64
+
54
65
  # Track the active override so Context.user/organization prefer it even when
55
66
  # the passed value is nil (distinguishing "explicitly nil" from "absent").
56
67
  had_override_user = store.key?(:rhino_current_user)
@@ -63,6 +74,11 @@ module Rhino
63
74
  store[:rhino_current_user] = user
64
75
  store[:rhino_organization] = organization
65
76
 
77
+ if route_group
78
+ RequestStore.store[:rhino_route_group] = route_group.to_s
79
+ store[:rhino_route_group] = route_group.to_s
80
+ end
81
+
66
82
  begin
67
83
  yield
68
84
  ensure
@@ -87,6 +103,20 @@ module Rhino
87
103
  else
88
104
  store.delete(:rhino_organization)
89
105
  end
106
+
107
+ if route_group
108
+ if had_group
109
+ RequestStore.store[:rhino_route_group] = prev_group
110
+ else
111
+ RequestStore.store.delete(:rhino_route_group)
112
+ end
113
+
114
+ if had_override_group
115
+ store[:rhino_route_group] = prev_override_group
116
+ else
117
+ store.delete(:rhino_route_group)
118
+ end
119
+ end
90
120
  end
91
121
  end
92
122
 
data/lib/rhino/query.rb CHANGED
@@ -21,9 +21,10 @@ module Rhino
21
21
  #
22
22
  # Fail closed: an org-scopable model with no org context RAISES
23
23
  # Rhino::MissingTenantContext rather than returning an unscoped relation —
24
- # unless the request is served by a route group declared non-tenant
25
- # (`tenant: false`), where a query legitimately spans every organization. An
26
- # explicit organization is always honored, in every group.
24
+ # unless the query belongs to a route group declared non-tenant
25
+ # (`tenant: false`), either because the request is served by that group or
26
+ # because the caller said so with Rhino.in_route_group(...). An explicit
27
+ # organization is always honored, in every group.
27
28
  def query(model_class)
28
29
  org = Rhino::Context.organization
29
30
 
@@ -53,6 +54,20 @@ module Rhino
53
54
  Rhino::PendingScopedContext.new(user: user)
54
55
  end
55
56
 
57
+ # Begin an explicit context that acts as the named route group, for use
58
+ # where no request resolves one — an Active Job, a rake task, the console,
59
+ # a test.
60
+ #
61
+ # The group's own configuration still decides the boundary: naming a group
62
+ # declared +tenant: false+ lets the query span every organization, while
63
+ # naming any other group keeps failing closed.
64
+ #
65
+ # Rhino.in_route_group(:admin).query(Task)
66
+ # Rhino.for_user(user).in_route_group(:admin).run { ... }
67
+ def in_route_group(route_group)
68
+ Rhino::PendingScopedContext.new(user: nil, route_group: route_group)
69
+ end
70
+
56
71
  # The ambient context resolver.
57
72
  def context
58
73
  Rhino::Context
@@ -89,9 +104,10 @@ module Rhino
89
104
  # Fluent explicit-context builder. Holds a user (and, once chained, an org) and
90
105
  # resolves queries with that context installed into RequestStore at build time.
91
106
  class PendingScopedContext
92
- def initialize(user:, organization: nil)
107
+ def initialize(user:, organization: nil, route_group: nil)
93
108
  @user = user
94
109
  @organization = organization
110
+ @route_group = route_group
95
111
  end
96
112
 
97
113
  def in_organization(organization)
@@ -99,6 +115,18 @@ module Rhino
99
115
  self
100
116
  end
101
117
 
118
+ # Act as the named route group for this context (see Rhino.in_route_group).
119
+ def in_route_group(route_group)
120
+ @route_group = route_group
121
+ self
122
+ end
123
+
124
+ # Set the explicit user for this context.
125
+ def for_user(user)
126
+ @user = user
127
+ self
128
+ end
129
+
102
130
  # Build a fully-baked relation for +model_class+ under this explicit context.
103
131
  #
104
132
  # Because Rails default_scopes bake at BUILD time, we install the user+org into
@@ -106,14 +134,14 @@ module Rhino
106
134
  # The org+user are baked into the returned relation — no stickiness, fully
107
135
  # isolated: a later Rhino.query with no context still fails closed.
108
136
  def query(model_class)
109
- Rhino::Context.with(user: @user, organization: @organization) do
137
+ Rhino::Context.with(user: @user, organization: @organization, route_group: @route_group) do
110
138
  Rhino.query(model_class)
111
139
  end
112
140
  end
113
141
 
114
142
  # Build a fully-baked, named-scoped relation under this explicit context.
115
143
  def scoped_query(model_class, scope_name = nil)
116
- Rhino::Context.with(user: @user, organization: @organization) do
144
+ Rhino::Context.with(user: @user, organization: @organization, route_group: @route_group) do
117
145
  Rhino.scoped_query(model_class, scope_name)
118
146
  end
119
147
  end
@@ -122,7 +150,7 @@ module Rhino
122
150
  # inside the block see the context; RequestStore is restored afterward.
123
151
  # Returns the block's value.
124
152
  def run(&block)
125
- Rhino::Context.with(user: @user, organization: @organization, &block)
153
+ Rhino::Context.with(user: @user, organization: @organization, route_group: @route_group, &block)
126
154
  end
127
155
  end
128
156
  end
@@ -58,8 +58,14 @@ Rhino.configure do |config|
58
58
  # Custom (non-Rhino) controllers publish their group by including
59
59
  # Rhino::RouteGroupContext and declaring `rhino_route_group :admin`, or by
60
60
  # tagging the route with `defaults: { route_group: 'admin' }`. Outside a
61
- # request (jobs, rake tasks) no group resolves and the resolver keeps failing
62
- # closed.
61
+ # request (jobs, rake tasks, console) no group resolves, so the caller names
62
+ # the one it is acting as:
63
+ #
64
+ # Rhino.in_route_group(:admin).query(Task)
65
+ # Rhino.for_user(operator).in_route_group(:admin).run { ... }
66
+ #
67
+ # The group's own `tenant:` still decides: naming a tenant group there changes
68
+ # nothing, the query still fails closed.
63
69
 
64
70
  # config.route_group :default, prefix: '', middleware: [], models: :all
65
71
 
data/lib/rhino/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rhino
4
- VERSION = "4.7.2"
4
+ VERSION = "4.7.3"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rhino-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.7.2
4
+ version: 4.7.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bruno Cipolla