jsonapi-resources 0.9.8 → 0.9.9

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: 180872f33ad6e44850227f9cf657698541cfdc495bba54b818f2be82591ffbe4
4
- data.tar.gz: e524645c43739882441606fe00d272092177f4b792d6a6f5d4fc09f444eef073
3
+ metadata.gz: 4eea7d8efe26fb352cbe612525f39300935a37aae5a7aefed2dbd85ae6c5d4f4
4
+ data.tar.gz: 34c5c45acc815e60e4c8eefff863cb52c12b5ec1ef007685305e6d7f5d016c96
5
5
  SHA512:
6
- metadata.gz: 7048b19d34a04968deee31dd728373abf15c4707ec937544479e68129f0caeb14a7199b2195b9c2944f57887f76d34527e49a3e30a9d1afe2c6739a29ca1c40c
7
- data.tar.gz: 2d9c25c7775f7eeae4f73c2129cd60b16e44fe1c419915e02ba30ea0dbda49f60db3f176d640d535544fd5191551d0e7ea49df0ccc44e875659155a7e98abf3d
6
+ metadata.gz: a59f2ba8abc7d5abd77dd210af2f132eb0631f8133c7b23bf595158cbf9cc09d0fc275740ca0b857dee172f02851f6f40b60c6584b5ec8b47f59b5b50bc09d44
7
+ data.tar.gz: df41e80df849d966a0a2dd86fa1b5f35133103568297ab4ac38710babca3eb41d3a289acce994ece2d51d98995813cd6b50bc1b1c86ffea9994ef777b3d53da4
@@ -125,7 +125,8 @@ module JSONAPI
125
125
  base_url: base_url,
126
126
  key_formatter: key_formatter,
127
127
  route_formatter: route_formatter,
128
- serialization_options: serialization_options
128
+ serialization_options: serialization_options,
129
+ controller: self
129
130
  )
130
131
  @resource_serializer
131
132
  end
@@ -33,7 +33,8 @@ module JSONAPI
33
33
  :resource_cache,
34
34
  :default_resource_cache_field,
35
35
  :resource_cache_digest_function,
36
- :resource_cache_usage_report_function
36
+ :resource_cache_usage_report_function,
37
+ :default_exclude_links
37
38
 
38
39
  def initialize
39
40
  #:underscored_key, :camelized_key, :dasherized_key, or custom
@@ -134,6 +135,12 @@ module JSONAPI
134
135
  # Optionally provide a callable which JSONAPI will call with information about cache
135
136
  # performance. Should accept three arguments: resource name, hits count, misses count.
136
137
  self.resource_cache_usage_report_function = nil
138
+
139
+ # Global configuration for links exclusion
140
+ # Controls whether to generate links like `self`, `related` with all the resources
141
+ # and relationships. Accepts either `:default`, `:none`, or array containing the
142
+ # specific default links to exclude, which may be `:self` and `:related`.
143
+ self.default_exclude_links = :none
137
144
  end
138
145
 
139
146
  def cache_formatters=(bool)
@@ -249,6 +256,8 @@ module JSONAPI
249
256
  attr_writer :resource_cache_digest_function
250
257
 
251
258
  attr_writer :resource_cache_usage_report_function
259
+
260
+ attr_writer :default_exclude_links
252
261
  end
253
262
 
254
263
  class << self
@@ -2,23 +2,24 @@ module JSONAPI
2
2
  class LinkBuilder
3
3
  attr_reader :base_url,
4
4
  :primary_resource_klass,
5
+ :route_formatter,
5
6
  :engine,
6
- :routes
7
+ :engine_mount_point,
8
+ :url_helpers
9
+
10
+ @@url_helper_methods = {}
7
11
 
8
12
  def initialize(config = {})
9
- @base_url = config[:base_url]
13
+ @base_url = config[:base_url]
10
14
  @primary_resource_klass = config[:primary_resource_klass]
11
- @engine = build_engine
12
-
13
- if engine?
14
- @routes = @engine.routes
15
- else
16
- @routes = Rails.application.routes
17
- end
15
+ @route_formatter = config[:route_formatter]
16
+ @engine = build_engine
17
+ @engine_mount_point = @engine ? @engine.routes.find_script_name({}) : ""
18
18
 
19
- # ToDo: Use NaiveCache for values. For this we need to not return nils and create composite keys which work
20
- # as efficient cache lookups. This could be an array of the [source.identifier, relationship] since the
21
- # ResourceIdentity will compare equality correctly
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]
22
23
  end
