nestful 1.0.0.pre → 1.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
data/README.markdown CHANGED
@@ -7,56 +7,70 @@ Nestful is a simple Ruby HTTP/REST client with a sane API.
7
7
  ## Features
8
8
 
9
9
  * Simple API
10
- * File buffering
11
- * Before/Progress/After Callbacks
12
10
  * JSON requests
13
- * Multipart requests (file uploading)
14
11
  * Resource API
15
12
  * Proxy support
16
13
  * SSL support
17
14
 
18
- ## Request Options
15
+ ## API
16
+
17
+ ### GET request
18
+
19
+ Nestful.get 'http://example.com' #=> "body"
20
+
21
+ ### POST request
22
+
23
+ Nestful.post 'http://example.com', :foo => 'bar'
24
+ Nestful.post 'http://example.com', {:foo => 'bar'}, :format => :json
25
+
26
+ ### Parameters
27
+
28
+ Nestful.get 'http://example.com', :nestled => {:vars => 1}
29
+
30
+ ## Request
31
+
32
+ `Request` is the base class for making HTTP requests - everthing else is just an abstraction upon it.
33
+
34
+ Request.new(url, options = {})
19
35
 
20
- Request options:
36
+ Valid `Request` options are:
21
37
 
22
38
  * headers (hash)
23
39
  * params (hash)
24
40
  * method (:get/:post/:put/:delete/:head)
25
-
26
- Connection options:
27
-
28
41
  * proxy
29
42
  * user
30
43
  * password
31
- * auth_type
44
+ * auth_type (:basic/:bearer)
32
45
  * timeout
33
46
  * ssl_options
34
47
 
35
- ## API
36
-
37
- ### GET request
38
-
39
- Nestful.get 'http://example.com' #=> "body"
40
-
41
- ### POST request
48
+ ## Endpoint
42
49
 
43
- Nestful.post 'http://example.com', :params => {:foo => 'bar'}
44
- Nestful.post 'http://example.com', :params => {:foo => 'bar'}, :format => :json
50
+ The `Endpoint` class provides a single object to work with restful services. The following example does a GET request to the URL; http://example.com/assets/1/
45
51
 
46
- ### Parameters
52
+ Nestful::Endpoint.new('http://example.com')['assets'][1].get
47
53
 
48
- Nestful.get 'http://example.com', :params => {:nestled => {:params => 1}}
54
+ ## Resource
49
55
 
50
- ### Endpoint
56
+ If you're building a binding for a REST API, then you should consider using the `Resource` class.
51
57
 
52
- The `Endpoint` class provides a single object to work with restful services. The following example does a GET request to the URL; http://example.com/assets/1/
58
+ class Charge < Nestful::Resource
59
+ url 'https://api.stripe.com/v1/charges'
53
60
 
54
- Nestful::Endpoint.new('http://example.com')['assets'][1].get
61
+ def self.all
62
+ self.new(get)
63
+ end
55
64
 
56
- ### Multipart post
65
+ def self.find(id)
66
+ self.new(get(id))
67
+ end
57
68
 
58
- Nestful.post 'http://example.com', :format => :multipart, :params => {:file => File.open('README')}
69
+ def refund
70
+ post(:refund)
71
+ end
72
+ end
59
73
 
60
74
  ## Credits
61
75
 
62
- Large parts of the connection code were taken from ActiveResource
76
+ Parts of the connection code were taken from ActiveResource
data/examples/resource.rb CHANGED
@@ -1,6 +1,27 @@
1
1
  require 'nestful'
2
2
 
3
- class Charge < Nestful::Resource
4
- url 'https://api.stripe.com/v1/charges'
5
- options :auth_type => :bearer, :password => 'sk_test_mkGsLqEW6SLnZa487HYfJVLf'
3
+ # Example of using Stripe's API
4
+
5
+ class Base < Nestful::Resource
6
+ endpoint 'https://api.stripe.com'
7
+ options :auth_type => :bearer, :password => ENV['SECRET_KEY']
8
+
9
+ def self.all(*args)
10
+ # We have to delve into the response,
11
+ # as Stripe doesn't return arrays for
12
+ # list responses.
13
+ self.new(Base.new(get('', *args)).data)
14
+ end
15
+
16
+ def self.find(id)
17
+ self.new(get(id))
18
+ end
19
+ end
20
+
21
+ class Charge < Base
22
+ path '/v1/charges'
23
+
24
+ def refund
25
+ post(:refund)
26
+ end
6
27
  end
@@ -5,12 +5,12 @@ module Nestful
5
5
  class Connection
6
6
  UriParser = URI.const_defined?(:Parser) ? URI::Parser.new : URI
7
7
 
8
- attr_reader :site, :auth_type, :timeout, :proxy, :ssl_options
8
+ attr_reader :endpoint, :auth_type, :timeout, :proxy, :ssl_options
9
9
 
10
- # The +site+ parameter is required and will set the +site+
10
+ # The +endpoint+ parameter is required and will set the +endpoint+
11
11
  # attribute to the URI for the remote resource service.
12
- def initialize(site, options = {})
13
- self.site = site
12
+ def initialize(endpoint, options = {})
13
+ self.endpoint = endpoint
14
14
 
15
15
  options.each do |key, value|
16
16
  self.send("#{key}=", value) unless value.nil?
@@ -18,8 +18,8 @@ module Nestful
18
18
  end
19
19
 
20
20
  # Set URI for remote service.
21
- def site=(site)
22
- @site = site.is_a?(URI) ? site : UriParser.parse(site)
21
+ def endpoint=(endpoint)
22
+ @endpoint = endpoint.is_a?(URI) ? endpoint : UriParser.parse(endpoint)
23
23
  end
24
24
 
25
25
  # Set the proxy for remote service.
@@ -51,37 +51,8 @@ module Nestful
51
51
 
52
52
  # Makes a request to the remote service.
53
53
  def request(method, path, *arguments)
54
- body = nil
55
- body = arguments.shift if [:put, :post].include?(method)
56
- headers = arguments.shift || {}
57
-
58
- method = Net::HTTP.const_get(method.to_s.capitalize)
59
- method = method.new(path)
60
-
61
- if body
62
- if body.respond_to?(:read)
63
- method.body_stream = body
64
- else
65
- method.body = body
66
- end
67
-
68
- if body.respond_to?(:size)
69
- headers['Content-Length'] ||= body.size
70
- end
71
- end
72
-
73
- headers.each do |name, value|
74
- next unless value
75
- method.add_field(name, value)
76
- end
77
-
78
- http.start do |stream|
79
- stream.request(method) do |rsp|
80
- handle_response(rsp)
81
- yield(rsp) if block_given?
82
- rsp
83
- end
84
- end
54
+ response = http.send(method, path, *arguments)
55
+ handle_response(response)
85
56
 
86
57
  rescue Timeout::Error => e
87
58
  raise TimeoutError.new(e.message)
@@ -131,11 +102,11 @@ module Nestful
131
102
 
132
103
  def new_http
133
104
  if proxy
