api-presenter 0.0.1 → 0.0.2

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.
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