roar 1.0.4 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +14 -6
- data/CHANGES.markdown +75 -58
- data/CONTRIBUTING.md +31 -0
- data/Gemfile +12 -2
- data/ISSUE_TEMPLATE.md +20 -0
- data/LICENSE +1 -1
- data/README.markdown +126 -250
- data/Rakefile +3 -1
- data/examples/example.rb +0 -0
- data/examples/example_server.rb +0 -0
- data/lib/roar.rb +3 -3
- data/lib/roar/client.rb +8 -3
- data/lib/roar/decorator.rb +2 -2
- data/lib/roar/http_verbs.rb +0 -16
- data/lib/roar/hypermedia.rb +30 -56
- data/lib/roar/json.rb +5 -5
- data/lib/roar/json/collection.rb +10 -2
- data/lib/roar/json/hal.rb +72 -82
- data/lib/roar/version.rb +1 -1
- data/lib/roar/xml.rb +1 -1
- data/roar.gemspec +6 -6
- data/test/client_test.rb +1 -1
- data/test/coercion_feature_test.rb +7 -2
- data/test/decorator_test.rb +17 -7
- data/test/hal_json_test.rb +98 -106
- data/test/hypermedia_feature_test.rb +13 -31
- data/test/hypermedia_test.rb +26 -92
- data/test/{decorator_client_test.rb → integration/decorator_client_test.rb} +5 -4
- data/test/{faraday_http_transport_test.rb → integration/faraday_http_transport_test.rb} +1 -0
- data/test/{http_verbs_test.rb → integration/http_verbs_test.rb} +3 -2
- data/test/integration/json_collection_test.rb +35 -0
- data/test/{net_http_transport_test.rb → integration/net_http_transport_test.rb} +1 -0
- data/test/integration/runner.rb +2 -3
- data/test/integration/server.rb +6 -0
- data/test/json_representer_test.rb +2 -29
- data/test/lonely_test.rb +1 -2
- data/test/ssl_client_certs_test.rb +1 -1
- data/test/test_helper.rb +21 -3
- data/test/xml_representer_test.rb +6 -5
- metadata +22 -36
- data/gemfiles/Gemfile.representable-1.7 +0 -6
- data/gemfiles/Gemfile.representable-1.8 +0 -6
- data/gemfiles/Gemfile.representable-2.0 +0 -5
- data/gemfiles/Gemfile.representable-2.1 +0 -5
- data/gemfiles/Gemfile.representable-head +0 -6
- data/lib/roar/json/collection_json.rb +0 -208
- data/lib/roar/json/json_api.rb +0 -233
- data/test/collection_json_test.rb +0 -132
- data/test/hal_links_test.rb +0 -31
- data/test/json_api_test.rb +0 -451
- data/test/lib/runner.rb +0 -134
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a9315b89b6aa93848741828ab6a94e338677e397
|
4
|
+
data.tar.gz: c373cdb3f85a40560502cd9ef09ad88363499394
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 059ab5ab647e9ec273976d485715dfed04895dc1538e8331025cea005995a1b9cf9cdb16c7e0ed34441f338594976e09c9ba48ccd071a2846aed5697a81f742c
|
7
|
+
data.tar.gz: 93101c0816d59b8ffe8465eddb7d5882f132322b1c00f4e44dd960c4b840ac0ce52f4d647245ebdec52682fccfb88c0716ee1c1b4d998c46e5edc6d92753cd4a
|
data/.travis.yml
CHANGED
@@ -1,9 +1,17 @@
|
|
1
|
+
sudo: false
|
1
2
|
rvm:
|
2
|
-
- 1.9
|
3
|
-
- 2.0
|
3
|
+
- 1.9.3
|
4
4
|
- 2.1
|
5
|
-
- 2.2
|
6
|
-
-
|
5
|
+
- 2.2.6
|
6
|
+
- 2.3.1
|
7
|
+
- 2.4.0
|
8
|
+
- jruby-9.1.6.0
|
9
|
+
- ruby-head
|
7
10
|
gemfile:
|
8
|
-
-
|
9
|
-
- gemfiles/Gemfile.representable-2.
|
11
|
+
- Gemfile
|
12
|
+
# - gemfiles/Gemfile.representable-2.4
|
13
|
+
before_install:
|
14
|
+
- gem install bundler
|
15
|
+
matrix:
|
16
|
+
allow_failures:
|
17
|
+
- rvm: ruby-head
|
data/CHANGES.markdown
CHANGED
@@ -1,6 +1,16 @@
|
|
1
|
+
# 1.1.0
|
2
|
+
|
3
|
+
* Require Representable 3.0.x
|
4
|
+
* Remove CollectionJSON support until we get more feedback.
|
5
|
+
* Move JSON API support (`Roar::JSON::JSONAPI`) to the separate [roar-jsonapi](https://github.com/trailblazer/roar-jsonapi) gem.
|
6
|
+
* When using `links[]`, you now need to provide the string name, as in `decorator.links["self"]`. Symbols are not supported, anymore.
|
7
|
+
* `::links` now accepts a String or Symbol as its first argument, enabling more straight-forward definition of CURIE links: e.g. `links 'doc:link_collection' do; end` (@edejong).
|
8
|
+
* Clients can now parse lonely collections. (@hilary)
|
9
|
+
|
10
|
+
|
1
11
|
# 1.0.4
|
2
12
|
|
3
|
-
* Representable < 2.4.
|
13
|
+
* Require Representable < 2.4.
|
4
14
|
|
5
15
|
# 1.0.3
|
6
16
|
|
@@ -17,7 +27,7 @@
|
|
17
27
|
|
18
28
|
# 1.0.1
|
19
29
|
|
20
|
-
* Allow calling `::has_one`, `::links` and `::has_many` in any order in JSON
|
30
|
+
* Allow calling `::has_one`, `::links` and `::has_many` in any order in JSON API. This requires Representable >= 2.1.4.
|
21
31
|
|
22
32
|
# 1.0.0
|
23
33
|
|
@@ -31,25 +41,24 @@
|
|
31
41
|
|
32
42
|
## Added
|
33
43
|
|
34
|
-
* `Roar::JSON::JSONAPI` supports JSON
|
35
|
-
|
44
|
+
* `Roar::JSON::JSONAPI` supports JSON API. A big thanks to @oliverbarnes for his continuous help, support and research on how to implement this standard.
|
36
45
|
|
37
46
|
## Relevant
|
38
47
|
|
39
48
|
* `Hyperlink#to_hash` now returns stringified keys.
|
40
49
|
* Removed `Representer#before_serialize` hook. Override `#serialize` yourself.
|
41
|
-
* Represented#links now returns `nil` when no parsing has happened.
|
50
|
+
* `Represented#links` now returns `nil` when no parsing has happened.
|
42
51
|
* Removed class methods `::from_json`, `::from_hash`, `::from_xml` and `::deserialize`. Please build the instance yourself and use something along `Song.new.from_json`.
|
43
52
|
|
44
53
|
## Internals
|
45
54
|
|
46
|
-
* Remove the concept of
|
55
|
+
* Remove the concept of `links_array`. `Hyperlink` instances for rendering or that have been parsed are always stored in a `LinkCollection` that is available via `#links`.
|
47
56
|
* `Hypermedia` is now 43% simpler.
|
48
57
|
* `HyperlinkCollection#each` now has different semantics for 1- or 2-arity.
|
49
58
|
|
50
59
|
# 0.12.8
|
51
60
|
|
52
|
-
* Last release to support
|
61
|
+
* Last release to support Representable < 2.0.
|
53
62
|
|
54
63
|
# 0.12.7
|
55
64
|
|
@@ -57,11 +66,11 @@
|
|
57
66
|
|
58
67
|
# 0.12.6
|
59
68
|
|
60
|
-
* Remove deprecations (most of 'em) from
|
69
|
+
* Remove deprecations (most of 'em) from Representable 1.8. Sorry for that.
|
61
70
|
|
62
71
|
# 0.12.5
|
63
72
|
|
64
|
-
* Roar runs with
|
73
|
+
* Roar runs with Representable <= 1.8.
|
65
74
|
|
66
75
|
# 0.12.4
|
67
76
|
|
@@ -79,23 +88,22 @@
|
|
79
88
|
* They now yield the request object to add headers etc before request is sent.
|
80
89
|
* They NO LONGER support positional arguments but one hash with `uri: "https://roar.de", body:, .. as: ..` and so on.
|
81
90
|
|
82
|
-
|
83
91
|
# 0.12.2
|
84
92
|
|
85
93
|
* Fix a bug where hyperlinks from nested objects weren't rendered in XML.
|
86
94
|
|
87
95
|
# 0.12.1
|
88
96
|
|
89
|
-
Allow
|
97
|
+
Allow Representable >= 1.6.
|
90
98
|
|
91
99
|
# 0.11.18
|
92
100
|
|
93
|
-
* Updating to
|
101
|
+
* Updating to Representable 1.5.2.
|
94
102
|
|
95
103
|
# 0.11.17
|
96
104
|
|
97
105
|
* Fixing HAL + Decorator.
|
98
|
-
* Requiring
|
106
|
+
* Requiring Representable 1.5.0.
|
99
107
|
|
100
108
|
# 0.11.16
|
101
109
|
|
@@ -103,15 +111,15 @@ Allow representable >= 1.6.
|
|
103
111
|
|
104
112
|
# 0.11.15
|
105
113
|
|
106
|
-
* Fixing [#66](https://github.com/
|
114
|
+
* Fixing [#66](https://github.com/trailblazer/roar/issues/66).
|
107
115
|
|
108
116
|
# 0.11.14
|
109
117
|
|
110
|
-
* Fixing Gemfile
|
118
|
+
* Fixing `Gemfile`.
|
111
119
|
|
112
120
|
# 0.11.13
|
113
121
|
|
114
|
-
* Adding `Roar::Decorator`, see [
|
122
|
+
* Adding `Roar::Decorator`, see [Representable docs](https://github.com/apotonick/Representable#decorator-vs-extend) for now.
|
115
123
|
|
116
124
|
# 0.11.12
|
117
125
|
|
@@ -121,104 +129,113 @@ Allow representable >= 1.6.
|
|
121
129
|
|
122
130
|
* Allow use of `::link(string)`.
|
123
131
|
|
124
|
-
|
132
|
+
# 0.11.10
|
125
133
|
|
126
134
|
* Fix a syntax error for Ruby 1.8.
|
127
|
-
* Store link definitions in `
|
135
|
+
* Store link definitions in `Representable_attrs(:links)` now and no longer in the `LinksDefinition` instance itself. removing `#links_definition` in favor of `#link_configs`.
|
128
136
|
|
129
|
-
|
137
|
+
# 0.11.9
|
130
138
|
|
131
139
|
* When using `Feature::Client` hyperlinks are no longer rendered in POST and PUT since we pass `links: false`.
|
132
140
|
* `Transport::NetHttp` now sets both `Accept:` and `Content-type:` header since Rails services seems to get confused.
|
133
141
|
|
134
|
-
|
142
|
+
# 0.11.8
|
135
143
|
|
136
144
|
* Fixed `JSON::HAL::Links` so that it keys links with `links` and not `_links`. The latter is still done by `JSON::HAL`.
|
137
145
|
|
138
|
-
|
146
|
+
# 0.11.7
|
139
147
|
|
140
148
|
* Maintenance release: Fixing the horrible bug fix from 0.11.6 and make it a bit less horrible.
|
141
149
|
|
142
|
-
|
150
|
+
# 0.11.6
|
143
151
|
|
144
152
|
* "Fixing" a bug where `links_definition_option` was missing when no link was set in a representer. Note that this is a quick and horrible bugfix and will soon be cleaned up.
|
145
153
|
|
146
|
-
|
154
|
+
# 0.11.5
|
147
155
|
|
148
|
-
* Introducing HAL::links method to map arrays of link objects in the HAL format. This completes the HAL/JSON specification.
|
156
|
+
* Introducing `HAL::links` method to map arrays of link objects in the HAL format. This completes the HAL/JSON specification.
|
149
157
|
|
150
|
-
|
158
|
+
# 0.11.4
|
151
159
|
|
152
160
|
* Links can now return a hash of attributes as `link :self do {:href => fruit_path(self), :title => "Yummy stuff"} end`.
|
153
161
|
|
154
|
-
|
162
|
+
# 0.11.3
|
155
163
|
|
156
164
|
* Fixed an installation issue under Windows.
|
157
165
|
|
158
|
-
|
166
|
+
# 0.11.2
|
159
167
|
|
160
168
|
* The request body in POST, PUT and PATCH is now actually sent in HttpVerbs. Thanks to @nleguen for finding this embarrassing bug. That's what happens when you don't have proper tests, kids!
|
161
169
|
|
162
|
-
|
170
|
+
# 0.11.1
|
171
|
+
|
172
|
+
* Since some users don't have access to my local hard-drive we now really require Representable 1.2.2.
|
163
173
|
|
164
|
-
|
174
|
+
# 0.11.0
|
165
175
|
|
166
|
-
|
176
|
+
* Using Representable 1.2.2 now. Be warned that in 1.2 parsing and rendering slightly changed. When a property is not found in the incoming document, it is ignored and thus might not be initialised in your represented model (empty collections are still set to an empty array). Also, the way `false` and `nil` values are rendered changed.
|
167
177
|
|
168
|
-
|
169
|
-
* A property with false value will now be included in the rendered representation. Same applies to parsing, false values will now be included. That particularly means properties that used to be unset (i.e. nil) after parsing might be false now.
|
170
|
-
* You can include nil values now in your representations since #property respects :represent_nil => true.
|
178
|
+
Quoted from the Representable CHANGES file:
|
171
179
|
|
172
|
-
*
|
173
|
-
*
|
174
|
-
<code>link :self, :title => "Mee!", "data-remote" => true</code>
|
175
|
-
When parsing, the options are avaible via `OpenStruct` compliant readers.
|
176
|
-
<code>link = Hyperlink.from_json({\"rel\":\"self\",\"data-url\":\"http://self\"} )
|
177
|
-
link.rel #=> "self"
|
178
|
-
link.send("data-url") #=> "http://self"
|
179
|
-
</code>
|
180
|
+
* A property with false value will now be included in the rendered representation. Same applies to parsing, false values will now be included. That particularly means properties that used to be unset (i.e. nil) after parsing might be false now.
|
181
|
+
* You can include nil values now in your representations since #property respects `:represent_nil => true`.
|
180
182
|
|
181
|
-
|
183
|
+
* The `:except` option was deprecated in favor of `:exclude`.
|
184
|
+
* Hyperlinks can now have arbitrary attributes.
|
185
|
+
To render, just provide `#link` with the options
|
186
|
+
|
187
|
+
```ruby
|
188
|
+
link :self, :title => "Mee!", "data-remote" => true
|
189
|
+
```
|
190
|
+
|
191
|
+
When parsing, the options are available via `OpenStruct` compliant readers.
|
192
|
+
|
193
|
+
```ruby
|
194
|
+
link = Hyperlink.from_json('{"rel":"self","data-url":"http://self"'})
|
195
|
+
link.rel #=> "self"
|
196
|
+
link.send("data-url") #=> "http://self"
|
197
|
+
```
|
198
|
+
|
199
|
+
# 0.10.2
|
182
200
|
|
183
201
|
* You can now pass values from outside to the render method (e.g. `#to_json`), they will be available as block parameters inside `#link`.
|
184
202
|
|
185
|
-
|
203
|
+
# 0.10.1
|
186
204
|
|
187
205
|
* Adding the Coercion feature.
|
188
206
|
|
189
|
-
|
207
|
+
# 0.10.0
|
190
208
|
|
191
|
-
* Requiring
|
209
|
+
* Requiring Representable 0.1.3.
|
192
210
|
* Added JSON-HAL support.
|
193
211
|
* Links are no longer rendered when `href` is `nil` or `false`.
|
194
212
|
* `Representer.link` class method now accepts either the `rel` value, only, or a hash of link attributes (defined in `Hypermedia::Hyperlink.params`), like `link :rel => :self, :title => "You're good" do..`
|
195
213
|
* API CHANGE: `Representer#links` no longer returns the `href` value but the link object. Use it like `object.links[:self].href` to retrieve the URL.
|
196
214
|
* `#from_json` won't throw an exception anymore when passed an empty json document.
|
197
215
|
|
198
|
-
|
216
|
+
# 0.9.2
|
199
217
|
|
200
|
-
* Using
|
218
|
+
* Using Representable 1.1.
|
201
219
|
|
202
|
-
|
220
|
+
# 0.9.1
|
203
221
|
|
204
|
-
* Removed
|
205
|
-
* Using
|
222
|
+
* Removed `Representer#to_attributes` and `#from_attributes`.
|
223
|
+
* Using Representable 1.0.1 now.
|
206
224
|
|
207
|
-
|
225
|
+
# 0.9.0
|
208
226
|
|
209
|
-
* Using
|
227
|
+
* Using Representable 0.12.x.
|
210
228
|
* `Representer::Base` is now simply `Representer`.
|
211
229
|
* Removed all the class methods from `HttpVerbs` except for `get`.
|
212
230
|
|
231
|
+
# 0.8.3
|
213
232
|
|
214
|
-
|
215
|
-
|
216
|
-
* Maintenance release for representable compat.
|
233
|
+
* Maintenance release for Representable compat.
|
217
234
|
|
218
|
-
|
235
|
+
# 0.8.2
|
219
236
|
|
220
237
|
* Removing `restfulie` dependency - we now use `Net::HTTP`.
|
221
238
|
|
222
|
-
|
239
|
+
# 0.8.1
|
223
240
|
|
224
|
-
* Added the
|
241
|
+
* Added the `:except` and `:include` options to `#from_*`.
|
data/CONTRIBUTING.md
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
## How to contribute to Roar
|
2
|
+
|
3
|
+
#### **Did you find a bug?**
|
4
|
+
|
5
|
+
* **Ensure the bug was not already reported** by searching on GitHub under [Issues](https://github.com/trailblazer/roar/issues).
|
6
|
+
|
7
|
+
* If you're unable to find an open issue addressing the problem, [open a new one](https://github.com/trailblazer/roar/issues/new). Be sure to follow the issue template.
|
8
|
+
|
9
|
+
#### **Did you write a patch that fixes a bug?**
|
10
|
+
|
11
|
+
* Open a new GitHub pull request with the patch.
|
12
|
+
|
13
|
+
* Ensure the PR description clearly describes the problem and solution. Include the relevant issue number if applicable.
|
14
|
+
|
15
|
+
* All code in pull requests is assumed to be MIT licensed. Do not submit a pull request if that isn't the case.
|
16
|
+
|
17
|
+
#### **Do you intend to add a new feature or change an existing one?**
|
18
|
+
|
19
|
+
* Suggest your change in the [Trailblazer Gitter Room](https://gitter.im/trailblazer/chat) and start writing code.
|
20
|
+
|
21
|
+
* Do not open an issue on GitHub until you have collected positive feedback about the change. GitHub issues are primarily intended for bug reports and fixes.
|
22
|
+
|
23
|
+
#### **Do you have questions using Roar?**
|
24
|
+
|
25
|
+
* Ask any questions about how to use Roar in the [Trailblazer Gitter Room](https://gitter.im/trailblazer/chat). Github issues are restricted to bug reports and fixes.
|
26
|
+
|
27
|
+
* GitHub Issues should not be used as a help forum and any such issues will be closed.
|
28
|
+
|
29
|
+
#### **Do you want to contribute to the Roar documentation?**
|
30
|
+
|
31
|
+
* Roar documentation is provided via the [Trailblazer site](http://trailblazer.to/gems/roar/) and not the repository readme. Please add your contributions to the [Trailblazer site repository](https://github.com/trailblazer/trailblazer.github.io)
|
data/Gemfile
CHANGED
@@ -3,5 +3,15 @@ source "http://rubygems.org"
|
|
3
3
|
# Specify your gem's dependencies in roar.gemspec
|
4
4
|
gemspec
|
5
5
|
|
6
|
-
|
7
|
-
|
6
|
+
install_if -> { RUBY_VERSION > '2.2.2' } do
|
7
|
+
gem 'sinatra', '~> 2.0.0.beta2'
|
8
|
+
gem 'sinatra-contrib', github: 'sinatra/sinatra'
|
9
|
+
end
|
10
|
+
|
11
|
+
gem 'nokogiri', '~> 1.6.8'
|
12
|
+
|
13
|
+
# gem "representable", path: "../representable"
|
14
|
+
# gem "representable", github: "apotonick/representable"
|
15
|
+
# gem "declarative", path: "../declarative"
|
16
|
+
gem "minitest-line"
|
17
|
+
gem "pry"
|
data/ISSUE_TEMPLATE.md
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Note: If you have a question about Roar, would like help using
|
2
|
+
Roar, want to request a feature, or do anything else other than
|
3
|
+
submit a bug report, please use the Trailblazer gitter channel.
|
4
|
+
|
5
|
+
### Complete Description of Issue
|
6
|
+
|
7
|
+
|
8
|
+
### Steps to reproduce
|
9
|
+
|
10
|
+
|
11
|
+
### Expected behavior
|
12
|
+
Tell us what should happen
|
13
|
+
|
14
|
+
### Actual behavior
|
15
|
+
Tell us what happens instead
|
16
|
+
|
17
|
+
### System configuration
|
18
|
+
**Roar version**:
|
19
|
+
|
20
|
+
### Full Backtrace of Exception (if any)
|
data/LICENSE
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
Copyright (c) 2011 -
|
1
|
+
Copyright (c) 2011 - 2017 Nick Sutterer and the roar contributors
|
2
2
|
|
3
3
|
Permission is hereby granted, free of charge, to any person obtaining
|
4
4
|
a copy of this software and associated documentation files (the
|
data/README.markdown
CHANGED
@@ -1,10 +1,47 @@
|
|
1
|
-
#
|
1
|
+
# Roar
|
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
|
-
[![Gem Version](https://badge.fury.io/rb/roar.svg)](http://badge.fury.io/rb/roar)
|
7
5
|
[![Gitter Chat](https://badges.gitter.im/trailblazer/chat.svg)](https://gitter.im/trailblazer/chat)
|
6
|
+
[![TRB Newsletter](https://img.shields.io/badge/TRB-newsletter-lightgrey.svg)](http://trailblazer.to/newsletter/)
|
7
|
+
[![Build Status](https://travis-ci.org/trailblazer/roar.svg?branch=master)](https://travis-ci.org/trailblazer/roar)
|
8
|
+
[![Gem Version](https://badge.fury.io/rb/roar.svg)](http://badge.fury.io/rb/roar)
|
9
|
+
|
10
|
+
## Table of Contents
|
11
|
+
|
12
|
+
* [Introduction](#introduction)
|
13
|
+
* [Representable](#representable)
|
14
|
+
* [Installation](#installation)
|
15
|
+
* [Dependencies](#dependencies)
|
16
|
+
* [Defining Representers](#defining-representers)
|
17
|
+
* [Rendering](#rendering)
|
18
|
+
* [Parsing](#parsing)
|
19
|
+
* [Module Representers](#module-representers)
|
20
|
+
* [Collections](#collections)
|
21
|
+
* [Nesting](#nesting)
|
22
|
+
* [Inline Representer](#inline-representer)
|
23
|
+
* [Syncing Objects](#syncing-objects)
|
24
|
+
* [Coercion](#coercion)
|
25
|
+
* [More Features](#more-features)
|
26
|
+
* [Hypermedia](#hypermedia)
|
27
|
+
* [Passing Options](#passing-options)
|
28
|
+
* [Specify Decorator](#specify-decorator)
|
29
|
+
* [Consuming Hypermedia](#consuming-hypermedia)
|
30
|
+
* [Media Formats](#media-formats)
|
31
|
+
* [HAL\-JSON](#hal-json)
|
32
|
+
* [Hypermedia](#hypermedia-1)
|
33
|
+
* [Nesting](#nesting-1)
|
34
|
+
* [JSON API](#json-api)
|
35
|
+
* [Client\-Side Support](#client-side-support)
|
36
|
+
* [HTTP Support](#http-support)
|
37
|
+
* [HTTPS](#https)
|
38
|
+
* [Basic Authentication](#basic-authentication)
|
39
|
+
* [Client SSL certificates](#client-ssl-certificates)
|
40
|
+
* [Request customization](#request-customization)
|
41
|
+
* [Error handling](#error-handling)
|
42
|
+
* [XML](#xml)
|
43
|
+
* [Support](#support)
|
44
|
+
* [License](#license)
|
8
45
|
|
9
46
|
## Introduction
|
10
47
|
|
@@ -12,15 +49,15 @@ Roar is a framework for parsing and rendering REST documents. Nothing more.
|
|
12
49
|
|
13
50
|
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.
|
14
51
|
|
15
|
-
Roar comes with built-in JSON, JSON-HAL
|
52
|
+
Roar comes with built-in JSON, JSON-HAL and XML support. JSON API support is available via the [JSON API](https://github.com/trailblazer/roar-jsonapi) gem. Its highly modular architecture provides features like coercion, hypermedia, HTTP transport, client caching and more.
|
16
53
|
|
17
|
-
Roar is completely framework-agnostic and loves being used in web kits like Rails,
|
54
|
+
Roar is completely framework-agnostic and loves being used in web kits like Rails, Hanami, Sinatra, Roda, etc. If you use Rails, consider [roar-rails](https://github.com/apotonick/roar-rails) for an enjoyable integration.
|
18
55
|
|
19
56
|
## Representable
|
20
57
|
|
21
|
-
Roar is just a thin layer on top of the [representable](https://github.com/
|
58
|
+
Roar is just a thin layer on top of the [representable](https://github.com/trailblazer/representable) gem. While Roar gives you a DSL and behaviour for creating hypermedia APIs, representable implements all the mapping functionality.
|
22
59
|
|
23
|
-
If in need for a feature, make sure to check the [representable API docs](https://github.com/
|
60
|
+
If in need for a feature, make sure to check the [representable API docs](https://github.com/trailblazer/representable) first.
|
24
61
|
|
25
62
|
## Installation
|
26
63
|
|
@@ -34,13 +71,13 @@ gem 'roar'
|
|
34
71
|
|
35
72
|
Roar does not bundle dependencies for JSON and XML.
|
36
73
|
|
37
|
-
If you want to use JSON, add the following to your Gemfile
|
74
|
+
If you want to use JSON, add the following to your `Gemfile`:
|
38
75
|
|
39
76
|
```ruby
|
40
77
|
gem 'multi_json'
|
41
78
|
```
|
42
79
|
|
43
|
-
If you want to use XML, add the following to your Gemfile
|
80
|
+
If you want to use XML, add the following to your `Gemfile`:
|
44
81
|
|
45
82
|
```ruby
|
46
83
|
gem 'nokogiri'
|
@@ -52,21 +89,22 @@ gem 'nokogiri'
|
|
52
89
|
Let's see how representers work. They're fun to use.
|
53
90
|
|
54
91
|
```ruby
|
92
|
+
require 'roar/decorator'
|
55
93
|
require 'roar/json'
|
56
94
|
|
57
|
-
|
95
|
+
class SongRepresenter < Roar::Decorator
|
58
96
|
include Roar::JSON
|
59
97
|
|
60
98
|
property :title
|
61
99
|
end
|
62
100
|
```
|
63
101
|
|
64
|
-
API documents are defined using a
|
102
|
+
API documents are defined using a decorator class. You can define plain attributes using the `::property` method.
|
65
103
|
|
66
|
-
Now let's assume we'd have `Song` which is an `ActiveRecord` class. Please note that Roar is not limited to ActiveRecord. In fact, it doesn't really care whether it's representing ActiveRecord,
|
104
|
+
Now let's assume we'd have `Song` which is an `ActiveRecord` class. Please note that Roar is not limited to ActiveRecord. In fact, it doesn't really care whether it's representing ActiveRecord, `Sequel::Model` or just an OpenStruct instance.
|
67
105
|
|
68
106
|
```ruby
|
69
|
-
class Song < ActiveRecord
|
107
|
+
class Song < ActiveRecord::Base
|
70
108
|
end
|
71
109
|
```
|
72
110
|
|
@@ -75,66 +113,62 @@ end
|
|
75
113
|
To render a document, you apply the representer to your model.
|
76
114
|
|
77
115
|
```ruby
|
78
|
-
song = Song.new(title: "
|
79
|
-
song.extend(SongRepresenter)
|
116
|
+
song = Song.new(title: "Medicine Balls")
|
80
117
|
|
81
|
-
song.to_json #=> {"title":"
|
118
|
+
SongRepresenter.new(song).to_json #=> {"title":"Medicine Balls"}
|
82
119
|
```
|
83
120
|
|
84
|
-
Here, the
|
121
|
+
Here, the `song` objects gets wrapped (or "decorated") by the decorator. It is treated as immutable - Roar won't mix in any behaviour.
|
85
122
|
|
86
123
|
## Parsing
|
87
124
|
|
88
125
|
The cool thing about representers is: they can be used for rendering and parsing. See how easy updating your model from a document is.
|
89
126
|
|
90
127
|
```ruby
|
91
|
-
song = Song.new
|
92
|
-
song.extend(SongRepresenter)
|
93
|
-
|
94
|
-
song.from_json('{"title":"Linoleum"}')
|
128
|
+
song = Song.new(title: "Medicine Balls")
|
95
129
|
|
130
|
+
SongRepresenter.new(song).from_json('{"title":"Linoleum"}')
|
96
131
|
song.title #=> Linoleum
|
97
132
|
```
|
98
133
|
|
99
|
-
Again, `#from_json` comes from the representer and just updates the known properties.
|
100
|
-
|
101
134
|
Unknown attributes in the parsed document are simply ignored, making half-baked solutions like `strong_parameters` redundant.
|
102
135
|
|
103
136
|
|
104
|
-
##
|
105
|
-
|
106
|
-
Many people dislike `#extend` due to eventual performance issue or object pollution. If you're one of those, just go with a decorator representer. They almost work identical to the module approach we just discovered.
|
137
|
+
## Module Representers
|
107
138
|
|
108
|
-
|
109
|
-
require 'roar/decorator'
|
139
|
+
**Module Representers are deprecated in Roar 1.1 and will be removed in Roar 2.0.**
|
110
140
|
|
111
|
-
|
112
|
-
include Roar::JSON
|
141
|
+
In place of inheriting from `Roar::Decorator`, you can also extend a singleton object with a representer module. Decorators and module representers actually have identical features. You can parse, render, nest, go nuts with both of them.
|
113
142
|
|
114
|
-
property :title
|
115
|
-
end
|
116
|
-
```
|
117
|
-
In place of a module you use a class, the DSL inside is the same you already know.
|
118
143
|
|
119
144
|
```ruby
|
120
|
-
song = Song.new(title: "
|
145
|
+
song = Song.new(title: "Fate")
|
146
|
+
song.extend(SongRepresenter)
|
121
147
|
|
122
|
-
|
148
|
+
song.to_json #=> {"title":"Fate"}
|
123
149
|
```
|
124
150
|
|
125
|
-
Here, the
|
151
|
+
Here, the representer is injected into the actual model and gives us a new `#to_json` method.
|
126
152
|
|
127
|
-
|
153
|
+
This also works both ways.
|
128
154
|
|
129
|
-
|
155
|
+
```ruby
|
156
|
+
song = Song.new
|
157
|
+
song.extend(SongRepresenter)
|
158
|
+
|
159
|
+
song.from_json('{"title":"Fate"}')
|
160
|
+
song #=> {"title":"Fate"}
|
161
|
+
```
|
162
|
+
|
163
|
+
It's worth noting though that many people dislike `#extend` due to well-known performance issues and object pollution. As such this approach is no longer recommended. In this README we'll use decorators to illustrate this library.
|
130
164
|
|
131
165
|
|
132
166
|
## Collections
|
133
167
|
|
134
|
-
Roar (or rather representable) also allows
|
168
|
+
Roar (or rather representable) also allows mapping collections in documents.
|
135
169
|
|
136
170
|
```ruby
|
137
|
-
|
171
|
+
class SongRepresenter < Roar::Decorator
|
138
172
|
include Roar::JSON
|
139
173
|
|
140
174
|
property :title
|
@@ -146,9 +180,8 @@ Where `::property` knows how to handle plain attributes, `::collection` does lis
|
|
146
180
|
|
147
181
|
```ruby
|
148
182
|
song = Song.new(title: "Roxanne", composers: ["Sting", "Stu Copeland"])
|
149
|
-
song.extend(SongRepresenter)
|
150
183
|
|
151
|
-
song.to_json #=> {"title":"Roxanne","composers":["Sting","Stu Copeland"]}
|
184
|
+
SongRepresenter.new(song).to_json #=> {"title":"Roxanne","composers":["Sting","Stu Copeland"]}
|
152
185
|
```
|
153
186
|
|
154
187
|
And, yes, this also works for parsing: `from_json` will create and populate the array of the `composers` attribute.
|
@@ -159,7 +192,7 @@ And, yes, this also works for parsing: `from_json` will create and populate the
|
|
159
192
|
Now what if we need to tackle with collections of `Song`s? We need to implement an `Album` class.
|
160
193
|
|
161
194
|
```ruby
|
162
|
-
class Album < ActiveRecord
|
195
|
+
class Album < ActiveRecord::Base
|
163
196
|
has_many :songs
|
164
197
|
end
|
165
198
|
```
|
@@ -167,7 +200,7 @@ end
|
|
167
200
|
Another representer to represent.
|
168
201
|
|
169
202
|
```ruby
|
170
|
-
|
203
|
+
class AlbumRepresenter < Roar::Decorator
|
171
204
|
include Roar::JSON
|
172
205
|
|
173
206
|
property :title
|
@@ -184,38 +217,35 @@ Consider the following object setup.
|
|
184
217
|
```ruby
|
185
218
|
album = Album.new(title: "True North")
|
186
219
|
album.songs << Song.new(title: "The Island")
|
187
|
-
album.songs << Song.new(:
|
220
|
+
album.songs << Song.new(title: "Changing Tide")
|
188
221
|
```
|
189
222
|
|
190
223
|
You apply the `AlbumRepresenter` and you get a nested document.
|
191
224
|
|
192
225
|
```ruby
|
193
|
-
|
194
|
-
|
195
|
-
album.to_json #=> {"title":"True North","songs":[{"title":"The Island"},{"title":"Changing Tide"}]}
|
226
|
+
AlbumRepresenter.new(album).to_json #=> {"title":"True North","songs":[{"title":"The Island"},{"title":"Changing Tide"}]}
|
196
227
|
```
|
197
228
|
|
198
229
|
This works vice-versa.
|
199
230
|
|
200
231
|
```ruby
|
201
232
|
album = Album.new
|
202
|
-
album.extend(AlbumRepresenter)
|
203
233
|
|
204
|
-
album.from_json('{"title":"Indestructible","songs":[{"title":"Tropical London"},{"title":"Roadblock"}]}')
|
234
|
+
AlbumRepresenter.new(album).from_json('{"title":"Indestructible","songs":[{"title":"Tropical London"},{"title":"Roadblock"}]}')
|
205
235
|
|
206
236
|
puts album.songs[1] #=> #<Song title="Roadblock">
|
207
237
|
```
|
208
238
|
|
209
239
|
The nesting of two representers can map composed object as you find them in many many APIs.
|
210
240
|
|
211
|
-
In case you're after virtual nesting, where a nested block in your document still maps to the same outer object, [check out the `::nested` method](https://github.com/
|
241
|
+
In case you're after virtual nesting, where a nested block in your document still maps to the same outer object, [check out the `::nested` method](https://github.com/trailblazer/representable#document-nesting).
|
212
242
|
|
213
243
|
## Inline Representer
|
214
244
|
|
215
245
|
Sometimes you don't wanna create two separate representers - although it makes them reusable across your app. Use inline representers if you're not intending this.
|
216
246
|
|
217
247
|
```ruby
|
218
|
-
|
248
|
+
class AlbumRepresenter < Roar::Decorator
|
219
249
|
include Roar::JSON
|
220
250
|
|
221
251
|
property :title
|
@@ -234,7 +264,7 @@ This will give you the same rendering and parsing behaviour as in the previous e
|
|
234
264
|
Usually, when parsing, nested objects are created from scratch. If you want nested objects to be updated instead of being newly created, use `parse_strategy:`.
|
235
265
|
|
236
266
|
```ruby
|
237
|
-
|
267
|
+
class AlbumRepresenter < Roar::Decorator
|
238
268
|
include Roar::JSON
|
239
269
|
|
240
270
|
property :title
|
@@ -248,14 +278,14 @@ This will advise Roar to update existing `songs`.
|
|
248
278
|
```ruby
|
249
279
|
album.songs[0].object_id #=> 81431220
|
250
280
|
|
251
|
-
album.from_json('{"title":"True North","songs":[{"title":"Secret Society"},{"title":"Changing Tide"}]}')
|
281
|
+
AlbumRepresenter.new(album).from_json('{"title":"True North","songs":[{"title":"Secret Society"},{"title":"Changing Tide"}]}')
|
252
282
|
|
253
283
|
album.songs[0].title #=> Secret Society
|
254
284
|
album.songs[0].object_id #=> 81431220
|
255
285
|
```
|
256
286
|
Roar didn't create a new `Song` instance but updated its attributes, only.
|
257
287
|
|
258
|
-
We're currently [working on](https://github.com/
|
288
|
+
We're currently [working on](https://github.com/trailblazer/roar/issues/85) better strategies to easily implement `POST` and `PUT` semantics in your APIs without having to worry about the nitty-gritties.
|
259
289
|
|
260
290
|
|
261
291
|
## Coercion
|
@@ -264,8 +294,9 @@ Roar provides coercion with the [virtus](https://github.com/solnic/virtus) gem.
|
|
264
294
|
|
265
295
|
```ruby
|
266
296
|
require 'roar/coercion'
|
297
|
+
require 'roar/json'
|
267
298
|
|
268
|
-
|
299
|
+
class SongRepresenter < Roar::Decorator
|
269
300
|
include Roar::JSON
|
270
301
|
include Roar::Coercion
|
271
302
|
|
@@ -278,9 +309,8 @@ The `:type` option allows to set a virtus-compatible type.
|
|
278
309
|
|
279
310
|
```ruby
|
280
311
|
song = Song.new
|
281
|
-
song.extend(SongRepresenter)
|
282
312
|
|
283
|
-
song.from_json('{"released_at":"1981/03/31"}')
|
313
|
+
SongRepresenter.new(song).from_json('{"released_at":"1981/03/31"}')
|
284
314
|
|
285
315
|
song.released_at #=> 1981-03-31T00:00:00+00:00
|
286
316
|
```
|
@@ -288,7 +318,7 @@ song.released_at #=> 1981-03-31T00:00:00+00:00
|
|
288
318
|
|
289
319
|
## More Features
|
290
320
|
|
291
|
-
Roar/representable gives you many more mapping features like
|
321
|
+
Roar/representable gives you many more mapping features like renaming attributes, wrapping, passing options, etc. See the [representable documentation](http://trailblazer.to/gems/representable/3.0/api.html) for a detailed explanation.
|
292
322
|
|
293
323
|
|
294
324
|
## Hypermedia
|
@@ -296,7 +326,7 @@ Roar/representable gives you many more mapping features like [renaming attribute
|
|
296
326
|
Roar comes with built-in support for embedding and processing hypermedia in your documents.
|
297
327
|
|
298
328
|
```ruby
|
299
|
-
|
329
|
+
class SongRepresenter < Roar::Decorator
|
300
330
|
include Roar::JSON
|
301
331
|
include Roar::Hypermedia
|
302
332
|
|
@@ -324,8 +354,7 @@ end
|
|
324
354
|
This will render links into your representation.
|
325
355
|
|
326
356
|
```ruby
|
327
|
-
|
328
|
-
song.to_json #=> {"title":"Roxanne","links":[{"rel":"self","href":"http://songs/Roxanne"}]}
|
357
|
+
SongRepresenter.new(song).to_json #=> {"title":"Roxanne","links":[{"rel":"self","href":"http://songs/Roxanne"}]}
|
329
358
|
```
|
330
359
|
|
331
360
|
Per default, links are pushed into the hash using the `links` key. Link blocks are executed in represented context, allowing you to call any instance method of your model (here, we call `#title`).
|
@@ -338,7 +367,7 @@ Also, note that [roar-rails](https://github.com/apotonick/roar-rails) allows usi
|
|
338
367
|
Sometimes you need more data in the link block. Data that's not available from the represented model.
|
339
368
|
|
340
369
|
```ruby
|
341
|
-
|
370
|
+
class SongRepresenter < Roar::Decorator
|
342
371
|
include Roar::JSON
|
343
372
|
|
344
373
|
property :title
|
@@ -352,20 +381,36 @@ end
|
|
352
381
|
Pass this data to the rendering method.
|
353
382
|
|
354
383
|
```ruby
|
355
|
-
|
384
|
+
representer = SongRepresenter.new(song)
|
385
|
+
representer.to_json(base_url: "localhost:3001/")
|
356
386
|
```
|
357
387
|
|
358
388
|
Any options passed to `#to_json` will be available as block arguments in the link blocks.
|
359
389
|
|
360
390
|
|
391
|
+
## Specify Decorator
|
392
|
+
|
393
|
+
If you have a property that is a separate class or model, you can specify a decorator for that property. Suppose there is a separate `Artist` model for an album. When the album is eagerly loaded, the artist model could be represented along with it.
|
394
|
+
|
395
|
+
```ruby
|
396
|
+
class ArtistRepresenter < Roar::Decorator
|
397
|
+
property :name
|
398
|
+
end
|
399
|
+
|
400
|
+
class AlbumRepresenter < Roar::Decorator
|
401
|
+
# ..
|
402
|
+
property :artist, decorator: ArtistRepresenter
|
403
|
+
end
|
404
|
+
```
|
405
|
+
|
361
406
|
## Consuming Hypermedia
|
362
407
|
|
363
408
|
Since we defined hypermedia attributes in the representer we can also consume this hypermedia when we parse documents.
|
364
409
|
|
365
410
|
```ruby
|
366
|
-
|
411
|
+
representer.from_json('{"title":"Roxanne","links":[{"rel":"self","href":"http://songs/Roxanne"}]}')
|
367
412
|
|
368
|
-
|
413
|
+
representer.links[:self].href #=> "http://songs/Roxanne"
|
369
414
|
```
|
370
415
|
|
371
416
|
Reading link attributes works by using `#links[]` on the consuming instance.
|
@@ -375,7 +420,7 @@ This allows an easy way to discover hypermedia and build navigational logic on t
|
|
375
420
|
|
376
421
|
## Media Formats
|
377
422
|
|
378
|
-
While Roar comes with a built-in hypermedia format, there's official media types that are widely recognized. Roar currently supports HAL and
|
423
|
+
While Roar comes with a built-in hypermedia format, there's official media types that are widely recognized. Roar currently supports HAL and JSON API.
|
379
424
|
|
380
425
|
Simply by including a module you make your representer understand the media type. This makes it easy to change formats during evaluation.
|
381
426
|
|
@@ -386,7 +431,7 @@ The [HAL](http://stateless.co/hal_specification.html) format is a simple media t
|
|
386
431
|
```ruby
|
387
432
|
require 'roar/json/hal'
|
388
433
|
|
389
|
-
|
434
|
+
class SongRepresenter < Roar::Decorator
|
390
435
|
include Roar::JSON::HAL
|
391
436
|
|
392
437
|
property :title
|
@@ -397,7 +442,7 @@ module SongRepresenter
|
|
397
442
|
end
|
398
443
|
```
|
399
444
|
|
400
|
-
Documentation for HAL can be found in the [API docs](http://rdoc.info/github/
|
445
|
+
Documentation for HAL can be found in the [API docs](http://rdoc.info/github/trailblazer/roar/Roar/JSON/HAL).
|
401
446
|
|
402
447
|
Make sure you [understand the different contexts](#hypermedia) for links when using decorators.
|
403
448
|
|
@@ -406,7 +451,7 @@ Make sure you [understand the different contexts](#hypermedia) for links when us
|
|
406
451
|
Including the `Roar::JSON::HAL` module adds some more DSL methods to your module. It still allows using `::link` but treats them slightly different.
|
407
452
|
|
408
453
|
```ruby
|
409
|
-
|
454
|
+
representer.to_json
|
410
455
|
#=> {"title":"Roxanne","_links":{"self":{"href":"http://songs/Roxanne"}}}
|
411
456
|
```
|
412
457
|
|
@@ -419,7 +464,7 @@ Parsing works like-wise: Roar will use the same setters as before but it knows h
|
|
419
464
|
Nested, or embedded, resources can be defined using the `:embedded` option.
|
420
465
|
|
421
466
|
```ruby
|
422
|
-
|
467
|
+
class AlbumRepresenter < Roar::Decorator
|
423
468
|
include Roar::JSON::HAL
|
424
469
|
|
425
470
|
property :title
|
@@ -433,187 +478,18 @@ end
|
|
433
478
|
To embed a resource, you can use an inline representer or use `:extend` to specify the representer name.
|
434
479
|
|
435
480
|
```ruby
|
436
|
-
album.to_json
|
481
|
+
AlbumRepresenter.new(album).to_json
|
437
482
|
|
438
483
|
#=> {"title":"True North","_embedded":{"songs":[{"title":"The Island"},{"title":"Changing Tide"}]}}
|
439
484
|
```
|
440
485
|
|
441
486
|
HAL keys nested resources under the `_embedded` key and then by their type.
|
442
487
|
|
443
|
-
All HAL features in Roar are discussed in the [API docs](http://rdoc.info/github/
|
444
|
-
|
445
|
-
|
446
|
-
## JSON-API
|
447
|
-
|
448
|
-
Roar also supports [JSON-API](http://jsonapi.org/) - yay! It can render _and_ parse singular and collection documents.
|
449
|
-
|
450
|
-
Note that you need representable >= 2.1.4 in your `Gemfile`.
|
451
|
-
|
452
|
-
### Resource
|
453
|
-
|
454
|
-
A minimal representation can be defined as follows.
|
455
|
-
|
456
|
-
```ruby
|
457
|
-
require 'roar/json/json_api'
|
458
|
-
|
459
|
-
module SongsRepresenter
|
460
|
-
include Roar::JSON::JSONAPI
|
461
|
-
type :songs
|
462
|
-
|
463
|
-
property :id
|
464
|
-
property :title
|
465
|
-
end
|
466
|
-
```
|
467
|
-
|
468
|
-
Properties of the represented model are defined in the root level.
|
469
|
-
|
470
|
-
### Hypermedia
|
471
|
-
|
472
|
-
You can add links to `linked` models within the resource section.
|
473
|
-
|
474
|
-
```ruby
|
475
|
-
module SongsRepresenter
|
476
|
-
# ...
|
477
|
-
|
478
|
-
has_one :composer
|
479
|
-
has_many :listeners
|
480
|
-
end
|
481
|
-
```
|
482
|
-
|
483
|
-
Global `links` can be added using the familiar `::link` method (this is still WIP as the DSL is not final).
|
484
|
-
|
485
|
-
```ruby
|
486
|
-
module SongsRepresenter
|
487
|
-
# ...
|
488
|
-
|
489
|
-
link "songs.album" do
|
490
|
-
{
|
491
|
-
type: "album",
|
492
|
-
href: "http://example.com/albums/{songs.album}"
|
493
|
-
}
|
494
|
-
end
|
495
|
-
end
|
496
|
-
```
|
497
|
-
|
498
|
-
### Compounds
|
499
|
-
|
500
|
-
To add compound models into the document, use `::compound`.
|
501
|
-
|
502
|
-
```ruby
|
503
|
-
module SongsRepresenter
|
504
|
-
# ...
|
505
|
-
|
506
|
-
compound do
|
507
|
-
property :album do
|
508
|
-
property :id
|
509
|
-
property :title
|
510
|
-
end
|
511
|
-
|
512
|
-
collection :musicians do
|
513
|
-
property :name
|
514
|
-
end
|
515
|
-
end
|
516
|
-
```
|
517
|
-
|
518
|
-
### Meta Data
|
519
|
-
|
520
|
-
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.
|
521
|
-
|
522
|
-
You can define meta data on your collection object and then let Roar compile it.
|
488
|
+
All HAL features in Roar are discussed in the [API docs](http://rdoc.info/github/trailblazer/roar/Roar/JSON/HAL), including [array links](https://github.com/trailblazer/roar/blob/master/lib/roar/json/hal.rb#L196).
|
523
489
|
|
524
|
-
|
525
|
-
module SongsRepresenter
|
526
|
-
# ..
|
527
|
-
|
528
|
-
meta do
|
529
|
-
property :page
|
530
|
-
property :total
|
531
|
-
end
|
532
|
-
```
|
533
|
-
|
534
|
-
Your collection object has to expose those methods.
|
535
|
-
|
536
|
-
```ruby
|
537
|
-
collection.page #=> 1
|
538
|
-
collection.total #=> 12
|
539
|
-
```
|
540
|
-
|
541
|
-
This will render the `{"meta": {"page": 1, "total": 12}}` hash into the JSON-API document.
|
542
|
-
|
543
|
-
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.
|
544
|
-
|
545
|
-
```ruby
|
546
|
-
collection.to_json("meta" => {page: params["page"], total: collection.size})
|
547
|
-
```
|
548
|
-
|
549
|
-
If you need more functionality (and parsing), please let us know.
|
550
|
-
|
551
|
-
### Usage
|
552
|
-
|
553
|
-
As JSON-API per definition can represent singular models and collections you have two entry points.
|
554
|
-
|
555
|
-
```ruby
|
556
|
-
SongsRepresenter.prepare(Song.find(1)).to_json
|
557
|
-
SongsRepresenter.prepare(Song.new).from_json("..")
|
558
|
-
```
|
559
|
-
|
560
|
-
Singular models can use the representer module directly.
|
561
|
-
|
562
|
-
```ruby
|
563
|
-
SongsRepresenter.for_collection.prepare([Song.find(1), Song.find(2)]).to_json
|
564
|
-
SongsRepresenter.for_collection.prepare([Song.new, Song.new]).from_json("..")
|
565
|
-
```
|
566
|
-
|
567
|
-
|
568
|
-
Parsing currently works great with singular documents - for collections, we are still working out how to encode the application semantics. Feel free to help.
|
569
|
-
|
570
|
-
|
571
|
-
## Collection+JSON
|
572
|
-
|
573
|
-
The [Collection+JSON media format](http://amundsen.com/media-types/collection/) defines document format and semantics for requests. It is currently experimental as we're still exploring how we optimize the support with Roar. Let us know if you're using it.
|
574
|
-
|
575
|
-
```ruby
|
576
|
-
module SongRepresenter
|
577
|
-
include Roar::JSON::CollectionJSON
|
578
|
-
version "1.0"
|
579
|
-
href { "http://localhost/songs/" }
|
580
|
-
|
581
|
-
property :title
|
582
|
-
|
583
|
-
items(:class => Song) do
|
584
|
-
href { "//songs/#{title}" }
|
585
|
-
|
586
|
-
property :title, :prompt => "Song title"
|
587
|
-
|
588
|
-
link(:download) { "//songs/#{title}.mp3" }
|
589
|
-
end
|
590
|
-
|
591
|
-
template do
|
592
|
-
property :title, :prompt => "Song title"
|
593
|
-
end
|
594
|
-
|
595
|
-
queries do
|
596
|
-
link :search do
|
597
|
-
{:href => "//search", :data => [{:name => "q", :value => ""}]}
|
598
|
-
end
|
599
|
-
end
|
600
|
-
end
|
601
|
-
```
|
602
|
-
|
603
|
-
It renders a document following the Collection+JSON specs.
|
604
|
-
|
605
|
-
```
|
606
|
-
#=> {"collection":{
|
607
|
-
"template":{"data":[{"name":"title","value":null}]},
|
608
|
-
"queries":[{"rel":"search","href":"//search","data":[{"name":"q","value":""}]}],
|
609
|
-
"version":"1.0",
|
610
|
-
"href":"http://localhost/songs/",
|
611
|
-
"title":"Roxanne",
|
612
|
-
"items":null}}
|
613
|
-
```
|
614
|
-
|
615
|
-
We have big plans with this media format, as the object model in Roar plays nicely with Collection+JSON's API semantics.
|
490
|
+
## JSON API
|
616
491
|
|
492
|
+
Roar also supports [JSON API](http://jsonapi.org/) via the [Roar JSON API gem](https://github.com/trailblazer/roar-jsonapi).
|
617
493
|
|
618
494
|
## Client-Side Support
|
619
495
|
|
@@ -622,7 +498,7 @@ Being a bi-directional mapper that does rendering _and_ parsing, Roar represente
|
|
622
498
|
Consider the following shared representer.
|
623
499
|
|
624
500
|
```ruby
|
625
|
-
|
501
|
+
class SongRepresenter < Roar::Decorator
|
626
502
|
include Roar::JSON
|
627
503
|
include Roar::Hypermedia
|
628
504
|
|
@@ -639,6 +515,7 @@ In a client where you don't have access to the database it is common to use `Ope
|
|
639
515
|
|
640
516
|
```ruby
|
641
517
|
require 'roar/client'
|
518
|
+
require 'roar/json'
|
642
519
|
|
643
520
|
class Song < OpenStruct
|
644
521
|
include Roar::JSON
|
@@ -734,7 +611,7 @@ rescue Roar::Transport::Error => exception
|
|
734
611
|
Roar also comes with XML support.
|
735
612
|
|
736
613
|
```ruby
|
737
|
-
|
614
|
+
class SongRepresenter < Roar::Decorator
|
738
615
|
include Roar::XML
|
739
616
|
include Roar::Hypermedia
|
740
617
|
|
@@ -751,9 +628,8 @@ Include the `Roar::XML` engine and get bi-directional XML for your objects.
|
|
751
628
|
|
752
629
|
```ruby
|
753
630
|
song = Song.new(title: "Roxanne", id: 42)
|
754
|
-
song.extend(XML::SongRepresenter)
|
755
631
|
|
756
|
-
song.to_xml
|
632
|
+
SongRepresenter.new(song).to_xml
|
757
633
|
```
|
758
634
|
|
759
635
|
Note that you now use `#to_xml` and `#from_xml`.
|
@@ -766,7 +642,7 @@ Note that you now use `#to_xml` and `#from_xml`.
|
|
766
642
|
</song>
|
767
643
|
```
|
768
644
|
|
769
|
-
Please consult the [representable XML documentation](https://github.com/
|
645
|
+
Please consult the [representable XML documentation](https://github.com/trailblazer/representable/#more-on-xml) for all its great features.
|
770
646
|
|
771
647
|
|
772
648
|
## Support
|