json_api_client 0.9.6 → 1.0.0.beta

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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +274 -84
  3. data/lib/json_api_client.rb +9 -4
  4. data/lib/json_api_client/connection.rb +5 -5
  5. data/lib/json_api_client/error_collector.rb +29 -0
  6. data/lib/json_api_client/errors.rb +7 -5
  7. data/lib/json_api_client/helpers.rb +3 -0
  8. data/lib/json_api_client/helpers/associable.rb +4 -3
  9. data/lib/json_api_client/helpers/attributable.rb +15 -46
  10. data/lib/json_api_client/helpers/custom_endpoints.rb +3 -10
  11. data/lib/json_api_client/helpers/dynamic_attributes.rb +61 -0
  12. data/lib/json_api_client/helpers/linkable.rb +28 -18
  13. data/lib/json_api_client/helpers/paginatable.rb +13 -0
  14. data/lib/json_api_client/helpers/parsable.rb +1 -1
  15. data/lib/json_api_client/helpers/queryable.rb +4 -3
  16. data/lib/json_api_client/helpers/requestable.rb +60 -0
  17. data/lib/json_api_client/linking.rb +7 -0
  18. data/lib/json_api_client/linking/included_data.rb +40 -0
  19. data/lib/json_api_client/linking/links.rb +29 -0
  20. data/lib/json_api_client/linking/top_level_links.rb +30 -0
  21. data/lib/json_api_client/meta_data.rb +10 -0
  22. data/lib/json_api_client/middleware/json_request.rb +3 -4
  23. data/lib/json_api_client/middleware/status.rb +10 -1
  24. data/lib/json_api_client/paginating.rb +5 -0
  25. data/lib/json_api_client/paginating/paginator.rb +80 -0
  26. data/lib/json_api_client/parsers.rb +5 -0
  27. data/lib/json_api_client/parsers/parser.rb +55 -0
  28. data/lib/json_api_client/query.rb +2 -7
  29. data/lib/json_api_client/query/builder.rb +126 -0
  30. data/lib/json_api_client/query/requestor.rb +77 -0
  31. data/lib/json_api_client/resource.rb +3 -59
  32. data/lib/json_api_client/result_set.rb +11 -29
  33. data/lib/json_api_client/schema.rb +15 -30
  34. data/lib/json_api_client/version.rb +1 -1
  35. metadata +36 -19
  36. data/lib/json_api_client/link.rb +0 -11
  37. data/lib/json_api_client/link_definition.rb +0 -27
  38. data/lib/json_api_client/linked_data.rb +0 -75
  39. data/lib/json_api_client/parser.rb +0 -63
  40. data/lib/json_api_client/query/base.rb +0 -38
  41. data/lib/json_api_client/query/create.rb +0 -17
  42. data/lib/json_api_client/query/custom.rb +0 -22
  43. data/lib/json_api_client/query/destroy.rb +0 -12
  44. data/lib/json_api_client/query/find.rb +0 -19
  45. data/lib/json_api_client/query/linked.rb +0 -24
  46. data/lib/json_api_client/query/update.rb +0 -13
  47. data/lib/json_api_client/scope.rb +0 -48
@@ -0,0 +1,77 @@
1
+ module JsonApiClient
2
+ module Query
3
+ class Requestor
4
+ extend Forwardable
5
+
6
+ def initialize(klass)
7
+ @klass = klass
8
+ end
9
+
10
+ # expects a record
11
+ def create(record)
12
+ request(:post, klass.path(record.attributes), {
13
+ data: record.attributes
14
+ })
15
+ end
16
+
17
+ def update(record)
18
+ request(:patch, resource_path(record.attributes), {
19
+ data: record.attributes
20
+ })
21
+ end
22
+
23
+ def find(args)
24
+ params = case args
25
+ when Hash
26
+ args
27
+ when Array
28
+ {klass.primary_key.to_s.pluralize.to_sym => args.join(",")}
29
+ else
30
+ {klass.primary_key => args}
31
+ end
32
+
33
+ path = resource_path(params)
34
+ params.delete(klass.primary_key)
35
+ request(:get, path, params)
36
+ end
37
+
38
+ def destroy(record)
39
+ request(:delete, resource_path(record.attributes), {})
40
+ end
41
+
42
+ def linked(path)
43
+ request(:get, path, {})
44
+ end
45
+
46
+ def custom(method_name, options, params)
47
+ path = resource_path(params)
48
+ params.delete(klass.primary_key)
49
+ path = File.join(path, method_name.to_s)
50
+
51
+ request(options.fetch(:request_method, :get), path, params)
52
+ end
53
+
54
+ protected
55
+
56
+ attr_reader :klass
57
+ def_delegators :klass, :connection
58
+
59
+ def resource_path(parameters)
60
+ if resource_id = parameters[klass.primary_key]
61
+ File.join(klass.path(parameters), encoded(resource_id))
62
+ else
63
+ klass.path(parameters)
64
+ end
65
+ end
66
+
67
+ def encoded(part)
68
+ Addressable::URI.encode_component(part, Addressable::URI::CharacterClasses::UNRESERVED)
69
+ end
70
+
71
+ def request(type, path, params)
72
+ klass.parse(connection.run(type, path, params))
73
+ end
74
+
75
+ end
76
+ end
77
+ end
@@ -8,11 +8,8 @@ require 'active_support/core_ext/enumerable'
8
8
 