134
- Net::HTTP.new(site.host, site.port,
105
+ Net::HTTP.new(endpoint.host, endpoint.port,
135
106
  proxy.host, proxy.port,
136
107
  proxy.user, proxy.password)
137
108
  else
138
- Net::HTTP.new(site.host, site.port)
109
+ Net::HTTP.new(endpoint.host, endpoint.port)
139
110
  end
140
111
  end
141
112
 
@@ -152,7 +123,7 @@ module Nestful
152
123
  end
153
124
 
154
125
  def apply_ssl_options(http)
155
- return http unless site.is_a?(URI::HTTPS)
126
+ return http unless endpoint.is_a?(URI::HTTPS)
156
127
 
157
128
  http.use_ssl = true
158
129
  http.verify_mode = OpenSSL::SSL::VERIFY_NONE
@@ -11,6 +11,7 @@ module Nestful
11
11
  message = "Failed."
12
12
  message << " Response code = #{response.code}." if response.respond_to?(:code)
13
13
  message << " Response message = #{response.message}." if response.respond_to?(:message)
14
+ message << " Response Body = #{response.body}." if response.respond_to?(:body)
14
15
  message
15
16
  end
16
17
  end
@@ -22,4 +22,4 @@ module Nestful
22
22
 
23
23
  JsonFormat = JSONFormat
24
24
  end
25
- end
25
+ end
@@ -2,40 +2,24 @@ require 'cgi'
2
2
 
3
3
  module Nestful
4
4
  module Helpers extend self
5
- def to_query(key, value)
6
- case key
7
- when Hash
8
- "#{CGI.escape(to_param(key))}=#{CGI.escape(to_param(value))}"
9
-
10
- when Array
11
- prefix = "#{key}[]"
12
- value.collect { |v| to_query(prefix, v) }.join('&')
13
-
5
+ def to_param(value, key = nil)
6
+ case value
7
+ when Hash then value.map { |k,v| to_param(v, append_key(key,k)) }.join('&')
8
+ when Array then value.map { |v| to_param(v, "#{key}[]") }.join('&')
9
+ when nil then ''
14
10
  else
15
- value
16
- end
17
- end
18
-
19
- def to_param(object, namespace = nil)
20
- case object
21
- when Hash
22
- object.map do |key, value|
23
- key = "#{namespace}[#{key}]" if namespace
24
- to_query(key, value)
25
- end.join('&')
26
-
27
- when Array
28
- object.each do |value|
29
- to_param(value)
30
- end.join('/')
31
-
32
- else
33
- value
11
+ "#{key}=#{CGI.escape(value.to_s)}"
34
12
  end
35
13
  end
36
14
 
37
15
  def camelize(value)
38
16
  value.to_s.split('_').map {|w| w.capitalize }.join
39
17
  end
18
+
19
+ protected
20
+
21
+ def append_key(root_key, key)
22
+ root_key.nil? ? key : "#{root_key}[#{key.to_s}]"
23
+ end
40
24
  end
41
25
  end
@@ -35,13 +35,13 @@ module Nestful
35
35
  def uri
36
36
  return @uri if @uri
37
37
 
38
- url = @url.match(/^http/) ? @url : "http://#{@url}"
38
+ url = @url.match(/\Ahttps?:\/\//) ? @url : "http://#{@url}"
39
39
 
40
40
  @uri = URI.parse(url)
41
- @uri.path = "/" if @uri.path.empty?
41
+ @uri.path = '/' if @uri.path.empty?
42
42
 
43
- @uri.query.split("&").each do |res|
44
- key, value = res.split("=")
43
+ @uri.query.split('&').each do |res|
44
+ key, value = res.split('=')
45
45
  @params[key] = value
46
46
  end if @uri.query
47
47
 
@@ -53,16 +53,10 @@ module Nestful
53
53
  end
54
54
 
55
55
  def execute
56
- result = nil
57
-
58
56
  if encoded?
59
- connection.send(method, path, encoded, build_headers) do |res|
60
- result = res
61
- end
57
+ result = connection.send(method, path, encoded, build_headers)
62
58
  else
63
- connection.send(method, query_path, build_headers) do |res|
64
- result = res
65
- end
59
+ result = connection.send(method, query_path, build_headers)
66
60
  end
67
61
 
68
62
  Response.new(result)
@@ -4,82 +4,93 @@ module Nestful
4
4
  class Resource
5
5
  def self.endpoint(value = nil)
6
6
  @endpoint = value if value
7
- @endpoint || ''
7
+ return @endpoint if @endpoint
8
+ superclass.respond_to?(:endpoint) ? superclass.endpoint : nil
8
9
  end
9
10
 
10
- def self.url(value = nil)
11
- @url = value if value
12
- @url ? URI.join(endpoint, @url) : raise('Must define url')
11
+ def self.path(value = nil)
12
+ @path = value if value
13
+ return @path if @path
14
+ superclass.respond_to?(:path) ? superclass.path : nil
13
15
  end
14
16
 
15
17
  def self.options(value = nil)
16
18
  @options = value if value
17
- @options || {}
19
+ return @options if @options
20
+ superclass.respond_to?(:options) ? superclass.options : nil
18
21
  end
19
22
 
20
- def self.get(url, params = {}, options = {})
21
- request(url, options.merge(:method => :get, :params => params))
23
+ def self.url
24
+ URI.join(endpoint.to_s, path.to_s).to_s
22
25
  end
23
26
 
24
- def self.put(url, params = {}, options = {})
25
- request(url, options.merge(:method => :put, :params => params))
27
+ def self.uri(*parts)
28
+ parts.unshift(path)
29
+ parts.unshift(endpoint)
30
+ URI.join(*parts.compact.map(&:to_s))
26
31
  end
27
32
 
28
- def self.post(url, params = {}, options = {})
29
- request(url, options.merge(:method => :post, :params => params))
33
+ def self.get(action = '', params = {}, options = {})
34
+ request(uri(action), options.merge(:method => :get, :params => params))
30
35
  end
31
36
 
32
- def self.delete(url, params = {}, options = {})
33
- request(url, options.merge(:method => :delete, :params => params))
37
+ def self.put(action = '', params = {}, options = {})
38
+ request(uri(action), options.merge(:method => :put, :params => params))
39
+ end
40
+
41
+ def self.post(action = '', params = {}, options = {})
42
+ request(uri(action), options.merge(:method => :post, :params => params))
43
+ end
44
+
45
+ def self.delete(action = '', params = {}, options = {})
46
+ request(uri(action), options.merge(:method => :delete, :params => params))
34
47
  end
35
48
 
36
49
  def self.request(url, options = {})
37
50
  Request.new(url, self.options.merge(options)).execute
38
51
  end
39
52
 
40
- def self.all
41
- self.new(get(url))
53
+ def self.all(*args)
54
+ self.new(get('', *args))
42
55
  end
43
56
 
44
57
  def self.find(id)
45
- self.new(get(URI.join(url, id.to_s)))
58
+ self.new(get(id))
46
59
  end
47
60
 
48
- def self.new(attributes = {}, options = {})
61
+ def self.new(attributes = {})
49
62
  if attributes.is_a?(Array)
50
- attributes.map {|set| super(set, options) }
63
+ attributes.map {|set| super(set) }
51
64
  else
52
65
  super
53
66
  end
54
67
  end
55
68
 
56
- attr_reader :attributes, :options
69
+ attr_reader :attributes
57
70
 
58
- def initialize(attributes = {}, options = {})
59
- @attributes = indifferent_attributes(attributes.to_hash)
60
- @options = self.class.options.merge(options)
71
+ def initialize(attributes = {})
72
+ @attributes = {}
73
+ load(attributes)
61
74
  end
62
75
 
63
- def get(*args)
64
- self.class.get(url, *args)
76
+ def get(action = '', *args)
77
+ self.class.get(uri(action), *args)
65
78
  end
66
79
 
67
- def put(*args)
68
- self.class.put(url, *args)
80
+ def put(action = '', *args)
81
+ self.class.put(uri(action), *args)
69
82
  end
70
83
 
71
- def post(*args)
72
- self.class.post(url, *args)
84
+ def post(action = '', *args)
85
+ self.class.post(uri(action), *args)
73
86
  end
74
87
 
75
- def delete(*args)
76
- self.class.delete(url, *args)
88
+ def delete(action = '', *args)
89
+ self.class.delete(uri(action), *args)
77
90
  end
78
91
 
79
- alias_method :destroy, :delete
80
-
81
- def url
82
- URI.join(self.class.url, self.id.to_s)
92
+ def uri(*parts)
93
+ self.class.uri(self.id, *parts)
83
94
  end
84
95
 
85
96
  def id #:nodoc:
@@ -99,7 +110,7 @@ module Nestful
99
110
  end
100
111
 
101
112
  def to_hash
102
- @attributes.dup
113
+ attributes.dup
103
114
  end
104
115
 
105
116
  alias_method :as_json, :to_hash
@@ -108,6 +119,12 @@ module Nestful
108
119
  as_json.to_json
109
120
  end
110
121
 
122
+ def load(attributes = {})
123
+ attributes.to_hash.each do |key, value|
124
+ send("#{key}=", value)
125
+ end
126
+ end
127
+
111
128
  alias_method :respond_to_without_attributes?, :respond_to?
112
129
 
113
130
  def respond_to?(method, include_priv = false)
@@ -123,19 +140,6 @@ module Nestful
123
140
 
124
141
  protected
125
142
 
126
- def indifferent_attributes(attributes)
127
- attributes = indifferent_hash.merge(attributes)
128
- attributes.each do |key, value|
129
- next unless value.is_a?(Hash)
130
- attributes[key] = indifferent_attributes(value)
131
- end
132
- end
133
-
134
- # Creates a Hash with indifferent access.
135
- def indifferent_hash
136
- Hash.new {|hash,key| hash[key.to_s] if Symbol === key }
137
- end
138
-
139
143
  def method_missing(method_symbol, *arguments) #:nodoc:
140
144
  method_name = method_symbol.to_s
141
145
 
@@ -1,3 +1,3 @@
1
1
  module Nestful
2
- VERSION = "1.0.0.pre"
2
+ VERSION = "1.0.0.rc1"
3
3
  end
data/lib/nestful.rb CHANGED
@@ -16,15 +16,15 @@ module Nestful
16
16
  Endpoint[url].get(*args)
17
17
  end
18
18
 
19
- def post(url, options = {})
19
+ def post(url, *args)
20
20
  Endpoint[url].post(*args)
21
21
  end
22
22
 
23
- def put(url, options = {})
23
+ def put(url, *args)
24
24
  Endpoint[url].put(*args)
25
25
  end
26
26
 
27
- def delete(url, options = {})
27
+ def delete(url, *args)
28
28
  Endpoint[url].delete(*args)
29
29
  end
30
30
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nestful
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0.pre
4
+ version: 1.0.0.rc1
5
5
  prerelease: 6
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-04-04 00:00:00.000000000 Z
12
+ date: 2013-04-05 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description:
15
15
  email:
@@ -23,7 +23,6 @@ files:
23
23
  - MIT-LICENSE
24
24
  - README.markdown
25
25
  - Rakefile
26
- - VERSION
27
26
  - examples/resource.rb
28
27
  - lib/nestful.rb
29
28
  - lib/nestful/.DS_Store
@@ -33,7 +32,6 @@ files:
33
32
  - lib/nestful/formats.rb
34
33
  - lib/nestful/formats/form_format.rb
35
34
  - lib/nestful/formats/json_format.rb
36
- - lib/nestful/formats/multipart_format.rb
37
35
  - lib/nestful/helpers.rb
38
36
  - lib/nestful/request.rb
39
37
  - lib/nestful/resource.rb
data/VERSION DELETED
@@ -1 +0,0 @@
1
- 0.0.8
@@ -1,80 +0,0 @@
1
- require 'securerandom'
2
- require 'tempfile'
3
-
4
- module Nestful
5
- module Formats
6
- class MultipartFormat < Format
7
- EOL = "\r\n"
8
-
9
- attr_reader :boundary, :stream
10
-
11
- def initialize(*args)
12
- super
13
- @boundary = SecureRandom.hex(10)
14
- @stream = Tempfile.new("nf.#{rand(1000)}")
15
- @stream.binmode
16
- end
17
-
18
- def mime_type
19
- %Q{multipart/form-data; boundary=#{boundary}}
20
- end
21
-
22
- def encode(params, options = nil)
23
- to_multipart(params)
24
- stream.write("--" + boundary + "--" + EOL)
25
- stream.flush
26
- stream.rewind
27
- stream
28
- end
29
-
30
- def decode(body)
31
- body
32
- end
33
-
34
- protected
35
- def to_multipart(params, namespace = nil)
36
- params.each do |key, value|
37
- key = namespace ? "#{namespace}[#{key}]" : key
38
-
39
- # Support nestled params
40
- if value.is_a?(Hash)
41
- to_multipart(value, key)
42
- next
43
- end
44
-
45
- stream.write("--" + boundary + EOL)
46
-
47
- if value.respond_to?(:read)
48
- create_file_field(key, value)
49
- else
50
- create_field(key, value)
51
- end
52
- end
53
- end
54
-
55
- def create_file_field(key, value)
56
- stream.write(%Q{Content-Disposition: form-data; name="#{key}"; filename="#{filename(value)}"} + EOL)
57
- stream.write(%Q{Content-Type: application/octet-stream} + EOL)
58
- stream.write(%Q{Content-Transfer-Encoding: binary} + EOL)
59
- stream.write(EOL)
60
- while data = value.read(8124)
61
- stream.write(data)
62
- end
63
- stream.write(EOL)
64
- end
65
-
66
- def create_field(key, value)
67
- stream.write(%Q{Content-Disposition: form-data; name="#{key}"} + EOL)
68
- stream.write(EOL)
69
- stream.write(value)
70
- stream.write(EOL)
71
- end
72
-
73
- def filename(body)
74
- return body.original_filename if body.respond_to?(:original_filename)
75
- return File.basename(body.path) if body.respond_to?(:path)
76
- 'Unknown'
77
- end
78
- end
79
- end
80
- end