roar 1.0.0 → 1.0.1
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/CHANGES.markdown +4 -0
- data/Gemfile +2 -2
- data/README.markdown +27 -4
- data/examples/example.rb +49 -55
- data/gemfiles/Gemfile.representable-2.1 +1 -1
- data/lib/roar/client.rb +3 -1
- data/lib/roar/json/json_api.rb +3 -3
- data/lib/roar/version.rb +1 -1
- data/roar.gemspec +2 -2
- data/test/decorator_client_test.rb +64 -0
- data/test/json_api_test.rb +310 -295
- metadata +6 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9ce6093e4d27114a605b64621c20ab9f5e156266
|
4
|
+
data.tar.gz: ac05b9e8989952f88b86caaf2d344a2467f48968
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 334f83c90798c648945a3d1f0077e503a26f6a51bd2499a3878f48bdb845ad3815a55f8e372078ac28cf5fba628043e7b4edb031625e1abf14ff36dc4fee890d
|
7
|
+
data.tar.gz: 329f87d78af5d6e8f214e5dc71a22ff9ce932967bc8bb0e86f5b45ecad6a21eabf546afecd0690ab05dc8e5ccbcfe5f98cfccddc2ddd18f381e6a7ddaf2e441e
|
data/CHANGES.markdown
CHANGED
data/Gemfile
CHANGED
@@ -3,8 +3,8 @@ 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
|
-
|
6
|
+
# gem "representable", "~> 2.1.0"
|
7
|
+
gem "representable", :path => "../representable"
|
8
8
|
|
9
9
|
# as long as this is not merged, i'll vendor the runner file.
|
10
10
|
# gem "sinatra-contrib", :git => "git@github.com:apotonick/sinatra-contrib.git", :branch => "runner"
|
data/README.markdown
CHANGED
@@ -8,10 +8,16 @@ Roar is a framework for parsing and rendering REST documents. Nothing more.
|
|
8
8
|
|
9
9
|
Representers let you define your API document structure and semantics. They allow both rendering representations from your models _and_ parsing documents to update your Ruby objects. The bi-directional nature of representers make them interesting for both server and client usage.
|
10
10
|
|
11
|
-
Roar comes with built-in JSON, JSON-HAL, JSON-API and XML support. Its highly
|
11
|
+
Roar comes with built-in JSON, JSON-HAL, JSON-API and XML support. Its highly modular architecture provides features like coercion, hypermedia, HTTP transport, client caching and more.
|
12
12
|
|
13
13
|
Roar is completely framework-agnostic and loves being used in web kits like Rails, Webmachine, Sinatra, Padrino, etc. If you use Rails, consider [roar-rails](https://github.com/apotonick/roar-rails) for an enjoyable integration.
|
14
14
|
|
15
|
+
<a href="https://leanpub.com/trailblazer">
|
16
|
+

|
17
|
+
</a>
|
18
|
+
|
19
|
+
Roar is part of the [Trailblazer project](https://github.com/apotonick/trailblazer). Please [buy the book](https://leanpub.com/trailblazer) to support the development. Several chapters will be dedicated to Roar, its integration into operations, hypermedia formats and client-side usage.
|
20
|
+
|
15
21
|
## Representable
|
16
22
|
|
17
23
|
Roar is just a thin layer on top of the [representable](https://github.com/apotonick/representable) gem. While Roar gives you a DSL and behaviour for creating hypermedia APIs, representable implements all the mapping functionality.
|
@@ -235,7 +241,7 @@ We're currently [working on](https://github.com/apotonick/roar/issues/85) better
|
|
235
241
|
Roar provides coercion with the [virtus](https://github.com/solnic/virtus) gem.
|
236
242
|
|
237
243
|
```ruby
|
238
|
-
require 'roar/
|
244
|
+
require 'roar/coercion'
|
239
245
|
|
240
246
|
module SongRepresenter
|
241
247
|
include Roar::JSON
|
@@ -280,7 +286,20 @@ module SongRepresenter
|
|
280
286
|
end
|
281
287
|
```
|
282
288
|
|
283
|
-
The `Hypermedia` feature allows declaring links using the `::link` method.
|
289
|
+
The `Hypermedia` feature allows declaring links using the `::link` method. In the block, you have access to the represented model. When using representer modules, the block is executed in the model's context.
|
290
|
+
|
291
|
+
However, when using decorators, the context is the decorator instance, allowing you to access additional data. Use `represented` to retrieve model data.
|
292
|
+
|
293
|
+
```ruby
|
294
|
+
class SongRepresenter < Roar::Decorator
|
295
|
+
# ..
|
296
|
+
link :self do
|
297
|
+
"http://songs/#{represented.title}"
|
298
|
+
end
|
299
|
+
end
|
300
|
+
```
|
301
|
+
|
302
|
+
This will render links into your representation.
|
284
303
|
|
285
304
|
```ruby
|
286
305
|
song.extend(SongRepresenter)
|
@@ -358,6 +377,8 @@ end
|
|
358
377
|
|
359
378
|
Documentation for HAL can be found in the [API docs](http://rdoc.info/github/apotonick/roar/Roar/Representer/JSON/HAL).
|
360
379
|
|
380
|
+
Make sure you [understand the different contexts](#hypermedia) for links when using decorators.
|
381
|
+
|
361
382
|
### Hypermedia
|
362
383
|
|
363
384
|
Including the `Roar::JSON::HAL` module adds some more DSL methods to your module. It still allows using `::link` but treats them slightly different.
|
@@ -404,6 +425,8 @@ All HAL features in Roar are discussed in the [API docs](http://rdoc.info/github
|
|
404
425
|
|
405
426
|
Roar also supports [JSON-API](http://jsonapi.org/) - yay! It can render _and_ parse singular and collection documents.
|
406
427
|
|
428
|
+
Note that you need representable >= 2.1.4 in your `Gemfile`.
|
429
|
+
|
407
430
|
### Resource
|
408
431
|
|
409
432
|
A minimal representation can be defined as follows.
|
@@ -412,7 +435,7 @@ A minimal representation can be defined as follows.
|
|
412
435
|
require 'roar/json/json_api'
|
413
436
|
|
414
437
|
module SongsRepresenter
|
415
|
-
include Roar::JSON::
|
438
|
+
include Roar::JSON::JSONAPI
|
416
439
|
type :songs
|
417
440
|
|
418
441
|
property :id
|
data/examples/example.rb
CHANGED
@@ -4,7 +4,7 @@ require 'bundler'
|
|
4
4
|
Bundler.setup
|
5
5
|
|
6
6
|
require 'ostruct'
|
7
|
-
require 'roar/
|
7
|
+
require 'roar/json'
|
8
8
|
|
9
9
|
def reset_representer(*module_name)
|
10
10
|
module_name.each do |mod|
|
@@ -19,12 +19,12 @@ class Song < OpenStruct
|
|
19
19
|
end
|
20
20
|
|
21
21
|
module SongRepresenter
|
22
|
-
include Roar::
|
22
|
+
include Roar::JSON
|
23
23
|
|
24
24
|
property :title
|
25
25
|
end
|
26
26
|
|
27
|
-
song = Song.new(title:
|
27
|
+
song = Song.new(title: 'Fate').extend(SongRepresenter)
|
28
28
|
puts song.to_json
|
29
29
|
|
30
30
|
# Parsing
|
@@ -40,13 +40,13 @@ require 'roar/decorator'
|
|
40
40
|
|
41
41
|
module Decorator
|
42
42
|
class SongRepresenter < Roar::Decorator
|
43
|
-
include Roar::
|
43
|
+
include Roar::JSON
|
44
44
|
|
45
45
|
property :title
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
49
|
-
song = Song.new(title:
|
49
|
+
song = Song.new(title: 'Medicine Balls')
|
50
50
|
puts Decorator::SongRepresenter.new(song).to_json
|
51
51
|
|
52
52
|
# Collections
|
@@ -54,14 +54,14 @@ puts Decorator::SongRepresenter.new(song).to_json
|
|
54
54
|
reset_representer(SongRepresenter)
|
55
55
|
|
56
56
|
module SongRepresenter
|
57
|
-
include Roar::
|
57
|
+
include Roar::JSON
|
58
58
|
|
59
59
|
property :title
|
60
60
|
collection :composers
|
61
61
|
end
|
62
62
|
|
63
63
|
|
64
|
-
song = Song.new(title:
|
64
|
+
song = Song.new(title: 'Roxanne', composers: ['Sting', 'Stu Copeland'])
|
65
65
|
song.extend(SongRepresenter)
|
66
66
|
puts song.to_json
|
67
67
|
|
@@ -71,13 +71,13 @@ class Album < OpenStruct
|
|
71
71
|
end
|
72
72
|
|
73
73
|
module AlbumRepresenter
|
74
|
-
include Roar::
|
74
|
+
include Roar::JSON
|
75
75
|
|
76
76
|
property :title
|
77
77
|
collection :songs, extend: SongRepresenter, class: Song
|
78
78
|
end
|
79
79
|
|
80
|
-
album = Album.new(title:
|
80
|
+
album = Album.new(title: 'True North', songs: [Song.new(title: 'The Island'), Song.new(:title => 'Changing Tide')])
|
81
81
|
album.extend(AlbumRepresenter)
|
82
82
|
puts album.to_json
|
83
83
|
|
@@ -88,12 +88,10 @@ album.from_json('{"title":"Indestructible","songs":[{"title":"Tropical London"},
|
|
88
88
|
|
89
89
|
puts album.songs.last.inspect
|
90
90
|
|
91
|
-
# Inline Representers # FIXME: what about collections?
|
92
|
-
|
93
91
|
reset_representer(AlbumRepresenter)
|
94
92
|
|
95
93
|
module AlbumRepresenter
|
96
|
-
include Roar::
|
94
|
+
include Roar::JSON
|
97
95
|
|
98
96
|
property :title
|
99
97
|
|
@@ -102,12 +100,10 @@ module AlbumRepresenter
|
|
102
100
|
end
|
103
101
|
end
|
104
102
|
|
105
|
-
album = Album.new(title:
|
103
|
+
album = Album.new(title: 'True North', songs: [Song.new(title: 'The Island'), Song.new(:title => 'Changing Tide')])
|
106
104
|
album.extend(AlbumRepresenter)
|
107
105
|
puts album.to_json
|
108
106
|
|
109
|
-
|
110
|
-
|
111
107
|
album = Album.new
|
112
108
|
album.extend(AlbumRepresenter)
|
113
109
|
album.from_json('{"title":"True North","songs":[{"title":"The Island"},{"title":"Changing Tide"}]}')
|
@@ -119,7 +115,7 @@ puts album.songs.first.title
|
|
119
115
|
reset_representer(AlbumRepresenter)
|
120
116
|
|
121
117
|
module AlbumRepresenter
|
122
|
-
include Roar::
|
118
|
+
include Roar::JSON
|
123
119
|
|
124
120
|
property :title
|
125
121
|
|
@@ -127,13 +123,13 @@ module AlbumRepresenter
|
|
127
123
|
end
|
128
124
|
|
129
125
|
|
130
|
-
album = Album.new(title:
|
126
|
+
album = Album.new(title: 'True North', songs: [Song.new(title: 'The Island'), Song.new(:title => 'Changing Tide')])
|
131
127
|
album.extend(AlbumRepresenter)
|
132
128
|
|
133
129
|
puts album.songs[0].object_id
|
134
130
|
album.from_json('{"title":"True North","songs":[{"title":"Secret Society"},{"title":"Changing Tide"}]}')
|
135
131
|
puts album.songs[0].title
|
136
|
-
puts album.songs[0].object_id
|
132
|
+
puts album.songs[0].object_id
|
137
133
|
|
138
134
|
# Coercion, renaming, ..
|
139
135
|
|
@@ -142,8 +138,8 @@ puts album.songs[0].object_id##
|
|
142
138
|
reset_representer(SongRepresenter)
|
143
139
|
|
144
140
|
module SongRepresenter
|
145
|
-
include Roar::
|
146
|
-
include Roar::
|
141
|
+
include Roar::JSON
|
142
|
+
include Roar::Hypermedia
|
147
143
|
|
148
144
|
property :title
|
149
145
|
|
@@ -163,7 +159,7 @@ puts song.to_json
|
|
163
159
|
reset_representer(SongRepresenter)
|
164
160
|
|
165
161
|
module SongRepresenter
|
166
|
-
include Roar::
|
162
|
+
include Roar::JSON
|
167
163
|
|
168
164
|
property :title
|
169
165
|
|
@@ -173,7 +169,7 @@ module SongRepresenter
|
|
173
169
|
end
|
174
170
|
|
175
171
|
song.extend(SongRepresenter)
|
176
|
-
puts song.to_json(base_url:
|
172
|
+
puts song.to_json(base_url: 'localhost:3001/')
|
177
173
|
|
178
174
|
|
179
175
|
# Discovering Hypermedia
|
@@ -184,11 +180,11 @@ puts song.links[:self].href
|
|
184
180
|
|
185
181
|
# Media Formats: HAL
|
186
182
|
|
187
|
-
require 'roar/
|
183
|
+
require 'roar/json/hal'
|
188
184
|
|
189
185
|
module HAL
|
190
186
|
module SongRepresenter
|
191
|
-
include Roar::
|
187
|
+
include Roar::JSON::HAL
|
192
188
|
|
193
189
|
property :title
|
194
190
|
|
@@ -204,7 +200,7 @@ puts song.to_json
|
|
204
200
|
reset_representer(AlbumRepresenter)
|
205
201
|
|
206
202
|
module AlbumRepresenter
|
207
|
-
include Roar::
|
203
|
+
include Roar::JSON::HAL
|
208
204
|
|
209
205
|
property :title
|
210
206
|
|
@@ -213,57 +209,55 @@ module AlbumRepresenter
|
|
213
209
|
end
|
214
210
|
end
|
215
211
|
|
216
|
-
album = Album.new(title:
|
212
|
+
album = Album.new(title: 'True North', songs: [Song.new(title: 'The Island'), Song.new(:title => 'Changing Tide')])
|
217
213
|
album.extend(AlbumRepresenter)
|
218
214
|
puts album.to_json
|
219
215
|
|
220
216
|
# Media Formats: JSON+Collection
|
221
217
|
|
222
|
-
require 'roar/
|
218
|
+
require 'roar/json/collection_json'
|
223
219
|
|
224
220
|
|
225
221
|
module Collection
|
226
222
|
module SongRepresenter
|
227
|
-
include Roar::
|
228
|
-
version
|
229
|
-
href {
|
223
|
+
include Roar::JSON::CollectionJSON
|
224
|
+
version '1.0'
|
225
|
+
href { 'http://localhost/songs/' }
|
230
226
|
|
231
227
|
property :title
|
232
228
|
|
233
229
|
items(:class => Song) do
|
234
230
|
href { "//songs/#{title}" }
|
235
231
|
|
236
|
-
property :title, :prompt =>
|
232
|
+
property :title, :prompt => 'Song title'
|
237
233
|
|
238
234
|
link(:download) { "//songs/#{title}.mp3" }
|
239
235
|
end
|
240
236
|
|
241
237
|
template do
|
242
|
-
property :title, :prompt =>
|
238
|
+
property :title, :prompt => 'Song title'
|
243
239
|
end
|
244
240
|
|
245
241
|
queries do
|
246
242
|
link :search do
|
247
|
-
{:href =>
|
243
|
+
{:href => '//search', :data => [{:name => 'q', :value => ''}]}
|
248
244
|
end
|
249
245
|
end
|
250
246
|
end
|
251
247
|
end
|
252
248
|
|
253
|
-
song = Song.new(title:
|
249
|
+
song = Song.new(title: 'Roxanne')
|
254
250
|
song.extend(Collection::SongRepresenter)
|
255
251
|
puts song.to_json
|
256
252
|
|
257
|
-
|
258
|
-
|
259
253
|
# Client-side
|
260
254
|
# share in gem, parse existing document.
|
261
255
|
|
262
256
|
reset_representer(SongRepresenter)
|
263
257
|
|
264
258
|
module SongRepresenter
|
265
|
-
include Roar::
|
266
|
-
include Roar::
|
259
|
+
include Roar::JSON
|
260
|
+
include Roar::Hypermedia
|
267
261
|
|
268
262
|
property :title
|
269
263
|
property :id
|
@@ -274,34 +268,34 @@ module SongRepresenter
|
|
274
268
|
end
|
275
269
|
|
276
270
|
|
277
|
-
require 'roar/
|
271
|
+
require 'roar/client'
|
278
272
|
|
279
273
|
module Client
|
280
274
|
class Song < OpenStruct
|
281
|
-
include Roar::
|
275
|
+
include Roar::JSON
|
282
276
|
include SongRepresenter
|
283
|
-
include Roar::
|
277
|
+
include Roar::Client
|
284
278
|
end
|
285
279
|
end
|
286
280
|
|
287
|
-
song = Client::Song.new(title:
|
288
|
-
song.post(
|
281
|
+
song = Client::Song.new(title: 'Roxanne')
|
282
|
+
song.post(uri: 'http://localhost:4567/songs', as: 'application/json')
|
289
283
|
puts song.id
|
290
284
|
|
291
285
|
|
292
286
|
song = Client::Song.new
|
293
|
-
song.get(
|
287
|
+
song.get(uri: 'http://localhost:4567/songs/1', as: 'application/json')
|
294
288
|
puts song.title
|
295
289
|
puts song.links[:self].href
|
296
290
|
|
297
291
|
# XML
|
298
292
|
|
299
|
-
require 'roar/
|
293
|
+
require 'roar/xml'
|
300
294
|
|
301
295
|
module XML
|
302
296
|
module SongRepresenter
|
303
|
-
include Roar::
|
304
|
-
include Roar::
|
297
|
+
include Roar::XML
|
298
|
+
include Roar::Hypermedia
|
305
299
|
|
306
300
|
property :title
|
307
301
|
property :id
|
@@ -312,7 +306,7 @@ module XML
|
|
312
306
|
end
|
313
307
|
end
|
314
308
|
|
315
|
-
song = Song.new(title:
|
309
|
+
song = Song.new(title: 'Roxanne', id: 42)
|
316
310
|
song.extend(XML::SongRepresenter)
|
317
311
|
puts song.to_xml
|
318
312
|
|
@@ -320,11 +314,11 @@ puts song.to_xml
|
|
320
314
|
|
321
315
|
reset_representer(SongRepresenter)
|
322
316
|
|
323
|
-
require 'roar/
|
317
|
+
require 'roar/coercion'
|
324
318
|
|
325
319
|
module SongRepresenter
|
326
|
-
include Roar::
|
327
|
-
include Roar::
|
320
|
+
include Roar::JSON
|
321
|
+
include Roar::Coercion
|
328
322
|
|
329
323
|
property :title
|
330
324
|
property :released_at, type: DateTime
|
@@ -342,7 +336,7 @@ class LinkOptionsCollection < Array
|
|
342
336
|
end
|
343
337
|
|
344
338
|
module HyperlinkiRepresenter
|
345
|
-
include Roar::
|
339
|
+
include Roar::JSON
|
346
340
|
|
347
341
|
def to_hash(*) # setup the link
|
348
342
|
# FIXME: why does self.to_s throw a stack level too deep (SystemStackError) ?
|
@@ -352,7 +346,7 @@ module HyperlinkiRepresenter
|
|
352
346
|
end
|
353
347
|
|
354
348
|
module Representer
|
355
|
-
include Roar::
|
349
|
+
include Roar::JSON
|
356
350
|
|
357
351
|
def self.links
|
358
352
|
[:self, :next]
|
@@ -363,8 +357,8 @@ module Representer
|
|
363
357
|
def links
|
364
358
|
# get link configurations from representable_attrs object.
|
365
359
|
#self.representable_attrs.links
|
366
|
-
LinkOptionsCollection.new([
|
360
|
+
LinkOptionsCollection.new(['self', 'next'])
|
367
361
|
end
|
368
362
|
end
|
369
363
|
|
370
|
-
puts
|
364
|
+
puts ''.extend(Representer).to_json
|
data/lib/roar/client.rb
CHANGED
@@ -1,10 +1,12 @@
|
|
1
1
|
require "roar/http_verbs"
|
2
2
|
|
3
3
|
module Roar
|
4
|
-
|
4
|
+
|
5
|
+
# Mix in HttpVerbs.
|
5
6
|
module Client
|
6
7
|
include HttpVerbs
|
7
8
|
|
9
|
+
# Add accessors for properties and collections to modules.
|
8
10
|
def self.extended(base)
|
9
11
|
base.instance_eval do
|
10
12
|
representable_attrs.each do |attr|
|