api-client 1.5.0 → 1.5.1

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.md CHANGED
@@ -1,12 +1,17 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## v1.5.0
4
+
5
+ * Handler of arrays as response.
6
+ * Method to redefine response object added.
7
+
3
8
  ## v1.4.1
4
9
 
5
10
  * ActiveModel compatibility for errors added. make all error keys symbolic.
6
11
 
7
12
  ## v1.4.0
8
13
 
9
- * functionality to validate on client side using ApiClient::Base added
14
+ * functionality to validate on client side using ApiClient::Base added.
10
15
 
11
16
  ## v1.3.0
12
17
 
data/README.md CHANGED
@@ -21,32 +21,42 @@ Or install it yourself as:
21
21
 
22
22
  Add this to your ApplicationController:
23
23
 
24
- rescue_from ApiClient::Exceptions::NotFound, :with => :not_found
24
+ ```ruby
25
+ rescue_from ApiClient::Exceptions::NotFound, :with => :not_found
25
26
 
26
- def not_found
27
- #Specify your own behavior here
28
- end
27
+ def not_found
28
+ #Specify your own behavior here
29
+ end
30
+ ```
29
31
 
30
32
  You can define a more generic rescue that will work for any error:
31
33
 
32
- rescue_from ApiClient::Exceptions::Generic, :with => :generic_error
34
+ ```ruby
35
+ rescue_from ApiClient::Exceptions::Generic, :with => :generic_error
36
+ ```
33
37
 
34
38
  On Your model, extend ApiClient::Base
35
39
 
36
- class User < ApiClient::Base
40
+ ```ruby
41
+ class User < ApiClient::Base
42
+ ```
37
43
 
38
44
  Then, on your action, just put into it:
39
45
 
40
- @user = User.get("http://api.example.com/user/3")
46
+ ```ruby
47
+ @user = User.get("http://api.example.com/user/3")
48
+ ```
41
49
 
42
50
  ## Advanced Usage
43
51
 
44
52
  ApiClient can read api responses with root nodes based on the name of the virtual class.
45
53
  In Some cases, that is not the required behavior. To Redefine it, use remote_object method:
46
54
 
47
- class Admin < ApiClient::Base
48
- self.remote_object = "user"
49
- end
55
+ ```ruby
56
+ class Admin < ApiClient::Base
57
+ self.remote_object = "user"
58
+ end
59
+ ```
50
60
 
51
61
  ## TODO
52
62
  * Add Support to Typhoeus and Faraday
@@ -59,4 +69,4 @@ In Some cases, that is not the required behavior. To Redefine it, use remote_obj
59
69
  2. Create your feature branch (`git checkout -b my-new-feature`)
60
70
  3. Commit your changes (`git commit -am 'Added some feature'`)
61
71
  4. Push to the branch (`git push origin my-new-feature`)
62
- 5. Create new Pull Request
72
+ 5. Create new Pull Request
@@ -2,7 +2,7 @@ class UserController < ApplicationController
2
2
  # It will works with respond_with.
3
3
  # Your action should looks like any other one: A model with a method call. =D
4
4
  def index
5
- @user = User.get("api.example.com/users")
5
+ @users = User.get("api.example.com/users")
6
6
  respond_with(@users)
7
7
  end
8
8
  end
@@ -1,4 +1,8 @@
1
1
  class User < ApiClient::Base
2
2
  # Any of this fields can be called to manage rails form.
3
3
  attr_accessor :id, :email, :password, :password_confirmation
4
+
5
+ # Validations will work as well
6
+ validates :email, :presence => true, :uniqueness => true
7
+ validates :password, :confirmation => true
4
8
  end
@@ -1,155 +1,69 @@
1
1
  require "active_model"
2
2
 