23
24
 
24
25
  def engine?
@@ -26,50 +27,60 @@ module JSONAPI
26
27
  end
27
28
 
28
29
  def primary_resources_url
29
- @primary_resources_url_cached ||= "#{ base_url }#{ primary_resources_path }"
30
- rescue NoMethodError
31
- warn "primary_resources_url for #{@primary_resource_klass} could not be generated" if JSONAPI.configuration.warn_on_missing_routes
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 }"
33
+ else
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
39
+ end
32
40
  end
33
41
 
34
42
  def query_link(query_params)
35
- "#{ primary_resources_url }?#{ query_params.to_query }"
43
+ url = primary_resources_url
44
+ return url if url.nil?
45
+ "#{ url }?#{ query_params.to_query }"
36
46
  end
37
47
 
38
48
  def relationships_related_link(source, relationship, query_params = {})
39
- if relationship.parent_resource.singleton?
40
- url_helper_name = singleton_related_url_helper_name(relationship)
41
- url = call_url_helper(url_helper_name)
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
42
53
  else
43
- url_helper_name = related_url_helper_name(relationship)
44
- url = call_url_helper(url_helper_name, source.id)
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
45
59
  end
46
-
47
- url = "#{ base_url }#{ url }"
48
- url = "#{ url }?#{ query_params.to_query }" if query_params.present?
49
- url
50
- rescue NoMethodError
51
- warn "related_link for #{relationship} could not be generated" if JSONAPI.configuration.warn_on_missing_routes
52
60
  end
53
61
 
54
62
  def relationships_self_link(source, relationship)
55
- if relationship.parent_resource.singleton?
56
- url_helper_name = singleton_relationship_self_url_helper_name(relationship)
57
- url = call_url_helper(url_helper_name)
63
+ if relationship._routed
64
+ "#{ self_link(source) }/relationships/#{ route_for_relationship(relationship) }"
58
65
  else
59
- url_helper_name = relationship_self_url_helper_name(relationship)
60
- url = call_url_helper(url_helper_name, source.id)
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
61
71
  end
62
-
63
- url = "#{ base_url }#{ url }"
64
- url
65
- rescue NoMethodError
66
- warn "self_link for #{relationship} could not be generated" if JSONAPI.configuration.warn_on_missing_routes
67
72
  end
68
73
 
69
74
  def self_link(source)
70
- "#{ base_url }#{ resource_path(source) }"
71
- rescue NoMethodError
72
- warn "self_link for #{source.class} could not be generated" if JSONAPI.configuration.warn_on_missing_routes
75
+ if source.class._routed
76
+ resource_url(source)
77
+ else
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
83
+ end
73
84
  end
74
85
 
75
86
  private
@@ -81,6 +92,7 @@ module JSONAPI
81
92
  unless scopes.empty?
82
93
  "#{ scopes.first.to_s.camelize }::Engine".safe_constantize
83
94
  end
95
+
84
96
  # :nocov:
85
97
  rescue LoadError => _e
86
98
  nil
@@ -88,98 +100,47 @@ module JSONAPI
88
100
  end
89
101
  end
90
102
 
91
- def call_url_helper(method, *args)
92
- routes.url_helpers.public_send(method, args)
93
- rescue NoMethodError => e
94
- raise e
103
+ def format_route(route)
104
+ route_formatter.format(route)
95
105
  end
96
106
 
97
- def path_from_resource_class(klass)
98
- url_helper_name = resources_url_helper_name_from_class(klass)
99
- call_url_helper(url_helper_name)
100
- end
107
+ def formatted_module_path_from_class(klass)
108
+ scopes = if @engine
109
+ module_scopes_from_class(klass)[1..-1]
110
+ else
111
+ module_scopes_from_class(klass)
112
+ end
101
113
 
102
- def resource_path(source)
103
- url_helper_name = resource_url_helper_name_from_source(source)
104
- if source.class.singleton?
105
- call_url_helper(url_helper_name)
114
+ unless scopes.empty?
115
+ "/#{ scopes.map {|scope| format_route(scope.to_s.underscore)}.compact.join('/') }/"
106
116
  else
107
- call_url_helper(url_helper_name, source.id)
117
+ "/"
108
118
  end
109
119
  end
110
120
 
111
- def primary_resources_path
112
- path_from_resource_class(primary_resource_klass)
121
+ def module_scopes_from_class(klass)
122
+ klass.name.to_s.split("::")[0...-1]
113
123
  end