9
9
  module JsonApiClient
10
10
  class Resource
11
- class_attribute :site, :primary_key, :link_style, :default_headers
12
-
11
+ class_attribute :site, :primary_key
13
12
  self.primary_key = :id
14
- self.link_style = :id # or :url
15
- self.default_headers = {}
16
13
 
17
14
  class << self
18
15
  # base URL for this resource
@@ -27,23 +24,6 @@ module JsonApiClient
27
24
  def resource_name
28
25
  name.demodulize.underscore
29
26
  end
30
-
31
- def find(conditions)
32
- run_request(Query::Find.new(self, conditions))
33
- end
34
-
35
- def create(conditions = {})
36
- result = run_request(Query::Create.new(self, conditions))
37
- if result.has_errors?
38
- yield(result) if block_given?
39
- return nil
40
- end
41
- result.first
42
- end
43
-
44
- def run_request(query)
45
- parse(connection.execute(query))
46
- end
47
27
  end
48
28
 
49
29
  include Helpers::Initializable
@@ -55,44 +35,8 @@ module JsonApiClient
55
35
  include Helpers::Linkable
56
36
  include Helpers::CustomEndpoints
57
37
  include Helpers::Schemable
58
-
59
- attr_reader :last_request_meta
60
-
61
- def save
62
- query = persisted? ?
63
- Query::Update.new(self.class, attributes) :
64
- Query::Create.new(self.class, attributes)
65
-
66
- run_request(query)
67
- end
68
-
69
- def destroy
70
- if run_request(Query::Destroy.new(self.class, attributes))
71
- self.attributes.clear
72
- true
73
- else
74
- false
75
- end
76
- end
77
-
78
- protected
79
-
80
- def run_request(query)
81
- # reset errors if a new request is being made
82
- self.errors.clear if self.errors
83
-
84
- result = self.class.run_request(query)
85
- self.errors = result.errors
86
- @last_request_meta = result.meta
87
- if result.has_errors?
88
- return false
89
- else
90
- if updated = result.first
91
- self.attributes = updated.attributes
92
- end
93
- return true
94
- end
95
- end
38
+ include Helpers::Paginatable
39
+ include Helpers::Requestable
96
40
 
97
41
  end
98
42
  end
@@ -1,40 +1,22 @@
1
+ require 'forwardable'
2
+
1
3
  module JsonApiClient
2
4
  class ResultSet < Array
5
+ extend Forwardable
3
6
 
4
- attr_accessor :total_pages,
5
- :total_entries,
6
- :offset,
7
- :per_page,
8
- :current_page,
9
- :errors,
7
+ attr_accessor :errors,
10
8
  :record_class,
11
- :meta
12
- alias_attribute :limit_value, :per_page
9
+ :meta,
10
+ :pages,
11
+ :uri,
12
+ :links
13
13
 
14
- def self.build(klass, data)
15
- # Objects representing an individual resource are
16
- # not necessarily wrapped in an Array; enforce wrapping
17
- result_data = [data.fetch(klass.table_name, [])].flatten
18
- new(result_data.map {|attributes| klass.new(attributes) }).tap do |result_set|
19
- result_set.record_class = klass
20
- yield(result_set) if block_given?
21
- end
22
- end
14
+ # pagination methods are handled by the paginator
15
+ def_delegators :pages, :total_pages, :total_entries, :offset, :per_page, :current_page, :limit_value, :next_page, :previous_page, :out_of_bounds?
23
16
 
24
17
  def has_errors?
25
- errors && errors.length > 0
26
- end
27
-
28
- def out_of_bounds?
29
- current_page > total_pages
18
+ errors.present?
30
19
  end
31
20
 
32
- def previous_page
33
- current_page > 1 ? (current_page - 1) : nil
34
- end
35
-
36
- def next_page
37
- current_page < total_pages ? (current_page + 1) : nil
38
- end
39
21
  end
40
22
  end
@@ -1,40 +1,25 @@
1
- require 'date'
2
-
3
1
  module JsonApiClient
4
2
  class Schema
