json_api_client 1.1.1 → 1.2.0

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
  SHA1:
3
- metadata.gz: 97343e5f4ab100e19f26bc047874835f90d8e76a
4
- data.tar.gz: 6009f0ddb1f43af631e3b10a6e0bd9bd5cf7c9c7
3
+ metadata.gz: 48365ff770cd7617bd8d4ca4bdc47815b1f16096
4
+ data.tar.gz: 1691aa8373f863f00f9cf8020c810424aebd0fb2
5
5
  SHA512:
6
- metadata.gz: f35608521648cf8337507f466831598a4bb77e5254f60c18a0bbeeac854c8832eeb5b7d8faf63b356e953d1bb4f11958339c98e9d453e69dab21d50c3b0e211d
7
- data.tar.gz: e3a07f87d572b36eab928742f87688d38dc02bbb2f65e62ee8e30f71fc125a6c1c5715f32e17e62a6d0a5bc63ab7f4386d76ccbecdfb9ed6719ae5b670accc72
6
+ metadata.gz: 72606bea939fb0c0770fd85120dcd7fa09762b44abcb8fcb53f95c2f0e60420b7c3acebfc55e3fec6e586585f7dad97df62a5bc6fab50961205f35085a7461c0
7
+ data.tar.gz: f08837628c8717af1b6040f296d6609843b708d274da8d2197464445278eca13066f28d30a5e68da880b69080444f7b3f4be59b98dbf1f13ffb8a6f9081a9014
data/README.md CHANGED
@@ -27,7 +27,7 @@ module MyApi
27
27
  end
28
28
  ```
29
29
 
30
- By convention, we figure guess the resource route from the class name. In the above example, `Article`'s path is "http://example.com/articles" and `Person`'s path would be "http://example.com/people".
30
+ By convention, we guess the resource route from the class name. In the above example, `Article`'s path is "http://example.com/articles" and `Person`'s path would be "http://example.com/people".
31
31
 
32
32
  Some basic example usage:
33
33
 
@@ -204,7 +204,7 @@ authors = results.map(&:author)
204
204
 
205
205
  ```ruby
206
206
  # makes request to /articles?fields[articles]=title,body
207
- article = Article.select("title,body").first
207
+ article = Article.select("title", "body").first
208
208
 
209
209
  # should have fetched the requested fields
210
210
  article.title
@@ -25,6 +25,12 @@ module JsonApiClient
25
25
  end
26
26
  end
27
27
 
28
+ class Conflict < ServerError
29
+ def message
30
+ "Resource already exists"
31
+ end
32
+ end
33
+
28
34
  class NotFound < ServerError
29
35
  attr_reader :uri
30
36
  def initialize(uri)
@@ -47,4 +53,4 @@ module JsonApiClient
47
53
  end
48
54
 
49
55
  end