114
124
 
115
- def url_helper_name_from_parts(parts)
116
- (parts << "path").reject(&:blank?).join("_")
125
+ def resources_path(source_klass)
126
+ formatted_module_path_from_class(source_klass) + format_route(source_klass._type.to_s)
117
127
  end
118
128
 
119
- def resources_path_parts_from_class(klass)
120
- if engine?
121
- scopes = module_scopes_from_class(klass)[1..-1]
122
- else
123
- scopes = module_scopes_from_class(klass)
124
- end
125
-
126
- base_path_name = scopes.map { |scope| scope.underscore }.join("_")
127
- end_path_name = klass._type.to_s
128
- [base_path_name, end_path_name]
129
- end
130
-
131
- def resources_url_helper_name_from_class(klass)
132
- url_helper_name_from_parts(resources_path_parts_from_class(klass))
133
- end
129
+ def resource_path(source)
130
+ url = "#{resources_path(source.class)}"
134
131
 
135
- def resource_path_parts_from_class(klass)
136
- if engine?
137
- scopes = module_scopes_from_class(klass)[1..-1]
138
- else
139
- scopes = module_scopes_from_class(klass)
132
+ unless source.class.singleton?
133
+ url = "#{url}/#{source.id}"
140
134
  end
141
-
142
- base_path_name = scopes.map { |scope| scope.underscore }.join("_")
143
- end_path_name = klass._type.to_s.singularize
144
- [base_path_name, end_path_name]
145
- end
146
-
147
- def resource_url_helper_name_from_source(source)
148
- url_helper_name_from_parts(resource_path_parts_from_class(source.class))
149
- end
150
-
151
- def related_url_helper_name(relationship)
152
- relationship_parts = resource_path_parts_from_class(relationship.parent_resource)
153
- relationship_parts << "related"
154
- relationship_parts << relationship.name
155
- url_helper_name_from_parts(relationship_parts)
156
- end
157
-
158
- def singleton_related_url_helper_name(relationship)
159
- relationship_parts = []
160
- relationship_parts << "related"
161
- relationship_parts << relationship.name
162
- relationship_parts += resource_path_parts_from_class(relationship.parent_resource)
163
- url_helper_name_from_parts(relationship_parts)
164
- end
165
-
166
- def relationship_self_url_helper_name(relationship)
167
- relationship_parts = resource_path_parts_from_class(relationship.parent_resource)
168
- relationship_parts << "relationships"
169
- relationship_parts << relationship.name
170
- url_helper_name_from_parts(relationship_parts)
135
+ url
171
136
  end
172
137
 
173
- def singleton_relationship_self_url_helper_name(relationship)
174
- relationship_parts = []
175
- relationship_parts << "relationships"
176
- relationship_parts << relationship.name
177
- relationship_parts += resource_path_parts_from_class(relationship.parent_resource)
178
- url_helper_name_from_parts(relationship_parts)
138
+ def resource_url(source)
139
+ "#{ base_url }#{ engine_mount_point }#{ resource_path(source) }"
179
140
  end
180
141
 
181
- def module_scopes_from_class(klass)
182
- klass.name.to_s.split("::")[0...-1]
142
+ def route_for_relationship(relationship)
143
+ format_route(relationship.name)
183
144
  end
184
145
  end
185
146
  end
@@ -4,6 +4,8 @@ module JSONAPI
4
4
  :class_name, :polymorphic, :always_include_linkage_data,
5
5
  :parent_resource, :eager_load_on_include
6
6
 
7
+ attr_accessor :_routed, :_warned_missing_route
8
+
7
9
  def initialize(name, options = {})
8
10
  @name = name.to_s
9
11
  @options = options
@@ -14,8 +16,10 @@ module JSONAPI
14
16
  @polymorphic = options.fetch(:polymorphic, false) == true
15
17
  @always_include_linkage_data = options.fetch(:always_include_linkage_data, false) == true
16
18
  @eager_load_on_include = options.fetch(:eager_load_on_include, true) == true
19
+ @_routed = false
20
+ @_warned_missing_route = false
17
21
 
18
- exclude_links(options.fetch(:exclude_links, :none))
22
+ exclude_links(options.fetch(:exclude_links, JSONAPI.configuration.default_exclude_links))
19
23
  end
20
24
 
21
25
  alias_method :polymorphic?, :polymorphic
@@ -457,6 +457,9 @@ module JSONAPI
457
457
  end
458
458
 
459
459
  check_reserved_resource_name(subclass._type, subclass.name)
460
+
461
+ subclass._routed = false
462
+ subclass._warned_missing_route = false
460
463
  end
461
464
 
462
465
  def rebuild_relationships(relationships)
@@ -502,7 +505,7 @@ module JSONAPI
502
505
  end
503
506
  end
504
507
 
505
- attr_accessor :_attributes, :_relationships, :_type, :_model_hints
508
+ attr_accessor :_attributes, :_relationships, :_type, :_model_hints, :_routed, :_warned_missing_route
506
509
  attr_writer :_allowed_filters, :_paginator
507
510
 
508
511
  def create(context)
@@ -1066,6 +1069,18 @@ module JSONAPI
1066
1069
  end
1067
1070
 
1068
1071
  def exclude_links(exclude)
1072
+ _resolve_exclude_links(exclude)
1073
+ end
1074
+
1075
+ def _exclude_links
1076
+ @_exclude_links ||= _resolve_exclude_links(JSONAPI.configuration.default_exclude_links)
1077
+ end
1078
+
1079
+ def exclude_link?(link)
1080
+ _exclude_links.include?(link.to_sym)
1081
+ end
1082
+
1083
+ def _resolve_exclude_links(exclude)
1069
1084
  case exclude
1070
1085
  when :default, "default"
1071
1086
  @_exclude_links = [:self]
@@ -1078,14 +1093,6 @@ module JSONAPI
1078
1093
  end
1079
1094
  end
1080
1095
 
1081
- def _exclude_links
1082
- @_exclude_links ||= []
1083
- end
1084
-
1085
- def exclude_link?(link)
1086
- _exclude_links.include?(link.to_sym)
1087
- end
1088
-
1089
1096
  def caching(val = true)
1090
1097
  @caching = val
1091
1098
  end
@@ -539,6 +539,8 @@ module JSONAPI
539
539
  LinkBuilder.new(
540
540
  base_url: options.fetch(:base_url, ''),
541
541
  primary_resource_klass: primary_resource_klass,
542
+ route_formatter: options.fetch(:route_formatter, JSONAPI.configuration.route_formatter),
543
+ url_helpers: options.fetch(:url_helpers, options[:controller]),
542
544
  )
543
545
  end
544
546
  end
@@ -1,5 +1,5 @@
1
1
  module JSONAPI
2
2
  module Resources
3
- VERSION = '0.9.8'
3
+ VERSION = '0.9.9'
4
4
  end
5
5
  end
@@ -20,6 +20,8 @@ module ActionDispatch
20
20
  @resource_type = resources.first
21
21
  res = JSONAPI::Resource.resource_for(resource_type_with_module_prefix(@resource_type))
22
22
 
23
+ res._routed = true
24
+
23
25
  unless res.singleton?
24
26
  warn "Singleton routes created for non singleton resource #{res}. Links may not be generated correctly."
25
27
  end
@@ -84,6 +86,8 @@ module ActionDispatch
84
86
  @resource_type = resources.first
85
87
  res = JSONAPI::Resource.resource_for(resource_type_with_module_prefix(@resource_type))
86
88
 
89
+ res._routed = true
90
+
87
91
  if res.singleton?
88
92
  warn "Singleton resource #{res} should use `jsonapi_resource` instead."
89
93
  end
@@ -223,6 +227,8 @@ module ActionDispatch
223
227
  relationship_name = relationship.first
224
228
  relationship = source._relationships[relationship_name]
225
229
 
230
+ relationship._routed = true
231
+
226
232
  formatted_relationship_name = format_route(relationship.name)
227
233
 
228
234
  if relationship.polymorphic?
@@ -245,6 +251,8 @@ module ActionDispatch
245
251
  relationship_name = relationship.first
246
252
  relationship = source._relationships[relationship_name]
247
253
 
254
+ relationship._routed = true
255
+
248
256
  formatted_relationship_name = format_route(relationship.name)
249
257
  related_resource = JSONAPI::Resource.resource_for(resource_type_with_module_prefix(relationship.class_name.underscore))
250
258
  options[:controller] ||= related_resource._type.to_s
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jsonapi-resources
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.8
4
+ version: 0.9.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dan Gebhardt
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2019-06-04 00:00:00.000000000 Z
12
+ date: 2019-06-26 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler