roar 0.12.2 → 0.12.3
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.
- checksums.yaml +4 -4
- data/.travis.yml +2 -4
- data/CHANGES.markdown +12 -0
- data/Gemfile +2 -2
- data/README.markdown +28 -3
- data/gemfiles/Gemfile.representable-1.7 +6 -0
- data/lib/roar/representer/feature/http_verbs.rb +10 -13
- data/lib/roar/representer/transport/faraday.rb +6 -6
- data/lib/roar/representer/transport/net_http.rb +90 -27
- data/lib/roar/version.rb +1 -1
- data/roar.gemspec +1 -1
- data/test/collection_json_test.rb +1 -3
- data/test/hal_json_test.rb +3 -1
- data/test/{http_verbs_feature_test.rb → http_verbs_test.rb} +38 -1
- data/test/integration/runner.rb +23 -12
- data/test/integration/server.rb +32 -0
- data/test/integration/ssl_server.rb +60 -0
- data/test/net_http_transport_test.rb +47 -11
- data/test/test_helper.rb +4 -0
- metadata +47 -28
- data/gemfiles/Gemfile.representable-1.5.ruby-1.8 +0 -6
- data/gemfiles/Gemfile.representable-1.5.ruby-1.9 +0 -5
- data/gemfiles/Gemfile.representable-1.6.ruby-1.9 +0 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 36037871272f0ad904505c747eeabb1a09c96c58
|
|
4
|
+
data.tar.gz: 964389ed57a09eb10a95f02a292865c909bda5d1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 33c07296b014315ac382722598061026f629d95ce212e6c6f98f49068a9eb5c0939eef39fc26d54b7a267f9e5e4c266f0fc7a4391787ad7958b1451ed4614a1f
|
|
7
|
+
data.tar.gz: 7391c7bc51baee1d3bf8c3c6eb3b37762b4182c36cdb8474e3f0a027e5184156e1153d37890c68a7b2bc5c3ca92d943cf7ffc20151279fe74efae38baf627914
|
data/.travis.yml
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
matrix:
|
|
2
2
|
include:
|
|
3
3
|
- rvm: 1.9.3
|
|
4
|
-
gemfile: gemfiles/Gemfile.representable-1.
|
|
4
|
+
gemfile: gemfiles/Gemfile.representable-1.7
|
|
5
5
|
- rvm: 2.0.0
|
|
6
|
-
gemfile: gemfiles/Gemfile.representable-1.
|
|
7
|
-
# - rvm: 1.8.7
|
|
8
|
-
# gemfile: gemfiles/Gemfile.representable-1.5.ruby-1.8
|
|
6
|
+
gemfile: gemfiles/Gemfile.representable-1.7
|
|
9
7
|
notifications:
|
|
10
8
|
irc: "irc.freenode.org#cells"
|
data/CHANGES.markdown
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
# 0.12.3
|
|
2
|
+
|
|
3
|
+
* Allow basic authentication with `basic_auth: [:admin, :password]`.
|
|
4
|
+
* Allow HTTPS.
|
|
5
|
+
* Removed `NetHTTP#do_request`. It is in `NetHTTP::Request` now.
|
|
6
|
+
|
|
7
|
+
### Changes for `HttpVerbs#get` and friends:
|
|
8
|
+
|
|
9
|
+
* They now yield the request object to add headers etc before request is sent.
|
|
10
|
+
* They NO LONGER support positional arguments but one hash with `uri: "https://roar.de", body:, .. as: ..` and so on.
|
|
11
|
+
|
|
12
|
+
|
|
1
13
|
# 0.12.2
|
|
2
14
|
|
|
3
15
|
* Fix a bug where hyperlinks from nested objects weren't rendered in XML.
|
data/Gemfile
CHANGED
|
@@ -3,5 +3,5 @@ source "http://rubygems.org"
|
|
|
3
3
|
# Specify your gem's dependencies in roar.gemspec
|
|
4
4
|
gemspec
|
|
5
5
|
|
|
6
|
-
gem "representable", :path => "../representable"
|
|
7
|
-
gem "sinatra",
|
|
6
|
+
#gem "representable", :path => "../representable"
|
|
7
|
+
gem "sinatra-contrib", :git => "git@github.com:apotonick/sinatra-contrib.git", :branch => "runner"
|
data/README.markdown
CHANGED
|
@@ -476,13 +476,13 @@ class Song < OpenStruct
|
|
|
476
476
|
end
|
|
477
477
|
```
|
|
478
478
|
|
|
479
|
-
|
|
479
|
+
## HTTP Support
|
|
480
480
|
|
|
481
481
|
The `Feature::Client` module mixes all necessary methods into the client class, e.g. it provides HTTP support
|
|
482
482
|
|
|
483
483
|
```ruby
|
|
484
484
|
song = Song.new(title: "Roxanne")
|
|
485
|
-
song.post("http://localhost:4567/songs", "application/json")
|
|
485
|
+
song.post(uri: "http://localhost:4567/songs", as: "application/json")
|
|
486
486
|
|
|
487
487
|
song.id #=> 42
|
|
488
488
|
```
|
|
@@ -500,7 +500,7 @@ Roar works with all HTTP request types, check out `GET`.
|
|
|
500
500
|
|
|
501
501
|
```ruby
|
|
502
502
|
song = Client::Song.new
|
|
503
|
-
song.get("http://localhost:4567/songs/1", "application/json")
|
|
503
|
+
song.get(uri: "http://localhost:4567/songs/1", as: "application/json")
|
|
504
504
|
|
|
505
505
|
song.title #=> "Roxanne"
|
|
506
506
|
song.links[:self].href #=> http://localhost/songs/1
|
|
@@ -508,6 +508,31 @@ song.links[:self].href #=> http://localhost/songs/1
|
|
|
508
508
|
|
|
509
509
|
As `GET` is not supposed to send any data, you can use `#get` on an empty object to populate it with the server data.
|
|
510
510
|
|
|
511
|
+
### HTTPS
|
|
512
|
+
|
|
513
|
+
Roar supports SSL connections - they are automatically detected via the protocol.
|
|
514
|
+
|
|
515
|
+
```ruby
|
|
516
|
+
song.get(uri: "https://localhost:4567/songs/1")`
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
### Basic Authentication
|
|
520
|
+
|
|
521
|
+
The HTTP verbs allow you to specify credentials for HTTP basic auth.
|
|
522
|
+
|
|
523
|
+
```ruby
|
|
524
|
+
song.get(uri: "http://localhost:4567/songs/1", basic_auth: ["username", "secret_password"])
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
### Request customization
|
|
528
|
+
|
|
529
|
+
All verbs yield the request object before the request is sent, allowing to modify it. It is a `Net::HTTP::Request` instance (unless you use Faraday).
|
|
530
|
+
|
|
531
|
+
```ruby
|
|
532
|
+
song.get(uri: "http://localhost:4567/songs/1") do |req|
|
|
533
|
+
req.add_field("Cookie", "Yumyum")
|
|
534
|
+
end
|
|
535
|
+
```
|
|
511
536
|
|
|
512
537
|
## XML
|
|
513
538
|
|
|
@@ -5,9 +5,6 @@ module Roar
|
|
|
5
5
|
module Representer
|
|
6
6
|
module Feature
|
|
7
7
|
module HttpVerbs
|
|
8
|
-
# TODO:
|
|
9
|
-
# req.basic_auth 'user', 'pass'
|
|
10
|
-
# Net::HTTP.start(uri.host, uri.port, use_ssl: true
|
|
11
8
|
|
|
12
9
|
class << self
|
|
13
10
|
attr_accessor :transport_engine
|
|
@@ -34,33 +31,33 @@ module Roar
|
|
|
34
31
|
|
|
35
32
|
# Serializes the object, POSTs it to +url+ with +format+, deserializes the returned document
|
|
36
33
|
# and updates properties accordingly.
|
|
37
|
-
def post(url, format)
|
|
38
|
-
response = http.post_uri(url, serialize, format)
|
|
34
|
+
def post(url, format, options={}, &block)
|
|
35
|
+
response = http.post_uri(options.merge(:uri => url, :body => serialize, :as => format), &block)
|
|
39
36
|
handle_response(response)
|
|
40
37
|
end
|
|
41
38
|
|
|
42
39
|
# GETs +url+ with +format+, deserializes the returned document and updates properties accordingly.
|
|
43
|
-
def get(url, format)
|
|
44
|
-
response = http.get_uri(url, format)
|
|
40
|
+
def get(url, format, options={}, &block)
|
|
41
|
+
response = http.get_uri(options.merge(:uri => url, :as => format), &block)
|
|
45
42
|
handle_response(response)
|
|
46
43
|
end
|
|
47
44
|
|
|
48
45
|
# Serializes the object, PUTs it to +url+ with +format+, deserializes the returned document
|
|
49
46
|
# and updates properties accordingly.
|
|
50
|
-
def put(url, format)
|
|
51
|
-
response = http.put_uri(url, serialize, format)
|
|
47
|
+
def put(url, format, options={}, &block)
|
|
48
|
+
response = http.put_uri(options.merge(:uri => url, :body => serialize, :as => format), &block)
|
|
52
49
|
handle_response(response)
|
|
53
50
|
self
|
|
54
51
|
end
|
|
55
52
|
|
|
56
|
-
def patch(url, format)
|
|
57
|
-
response = http.patch_uri(url, serialize, format)
|
|
53
|
+
def patch(url, format, options={}, &block)
|
|
54
|
+
response = http.patch_uri(options.merge(:uri => url, :body => serialize, :as => format), &block)
|
|
58
55
|
handle_response(response)
|
|
59
56
|
self
|
|
60
57
|
end
|
|
61
58
|
|
|
62
|
-
def delete(url, format)
|
|
63
|
-
http.delete_uri(url, format)
|
|
59
|
+
def delete(url, format, options={}, &block)
|
|
60
|
+
http.delete_uri(options.merge(:uri => url, :as => format), &block)
|
|
64
61
|
self
|
|
65
62
|
end
|
|
66
63
|
|
|
@@ -13,27 +13,27 @@ module Roar
|
|
|
13
13
|
# @see http://rubydoc.info/gems/faraday/file/README.md Faraday README
|
|
14
14
|
class Faraday
|
|
15
15
|
|
|
16
|
-
def get_uri(uri, as)
|
|
16
|
+
def get_uri(uri, as, *args)
|
|
17
17
|
build_connection(uri, as).get
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
-
def post_uri(uri, body, as)
|
|
20
|
+
def post_uri(uri, body, as, *args)
|
|
21
21
|
build_connection(uri, as).post(nil, body)
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
-
def put_uri(uri, body, as)
|
|
24
|
+
def put_uri(uri, body, as, *args)
|
|
25
25
|
build_connection(uri, as).put(nil, body)
|
|
26
26
|
end
|
|
27
27
|
|
|
28
|
-
def patch_uri(uri, body, as)
|
|
28
|
+
def patch_uri(uri, body, as, *args)
|
|
29
29
|
build_connection(uri, as).patch(nil, body)
|
|
30
30
|
end
|
|
31
31
|
|
|
32
|
-
def delete_uri(uri, as)
|
|
32
|
+
def delete_uri(uri, as, *args)
|
|
33
33
|
build_connection(uri, as).delete
|
|
34
34
|
end
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
private
|
|
37
37
|
|
|
38
38
|
def build_connection(uri, as)
|
|
39
39
|
::Faraday::Connection.new(
|
|
@@ -5,49 +5,112 @@ module Roar
|
|
|
5
5
|
module Representer
|
|
6
6
|
# Implements the (HTTP) transport interface with Net::HTTP.
|
|
7
7
|
module Transport
|
|
8
|
-
#
|
|
8
|
+
# Low-level interface for HTTP. The #get_uri and friends accept an options and an optional block, invoke
|
|
9
|
+
# the HTTP request and return the request object.
|
|
10
|
+
#
|
|
11
|
+
# The following options are available:
|
|
9
12
|
class NetHTTP
|
|
10
|
-
# TODO:
|
|
11
|
-
|
|
12
|
-
|
|
13
|
+
class Request # TODO: implement me.
|
|
14
|
+
def initialize(options)
|
|
15
|
+
@uri = parse_uri(options[:uri]) # TODO: add :uri.
|
|
16
|
+
@as = options[:as]
|
|
17
|
+
@body = options[:body]
|
|
18
|
+
@options = options
|
|
19
|
+
|
|
20
|
+
@http = Net::HTTP.new(uri.host, uri.port)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def call(what)
|
|
24
|
+
@req = what.new(uri.request_uri)
|
|
25
|
+
|
|
26
|
+
# if options[:ssl]
|
|
27
|
+
# uri.port = Net::HTTP.https_default_port()
|
|
28
|
+
# end
|
|
29
|
+
https!
|
|
30
|
+
basic_auth!
|
|
31
|
+
|
|
32
|
+
req.content_type = as
|
|
33
|
+
req["accept"] = as # TODO: test me. # DISCUSS: if Accept is not set, rails treats this request as as "text/html".
|
|
34
|
+
req.body = body if body
|
|
35
|
+
|
|
36
|
+
yield req if block_given?
|
|
37
|
+
|
|
38
|
+
http.request(req).tap do |res|
|
|
39
|
+
raise UnauthorizedError if res.is_a?(Net::HTTPUnauthorized) # FIXME: make this better. # DISCUSS: abstract all that crap here?
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def get
|
|
44
|
+
call(Net::HTTP::Get)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
attr_reader :uri, :as, :body, :options, :req, :http
|
|
49
|
+
|
|
50
|
+
def parse_uri(url)
|
|
51
|
+
uri = URI(url)
|
|
52
|
+
raise "Incorrect URL `#{url}`. Maybe you forgot http://?" if uri.instance_of?(URI::Generic)
|
|
53
|
+
uri
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def https!
|
|
57
|
+
return unless uri.scheme == 'https'
|
|
58
|
+
|
|
59
|
+
@http.use_ssl = true
|
|
60
|
+
@http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def basic_auth!
|
|
64
|
+
return unless options[:basic_auth]
|
|
65
|
+
|
|
66
|
+
@req.basic_auth(*options[:basic_auth])
|
|
67
|
+
end
|
|
13
68
|
end
|
|
14
69
|
|
|
15
|
-
|
|
16
|
-
|
|
70
|
+
|
|
71
|
+
def get_uri(*options, &block)
|
|
72
|
+
call(Net::HTTP::Get, *options, &block)
|
|
17
73
|
end
|
|
18
74
|
|
|
19
|
-
def
|
|
20
|
-
|
|
75
|
+
def post_uri(*options, &block)
|
|
76
|
+
call(Net::HTTP::Post, *options, &block)
|
|
21
77
|
end
|
|
22
78
|
|
|
23
|
-
def
|
|
24
|
-
|
|
79
|
+
def put_uri(*options, &block)
|
|
80
|
+
call(Net::HTTP::Put, *options, &block)
|
|
25
81
|
end
|
|
26
82
|
|
|
27
|
-
def delete_uri(
|
|
28
|
-
|
|
83
|
+
def delete_uri(*options, &block)
|
|
84
|
+
call(Net::HTTP::Delete, *options, &block)
|
|
29
85
|
end
|
|
30
86
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
uri = parse_uri(uri)
|
|
35
|
-
http = Net::HTTP.new(uri.host, uri.port)
|
|
36
|
-
req = what.new(uri.request_uri)
|
|
87
|
+
def patch_uri(*options, &block)
|
|
88
|
+
call(Net::HTTP::Patch, *options, &block)
|
|
89
|
+
end
|
|
37
90
|
|
|
91
|
+
private
|
|
92
|
+
def call(what, *args, &block)
|
|
93
|
+
options = handle_deprecated_args(args)
|
|
94
|
+
# TODO: generically handle return codes.
|
|
95
|
+
Request.new(options).call(what, &block)
|
|
96
|
+
end
|
|
38
97
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
98
|
+
def handle_deprecated_args(args) # TODO: remove in 1.0.
|
|
99
|
+
if args.size > 1
|
|
100
|
+
warn %{DEPRECATION WARNING: #get_uri, #post_uri, #put_uri, #delete_uri and #patch_uri no longer accept positional arguments. Please call them as follows:
|
|
101
|
+
get_uri(uri: "http://localhost/songs", as: "application/json")
|
|
102
|
+
post_uri(uri: "http://localhost/songs", as: "application/json", body: "{'id': 1}")
|
|
103
|
+
Thank you and have a lovely day.}
|
|
104
|
+
return {:uri => args[0], :as => args[1]} if args.size == 2
|
|
105
|
+
return {:uri => args[0], :as => args[2], :body => args[1]}
|
|
106
|
+
end
|
|
42
107
|
|
|
43
|
-
|
|
108
|
+
args.first
|
|
44
109
|
end
|
|
45
110
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
uri
|
|
50
|
-
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
class UnauthorizedError < RuntimeError # TODO: raise this from Faraday, too.
|
|
51
114
|
end
|
|
52
115
|
end
|
|
53
116
|
end
|
data/lib/roar/version.rb
CHANGED
data/roar.gemspec
CHANGED
|
@@ -23,7 +23,7 @@ Gem::Specification.new do |s|
|
|
|
23
23
|
|
|
24
24
|
s.add_development_dependency "rake", ">= 0.10.1"
|
|
25
25
|
s.add_development_dependency "test_xml", ">= 0.1.6"
|
|
26
|
-
s.add_development_dependency "minitest", "
|
|
26
|
+
s.add_development_dependency "minitest", "= 5.0.0"
|
|
27
27
|
s.add_development_dependency "sinatra"
|
|
28
28
|
s.add_development_dependency "virtus"
|
|
29
29
|
s.add_development_dependency "faraday"
|
|
@@ -4,8 +4,6 @@ require 'roar/representer/json/collection_json'
|
|
|
4
4
|
class CollectionJsonTest < MiniTest::Spec
|
|
5
5
|
let(:song) { OpenStruct.new(:title => "scarifice", :length => 43) }
|
|
6
6
|
|
|
7
|
-
# FIXME: test absence of ::items, ::template, etc.
|
|
8
|
-
|
|
9
7
|
representer_for([Roar::Representer::JSON::CollectionJSON]) do
|
|
10
8
|
version "1.0"
|
|
11
9
|
href { "//songs/" }
|
|
@@ -50,7 +48,7 @@ class CollectionJsonTest < MiniTest::Spec
|
|
|
50
48
|
},
|
|
51
49
|
|
|
52
50
|
"queries"=>[
|
|
53
|
-
{:rel=>:search, :href=>"//search",
|
|
51
|
+
{:rel=>:search, :href=>"//search",
|
|
54
52
|
:data=>[
|
|
55
53
|
{:name=>"q", :value=>""}
|
|
56
54
|
]
|
data/test/hal_json_test.rb
CHANGED
|
@@ -120,7 +120,9 @@ class HalJsonTest < MiniTest::Spec
|
|
|
120
120
|
it "doesn't require _links and _embedded to be present" do
|
|
121
121
|
@album.from_json("{\"id\":2}")
|
|
122
122
|
assert_equal 2, @album.id
|
|
123
|
-
|
|
123
|
+
|
|
124
|
+
# in newer representables, this is not overwritten to an empty [] anymore.
|
|
125
|
+
assert_equal ["Beer"], @album.songs.map(&:title)
|
|
124
126
|
@album.links.must_equal({})
|
|
125
127
|
end
|
|
126
128
|
end
|
|
@@ -10,6 +10,8 @@ class HttpVerbsTest < MiniTest::Spec
|
|
|
10
10
|
attr_accessor :name, :label
|
|
11
11
|
end
|
|
12
12
|
|
|
13
|
+
let (:band) { OpenStruct.new(:name => "bodyjar").extend(Roar::Representer::Feature::HttpVerbs, BandRepresenter) }
|
|
14
|
+
|
|
13
15
|
|
|
14
16
|
describe "HttpVerbs" do
|
|
15
17
|
before do
|
|
@@ -105,7 +107,42 @@ class HttpVerbsTest < MiniTest::Spec
|
|
|
105
107
|
end
|
|
106
108
|
end
|
|
107
109
|
|
|
108
|
-
|
|
110
|
+
describe "HTTPS and Authentication" do
|
|
111
|
+
let (:song) { OpenStruct.new(:name => "bodyjar").extend(Roar::Representer::Feature::HttpVerbs, BandRepresenter) }
|
|
112
|
+
|
|
113
|
+
describe "Basic Auth: passing manually" do
|
|
114
|
+
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
describe "HTTPS: passing manually" do
|
|
118
|
+
verbs do |verb|
|
|
119
|
+
it "allows #{verb}" do
|
|
120
|
+
song.send(verb, "https://localhost:8443/bands/bodyjar", "application/json")
|
|
121
|
+
|
|
122
|
+
if verb == "delete"
|
|
123
|
+
song.name.must_equal "bodyjar"
|
|
124
|
+
else
|
|
125
|
+
song.name.must_equal "Bodyjar"
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
describe "HTTPS+Basic Auth: passing manually" do
|
|
132
|
+
it "allows GET" do
|
|
133
|
+
song.get("https://localhost:8443/protected", "application/json", :basic_auth => [:admin, :password])
|
|
134
|
+
|
|
135
|
+
song.name.must_equal "Bodyjar"
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
109
139
|
|
|
140
|
+
describe "request customization" do
|
|
141
|
+
it "yields the request object" do
|
|
142
|
+
band.get("http://localhost:4567/cookies", "application/json") do |req|
|
|
143
|
+
req.add_field("Cookie", "Yumyum")
|
|
144
|
+
end.name.must_equal "Bodyjar"
|
|
145
|
+
end
|
|
146
|
+
end
|
|
110
147
|
end
|
|
111
148
|
end
|
data/test/integration/runner.rb
CHANGED
|
@@ -1,30 +1,41 @@
|
|
|
1
1
|
require "integration/band_representer"
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
require 'sinatra/runner'
|
|
3
|
+
|
|
4
|
+
class ServerRunner < Sinatra::Runner
|
|
4
5
|
def app_file
|
|
5
6
|
File.expand_path("../server.rb", __FILE__)
|
|
6
7
|
end
|
|
7
8
|
|
|
8
|
-
def
|
|
9
|
-
|
|
10
|
-
puts @pipe = IO.popen(command)
|
|
11
|
-
sleep 2
|
|
9
|
+
def command
|
|
10
|
+
"bundle exec ruby #{app_file} -p #{port} -e production"
|
|
12
11
|
end
|
|
13
12
|
|
|
13
|
+
def ping_path # to be overwritten
|
|
14
|
+
'/ping'
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
class SslServerRunner < ServerRunner
|
|
14
19
|
def command
|
|
15
|
-
"
|
|
20
|
+
"bundle exec ruby #{File.expand_path("../ssl_server.rb", __FILE__)}"
|
|
16
21
|
end
|
|
17
22
|
|
|
18
|
-
def
|
|
19
|
-
|
|
20
|
-
|
|
23
|
+
def port
|
|
24
|
+
8443
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def protocol
|
|
28
|
+
"https"
|
|
21
29
|
end
|
|
22
30
|
end
|
|
23
|
-
runner = ServerRunner.new
|
|
24
|
-
#at_exit { puts "killing it:"; runner.kill }
|
|
25
31
|
|
|
32
|
+
runner = ServerRunner.new
|
|
26
33
|
runner.run
|
|
27
34
|
|
|
35
|
+
ssl_runner = SslServerRunner.new
|
|
36
|
+
ssl_runner.run
|
|
37
|
+
|
|
28
38
|
MiniTest::Unit.after_tests do
|
|
29
39
|
runner.kill
|
|
40
|
+
ssl_runner.kill
|
|
30
41
|
end
|
data/test/integration/server.rb
CHANGED
|
@@ -2,6 +2,7 @@ require "bundler/setup"
|
|
|
2
2
|
require "sinatra"
|
|
3
3
|
require "ostruct"
|
|
4
4
|
require "roar/representer/json"
|
|
5
|
+
require "sinatra/multi_route"
|
|
5
6
|
|
|
6
7
|
require File.expand_path("../band_representer.rb", __FILE__)
|
|
7
8
|
|
|
@@ -75,3 +76,34 @@ end
|
|
|
75
76
|
delete '/bands/metallica' do
|
|
76
77
|
status 204
|
|
77
78
|
end
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
helpers do
|
|
82
|
+
def protected!
|
|
83
|
+
return if authorized?
|
|
84
|
+
headers['WWW-Authenticate'] = 'Basic realm="Restricted Area"'
|
|
85
|
+
halt 401, "Not authorized\n"
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def authorized?
|
|
89
|
+
@auth ||= Rack::Auth::Basic::Request.new(request.env)
|
|
90
|
+
@auth.provided? and @auth.basic? and @auth.credentials and @auth.credentials == ['admin', 'password']
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
route :get, :post, :put, :delete, "/protected/bands/bodyjar" do
|
|
95
|
+
protected!
|
|
96
|
+
|
|
97
|
+
OpenStruct.new(:name => "Bodyjar").
|
|
98
|
+
extend(Integration::BandRepresenter).
|
|
99
|
+
to_json
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
route :get, :post, :put, :delete, "/cookies" do
|
|
103
|
+
raise "No cookies!" unless request.env["HTTP_COOKIE"] == "Yumyum"
|
|
104
|
+
%{{"name": "Bodyjar"}}
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
get "/ping" do
|
|
108
|
+
"1"
|
|
109
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
require 'sinatra/base'
|
|
2
|
+
require 'webrick'
|
|
3
|
+
require 'webrick/https'
|
|
4
|
+
require 'openssl'
|
|
5
|
+
require 'sinatra/multi_route'
|
|
6
|
+
|
|
7
|
+
name = "/C=US/ST=SomeState/L=SomeCity/O=Organization/OU=Unit/CN=localhost"
|
|
8
|
+
ca = OpenSSL::X509::Name.parse(name)
|
|
9
|
+
key = OpenSSL::PKey::RSA.new(1024)
|
|
10
|
+
crt = OpenSSL::X509::Certificate.new
|
|
11
|
+
crt.version = 2
|
|
12
|
+
crt.serial = 1
|
|
13
|
+
crt.subject = ca
|
|
14
|
+
crt.issuer = ca
|
|
15
|
+
crt.public_key = key.public_key
|
|
16
|
+
crt.not_before = Time.now
|
|
17
|
+
crt.not_after = Time.now + 1 * 365 * 24 * 60 * 60 # 1 year
|
|
18
|
+
webrick_options = {
|
|
19
|
+
:Port => 8443,
|
|
20
|
+
:SSLEnable => true,
|
|
21
|
+
:SSLVerifyClient => OpenSSL::SSL::VERIFY_NONE,
|
|
22
|
+
:SSLCertificate => crt,
|
|
23
|
+
:SSLPrivateKey => key,
|
|
24
|
+
:SSLCertName => [[ "CN", WEBrick::Utils::getservername ]],
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
class SslServer < Sinatra::Base
|
|
28
|
+
register Sinatra::MultiRoute
|
|
29
|
+
|
|
30
|
+
get '/ping' do
|
|
31
|
+
"1"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
route :get, :post, :put, :delete, "/bands/bodyjar" do
|
|
35
|
+
%{{"name": "Bodyjar"}}
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# FIXME: redundant with server.rb.
|
|
39
|
+
helpers do
|
|
40
|
+
def protected!
|
|
41
|
+
return if authorized?
|
|
42
|
+
headers['WWW-Authenticate'] = 'Basic realm="Restricted Area"'
|
|
43
|
+
halt 401, "Not authorized\n"
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def authorized?
|
|
47
|
+
@auth ||= Rack::Auth::Basic::Request.new(request.env)
|
|
48
|
+
@auth.provided? and @auth.basic? and @auth.credentials and @auth.credentials == ['admin', 'password']
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
get "/protected" do
|
|
53
|
+
protected!
|
|
54
|
+
|
|
55
|
+
%{{"name": "Bodyjar"}}
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
server = ::Rack::Handler::WEBrick
|
|
59
|
+
|
|
60
|
+
server.run(SslServer, webrick_options)
|
|
@@ -5,44 +5,80 @@ class NetHTTPTransportTest < MiniTest::Spec
|
|
|
5
5
|
let(:url) { "http://localhost:4567/method" }
|
|
6
6
|
let(:body) { "booty" }
|
|
7
7
|
let(:as) { "application/xml" }
|
|
8
|
-
|
|
9
|
-
@transport = Roar::Representer::Transport::NetHTTP.new
|
|
10
|
-
end
|
|
8
|
+
let (:transport) { Roar::Representer::Transport::NetHTTP.new }
|
|
11
9
|
|
|
12
10
|
it "#get_uri returns response" do
|
|
13
|
-
|
|
11
|
+
transport.get_uri(:uri => url, :as => as).must_match_net_response :get, url, as
|
|
14
12
|
end
|
|
15
13
|
|
|
16
14
|
it "#post_uri returns response" do
|
|
17
|
-
|
|
15
|
+
transport.post_uri(:uri => url, :as => as, :body => body).must_match_net_response :post, url, as, body
|
|
18
16
|
end
|
|
19
17
|
|
|
20
18
|
it "#put_uri returns response" do
|
|
21
|
-
|
|
19
|
+
transport.put_uri(:uri => url, :as => as, :body => body).must_match_net_response :put, url, as, body
|
|
22
20
|
end
|
|
23
21
|
|
|
24
22
|
it "#delete_uri returns response" do
|
|
25
|
-
|
|
23
|
+
transport.delete_uri(:uri => url, :as => as).must_match_net_response :delete, url, as
|
|
26
24
|
end
|
|
27
25
|
|
|
28
26
|
it "#patch_uri returns response" do
|
|
29
|
-
|
|
27
|
+
transport.patch_uri(:uri => url, :as => as, :body => body).must_match_net_response :patch, url, as, body
|
|
30
28
|
end
|
|
31
29
|
|
|
32
30
|
it "complains with invalid URL" do
|
|
33
31
|
assert_raises RuntimeError do
|
|
34
|
-
|
|
32
|
+
transport.get_uri(:uri => "example.com", :as => as)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# TODO: test all verbs.
|
|
37
|
+
describe "request customization" do
|
|
38
|
+
#verbs do |verb|
|
|
39
|
+
verb = "get"
|
|
40
|
+
it "#{verb} yields the request object" do
|
|
41
|
+
transport.send("#{verb}_uri", :uri => "http://localhost:4567/cookies", :as => "application/json") do |req|
|
|
42
|
+
req.add_field("Cookie", "Yumyum")
|
|
43
|
+
end.body.must_equal %{{"name": "Bodyjar"}}
|
|
44
|
+
end
|
|
45
|
+
#end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
describe "basic auth" do
|
|
49
|
+
it "raises when no credentials provided" do
|
|
50
|
+
assert_raises Roar::Representer::Transport::UnauthorizedError do
|
|
51
|
+
transport.get_uri(:uri => "http://localhost:4567/protected/bands/bodyjar", :as => "application/json")
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it "raises when wrong credentials provided" do
|
|
56
|
+
assert_raises Roar::Representer::Transport::UnauthorizedError do
|
|
57
|
+
transport.get_uri(:uri => "http://localhost:4567/protected/bands/bodyjar", :as => "application/json", :basic_auth => ["admin", "wrong--!!!--password"])
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
it "what" do
|
|
62
|
+
transport.get_uri(:uri => "http://localhost:4567/protected/bands/bodyjar", :as => "application/json", :basic_auth => ["admin", "password"])
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
describe "deprecations" do
|
|
67
|
+
it "old GET API still works" do
|
|
68
|
+
transport.get_uri(url, as).must_match_net_response :get, url, as
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
it "old POST API still works" do
|
|
72
|
+
transport.post_uri(url, body, as).must_match_net_response :post, url, as, body
|
|
35
73
|
end
|
|
36
74
|
end
|
|
37
75
|
end
|
|
38
76
|
|
|
39
77
|
module MiniTest::Assertions
|
|
40
|
-
|
|
41
78
|
def assert_net_response(type, response, url, as, body = nil)
|
|
42
79
|
# TODO: Assert headers
|
|
43
80
|
assert_equal "<method>#{type}#{(' - ' + body) if body}</method>", response.body
|
|
44
81
|
end
|
|
45
|
-
|
|
46
82
|
end
|
|
47
83
|
|
|
48
84
|
Net::HTTPOK.infect_an_assertion :assert_net_response, :must_match_net_response
|
data/test/test_helper.rb
CHANGED
metadata
CHANGED
|
@@ -1,125 +1,125 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: roar
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.12.
|
|
4
|
+
version: 0.12.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Nick Sutterer
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2014-02-28 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: representable
|
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
|
16
16
|
requirements:
|
|
17
|
-
- -
|
|
17
|
+
- - ">="
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
19
|
version: 1.6.0
|
|
20
20
|
type: :runtime
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
|
-
- -
|
|
24
|
+
- - ">="
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
26
|
version: 1.6.0
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
28
|
name: rake
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
30
30
|
requirements:
|
|
31
|
-
- -
|
|
31
|
+
- - ">="
|
|
32
32
|
- !ruby/object:Gem::Version
|
|
33
33
|
version: 0.10.1
|
|
34
34
|
type: :development
|
|
35
35
|
prerelease: false
|
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
37
|
requirements:
|
|
38
|
-
- -
|
|
38
|
+
- - ">="
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
40
|
version: 0.10.1
|
|
41
41
|
- !ruby/object:Gem::Dependency
|
|
42
42
|
name: test_xml
|
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
|
44
44
|
requirements:
|
|
45
|
-
- -
|
|
45
|
+
- - ">="
|
|
46
46
|
- !ruby/object:Gem::Version
|
|
47
47
|
version: 0.1.6
|
|
48
48
|
type: :development
|
|
49
49
|
prerelease: false
|
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
|
51
51
|
requirements:
|
|
52
|
-
- -
|
|
52
|
+
- - ">="
|
|
53
53
|
- !ruby/object:Gem::Version
|
|
54
54
|
version: 0.1.6
|
|
55
55
|
- !ruby/object:Gem::Dependency
|
|
56
56
|
name: minitest
|
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
|
58
58
|
requirements:
|
|
59
|
-
- - '
|
|
59
|
+
- - '='
|
|
60
60
|
- !ruby/object:Gem::Version
|
|
61
61
|
version: 5.0.0
|
|
62
62
|
type: :development
|
|
63
63
|
prerelease: false
|
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
|
65
65
|
requirements:
|
|
66
|
-
- - '
|
|
66
|
+
- - '='
|
|
67
67
|
- !ruby/object:Gem::Version
|
|
68
68
|
version: 5.0.0
|
|
69
69
|
- !ruby/object:Gem::Dependency
|
|
70
70
|
name: sinatra
|
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
|
72
72
|
requirements:
|
|
73
|
-
- -
|
|
73
|
+
- - ">="
|
|
74
74
|
- !ruby/object:Gem::Version
|
|
75
75
|
version: '0'
|
|
76
76
|
type: :development
|
|
77
77
|
prerelease: false
|
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
|
79
79
|
requirements:
|
|
80
|
-
- -
|
|
80
|
+
- - ">="
|
|
81
81
|
- !ruby/object:Gem::Version
|
|
82
82
|
version: '0'
|
|
83
83
|
- !ruby/object:Gem::Dependency
|
|
84
84
|
name: virtus
|
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
|
86
86
|
requirements:
|
|
87
|
-
- -
|
|
87
|
+
- - ">="
|
|
88
88
|
- !ruby/object:Gem::Version
|
|
89
89
|
version: '0'
|
|
90
90
|
type: :development
|
|
91
91
|
prerelease: false
|
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
|
93
93
|
requirements:
|
|
94
|
-
- -
|
|
94
|
+
- - ">="
|
|
95
95
|
- !ruby/object:Gem::Version
|
|
96
96
|
version: '0'
|
|
97
97
|
- !ruby/object:Gem::Dependency
|
|
98
98
|
name: faraday
|
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
|
100
100
|
requirements:
|
|
101
|
-
- -
|
|
101
|
+
- - ">="
|
|
102
102
|
- !ruby/object:Gem::Version
|
|
103
103
|
version: '0'
|
|
104
104
|
type: :development
|
|
105
105
|
prerelease: false
|
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
|
107
107
|
requirements:
|
|
108
|
-
- -
|
|
108
|
+
- - ">="
|
|
109
109
|
- !ruby/object:Gem::Version
|
|
110
110
|
version: '0'
|
|
111
111
|
- !ruby/object:Gem::Dependency
|
|
112
112
|
name: json
|
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
|
114
114
|
requirements:
|
|
115
|
-
- -
|
|
115
|
+
- - ">="
|
|
116
116
|
- !ruby/object:Gem::Version
|
|
117
117
|
version: '0'
|
|
118
118
|
type: :development
|
|
119
119
|
prerelease: false
|
|
120
120
|
version_requirements: !ruby/object:Gem::Requirement
|
|
121
121
|
requirements:
|
|
122
|
-
- -
|
|
122
|
+
- - ">="
|
|
123
123
|
- !ruby/object:Gem::Version
|
|
124
124
|
version: '0'
|
|
125
125
|
description: Streamlines the development of RESTful, resource-oriented architectures
|
|
@@ -130,8 +130,8 @@ executables: []
|
|
|
130
130
|
extensions: []
|
|
131
131
|
extra_rdoc_files: []
|
|
132
132
|
files:
|
|
133
|
-
- .gitignore
|
|
134
|
-
- .travis.yml
|
|
133
|
+
- ".gitignore"
|
|
134
|
+
- ".travis.yml"
|
|
135
135
|
- CHANGES.markdown
|
|
136
136
|
- Gemfile
|
|
137
137
|
- LICENSE
|
|
@@ -140,9 +140,7 @@ files:
|
|
|
140
140
|
- TODO.markdown
|
|
141
141
|
- examples/example.rb
|
|
142
142
|
- examples/example_server.rb
|
|
143
|
-
- gemfiles/Gemfile.representable-1.
|
|
144
|
-
- gemfiles/Gemfile.representable-1.5.ruby-1.9
|
|
145
|
-
- gemfiles/Gemfile.representable-1.6.ruby-1.9
|
|
143
|
+
- gemfiles/Gemfile.representable-1.7
|
|
146
144
|
- lib/roar.rb
|
|
147
145
|
- lib/roar/decorator.rb
|
|
148
146
|
- lib/roar/representer.rb
|
|
@@ -165,13 +163,14 @@ files:
|
|
|
165
163
|
- test/faraday_http_transport_test.rb
|
|
166
164
|
- test/hal_json_test.rb
|
|
167
165
|
- test/hal_links_test.rb
|
|
168
|
-
- test/
|
|
166
|
+
- test/http_verbs_test.rb
|
|
169
167
|
- test/hypermedia_feature_test.rb
|
|
170
168
|
- test/hypermedia_test.rb
|
|
171
169
|
- test/integration/Gemfile
|
|
172
170
|
- test/integration/band_representer.rb
|
|
173
171
|
- test/integration/runner.rb
|
|
174
172
|
- test/integration/server.rb
|
|
173
|
+
- test/integration/ssl_server.rb
|
|
175
174
|
- test/json_representer_test.rb
|
|
176
175
|
- test/net_http_transport_test.rb
|
|
177
176
|
- test/representer_test.rb
|
|
@@ -187,18 +186,38 @@ require_paths:
|
|
|
187
186
|
- lib
|
|
188
187
|
required_ruby_version: !ruby/object:Gem::Requirement
|
|
189
188
|
requirements:
|
|
190
|
-
- -
|
|
189
|
+
- - ">="
|
|
191
190
|
- !ruby/object:Gem::Version
|
|
192
191
|
version: '0'
|
|
193
192
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
194
193
|
requirements:
|
|
195
|
-
- -
|
|
194
|
+
- - ">="
|
|
196
195
|
- !ruby/object:Gem::Version
|
|
197
196
|
version: '0'
|
|
198
197
|
requirements: []
|
|
199
198
|
rubyforge_project: roar
|
|
200
|
-
rubygems_version: 2.
|
|
199
|
+
rubygems_version: 2.2.1
|
|
201
200
|
signing_key:
|
|
202
201
|
specification_version: 4
|
|
203
202
|
summary: Resource-oriented architectures in Ruby.
|
|
204
|
-
test_files:
|
|
203
|
+
test_files:
|
|
204
|
+
- test/client_test.rb
|
|
205
|
+
- test/coercion_feature_test.rb
|
|
206
|
+
- test/collection_json_test.rb
|
|
207
|
+
- test/decorator_test.rb
|
|
208
|
+
- test/faraday_http_transport_test.rb
|
|
209
|
+
- test/hal_json_test.rb
|
|
210
|
+
- test/hal_links_test.rb
|
|
211
|
+
- test/http_verbs_test.rb
|
|
212
|
+
- test/hypermedia_feature_test.rb
|
|
213
|
+
- test/hypermedia_test.rb
|
|
214
|
+
- test/integration/Gemfile
|
|
215
|
+
- test/integration/band_representer.rb
|
|
216
|
+
- test/integration/runner.rb
|
|
217
|
+
- test/integration/server.rb
|
|
218
|
+
- test/integration/ssl_server.rb
|
|
219
|
+
- test/json_representer_test.rb
|
|
220
|
+
- test/net_http_transport_test.rb
|
|
221
|
+
- test/representer_test.rb
|
|
222
|
+
- test/test_helper.rb
|
|
223
|
+
- test/xml_representer_test.rb
|