json_api_client 0.9.6 → 1.0.0.beta

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