api-client 2.4.0 → 2.5.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +2 -1
- data/Gemfile +7 -1
- data/README.md +35 -2
- data/api-client.gemspec +1 -1
- data/lib/api-client.rb +19 -0
- data/lib/api-client/base.rb +15 -6
- data/lib/api-client/class_methods.rb +25 -25
- data/lib/api-client/collection.rb +26 -13
- data/lib/api-client/configuration.rb +4 -3
- data/lib/api-client/dispatcher.rb +19 -7
- data/lib/api-client/dispatcher/net-http.rb +1 -1
- data/lib/api-client/dispatcher/parallel.rb +72 -0
- data/lib/api-client/dispatcher/typhoeus.rb +1 -3
- data/lib/api-client/exceptions.rb +1 -0
- data/lib/api-client/exceptions/not_possible.rb +9 -0
- data/lib/api-client/instance_methods.rb +26 -28
- data/lib/api-client/version.rb +1 -1
- data/spec/api-client/base_spec.rb +14 -4
- data/spec/api-client/class_methods_spec.rb +0 -22
- data/spec/api-client/collection_spec.rb +40 -10
- data/spec/api-client/dispatcher_spec.rb +1 -1
- data/spec/api-client/instance_methods_spec.rb +0 -22
- data/spec/api-client_spec.rb +69 -0
- metadata +11 -12
- data/spec/api-client.rb +0 -17
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -45,12 +45,16 @@ Create an initializer:
|
|
45
45
|
|
46
46
|
```ruby
|
47
47
|
ApiClient.configure do |config|
|
48
|
-
#
|
48
|
+
# You can define an api entry point
|
49
49
|
config.path = 'http://api.example.com'
|
50
|
+
# or several ones
|
51
|
+
config.paths = { :default => 'http://api.example.com', :auth => 'http://auth.example.com' }
|
50
52
|
# Default header
|
51
53
|
config.header = { 'param1' => '123329845729384759237592348712876817234'}
|
52
54
|
# Basic Auth
|
53
55
|
config.basic_auth('user', 'pass')
|
56
|
+
# If inside Rails
|
57
|
+
config.mock = Rails.env.test?
|
54
58
|
end
|
55
59
|
```
|
56
60
|
|
@@ -80,6 +84,8 @@ Then, on your action, just put into it:
|
|
80
84
|
|
81
85
|
```ruby
|
82
86
|
@user = User.get(3)
|
87
|
+
#or
|
88
|
+
@user = User.find(3)
|
83
89
|
```
|
84
90
|
|
85
91
|
where 3 is the id of the user.
|
@@ -88,6 +94,8 @@ To a request that returns a collection of the object, use:
|
|
88
94
|
|
89
95
|
```ruby
|
90
96
|
@user = User.collection
|
97
|
+
#or
|
98
|
+
@user = User.all
|
91
99
|
```
|
92
100
|
|
93
101
|
## Advanced Usage
|
@@ -117,6 +125,15 @@ class Person < ApiClient::Base
|
|
117
125
|
end
|
118
126
|
```
|
119
127
|
|
128
|
+
In case you need to work with one api entry point, it will define you path as the default.
|
129
|
+
If you need multiple entry points, you must define a name to each one, so you can refer to them on the model as:
|
130
|
+
|
131
|
+
```ruby
|
132
|
+
class User < ApiClient::Base
|
133
|
+
self.path = :auth
|
134
|
+
end
|
135
|
+
```
|
136
|
+
|
120
137
|
This code will create a setter and a getter for houses and cars and initialize the respective class.
|
121
138
|
|
122
139
|
Since version 2.0.0, it is possible to make api calls from the object. The syntax is pretty much the same.
|
@@ -134,11 +151,27 @@ end
|
|
134
151
|
|
135
152
|
With this setting no requisitions will be made. All calls will just return a new object with the attributes received.
|
136
153
|
|
154
|
+
## Parallel
|
155
|
+
|
156
|
+
When making parallel requests, the requests are made in threads, so to get the response it is necessary to specify
|
157
|
+
an initialized object to update when the requisition is complete.
|
158
|
+
|
159
|
+
```ruby
|
160
|
+
@users = ApiClient::Collection.new({}, User)
|
161
|
+
@cars = ApiClient::Collection.new({}, Car)
|
162
|
+
@house = House.new
|
163
|
+
|
164
|
+
ApiClient.parallel do
|
165
|
+
User.all.on_complete_update(@users)
|
166
|
+
Car.all.on_complete_update(@cars)
|
167
|
+
House.find(1).on_complete_update(@house)
|
168
|
+
end
|
169
|
+
```
|
170
|
+
|
137
171
|
## More Examples
|
138
172
|
[Project](https://github.com/zertico/api-client/tree/master/examples)
|
139
173
|
|
140
174
|
## TODO
|
141
|
-
* Add support for parallel requests
|
142
175
|
* Add more Response Handlers
|
143
176
|
|
144
177
|
## Mantainers
|
data/api-client.gemspec
CHANGED
@@ -6,7 +6,7 @@ Gem::Specification.new do |gem|
|
|
6
6
|
gem.version = ApiClient::VERSION
|
7
7
|
gem.authors = %q{Paulo Henrique Lopes Ribeiro}
|
8
8
|
gem.email = %q{plribeiro3000@gmail.com}
|
9
|
-
gem.summary = %q{
|
9
|
+
gem.summary = %q{Api client easy to play with parallelism support!}
|
10
10
|
|
11
11
|
gem.files = `git ls-files`.split("\n")
|
12
12
|
gem.test_files = `git ls-files -- {test,spec,features,examples,gemfiles}/*`.split("\n")
|
data/lib/api-client.rb
CHANGED
@@ -16,19 +16,38 @@ module ApiClient
|
|
16
16
|
# ApiClient.configure do |config|
|
17
17
|
# config.path = "api.example.com"
|
18
18
|
# end
|
19
|
+
# @yield Yield the configuration object
|
20
|
+
# @yieldparam block The Configuration object
|
21
|
+
# @yieldreturn [ApiClient::Configuration] The Configuration object
|
19
22
|
def self.configure(&block)
|
20
23
|
yield @config ||= ApiClient::Configuration.new
|
21
24
|
end
|
22
25
|
|
23
26
|
# Global settings for ApiClient
|
27
|
+
#
|
28
|
+
# @return [Hash] configuration attributes
|
24
29
|
def self.config
|
25
30
|
@config
|
26
31
|
end
|
27
32
|
|
33
|
+
# Parallel api requisitions
|
34
|
+
#
|
35
|
+
# @yield The requisitions to be made
|
36
|
+
# @yieldparam block A block with requisition objects
|
37
|
+
# @return [False] the value of the hydra config
|
38
|
+
def self.parallel(&block)
|
39
|
+
raise Exceptions::NotPossible unless defined?(::Typhoeus)
|
40
|
+
config.hydra = ::Typhoeus::Hydra.new
|
41
|
+
yield
|
42
|
+
config.hydra.run
|
43
|
+
config.hydra = false
|
44
|
+
end
|
45
|
+
|
28
46
|
# Default Settings
|
29
47
|
configure do |config|
|
30
48
|
config.path = ''
|
31
49
|
config.header = {}
|
32
50
|
config.mock = false
|
51
|
+
config.hydra = false
|
33
52
|
end
|
34
53
|
end
|
data/lib/api-client/base.rb
CHANGED
@@ -42,7 +42,6 @@ module ApiClient
|
|
42
42
|
false
|
43
43
|
end
|
44
44
|
|
45
|
-
|
46
45
|
# Return the api name to be used by this model.
|
47
46
|
#
|
48
47
|
# @return [False] return the default api name.
|
@@ -74,7 +73,7 @@ module ApiClient
|
|
74
73
|
|
75
74
|
# Set the resource path of the object on the api.
|
76
75
|
#
|
77
|
-
# @param [String]
|
76
|
+
# @param [String] resource_path path string.
|
78
77
|
def self.resource_path=(resource_path)
|
79
78
|
resource_path = resource_path[1, resource_path.size - 1] if resource_path[0, 1] == '/'
|
80
79
|
@resource_path = resource_path
|
@@ -96,7 +95,7 @@ module ApiClient
|
|
96
95
|
|
97
96
|
# Set methods to initialize associated objects.
|
98
97
|
#
|
99
|
-
# @param [Hash]
|
98
|
+
# @param [Hash] associations classes.
|
100
99
|
def self.associations=(associations = {})
|
101
100
|
associations.each do |association, class_name|
|
102
101
|
class_eval <<-EVAL
|
@@ -117,7 +116,7 @@ module ApiClient
|
|
117
116
|
|
118
117
|
# Overwrite #attr_acessor method to save instance_variable names.
|
119
118
|
#
|
120
|
-
# @param [Array] instance variables.
|
119
|
+
# @param [Array] vars instance variables.
|
121
120
|
def self.attr_accessor(*vars)
|
122
121
|
@attributes ||= []
|
123
122
|
@attributes.concat(vars)
|
@@ -138,6 +137,15 @@ module ApiClient
|
|
138
137
|
self.class.instance_variable_get('@attributes').inject({}) { |hash, attribute| hash.merge(attribute.to_sym => self.send("#{attribute}")) }
|
139
138
|
end
|
140
139
|
|
140
|
+
# Update instance values based on a hash
|
141
|
+
#
|
142
|
+
# @param attr New attributes
|
143
|
+
def attributes=(attr = {})
|
144
|
+
remove_root(attr).each do |key, value|
|
145
|
+
send("#{key}=", value)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
141
149
|
# Return a hash with a root node and all instance variables setted through attr_accessor and its currently values.
|
142
150
|
#
|
143
151
|
# @return [Hash] instance variables and its values.
|
@@ -148,10 +156,11 @@ module ApiClient
|
|
148
156
|
# Initialize a collection of objects. The collection will be an ApiClient::Collection object.
|
149
157
|
# The objects in the collection will be all instances of this (ApiClient::Base) class.
|
150
158
|
#
|
151
|
-
# @param [String] url to get the collection.
|
152
159
|
# @return [Collection] a collection of objects.
|
153
160
|
def self.collection
|
154
|
-
ApiClient
|
161
|
+
url = "#{ApiClient.config.path[path]}#{resource_path}"
|
162
|
+
attributes = ApiClient::Parser.response(ApiClient::Dispatcher.get(url), url)
|
163
|
+
ApiClient::Collection.new(attributes, self)
|
155
164
|
end
|
156
165
|
|
157
166
|
class << self
|
@@ -1,27 +1,16 @@
|
|
1
1
|
module ApiClient
|
2
2
|
# This module handles the logic to make an api call and initialize an object with the response.
|
3
3
|
module ClassMethods
|
4
|
-
# Initialize an object based on a hash of attributes.
|
5
|
-
#
|
6
|
-
# @param [Hash] attributes hash of attributes.
|
7
|
-
# @return [Base] the object initialized.
|
8
|
-
def build(attributes)
|
9
|
-
hash = remove_root(attributes)
|
10
|
-
hash = hash.merge({ 'response' => attributes })
|
11
|
-
new(hash)
|
12
|
-
end
|
13
|
-
|
14
4
|
# Make a get requisition and initialize an object with the response.
|
15
5
|
#
|
16
6
|
# @param [Integer] id id of the object.
|
17
7
|
# @param [Hash] header hash with the header options.
|
18
8
|
# @return [Base] the object initialized.
|
19
9
|
def get(id, header = {})
|
20
|
-
return
|
10
|
+
return new(:id => id) if ApiClient.config.mock
|
21
11
|
url = "#{ApiClient.config.path[path]}#{self.resource_path}/#{id}"
|
22
12
|
response = ApiClient::Dispatcher.get(url, header)
|
23
|
-
|
24
|
-
build(params)
|
13
|
+
build(response, url)
|
25
14
|
end
|
26
15
|
|
27
16
|
alias_method :find, :get
|
@@ -32,11 +21,10 @@ module ApiClient
|
|
32
21
|
# @param [Hash] header hash with the header options.
|
33
22
|
# @return [Base] the object initialized.
|
34
23
|
def post(attributes, header = {})
|
35
|
-
return
|
24
|
+
return new(attributes) if ApiClient.config.mock
|
36
25
|
url = "#{ApiClient.config.path[path]}#{self.resource_path}"
|
37
26
|
response = ApiClient::Dispatcher.post(url, attributes, header)
|
38
|
-
|
39
|
-
build(params)
|
27
|
+
build(response, url)
|
40
28
|
end
|
41
29
|
|
42
30
|
alias_method :create, :post
|
@@ -47,11 +35,10 @@ module ApiClient
|
|
47
35
|
# @param [Hash] header hash with the header options.
|
48
36
|
# @return [Base] the object initialized.
|
49
37
|
def put(attributes, header = {})
|
50
|
-
return
|
38
|
+
return new(attributes) if ApiClient.config.mock
|
51
39
|
url = "#{ApiClient.config.path[path]}#{self.resource_path}"
|
52
40
|
response = ApiClient::Dispatcher.put(url, attributes, header)
|
53
|
-
|
54
|
-
build(params)
|
41
|
+
build(response, url)
|
55
42
|
end
|
56
43
|
|
57
44
|
alias_method :update_attributes, :put
|
@@ -62,11 +49,10 @@ module ApiClient
|
|
62
49
|
# @param [Hash] header hash with the header options.
|
63
50
|
# @return [Base] the object initialized.
|
64
51
|
def patch(attributes, header = {})
|
65
|
-
return
|
52
|
+
return new(attributes) if ApiClient.config.mock
|
66
53
|
url = "#{ApiClient.config.path[path]}#{self.resource_path}"
|
67
54
|
response = ApiClient::Dispatcher.patch(url, attributes, header)
|
68
|
-
|
69
|
-
build(params)
|
55
|
+
build(response, url)
|
70
56
|
end
|
71
57
|
|
72
58
|
# Make a delete requisition and initialize an object with the response.
|
@@ -75,11 +61,10 @@ module ApiClient
|
|
75
61
|
# @param [Hash] header hash with the header options.
|
76
62
|
# @return [Base] the object initialized.
|
77
63
|
def delete(id, header = {})
|
78
|
-
return
|
64
|
+
return new(:id => id) if ApiClient.config.mock
|
79
65
|
url = "#{ApiClient.config.path[path]}#{self.resource_path}/#{id}"
|
80
66
|
response = ApiClient::Dispatcher.delete(url, header)
|
81
|
-
|
82
|
-
build(params)
|
67
|
+
build(response, url)
|
83
68
|
end
|
84
69
|
|
85
70
|
alias_method :destroy, :delete
|
@@ -93,5 +78,20 @@ module ApiClient
|
|
93
78
|
attributes = attributes[self.root_node.to_s] if attributes.key?(self.root_node.to_s)
|
94
79
|
attributes
|
95
80
|
end
|
81
|
+
|
82
|
+
protected
|
83
|
+
|
84
|
+
# Initialize an object based on a hash of attributes.
|
85
|
+
#
|
86
|
+
# @param [Response] response requisition response.
|
87
|
+
# @param [String] url the url of the requisition.
|
88
|
+
# @return [Base] the object initialized.
|
89
|
+
def build(response, url)
|
90
|
+
return response if ApiClient.config.hydra
|
91
|
+
attributes = ApiClient::Parser.response(response, url)
|
92
|
+
hash = remove_root(attributes)
|
93
|
+
hash = hash.merge({ 'response' => attributes })
|
94
|
+
new(hash)
|
95
|
+
end
|
96
96
|
end
|
97
97
|
end
|
@@ -1,20 +1,33 @@
|
|
1
1
|
# ApiClient::Collection handle a collection of objects
|
2
|
-
class ApiClient::Collection
|
3
|
-
|
4
|
-
|
5
|
-
attr_accessor :collection
|
6
|
-
|
7
|
-
# Initialize a collection of given objects
|
2
|
+
class ApiClient::Collection < Array
|
3
|
+
# Initialize a collection of objects based on attributes.
|
8
4
|
#
|
5
|
+
# @param [String] attributes the array of attributes.
|
9
6
|
# @param [Class] klass The class to instantiate the objects.
|
10
|
-
# @param [String] path The url of the api.
|
11
|
-
# @param [String] resource_path The url to get the data.
|
12
7
|
# @return [Collection] the collection of objects.
|
13
|
-
def initialize(
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
8
|
+
def initialize(attributes, klass)
|
9
|
+
@klass = klass
|
10
|
+
if attributes.instance_of?(Array)
|
11
|
+
attributes.each do |attr|
|
12
|
+
self << @klass.new(attr)
|
13
|
+
end
|
14
|
+
else
|
15
|
+
self << @klass.new(attributes)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Update the collection of objects based on the new attributes.
|
20
|
+
#
|
21
|
+
# @param [String] attributes the array of attributes.
|
22
|
+
# @return [Collection] the collection of objects.
|
23
|
+
def update(attributes)
|
24
|
+
self.clear
|
25
|
+
if attributes.instance_of?(Array)
|
26
|
+
attributes.each do |attr|
|
27
|
+
self << @klass.new(attr)
|
28
|
+
end
|
29
|
+
else
|
30
|
+
self << @klass.new(attributes)
|
18
31
|
end
|
19
32
|
end
|
20
33
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module ApiClient
|
2
2
|
# ApiClient::Configuration provides a way to configure ApiClient globally.
|
3
3
|
class Configuration
|
4
|
-
attr_accessor :mock
|
4
|
+
attr_accessor :mock, :hydra
|
5
5
|
attr_reader :header
|
6
6
|
|
7
7
|
# Return the api url.
|
@@ -24,7 +24,7 @@ module ApiClient
|
|
24
24
|
|
25
25
|
# Set several api urls.
|
26
26
|
#
|
27
|
-
# @param [Hash] hash with paths to api urls.
|
27
|
+
# @param [Hash] paths hash with paths to api urls.
|
28
28
|
def paths=(paths = {})
|
29
29
|
@paths = {}
|
30
30
|
paths.each do |name, path|
|
@@ -45,7 +45,8 @@ module ApiClient
|
|
45
45
|
|
46
46
|
# Set a basic authentication for all requisitions.
|
47
47
|
#
|
48
|
-
# @param [
|
48
|
+
# @param [String] account the user for requisitions.
|
49
|
+
# @param [String] password the password for requisitions.
|
49
50
|
def basic_auth(account, password)
|
50
51
|
@header.merge!({ 'Authorization' => "Basic #{["#{account}:#{password}"].pack('m').delete("\r\n")}" })
|
51
52
|
end
|
@@ -2,12 +2,20 @@
|
|
2
2
|
module ApiClient::Dispatcher
|
3
3
|
autoload :Typhoeus, 'api-client/dispatcher/typhoeus'
|
4
4
|
autoload :NetHttp, 'api-client/dispatcher/net-http'
|
5
|
+
autoload :Parallel, 'api-client/dispatcher/parallel'
|
5
6
|
|
7
|
+
# It passes the call to a more specific class to handle the dispatch logic based on the environment.
|
8
|
+
#
|
9
|
+
# @param [Symbol] method_name the name of the method.
|
10
|
+
# @param [Array] args array of params to be passed ahead.
|
6
11
|
def self.method_missing(method_name, *args)
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
12
|
+
case true
|
13
|
+
when ApiClient.config.hydra != false && defined?(::Typhoeus) != nil
|
14
|
+
return Parallel.send(method_name, *args) if Parallel.respond_to?(method_name)
|
15
|
+
when defined?(::Typhoeus)
|
16
|
+
return Typhoeus.send(method_name, *args) if Typhoeus.respond_to?(method_name)
|
17
|
+
else
|
18
|
+
return NetHttp.send(method_name, *args) if NetHttp.respond_to?(method_name)
|
11
19
|
end
|
12
20
|
super
|
13
21
|
end
|
@@ -18,10 +26,14 @@ module ApiClient::Dispatcher
|
|
18
26
|
# @param [Boolean] include_private if it does work to private methods as well.
|
19
27
|
# @return [Boolean] if it responds to the method or not.
|
20
28
|
def self.respond_to_missing?(method_name, include_private = false)
|
21
|
-
|
22
|
-
|
29
|
+
case true
|
30
|
+
when ApiClient.config.hydra && defined?(::Typhoeus)
|
31
|
+
return true if Parallel.respond_to?(method_name)
|
32
|
+
when defined?(::Typhoeus)
|
33
|
+
return true if Typhoeus.respond_to?(method_name)
|
34
|
+
else
|
35
|
+
return true if NetHttp.respond_to?(method_name)
|
23
36
|
end
|
24
|
-
return true if NetHttp.respond_to?(method_name)
|
25
37
|
super
|
26
38
|
end
|
27
39
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'net/http'
|
2
2
|
require 'api-client/net/http' unless Net::HTTP.new('').respond_to?(:patch)
|
3
3
|
|
4
|
-
# ApiClient::Dispatcher provides methods to make requests using the native ruby library 'net/http'
|
4
|
+
# ApiClient::Dispatcher::NetHttp provides methods to make requests using the native ruby library 'net/http'
|
5
5
|
module ApiClient::Dispatcher::NetHttp
|
6
6
|
# Make a get request and returns it.
|
7
7
|
#
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# ApiClient::Dispatcher provides methods to make requests using typhoeus
|
2
|
+
class ApiClient::Dispatcher::Parallel
|
3
|
+
# Initialize a new object and save the request in a instance variable.
|
4
|
+
#
|
5
|
+
# @param [Typhoeus::Request] requisition the requisition object.
|
6
|
+
def initialize(requisition)
|
7
|
+
@requisition = requisition
|
8
|
+
end
|
9
|
+
|
10
|
+
# When the requisition finish, this method update the given object with the response.
|
11
|
+
#
|
12
|
+
# @param [ApiClient::Base, ApiClient::Collection] variable the object to update with the response.
|
13
|
+
def on_complete_update(variable)
|
14
|
+
@requisition.on_complete do |response|
|
15
|
+
attributes = ApiClient::Parser.response(response, response.effective_url)
|
16
|
+
if variable.instance_of?(ApiClient::Collection)
|
17
|
+
variable.update(attributes)
|
18
|
+
else
|
19
|
+
variable.attributes = attributes
|
20
|
+
end
|
21
|
+
end
|
22
|
+
ApiClient.config.hydra.queue @requisition
|
23
|
+
end
|
24
|
+
|
25
|
+
# Make a get request and returns it.
|
26
|
+
#
|
27
|
+
# @param [String] url of the api request.
|
28
|
+
# @param [Hash] header attributes of the request.
|
29
|
+
# @return [Typhoeus::Request] the response object.
|
30
|
+
def self.get(url, header = {})
|
31
|
+
new(::Typhoeus::Request.new(url, :headers => ApiClient.config.header.merge(header)))
|
32
|
+
end
|
33
|
+
|
34
|
+
# Make a post request and returns it.
|
35
|
+
#
|
36
|
+
# @param [String] url of the api request.
|
37
|
+
# @param [Hash] args attributes of object.
|
38
|
+
# @param [Hash] header attributes of the request.
|
39
|
+
# @return [Typhoeus::Request] the response object.
|
40
|
+
def self.post(url, args, header = {})
|
41
|
+
new(::Typhoeus.Request.new(url, :method => :post, :body => args, :headers => ApiClient.config.header.merge(header)))
|
42
|
+
end
|
43
|
+
|
44
|
+
# Make a put request and returns it.
|
45
|
+
#
|
46
|
+
# @param [String] url of the api request.
|
47
|
+
# @param [Hash] args attributes of object.
|
48
|
+
# @param [Hash] header attributes of the request.
|
49
|
+
# @return [Typhoeus::Request] the response object.
|
50
|
+
def self.put(url, args, header = {})
|
51
|
+
new(::Typhoeus.Request.new(url, :method => :put, :body => args, :headers => ApiClient.config.header.merge(header)))
|
52
|
+
end
|
53
|
+
|
54
|
+
# Make a patch request and returns it.
|
55
|
+
#
|
56
|
+
# @param [String] url of the api request.
|
57
|
+
# @param [Hash] args attributes of object.
|
58
|
+
# @param [Hash] header attributes of the request.
|
59
|
+
# @return [Typhoeus::Request] the response object.
|
60
|
+
def self.patch(url, args, header = {})
|
61
|
+
new(::Typhoeus.Request.new(url, :method => :patch, :body => args, :headers => ApiClient.config.header.merge(header)))
|
62
|
+
end
|
63
|
+
|
64
|
+
# Make a delete request and returns it.
|
65
|
+
#
|
66
|
+
# @param [String] url of the api request.
|
67
|
+
# @param [Hash] header attributes of the request.
|
68
|
+
# @return [Typhoeus::Request] the response object.
|
69
|
+
def self.delete(url, header = {})
|
70
|
+
new(::Typhoeus.Request.new(url, :method => :delete, :headers => ApiClient.config.header.merge(header)))
|
71
|
+
end
|
72
|
+
end
|
@@ -7,6 +7,7 @@ module ApiClient::Exceptions
|
|
7
7
|
autoload :Generic, 'api-client/exceptions/generic'
|
8
8
|
autoload :InternalServerError, 'api-client/exceptions/internal_server_error'
|
9
9
|
autoload :NotFound, 'api-client/exceptions/not_found'
|
10
|
+
autoload :NotPossible, 'api-client/exceptions/not_possible'
|
10
11
|
autoload :ServiceUnavailable, 'api-client/exceptions/service_unavailable'
|
11
12
|
autoload :Unauthorized, 'api-client/exceptions/unauthorized'
|
12
13
|
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
# Exception raised when a parallel requisition can' be performed as Typhoeus must be defined on the Project
|
2
|
+
class ApiClient::Exceptions::NotPossible < ApiClient::Exceptions::Generic
|
3
|
+
# Initialize a new exception.
|
4
|
+
#
|
5
|
+
# @return [NotPossible] a new exception.
|
6
|
+
def self.initialize
|
7
|
+
super('Typhoeus must be defined to make parallel requests!')
|
8
|
+
end
|
9
|
+
end
|
@@ -1,29 +1,15 @@
|
|
1
1
|
module ApiClient
|
2
2
|
# This module handles the logic to make an api call and update_attributes the current object with the response.
|
3
3
|
module InstanceMethods
|
4
|
-
# Update an object based on a hash of attributes.
|
5
|
-
#
|
6
|
-
# @param [Hash] attributes hash of attributes.
|
7
|
-
# @return [Base] the update_attributes object.
|
8
|
-
def update(attributes)
|
9
|
-
hash = remove_root(attributes)
|
10
|
-
hash = hash.merge({ 'response' => attributes })
|
11
|
-
hash.each do |key, value|
|
12
|
-
send("#{key}=", value)
|
13
|
-
end
|
14
|
-
self
|
15
|
-
end
|
16
|
-
|
17
4
|
# Make a get requisition and update the object with the response.
|
18
5
|
#
|
19
6
|
# @param [Hash] header hash with the header options.
|
20
7
|
# @return [Base] the object updated.
|
21
8
|
def get(header = {})
|
22
|
-
return
|
9
|
+
return self if ApiClient.config.mock
|
23
10
|
url = "#{ApiClient.config.path[path]}#{self.class.resource_path}/#{id}"
|
24
11
|
response = ApiClient::Dispatcher.get(url, header)
|
25
|
-
|
26
|
-
update(attributes)
|
12
|
+
update(response, url)
|
27
13
|
end
|
28
14
|
|
29
15
|
alias_method :reload, :get
|
@@ -33,11 +19,10 @@ module ApiClient
|
|
33
19
|
# @param [Hash] header hash with the header options.
|
34
20
|
# @return [Base] the object updated.
|
35
21
|
def post(header = {})
|
36
|
-
return
|
22
|
+
return self if ApiClient.config.mock
|
37
23
|
url = "#{ApiClient.config.path[path]}#{self.class.resource_path}"
|
38
24
|
response = ApiClient::Dispatcher.post(url, self.to_hash, header)
|
39
|
-
|
40
|
-
update(attributes)
|
25
|
+
update(response, url)
|
41
26
|
end
|
42
27
|
|
43
28
|
alias_method :create, :post
|
@@ -47,11 +32,10 @@ module ApiClient
|
|
47
32
|
# @param [Hash] header hash with the header options.
|
48
33
|
# @return [Base] the object updated.
|
49
34
|
def put(header = {})
|
50
|
-
return
|
35
|
+
return self if ApiClient.config.mock
|
51
36
|
url = "#{ApiClient.config.path[path]}#{self.class.resource_path}"
|
52
37
|
response = ApiClient::Dispatcher.put(url, self.to_hash, header)
|
53
|
-
|
54
|
-
update(attributes)
|
38
|
+
update(response, url)
|
55
39
|
end
|
56
40
|
|
57
41
|
alias_method :update_attributes, :put
|
@@ -61,11 +45,10 @@ module ApiClient
|
|
61
45
|
# @param [Hash] header hash with the header options.
|
62
46
|
# @return [Base] the object updated.
|
63
47
|
def patch(header = {})
|
64
|
-
return
|
48
|
+
return self if ApiClient.config.mock
|
65
49
|
url = "#{ApiClient.config.path[path]}#{self.class.resource_path}"
|
66
50
|
response = ApiClient::Dispatcher.patch(url, self.to_hash, header)
|
67
|
-
|
68
|
-
update(attributes)
|
51
|
+
update(response, url)
|
69
52
|
end
|
70
53
|
|
71
54
|
# Make a delete requisition and update the object with the response.
|
@@ -73,11 +56,10 @@ module ApiClient
|
|
73
56
|
# @param [Hash] header hash with the header options.
|
74
57
|
# @return [Base] the object updated.
|
75
58
|
def delete(header = {})
|
76
|
-
return
|
59
|
+
return self if ApiClient.config.mock
|
77
60
|
url = "#{ApiClient.config.path[path]}#{self.class.resource_path}/#{id}"
|
78
61
|
response = ApiClient::Dispatcher.delete(url, header)
|
79
|
-
|
80
|
-
update(attributes)
|
62
|
+
update(response, url)
|
81
63
|
end
|
82
64
|
|
83
65
|
alias_method :destroy, :delete
|
@@ -91,5 +73,21 @@ module ApiClient
|
|
91
73
|
attributes = attributes[self.class.root_node.to_s] if attributes.key?(self.class.root_node.to_s)
|
92
74
|
attributes
|
93
75
|
end
|
76
|
+
|
77
|
+
protected
|
78
|
+
|
79
|
+
# Update an object based on a hash of attributes.
|
80
|
+
#
|
81
|
+
# @param [Response] response requisition response.
|
82
|
+
# @param [String] url the url of the requisition.
|
83
|
+
# @return [Base] the update_attributes object.
|
84
|
+
def update(response, url)
|
85
|
+
return response if ApiClient.config.hydra
|
86
|
+
attributes = ApiClient::Parser.response(response, url)
|
87
|
+
hash = remove_root(attributes)
|
88
|
+
hash = hash.merge({ 'response' => attributes })
|
89
|
+
self.attributes = hash
|
90
|
+
self
|
91
|
+
end
|
94
92
|
end
|
95
93
|
end
|
data/lib/api-client/version.rb
CHANGED
@@ -162,7 +162,7 @@ describe ApiClient::Base do
|
|
162
162
|
|
163
163
|
describe '.attributes' do
|
164
164
|
it 'should return an array of attributes' do
|
165
|
-
User.attributes.should == [:a, :b]
|
165
|
+
User.attributes.should == [ :a, :b ]
|
166
166
|
end
|
167
167
|
end
|
168
168
|
|
@@ -172,6 +172,17 @@ describe ApiClient::Base do
|
|
172
172
|
end
|
173
173
|
end
|
174
174
|
|
175
|
+
describe '#attributes=' do
|
176
|
+
before :each do
|
177
|
+
@user = User.new
|
178
|
+
@user.attributes=({ :a => 'a', :b => 'b' })
|
179
|
+
end
|
180
|
+
|
181
|
+
it 'should update the attributes' do
|
182
|
+
@user.attributes.should == { :a => 'a', :b => 'b' }
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
175
186
|
describe '#to_hash' do
|
176
187
|
it 'should return a hash with root node and the attributes and its currently values' do
|
177
188
|
User.new.to_hash.should == { :user => { :a => nil, :b => nil } }
|
@@ -180,12 +191,11 @@ describe ApiClient::Base do
|
|
180
191
|
|
181
192
|
describe '.collection' do
|
182
193
|
before :each do
|
183
|
-
|
184
|
-
collection.stub(:collection => [ user, user ])
|
194
|
+
stub_request(:any, 'http://api.example.com/users').to_return(:body => '[]')
|
185
195
|
end
|
186
196
|
|
187
197
|
it 'should return a collection of objects' do
|
188
|
-
User.collection.should
|
198
|
+
User.collection.should be_an_instance_of(ApiClient::Collection)
|
189
199
|
end
|
190
200
|
end
|
191
201
|
|
@@ -7,28 +7,6 @@ describe ApiClient::ClassMethods do
|
|
7
7
|
stub_request(:any, 'http://api.example.com/users/1').to_return(:body => {'a' => 'b'}.to_json)
|
8
8
|
end
|
9
9
|
|
10
|
-
context '.build' do
|
11
|
-
context 'with a root node' do
|
12
|
-
it 'should return an user' do
|
13
|
-
User.build('user' => { 'a' => 'b'}).should be_an_instance_of(User)
|
14
|
-
end
|
15
|
-
|
16
|
-
it 'should set the response' do
|
17
|
-
User.build('user' => { 'a' => 'b'}).response.should == { 'user' => { 'a' => 'b' } }
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
context 'without a root node' do
|
22
|
-
it 'should return an user' do
|
23
|
-
User.build('a' => 'b').should be_an_instance_of(User)
|
24
|
-
end
|
25
|
-
|
26
|
-
it 'should set the response' do
|
27
|
-
User.build('a' => 'b').response.should == { 'a' => 'b' }
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
10
|
context '.get' do
|
33
11
|
it 'should return an user' do
|
34
12
|
User.get(1).should be_an_instance_of(User)
|
@@ -3,19 +3,49 @@ require 'spec_helper'
|
|
3
3
|
describe ApiClient::Collection do
|
4
4
|
let(:user) { User.new }
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
6
|
+
describe '.initialize' do
|
7
|
+
it 'should save the class in an instance_variable' do
|
8
|
+
ApiClient::Collection.new([], User).instance_variable_get('@klass').should == User
|
9
|
+
end
|
10
|
+
|
11
|
+
context 'with a collection of attributes' do
|
12
|
+
before :each do
|
13
|
+
attributes = [ { :user => { :a => 'a' } }, { :user => { :b => 'b' } } ]
|
14
|
+
@collection = ApiClient::Collection.new(attributes, User)
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should initialize a collection of objects' do
|
18
|
+
@collection.size.should == 2
|
19
|
+
end
|
20
|
+
end
|
11
21
|
|
12
|
-
|
13
|
-
|
22
|
+
context 'with a single object attribute' do
|
23
|
+
before :each do
|
24
|
+
attributes = { :user => { :a => 'a' } }
|
25
|
+
@collection = ApiClient::Collection.new(attributes, User)
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should initialize a single object' do
|
29
|
+
@collection.size.should == 1
|
30
|
+
end
|
31
|
+
end
|
14
32
|
end
|
15
33
|
|
16
|
-
describe '
|
17
|
-
|
18
|
-
|
34
|
+
describe '#update' do
|
35
|
+
before :each do
|
36
|
+
attributes = [ { :user => { :a => 'a' } }, { :user => { :b => 'b' } } ]
|
37
|
+
@collection = ApiClient::Collection.new(attributes, User)
|
38
|
+
end
|
39
|
+
context 'with a collection of attributes' do
|
40
|
+
it 'Should update the collection with new objects based on the attributes' do
|
41
|
+
@collection.update([ { :user => { :a => 'a' } }, { :user => { :b => 'b' } } ]).size.should == 2
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context 'with a single object attribute' do
|
46
|
+
it 'should update the collection with the new object based on the attributes' do
|
47
|
+
@collection.update({ :user => { :a => 'a' } }).size.should == 1
|
48
|
+
end
|
19
49
|
end
|
20
50
|
end
|
21
51
|
end
|
@@ -27,7 +27,7 @@ describe ApiClient::Dispatcher do
|
|
27
27
|
it 'should return the request' do
|
28
28
|
ApiClient::Dispatcher.patch('http://api.example.com/user/5', {}, {}).body.should == ('asd')
|
29
29
|
end
|
30
|
-
end
|
30
|
+
end
|
31
31
|
|
32
32
|
describe '.delete' do
|
33
33
|
it 'should return the request' do
|
@@ -12,28 +12,6 @@ describe ApiClient::InstanceMethods do
|
|
12
12
|
stub_request(:any, 'http://api.example.com/users/1').to_return(:body => {'a' => 'b'}.to_json)
|
13
13
|
end
|
14
14
|
|
15
|
-
context '.update' do
|
16
|
-
context 'with a root node' do
|
17
|
-
it 'should update the object' do
|
18
|
-
user.update('user' => { 'a' => 'b'}).should be_an_instance_of(User)
|
19
|
-
end
|
20
|
-
|
21
|
-
it 'should set the response' do
|
22
|
-
user.update('user' => { 'a' => 'b'}).response.should == { 'user' => { 'a' => 'b' } }
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
context 'without a root node' do
|
27
|
-
it 'should update the object' do
|
28
|
-
user.update('a' => 'b').should be_an_instance_of(User)
|
29
|
-
end
|
30
|
-
|
31
|
-
it 'should set the response' do
|
32
|
-
user.update('a' => 'b').response.should == { 'a' => 'b' }
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
15
|
context '.get' do
|
38
16
|
it 'should return an user' do
|
39
17
|
user.get.should be_an_instance_of(User)
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ApiClient do
|
4
|
+
describe '.parallel' do
|
5
|
+
if defined?(::Typhoeus)
|
6
|
+
before :each do
|
7
|
+
stub_request(:any, 'http://api.example.com/users/1').to_return(:body => {'a' => '1'}.to_json)
|
8
|
+
stub_request(:any, 'http://api.example.com/users/2').to_return(:body => {'a' => '2'}.to_json)
|
9
|
+
stub_request(:any, 'http://api.example.com/users/3').to_return(:body => {'a' => '3'}.to_json)
|
10
|
+
stub_request(:any, 'http://api.example.com/users/4').to_return(:body => {'a' => '4'}.to_json)
|
11
|
+
stub_request(:any, 'http://api.example.com/users/5').to_return(:body => {'a' => '5'}.to_json)
|
12
|
+
@user1 = User.new
|
13
|
+
@user2 = User.new
|
14
|
+
@user3 = User.new
|
15
|
+
@user4 = User.new
|
16
|
+
@user5 = User.new
|
17
|
+
ApiClient.parallel do
|
18
|
+
User.find(1).on_complete_update(@user1)
|
19
|
+
User.find(2).on_complete_update(@user2)
|
20
|
+
User.find(3).on_complete_update(@user3)
|
21
|
+
User.find(4).on_complete_update(@user4)
|
22
|
+
User.find(5).on_complete_update(@user5)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'initialize user1' do
|
27
|
+
@user1.attributes.should == { :a => '1', :b => nil }
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'initialize user2' do
|
31
|
+
@user2.attributes.should == { :a => '2', :b => nil }
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'initialize user3' do
|
35
|
+
@user3.attributes.should == { :a => '3', :b => nil }
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'initialize user4' do
|
39
|
+
@user4.attributes.should == { :a => '4', :b => nil }
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'initialize user5' do
|
43
|
+
@user5.attributes.should == { :a => '5', :b => nil }
|
44
|
+
end
|
45
|
+
else
|
46
|
+
it 'should raise an exception' do
|
47
|
+
expect { ApiClient.parallel {} }.to raise_error(ApiClient::Exceptions::NotPossible)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context 'settings' do
|
53
|
+
it 'should return the configured path' do
|
54
|
+
ApiClient.config.path.should == { :default => 'http://api.example.com/' }
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'should return the default header' do
|
58
|
+
ApiClient.config.header.should == { 'Content-Type' => 'application/json' }
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'should have mock equal false' do
|
62
|
+
ApiClient.config.mock.should be_false
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'should have hydra equal false' do
|
66
|
+
ApiClient.config.hydra.should be_false
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
metadata
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: api-client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
5
|
-
prerelease:
|
4
|
+
version: 2.5.0.rc1
|
5
|
+
prerelease: 6
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Paulo Henrique Lopes Ribeiro
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-09-
|
12
|
+
date: 2013-09-16 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|
@@ -159,6 +159,7 @@ files:
|
|
159
159
|
- lib/api-client/configuration.rb
|
160
160
|
- lib/api-client/dispatcher.rb
|
161
161
|
- lib/api-client/dispatcher/net-http.rb
|
162
|
+
- lib/api-client/dispatcher/parallel.rb
|
162
163
|
- lib/api-client/dispatcher/typhoeus.rb
|
163
164
|
- lib/api-client/errors.rb
|
164
165
|
- lib/api-client/exceptions.rb
|
@@ -169,13 +170,13 @@ files:
|
|
169
170
|
- lib/api-client/exceptions/generic.rb
|
170
171
|
- lib/api-client/exceptions/internal_server_error.rb
|
171
172
|
- lib/api-client/exceptions/not_found.rb
|
173
|
+
- lib/api-client/exceptions/not_possible.rb
|
172
174
|
- lib/api-client/exceptions/service_unavailable.rb
|
173
175
|
- lib/api-client/exceptions/unauthorized.rb
|
174
176
|
- lib/api-client/instance_methods.rb
|
175
177
|
- lib/api-client/net/http.rb
|
176
178
|
- lib/api-client/parser.rb
|
177
179
|
- lib/api-client/version.rb
|
178
|
-
- spec/api-client.rb
|
179
180
|
- spec/api-client/base_spec.rb
|
180
181
|
- spec/api-client/class_methods_spec.rb
|
181
182
|
- spec/api-client/collection_spec.rb
|
@@ -184,6 +185,7 @@ files:
|
|
184
185
|
- spec/api-client/errors_spec.rb
|
185
186
|
- spec/api-client/instance_methods_spec.rb
|
186
187
|
- spec/api-client/parser_spec.rb
|
188
|
+
- spec/api-client_spec.rb
|
187
189
|
- spec/spec_helper.rb
|
188
190
|
homepage:
|
189
191
|
licenses:
|
@@ -200,22 +202,19 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
200
202
|
version: '0'
|
201
203
|
segments:
|
202
204
|
- 0
|
203
|
-
hash: -
|
205
|
+
hash: -3068055785354345759
|
204
206
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
205
207
|
none: false
|
206
208
|
requirements:
|
207
|
-
- - ! '
|
209
|
+
- - ! '>'
|
208
210
|
- !ruby/object:Gem::Version
|
209
|
-
version:
|
210
|
-
segments:
|
211
|
-
- 0
|
212
|
-
hash: -3601308474644028566
|
211
|
+
version: 1.3.1
|
213
212
|
requirements: []
|
214
213
|
rubyforge_project:
|
215
214
|
rubygems_version: 1.8.25
|
216
215
|
signing_key:
|
217
216
|
specification_version: 3
|
218
|
-
summary:
|
217
|
+
summary: Api client easy to play with parallelism support!
|
219
218
|
test_files:
|
220
219
|
- examples/config/initializers/api-client.rb
|
221
220
|
- examples/controllers/application_controller.rb
|
@@ -229,7 +228,6 @@ test_files:
|
|
229
228
|
- examples/scripts/example.rb
|
230
229
|
- gemfiles/Gemfile.net_http
|
231
230
|
- gemfiles/Gemfile.typhoeus
|
232
|
-
- spec/api-client.rb
|
233
231
|
- spec/api-client/base_spec.rb
|
234
232
|
- spec/api-client/class_methods_spec.rb
|
235
233
|
- spec/api-client/collection_spec.rb
|
@@ -238,5 +236,6 @@ test_files:
|
|
238
236
|
- spec/api-client/errors_spec.rb
|
239
237
|
- spec/api-client/instance_methods_spec.rb
|
240
238
|
- spec/api-client/parser_spec.rb
|
239
|
+
- spec/api-client_spec.rb
|
241
240
|
- spec/spec_helper.rb
|
242
241
|
has_rdoc:
|
data/spec/api-client.rb
DELETED
@@ -1,17 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe ApiClient do
|
4
|
-
context 'default settings' do
|
5
|
-
it 'should have a blank path' do
|
6
|
-
ApiClient.config.path.should == ''
|
7
|
-
end
|
8
|
-
|
9
|
-
it 'should have an empty header' do
|
10
|
-
ApiClient.config.header.should == {}
|
11
|
-
end
|
12
|
-
|
13
|
-
it 'should have mock equal false' do
|
14
|
-
ApiClient.config.mock.should be_false
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|