jsonapi-resources 0.10.0.beta5 → 0.10.0.beta6

Sign up to get free protection for your applications and to get access to all the features.
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