jsonapi-resources 0.10.0.beta5 → 0.10.0.beta6

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: 59bf56567199fc023e62eb521bfab9887346b0b1201a89915de98edb56bb2455
4
- data.tar.gz: 3321b686ec96c9828e68595ced3a9e82600605cc0ce24d36eb70905141feaa74
3
+ metadata.gz: 0ac7e65d3c40e15413e9d5a7552a8f871a2313c9f9d41f4cd520ab62f8e3c7d8
4
+ data.tar.gz: 7ba4da6469f82a35eee2df2e3475d6fb28f6b229bc78863f57c5c1576cf9973e
5
5
  SHA512:
6
- metadata.gz: 53a6d23df106df92c12c4c7df292c330ed285550d2c61a88eec3e078884a7d565522e84d70464afa1084c741b472c4eda5de308227a0138b89c11bb4479a304f
7
- data.tar.gz: 8a4fc235a30de87a892010b822a3d9f6e6b27b5fcb9da453fafafc700b99edd3dca128e2965532c40eba6493befba33ca73730bc374ae0502c1242b378a292bf
6
+ metadata.gz: a2ee268639e83ea3f490258f85ba8fe13785f31fd85d3a5b46cc92d7c340f3ba95f3316ca1dfcf2f33a92f31e17c023aa093d39d53b39d3c48cf8c9af22cd7ad
7
+ data.tar.gz: 76a30f8af5d5569cf40f979f296dc90e95a83f5f57b178c977ba8067755e20d5ac5e46f588f3bed4dddaf587faf7d6081e04b7116657af4d65faa6f858472a0f
@@ -102,7 +102,8 @@ module JSONAPI
102
102
  base_url: base_url,
103
103
  key_formatter: key_formatter,
104
104
  route_formatter: route_formatter,
105
- serialization_options: serialization_options
105
+ serialization_options: serialization_options,
106
+ controller: self
106
107
  )
107
108
  op.options[:cache_serializer_output] = !JSONAPI.configuration.resource_cache.nil?
108
109
 
@@ -365,7 +365,7 @@ module JSONAPI
365
365
 
366
366
  @reload_needed = true
367
367
  else
368
- @model.public_send(relationship.relation_name(context: @context)).destroy(key)
368
+ @model.public_send(relationship.relation_name(context: @context)).delete(key)
369
369
  end
370
370
 
371
371
  :completed
@@ -448,6 +448,9 @@ module JSONAPI
448
448
  end
449
449
 
450
450
  check_reserved_resource_name(subclass._type, subclass.name)
451
+
452
+ subclass._routed = false
453
+ subclass._warned_missing_route = false
451
454
  end
452
455
 
453
456
  def rebuild_relationships(relationships)
@@ -494,7 +497,7 @@ module JSONAPI
494
497
  end
495
498
  end
496
499
 
497
- attr_accessor :_attributes, :_relationships, :_type, :_model_hints
500
+ attr_accessor :_attributes, :_relationships, :_type, :_model_hints, :_routed, :_warned_missing_route
498
501
  attr_writer :_allowed_filters, :_paginator, :_allowed_sort
499
502
 
500
503
  def create(context)
@@ -960,21 +963,25 @@ module JSONAPI
960
963
  !@immutable
961
964
  end
962
965
 
963
- def exclude_links(exclude)
966
+ def parse_exclude_links(exclude)
964
967
  case exclude
965
968
  when :default, "default"
966
- @_exclude_links = [:self]
969
+ [:self]
967
970
  when :none, "none"
968
- @_exclude_links = []
971
+ []
969
972
  when Array
970
- @_exclude_links = exclude.collect {|link| link.to_sym}
973
+ exclude.collect {|link| link.to_sym}
971
974
  else
972
975
  fail "Invalid exclude_links"
973
976
  end
974
977
  end
975
978
 
979
+ def exclude_links(exclude)
980
+ @_exclude_links = parse_exclude_links(exclude)
981
+ end
982
+
976
983
  def _exclude_links
977
- @_exclude_links ||= []
984
+ @_exclude_links ||= parse_exclude_links(JSONAPI.configuration.default_exclude_links)
978
985
  end
979
986
 
980
987
  def exclude_link?(link)
@@ -37,7 +37,8 @@ module JSONAPI
37
37
  :default_caching,
38
38
  :default_resource_cache_field,
39
39
  :resource_cache_digest_function,