3
- # ApiClient::Base provides a way to make easy api requests as well as making possible to use it inside rails.
4
- # A possible implementation:
5
- # class Car < ApiClient::Base
6
- # attr_accessor :color, :name, :year
7
- # end
8
- # This class will handle Rails form as well as it works with respond_with.
9
- class ApiClient::Base
10
- include ActiveModel::Validations
11
- include ActiveModel::Conversion
12
- extend ActiveModel::Naming
3
+ module ApiClient
4
+ # ApiClient::Base provides a way to make easy api requests as well as making possible to use it inside rails.
5
+ # A possible implementation:
6
+ # class Car < ApiClient::Base
7
+ # attr_accessor :color, :name, :year
8
+ # end
9
+ # This class will handle Rails form as well as it works with respond_with.
10
+ class Base
11
+ include ActiveModel::Validations
12
+ include ActiveModel::Conversion
13
+ extend ActiveModel::Naming
13
14
 
14
- extend ApiClient::Parser
15
- extend ApiClient::Dispatcher
16
-
17
- # Initialize an object based on a hash of attributes.
18
- #
19
- # @param [Hash] attributes object attributes.
20
- # @return [Base] the object initialized.
21
- def initialize(attributes = {})
22
- attributes.each do |name, value|
23
- send("#{name}=", value)
15
+ # Initialize an object based on a hash of attributes.
16
+ #
17
+ # @param [Hash] attributes object attributes.
18
+ # @return [Base] the object initialized.
19
+ def initialize(attributes = {})
20
+ attributes.each do |name, value|
21
+ send("#{name}=", value)
22
+ end
24
23
  end
25
- end
26
-
27
- # Return if a object is persisted on the database or not.
28
- #
29
- # @return [False] always return false.
30
- def persisted?
31
- false
32
- end
33
-
34
- # Return the hash of errors if existent, otherwise instantiate a new ApiClient::Errors object with self.
35
- #
36
- # @return [ApiClient::Errors] the validation errors.
37
- def errors
38
- @errors ||= ApiClient::Errors.new(self)
39
- end
40
24
 
41
- # Set the hash of errors, making keys symbolic.
42
- #
43
- # @param [Hash] errors of the object..
44
- def errors=(errors = {})
45
- @errors = Hash[errors.map{|(key,value)| [key.to_sym,value]}]
46
- end
47
-
48
- # Make a get request and returns a new instance with te response attributes.
49
- #
50
- # @param [String] url of the api request.
51
- # @param [Hash] header attributes of the request.
52
- # @return [Base] a new object initialized with the response.
53
- # @raise [ApiClient::Exceptions::ConnectionRefused] The request was refused by the api.
54
- # @raise [ApiClient::Exceptions::Unauthorized] The request requires user authentication.
55
- # @raise [ApiClient::Exceptions::Forbidden] The request was refused.
56
- # @raise [ApiClient::Exceptions::NotFound] The request uri has not be found.
57
- # @raise [ApiClient::Exceptions::InternalServerError] The request was not fulfilled by the api.
58
- # @raise [ApiClient::Exceptions::BadGateway] The request was not fulfilled by the api.
59
- # @raise [ApiClient::Exceptions::ServiceUnavailable] The api was unable to handle the request.
60
- def self.get(url = '', header = {})
61
- dispatch { _get(url, header) }
62
- end
63
-
64
- # Make a post request and returns a new instance with te response attributes.
65
- #
66
- # @param [String] url of the api request.
67
- # @param [Hash] args attributes of object.
68
- # @param [Hash] header attributes of the request.
69
- # @return [Base] a new object initialized with the response.
70
- # @raise [ApiClient::Exceptions::ConnectionRefused] The request was refused by the api.
71
- # @raise [ApiClient::Exceptions::Unauthorized] The request requires user authentication.
72
- # @raise [ApiClient::Exceptions::Forbidden] The request was refused.
73
- # @raise [ApiClient::Exceptions::NotFound] The request uri has not be found.
74
- # @raise [ApiClient::Exceptions::InternalServerError] The request was not fulfilled by the api.
75
- # @raise [ApiClient::Exceptions::BadGateway] The request was not fulfilled by the api.
76
- # @raise [ApiClient::Exceptions::ServiceUnavailable] The api was unable to handle the request.
77
- def self.post(url = '', args = {}, header = {})
78
- dispatch { _post(url, args, header) }
79
- end
80
-
81
- # Make a put request and returns a new instance with te response attributes.
82
- #
83
- # @param [String] url of the api request.
84
- # @param [Hash] args attributes of object.
85
- # @param [Hash] header attributes of the request.
86
- # @return [Base] a new object initialized with the response.
87
- # @raise [ApiClient::Exceptions::ConnectionRefused] The request was refused by the api.
88
- # @raise [ApiClient::Exceptions::Unauthorized] The request requires user authentication.
89
- # @raise [ApiClient::Exceptions::Forbidden] The request was refused.
90
- # @raise [ApiClient::Exceptions::NotFound] The request uri has not be found.
91
- # @raise [ApiClient::Exceptions::InternalServerError] The request was not fulfilled by the api.
92
- # @raise [ApiClient::Exceptions::BadGateway] The request was not fulfilled by the api.
93
- # @raise [ApiClient::Exceptions::ServiceUnavailable] The api was unable to handle the request.
94
- def self.put(url = '', args = {}, header = {})
95
- dispatch { _put(url, args, header) }
96
- end
25
+ # Return the Remote Object Name.
26
+ #
27
+ # @return [String] a string with the remote object class name.
28
+ def self.remote_object
29
+ @remote_object.blank? ? self.to_s.split('::').last.downcase : @remote_object
30
+ end
97
31
 
98
- # Make a patch request and returns a new instance with te response attributes.
99
- #
100
- # @param [String] url of the api request.
101
- # @param [Hash] args attributes of object.
102
- # @param [Hash] header attributes of the request.
103
- # @return [Base] a new object initialized with the response.
104
- # @raise [ApiClient::Exceptions::ConnectionRefused] The request was refused by the api.
105
- # @raise [ApiClient::Exceptions::Unauthorized] The request requires user authentication.
106
- # @raise [ApiClient::Exceptions::Forbidden] The request was refused.
107
- # @raise [ApiClient::Exceptions::NotFound] The request uri has not be found.
108
- # @raise [ApiClient::Exceptions::InternalServerError] The request was not fulfilled by the api.
109
- # @raise [ApiClient::Exceptions::BadGateway] The request was not fulfilled by the api.
110
- # @raise [ApiClient::Exceptions::ServiceUnavailable] The api was unable to handle the request.
111
- def self.patch(url = '', args = {}, header = {})
112
- dispatch { _patch(url, args, header) }
113
- end
32
+ # Set a custom remote object name instead of the virtual class name.
33
+ #
34
+ # @param [String] remote_object name.
35
+ def self.remote_object=(remote_object)
36
+ @remote_object = remote_object
37
+ end
114
38
 
115
- # Make a delete request and returns a new instance with te response attributes.
116
- #
117
- # @param [String] url of the api request.
118
- # @param [Hash] header attributes of the request.
119
- # @return [Base] a new object initialized with the response.
120
- # @raise [ApiClient::Exceptions::ConnectionRefused] The request was refused by the api.
121
- # @raise [ApiClient::Exceptions::Unauthorized] The request requires user authentication.
122
- # @raise [ApiClient::Exceptions::Forbidden] The request was refused.
123
- # @raise [ApiClient::Exceptions::NotFound] The request uri has not be found.
124
- # @raise [ApiClient::Exceptions::InternalServerError] The request was not fulfilled by the api.
125
- # @raise [ApiClient::Exceptions::BadGateway] The request was not fulfilled by the api.
126
- # @raise [ApiClient::Exceptions::ServiceUnavailable] The api was unable to handle the request.
127
- def self.delete(url = '', header = {})
128
- dispatch { _delete(url, header) }
129
- end
39
+ # Return if a object is persisted on the database or not.
40
+ #
41
+ # @return [False] always return false.
42
+ def persisted?
43
+ false
44
+ end
130
45
 