5
- DEFAULT_PROPERTY_TYPES = {
6
- int: lambda {|value| value.to_i },
7
- integer: lambda {|value| value.to_i },
8
- string: lambda {|value| value.to_s },
9
- float: lambda {|value| value.to_f },
10
- boolean: lambda {|value| value.is_a?(String) ? (value != "false") : !!value },
11
- timestamp: lambda {|value| value.is_a?(DateTime) ? value : Time.at(value.to_f).to_datetime },
12
- timestamp_ms: lambda {|value| value.is_a?(DateTime) ? value : Time.at(value.to_f/1000).to_datetime },
13
- datetime: lambda {|value| value.is_a?(DateTime) ? value : DateTime.parse(value.to_s) },
14
- date: lambda {|value| value.is_a?(Date) ? value : Date.parse(value.to_s) }
15
- }
16
-
17
- class << self
18
- def property_types
19
- @property_types ||= DEFAULT_PROPERTY_TYPES
20
- end
21
-
22
- def register_property_type(name, caster)
23
- property_types[name.to_sym] = caster
24
- end
25
-
26
- def find_property_type(name)
27
- property_types[name.to_sym]
28
- end
29
- end
30
-
31
3
  Property = Struct.new(:name, :type, :default) do
32
4
  def cast(value)
33
5
  return nil if value.nil?
34
6
  return value if type.nil?
35
7
 
36
- if caster = Schema.find_property_type(type)
37
- caster.call(value)
8
+ case type.to_sym
9
+ when :int, :integer
10
+ value.to_i
11
+ when :string
12
+ value.to_s
13
+ when :float
14
+ value.to_f
15
+ when :time
16
+ value.is_a?(Time) || nil ? value : Time.parse(value)
17
+ when :boolean
18
+ if value.is_a?(String)
19
+ value == "false" ? false : true
20
+ else
21
+ !!value
22
+ end
38
23
  else
39
24
  value
40
25
  end
@@ -1,3 +1,3 @@
1
1
  module JsonApiClient
2
- VERSION = "0.9.6"
2
+ VERSION = "1.0.0.beta"
3
3
  end
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: 0.9.6
4
+ version: 1.0.0.beta
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-07-07 00:00:00.000000000 Z
11
+ date: 2015-05-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
19
+ version: 3.2.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '0'
26
+ version: 3.2.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: faraday
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: 0.8.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: faraday_middleware
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0.9'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0.9'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: addressable
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -96,37 +110,40 @@ files:
96
110
  - lib/json_api_client/associations/has_many.rb
97
111
  - lib/json_api_client/associations/has_one.rb
98
112
  - lib/json_api_client/connection.rb
113
+ - lib/json_api_client/error_collector.rb
99
114
  - lib/json_api_client/errors.rb
100
115
  - lib/json_api_client/helpers.rb
101
116
  - lib/json_api_client/helpers/associable.rb
102
117
  - lib/json_api_client/helpers/attributable.rb
103
118
  - lib/json_api_client/helpers/custom_endpoints.rb
119
+ - lib/json_api_client/helpers/dynamic_attributes.rb
104
120
  - lib/json_api_client/helpers/initializable.rb
105
121
  - lib/json_api_client/helpers/linkable.rb
122
+ - lib/json_api_client/helpers/paginatable.rb
106
123
  - lib/json_api_client/helpers/parsable.rb
107
124
  - lib/json_api_client/helpers/queryable.rb
125
+ - lib/json_api_client/helpers/requestable.rb
108
126
  - lib/json_api_client/helpers/schemable.rb
109
127
  - lib/json_api_client/helpers/serializable.rb
110
- - lib/json_api_client/link.rb
111
- - lib/json_api_client/link_definition.rb
112
- - lib/json_api_client/linked_data.rb
128
+ - lib/json_api_client/linking.rb
129
+ - lib/json_api_client/linking/included_data.rb
130
+ - lib/json_api_client/linking/links.rb
131
+ - lib/json_api_client/linking/top_level_links.rb
132
+ - lib/json_api_client/meta_data.rb
113
133
  - lib/json_api_client/middleware.rb
114
134
  - lib/json_api_client/middleware/json_request.rb
115
135
  - lib/json_api_client/middleware/parse_json.rb
116
136
  - lib/json_api_client/middleware/status.rb
117
- - lib/json_api_client/parser.rb
137
+ - lib/json_api_client/paginating.rb
138
+ - lib/json_api_client/paginating/paginator.rb
139
+ - lib/json_api_client/parsers.rb
140
+ - lib/json_api_client/parsers/parser.rb
118
141
  - lib/json_api_client/query.rb