40
- :resource_cache_usage_report_function
40
+ :resource_cache_usage_report_function,
41
+ :default_exclude_links
41
42
 
42
43
  def initialize
43
44
  #:underscored_key, :camelized_key, :dasherized_key, or custom
@@ -149,6 +150,12 @@ module JSONAPI
149
150
  # Optionally provide a callable which JSONAPI will call with information about cache
150
151
  # performance. Should accept three arguments: resource name, hits count, misses count.
151
152
  self.resource_cache_usage_report_function = nil
153
+
154
+ # Global configuration for links exclusion
155
+ # Controls whether to generate links like `self`, `related` with all the resources
156
+ # and relationships. Accepts either `:default`, `:none`, or array containing the
157
+ # specific default links to exclude, which may be `:self` and `:related`.
158
+ self.default_exclude_links = :none
152
159
  end
153
160
 
154
161
  def cache_formatters=(bool)
@@ -276,6 +283,8 @@ module JSONAPI
276
283
  attr_writer :resource_cache_digest_function
277
284
 
278
285
  attr_writer :resource_cache_usage_report_function
286
+
287
+ attr_writer :default_exclude_links
279
288
  end
280
289
 
281
290
  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,105 +92,55 @@ module JSONAPI
81
92
  unless scopes.empty?
82
93
  "#{ scopes.first.to_s.camelize }::Engine".safe_constantize
83
94
  end
84
- # :nocov:
95
+
96
+ # :nocov:
85
97
  rescue LoadError => _e
86
98
  nil
87
- # :nocov:
99
+ # :nocov:
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
@@ -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,7 +29,10 @@ module JSONAPI
27
29
  @class_name = nil
28
30
  @inverse_relationship = nil
29
31
 
30
- exclude_links(options.fetch(:exclude_links, :none))
32
+ @_routed = false
33
+ @_warned_missing_route = false
34
+
35
+ exclude_links(options.fetch(:exclude_links, JSONAPI.configuration.default_exclude_links))
31
36
 
32
37
  # Custom methods are reserved for future use
33
38
  @custom_methods = options.fetch(:custom_methods, {})
@@ -10,6 +10,9 @@ module JSONAPI
10
10
  JSONAPI::ActsAsResourceController
11
11
  ].freeze
12
12
 
13
+ # Note, the url_helpers are not loaded. This will prevent links from being generated for resources, and warnings
14
+ # will be emitted. Link support can be added by including `Rails.application.routes.url_helpers`, and links
15
+ # can be disabled, and warning suppressed, for a resource with `exclude_links :default`
13
16
  MODULES.each do |mod|
14
17
  include mod
15
18
  end
@@ -381,6 +381,8 @@ module JSONAPI
381
381
  LinkBuilder.new(
382
382
  base_url: options.fetch(:base_url, ''),
383
383
  primary_resource_klass: primary_resource_klass,
384
+ route_formatter: options.fetch(:route_formatter, JSONAPI.configuration.route_formatter),
385
+ url_helpers: options.fetch(:url_helpers, options[:controller]),
384
386
  )
385
387
  end
386
388
  end
@@ -1,5 +1,5 @@
1
1
  module JSONAPI
2
2
  module Resources
3
- VERSION = '0.10.0.beta5'
3
+ VERSION = '0.10.0.beta6'
4
4
  end
5
5
  end
@@ -20,6 +20,8 @@ module ActionDispatch
20
20
  @resource_type = resources.first
21
21
  res = JSONAPI::Resource.resource_klass_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_klass_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
@@ -220,6 +224,8 @@ module ActionDispatch
220
224
  relationship_name = relationship.first
221
225
  relationship = source._relationships[relationship_name]
222
226
 
227
+ relationship._routed = true
228
+
223
229
  formatted_relationship_name = format_route(relationship.name)
224
230
 
225
231
  if relationship.polymorphic?
@@ -242,6 +248,8 @@ module ActionDispatch
242
248
  relationship_name = relationship.first
243
249
  relationship = source._relationships[relationship_name]
244
250
 
251
+ relationship._routed = true
252
+
245
253
  formatted_relationship_name = format_route(relationship.name)
246
254
  related_resource = JSONAPI::Resource.resource_klass_for(resource_type_with_module_prefix(relationship.class_name.underscore))
247
255
  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.10.0.beta5
4
+ version: 0.10.0.beta6
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-07-09 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler