api-presenter 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # Api::Presenter
2
2
 
3
- TODO: Write a gem description
3
+ This gem builds the basics for presenting your data using the media type described in the [api doc](https://github.com/ncuesta/api-doc). Here you will find classes to represent your resources
4
+ and also the functions to convert them to json.
4
5
 
5
6
  ## Installation
6
7
 
@@ -18,7 +19,340 @@ Or install it yourself as:
18
19
 
19
20
  ## Usage
20
21
 
21
- TODO: Write usage instructions here
22
+ So, first you should now that there are three kinds of resources:
23
+
24
+ * A simple resource (Api::Presenter::Resource) that represents a single unit of information.
25
+ * A collection resource (Api::Presenter::CollectionResource) that represents an homogeneous group of resources.
26
+ * A search resource (Api::Presenter::SearchResource) it's like a collection resource but adds the query information.
27
+
28
+ ### Simple Resource
29
+
30
+ Now, most probably you are needing to represent some class that contains your model information using the media type
31
+ described by [api-doc](https://github.com/ncuesta/api-doc). For this you will be needing two things:
32
+
33
+ 1. Create a class that represents your resource.
34
+ 2. Let your model class know how to turn into a resource.
35
+
36
+ Let's say you have a Person model:
37
+
38
+ ```ruby
39
+ class Person
40
+ attr_accessor :name, :age
41
+
42
+ def initialize(name, age)
43
+ @name = name
44
+ @age = age
45
+ end
46
+ end
47
+ ```
48
+
49
+ In order to be able to represent this resource we need to have a PersonResource class
50
+
51
+ ```ruby
52
+ class PersonResource < Api::Presenter::Resource
53
+ def self.hypermedia_properties
54
+ {
55
+ simple: [:name, :age],
56
+ resource: []
57
+ }
58
+ end
59
+
60
+ def self_link
61
+ "/person/#{@resource.name}"
62
+ end
63
+ end
64
+ ```
65
+
66
+ The class method ```hypermedia_properties``` describes the model properties:
67
+
68
+ * simple: An array containing method names that represent simple data (integers, strings, dates, etc.).
69
+ * resource: An array containing method names thar represent related resources.
70
+
71
+ The self_link definition tell which is the link that represents itself.
72
+
73
+ So now there is only one more thing left to do: Add the to_resource method used to convert the model
74
+ into a resource.
75
+
76
+ ```ruby
77
+ class Person
78
+ def to_resource
79
+ PersonResource.new(self)
80
+ end
81
+ end
82
+ ```
83
+
84
+ And that's it, now we can get the representation in json using.
85
+
86
+ ```ruby
87
+ data = Person.new("Alvaro", 27)
88
+
89
+ resource = data.to_resource # or just PersonResource.new(data)
90
+
91
+ Api::Presenter::Hypermedia.present resource
92
+ ```
93
+
94
+ It will look like this:
95
+ ```json
96
+ {
97
+ "links":
98
+ {
99
+ "self":
100
+ {
101
+ "href": "/person/Alvaro"
102
+ }
103
+ }
104
+ "name": "Alvaro",
105
+ "age": 27
106
+ }
107
+ ```
108
+ ### Related resources
109
+
110
+ It's very common that our model is related to others, and we may want to show this in our representation.
111
+ To do so, need to create a resource class for each one and add them to our ```resource``` array in
112
+ hypermedia_properties.
113
+
114
+ Using the example above, now our person has a dog. So:
115
+
116
+ ```ruby
117
+ class DogResource < Api::Presenter::Resource
118
+ def self.hypermedia_properties
119
+ {
120
+ simple: [:name],
121
+ resource: [:owner]
122
+ }
123
+ end
124
+
125
+ def self_link
126
+ "/dog/#{@resource.name}"
127
+ end
128
+ end
129
+
130
+ class Dog
131
+ attr_accessor :name, :owner
132
+
133
+ def initialize(name, owner)
134
+ @name = name
135
+ @owner = owner
136
+ end
137
+
138
+ def to_resource
139
+ DogResource.new(self)
140
+ end
141
+ end
142
+
143
+ class PersonResource < Api::Presenter::Resource
144
+ def self.hypermedia_properties
145
+ {
146
+ simple: [:name, :age],
147
+ resource: [:dog]
148
+ }
149
+ end
150
+ end
151
+ ```
152
+
153
+ Finally we present it:
154
+
155
+ ```ruby
156
+ person = Person.new("Alvaro", 27)
157
+
158
+ dog = Dog.new("Cleo", person)
159
+
160
+ person_resource = person.to_resource # or just PersonResource.new(person)
161
+
162
+ Api::Presenter::Hypermedia.present person_resource
163
+ ```
164
+
165
+ It will look like this:
166
+
167
+ ```json
168
+ {
169
+ "links":
170
+ {
171
+ "self":
172
+ {
173
+ "href": "/person/Alvaro"
174
+ },
175
+ "dog":
176
+ {
177
+ "href": "/dog/Cleo"
178
+ }
179
+ }
180
+ "name": "Alvaro",
181
+ "age": 27
182
+ }
183
+ ```
184
+
185
+ ### Collection resource
186
+
187
+ A collection resource it's basically any collection that contains objects that responds to ```to_resource```,
188
+ also must respond to:
189
+
190
+ * total
191
+ * offset
192
+ * limit
193
+ * each
194
+
195
+ Now, using the Person example:
196
+
197
+ ```ruby
198
+ # building a collection on top of array that responds to each, total, limit and offset
199
+ class Collection
200
+ attr_reader :col
201
+
202
+ def initialize(col = [])
203
+ @col = col
204
+ end
205
+
206
+ def each(&block)
207
+ @col.each(&block)
208
+ end
209
+
210
+ def total
211
+ @col.count
212
+ end
213
+
214
+ def limit
215
+ 10
216
+ end
217
+
218
+ def offset
219
+ 0
220
+ end
221
+ end
222
+
223
+ family = Collection.new([Person.new("Joe", 50), Person.new("Jane", 45), Person.new("Timmy", 10), Person.new("Sussie", 12)])
224
+
225
+ class FamilyResource < Api::Presenter::CollectionResource
226
+ def self_link
227
+ "/family"
228
+ end
229
+ end
230
+
231
+ Api::Presenter::Hypermedia.present FamilyResource.new(family)
232
+ ```
233
+
234
+ It will look like this:
235
+
236
+ ```json
237
+ {
238
+ "links":
239
+ {
240
+ "self":
241
+ {
242
+ "href": "/family"
243
+ },
244
+ }
245
+ "offset": 0,
246
+ "limit": 10,
247
+ "total": 4,
248
+ "entries":
249
+ [
250
+ {
251
+ "self":
252
+ {
253
+ "href" : "/person/Joe"
254
+ }
255
+ },
256
+ {
257
+ "self":
258
+ {
259
+ "href" : "/person/Jane"
260
+ }
261
+ },
262
+ {
263
+ "self":
264
+ {
265
+ "href" : "/person/Timmy"
266
+ },
267
+ },
268
+ {
269
+ "self":
270
+ {
271
+ "href" : "/person/Sussie"
272
+ }
273
+ }
274
+ ]
275
+ }
276
+ ```
277
+
278
+ ### Search resource
279
+
280
+ This is a special case of ```Api::Presenter::CollectionResource``` where it also has a query string and parameters.
281
+ The main difference with a Collection is that it receives as a parameter, the parameters which where used to build
282
+ the collection and also adds them to the response.
283
+
284
+ The method ```self.hypermedia_query_parameters``` determins which parameters are used in the search. It's later used
285
+ by the helper method ```query_string``` to build the query string.
286
+
287
+ ```ruby
288
+ class PersonSearchResource < Api::Presenter::SearchResource
289
+ def self.hypermedia_query_parameters
290
+ ["name", "age"]
291
+ end
292
+
293
+ def self_link
294
+ "/search_person#{query_string}"
295
+ end
296
+ end
297
+
298
+ search = PersonSearchResource.new(Collection.new([Person.new("Joe", 50), Person.new("Jane", 45)]), age: 45)
299
+
300
+ Api::Presenter::Hypermedia.present search
301
+ ```
302
+
303
+ It will look like this:
304
+
305
+ ```json
306
+ {
307
+ "links":
308
+ {
309
+ "self":
310
+ {
311
+ "href": "/search_person?query[age]=45query[name]="
312
+ },
313
+ }
314
+ "offset": 0,
315
+ "limit": 10,
316
+ "total": 2,
317
+ "query":
318
+ {
319
+ "age": 45,
320
+ "name": nil
321
+ },
322
+ "entries":
323
+ [
324
+ {
325
+ "self":
326
+ {
327
+ "href" : "/person/Joe"
328
+ }
329
+ },
330
+ {
331
+ "self":
332
+ {
333
+ "href" : "/person/Jane"
334
+ }
335
+ }
336
+ ]
337
+ }
338
+ ```
339
+
340
+ ### Adding additional links
341
+
342
+ When building a resource there may be the need to build custom links. In order to do so, you need to define
343
+ two methods in your resource class:
344
+
345
+ ```ruby
346
+ def custom_link
347
+ "/path/to/custom_link"
348
+ end
349
+
350
+
351
+ def custom_link?(options = {})
352
+ a_condition_that_determins_if_it_should_be_displayed returning true or false
353
+ end
354
+ ```
355
+
22
356
 
23
357
  ## Contributing
24
358
 
@@ -27,7 +27,7 @@ module Api
27
27
  if entries_property
28
28
  representation[entries_property.to_s] = []
29
29
  resource.send(entries_property).each do |nested_resource|
30
- representation[entries_property.to_s] << nested_resource.to_resource.links(embed: true)
30
+ representation[entries_property.to_s] << build_links(nested_resource.to_resource, embed: true)
31
31
  end
32
32
  end
33
33
 
@@ -1,5 +1,5 @@
1
1
  module Api
2
2
  module Presenter
3
- VERSION = "0.0.1"
3
+ VERSION = "0.0.2"
4
4
  end
5
5
  end
@@ -21,27 +21,39 @@ describe Api::Presenter::Hypermedia do
21
21
  "entries" =>
22
22
  [
23
23
  {
24
- "self" =>
24
+ "links" =>
25
25
  {
26
- "href" => "/path/to/single_resource/1"
26
+ "self" =>
27
+ {
28
+ "href" => "/path/to/single_resource/1"
29
+ }
27
30
  }
28
31
  },
29
32
  {
30
- "self" =>
33
+ "links" =>
31
34
  {
32
- "href" => "/path/to/single_resource/2"
35
+ "self" =>
36
+ {
37
+ "href" => "/path/to/single_resource/2"
38
+ }
33
39
  }
34
40
  },
35
41
  {
36
- "self" =>
42
+ "links" =>
37
43
  {
38
- "href" => "/path/to/single_resource/3"
44
+ "self" =>
45
+ {
46
+ "href" => "/path/to/single_resource/3"
47
+ }
39
48
  }
40
49
  },
41
50
  {
42
- "self" =>
51
+ "links" =>
43
52
  {
44
- "href" => "/path/to/single_resource/4"
53
+ "self" =>
54
+ {
55
+ "href" => "/path/to/single_resource/4"
56
+ }
45
57
  }
46
58
  }
47
59
  ]
@@ -91,30 +103,44 @@ describe Api::Presenter::Hypermedia do
91
103
  },
92
104
 
93
105
  "entries" =>
94
- [ {
95
- "self" =>
106
+ [
107
+ {
108
+ "links" =>
96
109
  {
97
- "href" => "/path/to/single_resource/1"
110
+ "self" =>
111
+ {
112
+ "href" => "/path/to/single_resource/1"
113
+ }
98
114
  }
99
115
  },
100
116
  {
101
- "self" =>
117
+ "links" =>
102
118
  {
103
- "href" => "/path/to/single_resource/2"
119
+ "self" =>
120
+ {
121
+ "href" => "/path/to/single_resource/2"
122
+ }
104
123
  }
105
124
  },
106
125
  {
107
- "self" =>
126
+ "links" =>
108
127
  {
109
- "href" => "/path/to/single_resource/3"
128
+ "self" =>
129
+ {
130
+ "href" => "/path/to/single_resource/3"
131
+ }
110
132
  }
111
133
  },
112
134
  {
113
- "self" =>
135
+ "links" =>
114
136
  {
115
- "href" => "/path/to/single_resource/4"
137
+ "self" =>
138
+ {
139
+ "href" => "/path/to/single_resource/4"
140
+ }
116
141
  }
117
- } ]
142
+ }
143
+ ]
118
144
  }
119
145
  end
120
146
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: api-presenter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-06-03 00:00:00.000000000 Z
12
+ date: 2013-06-04 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler