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 CHANGED
@@ -6,4 +6,5 @@ rvm:
6
6
  - ree
7
7
  gemfile:
8
8
  - gemfiles/Gemfile.net_http
9
- - gemfiles/Gemfile.typhoeus
9
+ - gemfiles/Gemfile.typhoeus
10
+ install: bundle install --without developer
data/Gemfile CHANGED
@@ -1,4 +1,10 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in api-client.gemspec
4
- gemspec
4
+ gemspec
5
+
6
+ group :developer do
7
+ gem 'redcarpet'
8
+ gem 'pry'
9
+ gem 'pry-debugger'
10
+ end
data/README.md CHANGED
@@ -45,12 +45,16 @@ Create an initializer:
45
45
 
46
46
  ```ruby
47
47
  ApiClient.configure do |config|
48
- # Api path
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{Client to make Api calls}
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
@@ -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] resource path 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] association classes.
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::Collection.new(self, self.path, self.resource_path).collection
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 build(:id => id) if ApiClient.config.mock
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
- params = ApiClient::Parser.response(response, url)
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 build(attributes) if ApiClient.config.mock
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
- params = ApiClient::Parser.response(response, url)
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 build(attributes) if ApiClient.config.mock
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
- params = ApiClient::Parser.response(response, url)
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 build(attributes) if ApiClient.config.mock
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
- params = ApiClient::Parser.response(response, url)
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 build(:id => id) if ApiClient.config.mock
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
- params = ApiClient::Parser.response(response, url)
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
- include Enumerable
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(klass, path, resource_path)
14
- url = "#{ApiClient.config.path[path]}#{resource_path}"
15
- @collection = ApiClient::Parser.response(ApiClient::Dispatcher.get(url), url)
16
- @collection.map! do |attributes|
17
- klass.new(attributes)
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 [Hash] header the default header for requisitions.
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
- if defined?(::Typhoeus)
8
- return Typhoeus.send(method_name, *args) if Typhoeus.respond_to?(method_name)
9
- else
10
- return NetHttp.send(method_name, *args) if NetHttp.respond_to?(method_name)
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
- if defined?(::Typhoeus)
22
- return true if Typhoeus.respond_to?(method_name)
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
@@ -1,6 +1,4 @@
1
- require 'typhoeus'
2
-
3
- # ApiClient::Dispatcher provides methods to make requests using typhoeus
1
+ # ApiClient::Dispatcher::Typhoeus provides methods to make requests using typhoeus
4
2
  module ApiClient::Dispatcher::Typhoeus
5
3
  # Make a get request and returns it.
6
4
  #
@@ -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 update({}) if ApiClient.config.mock
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
- attributes = ApiClient::Parser.response(response, url)
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 update({}) if ApiClient.config.mock
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
- attributes = ApiClient::Parser.response(response, url)
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 update({}) if ApiClient.config.mock
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
- attributes = ApiClient::Parser.response(response, url)
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 update({}) if ApiClient.config.mock
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
- attributes = ApiClient::Parser.response(response, url)
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 update({}) if ApiClient.config.mock
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
- attributes = ApiClient::Parser.response(response, url)
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
@@ -1,5 +1,5 @@
1
1
  # High Level Namespace of the library ApiClient.
2
2
  module ApiClient
3
3
  # Version of the library.
4
- VERSION = '2.4.0'
4
+ VERSION = '2.5.0.rc1'
5
5
  end
@@ -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
- ApiClient::Collection.stub(:new).with(User, :default, 'users').and_return(collection)
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 == [ user, user ]
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
- before :each do
7
- stub_request(:get, 'http://api.example.com/users').to_return(:body => [ { 'a' => 'b' }, { 'a' => 'b2' } ].to_json)
8
- User.stub(:new => user)
9
- @collection = ApiClient::Collection.new(User, :default, 'users')
10
- end
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
- it 'should include enumerable module' do
13
- @collection.should respond_to(:first)
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 '.initialize' do
17
- it 'Should initialize a collection of Objects' do
18
- @collection.collection.should == [user, user]
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 if ApiClient::Dispatcher.respond_to?(:patch)
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.4.0
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-11 00:00:00.000000000 Z
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: -3601308474644028566
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: '0'
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: Client to make Api calls
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