50
- end
56
+ end
@@ -0,0 +1,145 @@
1
+ # Taken form jsonapi_resources formatter
2
+
3
+ require 'active_support/inflector'
4
+
5
+ module JsonApiClient
6
+ class Formatter
7
+ class << self
8
+ def format(arg)
9
+ arg.to_s
10
+ end
11
+
12
+ def unformat(arg)
13
+ # We call to_s() here so that unformat consistently returns a string
14
+ # (instead of a symbol) regardless which Formatter subclass it is called on
15
+ arg.to_s
16
+ end
17
+
18
+ def formatter_for(format)
19
+ formatter_class_name = "JsonApiClient::#{format.to_s.camelize}Formatter"
20
+ formatter_class_name.safe_constantize
21
+ end
22
+ end
23
+ end
24
+
25
+ class KeyFormatter < Formatter
26
+ class << self
27
+ def format(key)
28
+ super
29
+ end
30
+
31
+ def format_keys(hash)
32
+ Hash[
33
+ hash.map do |key, value|
34
+ [format(key).to_sym, value]
35
+ end
36
+ ]
37
+ end
38
+
39
+ def unformat(formatted_key)
40
+ super
41
+ end
42
+ end
43
+ end
44
+
45
+ class RouteFormatter < Formatter
46
+ class << self
47
+ def format(route)
48
+ super
49
+ end
50
+
51
+ def unformat(formatted_route)
52
+ super
53
+ end
54
+ end
55
+ end
56
+
57
+ class ValueFormatter < Formatter
58
+ class << self
59
+ def format(raw_value)
60
+ super(raw_value)
61
+ end
62
+
63
+ def unformat(value)
64
+ super(value)
65
+ end
66
+
67
+ def value_formatter_for(type)
68
+ formatter_name = "#{type.to_s.camelize}Value"
69
+ formatter_for(formatter_name)
70
+ end
71
+ end
72
+ end
73
+
74
+ class UnderscoredKeyFormatter < KeyFormatter
75
+ end
76
+
77
+ class CamelizedKeyFormatter < KeyFormatter
78
+ class << self
79
+ def format(key)
80
+ super.camelize(:lower)
81
+ end
82
+
83
+ def unformat(formatted_key)
84
+ formatted_key.to_s.underscore
85
+ end
86
+ end
87
+ end
88
+
89
+ class DasherizedKeyFormatter < KeyFormatter
90
+ class << self
91
+ def format(key)
92
+ super.dasherize
93
+ end
94
+
95
+ def unformat(formatted_key)
96
+ formatted_key.to_s.underscore
97
+ end
98
+ end
99
+ end
100
+
101
+ class DefaultValueFormatter < ValueFormatter
102
+ class << self
103
+ def format(raw_value)
104
+ raw_value
105
+ end
106
+ end
107
+ end
108
+
109
+ class IdValueFormatter < ValueFormatter
110
+ class << self
111
+ def format(raw_value)
112
+ return if raw_value.nil?
113
+ raw_value.to_s
114
+ end
115
+ end
116
+ end
117
+
118
+ class UnderscoredRouteFormatter < RouteFormatter
119
+ end
120
+
121
+ class CamelizedRouteFormatter < RouteFormatter
122
+ class << self
123
+ def format(route)
124
+ super.camelize(:lower)
125
+ end
126
+
127
+ def unformat(formatted_route)
128
+ formatted_route.to_s.underscore
129
+ end
130
+ end
131
+ end
132
+
133
+ class DasherizedRouteFormatter < RouteFormatter
134
+ class << self
135
+ def format(route)
136
+ super.dasherize
137
+ end
138
+
139
+ def unformat(formatted_route)
140
+ formatted_route.to_s.underscore
141
+ end
142
+ end
143
+ end
144
+
145
+ end
@@ -38,7 +38,13 @@ module JsonApiClient
38
38
  protected
39
39
 
40
40
  def method_missing(method, *args, &block)
41
- if method.to_s =~ /^(.*)=$/
41
+ normalized_method = if key_formatter
42
+ key_formatter.unformat(method.to_s)
43
+ else
44
+ method.to_s
45
+ end
46
+
47
+ if normalized_method =~ /^(.*)=$/
42
48
  set_attribute($1, args.first)
43
49
  elsif has_attribute?(method)
44
50
  attributes[method]
@@ -55,6 +61,10 @@ module JsonApiClient
55
61
  attributes[name] = value
56
62
  end
57
63
 
64
+ def key_formatter
65
+ self.class.respond_to?(:key_formatter) && self.class.key_formatter
66
+ end
67
+
58
68
  end
59
69
  end
60
70
  end
@@ -6,7 +6,7 @@ module JsonApiClient
6
6
  record_class = result_set.record_class
7
7
  grouped_data = data.group_by{|datum| datum["type"]}
8
8
  @data = grouped_data.inject({}) do |h, (type, records)|
9
- klass = Utils.compute_type(record_class, type.singularize.classify)
9
+ klass = Utils.compute_type(record_class, record_class.key_formatter.unformat(type).singularize.classify)
10
10
  h[type] = records.map do |datum|
11
11
  params = klass.parser.parameters_from_resource(datum)
12
12
  resource = klass.load(params)
@@ -2,9 +2,18 @@ module JsonApiClient
2
2
  class MetaData
3
3
  include Helpers::DynamicAttributes
4
4
 
5
- def initialize(data)
5
+ attr_accessor :record_class
6
+
7
+ def initialize(data, record_class = nil)
8
+ self.record_class = record_class
6
9
  self.attributes = data
7
10
  end
8
11
 
12
+ protected
13
+
14
+ def key_formatter
15
+ record_class && record_class.key_formatter
16
+ end
17
+
9
18
  end
10
- end
19
+ end
@@ -26,6 +26,8 @@ module JsonApiClient
26
26
  raise Errors::AccessDenied, env
27
27
  when 404
28
28
  raise Errors::NotFound, env[:url]
29
+ when 409
30
+ raise Errors::Conflict, env
29
31
  when 400..499
30
32
  # some other error
31
33
  when 500..599
@@ -36,4 +38,4 @@ module JsonApiClient
36
38
  end
37
39
  end