131
- protected
46
+ # Return the hash of errors if existent, otherwise instantiate a new ApiClient::Errors object with self.
47
+ #
48
+ # @return [ApiClient::Errors] the validation errors.
49
+ def errors
50
+ @errors ||= Errors.new(self)
51
+ end
132
52
 
133
- def self.dispatch
134
- begin
135
- code, object = _response(yield)
136
- rescue Errno::ECONNREFUSED
137
- raise ApiClient::Exceptions::ConnectionRefused
53
+ # Set the hash of errors, making keys symbolic.
54
+ #
55
+ # @param [Hash] errors of the object..
56
+ def errors=(errors = {})
57
+ @errors = Errors.new(self).add_errors(Hash[errors.map{|(key,value)| [key.to_sym,value]}])
138
58
  end
139
- raise_exception(code)
140
- return object.map { |a| new(a) } if object.instance_of?(Array)
141
- new(object)
142
- end
143
59
 
144
- def self.raise_exception(code)
145
- case code
146
- when '401' then raise ApiClient::Exceptions::Unauthorized
147
- when '403' then raise ApiClient::Exceptions::Forbidden
148
- when '404' then raise ApiClient::Exceptions::NotFound
149
- when '500' then raise ApiClient::Exceptions::InternalServerError
150
- when '502' then raise ApiClient::Exceptions::BadGateway
151
- when '503' then raise ApiClient::Exceptions::ServiceUnavailable
152
- else return
60
+ protected
61
+
62
+ def self.method_missing(method, *args)
63
+ return super unless Dispatcher.respond_to?(method)
64
+ json_object = Parser.response(Dispatcher.send(method, *args), remote_object)
65
+ return json_object.map { |a| new(a) } if json_object.instance_of?(Array)
66
+ new(json_object)
153
67
  end
154
68
  end
155
- end
69
+ end
@@ -7,9 +7,9 @@ module ApiClient::Dispatcher
7
7
  # @param [String] url of the api request.
8
8
  # @param [Hash] header attributes of the request.
9
9
  # @return [HTTP] the response object.
10
- def _get(url, header)
10
+ def self.get(url, header = {})
11
11
  initialize_connection(url)
12
- @http.get(@uri.path, header)
12
+ call { @http.get(@uri.path, header) }
13
13
  end
14
14
 
15
15
  # Make a post request and returns it.
@@ -18,9 +18,9 @@ module ApiClient::Dispatcher
18
18
  # @param [Hash] args attributes of object.
19
19
  # @param [Hash] header attributes of the request.
20
20
  # @return [HTTP] the response object.
21
- def _post(url, args, header)
21
+ def self.post(url, args, header = {})
22
22
  initialize_connection(url)
23
- @http.post(@uri.path, args.to_json, { 'Content-Type' => 'application/json' }.merge(header))
23
+ call { @http.post(@uri.path, args.to_json, { 'Content-Type' => 'application/json' }.merge(header)) }
24
24
  end
25
25
 
26
26
  # Make a put request and returns it.
@@ -29,9 +29,9 @@ module ApiClient::Dispatcher
29
29
  # @param [Hash] args attributes of object.
30
30
  # @param [Hash] header attributes of the request.
31
31
  # @return [HTTP] the response object.
32
- def _put(url, args, header)
32
+ def self.put(url, args, header = {})
33
33
  initialize_connection(url)
34
- @http.put(@uri.path, args.to_json, { 'Content-Type' => 'application/json' }.merge(header))
34
+ call { @http.put(@uri.path, args.to_json, { 'Content-Type' => 'application/json' }.merge(header)) }
35
35
  end
36
36
 
37
37
  # Make a patch request and returns it.
@@ -40,9 +40,9 @@ module ApiClient::Dispatcher
40
40
  # @param [Hash] args attributes of object.
41
41
  # @param [Hash] header attributes of the request.
42
42
  # @return [HTTP] the response object.
43
- def _patch(url, args, header)
43
+ def self.patch(url, args, header = {})
44
44
  initialize_connection(url)
45
- @http.patch(@uri.path, args.to_json, { 'Content-Type' => 'application/json' }.merge(header))
45
+ call { @http.patch(@uri.path, args.to_json, { 'Content-Type' => 'application/json' }.merge(header)) }
46
46
  end
47
47
 
48
48
  # Make a delete request and returns it.
@@ -50,15 +50,23 @@ module ApiClient::Dispatcher
50
50
  # @param [String] url of the api request.
51
51
  # @param [Hash] header attributes of the request.
52
52
  # @return [HTTP] the response object.
53
- def _delete(url, header)
53
+ def self.delete(url, header = {})
54
54
  initialize_connection(url)
55
- @http.delete(@uri.path, header)
55
+ call { @http.delete(@uri.path, header) }
56
56
  end
57
57
 
58
58
  protected
59
59
 
60
- def initialize_connection(url = '')
60
+ def self.initialize_connection(url = '')
61
61
  @uri = URI(url)
62
62
  @http = Net::HTTP.new(@uri.host, @uri.port)
63
63
  end
64
+
65
+ def self.call
66
+ begin
67
+ yield
68
+ rescue Errno::ECONNREFUSED
69
+ raise ApiClient::Exceptions::ConnectionRefused
70
+ end
71
+ end
64
72
  end
@@ -2,16 +2,25 @@ require "active_model"
2
2
 
3
3
  # ApiClient::Errors provide extra functionality to ActiveModel::Errors.
4
4
  class ApiClient::Errors < ActiveModel::Errors
5
+ # Add serveral errors from a hash to the object.
6
+ #
7
+ # @return [ApiClient::Errors] The Error object.
8
+ def add_errors(errors = {})
9
+ errors.each do |key, value|
10
+ self.set(key, value)
11
+ end
12
+ self
13
+ end
14
+
5
15
  # Create a hash of attributes with unique validation error messages.
6
16
  #
7
17
  # Example:
8
18
  # user.errors.unique_messages #=> { :name => [ can't be empty and is invalid ]}
9
19
  #
10
20
  # @return [Hash] The hash of attributes with a unique error message.
11
- #
12
21
  def unique_messages
13
22
  errors = {}
14
23
  to_hash.each do |attribute, messages| errors[attribute] = messages.join(" and ") end
15
24
  errors
16
25
  end
17
- end
26
+ end
@@ -4,28 +4,29 @@ module ApiClient::Parser
4
4
  #
5
5
  # @param [HTTP] response HTTP object for the request.
6
6
  # @return [Array] the code and the body parsed.
7
- def _response(response)
7
+ def self.response(response, remote_object)
8
8
  begin
9
9
  object = JSON.parse(response.body)
10
10
  object = object[remote_object] if object.key?(remote_object)
11
11
  object = object[remote_object.pluralize] if object.key?(remote_object.pluralize)
12
12
  rescue JSON::ParserError
13
- object = nil
13
+ object = {}
14
14
  end
15
- return response.code, object
15
+ raise_exception(response.code)
16
+ object
16
17
  end
17
18
 
18
- # Return the Remote Object Name.
19
- #
20
- # @return [String] a string with the remote object class name.
21
- def remote_object
22
- @remote_object.blank? ? self.to_s.split('::').last.downcase : @remote_object
23
- end
19
+ protected
24
20
 
25
- # Set a custom remote object name instead of the virtual class name.
26
- #
27
- # @param [String] remote_object name.
28
- def remote_object=(remote_object)
29
- @remote_object = remote_object
21
+ def self.raise_exception(code)
22
+ case code
23
+ when '401' then raise ApiClient::Exceptions::Unauthorized
24
+ when '403' then raise ApiClient::Exceptions::Forbidden
25
+ when '404' then raise ApiClient::Exceptions::NotFound
26
+ when '500' then raise ApiClient::Exceptions::InternalServerError
27
+ when '502' then raise ApiClient::Exceptions::BadGateway
28
+ when '503' then raise ApiClient::Exceptions::ServiceUnavailable
29
+ else return
30
+ end
30
31
  end
31
32
  end