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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 10476a18173be43334b532ee1d5d88c41de38244
4
- data.tar.gz: 95912cb828884fec27f2c2a74b4c95821f217c07
3
+ metadata.gz: 9b58dcace50d4224e36f81444ce64ca73dfec11a
4
+ data.tar.gz: 419929c85da4aef8a13243364b1c918acfd5312c
5
5
  SHA512:
6
- metadata.gz: 8db19659880efe5b15ea0ea19091529822a2afaf4977fcfd9a9f7d9440eee0d6d5656ef0020731aec024cd67682aee24de28c394630aeab7476d54a280ee29ae
7
- data.tar.gz: 22502b9bb0e66fe182f95866825dc27cc92d1be702b94ebf9313600a12d793375fc804139bdc04ca076e0f33a5418d82e1a09c5b56144ebb4997f51be85f8582
6
+ metadata.gz: c090f6a70e513ab5ad28155a9c21a453f0e37a289acb2af3a7ade51484d8cc1a76d0c14a02cf6343b054fb9ef2dd392dcae2383b7d219c77e43513d370369c62
7
+ data.tar.gz: 3253825814038580793eebb22da120b3af1ce69723d81283b65953e5b30806a91b7627ebb370de30b46440b07b749465ec2db1a9235b884b5de132becf2d4c84
@@ -3,7 +3,7 @@ rvm:
3
3
  - 2.0
4
4
  - 2.1
5
5
  - 2.2
6
- gemfiles:
6
+ gemfile:
7
7
  - gemfiles/Gemfile.representable-2.0
8
8
  - gemfiles/Gemfile.representable-2.1
9
9
  notifications:
@@ -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
 
@@ -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.
@@ -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 = representable_attrs[:resource_representer].new(represented).to_hash # creates links: section.
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!("linked" => compound) if compound && compound.size > 0
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, *args, &block)
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
- def handle_deprecated_args(args) # TODO: remove in 1.0.
40
- if args.size > 1
41
- warn %{DEPRECATION WARNING: #get_uri, #post_uri, #put_uri, #delete_uri and #patch_uri no longer accept positional arguments. Please call them as follows:
42
- get_uri(uri: "http://localhost/songs", as: "application/json")
43
- post_uri(uri: "http://localhost/songs", as: "application/json", body: "{'id': 1}")
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
- end
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
- raise UnauthorizedError if res.is_a?(Net::HTTPUnauthorized) # FIXME: make this better. # DISCUSS: abstract all that crap here?
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
@@ -1,3 +1,3 @@
1
1
  module Roar
2
- VERSION = "1.0.0.beta2"
2
+ VERSION = "1.0.0"
3
3
  end
@@ -58,11 +58,14 @@ class HttpVerbsTest < MiniTest::Spec
58
58
  end
59
59
  end
60
60
 
61
- it 'performs no HTTP error handling with NetHttpTransport' do
61
+ it 'returns Roar::Transport::Error for NetHttpTransport in case of non 20x' do
62
62
  @band.transport_engine = Roar::Transport::NetHTTP
63
- assert_raises(MultiJson::LoadError) do
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
@@ -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::UnauthorizedError do
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::UnauthorizedError do
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.beta2
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-10-28 00:00:00.000000000 Z
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: 1.3.1
226
+ version: '0'
227
227
  requirements: []
228
228
  rubyforge_project:
229
229
  rubygems_version: 2.2.2