roar 1.0.1 → 1.0.2

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: 9ce6093e4d27114a605b64621c20ab9f5e156266
4
- data.tar.gz: ac05b9e8989952f88b86caaf2d344a2467f48968
3
+ metadata.gz: c60a365b03b4c4196033eccc186b649c50f8f40a
4
+ data.tar.gz: 4e5dfbfe2caa91f334c2568d6a563c223776cdab
5
5
  SHA512:
6
- metadata.gz: 334f83c90798c648945a3d1f0077e503a26f6a51bd2499a3878f48bdb845ad3815a55f8e372078ac28cf5fba628043e7b4edb031625e1abf14ff36dc4fee890d
7
- data.tar.gz: 329f87d78af5d6e8f214e5dc71a22ff9ce932967bc8bb0e86f5b45ecad6a21eabf546afecd0690ab05dc8e5ccbcfe5f98cfccddc2ddd18f381e6a7ddaf2e441e
6
+ metadata.gz: f2ea14d0d3b644f2f0f4c0677e35419a91955e97f6357a283a2f21206c19a35fd19ef8c166f3b4540449fc09cf3c70ab54688c4266bfca1aa00c2a33bf6e3769
7
+ data.tar.gz: ee6b42fc8b33f3e21955d6ee4ba658a4903503c4208cfc766017123ddceb802a906c759a845dbe9e9a202cd3465f0ad9db5e1c835a61adeb420a2f4308a0d07b
@@ -3,6 +3,7 @@ rvm:
3
3
  - 2.0
4
4
  - 2.1
5
5
  - 2.2
6
+ - rbx-2
6
7
  gemfile:
7
8
  - gemfiles/Gemfile.representable-2.0
8
9
  - gemfiles/Gemfile.representable-2.1
@@ -1,3 +1,12 @@
1
+ # 1.0.2
2
+
3
+ * Roar runs on Rubinius.
4
+
5
+ ## JSON::HAL
6
+
7
+ * `"_embedded"` will always be rendered before `"_links"`.
8
+ * `render_nil: false` is now respected.
9
+
1
10
  # 1.0.1
2
11
 
3
12
  * Allow calling `::has_one`, `::links` and `::has_many` in any order in JSON-API. This requires representable >= 2.1.4.
@@ -14,7 +23,7 @@
14
23
 
15
24
  ## Added
16
25
 
17
- * `Roar::JSON::JsonApi` supports JSON-API. A big thanks to @oliverbarnes for his continous help, support and research on how to implement this standard.
26
+ * `Roar::JSON::JSONAPI` supports JSON-API. A big thanks to @oliverbarnes for his continous help, support and research on how to implement this standard.
18
27
 
19
28
 
20
29
  ## Relevant
data/Gemfile CHANGED
@@ -3,8 +3,5 @@ source "http://rubygems.org"
3
3
  # Specify your gem's dependencies in roar.gemspec
4
4
  gemspec
5
5
 
6
- # gem "representable", "~> 2.1.0"
7
- gem "representable", :path => "../representable"
8
-
9
6
  # as long as this is not merged, i'll vendor the runner file.
10
7
  # gem "sinatra-contrib", :git => "git@github.com:apotonick/sinatra-contrib.git", :branch => "runner"
@@ -2,6 +2,8 @@
2
2
 
3
3
  _Resource-Oriented Architectures in Ruby._
4
4
 
