jsonapi-resources 0.9.8 → 0.9.9

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: 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