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 +4 -4
- data/.travis.yml +1 -0
- data/CHANGES.markdown +10 -1
- data/Gemfile +0 -3
- data/README.markdown +32 -6
- data/gemfiles/Gemfile.representable-1.7 +6 -0
- data/gemfiles/Gemfile.representable-1.8 +6 -0
- data/gemfiles/Gemfile.representable-head +6 -0
- data/lib/roar/coercion.rb +2 -2
- data/lib/roar/hypermedia.rb +1 -1
- data/lib/roar/json/hal.rb +17 -18
- data/lib/roar/json/json_api.rb +5 -0
- data/lib/roar/version.rb +1 -1
- data/test/collection_json_test.rb +103 -101
- data/test/hal_json_test.rb +32 -1
- data/test/integration/runner.rb +13 -6
- data/test/integration/ssl_server.rb +1 -0
- data/test/json_api_test.rb +41 -0
- metadata +7 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c60a365b03b4c4196033eccc186b649c50f8f40a
|
4
|
+
data.tar.gz: 4e5dfbfe2caa91f334c2568d6a563c223776cdab
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f2ea14d0d3b644f2f0f4c0677e35419a91955e97f6357a283a2f21206c19a35fd19ef8c166f3b4540449fc09cf3c70ab54688c4266bfca1aa00c2a33bf6e3769
|
7
|
+
data.tar.gz: ee6b42fc8b33f3e21955d6ee4ba658a4903503c4208cfc766017123ddceb802a906c759a845dbe9e9a202cd3465f0ad9db5e1c835a61adeb420a2f4308a0d07b
|
data/.travis.yml
CHANGED
data/CHANGES.markdown
CHANGED
@@ -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::
|
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"
|
data/README.markdown
CHANGED
@@ -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/
|
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/
|
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::
|
717
|
-
include Roar::
|
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::
|
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)
|
data/lib/roar/coercion.rb
CHANGED
@@ -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::
|
9
|
-
# include Roar::
|
8
|
+
# include Roar::JSON
|
9
|
+
# include Roar::Coercion
|
10
10
|
#
|
11
11
|
# property :composed_at, :type => DateTime, :default => "May 12th, 2012"
|
12
12
|
# end
|
data/lib/roar/hypermedia.rb
CHANGED
data/lib/roar/json/hal.rb
CHANGED
@@ -16,7 +16,7 @@ module Roar
|
|
16
16
|
# Example:
|
17
17
|
#
|
18
18
|
# module OrderRepresenter
|
19
|
-
# include Roar::
|
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
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
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
|
69
|
-
|
70
|
-
|
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::
|
97
|
-
# include Roar::
|
95
|
+
# include Roar::JSON
|
96
|
+
# include Roar::JSON::HAL::Links
|
98
97
|
#
|
99
98
|
# link :self { "http://self" }
|
100
99
|
# end
|
data/lib/roar/json/json_api.rb
CHANGED
@@ -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
|
|
data/lib/roar/version.rb
CHANGED
@@ -1,130 +1,132 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
require 'roar/json/collection_json'
|
3
3
|
|
4
|
-
|
5
|
-
|
4
|
+
if RUBY_ENGINE != "rbx"
|
5
|
+
class CollectionJsonTest < MiniTest::Spec
|
6
|
+
let(:song) { OpenStruct.new(:title => "scarifice", :length => 43) }
|
6
7
|
|
7
|
-
|
8
|
-
|
9
|
-
|
8
|
+
representer_for([Roar::JSON::CollectionJSON]) do
|
9
|
+
version "1.0"
|
10
|
+
href { "//songs/" }
|
10
11
|
|
11
|
-
|
12
|
+
link(:feed) { "//songs/feed" }
|
12
13
|
|
13
|
-
|
14
|
-
|
14
|
+
items(:class => Song) do
|
15
|
+
href { "//songs/scarifice" }
|
15
16
|
|
16
|
-
|
17
|
-
|
17
|
+
property :title, :prompt => "Song title"
|
18
|
+
property :length, :prompt => "Song length"
|
18
19
|
|
19
|
-
|
20
|
-
|
21
|
-
|
20
|
+
link(:download) { "//songs/scarifice.mp3" }
|
21
|
+
link(:stats) { "//songs/scarifice/stats" }
|
22
|
+
end
|
22
23
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
24
|
+
template do
|
25
|
+
property :title, :prompt => "Song title"
|
26
|
+
property :length, :prompt => "Song length"
|
27
|
+
end
|
27
28
|
|
28
|
-
|
29
|
-
|
30
|
-
|
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
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
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
|
-
|
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=>
|
67
|
-
{:name=>"length", :value=>
|
46
|
+
{:name=>"title", :value=>nil},
|
47
|
+
{:name=>"length", :value=>nil}
|
68
48
|
]
|
69
|
-
}
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
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
|
-
|
81
|
-
|
81
|
+
describe "#from_json" do
|
82
|
+
subject { [].extend(rpr).from_json [song].extend(rpr).to_json }
|
82
83
|
|
83
|
-
|
84
|
-
|
85
|
-
|
84
|
+
it "provides #version" do
|
85
|
+
subject.version.must_equal "1.0"
|
86
|
+
end
|
86
87
|
|
87
|
-
|
88
|
-
|
89
|
-
|
88
|
+
it "provides #href" do
|
89
|
+
subject.href.must_equal link(:href => "//songs/")
|
90
|
+
end
|
90
91
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
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
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
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
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
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
|
-
|
114
|
-
|
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
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
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
|
data/test/hal_json_test.rb
CHANGED
@@ -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
|
data/test/integration/runner.rb
CHANGED
@@ -30,13 +30,20 @@ class SslServerRunner < ServerRunner
|
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
|
-
|
34
|
-
runner.
|
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
|
-
|
47
|
+
|
48
|
+
raise e
|
49
|
+
end
|
data/test/json_api_test.rb
CHANGED
@@ -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.
|
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-
|
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.
|
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:
|