nestful 1.0.0.rc1 → 1.0.0.rc2

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -2,3 +2,7 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in nestful.gemspec
4
4
  gemspec
5
+
6
+ group :test do
7
+ gem 'webmock'
8
+ end
data/README.markdown CHANGED
@@ -20,18 +20,22 @@ Nestful is a simple Ruby HTTP/REST client with a sane API.
20
20
 
21
21
  ### POST request
22
22
 
23
+ # url-encoded form POST
23
24
  Nestful.post 'http://example.com', :foo => 'bar'
25
+
26
+ # JSON POST
24
27
  Nestful.post 'http://example.com', {:foo => 'bar'}, :format => :json
25
28
 
26
29
  ### Parameters
27
30
 
31
+ # You can also provide nestled params
28
32
  Nestful.get 'http://example.com', :nestled => {:vars => 1}
29
33
 
30
34
  ## Request
31
35
 
32
36
  `Request` is the base class for making HTTP requests - everthing else is just an abstraction upon it.
33
37
 
34
- Request.new(url, options = {})
38
+ Nestful::Request.new(url, options).execute #=> <Nestful::Response>
35
39
 
36
40
  Valid `Request` options are:
37
41
 
@@ -45,11 +49,13 @@ Valid `Request` options are:
45
49
  * timeout
46
50
  * ssl_options
47
51
 
52
+ Requests are run via the `execute` method.
53
+
48
54
  ## Endpoint
49
55
 
50
56
  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/
51
57
 
52
- Nestful::Endpoint.new('http://example.com')['assets'][1].get
58
+ Nestful::Endpoint.new('http://example.com')['assets'][1].get #=> Nestful::Response
53
59
 
54
60
  ## Resource
55
61
 
@@ -57,6 +63,7 @@ If you're building a binding for a REST API, then you should consider using the
57
63
 
58
64
  class Charge < Nestful::Resource
59
65
  url 'https://api.stripe.com/v1/charges'
66
+ options :auth_type => :bearer, :password => 'sk_bar'
60
67
 
61
68
  def self.all
62
69
  self.new(get)
@@ -71,6 +78,27 @@ If you're building a binding for a REST API, then you should consider using the
71
78
  end
72
79
  end
73
80
 
81
+ Charge.all #=> []
82
+ Charge.find('ch_bar').amount
83
+
84
+ ## Response
85
+
86
+ All HTTP responses are in the form of a `Nestful::Response` instance. This contains the raw HTTP response, body, headers and a few helper methods:
87
+
88
+ response = Nestful.get('http://www.google.com')
89
+ response.body #=> '<html>...'
90
+ response.headers #=> {'Content-Type' => 'text/html'}
91
+ response.status #=> 200
92
+
93
+ You can also access the decoded body if available, such as for JSON responses:
94
+
95
+ response = Nestful.get('http://api.stripe.com/v1/charges')
96
+ charges = response.decoded
97
+
98
+ All calls are proxied to the decoded body, so you can access JSON properties like this:
99
+
100
+ charges = Nestful.get('http://api.stripe.com/v1/charges')['data']
101
+
74
102
  ## Credits
75
103
 
76
- Parts of the connection code were taken from ActiveResource
104
+ Parts of the connection code were inspired from ActiveResource.
data/Rakefile CHANGED
@@ -1 +1,8 @@
1
- require "bundler/gem_tasks"
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.libs << 'test'
6
+ t.test_files = FileList['test/**/test*.rb']
7
+ t.verbose = true
8
+ end
data/examples/resource.rb CHANGED
@@ -24,4 +24,8 @@ class Charge < Base
24
24
  def refund
25
25
  post(:refund)
26
26
  end
27
+ end
28
+
29
+ class Token < Base
30
+ path '/v1/tokens'
27
31
  end
@@ -32,7 +32,7 @@ module Nestful
32
32
  end
33
33
 
34
34
  def delete(path, headers = {}, &block)
35
- request(:delete, path, header, &block)
35
+ request(:delete, path, headers, &block)
36
36
  end
37
37
 
38
38
  def head(path, headers = {}, &block)
@@ -2,6 +2,10 @@ require 'cgi'
2
2
 
3
3
  module Nestful
4
4
  module Helpers extend self
5
+ def to_path(*params)
6
+ params.map(&:to_s).reject(&:empty?) * '/'
7
+ end
8
+
5
9
  def to_param(value, key = nil)
6
10
  case value
7
11
  when Hash then value.map { |k,v| to_param(v, append_key(key,k)) }.join('&')
