Skip to content

Commit 85e9936

Browse files
author
Lee Richmond
committed
Allow scopes access to runtime context
In Rails, this means scopes would have access controller. So you can do things like: ```ruby default_filter :foo do |scope, ctx| scope.where(user_id: ctx.current_user.id) end ```
1 parent e9a35a5 commit 85e9936

File tree

11 files changed

+115
-6
lines changed

11 files changed

+115
-6
lines changed

lib/generators/jsonapi/field_generator.rb

Whitespace-only changes.

lib/jsonapi_compliable/scoping/default_filter.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ class Scoping::DefaultFilter < Scoping::Base
3838
def apply
3939
resource.default_filters.each_pair do |name, opts|
4040
next if overridden?(name)
41-
@scope = opts[:filter].call(@scope)
41+
@scope = opts[:filter].call(@scope, resource.context)
4242
end
4343

4444
@scope

lib/jsonapi_compliable/scoping/extra_fields.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ class Scoping::ExtraFields < Scoping::Base
3535
# @return the scope object we are chaining/modofying
3636
def apply
3737
each_extra_field do |callable|
38-
@scope = callable.call(@scope)
38+
@scope = callable.call(@scope, resource.context)
3939
end
4040

4141
@scope

lib/jsonapi_compliable/scoping/filter.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ def apply
4141
# specified in the adapter.
4242
def filter_scope(filter, value)
4343
if custom_scope = filter.values.first[:filter]
44-
custom_scope.call(@scope, value)
44+
custom_scope.call(@scope, value, resource.context)
4545
else
4646
resource.adapter.filter(@scope, filter.keys.first, value)
4747
end

lib/jsonapi_compliable/scoping/paginate.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ def apply_standard_scope
6161

6262
# Apply the custom pagination proc
6363
def apply_custom_scope
64-
custom_scope.call(@scope, number, size)
64+
custom_scope.call(@scope, number, size, resource.context)
6565
end
6666

6767
private

lib/jsonapi_compliable/scoping/sort.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ def apply_standard_scope
3131
# @return the scope we are chaining/modifying
3232
def apply_custom_scope
3333
each_sort do |attribute, direction|
34-
@scope = custom_scope.call(@scope, attribute, direction)
34+
@scope = custom_scope
35+
.call(@scope, attribute, direction, resource.context)
3536
end
3637
@scope
3738
end

spec/extra_fields_spec.rb

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,36 @@ class SerializableTestExtraFields < JSONAPI::Serializable::Resource
99
extra_attribute :net_worth, if: proc { !@context || @context.allow_net_worth? } do
1010
100_000_000
1111
end
12+
13+
extra_attribute :runtime_id do
14+
@context.runtime_id
15+
end
1216
end
1317

1418
before do
1519
resource_class.class_eval do
1620
extra_field :net_worth do |scope|
1721
scope.include_foo!
1822
end
23+
24+
extra_field :runtime_id do |scope, ctx|
25+
scope.runtime_id = ctx.runtime_id
26+
scope
27+
end
1928
end
2029
end
2130

2231
let!(:scope_object) do
2332
scope = Author.all
2433
scope.instance_eval do
34+
def runtime_id=(val)
35+
@runtime_id = val
36+
end
37+
38+
def runtime_id
39+
@runtime_id
40+
end
41+
2542
def include_foo!
2643
self
2744
end
@@ -53,6 +70,21 @@ def include_foo!
5370
expect(json['data'][0]['attributes'].keys).to match_array(%w(first_name last_name net_worth))
5471
end
5572

73+
context 'when accessing runtime context' do
74+
before do
75+
params[:extra_fields] = { authors: 'runtime_id' }
76+
end
77+
78+
it 'works' do
79+
expect(scope_object).to receive(:runtime_id=).with(789)
80+
ctx = double(runtime_id: 789).as_null_object
81+
resource.with_context ctx do
82+
attrs = json['data'][0]['attributes']
83+
expect(attrs['runtime_id']).to eq(789)
84+
end
85+
end
86+
end
87+
5688
context 'when extra field is requested but guarded' do
5789
before do
5890
params[:extra_fields] = { authors: 'net_worth' }

spec/filtering_spec.rb

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414
scope.where(['first_name like ?', "#{value}%"])
1515
end
1616
allow_filter :active
17+
allow_filter :temp do |scope, value, ctx|
18+
scope.where(id: ctx.runtime_id)
19+
end
1720
end
1821
end
1922

@@ -27,6 +30,15 @@
2730
expect(scope.resolve.map(&:id)).to eq([author1.id])
2831
end
2932

33+
# For example, getting current user from controller
34+
it 'has access to calling context' do
35+
ctx = double(runtime_id: author3.id).as_null_object
36+
JsonapiCompliable.with_context(ctx, {}) do
37+
params[:filter] = { temp: true }
38+
expect(scope.resolve.map(&:id)).to eq([author3.id])
39+
end
40+
end
41+
3042
context 'when filter is a "string nil"' do
3143
before do
3244
params[:filter] = { first_name: 'nil' }
@@ -137,6 +149,23 @@
137149
params[:filter] = { name: 'Stephen' }
138150
expect(scope.resolve.map(&:id)).to eq([author1.id])
139151
end
152+
153+
context 'when accessing calling context' do
154+
before do
155+
resource_class.class_eval do
156+
default_filter :first_name do |scope, ctx|
157+
scope.where(id: ctx.runtime_id)
158+
end
159+
end
160+
end
161+
162+
it 'works' do
163+
ctx = double(runtime_id: author3.id).as_null_object
164+
JsonapiCompliable.with_context(ctx, {}) do
165+
expect(scope.resolve.map(&:id)).to eq([author3.id])
166+
end
167+
end
168+
end
140169
end
141170

142171
context 'when the filter is guarded' do

spec/pagination_spec.rb

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,5 +52,22 @@
5252
it 'uses the custom pagination function' do
5353
expect(scope.resolve).to eq([])
5454
end
55+
56+
context 'and it accesses runtime context' do
57+
before do
58+
resource_class.class_eval do
59+
paginate do |scope, page, per_page, ctx|
60+
scope.limit(ctx.runtime_limit)
61+
end
62+
end
63+
end
64+
65+
it 'works' do
66+
ctx = double(runtime_limit: 2).as_null_object
67+
JsonapiCompliable.with_context(ctx, {}) do
68+
expect(scope.resolve.length).to eq(2)
69+
end
70+
end
71+
end
5572
end
5673
end

spec/scope_spec.rb

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,17 @@
22

33
RSpec.describe JsonapiCompliable::Scope do
44
let(:object) { double.as_null_object }
5-
let(:resource) { double(type: :authors, default_page_size: 1).as_null_object }
65
let(:query_hash) { JsonapiCompliable::Query.default_hash }
76
let(:query) { double(to_hash: { authors: query_hash }) }
87
let(:instance) { described_class.new(object, resource, query) }
98

9+
let(:resource) do
10+
dbl = double type: :authors,
11+
default_page_size: 1,
12+
pagination: nil
13+
dbl.as_null_object
14+
end
15+
1016
describe '#resolve' do
1117
before do
1218
allow(query).to receive(:zero_results?) { false }

0 commit comments

Comments
 (0)