nestful 1.0.0.pre → 1.0.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/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