38
40
  end
39
- end
41
+ end
@@ -40,7 +40,7 @@ module JsonApiClient
40
40
  def total_entries
41
41
  per_page * total_pages
42
42
  end
43
- alias_method :total_count, :total_entries
43
+ def total_count; total_entries; end
44
44
 
45
45
  def offset
46
46
  per_page * (current_page - 1)
@@ -65,7 +65,7 @@ module JsonApiClient
65
65
 
66
66
  # we will treat everything as an Array
67
67
  results = [results] unless results.is_a?(Array)
68
- resources = results.map do |res|
68
+ resources = results.compact.map do |res|
69
69
  resource = result_set.record_class.load(parameters_from_resource(res))
70
70
  resource.last_result_set = result_set
71
71
  resource
@@ -78,7 +78,7 @@ module JsonApiClient
78
78
  end
79
79
 
80
80
  def handle_meta(result_set, data)
81
- result_set.meta = MetaData.new(data.fetch("meta", {}))
81
+ result_set.meta = MetaData.new(data.fetch("meta", {}), result_set.record_class)
82
82
  end
83
83
 
84
84
  def handle_links(result_set, data)
@@ -3,6 +3,7 @@ module JsonApiClient
3
3
  class Builder
4
4
 
5
5
  attr_reader :klass, :path_params
6
+ delegate :key_formatter, to: :klass
6
7
 
7
8
  def initialize(klass)
8
9
  @klass = klass
@@ -33,8 +34,12 @@ module JsonApiClient
33
34
  self
34
35
  end
35
36
 
36
- def select(fields)
37
- @fields += fields.split(",").map(&:strip)
37
+ def select(*fields)
38
+ fields = Array(fields).flatten
39
+ fields = fields.map { |i| i.to_s.split(",") }.flatten
40
+
41
+ @fields += fields.map(&:strip)
42
+
38
43
  self
39
44
  end
40
45
 
@@ -151,7 +156,7 @@ module JsonApiClient
151
156
  parse_related_links(*v)
152
157
  end
153
158
  else
154
- table
159
+ key_formatter.format(table)
155
160
  end
156
161
  end.flatten
157
162
  end
@@ -29,6 +29,8 @@ module JsonApiClient
29
29
  :read_only_attributes,
30
30
  :requestor_class,
31
31
  :associations,
32
+ :json_key_format,
33
+ :route_format,
32
34
  instance_accessor: false
33
35
  self.primary_key = :id
34
36
  self.parser = Parsers::Parser
@@ -42,6 +44,12 @@ module JsonApiClient
42
44
  self.requestor_class = Query::Requestor
43
45
  self.associations = []
44
46
 
47
+ #:underscored_key, :camelized_key, :dasherized_key, or custom
48
+ self.json_key_format = :underscored_key
49
+
50
+ #:underscored_route, :camelized_route, :dasherized_route, or custom
51
+ self.route_format = :underscored_route
52
+
45
53
  include Associations::BelongsTo
46
54
  include Associations::HasMany
47
55
  include Associations::HasOne
@@ -54,7 +62,7 @@ module JsonApiClient
54
62
  #
55
63
  # @return [String] The table name for this resource
56
64
  def table_name
57
- resource_name.pluralize
65
+ route_formatter.format(resource_name.pluralize)
58
66
  end
59
67
 
60
68
  # The name of a single resource. i.e. Article -> article, Person -> person
@@ -64,6 +72,22 @@ module JsonApiClient
64
72
  name.demodulize.underscore
65
73
  end
66
74
 
75
+ # Specifies the JSON API resource type. By default this is inferred
76
+ # from the resource class name.
77
+ #
78
+ # @return [String] Resource path
79
+ def type
80
+ table_name
81
+ end
82
+
83
+ # Specifies the relative path that should be used for this resource;
84
+ # by default, this is inferred from the resource class name.
85
+ #
86
+ # @return [String] Resource path
87
+ def resource_path
88
+ table_name
89
+ end
90
+
67
91
  # Load a resource object from attributes and consider it persisted
68
92
  #
69
93
  # @return [Resource] Persisted resource object
@@ -92,14 +116,14 @@ module JsonApiClient
92
116
 
93
117
  # Return the path or path pattern for this resource
94
118
  def path(params = nil)
95
- parts = [table_name]
96
- if params
119
+ parts = [resource_path]
120
+ if params && _prefix_path.present?
97
121
  path_params = params.delete(:path) || params
98
122
  parts.unshift(_prefix_path % path_params.symbolize_keys)