5
+ [![Build Status](https://travis-ci.org/apotonick/roar.svg?branch=master)](https://travis-ci.org/apotonick/roar)
6
+
5
7
  ## Introduction
6
8
 
7
9
  Roar is a framework for parsing and rendering REST documents. Nothing more.
@@ -24,6 +26,30 @@ Roar is just a thin layer on top of the [representable](https://github.com/apoto
24
26
 
25
27
  If in need for a feature, make sure to check the [representable API docs](https://github.com/apotonick/representable) first.
26
28
 
29
+ ## Installation
30
+
31
+ The roar gem runs with all Ruby versions >= 1.9.3.
32
+
33
+ ```ruby
34
+ gem 'roar'
35
+ ```
36
+
37
+ ### Dependencies
38
+
39
+ Roar does not bundle dependencies for JSON and XML.
40
+
41
+ If you want to use JSON, add the following to your Gemfile:
42
+
43
+ ```ruby
44
+ gem 'multi_json'
45
+ ```
46
+
47
+ If you want to use XML, add the following to your Gemfile:
48
+
49
+ ```ruby
50
+ gem 'nokogiri'
51
+ ```
52
+
27
53
 
28
54
  ## Defining Representers
29
55
 
@@ -375,7 +401,7 @@ module SongRepresenter
375
401
  end
376
402
  ```
377
403
 
378
- Documentation for HAL can be found in the [API docs](http://rdoc.info/github/apotonick/roar/Roar/Representer/JSON/HAL).
404
+ Documentation for HAL can be found in the [API docs](http://rdoc.info/github/apotonick/roar/Roar/JSON/HAL).
379
405
 
380
406
  Make sure you [understand the different contexts](#hypermedia) for links when using decorators.
381
407
 
@@ -418,7 +444,7 @@ album.to_json
418
444
 
419
445
  HAL keys nested resources under the `_embedded` key and then by their type.
420
446
 
421
- All HAL features in Roar are discussed in the [API docs](http://rdoc.info/github/apotonick/roar/Roar/Representer/JSON/HAL), including [array links](https://github.com/apotonick/roar/blob/master/lib/roar/json/hal.rb#L176).
447
+ All HAL features in Roar are discussed in the [API docs](http://rdoc.info/github/apotonick/roar/Roar/JSON/HAL), including [array links](https://github.com/apotonick/roar/blob/master/lib/roar/json/hal.rb#L196).
422
448
 
423
449
 
424
450
  ## JSON-API
@@ -662,7 +688,7 @@ As `GET` is not supposed to send any data, you can use `#get` on an empty object
662
688
  Roar supports SSL connections - they are automatically detected via the protocol.
663
689
 
664
690
  ```ruby
665
- song.get(uri: "https://localhost:4567/songs/1")`
691
+ song.get(uri: "https://localhost:4567/songs/1")
666
692
  ```
667
693
 
668
694
  ### Basic Authentication
@@ -713,8 +739,8 @@ Roar also comes with XML support.
713
739
 
714
740
  ```ruby
715
741
  module SongRepresenter
716
- include Roar::Representer::XML
717
- include Roar::Representer::Hypermedia
742
+ include Roar::XML
743
+ include Roar::Hypermedia
718
744
 
719
745
  property :title
720
746
  property :id
@@ -725,7 +751,7 @@ module SongRepresenter
725
751
  end
726
752
  ```
727
753
 
728
- Include the `Roar::Representer::XML` engine and get bi-directional XML for your objects.
754
+ Include the `Roar::XML` engine and get bi-directional XML for your objects.
729
755
 
730
756
  ```ruby
731
757
  song = Song.new(title: "Roxanne", id: 42)
@@ -0,0 +1,6 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec path: '../'
4
+
5
+ gem 'representable', '~> 1.7.0'
6
+ gem 'sinatra-contrib', github: 'apotonick/sinatra-contrib', branch: 'runner'
@@ -0,0 +1,6 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec path: '../'
4
+
5
+ gem 'representable', '~> 1.8.0'
6
+ gem 'sinatra-contrib', github: 'apotonick/sinatra-contrib', branch: 'runner'
@@ -0,0 +1,6 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec path: '../'
4
+
5
+ gem 'representable', github: 'apotonick/representable'
6
+ gem 'sinatra-contrib', github: 'apotonick/sinatra-contrib', branch: 'runner'
@@ -5,8 +5,8 @@ require 'representable/coercion'
5
5
  module Roar
6
6
  # Use the +:type+ option to specify the conversion type.
7
7
  # class ImmigrantSong
8
- # include Roar::Representer::JSON
9
- # include Roar::Representer::Feature::Coercion
8
+ # include Roar::JSON
9
+ # include Roar::Coercion
10
10
  #
11
11
  # property :composed_at, :type => DateTime, :default => "May 12th, 2012"
12
12
  # end
@@ -4,7 +4,7 @@ module Roar
4
4
  # Example:
5
5
  #
6
6
  # class Order
7
- # include Roar::Representer::JSON
7
+ # include Roar:JSON
8
8
  #
9
9
  # property :id
10
10
  #
@@ -16,7 +16,7 @@ module Roar
16
16
  # Example:
17
17
  #
18
18
  # module OrderRepresenter
19
- # include Roar::Representer::JSON::HAL
19
+ # include Roar::JSON::HAL
20
20
  #
21
21
  # property :id
22
22
  # collection :items, :class => Item, :extend => ItemRepresenter, :embedded => true
@@ -48,27 +48,26 @@ module Roar
48
48
  include Links # overwrites #links_definition_options.
49
49
  extend ClassMethods # overwrites #links_definition_options, again.
50
50
  include Resources
51
-
52
- def representable_mapper(*) # TODO: make this easier to override.
53
- super.tap do |map|
54
- map.extend Resources
55
- end
56
- end
57
51
  end
58
52
  end
59
53
 
60
54
  module Resources
61
- # Write the property to the +_embedded+ hash when it's a resource.
62
- def compile_fragment(bin, doc)
63
- embedded = bin[:embedded]
64
- return super unless embedded
65
- super(bin, doc[:_embedded] ||= {})
55
+ def to_hash(*)
56
+ super.tap do |hash|
57
+ embedded = {}
58
+ representable_attrs.find_all do |dfn|
59
+ next unless dfn[:embedded] and fragment = hash.delete(dfn.name)
60
+ embedded[dfn.name] = fragment
61
+ end
62
+
63
+ hash["_embedded"] = embedded if embedded.any?
64
+ hash["_links"] = hash.delete("_links") if hash["_links"] # always render _links after _embedded.
65
+ end
66
66
  end
67
67
 
68
- def uncompile_fragment(bin, doc)
69
- embedded = bin[:embedded]
70
- return super unless embedded
71
- super(bin, doc["_embedded"] || {})
68
+ def from_hash(hash, *)
69
+ hash.fetch("_embedded", []).each { |name, fragment| hash[name] = fragment }
70
+ super
72
71
  end
73
72
  end
74
73
 
@@ -93,8 +92,8 @@ module Roar
93
92
  # following the HAL specification: http://stateless.co/hal_specification.html
94
93
  #
95
94
  # module SongRepresenter
96
- # include Roar::Representer::JSON
97
- # include Roar::Representer::JSON::HAL::Links
95
+ # include Roar::JSON
96
+ # include Roar::JSON::HAL::Links
98
97
  #
99
98
  # link :self { "http://self" }
100
99
  # end
@@ -14,6 +14,11 @@ module Roar
14
14
  extend ForCollection
15
15
 
16
16
  representable_attrs[:resource_representer] = Class.new(Resource::Representer)
17
+
18
+ private
19
+ def create_representation_with(doc, options, format)
20
+ super(doc, options.merge(:only_body => true), format)
21
+ end
17
22
  end
18
23
  end
19
24
 
@@ -1,3 +1,3 @@
1
1
  module Roar
2
- VERSION = "1.0.1"
2
+ VERSION = "1.0.2"
3
3
  end
@@ -1,130 +1,132 @@
1
1
  require 'test_helper'
2
2
  require 'roar/json/collection_json'
3
3
 
4
- class CollectionJsonTest < MiniTest::Spec
5
- let(:song) { OpenStruct.new(:title => "scarifice", :length => 43) }
4
+ if RUBY_ENGINE != "rbx"
5
+ class CollectionJsonTest < MiniTest::Spec
6
+ let(:song) { OpenStruct.new(:title => "scarifice", :length => 43) }
6
7
 
7
- representer_for([Roar::JSON::CollectionJSON]) do
8
- version "1.0"
9
- href { "//songs/" }
8
+ representer_for([Roar::JSON::CollectionJSON]) do
9
+ version "1.0"
10
+ href { "//songs/" }
10
11
 
11
- link(:feed) { "//songs/feed" }
12
+ link(:feed) { "//songs/feed" }
12
13
 
13
- items(:class => Song) do
14
- href { "//songs/scarifice" }
14
+ items(:class => Song) do
15
+ href { "//songs/scarifice" }
15
16
 
16
- property :title, :prompt => "Song title"
17
- property :length, :prompt => "Song length"
17
+ property :title, :prompt => "Song title"
18
+ property :length, :prompt => "Song length"
18
19
 
19
- link(:download) { "//songs/scarifice.mp3" }
20
- link(:stats) { "//songs/scarifice/stats" }
21
- end
20
+ link(:download) { "//songs/scarifice.mp3" }
21
+ link(:stats) { "//songs/scarifice/stats" }
22
+ end
22
23
 
23
- template do
24
- property :title, :prompt => "Song title"
25
- property :length, :prompt => "Song length"
26
- end
24
+ template do
25
+ property :title, :prompt => "Song title"
26
+ property :length, :prompt => "Song length"
27
+ end
27
28
 
28
- queries do
29
- link :search do
30
- {:href => "//search", :data => [{:name => "q", :value => ""}]}
29
+ queries do
30
+ link :search do
31
+ {:href => "//search", :data => [{:name => "q", :value => ""}]}
32
+ end
31
33
  end
32
34
  end
33
- end
34
-
35
- describe "#to_json" do
36
- it "renders document" do
37
- [song].extend(rpr).to_hash.must_equal(
38
- {
39
- "collection"=>{
40
- "version"=>"1.0",
41
- "href"=>"//songs/",
42
35
 
43
- "template"=>{
44
- :data=>[
45
- {:name=>"title", :value=>nil},
46
- {:name=>"length", :value=>nil}
47
- ]
48
- },
36
+ describe "#to_json" do
37
+ it "renders document" do
38
+ [song].extend(rpr).to_hash.must_equal(
39
+ {
40
+ "collection"=>{
41
+ "version"=>"1.0",
42
+ "href"=>"//songs/",
49
43
 
50
- "queries"=>[
51
- {"rel"=>"search", "href"=>"//search",
52
- "data"=>[
53
- {:name=>"q", :value=>""}
54
- ]
55
- }
56
- ],
57
-
58
- "items"=>[
59
- {
60
- "links"=>[
61
- {"rel"=>"download", "href"=>"//songs/scarifice.mp3"},
62
- {"rel"=>"stats", "href"=>"//songs/scarifice/stats"}
63
- ],
64
- "href"=>"//songs/scarifice",
44
+ "template"=>{
65
45
  :data=>[
66
- {:name=>"title", :value=>"scarifice"},
67
- {:name=>"length", :value=>43}
46
+ {:name=>"title", :value=>nil},
47
+ {:name=>"length", :value=>nil}
68
48
  ]
69
- }
70
- ],
71
-
72
- "links"=>[
73
- {"rel"=>"feed", "href"=>"//songs/feed"}
74
- ]
75
- }
76
- })# %{{"collection":{"version":"1.0","href":"//songs/","items":[{"href":"//songs/scarifice","links":[{"rel":"download","href":"//songs/scarifice.mp3"},{"rel":"stats","href":"//songs/scarifice/stats"}],"data":[{"name":"title","value":"scarifice"},{"name":"length","value":43}]}],"template":{"data":[{"name":"title","value":null},{"name":"length","value":null}]},"queries":[{"rel":"search","href":"//search","data":[{"name":"q","value":""}]}],"links":[{"rel":"feed","href":"//songs/feed"}]}}}
49
+ },
50
+
51
+ "queries"=>[
52
+ {"rel"=>"search", "href"=>"//search",
53
+ "data"=>[
54
+ {:name=>"q", :value=>""}
55
+ ]
56
+ }
57
+ ],
58
+
59
+ "items"=>[
60
+ {
61
+ "links"=>[
62
+ {"rel"=>"download", "href"=>"//songs/scarifice.mp3"},
63
+ {"rel"=>"stats", "href"=>"//songs/scarifice/stats"}
64
+ ],
65
+ "href"=>"//songs/scarifice",
66
+ :data=>[
67
+ {:name=>"title", :value=>"scarifice"},
68
+ {:name=>"length", :value=>43}
69
+ ]
70
+ }
71
+ ],
72
+
73
+ "links"=>[
74
+ {"rel"=>"feed", "href"=>"//songs/feed"}
75
+ ]
76
+ }
77
+ })# %{{"collection":{"version":"1.0","href":"//songs/","items":[{"href":"//songs/scarifice","links":[{"rel":"download","href":"//songs/scarifice.mp3"},{"rel":"stats","href":"//songs/scarifice/stats"}],"data":[{"name":"title","value":"scarifice"},{"name":"length","value":43}]}],"template":{"data":[{"name":"title","value":null},{"name":"length","value":null}]},"queries":[{"rel":"search","href":"//search","data":[{"name":"q","value":""}]}],"links":[{"rel":"feed","href":"//songs/feed"}]}}}
78
+ end
77
79
  end
78
- end
79
80
 
80
- describe "#from_json" do
81
- subject { [].extend(rpr).from_json [song].extend(rpr).to_json }
81
+ describe "#from_json" do
82
+ subject { [].extend(rpr).from_json [song].extend(rpr).to_json }
82
83
 
83
- it "provides #version" do
84
- subject.version.must_equal "1.0"
85
- end
84
+ it "provides #version" do
85
+ subject.version.must_equal "1.0"
86
+ end
86
87
 
87
- it "provides #href" do
88
- subject.href.must_equal link(:href => "//songs/")
89
- end
88
+ it "provides #href" do
89
+ subject.href.must_equal link(:href => "//songs/")
90
+ end
90
91
 
91
- it "provides #template" do
92
- # DISCUSS: this might return a Template instance, soon.
93
- subject.template.must_equal([
94
- {"name"=>"title", "value"=>nil},
95
- {"name"=>"length", "value"=>nil}])
96
- end
92
+ it "provides #template" do
93
+ # DISCUSS: this might return a Template instance, soon.
94
+ subject.template.must_equal([
95
+ {"name"=>"title", "value"=>nil},
96
+ {"name"=>"length", "value"=>nil}])
97
+ end
97
98
 
98
- it "provides #queries" do
99
- # DISCUSS: this might return CollectionJSON::Hyperlink instances that support some kind of substitution operation for the :data attribute.
100
- # FIXME: this is currently _not_ parsed!
101
- subject.queries.must_equal([link(:rel => :search, :href=>"//search", :data=>[{:name=>"q", :value=>""}])])
102
- end
99
+ it "provides #queries" do
100
+ # DISCUSS: this might return CollectionJSON::Hyperlink instances that support some kind of substitution operation for the :data attribute.
101
+ # FIXME: this is currently _not_ parsed!
102
+ subject.queries.must_equal([link(:rel => :search, :href=>"//search", :data=>[{:name=>"q", :value=>""}])])
103
+ end
103
104
 
104
- it "provides #items" do
105
- subject.items.must_equal([Song.new(:title => "scarifice", :length => "43")])
106
- song = subject.items.first
107
- song.title.must_equal "scarifice"
108
- song.length.must_equal 43
109
- song.links.must_equal("download" => link({:rel=>"download", :href=>"//songs/scarifice.mp3"}), "stats" => link({:rel=>"stats", :href=>"//songs/scarifice/stats"}))
110
- song.href.must_equal link(:href => "//songs/scarifice")
111
- end
105
+ it "provides #items" do
106
+ subject.items.must_equal([Song.new(:title => "scarifice", :length => "43")])
107
+ song = subject.items.first
108
+ song.title.must_equal "scarifice"
109
+ song.length.must_equal 43
110
+ song.links.must_equal("download" => link({:rel=>"download", :href=>"//songs/scarifice.mp3"}), "stats" => link({:rel=>"stats", :href=>"//songs/scarifice/stats"}))
111
+ song.href.must_equal link(:href => "//songs/scarifice")
112
+ end
112
113
 
113
- it "provides #links" do
114
- subject.links.must_equal({"feed" => link(:rel => "feed", :href => "//songs/feed")})
114
+ it "provides #links" do
115
+ subject.links.must_equal({"feed" => link(:rel => "feed", :href => "//songs/feed")})
116
+ end
115
117
  end
116
- end
117
118
 
118
- describe "template_representer#from_json" do
119
- it "parses object" do
120
- song = OpenStruct.new.extend(rpr.template_representer).from_hash(
121
- "template"=>{
122
- "data"=>[
123
- {"name"=>"title", "value"=>"Black Star"},
124
- {"name"=>"length", "value"=>"4.53"}
125
- ]
126
- })
127
- song.title.must_equal "Black Star"
119
+ describe "template_representer#from_json" do
120
+ it "parses object" do
121
+ song = OpenStruct.new.extend(rpr.template_representer).from_hash(
122
+ "template"=>{
123
+ "data"=>[
124
+ {"name"=>"title", "value"=>"Black Star"},
125
+ {"name"=>"length", "value"=>"4.53"}
126
+ ]
127
+ })
128
+ song.title.must_equal "Black Star"
129
+ end
128
130
  end
129
131
  end
130
132
  end
@@ -126,8 +126,39 @@ class HalJsonTest < MiniTest::Spec
126
126
  @album.links.must_equal nil
127
127
  end
128
128
  end
129
+
130
+ end
131
+
132
+ class JsonHalTest < MiniTest::Spec
133
+ Album = Struct.new(:artist, :songs)
134
+ Artist = Struct.new(:name)
135
+ Song = Struct.new(:title)
136
+
137
+ def self.representer!
138
+ super([Roar::JSON::HAL])
139
+ end
140
+
141
+ def representer
142
+ rpr
143
+ end
144
+
145
+ describe "render_nil: false" do
146
+ representer! do
147
+ property :artist, embedded: true, render_nil: false do
148
+ property :name
149
+ end
150
+
151
+ collection :songs, embedded: true, render_empty: false do
152
+ property :title
153
+ end
154
+ end
155
+
156
+ it { Album.new(Artist.new("Bare, Jr."), [Song.new("Tobacco Spit")]).extend(representer).to_hash.must_equal({"_embedded"=>{"artist"=>{"name"=>"Bare, Jr."}, "songs"=>[{"title"=>"Tobacco Spit"}]}}) }
157
+ it { Album.new.extend(representer).to_hash.must_equal({}) }
158
+ end
129
159
  end
130
160
 
161
+
131
162
  class LinkCollectionTest < MiniTest::Spec
132
163
  subject { Roar::JSON::HAL::LinkCollection.new([:self, "next"]) }
133
164
  describe "#is_array?" do
@@ -157,4 +188,4 @@ class HalCurieTest < MiniTest::Spec
157
188
  end
158
189
 
159
190
  it { Object.new.extend(rpr).to_hash.must_equal({"_links"=>{"doc:self"=>{"href"=>"/"}, :curies=>[{"name"=>:doc, "href"=>"//docs/{rel}", "templated"=>true}]}}) }
160
- end
191
+ end
@@ -30,13 +30,20 @@ class SslServerRunner < ServerRunner
30
30
  end
31
31
  end
32
32
 
33
- runner = ServerRunner.new
34
- runner.run
33
+ begin
34
+ runner = ServerRunner.new
35
+ runner.run
35
36
 
36
- ssl_runner = SslServerRunner.new
37
- ssl_runner.run
37
+ ssl_runner = SslServerRunner.new
38
+ ssl_runner.run
38
39
 
39
- Minitest.after_run do
40
+ Minitest.after_run do
41
+ runner.kill
42
+ ssl_runner.kill
43
+ end
44
+ rescue Exception => e
40
45
  runner.kill
41
46
  ssl_runner.kill
42
- end
47
+
48
+ raise e
49
+ end
@@ -15,6 +15,7 @@ crt.issuer = ca
15
15
  crt.public_key = key.public_key
16
16
  crt.not_before = Time.now
17
17
  crt.not_after = Time.now + 1 * 365 * 24 * 60 * 60 # 1 year
18
+ crt.sign(key, OpenSSL::Digest::SHA1.new)
18
19
  webrick_options = {
19
20
  :Port => 8443,
20
21
  :SSLEnable => true,
@@ -349,6 +349,47 @@ if Gem::Version.new(Representable::VERSION) >= Gem::Version.new("2.1.4") # TODO:
349
349
  end
350
350
  end
351
351
 
352
+ class CompoundCollectionUsingExtend < self
353
+ module SongRepresenter
354
+ include Roar::JSON::JSONAPI
355
+
356
+ type :songs
357
+ property :id
358
+ property :title
359
+ end
360
+
361
+ module AlbumRepresenter
362
+ include Roar::JSON::JSONAPI
363
+
364
+ type :albums
365
+ property :id
366
+ compound do
367
+ collection :songs, extend: SongRepresenter
368
+ end
369
+ end
370
+
371
+ let(:songs) do
372
+ struct = Struct.new(:id, :title)
373
+ [struct.new(1, 'Stand Up'), struct.new(2, 'Audition Mantra')]
374
+ end
375
+
376
+ let(:album) { Struct.new(:id, :songs).new(1, songs) }
377
+
378
+ subject { album.extend(AlbumRepresenter) }
379
+
380
+ # to_hash
381
+ it do
382
+ subject.to_hash.must_equal({
383
+ 'albums' => { 'id' => 1 },
384
+ 'linked' => {
385
+ 'songs' => [
386
+ {'id' => 1, 'title' => 'Stand Up'},
387
+ {'id' => 2, 'title' => 'Audition Mantra'}
388
+ ]
389
+ }
390
+ })
391
+ end
392
+ end
352
393
 
353
394
  class ExplicitMeta < self
354
395
  module Representer
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.1
4
+ version: 1.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nick Sutterer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-02-26 00:00:00.000000000 Z
11
+ date: 2015-09-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: representable
@@ -160,8 +160,11 @@ files:
160
160
  - TODO.markdown
161
161
  - examples/example.rb
162
162
  - examples/example_server.rb
163
+ - gemfiles/Gemfile.representable-1.7
164
+ - gemfiles/Gemfile.representable-1.8
163
165
  - gemfiles/Gemfile.representable-2.0
164
166
  - gemfiles/Gemfile.representable-2.1
167
+ - gemfiles/Gemfile.representable-head
165
168
  - lib/roar.rb
166
169
  - lib/roar/client.rb
167
170
  - lib/roar/coercion.rb
@@ -227,8 +230,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
227
230
  version: '0'
228
231
  requirements: []
229
232
  rubyforge_project:
230
- rubygems_version: 2.2.2
233
+ rubygems_version: 2.4.8
231
234
  signing_key:
232
235
  specification_version: 4
233
236
  summary: Parse and render REST API documents using representers.
234
237
  test_files: []
238
+ has_rdoc: