roar 0.12.1 → 0.12.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -42,6 +42,10 @@ MiniTest::Spec.class_eval do
42
42
  end
43
43
  end
44
44
  end
45
+
46
+ def self.representer!(*args)
47
+ representer_for(*args)
48
+ end
45
49
  end
46
50
 
47
51
  Roar::Representer::Feature::Hypermedia::Hyperlink.class_eval do
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: 0.12.1
4
+ version: 0.12.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: 2013-11-27 00:00:00.000000000 Z
11
+ date: 2013-12-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: representable
@@ -135,9 +135,11 @@ files:
135
135
  - CHANGES.markdown
136
136
  - Gemfile
137
137
  - LICENSE
138
- - README.textile
138
+ - README.markdown
139
139
  - Rakefile
140
140
  - TODO.markdown
141
+ - examples/example.rb
142
+ - examples/example_server.rb
141
143
  - gemfiles/Gemfile.representable-1.5.ruby-1.8
142
144
  - gemfiles/Gemfile.representable-1.5.ruby-1.9
143
145
  - gemfiles/Gemfile.representable-1.6.ruby-1.9
@@ -1,331 +0,0 @@
1
- h1. ROAR
2
-
3
- _Resource-Oriented Architectures in Ruby._
4
-
5
- h2. Introduction
6
-
7
- Roar is a framework for parsing and rendering REST documents. Nothing more.
8
-
9
-
10
-
11
- coercion
12
-
13
-
14
- With Roar, REST documents - also known as representations - are defined using a new concept called representers. Both syntax and semantics are declared in Ruby modules that can be mixed into your domain models, following clean OOP patterns.
15
-
16
- Roar comes with built-in JSON, JSON::HAL and XML support. It exposes a highly modular architecture and makes it very simple to add new media types and functionality where needed. Additional features include client HTTP support, coercion, client-side caching, awesome hypermedia support and more. Representers fit pretty well in DCI environments, too.
17
-
18
- Roar is completely framework-agnostic and loves being used in web kits like Rails, Webmachine, Sinatra, Padrino, etc. Actually, Roar makes it fun designing real, hypermedia-driven, and resource-oriented systems that will even make Steve sleep happily at night so he finally gets some REST!
19
-
20
- Note: This README sucks and will be updated this week (April 10, 2013).
21
-
22
- h2. Example
23
-
24
- Say your webshop consists of two completely separated apps. The REST backend, a Sinatra app, serves articles and processes orders. The frontend, being browsed by your clients, is a rich Rails application. It queries the services for articles, renders them nicely and reads or writes orders with REST calls. That being said, the frontend turns out to be a pure REST client.
25
-
26
-
27
- h2. Representations
28
-
29
- Representations are the pivotal elements of REST. Work in a REST system means working with representations, which can be put down to parsing or extracting representations and rendering the like.
30
-
31
- Roar makes it easy to render and parse representations of resources after defining the formats.
32
-
33
-
34
- h3. Creating Representations
35
-
36
- Why not GET a particular article, what about a good beer?
37
-
38
- @GET http://articles/lonestarbeer@
39
-
40
- It's cheap and it's good. The response of a GET is a representation of the requested resource. A *representation* is always a *document*. In this example, it's a bit of JSON.
41
-
42
- pre. { "article": {
43
- "title": "Lonestar Beer",
44
- "id": 4711,
45
- "links":[
46
- { "rel": "self",
47
- "href": "http://articles/lonestarbeer"}
48
- ]}
49
- }
50
-
51
- p. In addition to boring article data, there's a _link_ embedded in the document. This is *hypermedia*, yeah! We will learn more about that shortly.
52
-
53
- So, how did the service render that JSON document? It could use an ERB template, @#to_json@, or maybe another gem. The document could also be created by a *representer*.
54
-
55
- Representers are the key ingredience in Roar, so let's check them out!
56
-
57
-
58
- h2. Representers
59
-
60
- Representers are most usable when defined in a module, and then mixed into a host class. In our example, the host class is the article.
61
-
62
- <pre>
63
- class Article
64
- attr_accessor :title, :id
65
- end
66
- </pre>
67
-
68
- To render a representational document from the article, the backend service has to define a representer.
69
-
70
- <pre>
71
- require 'roar/representer/json'
72
- require 'roar/representer/feature/hypermedia'
73
-
74
-
75
- module ArticleRepresenter
76
- include Roar::Representer::JSON
77
- include Roar::Representer::Feature::Hypermedia
78
-
79
- property :title
80
- property :id
81
-
82
- link :self do
83
- article_url(self)
84
- end
85
- end
86
- </pre>
87
-
88
- Hooray, we can define plain properties and embedd links easily - and we can even use URL helpers (in Rails, using the "roar-rails gem":https://github.com/apotonick/roar-rails). There's even more, nesting, collections, but more on that later!
89
-
90
-
91
- h3. Rendering Representations in the Service
92
-
93
- In order to *render* an actual document, the backend service would have to do a few steps: creating a representer, filling in data, and then serialize it.
94
-
95
- <pre>loney = Article.new.extend(ArticleRepresenter)
96
- loney.title = "Lonestar"
97
- loney.id = 666
98
- loney.to_json # => "{\"article\":{\"id\":666, ...
99
- </pre>
100
-
101
- Articles itself are useless, so they may be placed into orders. This is the next example.
102
-
103
-
104
- h3. Nesting Representations
105
-
106
- What if we wanted to check an existing order? We'd @GET http://orders/1@, right?
107
-
108
- <pre>{ "order": {
109
- "id": 1,
110
- "client_id": "815",
111
- "articles": [
112
- {"title": "Lonestar Beer",
113
- "id": 666,
114
- "links":[
115
- { "rel": "self",
116
- "href": "http://articles/lonestarbeer"}
117
- ]}
118
- ],
119
- "links":[
120
- { "rel": "self",
121
- "href": "http://orders/1"},
122
- { "rel": "items",
123
- "href": "http://orders/1/items"}
124
- ]}
125
- }
126
- </pre>
127
-
128
- The order model is simple.
129
-
130
- <pre>
131
- class Order
132
- attr_accessor :id, :client_id, :articles
133
- end
134
- </pre>
135
-
136
- Since orders may contain a composition of articles, how would the order service define its representer?
137
-
138
- <pre>
139
- module OrderRepresenter
140
- include Roar::Representer::JSON
141
- include Roar::Representer::Feature::Hypermedia
142
-
143
- property :id
144
- property :client_id
145
-
146
- collection :articles, :class => Article
147
-
148
- link :self do
149
- order_url(represented)
150
- end
151
-
152
- link :items do
153
- items_url
154
- end
155
- end
156
- </pre>
157
-
158
- Representers don't have to be in modules, but can be
159
-
160
- The declarative @#collection@ method lets us define compositions of representers.
161
-
162
-
163
- h3. Parsing Documents in the Service
164
-
165
- Rendering stuff is easy: Representers allow defining the layout and serializing documents for us. However, representers can do more. They work _bi-directional_ in terms of rendering outgoing _and_ parsing incoming representation documents.
166
-
167
- If we were to implement an endpoint for creating new orders, we'd allow POST to @http://orders/@. Let's explore the service code for parsing and creation.
168
-
169
- <pre>
170
- post "/orders" do
171
- order = Order.new.extend(OrderRepresenter)
172
- order.from_json(request.body.string)
173
- order.to_json
174
- end
175
- </pre>
176
-
177
- Look how the @#from_json@ method helps extracting data from the incoming document and, again, @#to_json@ returns the freshly created order's representation. Roar's representers are truely working in both directions, rendering and parsing and thus prevent you from redundant knowledge sharing.
178
-
179
-
180
- h2. Representers in the Client
181
-
182
- The new representer abstraction layer seems complex and irritating first, where you used @params[]@ and @#to_json@ is a new OOP instance now. But... the cool thing is: You can package representers in gems and distribute them to your client layer as well. In our example, the web frontend can take advantage of the representers, too.
183
-
184
-
185
- h3. Using HTTP
186
-
187
- Communication between REST clients and services happens via HTTP - clients request, services respond. There are plenty of great gems helping out, namely Restfulie, HTTParty, etc. Representers in Roar provide support for HTTP as well, given you mix in the @HTTPVerbs@ feature module!
188
-
189
- To create a new order, the frontend needs to POST to the REST backend. Here's how this could happen using a representer on HTTP.
190
-
191
-
192
- <pre>
193
- order = Order.new(:client_id => current_user.id)
194
- order.post("http://orders/")
195
- </pre>
196
-
197
- A couple of noteworthy steps happen here.
198
-
199
- # Using the constructor a blank order document is created.
200
- # Initial values like the client's id are passed as arguments and placed in the document.
201
- # The filled-out document is POSTed to the given URL.
202
- # The backend service creates an actual order record and sends back the representation.
203
- # In the @#post@ call, the returned document is parsed and attributes in the representer instance are updated accordingly,
204
-
205
- After the HTTP roundtrip, the order instance keeps all the information we need for proceeding the ordering workflow.
206
-
207
- <pre>
208
- order.id #=> 42
209
- </pre>
210
-
211
- h3. Discovering Hypermedia
212
-
213
- Now that we got a fresh order, let's place some items! The system's API allows adding articles to an existing order by POSTing articles to a specific resource. This endpoint is propagated in the order document using *hypermedia*.
214
-
215
- Where and what is this hypermedia?
216
-
217
- First, check the JSON document we get back from the POST.
218
-
219
- <pre>{ "order": {
220
- "id": 42,
221
- "client_id": 1337,
222
- "articles": [],
223
- "links":[
224
- { "rel": "self",
225
- "href": "http://orders/42"},
226
- { "rel": "items",
227
- "href": "http://orders/42/items"}
228
- ]}
229
- }
230
- </pre>
231
-
232
- Two hypermedia links are embedded in this representation, both feature a @rel@ attribute for defining a link semantic - a "meaning" - and a @href@ attribute for a network address. Isn't that great?
233
-
234
- * The @self@ link refers to the actual resource. It's a REST best practice and representations should always refer to their resource address.
235
- * The @items@ link is what we want. The address @http://orders/42/items@ is what we have to refer to when adding articles to this order. Why? Cause we decided that!
236
-
237
-
238
- h3. Using Hypermedia
239
-
240
- Let the frontend add the delicious "Lonestar" beer to our order, now!
241
-
242
- <pre>
243
- beer = Article.new(:title => "Lonestar Beer")
244
- beer.post(order.links[:items])
245
- </pre>
246
-
247
- That's all we need to do.
248
-
249
- # First, we create an appropriate article representation.
250
- # Then, the @#links@ method helps extracting the @items@ link URL from the order document.
251
- # A simple POST to the respective address places the item in the order.
252
-
253
- The @order@ instance in the frontend is now stale - it doesn't contain articles, yet, since it is still the document from the first post to @http://orders/@.
254
-
255
- <pre>
256
- order.items #=> []
257
- </pre>
258
-
259
- To update attributes, a GET is needed.
260
-
261
- <pre>
262
- order.get!(order.links[:self])
263
- </pre>
264
-
265
- Again, we use hypermedia to retrieve the order's URL. And now, the added article is included in the order.
266
-
267
- [*Note:* If this looks clumsy - It's just the raw API for representers. You might be interested in the upcoming DSL that simplifys frequent workflows as updating a representer.]
268
-
269
- <pre>
270
- order.to_attributes #=> {:id => 42, :client_id => 1337,
271
- :articles => [{:title => "Lonestar Beer", :id => 666}]}
272
- </pre>
273
-
274
- This is cool, we used REST representers and hypermedia to create an order and fill it with articles. It's time for a beer, isn't it?
275
-
276
-
277
- h3. Using Accessors
278
-
279
- What if the ordering API is going a different way? What if we had to place articles into the order document ourselves, and then PUT this representation to @http://orders/42@? No problem with representers!
280
-
281
- Here's what could happen in the frontend.
282
-
283
- <pre>
284
- beer = Article.new(:title => "Lonestar Beer")
285
- order.items << beer
286
- order.post(order.links[:self])
287
- </pre>
288
-
289
- This was dead simple since representations can be composed of different documents in Roar.
290
-
291
- h2. Decorators
292
-
293
- You can use `Roar::Decorator` as a representer. More docs coming soon. Note that if you want your represented object to save incoming hypermedia you need to do two things.
294
-
295
- <pre>
296
- class SongClient
297
- # do whatever here
298
-
299
- attr_accessor :links
300
- end
301
-
302
- class SongRepresenter < Roar::Decorator
303
- include Roar::Representer::JSON
304
- include Roar::Decorator::HypermediaConsumer
305
- end
306
- </pre>
307
-
308
-
309
- h2. More Features
310
-
311
- Be sure to check out the bundled features.
312
-
313
- # *Coercion* transforms values to typed objects when parsing a document. Uses virtus.
314
- # *Faraday* support, if you want to use it install the Faraday gem and require 'roar/representer/transport/faraday' and configure 'Roar::Representer::Feature::HttpVerbs.transport_engine = Roar::Representer::Transport::Faraday'
315
-
316
- h2. What is REST about?
317
-
318
- Making that system RESTful basically means
319
-
320
- # The frontend knows one _single entry point_ URL to the REST services. This is @http://orders@.
321
- # Do _not_ let the frontend compute any URLs to further actions.
322
- # Showing articles, creating a new order, adding articles to it and finally placing the order - this all requires further URLs. These URLs are embedded as _hypermedia_ in the representations sent by the REST backend.
323
-
324
- h2. Support
325
-
326
- Questions? Need help? Free 1st Level Support on irc.freenode.org#roar !
327
- We also have a "mailing list":https://groups.google.com/forum/?fromgroups#!forum/roar-talk, yiha!
328
-
329
- h2. License
330
-
331
- Roar is released under the "MIT License":http://www.opensource.org/licenses/MIT.