jsonapi_compliable 0.11.34 → 1.0.alpha.2
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 +5 -5
- data/.ruby-version +1 -1
- data/.travis.yml +1 -2
- data/Rakefile +7 -3
- data/jsonapi_compliable.gemspec +7 -3
- data/lib/generators/jsonapi/resource_generator.rb +8 -79
- data/lib/generators/jsonapi/templates/application_resource.rb.erb +2 -1
- data/lib/generators/jsonapi/templates/controller.rb.erb +19 -64
- data/lib/generators/jsonapi/templates/resource.rb.erb +5 -47
- data/lib/generators/jsonapi/templates/resource_reads_spec.rb.erb +62 -0
- data/lib/generators/jsonapi/templates/resource_writes_spec.rb.erb +63 -0
- data/lib/jsonapi_compliable.rb +87 -18
- data/lib/jsonapi_compliable/adapters/abstract.rb +202 -45
- data/lib/jsonapi_compliable/adapters/active_record.rb +6 -130
- data/lib/jsonapi_compliable/adapters/active_record/base.rb +247 -0
- data/lib/jsonapi_compliable/adapters/active_record/belongs_to_sideload.rb +17 -0
- data/lib/jsonapi_compliable/adapters/active_record/has_many_sideload.rb +17 -0
- data/lib/jsonapi_compliable/adapters/active_record/has_one_sideload.rb +17 -0
- data/lib/jsonapi_compliable/adapters/active_record/inferrence.rb +12 -0
- data/lib/jsonapi_compliable/adapters/active_record/many_to_many_sideload.rb +30 -0
- data/lib/jsonapi_compliable/adapters/null.rb +177 -6
- data/lib/jsonapi_compliable/base.rb +33 -320
- data/lib/jsonapi_compliable/context.rb +16 -0
- data/lib/jsonapi_compliable/deserializer.rb +14 -39
- data/lib/jsonapi_compliable/errors.rb +227 -24
- data/lib/jsonapi_compliable/extensions/extra_attribute.rb +3 -1
- data/lib/jsonapi_compliable/filter_operators.rb +25 -0
- data/lib/jsonapi_compliable/hash_renderer.rb +57 -0
- data/lib/jsonapi_compliable/query.rb +190 -202
- data/lib/jsonapi_compliable/rails.rb +12 -6
- data/lib/jsonapi_compliable/railtie.rb +64 -0
- data/lib/jsonapi_compliable/renderer.rb +60 -0
- data/lib/jsonapi_compliable/resource.rb +35 -663
- data/lib/jsonapi_compliable/resource/configuration.rb +239 -0
- data/lib/jsonapi_compliable/resource/dsl.rb +138 -0
- data/lib/jsonapi_compliable/resource/interface.rb +32 -0
- data/lib/jsonapi_compliable/resource/polymorphism.rb +68 -0
- data/lib/jsonapi_compliable/resource/sideloading.rb +102 -0
- data/lib/jsonapi_compliable/resource_proxy.rb +127 -0
- data/lib/jsonapi_compliable/responders.rb +19 -0
- data/lib/jsonapi_compliable/runner.rb +25 -0
- data/lib/jsonapi_compliable/scope.rb +37 -79
- data/lib/jsonapi_compliable/scoping/extra_attributes.rb +29 -0
- data/lib/jsonapi_compliable/scoping/filter.rb +39 -58
- data/lib/jsonapi_compliable/scoping/filterable.rb +9 -14
- data/lib/jsonapi_compliable/scoping/paginate.rb +9 -3
- data/lib/jsonapi_compliable/scoping/sort.rb +16 -4
- data/lib/jsonapi_compliable/sideload.rb +221 -347
- data/lib/jsonapi_compliable/sideload/belongs_to.rb +34 -0
- data/lib/jsonapi_compliable/sideload/has_many.rb +16 -0
- data/lib/jsonapi_compliable/sideload/has_one.rb +9 -0
- data/lib/jsonapi_compliable/sideload/many_to_many.rb +24 -0
- data/lib/jsonapi_compliable/sideload/polymorphic_belongs_to.rb +108 -0
- data/lib/jsonapi_compliable/stats/payload.rb +4 -8
- data/lib/jsonapi_compliable/types.rb +172 -0
- data/lib/jsonapi_compliable/util/attribute_check.rb +88 -0
- data/lib/jsonapi_compliable/util/persistence.rb +29 -7
- data/lib/jsonapi_compliable/util/relationship_payload.rb +4 -4
- data/lib/jsonapi_compliable/util/render_options.rb +4 -32
- data/lib/jsonapi_compliable/util/serializer_attributes.rb +98 -0
- data/lib/jsonapi_compliable/util/validation_response.rb +15 -9
- data/lib/jsonapi_compliable/version.rb +1 -1
- metadata +105 -24
- data/lib/generators/jsonapi/field_generator.rb +0 -0
- data/lib/generators/jsonapi/templates/create_request_spec.rb.erb +0 -29
- data/lib/generators/jsonapi/templates/destroy_request_spec.rb.erb +0 -20
- data/lib/generators/jsonapi/templates/index_request_spec.rb.erb +0 -22
- data/lib/generators/jsonapi/templates/payload.rb.erb +0 -39
- data/lib/generators/jsonapi/templates/serializer.rb.erb +0 -25
- data/lib/generators/jsonapi/templates/show_request_spec.rb.erb +0 -19
- data/lib/generators/jsonapi/templates/update_request_spec.rb.erb +0 -33
- data/lib/jsonapi_compliable/adapters/active_record_sideloading.rb +0 -152
- data/lib/jsonapi_compliable/scoping/extra_fields.rb +0 -58
@@ -0,0 +1,102 @@
|
|
1
|
+
module JsonapiCompliable
|
2
|
+
class Resource
|
3
|
+
module Sideloading
|
4
|
+
def self.included(klass)
|
5
|
+
klass.extend ClassMethods
|
6
|
+
end
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
def allow_sideload(name, opts = {}, &blk)
|
10
|
+
klass = Class.new(opts.delete(:class) || Sideload)
|
11
|
+
klass.class_eval(&blk) if blk
|
12
|
+
opts[:parent_resource] = self
|
13
|
+
relationship_option(opts, :readable)
|
14
|
+
relationship_option(opts, :writable)
|
15
|
+
sideload = klass.new(name, opts)
|
16
|
+
if parent = opts[:parent]
|
17
|
+
parent.children[name] = sideload
|
18
|
+
else
|
19
|
+
config[:sideloads][name] = sideload
|
20
|
+
apply_sideloads_to_serializer
|
21
|
+
end
|
22
|
+
sideload
|
23
|
+
end
|
24
|
+
|
25
|
+
def apply_sideloads_to_serializer
|
26
|
+
config[:sideloads].each_pair do |name, sideload|
|
27
|
+
if serializer.relationship_blocks[name].nil? && sideload.readable?
|
28
|
+
serializer.relationship(name)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def has_many(name, opts = {}, &blk)
|
34
|
+
opts[:class] = adapter.sideloading_classes[:has_many]
|
35
|
+
allow_sideload(name, opts, &blk)
|
36
|
+
end
|
37
|
+
|
38
|
+
def belongs_to(name, opts = {}, &blk)
|
39
|
+
opts[:class] = adapter.sideloading_classes[:belongs_to]
|
40
|
+
allow_sideload(name, opts, &blk)
|
41
|
+
end
|
42
|
+
|
43
|
+
def has_one(name, opts = {}, &blk)
|
44
|
+
opts[:class] = adapter.sideloading_classes[:has_one]
|
45
|
+
allow_sideload(name, opts, &blk)
|
46
|
+
end
|
47
|
+
|
48
|
+
def many_to_many(name, opts = {}, &blk)
|
49
|
+
opts[:class] = adapter.sideloading_classes[:many_to_many]
|
50
|
+
allow_sideload(name, opts, &blk)
|
51
|
+
end
|
52
|
+
|
53
|
+
def polymorphic_belongs_to(name, opts = {}, &blk)
|
54
|
+
opts[:resource] ||= Class.new(::JsonapiCompliable::Resource) do
|
55
|
+
self.polymorphic = []
|
56
|
+
self.abstract_class = true
|
57
|
+
end
|
58
|
+
# adapters *probably* don't need to override this, but it's allowed
|
59
|
+
opts[:class] ||= adapter.sideloading_classes[:polymorphic_belongs_to]
|
60
|
+
opts[:class] ||= ::JsonapiCompliable::Sideload::PolymorphicBelongsTo
|
61
|
+
allow_sideload(name, opts, &blk)
|
62
|
+
end
|
63
|
+
|
64
|
+
def sideload(name)
|
65
|
+
sideloads[name]
|
66
|
+
end
|
67
|
+
|
68
|
+
def all_sideloads(memo = {})
|
69
|
+
sideloads.each_pair do |name, sideload|
|
70
|
+
unless memo[name]
|
71
|
+
memo[name] = sideload
|
72
|
+
memo.merge!(sideload.resource.class.all_sideloads(memo))
|
73
|
+
end
|
74
|
+
end
|
75
|
+
memo
|
76
|
+
end
|
77
|
+
|
78
|
+
def association_names(memo = [])
|
79
|
+
all_sideloads.each_pair do |name, sl|
|
80
|
+
unless memo.include?(sl.name)
|
81
|
+
memo << sl.name
|
82
|
+
memo |= sl.resource.class.association_names(memo)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
memo
|
87
|
+
end
|
88
|
+
|
89
|
+
def association_types(memo = [])
|
90
|
+
all_sideloads.each_pair do |name, sl|
|
91
|
+
unless memo.include?(sl.resource.type)
|
92
|
+
memo << sl.resource.type
|
93
|
+
memo |= sl.resource.class.association_types(memo)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
memo
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
module JsonapiCompliable
|
2
|
+
class ResourceProxy
|
3
|
+
include Enumerable
|
4
|
+
|
5
|
+
attr_reader :resource, :query, :scope
|
6
|
+
|
7
|
+
def initialize(resource, scope, query, payload: nil, single: false, raise_on_missing: false)
|
8
|
+
@resource = resource
|
9
|
+
@scope = scope
|
10
|
+
@query = query
|
11
|
+
@payload = payload
|
12
|
+
@single = single
|
13
|
+
@raise_on_missing = raise_on_missing
|
14
|
+
end
|
15
|
+
|
16
|
+
def single?
|
17
|
+
!!@single
|
18
|
+
end
|
19
|
+
|
20
|
+
def raise_on_missing?
|
21
|
+
!!@raise_on_missing
|
22
|
+
end
|
23
|
+
|
24
|
+
def errors
|
25
|
+
data.errors
|
26
|
+
end
|
27
|
+
|
28
|
+
def [](val)
|
29
|
+
data[val]
|
30
|
+
end
|
31
|
+
|
32
|
+
def jsonapi_render_options(opts = {})
|
33
|
+
opts[:meta] ||= {}
|
34
|
+
opts[:expose] ||= {}
|
35
|
+
opts[:expose][:context] = JsonapiCompliable.context[:object]
|
36
|
+
opts
|
37
|
+
end
|
38
|
+
|
39
|
+
def to_jsonapi(options = {})
|
40
|
+
options = jsonapi_render_options(options)
|
41
|
+
Renderer.new(self, options).to_jsonapi
|
42
|
+
end
|
43
|
+
|
44
|
+
def to_json(options = {})
|
45
|
+
Renderer.new(self, options).to_json
|
46
|
+
end
|
47
|
+
|
48
|
+
def to_xml(options = {})
|
49
|
+
Renderer.new(self, options).to_xml
|
50
|
+
end
|
51
|
+
|
52
|
+
def data
|
53
|
+
@data ||= begin
|
54
|
+
records = @scope.resolve
|
55
|
+
if records.empty? && raise_on_missing?
|
56
|
+
raise JsonapiCompliable::Errors::RecordNotFound
|
57
|
+
end
|
58
|
+
records = records[0] if single?
|
59
|
+
records
|
60
|
+
end
|
61
|
+
end
|
62
|
+
alias :to_a :data
|
63
|
+
|
64
|
+
def each(&blk)
|
65
|
+
to_a.each(&blk)
|
66
|
+
end
|
67
|
+
|
68
|
+
def stats
|
69
|
+
@stats ||= @scope.resolve_stats
|
70
|
+
end
|
71
|
+
|
72
|
+
def save(action: :create)
|
73
|
+
validator = persist do
|
74
|
+
@resource.persist_with_relationships \
|
75
|
+
@payload.meta(action: action),
|
76
|
+
@payload.attributes,
|
77
|
+
@payload.relationships
|
78
|
+
end
|
79
|
+
@data, success = validator.to_a
|
80
|
+
success
|
81
|
+
end
|
82
|
+
|
83
|
+
def destroy
|
84
|
+
validator = @resource.transaction do
|
85
|
+
model = @resource.destroy(@query.filters[:id])
|
86
|
+
model.instance_variable_set(:@__serializer_klass, @resource.serializer)
|
87
|
+
validator = ::JsonapiCompliable::Util::ValidationResponse.new \
|
88
|
+
model, @payload
|
89
|
+
validator.validate!
|
90
|
+
@resource.before_commit(model, :destroy)
|
91
|
+
validator
|
92
|
+
end
|
93
|
+
@data, success = validator.to_a
|
94
|
+
success
|
95
|
+
end
|
96
|
+
|
97
|
+
def update_attributes
|
98
|
+
save(action: :update)
|
99
|
+
end
|
100
|
+
|
101
|
+
def include_hash
|
102
|
+
@payload ? @payload.include_hash : @query.include_hash
|
103
|
+
end
|
104
|
+
|
105
|
+
def fields
|
106
|
+
query.fields
|
107
|
+
end
|
108
|
+
|
109
|
+
def extra_fields
|
110
|
+
query.extra_fields
|
111
|
+
end
|
112
|
+
|
113
|
+
private
|
114
|
+
|
115
|
+
def persist
|
116
|
+
@resource.transaction do
|
117
|
+
::JsonapiCompliable::Util::Hooks.record do
|
118
|
+
model = yield
|
119
|
+
validator = ::JsonapiCompliable::Util::ValidationResponse.new \
|
120
|
+
model, @payload
|
121
|
+
validator.validate!
|
122
|
+
validator
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# If you're using Rails + responders gem to get respond_with
|
2
|
+
module JsonapiCompliable
|
3
|
+
module Responders
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
include ActionController::MimeResponds
|
8
|
+
respond_to :json, :jsonapi, :xml, :api_json
|
9
|
+
end
|
10
|
+
|
11
|
+
# Override to avoid location url generation (for now)
|
12
|
+
def respond_with(*args, &blk)
|
13
|
+
opts = args.extract_options!
|
14
|
+
opts[:location] = nil
|
15
|
+
args << opts
|
16
|
+
super(*args, &blk)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module JsonapiCompliable
|
2
|
+
class Runner
|
3
|
+
attr_reader :params
|
4
|
+
include JsonapiCompliable::Base
|
5
|
+
|
6
|
+
def initialize(resource_class, params)
|
7
|
+
@resource_class = resource_class
|
8
|
+
@params = params
|
9
|
+
end
|
10
|
+
|
11
|
+
def jsonapi_resource
|
12
|
+
@jsonapi_resource ||= @resource_class.new
|
13
|
+
end
|
14
|
+
|
15
|
+
# Typically, this is 'self' of a controller
|
16
|
+
# We're overriding here so we can do stuff like
|
17
|
+
#
|
18
|
+
# JsonapiCompliable.with_context my_context, {} do
|
19
|
+
# Runner.new ...
|
20
|
+
# end
|
21
|
+
def jsonapi_context
|
22
|
+
JsonapiCompliable.context[:object]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -1,112 +1,69 @@
|
|
1
1
|
module JsonapiCompliable
|
2
|
-
# A Scope wraps an underlying object. It modifies that object
|
3
|
-
# using the corresponding Resource and Query, and how to resolve
|
4
|
-
# that underlying object scope.
|
5
|
-
#
|
6
|
-
# @example Basic Controller usage
|
7
|
-
# def index
|
8
|
-
# base = Post.all
|
9
|
-
# scope = jsonapi_scope(base)
|
10
|
-
# scope.object == base # => true
|
11
|
-
# scope.object = scope.object.where(active: true)
|
12
|
-
#
|
13
|
-
# # actually fires sql
|
14
|
-
# scope.resolve #=> [#<Post ...>, #<Post ...>, etc]
|
15
|
-
# end
|
16
2
|
class Scope
|
17
|
-
|
3
|
+
attr_accessor :object, :unpaginated_object
|
18
4
|
|
19
|
-
# @param object - The underlying, chainable base scope object
|
20
|
-
# @param resource - The Resource that will process the object
|
21
|
-
# @param query - The Query object for the current request
|
22
|
-
# @param [Hash] opts Options to configure the Scope
|
23
|
-
# @option opts [String] :namespace The nested relationship name
|
24
|
-
# @option opts [Boolean] :filter Opt-out of filter scoping
|
25
|
-
# @option opts [Boolean] :extra_fields Opt-out of extra_fields scoping
|
26
|
-
# @option opts [Boolean] :sort Opt-out of sort scoping
|
27
|
-
# @option opts [Boolean] :pagination Opt-out of pagination scoping
|
28
|
-
# @option opts [Boolean] :default_paginate Opt-out of default pagination when not specified in request
|
29
5
|
def initialize(object, resource, query, opts = {})
|
30
6
|
@object = object
|
31
7
|
@resource = resource
|
32
8
|
@query = query
|
9
|
+
@opts = opts
|
33
10
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
# when hitting /authors?include=state its 'state'
|
38
|
-
@namespace = opts.delete(:namespace) || resource.type
|
39
|
-
|
40
|
-
apply_scoping(opts)
|
11
|
+
@object = @resource.around_scoping(@object, query_hash) do |scope|
|
12
|
+
apply_scoping(scope, opts)
|
13
|
+
end
|
41
14
|
end
|
42
15
|
|
43
|
-
# Resolve the requested stats. Returns hash like:
|
44
|
-
#
|
45
|
-
# { rating: { average: 5.5, maximum: 9 } }
|
46
|
-
#
|
47
|
-
# @return [Hash] the resolved stat info
|
48
|
-
# @api private
|
49
16
|
def resolve_stats
|
50
|
-
|
17
|
+
if query_hash[:stats]
|
18
|
+
Stats::Payload.new(@resource, @query, @unpaginated_object).generate
|
19
|
+
else
|
20
|
+
{}
|
21
|
+
end
|
51
22
|
end
|
52
23
|
|
53
|
-
# Resolve the scope. This is where SQL is actually fired, an HTTP
|
54
|
-
# request is actually made, etc.
|
55
|
-
#
|
56
|
-
# Does nothing if the user requested zero results, ie /posts?page[size]=0
|
57
|
-
#
|
58
|
-
# First resolves the top-level resource, then processes each relevant sideload
|
59
|
-
#
|
60
|
-
# @see Resource#resolve
|
61
|
-
# @return [Array] an array of resolved model instances
|
62
24
|
def resolve
|
63
25
|
if @query.zero_results?
|
64
26
|
[]
|
65
27
|
else
|
66
28
|
resolved = @resource.resolve(@object)
|
29
|
+
assign_serializer(resolved)
|
67
30
|
yield resolved if block_given?
|
68
|
-
|
31
|
+
if @opts[:after_resolve]
|
32
|
+
@opts[:after_resolve].call(resolved)
|
33
|
+
end
|
34
|
+
sideload(resolved) unless @query.sideloads.empty?
|
69
35
|
resolved
|
70
36
|
end
|
71
37
|
end
|
72
38
|
|
73
|
-
# The slice of Query#to_hash for the current namespace
|
74
|
-
# @see Query#to_hash
|
75
|
-
# @api private
|
76
39
|
def query_hash
|
77
|
-
@query_hash ||= @query.to_hash
|
40
|
+
@query_hash ||= @query.to_hash
|
78
41
|
end
|
79
42
|
|
80
43
|
private
|
81
44
|
|
82
|
-
|
45
|
+
# Used to ensure the resource's serializer is used
|
46
|
+
# Not one derived through the usual jsonapi-rb logic
|
47
|
+
def assign_serializer(records)
|
48
|
+
records.each do |r|
|
49
|
+
serializer = @resource.serializer_for(r)
|
50
|
+
r.instance_variable_set(:@__serializer_klass, serializer)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def sideload(results)
|
83
55
|
return if results == []
|
84
56
|
|
85
57
|
concurrent = ::JsonapiCompliable.config.experimental_concurrency
|
86
58
|
promises = []
|
87
59
|
|
88
|
-
|
89
|
-
sideload = @resource.sideload(name)
|
90
|
-
|
91
|
-
if
|
92
|
-
|
93
|
-
raise JsonapiCompliable::Errors::InvalidInclude
|
94
|
-
.new(name, @resource.type)
|
95
|
-
end
|
60
|
+
@query.sideloads.each_pair do |name, q|
|
61
|
+
sideload = @resource.class.sideload(name)
|
62
|
+
resolve_sideload = -> { sideload.resolve(results, q) }
|
63
|
+
if concurrent
|
64
|
+
promises << Concurrent::Promise.execute(&resolve_sideload)
|
96
65
|
else
|
97
|
-
|
98
|
-
resolve_sideload = -> {
|
99
|
-
begin
|
100
|
-
sideload.resolve(results, @query, namespace)
|
101
|
-
ensure
|
102
|
-
ActiveRecord::Base.clear_active_connections! if defined?(ActiveRecord)
|
103
|
-
end
|
104
|
-
}
|
105
|
-
if concurrent
|
106
|
-
promises << Concurrent::Promise.execute(&resolve_sideload)
|
107
|
-
else
|
108
|
-
resolve_sideload.call
|
109
|
-
end
|
66
|
+
resolve_sideload.call
|
110
67
|
end
|
111
68
|
end
|
112
69
|
|
@@ -124,16 +81,17 @@ module JsonapiCompliable
|
|
124
81
|
end
|
125
82
|
end
|
126
83
|
|
127
|
-
|
84
|
+
def apply_scoping(scope, opts)
|
85
|
+
@object = scope
|
128
86
|
add_scoping(nil, JsonapiCompliable::Scoping::DefaultFilter, opts)
|
129
87
|
add_scoping(:filter, JsonapiCompliable::Scoping::Filter, opts)
|
130
|
-
add_scoping(:extra_fields, JsonapiCompliable::Scoping::ExtraFields, opts)
|
131
88
|
add_scoping(:sort, JsonapiCompliable::Scoping::Sort, opts)
|
132
|
-
add_scoping(:paginate, JsonapiCompliable::Scoping::Paginate, opts
|
89
|
+
add_scoping(:paginate, JsonapiCompliable::Scoping::Paginate, opts)
|
90
|
+
@object
|
133
91
|
end
|
134
92
|
|
135
93
|
def add_scoping(key, scoping_class, opts, default = {})
|
136
|
-
@object = scoping_class.new(@resource, query_hash, @object,
|
94
|
+
@object = scoping_class.new(@resource, query_hash, @object, opts).apply
|
137
95
|
@unpaginated_object = @object unless key == :paginate
|
138
96
|
end
|
139
97
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module JsonapiCompliable
|
2
|
+
class Scoping::ExtraAttributes < Scoping::Base
|
3
|
+
# Loop through all requested extra fields. If custom scoping
|
4
|
+
# logic is define for that field, run it. Otherwise, do nothing.
|
5
|
+
#
|
6
|
+
# @return the scope object we are chaining/modofying
|
7
|
+
def apply
|
8
|
+
each_extra_attribute do |callable|
|
9
|
+
@scope = callable.call(@scope, resource.context)
|
10
|
+
end
|
11
|
+
|
12
|
+
@scope
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def each_extra_field
|
18
|
+
resource.extra_fields.each_pair do |name, callable|
|
19
|
+
if extra_fields.include?(name)
|
20
|
+
yield callable
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def extra_fields
|
26
|
+
query_hash[:extra_fields][resource.type] || []
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|