@@ -23,7 +23,7 @@ module Nestful
23
23
 
24
24
  def format=(mime_or_format)
25
25
  @format = mime_or_format.is_a?(Symbol) ?
26
- Formats[mime_or_format] : mime_or_format
26
+ Formats[mime_or_format].new : mime_or_format
27
27
  end
28
28
 
29
29
  def url=(value)
@@ -17,7 +17,7 @@ module Nestful
17
17
  def self.options(value = nil)
18
18
  @options = value if value
19
19
  return @options if @options
20
- superclass.respond_to?(:options) ? superclass.options : nil
20
+ superclass.respond_to?(:options) ? superclass.options : {}
21
21
  end
22
22
 
23
23
  def self.url
@@ -25,9 +25,7 @@ module Nestful
25
25
  end
26
26
 
27
27
  def self.uri(*parts)
28
- parts.unshift(path)
29
- parts.unshift(endpoint)
30
- URI.join(*parts.compact.map(&:to_s))
28
+ URI.parse(Helpers.to_path(url, *parts))
31
29
  end
32
30
 
33
31
  def self.get(action = '', params = {}, options = {})
@@ -74,23 +72,23 @@ module Nestful
74
72
  end
75
73
 
76
74
  def get(action = '', *args)
77
- self.class.get(uri(action), *args)
75
+ self.class.get(path(action), *args)
78
76
  end
79
77
 
80
78
  def put(action = '', *args)
81
- self.class.put(uri(action), *args)
79
+ self.class.put(path(action), *args)
82
80
  end
83
81
 
84
82
  def post(action = '', *args)
85
- self.class.post(uri(action), *args)
83
+ self.class.post(path(action), *args)
86
84
  end
87
85
 
88
86
  def delete(action = '', *args)
89
- self.class.delete(uri(action), *args)
87
+ self.class.delete(path(action), *args)
90
88
  end
91
89
 
92
- def uri(*parts)
93
- self.class.uri(self.id, *parts)
90
+ def path(*parts)
91
+ Helpers.to_path(self.id, *parts)
94
92
  end
95
93
 
96
94
  def id #:nodoc:
@@ -127,7 +125,7 @@ module Nestful
127
125
 
128
126
  alias_method :respond_to_without_attributes?, :respond_to?
129
127
 
130
- def respond_to?(method, include_priv = false)
128
+ def respond_to?(method)
131
129
  method_name = method.to_s
132
130
  if attributes.nil?
133
131
  super
@@ -22,6 +22,8 @@ module Nestful
22
22
  response.code.to_i
23
23
  end
24
24
 
25
+ alias_method :status, :code
26
+
25
27
  def decoded
26
28
  @decoded ||= parser ? parser.decode(body) : body
27
29
  end
@@ -1,3 +1,3 @@
1
1
  module Nestful
2
- VERSION = "1.0.0.rc1"
2
+ VERSION = "1.0.0.rc2"
3
3
  end
@@ -0,0 +1,24 @@
1
+ require 'minitest/autorun'
2
+ require 'webmock/minitest'
3
+ require 'nestful'
4
+
5
+ WebMock.disable_net_connect!
6
+
7
+ class TestEndpoint < MiniTest::Unit::TestCase
8
+ def test_join
9
+ endpoint = Nestful::Endpoint['http://example.com']['charges'][1]
10
+ assert_equal 'http://example.com/charges/1', endpoint.url
11
+ end
12
+
13
+ def test_get
14
+ stub_request(:any, 'http://example.com/charges?limit=10')
15
+ endpoint = Nestful::Endpoint['http://example.com']['charges'].get(:limit => 10)
16
+ assert_requested(:get, 'http://example.com/charges?limit=10')
17
+ end
18
+
19
+ def test_post
20
+ stub_request(:any, 'http://example.com/charges')
21
+ endpoint = Nestful::Endpoint['http://example.com']['charges'].post
22
+ assert_requested(:post, 'http://example.com/charges')
23
+ end
24
+ end
@@ -0,0 +1,71 @@
1
+ require 'minitest/autorun'
2
+ require 'webmock/minitest'
3
+ require 'nestful'
4
+
5
+ WebMock.disable_net_connect!
6
+
7
+ class TestRequest < MiniTest::Unit::TestCase
8
+ def test_get
9
+ stub_request(:any, 'http://example.com/v1/tokens')
10
+ Nestful::Request.new('http://example.com/v1/tokens', :method => :get).execute
11
+ assert_requested(:get, 'http://example.com/v1/tokens')
12
+ end
13
+
14
+ def test_post
15
+ stub_request(:any, 'http://example.com/v1/tokens')
16
+ Nestful::Request.new('http://example.com/v1/tokens', :method => :post).execute
17
+ assert_requested(:post, 'http://example.com/v1/tokens')
18
+ end
19
+
20
+ def test_delete
21
+ stub_request(:any, 'http://example.com/v1/tokens')
22
+ Nestful::Request.new('http://example.com/v1/tokens', :method => :delete).execute
23
+ assert_requested(:delete, 'http://example.com/v1/tokens')
24
+ end
25
+
26
+ def test_put
27
+ stub_request(:any, 'http://example.com/v1/tokens')
28
+ Nestful::Request.new('http://example.com/v1/tokens', :method => :put).execute
29
+ assert_requested(:put, 'http://example.com/v1/tokens')
30
+ end
31
+
32
+ def test_head
33
+ stub_request(:any, 'http://example.com/v1/tokens')
34
+ Nestful::Request.new('http://example.com/v1/tokens', :method => :head).execute
35
+ assert_requested(:head, 'http://example.com/v1/tokens')
36
+ end
37
+
38
+ def test_query_params
39
+ stub_request(:any, 'http://example.com/v1/tokens?card=1')
40
+ Nestful::Request.new('http://example.com/v1/tokens', :method => :get, :params => {:card => 1}).execute
41
+ assert_requested(:get, 'http://example.com/v1/tokens?card=1')
42
+ end
43
+
44
+ def test_form_params
45
+ stub_request(:any, 'http://example.com/v1/tokens')
46
+ Nestful::Request.new('http://example.com/v1/tokens', :method => :post, :params => {:number => 4242, :exp_month => 12}).execute
47
+ assert_requested(:post, 'http://example.com/v1/tokens', :body => 'number=4242&exp_month=12')
48
+ end
49
+
50
+ def test_nestled_form_params
51
+ stub_request(:any, 'http://example.com/v1/tokens')
52
+ Nestful::Request.new('http://example.com/v1/tokens', :method => :post, :params => {:card => {:number => 4242, :exp_month => 12}}).execute
53
+ assert_requested(:post, 'http://example.com/v1/tokens', :body => 'card[number]=4242&card[exp_month]=12')
54
+ end
55
+
56
+ def test_json_params
57
+ stub_request(:any, 'http://example.com/v1/tokens')
58
+ Nestful::Request.new('http://example.com/v1/tokens', :method => :post, :format => :json, :params => {:card => {:number => 4242, :exp_month => 12}}).execute
59
+ assert_requested(:post, 'http://example.com/v1/tokens', :body => '{"card":{"number":4242,"exp_month":12}}', :headers => {'Content-Type' => 'application/json'})
60
+ end
61
+
62
+ def test_timeout
63
+ skip # TODO
64
+ end
65
+
66
+ def test_auth
67
+ stub_request(:any, 'http://example.com/v1/tokens')
68
+ Nestful::Request.new('http://example.com/v1/tokens', :auth_type => :bearer, :password => 'password1').execute
69
+ assert_requested(:get, 'http://example.com/v1/tokens', :headers => {'Authorization' => 'Bearer password1'})
70
+ end
71
+ end
@@ -0,0 +1,45 @@
1
+ require 'minitest/autorun'
2
+ require 'webmock/minitest'
3
+ require 'nestful'
4
+
5
+ WebMock.disable_net_connect!
6
+
7
+ class TestResource < MiniTest::Unit::TestCase
8
+ class Charge < Nestful::Resource
9
+ endpoint 'http://example.com'
10
+ path '/v1/charges'
11
+ end
12
+
13
+ def setup
14
+ end
15
+
16
+ def test_get
17
+ stub_request(:any, 'http://example.com/v1/charges').to_return(:body => '')
18
+ Charge.get
19
+ assert_requested(:get, 'http://example.com/v1/charges')
20
+ end
21
+
22
+ def test_post
23
+ stub_request(:any, 'http://example.com/v1/charges/unknown').to_return(:body => '')
24
+ Charge.post 'unknown'
25
+ assert_requested(:post, 'http://example.com/v1/charges/unknown')
26
+ end
27
+
28
+ def test_get_json
29
+ stub_request(:any, 'http://example.com/v1/charges/1').to_return(
30
+ :body => '{"id": 1, "amount": 2000}',
31
+ :headers => {'Content-Type' => 'application/json'}
32
+ )
33
+ charge = Charge.find(1)
34
+ assert_requested(:get, 'http://example.com/v1/charges/1')
35
+ assert_equal 1, charge.id
36
+ assert_equal 2000, charge.amount
37
+ end
38
+
39
+ def test_instance_put
40
+ stub_request(:any, 'http://example.com/v1/charges/1/capture')
41
+ charge = Charge.new({:id => 1})
42
+ charge.put(:capture)
43
+ assert_requested(:put, 'http://example.com/v1/charges/1/capture')
44
+ end
45
+ end
@@ -0,0 +1,61 @@
1
+ require 'minitest/autorun'
2
+ require 'webmock/minitest'
3
+ require 'nestful'
4
+
5
+ WebMock.disable_net_connect!
6
+
7
+ class TestResponse < MiniTest::Unit::TestCase
8
+ def test_headers
9
+ stub_request(:any, 'http://example.com/v1/charges').to_return(:headers => {'X-TEST' => 'BAR'})
10
+ response = Nestful.get('http://example.com/v1/charges')
11
+ assert_equal 'BAR', response.headers['X-TEST']
12
+ end
13
+
14
+ def test_content_type
15
+ stub_request(:any, 'http://example.com/v1/charges').to_return(:headers => {'Content-Type' => 'application/json'})
16
+ response = Nestful.get('http://example.com/v1/charges')
17
+ assert_equal 'application/json', response.headers.content_type
18
+ end
19
+
20
+ def test_body
21
+ stub_request(:any, 'http://example.com/v1/charges').to_return(:body => 'ok')
22
+ response = Nestful.get('http://example.com/v1/charges')
23
+ assert_equal 'ok', response.body
24
+ end
25
+
26
+ def test_parse_json
27
+ stub_request(:any, 'http://example.com/v1/charges').to_return(:headers => {'Content-Type' => 'application/json'}, :body => '{"result":true}')
28
+ response = Nestful.get('http://example.com/v1/charges')
29
+ assert_equal({'result' => true}, response.decoded)
30
+ end
31
+
32
+ def test_status
33
+ stub_request(:any, 'http://example.com/v1/charges').to_return(:status => 201)
34
+ response = Nestful.get('http://example.com/v1/charges')
35
+ assert_equal 201, response.status
36
+ end
37
+
38
+ def test_raises_404
39
+ stub_request(:any, 'http://example.com/v1/charges').to_return(:status => 404)
40
+
41
+ assert_raises Nestful::ResourceNotFound do
42
+ Nestful.get('http://example.com/v1/charges')
43
+ end
44
+ end
45
+
46
+ def test_raises_400
47
+ stub_request(:any, 'http://example.com/v1/charges').to_return(:status => 400)
48
+
49
+ assert_raises Nestful::BadRequest do
50
+ Nestful.get('http://example.com/v1/charges')
51
+ end
52
+ end
53
+
54
+ def test_delegation
55
+ stub_request(:any, 'http://example.com/v1/charges').to_return(:headers => {'Content-Type' => 'application/json'}, :body => '{"result":true}')
56
+ response = Nestful.get('http://example.com/v1/charges')
57
+
58
+ assert response.respond_to?(:fetch)
59
+ assert response.fetch('result')
60
+ end
61
+ 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.rc1
4
+ version: 1.0.0.rc2
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-05 00:00:00.000000000 Z
12
+ date: 2013-04-10 00:00:00.000000000Z
13
13
  dependencies: []
14
14
  description:
15
15
  email:
@@ -39,6 +39,10 @@ files:
39
39
  - lib/nestful/response/headers.rb
40
40
  - lib/nestful/version.rb
41
41
  - nestful.gemspec
42
+ - test/nestful/test_endpoint.rb
43
+ - test/nestful/test_request.rb
44
+ - test/nestful/test_resource.rb
45
+ - test/nestful/test_response.rb
42
46
  homepage: https://github.com/maccman/nestful
43
47
  licenses: []
44
48
  post_install_message:
@@ -59,8 +63,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
59
63
  version: 1.3.1
60
64
  requirements: []
61
65
  rubyforge_project:
62
- rubygems_version: 1.8.24
66
+ rubygems_version: 1.8.15
63
67
  signing_key:
64
68
  specification_version: 3
65
69
  summary: Simple Ruby HTTP/REST client with a sane API
66
- test_files: []
70
+ test_files:
71
+ - test/nestful/test_endpoint.rb
72
+ - test/nestful/test_request.rb
73
+ - test/nestful/test_resource.rb
74
+ - test/nestful/test_response.rb