jsonapi-resources 0.10.0.beta2 → 0.10.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 +5 -5
- data/lib/jsonapi/{active_relation_resource_finder → active_relation}/adapters/join_left_active_record_adapter.rb +2 -2
- data/lib/jsonapi/active_relation/join_manager.rb +297 -0
- data/lib/jsonapi/{active_relation_resource_finder.rb → active_relation_resource.rb} +189 -146
- data/lib/jsonapi/acts_as_resource_controller.rb +4 -3
- data/lib/jsonapi/basic_resource.rb +1162 -0
- data/lib/jsonapi/cached_response_fragment.rb +66 -49
- data/lib/jsonapi/configuration.rb +18 -13
- data/lib/jsonapi/include_directives.rb +6 -8
- data/lib/jsonapi/link_builder.rb +71 -85
- data/lib/jsonapi/operation_result.rb +2 -2
- data/lib/jsonapi/path.rb +8 -0
- data/lib/jsonapi/path_segment.rb +17 -5
- data/lib/jsonapi/processor.rb +6 -7
- data/lib/jsonapi/relationship.rb +32 -3
- data/lib/jsonapi/request_parser.rb +44 -10
- data/lib/jsonapi/resource.rb +3 -1106
- data/lib/jsonapi/resource_controller_metal.rb +3 -0
- data/lib/jsonapi/resource_serializer.rb +39 -33
- data/lib/jsonapi/resource_set.rb +118 -48
- data/lib/jsonapi/resources/railtie.rb +9 -0
- data/lib/jsonapi/resources/version.rb +1 -1
- data/lib/jsonapi/response_document.rb +7 -2
- data/lib/jsonapi/routing_ext.rb +24 -4
- data/lib/jsonapi-resources.rb +12 -3
- data/lib/tasks/check_upgrade.rake +52 -0
- metadata +28 -12
- data/lib/jsonapi/active_relation_resource_finder/join_tree.rb +0 -227
|
@@ -1,22 +1,42 @@
|
|
|
1
1
|
module JSONAPI
|
|
2
2
|
class CachedResponseFragment
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
results = self.lookup(resource_klass, serializer_config_key, context, context_key, cache_ids)
|
|
9
|
-
|
|
10
|
-
if JSONAPI.configuration.resource_cache_usage_report_function
|
|
11
|
-
miss_ids = results.select{|_k,v| v.nil? }.keys
|
|
12
|
-
JSONAPI.configuration.resource_cache_usage_report_function.call(
|
|
13
|
-
resource_klass.name,
|
|
14
|
-
cache_ids.size - miss_ids.size,
|
|
15
|
-
miss_ids.size
|
|
16
|
-
)
|
|
3
|
+
|
|
4
|
+
Lookup = Struct.new(:resource_klass, :serializer_config_key, :context, :context_key, :cache_ids) do
|
|
5
|
+
|
|
6
|
+
def type
|
|
7
|
+
resource_klass._type
|
|
17
8
|
end
|
|
18
9
|
|
|
19
|
-
|
|
10
|
+
def keys
|
|
11
|
+
cache_ids.map do |(id, cache_key)|
|
|
12
|
+
[type, id, cache_key, serializer_config_key, context_key]
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
Write = Struct.new(:resource_klass, :resource, :serializer, :serializer_config_key, :context, :context_key, :relationship_data) do
|
|
18
|
+
def to_key_value
|
|
19
|
+
|
|
20
|
+
(id, cache_key) = resource.cache_id
|
|
21
|
+
|
|
22
|
+
json = serializer.object_hash(resource, relationship_data)
|
|
23
|
+
|
|
24
|
+
cr = CachedResponseFragment.new(
|
|
25
|
+
resource_klass,
|
|
26
|
+
id,
|
|
27
|
+
json['type'],
|
|
28
|
+
context,
|
|
29
|
+
resource.fetchable_fields,
|
|
30
|
+
json['relationships'],
|
|
31
|
+
json['links'],
|
|
32
|
+
json['attributes'],
|
|
33
|
+
json['meta']
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
key = [resource_klass._type, id, cache_key, serializer_config_key, context_key]
|
|
37
|
+
|
|
38
|
+
[key, cr]
|
|
39
|
+
end
|
|
20
40
|
end
|
|
21
41
|
|
|
22
42
|
attr_reader :resource_klass, :id, :type, :context, :fetchable_fields, :relationships,
|
|
@@ -50,26 +70,46 @@ module JSONAPI
|
|
|
50
70
|
}
|
|
51
71
|
end
|
|
52
72
|
|
|
53
|
-
|
|
73
|
+
# @param [Lookup[]] lookups
|
|
74
|
+
# @return [Hash<Class<Resource>, Hash<ID, CachedResourceFragment>>]
|
|
75
|
+
def self.lookup(lookups, context)
|
|
76
|
+
type_to_klass = lookups.map {|l| [l.type, l.resource_klass]}.to_h
|
|
54
77
|
|
|
55
|
-
|
|
56
|
-
type = resource_klass._type
|
|
78
|
+
keys = lookups.map(&:keys).flatten(1)
|
|
57
79
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
80
|
+
hits = JSONAPI.configuration.resource_cache.read_multi(*keys).reject {|_, v| v.nil?}
|
|
81
|
+
|
|
82
|
+
return keys.inject({}) do |hash, key|
|
|
83
|
+
(type, id, _, _) = key
|
|
84
|
+
resource_klass = type_to_klass[type]
|
|
85
|
+
hash[resource_klass] ||= {}
|
|
61
86
|
|
|
62
|
-
hits = JSONAPI.configuration.resource_cache.read_multi(*keys).reject{|_,v| v.nil? }
|
|
63
|
-
return keys.each_with_object({}) do |key, hash|
|
|
64
|
-
(_, id, _, _) = key
|
|
65
87
|
if hits.has_key?(key)
|
|
66
|
-
hash[id] = self.from_cache_value(resource_klass, context, hits[key])
|
|
88
|
+
hash[resource_klass][id] = self.from_cache_value(resource_klass, context, hits[key])
|
|
67
89
|
else
|
|
68
|
-
hash[id] = nil
|
|
90
|
+
hash[resource_klass][id] = nil
|
|
69
91
|
end
|
|
92
|
+
|
|
93
|
+
hash
|
|
70
94
|
end
|
|
71
95
|
end
|
|
72
96
|
|
|
97
|
+
# @param [Write[]] lookups
|
|
98
|
+
def self.write(writes)
|
|
99
|
+
key_values = writes.map(&:to_key_value)
|
|
100
|
+
|
|
101
|
+
to_write = key_values.map {|(k, v)| [k, v.to_cache_value]}.to_h
|
|
102
|
+
|
|
103
|
+
if JSONAPI.configuration.resource_cache.respond_to? :write_multi
|
|
104
|
+
JSONAPI.configuration.resource_cache.write_multi(to_write)
|
|
105
|
+
else
|
|
106
|
+
to_write.each do |key, value|
|
|
107
|
+
JSONAPI.configuration.resource_cache.write(key, value)
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
end
|
|
112
|
+
|
|
73
113
|
def self.from_cache_value(resource_klass, context, h)
|
|
74
114
|
new(
|
|
75
115
|
resource_klass,
|
|
@@ -83,28 +123,5 @@ module JSONAPI
|
|
|
83
123
|
h.fetch(:meta, nil)
|
|
84
124
|
)
|
|
85
125
|
end
|
|
86
|
-
|
|
87
|
-
def self.write(resource_klass, resource, serializer, serializer_config_key, context, context_key, relationship_data )
|
|
88
|
-
(id, cache_key) = resource.cache_id
|
|
89
|
-
|
|
90
|
-
json = serializer.object_hash(resource, relationship_data)
|
|
91
|
-
|
|
92
|
-
cr = self.new(
|
|
93
|
-
resource_klass,
|
|
94
|
-
id,
|
|
95
|
-
json['type'],
|
|
96
|
-
context,
|
|
97
|
-
resource.fetchable_fields,
|
|
98
|
-
json['relationships'],
|
|
99
|
-
json['links'],
|
|
100
|
-
json['attributes'],
|
|
101
|
-
json['meta']
|
|
102
|
-
)
|
|
103
|
-
|
|
104
|
-
key = [resource_klass._type, id, cache_key, serializer_config_key, context_key]
|
|
105
|
-
JSONAPI.configuration.resource_cache.write(key, cr.to_cache_value)
|
|
106
|
-
return [id, cr]
|
|
107
|
-
end
|
|
108
|
-
|
|
109
126
|
end
|
|
110
127
|
end
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
require 'jsonapi/formatter'
|
|
2
2
|
require 'jsonapi/processor'
|
|
3
|
-
require 'jsonapi/active_relation_resource_finder'
|
|
4
3
|
require 'concurrent'
|
|
5
4
|
|
|
6
5
|
module JSONAPI
|
|
@@ -10,6 +9,8 @@ module JSONAPI
|
|
|
10
9
|
:route_format,
|
|
11
10
|
:raise_if_parameters_not_allowed,
|
|
12
11
|
:warn_on_route_setup_issues,
|
|
12
|
+
:warn_on_missing_routes,
|
|
13
|
+
:warn_on_performance_issues,
|
|
13
14
|
:default_allow_include_to_one,
|
|
14
15
|
:default_allow_include_to_many,
|
|
15
16
|
:allow_sort,
|
|
@@ -17,7 +18,6 @@ module JSONAPI
|
|
|
17
18
|
:default_paginator,
|
|
18
19
|
:default_page_size,
|
|
19
20
|
:maximum_page_size,
|
|
20
|
-
:resource_finder,
|
|
21
21
|
:default_processor_klass,
|
|
22
22
|
:use_text_errors,
|
|
23
23
|
:top_level_links_include_pagination,
|
|
@@ -38,7 +38,8 @@ module JSONAPI
|
|
|
38
38
|
:default_caching,
|
|
39
39
|
:default_resource_cache_field,
|
|
40
40
|
:resource_cache_digest_function,
|
|
41
|
-
:resource_cache_usage_report_function
|
|
41
|
+
:resource_cache_usage_report_function,
|
|
42
|
+
:default_exclude_links
|
|
42
43
|
|
|
43
44
|
def initialize
|
|
44
45
|
#:underscored_key, :camelized_key, :dasherized_key, or custom
|
|
@@ -59,6 +60,8 @@ module JSONAPI
|
|
|
59
60
|
self.raise_if_parameters_not_allowed = true
|
|
60
61
|
|
|
61
62
|
self.warn_on_route_setup_issues = true
|
|
63
|
+
self.warn_on_missing_routes = true
|
|
64
|
+
self.warn_on_performance_issues = true
|
|
62
65
|
|
|
63
66
|
# :none, :offset, :paged, or a custom paginator name
|
|
64
67
|
self.default_paginator = :none
|
|
@@ -105,12 +108,6 @@ module JSONAPI
|
|
|
105
108
|
self.always_include_to_one_linkage_data = false
|
|
106
109
|
self.always_include_to_many_linkage_data = false
|
|
107
110
|
|
|
108
|
-
# ResourceFinder Mixin
|
|
109
|
-
# The default ResourceFinder is the ActiveRelationResourceFinder which provides
|
|
110
|
-
# access to ActiveRelation backed models. Custom ResourceFinders can be specified
|
|
111
|
-
# in order to support other ORMs.
|
|
112
|
-
self.resource_finder = JSONAPI::ActiveRelationResourceFinder
|
|
113
|
-
|
|
114
111
|
# The default Operation Processor to use if one is not defined specifically
|
|
115
112
|
# for a Resource.
|
|
116
113
|
self.default_processor_klass = JSONAPI::Processor
|
|
@@ -155,6 +152,12 @@ module JSONAPI
|
|
|
155
152
|
# Optionally provide a callable which JSONAPI will call with information about cache
|
|
156
153
|
# performance. Should accept three arguments: resource name, hits count, misses count.
|
|
157
154
|
self.resource_cache_usage_report_function = nil
|
|
155
|
+
|
|
156
|
+
# Global configuration for links exclusion
|
|
157
|
+
# Controls whether to generate links like `self`, `related` with all the resources
|
|
158
|
+
# and relationships. Accepts either `:default`, `:none`, or array containing the
|
|
159
|
+
# specific default links to exclude, which may be `:self` and `:related`.
|
|
160
|
+
self.default_exclude_links = :none
|
|
158
161
|
end
|
|
159
162
|
|
|
160
163
|
def cache_formatters=(bool)
|
|
@@ -225,10 +228,6 @@ module JSONAPI
|
|
|
225
228
|
@default_processor_klass = default_processor_klass
|
|
226
229
|
end
|
|
227
230
|
|
|
228
|
-
def resource_finder=(resource_finder)
|
|
229
|
-
@resource_finder = resource_finder
|
|
230
|
-
end
|
|
231
|
-
|
|
232
231
|
def allow_include=(allow_include)
|
|
233
232
|
ActiveSupport::Deprecation.warn('`allow_include` has been replaced by `default_allow_include_to_one` and `default_allow_include_to_many` options.')
|
|
234
233
|
@default_allow_include_to_one = allow_include
|
|
@@ -273,6 +272,10 @@ module JSONAPI
|
|
|
273
272
|
|
|
274
273
|
attr_writer :warn_on_route_setup_issues
|
|
275
274
|
|
|
275
|
+
attr_writer :warn_on_missing_routes
|
|
276
|
+
|
|
277
|
+
attr_writer :warn_on_performance_issues
|
|
278
|
+
|
|
276
279
|
attr_writer :use_relationship_reflection
|
|
277
280
|
|
|
278
281
|
attr_writer :resource_cache
|
|
@@ -284,6 +287,8 @@ module JSONAPI
|
|
|
284
287
|
attr_writer :resource_cache_digest_function
|
|
285
288
|
|
|
286
289
|
attr_writer :resource_cache_usage_report_function
|
|
290
|
+
|
|
291
|
+
attr_writer :default_exclude_links
|
|
287
292
|
end
|
|
288
293
|
|
|
289
294
|
class << self
|
|
@@ -4,14 +4,12 @@ module JSONAPI
|
|
|
4
4
|
# For example ['posts.comments.tags']
|
|
5
5
|
# will transform into =>
|
|
6
6
|
# {
|
|
7
|
-
# posts:{
|
|
8
|
-
#
|
|
9
|
-
# include_related:{
|
|
7
|
+
# posts: {
|
|
8
|
+
# include_related: {
|
|
10
9
|
# comments:{
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
# include:true
|
|
10
|
+
# include_related: {
|
|
11
|
+
# tags: {
|
|
12
|
+
# include_related: {}
|
|
15
13
|
# }
|
|
16
14
|
# }
|
|
17
15
|
# }
|
|
@@ -44,7 +42,7 @@ module JSONAPI
|
|
|
44
42
|
path.segments.each do |segment|
|
|
45
43
|
relationship_name = segment.relationship.name.to_sym
|
|
46
44
|
|
|
47
|
-
current[:include_related][relationship_name] ||= {
|
|
45
|
+
current[:include_related][relationship_name] ||= { include_related: {} }
|
|
48
46
|
current = current[:include_related][relationship_name]
|
|
49
47
|
end
|
|
50
48
|
|
data/lib/jsonapi/link_builder.rb
CHANGED
|
@@ -3,109 +3,100 @@ module JSONAPI
|
|
|
3
3
|
attr_reader :base_url,
|
|
4
4
|
:primary_resource_klass,
|
|
5
5
|
:route_formatter,
|
|
6
|
-
:
|
|
6
|
+
:engine,
|
|
7
|
+
:engine_mount_point,
|
|
8
|
+
:url_helpers
|
|
9
|
+
|
|
10
|
+
@@url_helper_methods = {}
|
|
7
11
|
|
|
8
12
|
def initialize(config = {})
|
|
9
|
-
@base_url
|
|
13
|
+
@base_url = config[:base_url]
|
|
10
14
|
@primary_resource_klass = config[:primary_resource_klass]
|
|
11
|
-
@route_formatter
|
|
12
|
-
@
|
|
15
|
+
@route_formatter = config[:route_formatter]
|
|
16
|
+
@engine = build_engine
|
|
17
|
+
@engine_mount_point = @engine ? @engine.routes.find_script_name({}) : ""
|
|
13
18
|
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
end
|
|
19
|
+
# url_helpers may be either a controller which has the route helper methods, or the application router's
|
|
20
|
+
# url helpers module, `Rails.application.routes.url_helpers`. Because the method no longer behaves as a
|
|
21
|
+
# singleton, and it's expensive to generate the module, the controller is preferred.
|
|
22
|
+
@url_helpers = config[:url_helpers]
|
|
19
23
|
end
|
|
20
24
|
|
|
21
25
|
def engine?
|
|
22
|
-
!!@
|
|
26
|
+
!!@engine
|
|
23
27
|
end
|
|
24
28
|
|
|
25
29
|
def primary_resources_url
|
|
26
|
-
if
|
|
27
|
-
|
|
30
|
+
if @primary_resource_klass._routed
|
|
31
|
+
primary_resources_path = resources_path(primary_resource_klass)
|
|
32
|
+
@primary_resources_url_cached ||= "#{ base_url }#{ engine_mount_point }#{ primary_resources_path }"
|
|
28
33
|
else
|
|
29
|
-
|
|
34
|
+
if JSONAPI.configuration.warn_on_missing_routes && !@primary_resource_klass._warned_missing_route
|
|
35
|
+
warn "primary_resources_url for #{@primary_resource_klass} could not be generated"
|
|
36
|
+
@primary_resource_klass._warned_missing_route = true
|
|
37
|
+
end
|
|
38
|
+
nil
|
|
30
39
|
end
|
|
31
40
|
end
|
|
32
41
|
|
|
33
42
|
def query_link(query_params)
|
|
34
|
-
|
|
43
|
+
url = primary_resources_url
|
|
44
|
+
return url if url.nil?
|
|
45
|
+
"#{ url }?#{ query_params.to_query }"
|
|
35
46
|
end
|
|
36
47
|
|
|
37
48
|
def relationships_related_link(source, relationship, query_params = {})
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
49
|
+
if relationship._routed
|
|
50
|
+
url = "#{ self_link(source) }/#{ route_for_relationship(relationship) }"
|
|
51
|
+
url = "#{ url }?#{ query_params.to_query }" if query_params.present?
|
|
52
|
+
url
|
|
53
|
+
else
|
|
54
|
+
if JSONAPI.configuration.warn_on_missing_routes && !relationship._warned_missing_route
|
|
55
|
+
warn "related_link for #{relationship} could not be generated"
|
|
56
|
+
relationship._warned_missing_route = true
|
|
57
|
+
end
|
|
58
|
+
nil
|
|
59
|
+
end
|
|
41
60
|
end
|
|
42
61
|
|
|
43
62
|
def relationships_self_link(source, relationship)
|
|
44
|
-
|
|
63
|
+
if relationship._routed
|
|
64
|
+
"#{ self_link(source) }/relationships/#{ route_for_relationship(relationship) }"
|
|
65
|
+
else
|
|
66
|
+
if JSONAPI.configuration.warn_on_missing_routes && !relationship._warned_missing_route
|
|
67
|
+
warn "self_link for #{relationship} could not be generated"
|
|
68
|
+
relationship._warned_missing_route = true
|
|
69
|
+
end
|
|
70
|
+
nil
|
|
71
|
+
end
|
|
45
72
|
end
|
|
46
73
|
|
|
47
74
|
def self_link(source)
|
|
48
|
-
if
|
|
49
|
-
|
|
75
|
+
if source.class._routed
|
|
76
|
+
resource_url(source)
|
|
50
77
|
else
|
|
51
|
-
|
|
78
|
+
if JSONAPI.configuration.warn_on_missing_routes && !source.class._warned_missing_route
|
|
79
|
+
warn "self_link for #{source.class} could not be generated"
|
|
80
|
+
source.class._warned_missing_route = true
|
|
81
|
+
end
|
|
82
|
+
nil
|
|
52
83
|
end
|
|
53
84
|
end
|
|
54
85
|
|
|
55
86
|
private
|
|
56
87
|
|
|
57
|
-
def
|
|
88
|
+
def build_engine
|
|
58
89
|
scopes = module_scopes_from_class(primary_resource_klass)
|
|
59
90
|
|
|
60
91
|
begin
|
|
61
92
|
unless scopes.empty?
|
|
62
93
|
"#{ scopes.first.to_s.camelize }::Engine".safe_constantize
|
|
63
94
|
end
|
|
64
|
-
|
|
95
|
+
|
|
96
|
+
# :nocov:
|
|
65
97
|
rescue LoadError => _e
|
|
66
98
|
nil
|
|
67
|
-
|
|
68
|
-
end
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
def engine_path_from_resource_class(klass)
|
|
72
|
-
path_name = engine_resources_path_name_from_class(klass)
|
|
73
|
-
engine_name.routes.url_helpers.public_send(path_name)
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
def engine_primary_resources_path
|
|
77
|
-
engine_path_from_resource_class(primary_resource_klass)
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
def engine_primary_resources_url
|
|
81
|
-
"#{ base_url }#{ engine_primary_resources_path }"
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
def engine_resource_path(source)
|
|
85
|
-
resource_path_name = engine_resource_path_name_from_source(source)
|
|
86
|
-
engine_name.routes.url_helpers.public_send(resource_path_name, source.id)
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
def engine_resource_path_name_from_source(source)
|
|
90
|
-
scopes = module_scopes_from_class(source.class)[1..-1]
|
|
91
|
-
base_path_name = scopes.map { |scope| scope.underscore }.join("_")
|
|
92
|
-
end_path_name = source.class._type.to_s.singularize
|
|
93
|
-
[base_path_name, end_path_name, "path"].reject(&:blank?).join("_")
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
def engine_resource_url(source)
|
|
97
|
-
"#{ base_url }#{ engine_resource_path(source) }"
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
def engine_resources_path_name_from_class(klass)
|
|
101
|
-
scopes = module_scopes_from_class(klass)[1..-1]
|
|
102
|
-
base_path_name = scopes.map { |scope| scope.underscore }.join("_")
|
|
103
|
-
end_path_name = klass._type.to_s
|
|
104
|
-
|
|
105
|
-
if base_path_name.blank?
|
|
106
|
-
"#{ end_path_name }_path"
|
|
107
|
-
else
|
|
108
|
-
"#{ base_path_name }_#{ end_path_name }_path"
|
|
99
|
+
# :nocov:
|
|
109
100
|
end
|
|
110
101
|
end
|
|
111
102
|
|
|
@@ -114,10 +105,14 @@ module JSONAPI
|
|
|
114
105
|
end
|
|
115
106
|
|
|
116
107
|
def formatted_module_path_from_class(klass)
|
|
117
|
-
scopes =
|
|
108
|
+
scopes = if @engine
|
|
109
|
+
module_scopes_from_class(klass)[1..-1]
|
|
110
|
+
else
|
|
111
|
+
module_scopes_from_class(klass)
|
|
112
|
+
end
|
|
118
113
|
|
|
119
114
|
unless scopes.empty?
|
|
120
|
-
"/#{ scopes.map{
|
|
115
|
+
"/#{ scopes.map {|scope| format_route(scope.to_s.underscore)}.compact.join('/') }/"
|
|
121
116
|
else
|
|
122
117
|
"/"
|
|
123
118
|
end
|
|
@@ -127,30 +122,21 @@ module JSONAPI
|
|
|
127
122
|
klass.name.to_s.split("::")[0...-1]
|
|
128
123
|
end
|
|
129
124
|
|
|
130
|
-
def
|
|
131
|
-
@
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
def regular_primary_resources_path
|
|
135
|
-
regular_resources_path(primary_resource_klass)
|
|
125
|
+
def resources_path(source_klass)
|
|
126
|
+
@_resources_path ||= {}
|
|
127
|
+
@_resources_path[source_klass] ||= formatted_module_path_from_class(source_klass) + format_route(source_klass._type.to_s)
|
|
136
128
|
end
|
|
137
129
|
|
|
138
|
-
def
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
def regular_resource_path(source)
|
|
143
|
-
if source.is_a?(JSONAPI::CachedResponseFragment)
|
|
144
|
-
# :nocov:
|
|
145
|
-
"#{regular_resources_path(source.resource_klass)}/#{source.id}"
|
|
146
|
-
# :nocov:
|
|
130
|
+
def resource_path(source)
|
|
131
|
+
if source.class.singleton?
|
|
132
|
+
resources_path(source.class)
|
|
147
133
|
else
|
|
148
|
-
"#{
|
|
134
|
+
"#{resources_path(source.class)}/#{source.id}"
|
|
149
135
|
end
|
|
150
136
|
end
|
|
151
137
|
|
|
152
|
-
def
|
|
153
|
-
"#{ base_url }#{
|
|
138
|
+
def resource_url(source)
|
|
139
|
+
"#{ base_url }#{ engine_mount_point }#{ resource_path(source) }"
|
|
154
140
|
end
|
|
155
141
|
|
|
156
142
|
def route_for_relationship(relationship)
|
|
@@ -100,7 +100,7 @@ module JSONAPI
|
|
|
100
100
|
end
|
|
101
101
|
end
|
|
102
102
|
|
|
103
|
-
class
|
|
103
|
+
class RelationshipOperationResult < OperationResult
|
|
104
104
|
attr_accessor :parent_resource, :relationship, :resource_ids
|
|
105
105
|
|
|
106
106
|
def initialize(code, parent_resource, relationship, resource_ids, options = {})
|
|
@@ -112,7 +112,7 @@ module JSONAPI
|
|
|
112
112
|
|
|
113
113
|
def to_hash(serializer = nil)
|
|
114
114
|
if serializer
|
|
115
|
-
serializer.
|
|
115
|
+
serializer.serialize_to_relationship_hash(parent_resource, relationship, resource_ids)
|
|
116
116
|
else
|
|
117
117
|
# :nocov:
|
|
118
118
|
{}
|
data/lib/jsonapi/path.rb
CHANGED
|
@@ -31,5 +31,13 @@ module JSONAPI
|
|
|
31
31
|
def relationship_path_string
|
|
32
32
|
relationship_segments.collect(&:to_s).join('.')
|
|
33
33
|
end
|
|
34
|
+
|
|
35
|
+
def last_relationship
|
|
36
|
+
if @segments.last.is_a?(PathSegment::Relationship)
|
|
37
|
+
@segments.last
|
|
38
|
+
else
|
|
39
|
+
@segments[-2]
|
|
40
|
+
end
|
|
41
|
+
end
|
|
34
42
|
end
|
|
35
43
|
end
|
data/lib/jsonapi/path_segment.rb
CHANGED
|
@@ -22,19 +22,27 @@ module JSONAPI
|
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
class Relationship
|
|
25
|
-
attr_reader :relationship
|
|
25
|
+
attr_reader :relationship, :resource_klass
|
|
26
26
|
|
|
27
|
-
def initialize(relationship:, resource_klass:)
|
|
27
|
+
def initialize(relationship:, resource_klass: nil)
|
|
28
28
|
@relationship = relationship
|
|
29
29
|
@resource_klass = resource_klass
|
|
30
30
|
end
|
|
31
31
|
|
|
32
|
+
def eql?(other)
|
|
33
|
+
relationship == other.relationship && resource_klass == other.resource_klass
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def hash
|
|
37
|
+
[relationship, resource_klass].hash
|
|
38
|
+
end
|
|
39
|
+
|
|
32
40
|
def to_s
|
|
33
|
-
@resource_klass ? "#{relationship.name}##{resource_klass._type}" : "#{relationship.name}"
|
|
41
|
+
@resource_klass ? "#{relationship.parent_resource_klass._type}.#{relationship.name}##{resource_klass._type}" : "#{resource_klass._type}.#{relationship.name}"
|
|
34
42
|
end
|
|
35
43
|
|
|
36
44
|
def resource_klass
|
|
37
|
-
@resource_klass ||
|
|
45
|
+
@resource_klass || relationship.resource_klass
|
|
38
46
|
end
|
|
39
47
|
|
|
40
48
|
def path_specified_resource_klass?
|
|
@@ -50,13 +58,17 @@ module JSONAPI
|
|
|
50
58
|
@field_name = field_name
|
|
51
59
|
end
|
|
52
60
|
|
|
61
|
+
def eql?(other)
|
|
62
|
+
field_name == other.field_name && resource_klass == other.resource_klass
|
|
63
|
+
end
|
|
64
|
+
|
|
53
65
|
def delegated_field_name
|
|
54
66
|
resource_klass._attribute_delegated_name(field_name)
|
|
55
67
|
end
|
|
56
68
|
|
|
57
69
|
def to_s
|
|
58
70
|
# :nocov:
|
|
59
|
-
field_name.to_s
|
|
71
|
+
"#{resource_klass._type}.#{field_name.to_s}"
|
|
60
72
|
# :nocov:
|
|
61
73
|
end
|
|
62
74
|
end
|
data/lib/jsonapi/processor.rb
CHANGED
|
@@ -130,11 +130,11 @@ module JSONAPI
|
|
|
130
130
|
find_options,
|
|
131
131
|
nil)
|
|
132
132
|
|
|
133
|
-
return JSONAPI::
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
133
|
+
return JSONAPI::RelationshipOperationResult.new(:ok,
|
|
134
|
+
parent_resource,
|
|
135
|
+
resource_klass._relationship(relationship_type),
|
|
136
|
+
resource_id_tree.fragments.keys,
|
|
137
|
+
result_options)
|
|
138
138
|
end
|
|
139
139
|
|
|
140
140
|
def show_related_resource
|
|
@@ -429,8 +429,7 @@ module JSONAPI
|
|
|
429
429
|
def load_included(resource_klass, source_resource_id_tree, include_related, options)
|
|
430
430
|
source_rids = source_resource_id_tree.fragments.keys
|
|
431
431
|
|
|
432
|
-
include_related.try(:
|
|
433
|
-
next unless value[:include]
|
|
432
|
+
include_related.try(:each_key) do |key|
|
|
434
433
|
relationship = resource_klass._relationship(key)
|
|
435
434
|
relationship_name = relationship.name.to_sym
|
|
436
435
|
|
data/lib/jsonapi/relationship.rb
CHANGED
|
@@ -7,6 +7,8 @@ module JSONAPI
|
|
|
7
7
|
|
|
8
8
|
attr_writer :allow_include
|
|
9
9
|
|
|
10
|
+
attr_accessor :_routed, :_warned_missing_route
|
|
11
|
+
|
|
10
12
|
def initialize(name, options = {})
|
|
11
13
|
@name = name.to_s
|
|
12
14
|
@options = options
|
|
@@ -27,11 +29,17 @@ module JSONAPI
|
|
|
27
29
|
@class_name = nil
|
|
28
30
|
@inverse_relationship = nil
|
|
29
31
|
|
|
30
|
-
|
|
32
|
+
@_routed = false
|
|
33
|
+
@_warned_missing_route = false
|
|
34
|
+
|
|
35
|
+
exclude_links(options.fetch(:exclude_links, JSONAPI.configuration.default_exclude_links))
|
|
36
|
+
|
|
37
|
+
# Custom methods are reserved for future use
|
|
31
38
|
@custom_methods = options.fetch(:custom_methods, {})
|
|
32
39
|
end
|
|
33
40
|
|
|
34
41
|
alias_method :polymorphic?, :polymorphic
|
|
42
|
+
alias_method :parent_resource_klass, :parent_resource
|
|
35
43
|
|
|
36
44
|
def primary_key
|
|
37
45
|
# :nocov:
|
|
@@ -98,6 +106,27 @@ module JSONAPI
|
|
|
98
106
|
@options[:readonly]
|
|
99
107
|
end
|
|
100
108
|
|
|
109
|
+
def exclude_links(exclude)
|
|
110
|
+
case exclude
|
|
111
|
+
when :default, "default"
|
|
112
|
+
@_exclude_links = [:self, :related]
|
|
113
|
+
when :none, "none"
|
|
114
|
+
@_exclude_links = []
|
|
115
|
+
when Array
|
|
116
|
+
@_exclude_links = exclude.collect {|link| link.to_sym}
|
|
117
|
+
else
|
|
118
|
+
fail "Invalid exclude_links"
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def _exclude_links
|
|
123
|
+
@_exclude_links ||= []
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def exclude_link?(link)
|
|
127
|
+
_exclude_links.include?(link.to_sym)
|
|
128
|
+
end
|
|
129
|
+
|
|
101
130
|
class ToOne < Relationship
|
|
102
131
|
attr_reader :foreign_key_on
|
|
103
132
|
|
|
@@ -113,7 +142,7 @@ module JSONAPI
|
|
|
113
142
|
|
|
114
143
|
def to_s
|
|
115
144
|
# :nocov: useful for debugging
|
|
116
|
-
"#{parent_resource
|
|
145
|
+
"#{parent_resource}.#{name}(#{belongs_to? ? 'BelongsToOne' : 'ToOne'})"
|
|
117
146
|
# :nocov:
|
|
118
147
|
end
|
|
119
148
|
|
|
@@ -163,7 +192,7 @@ module JSONAPI
|
|
|
163
192
|
|
|
164
193
|
def to_s
|
|
165
194
|
# :nocov: useful for debugging
|
|
166
|
-
"#{parent_resource
|
|
195
|
+
"#{parent_resource}.#{name}(ToMany)"
|
|
167
196
|
# :nocov:
|
|
168
197
|
end
|
|
169
198
|
|