roar 1.0.0.beta2 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +1 -1
- data/CHANGES.markdown +1 -0
- data/README.markdown +43 -0
- data/lib/roar/json/json_api.rb +25 -5
- data/lib/roar/transport/net_http.rb +8 -16
- data/lib/roar/transport/net_http/request.rb +6 -1
- data/lib/roar/version.rb +1 -1
- data/test/http_verbs_test.rb +5 -2
- data/test/json_api_test.rb +58 -0
- data/test/net_http_transport_test.rb +6 -12
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9b58dcace50d4224e36f81444ce64ca73dfec11a
|
4
|
+
data.tar.gz: 419929c85da4aef8a13243364b1c918acfd5312c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c090f6a70e513ab5ad28155a9c21a453f0e37a289acb2af3a7ade51484d8cc1a76d0c14a02cf6343b054fb9ef2dd392dcae2383b7d219c77e43513d370369c62
|
7
|
+
data.tar.gz: 3253825814038580793eebb22da120b3af1ce69723d81283b65953e5b30806a91b7627ebb370de30b46440b07b749465ec2db1a9235b884b5de132becf2d4c84
|
data/.travis.yml
CHANGED
data/CHANGES.markdown
CHANGED
@@ -6,6 +6,7 @@
|
|
6
6
|
* `Roar::Representer::Feature::Hypermedia` --> `Roar::Hypermedia`
|
7
7
|
* `Roar::Representer::JSON` --> `Roar::JSON`
|
8
8
|
* Removed positional arguments for `HttpVerbs#get` and friends.
|
9
|
+
* `HttpVerbs#get` and friends will now raise an exception when the response code is not 2xx. You can get the original response via `Roar::Transport::Error#response`. Thanks to @paulccarey for all his inspiring work and patience!
|
9
10
|
|
10
11
|
## Added
|
11
12
|
|
data/README.markdown
CHANGED
@@ -470,6 +470,39 @@ compound do
|
|
470
470
|
end
|
471
471
|
```
|
472
472
|
|
473
|
+
### Meta Data
|
474
|
+
|
475
|
+
Meta data can be included into the rendered collection document in two ways. Please note that parsing the `meta` field is not implemented, yet, as I wasn't sure if people need it.
|
476
|
+
|
477
|
+
You can define meta data on your collection object and then let Roar compile it.
|
478
|
+
|
479
|
+
```ruby
|
480
|
+
module SongsRepresenter
|
481
|
+
# ..
|
482
|
+
|
483
|
+
meta do
|
484
|
+
property :page
|
485
|
+
property :total
|
486
|
+
end
|
487
|
+
```
|
488
|
+
|
489
|
+
Your collection object has to expose those methods.
|
490
|
+
|
491
|
+
```ruby
|
492
|
+
collection.page #=> 1
|
493
|
+
collection.total #=> 12
|
494
|
+
```
|
495
|
+
|
496
|
+
This will render the `{"meta": {"page": 1, "total": 12}}` hash into the JSON-API document.
|
497
|
+
|
498
|
+
Another way is to provide the _complete_ meta data hash when rendering. You must not define any `meta` properties in the representer when using this approach.
|
499
|
+
|
500
|
+
```ruby
|
501
|
+
collection.to_json("meta" => {page: params["page"], total: collection.size})
|
502
|
+
```
|
503
|
+
|
504
|
+
If you need more functionality (and parsing), please let us know.
|
505
|
+
|
473
506
|
### Usage
|
474
507
|
|
475
508
|
As JSON-API per definition can represent singular models and collections you have two entry points.
|
@@ -641,6 +674,16 @@ song.get(uri: "http://localhost:4567/songs/1") do |req|
|
|
641
674
|
end
|
642
675
|
```
|
643
676
|
|
677
|
+
### Error handling
|
678
|
+
|
679
|
+
In case of a non-2xx response status, `#get` and friends raise a `Roar::Transport::Error` exception. The original response can be accessed as follows.
|
680
|
+
|
681
|
+
```ruby
|
682
|
+
song.get(uri: "http://localhost/songs1") # not-existing.
|
683
|
+
rescue Roar::Transport::Error => exception
|
684
|
+
exception.response.code #=> 404
|
685
|
+
```
|
686
|
+
|
644
687
|
## XML
|
645
688
|
|
646
689
|
Roar also comes with XML support.
|
data/lib/roar/json/json_api.rb
CHANGED
@@ -26,6 +26,7 @@ module Roar
|
|
26
26
|
items extend: singular, :parse_strategy => :sync
|
27
27
|
|
28
28
|
representable_attrs[:resource_representer] = singular.representable_attrs[:resource_representer]
|
29
|
+
representable_attrs[:meta_representer] = singular.representable_attrs[:meta_representer] # DISCUSS: do we need that?
|
29
30
|
representable_attrs[:_wrap] = singular.representable_attrs[:_wrap]
|
30
31
|
end
|
31
32
|
end
|
@@ -99,6 +100,10 @@ module Roar
|
|
99
100
|
def compound(&block)
|
100
101
|
nested(:linked, &block)
|
101
102
|
end
|
103
|
+
|
104
|
+
def meta(&block)
|
105
|
+
representable_attrs[:meta_representer] = Class.new(Roar::Decorator, &block)
|
106
|
+
end
|
102
107
|
end
|
103
108
|
end
|
104
109
|
|
@@ -111,7 +116,7 @@ module Roar
|
|
111
116
|
return res if options[:only_body]
|
112
117
|
# this is the only "dirty" part: this module is always included in the Singular document representer, when used in collection, we don't want it to do the extra work. this mechanism here might be changed soon.
|
113
118
|
|
114
|
-
to_document(res)
|
119
|
+
to_document(res, options)
|
115
120
|
end
|
116
121
|
|
117
122
|
def from_hash(hash, options={})
|
@@ -122,8 +127,9 @@ module Roar
|
|
122
127
|
end
|
123
128
|
|
124
129
|
private
|
125
|
-
def to_document(res)
|
126
|
-
links
|
130
|
+
def to_document(res, options)
|
131
|
+
links = render_links
|
132
|
+
meta = render_meta(options)
|
127
133
|
# FIXME: provide two different #to_document
|
128
134
|
|
129
135
|
if res.is_a?(Array)
|
@@ -134,7 +140,8 @@ module Roar
|
|
134
140
|
|
135
141
|
{representable_attrs[:_wrap] => res}.tap do |doc|
|
136
142
|
doc.merge!(links)
|
137
|
-
doc.merge!(
|
143
|
+
doc.merge!(meta)
|
144
|
+
doc.merge!("linked" => compound) if compound && compound.size > 0 # FIXME: make that like the above line.
|
138
145
|
end
|
139
146
|
end
|
140
147
|
|
@@ -173,13 +180,26 @@ module Roar
|
|
173
180
|
compound
|
174
181
|
end
|
175
182
|
|
183
|
+
def render_links
|
184
|
+
representable_attrs[:resource_representer].new(represented).to_hash # creates links: section.
|
185
|
+
end
|
186
|
+
|
187
|
+
def render_meta(options)
|
188
|
+
# TODO: this will call collection.page etc, directly on the collection. we could allow using a "meta"
|
189
|
+
# object to hold this data.
|
190
|
+
# `meta call_meta: true` or something
|
191
|
+
return {"meta" => options["meta"]} if options["meta"]
|
192
|
+
return {} unless representer = representable_attrs[:meta_representer]
|
193
|
+
{"meta" => representer.new(represented).extend(Representable::Hash).to_hash}
|
194
|
+
end
|
195
|
+
|
176
196
|
|
177
197
|
module Collection
|
178
198
|
include Document
|
179
199
|
|
180
200
|
def to_hash(options={})
|
181
201
|
res = super(options.merge(:only_body => true))
|
182
|
-
to_document(res)
|
202
|
+
to_document(res, options)
|
183
203
|
end
|
184
204
|
|
185
205
|
def from_hash(hash, options={})
|
@@ -30,28 +30,20 @@ module Roar
|
|
30
30
|
end
|
31
31
|
|
32
32
|
private
|
33
|
-
def call(what,
|
34
|
-
options = handle_deprecated_args(args)
|
33
|
+
def call(what, options, &block)
|
35
34
|
# TODO: generically handle return codes.
|
36
35
|
Request.new(options).call(what, &block)
|
37
36
|
end
|
37
|
+
end
|
38
38
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
Thank you and have a lovely day.}
|
45
|
-
return {:uri => args[0], :as => args[1]} if args.size == 2
|
46
|
-
return {:uri => args[0], :as => args[2], :body => args[1]}
|
47
|
-
end
|
48
|
-
|
49
|
-
args.first
|
39
|
+
# Wraps the original response from NetHttp and provides it via #response.
|
40
|
+
class Error < RuntimeError # TODO: raise this from Faraday, too.
|
41
|
+
def initialize(response)
|
42
|
+
@response = response
|
43
|
+
super("Roar error: #{response}")
|
50
44
|
end
|
51
45
|
|
52
|
-
|
53
|
-
|
54
|
-
class UnauthorizedError < RuntimeError # TODO: raise this from Faraday, too.
|
46
|
+
attr_reader :response
|
55
47
|
end
|
56
48
|
end
|
57
49
|
end
|
@@ -37,7 +37,7 @@ module Roar
|
|
37
37
|
yield req if block_given?
|
38
38
|
|
39
39
|
http.request(req).tap do |res|
|
40
|
-
|
40
|
+
handle_error!(res)
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
@@ -66,6 +66,11 @@ module Roar
|
|
66
66
|
|
67
67
|
@req.basic_auth(*options[:basic_auth])
|
68
68
|
end
|
69
|
+
|
70
|
+
def handle_error!(res)
|
71
|
+
status = res.code.to_i
|
72
|
+
raise Error.new(res) unless status >= 200 and status < 300
|
73
|
+
end
|
69
74
|
end
|
70
75
|
end
|
71
76
|
end
|
data/lib/roar/version.rb
CHANGED
data/test/http_verbs_test.rb
CHANGED
@@ -58,11 +58,14 @@ class HttpVerbsTest < MiniTest::Spec
|
|
58
58
|
end
|
59
59
|
end
|
60
60
|
|
61
|
-
it '
|
61
|
+
it 'returns Roar::Transport::Error for NetHttpTransport in case of non 20x' do
|
62
62
|
@band.transport_engine = Roar::Transport::NetHTTP
|
63
|
-
|
63
|
+
|
64
|
+
exception = assert_raises(Roar::Transport::Error) do
|
64
65
|
@band.get(uri: 'http://localhost:4567/bands/anthrax', as: "application/json")
|
65
66
|
end
|
67
|
+
|
68
|
+
exception.response.code.must_equal "404"
|
66
69
|
end
|
67
70
|
end
|
68
71
|
end
|
data/test/json_api_test.rb
CHANGED
@@ -334,4 +334,62 @@ class JSONAPITest < MiniTest::Spec
|
|
334
334
|
)
|
335
335
|
end
|
336
336
|
end
|
337
|
+
|
338
|
+
|
339
|
+
class ExplicitMeta < self
|
340
|
+
module Representer
|
341
|
+
include Roar::JSON::JSONAPI
|
342
|
+
|
343
|
+
type :songs
|
344
|
+
property :id
|
345
|
+
|
346
|
+
meta do
|
347
|
+
property :page
|
348
|
+
end
|
349
|
+
end
|
350
|
+
|
351
|
+
module Page
|
352
|
+
def page
|
353
|
+
2
|
354
|
+
end
|
355
|
+
end
|
356
|
+
|
357
|
+
let (:song) { Struct.new(:id).new(1) }
|
358
|
+
|
359
|
+
subject { [song, song].extend(Representer.for_collection).extend(Page) }
|
360
|
+
|
361
|
+
# to_json
|
362
|
+
it do
|
363
|
+
subject.to_hash.must_equal(
|
364
|
+
{
|
365
|
+
"songs"=>[{"id"=>1}, {"id"=>1}],
|
366
|
+
"meta" =>{"page"=>2}
|
367
|
+
}
|
368
|
+
)
|
369
|
+
end
|
370
|
+
end
|
371
|
+
|
372
|
+
|
373
|
+
class ImplicitMeta < self
|
374
|
+
module Representer
|
375
|
+
include Roar::JSON::JSONAPI
|
376
|
+
|
377
|
+
type :songs
|
378
|
+
property :id
|
379
|
+
end
|
380
|
+
|
381
|
+
let (:song) { Struct.new(:id).new(1) }
|
382
|
+
|
383
|
+
subject { [song, song].extend(Representer.for_collection) }
|
384
|
+
|
385
|
+
# to_json
|
386
|
+
it do
|
387
|
+
subject.to_hash("meta" => {"page" => 2}).must_equal(
|
388
|
+
{
|
389
|
+
"songs"=>[{"id"=>1}, {"id"=>1}],
|
390
|
+
"meta" =>{"page"=>2}
|
391
|
+
}
|
392
|
+
)
|
393
|
+
end
|
394
|
+
end
|
337
395
|
end
|
@@ -47,31 +47,25 @@ class NetHTTPTransportTest < MiniTest::Spec
|
|
47
47
|
|
48
48
|
describe "basic auth" do
|
49
49
|
it "raises when no credentials provided" do
|
50
|
-
assert_raises Roar::Transport::
|
50
|
+
exception = assert_raises Roar::Transport::Error do
|
51
51
|
transport.get_uri(:uri => "http://localhost:4567/protected/bands/bodyjar", :as => "application/json")
|
52
52
|
end
|
53
|
+
|
54
|
+
exception.response.code.must_equal "401"
|
53
55
|
end
|
54
56
|
|
55
57
|
it "raises when wrong credentials provided" do
|
56
|
-
assert_raises Roar::Transport::
|
58
|
+
exception = assert_raises Roar::Transport::Error do
|
57
59
|
transport.get_uri(:uri => "http://localhost:4567/protected/bands/bodyjar", :as => "application/json", :basic_auth => ["admin", "wrong--!!!--password"])
|
58
60
|
end
|
61
|
+
|
62
|
+
exception.response.code.must_equal "401"
|
59
63
|
end
|
60
64
|
|
61
65
|
it "what" do
|
62
66
|
transport.get_uri(:uri => "http://localhost:4567/protected/bands/bodyjar", :as => "application/json", :basic_auth => ["admin", "password"])
|
63
67
|
end
|
64
68
|
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
|
73
|
-
end
|
74
|
-
end
|
75
69
|
end
|
76
70
|
|
77
71
|
module MiniTest::Assertions
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: roar
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.0
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nick Sutterer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-12-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: representable
|
@@ -221,9 +221,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
221
221
|
version: '0'
|
222
222
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
223
223
|
requirements:
|
224
|
-
- - "
|
224
|
+
- - ">="
|
225
225
|
- !ruby/object:Gem::Version
|
226
|
-
version:
|
226
|
+
version: '0'
|
227
227
|
requirements: []
|
228
228
|
rubyforge_project:
|
229
229
|
rubygems_version: 2.2.2
|