99
123
  else
100
124
  parts.unshift(_prefix_path)
101
125
  end
102
- parts.reject!{|part| part == "" }
126
+ parts.reject!(&:blank?)
103
127
  File.join(*parts)
104
128
  rescue KeyError
105
129
  raise ArgumentError, "Not all prefix parameters specified"
@@ -147,7 +171,7 @@ module JsonApiClient
147
171
  #
148
172
  # @return [Hash] Default attributes
149
173
  def default_attributes
150
- {type: table_name}
174
+ {type: type}
151
175
  end
152
176
 
153
177
  # Returns the schema for this resource class
@@ -157,6 +181,14 @@ module JsonApiClient
157
181
  @schema ||= Schema.new
158
182
  end
159
183
 
184
+ def key_formatter
185
+ JsonApiClient::Formatter.formatter_for(json_key_format)
186
+ end
187
+
188
+ def route_formatter
189
+ JsonApiClient::Formatter.formatter_for(route_format)
190
+ end
191
+
160
192
  protected
161
193
 
162
194
  # Declares a new class/instance method that acts on the collection/member
@@ -258,6 +290,7 @@ module JsonApiClient
258
290
  #
259
291
  # @param params [Hash] Attributes, links, and relationships
260
292
  def initialize(params = {})
293
+ @persisted = nil
261
294
  self.links = self.class.linker.new(params.delete("links") || {})
262
295
  self.relationships = self.class.relationship_linker.new(params.delete("relationships") || {})
263
296
  self.class.associations.each do |association|
@@ -321,9 +354,9 @@ module JsonApiClient
321
354
  def as_json_api(*)
322
355
  attributes.slice(:id, :type).tap do |h|
323
356
  relationships_for_serialization.tap do |r|
324
- h[:relationships] = r unless r.empty?
357
+ h[:relationships] = self.class.key_formatter.format_keys(r) unless r.empty?
325
358
  end
326
- h[:attributes] = attributes_for_serialization
359
+ h[:attributes] = self.class.key_formatter.format_keys(attributes_for_serialization)
327
360
  end
328
361
  end
329
362
 
@@ -444,7 +477,9 @@ module JsonApiClient
444
477
  end
445
478
 
446
479
  def association_for(name)
447
- self.class.associations.detect{|association| association.attr_name == name}
480
+ self.class.associations.detect do |association|
481
+ association.attr_name.to_s == self.class.key_formatter.unformat(name)
482
+ end
448
483
  end
449
484
 
450
485
  def attributes_for_serialization
@@ -1,3 +1,3 @@
1
1
  module JsonApiClient
2
- VERSION = "1.1.1"
2
+ VERSION = "1.2.0"
3
3
  end
@@ -1,7 +1,7 @@
1
1
  require 'faraday'
2
2
  require 'faraday_middleware'
3
3
  require 'json'
4
- require "addressable/uri"
4
+ require 'addressable/uri'
5
5
 
6
6
  module JsonApiClient
7
7
  autoload :Associations, 'json_api_client/associations'
@@ -9,6 +9,7 @@ module JsonApiClient
9
9
  autoload :Connection, 'json_api_client/connection'
10
10
  autoload :Errors, 'json_api_client/errors'
11
11
  autoload :ErrorCollector, 'json_api_client/error_collector'
12
+ autoload :Formatter, 'json_api_client/formatter'
12
13
  autoload :Helpers, 'json_api_client/helpers'
13
14
  autoload :Implementation, 'json_api_client/implementation'
14
15
  autoload :IncludedData, 'json_api_client/included_data'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: json_api_client
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeff Ching
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-02-25 00:00:00.000000000 Z
11
+ date: 2016-07-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -126,6 +126,7 @@ files:
126
126
  - lib/json_api_client/connection.rb
127
127
  - lib/json_api_client/error_collector.rb
128
128
  - lib/json_api_client/errors.rb
129
+ - lib/json_api_client/formatter.rb
129
130
  - lib/json_api_client/helpers.rb
130
131
  - lib/json_api_client/helpers/callbacks.rb
131
132
  - lib/json_api_client/helpers/dirty.rb
@@ -175,9 +176,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
175
176
  version: '0'
176
177
  requirements: []
177
178
  rubyforge_project:
178
- rubygems_version: 2.4.8
179
+ rubygems_version: 2.4.5
179
180
  signing_key:
180
181
  specification_version: 4
181
182
  summary: Build client libraries compliant with specification defined by jsonapi.org
182
183
  test_files: []
183
- has_rdoc: