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.
- checksums.yaml +4 -4
- data/README.md +274 -84
- data/lib/json_api_client.rb +9 -4
- data/lib/json_api_client/connection.rb +5 -5
- data/lib/json_api_client/error_collector.rb +29 -0
- data/lib/json_api_client/errors.rb +7 -5
- data/lib/json_api_client/helpers.rb +3 -0
- data/lib/json_api_client/helpers/associable.rb +4 -3
- data/lib/json_api_client/helpers/attributable.rb +15 -46
- data/lib/json_api_client/helpers/custom_endpoints.rb +3 -10
- data/lib/json_api_client/helpers/dynamic_attributes.rb +61 -0
- data/lib/json_api_client/helpers/linkable.rb +28 -18
- data/lib/json_api_client/helpers/paginatable.rb +13 -0
- data/lib/json_api_client/helpers/parsable.rb +1 -1
- data/lib/json_api_client/helpers/queryable.rb +4 -3
- data/lib/json_api_client/helpers/requestable.rb +60 -0
- data/lib/json_api_client/linking.rb +7 -0
- data/lib/json_api_client/linking/included_data.rb +40 -0
- data/lib/json_api_client/linking/links.rb +29 -0
- data/lib/json_api_client/linking/top_level_links.rb +30 -0
- data/lib/json_api_client/meta_data.rb +10 -0
- data/lib/json_api_client/middleware/json_request.rb +3 -4
- data/lib/json_api_client/middleware/status.rb +10 -1
- data/lib/json_api_client/paginating.rb +5 -0
- data/lib/json_api_client/paginating/paginator.rb +80 -0
- data/lib/json_api_client/parsers.rb +5 -0
- data/lib/json_api_client/parsers/parser.rb +55 -0
- data/lib/json_api_client/query.rb +2 -7
- data/lib/json_api_client/query/builder.rb +126 -0
- data/lib/json_api_client/query/requestor.rb +77 -0
- data/lib/json_api_client/resource.rb +3 -59
- data/lib/json_api_client/result_set.rb +11 -29
- data/lib/json_api_client/schema.rb +15 -30
- data/lib/json_api_client/version.rb +1 -1
- metadata +36 -19
- data/lib/json_api_client/link.rb +0 -11
- data/lib/json_api_client/link_definition.rb +0 -27
- data/lib/json_api_client/linked_data.rb +0 -75
- data/lib/json_api_client/parser.rb +0 -63
- data/lib/json_api_client/query/base.rb +0 -38
- data/lib/json_api_client/query/create.rb +0 -17
- data/lib/json_api_client/query/custom.rb +0 -22
- data/lib/json_api_client/query/destroy.rb +0 -12
- data/lib/json_api_client/query/find.rb +0 -19
- data/lib/json_api_client/query/linked.rb +0 -24
- data/lib/json_api_client/query/update.rb +0 -13
- 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
|
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
|
-
|
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 :
|
5
|
-
:total_entries,
|
6
|
-
:offset,
|
7
|
-
:per_page,
|
8
|
-
:current_page,
|
9
|
-
:errors,
|
7
|
+
attr_accessor :errors,
|
10
8
|
:record_class,
|
11
|
-
:meta
|
12
|
-
|
9
|
+
:meta,
|
10
|
+
:pages,
|
11
|
+
:uri,
|
12
|
+
:links
|
13
13
|
|
14
|
-
|
15
|
-
|
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
|
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
|
-
|
37
|
-
|
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
|
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.
|
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:
|
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:
|
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:
|
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/
|
111
|
-
- lib/json_api_client/
|
112
|
-
- lib/json_api_client/
|
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/
|
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/
|
120
|
-
- lib/json_api_client/query/
|
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:
|
166
|
+
version: 1.3.1
|
150
167
|
requirements: []
|
151
168
|
rubyforge_project:
|
152
|
-
rubygems_version: 2.
|
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
|
data/lib/json_api_client/link.rb
DELETED
@@ -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
|