api-client 2.4.0 → 2.5.0.rc1
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.
- 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
|