119
- - lib/json_api_client/query/base.rb
120
- - lib/json_api_client/query/create.rb
121
- - lib/json_api_client/query/custom.rb
122
- - lib/json_api_client/query/destroy.rb
123
- - lib/json_api_client/query/find.rb
124
- - lib/json_api_client/query/linked.rb
125
- - lib/json_api_client/query/update.rb
142
+ - lib/json_api_client/query/builder.rb
143
+ - lib/json_api_client/query/requestor.rb
126
144
  - lib/json_api_client/resource.rb
127
145
  - lib/json_api_client/result_set.rb
128
146
  - lib/json_api_client/schema.rb
129
- - lib/json_api_client/scope.rb
130
147
  - lib/json_api_client/utils.rb
131
148
  - lib/json_api_client/version.rb
132
149
  homepage: http://github.com/chingor13/json_api_client
@@ -144,12 +161,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
144
161
  version: '0'
145
162
  required_rubygems_version: !ruby/object:Gem::Requirement
146
163
  requirements:
147
- - - ">="
164
+ - - ">"
148
165
  - !ruby/object:Gem::Version
149
- version: '0'
166
+ version: 1.3.1
150
167
  requirements: []
151
168
  rubyforge_project:
152
- rubygems_version: 2.4.8
169
+ rubygems_version: 2.2.2
153
170
  signing_key:
154
171
  specification_version: 4
155
172
  summary: Build client libraries compliant with specification defined by jsonapi.org
@@ -1,11 +0,0 @@
1
- module JsonApiClient
2
- class Link
3
- attr_accessor :type
4
-
5
- def initialize(type, spec)
6
- @type = type
7
- @spec = spec
8
- end
9
-
10
- end
11
- end
@@ -1,27 +0,0 @@
1
- module JsonApiClient
2
- class LinkDefinition
3
-
4
- def initialize(spec)
5
- @spec = {}.with_indifferent_access
6
- spec.each do |type, definition|
7
- @spec[type.split(".").last] = definition.merge({slurp: type})
8
- end
9
- end
10
-
11
- def has_link?(type)
12
- @spec.has_key?(type)
13
- end
14
-
15
- def attribute_name_for(type)
16
- @spec.fetch(type).fetch("type")
17
- end
18
-
19
- def url_for(type, ids)
20
- definition = @spec.fetch(type)
21
- href = definition.fetch("href")
22
- slurp = definition.fetch("slurp")
23
- href.gsub("{#{slurp}}", Array(ids).join(","))
24
- end
25
-
26
- end
27
- end
@@ -1,75 +0,0 @@
1
- # This object holds the preloaded data from the json response - essentially
2
- # the preloaded foreign keys
3
- module JsonApiClient
4
- class LinkedData
5
- attr_reader :link_definition,
6
- :record_class
7
-
8
- extend Forwardable
9
- def_delegators :link_definition, :has_link?
10
-
11
- def initialize(data, link_definition, record_class)
12
- @link_definition = link_definition
13
- @record_class = record_class
14
- @results_by_type_by_id = {}
15
-
16
- data.each do |type, results|
17
- klass = klass_for(type)
18
- add_data(type, results.map{|result| klass.new(result)})
19
- end
20
-
21
- @results_by_type_by_id.values.each do |results|
22
- results.values.each do |result|
23
- result.linked_data = self
24
- end
25
- end
26
- end
27
-
28
- def data_for(type, ids)
29
- ids = Array(ids)
30
-
31
- # the name of the linked data is provided by the link definition from the result
32
- attr_name = link_definition.attribute_name_for(type)
33
-
34
- # get any preloaded data from the result
35
- type_data = @results_by_type_by_id.fetch(attr_name, {})
36
-
37
- # find the associated class for the data
38
- klass = klass_for(type)
39
-
40
- # return all the found records
41
- found, missing = ids.partition { |id| type_data[id].present? }
42
-
43
- # make another api request if there are missing records
44
- fetch_data(klass, type, missing) if missing.present?
45
-
46
- # reload data
47
- type_data = @results_by_type_by_id.fetch(attr_name, {})
48
-
49
- ids.map do |id|
50
- type_data[id]
51
- end
52
- end
53
-
54
- # make an api request to fetch the missing data
55
- def fetch_data(klass, type, missing_ids)
56
- uri = URI(link_definition.url_for(type, missing_ids)).to_s
57
-
58
- query = Query::Linked.new(uri)
59
- results = klass.run_request(query)
60
-
61
- key = link_definition.attribute_name_for(type).to_s
62
- add_data(key, results)
63
- end
64
-
65
- def add_data(key, data)
66
- @results_by_type_by_id[key] ||= {}
67
- @results_by_type_by_id[key].merge!(data.index_by{|datum| datum["id"]})
68
- end
69
-
70
- def klass_for(type)
71
- Utils.compute_type(record_class, type.to_s.pluralize.classify)
72
- end
73
-
74
- end
75
- end