roar 1.0.1 → 1.0.2

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 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: