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