roar 1.0.0.beta2 → 1.0.0
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 +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
|