restfully 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. data/.document +5 -0
  2. data/.gitignore +5 -0
  3. data/LICENSE +20 -0
  4. data/README.rdoc +90 -0
  5. data/Rakefile +75 -0
  6. data/TODO.rdoc +3 -0
  7. data/VERSION +1 -0
  8. data/bin/restfully +80 -0
  9. data/examples/grid5000.rb +28 -0
  10. data/lib/restfully.rb +19 -0
  11. data/lib/restfully/collection.rb +63 -0
  12. data/lib/restfully/error.rb +4 -0
  13. data/lib/restfully/extensions.rb +41 -0
  14. data/lib/restfully/http.rb +9 -0
  15. data/lib/restfully/http/adapters/abstract_adapter.rb +30 -0
  16. data/lib/restfully/http/adapters/patron_adapter.rb +16 -0
  17. data/lib/restfully/http/adapters/rest_client_adapter.rb +31 -0
  18. data/lib/restfully/http/error.rb +20 -0
  19. data/lib/restfully/http/headers.rb +20 -0
  20. data/lib/restfully/http/request.rb +24 -0
  21. data/lib/restfully/http/response.rb +19 -0
  22. data/lib/restfully/link.rb +35 -0
  23. data/lib/restfully/parsing.rb +31 -0
  24. data/lib/restfully/resource.rb +117 -0
  25. data/lib/restfully/session.rb +61 -0
  26. data/lib/restfully/special_array.rb +5 -0
  27. data/lib/restfully/special_hash.rb +5 -0
  28. data/restfully.gemspec +99 -0
  29. data/spec/collection_spec.rb +93 -0
  30. data/spec/fixtures/grid5000-sites.json +489 -0
  31. data/spec/http/error_spec.rb +18 -0
  32. data/spec/http/headers_spec.rb +17 -0
  33. data/spec/http/request_spec.rb +45 -0
  34. data/spec/http/response_spec.rb +15 -0
  35. data/spec/http/rest_client_adapter_spec.rb +33 -0
  36. data/spec/link_spec.rb +58 -0
  37. data/spec/parsing_spec.rb +25 -0
  38. data/spec/resource_spec.rb +198 -0
  39. data/spec/restfully_spec.rb +13 -0
  40. data/spec/session_spec.rb +105 -0
  41. data/spec/spec_helper.rb +13 -0
  42. metadata +117 -0
data/restfully.gemspec ADDED
@@ -0,0 +1,99 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{restfully}
8
+ s.version = "0.2.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Cyril Rohr"]
12
+ s.date = %q{2009-09-17}
13
+ s.default_executable = %q{restfully}
14
+ s.description = %q{Experimental code for auto-generation of wrappers on top of RESTful APIs that follow HATEOAS principles and provide OPTIONS methods and/or Allow headers.}
15
+ s.email = %q{cyril.rohr@gmail.com}
16
+ s.executables = ["restfully"]
17
+ s.extra_rdoc_files = [
18
+ "LICENSE",
19
+ "README.rdoc"
20
+ ]
21
+ s.files = [
22
+ ".document",
23
+ ".gitignore",
24
+ "LICENSE",
25
+ "README.rdoc",
26
+ "Rakefile",
27
+ "TODO.rdoc",
28
+ "VERSION",
29
+ "bin/restfully",
30
+ "examples/grid5000.rb",
31
+ "lib/restfully.rb",
32
+ "lib/restfully/collection.rb",
33
+ "lib/restfully/error.rb",
34
+ "lib/restfully/extensions.rb",
35
+ "lib/restfully/http.rb",
36
+ "lib/restfully/http/adapters/abstract_adapter.rb",
37
+ "lib/restfully/http/adapters/patron_adapter.rb",
38
+ "lib/restfully/http/adapters/rest_client_adapter.rb",
39
+ "lib/restfully/http/error.rb",
40
+ "lib/restfully/http/headers.rb",
41
+ "lib/restfully/http/request.rb",
42
+ "lib/restfully/http/response.rb",
43
+ "lib/restfully/link.rb",
44
+ "lib/restfully/parsing.rb",
45
+ "lib/restfully/resource.rb",
46
+ "lib/restfully/session.rb",
47
+ "lib/restfully/special_array.rb",
48
+ "lib/restfully/special_hash.rb",
49
+ "restfully.gemspec",
50
+ "spec/collection_spec.rb",
51
+ "spec/fixtures/grid5000-sites.json",
52
+ "spec/http/error_spec.rb",
53
+ "spec/http/headers_spec.rb",
54
+ "spec/http/request_spec.rb",
55
+ "spec/http/response_spec.rb",
56
+ "spec/http/rest_client_adapter_spec.rb",
57
+ "spec/link_spec.rb",
58
+ "spec/parsing_spec.rb",
59
+ "spec/resource_spec.rb",
60
+ "spec/restfully_spec.rb",
61
+ "spec/session_spec.rb",
62
+ "spec/spec_helper.rb"
63
+ ]
64
+ s.has_rdoc = true
65
+ s.homepage = %q{http://github.com/crohr/restfully}
66
+ s.rdoc_options = ["--charset=UTF-8"]
67
+ s.require_paths = ["lib"]
68
+ s.rubyforge_project = %q{restfully}
69
+ s.rubygems_version = %q{1.3.2}
70
+ s.summary = %q{Experimental code for auto-generation of wrappers on top of RESTful APIs that follow some specific conventions.}
71
+ s.test_files = [
72
+ "spec/collection_spec.rb",
73
+ "spec/http/error_spec.rb",
74
+ "spec/http/headers_spec.rb",
75
+ "spec/http/request_spec.rb",
76
+ "spec/http/response_spec.rb",
77
+ "spec/http/rest_client_adapter_spec.rb",
78
+ "spec/link_spec.rb",
79
+ "spec/parsing_spec.rb",
80
+ "spec/resource_spec.rb",
81
+ "spec/restfully_spec.rb",
82
+ "spec/session_spec.rb",
83
+ "spec/spec_helper.rb",
84
+ "examples/grid5000.rb"
85
+ ]
86
+
87
+ if s.respond_to? :specification_version then
88
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
89
+ s.specification_version = 3
90
+
91
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
92
+ s.add_runtime_dependency(%q<rest-client>, [">= 1.0"])
93
+ else
94
+ s.add_dependency(%q<rest-client>, [">= 1.0"])
95
+ end
96
+ else
97
+ s.add_dependency(%q<rest-client>, [">= 1.0"])
98
+ end
99
+ end
@@ -0,0 +1,93 @@
1
+ require File.dirname(__FILE__)+'/spec_helper'
2
+
3
+ include Restfully
4
+ describe Collection do
5
+
6
+ it "should have all methods of a hash" do
7
+ collection = Collection.new("uri", session=mock('session'))
8
+ collection.size.should == 0
9
+ collection.store("rennes", resource = mock(Resource))
10
+ collection.size.should == 1
11
+ collection.should == {'rennes' => resource}
12
+ collection.should respond_to(:each)
13
+ collection.should respond_to(:store)
14
+ collection.should respond_to(:[])
15
+ collection.should respond_to(:length)
16
+ end
17
+
18
+
19
+
20
+ describe "loading" do
21
+ before(:all) do
22
+ @raw = fixture("grid5000-sites.json")
23
+ @response_200 = Restfully::HTTP::Response.new(200, {'Content-Type' => 'application/json;charset=utf-8', 'Content-Length' => @raw.length}, @raw)
24
+ @logger = Logger.new(STDOUT)
25
+ end
26
+ it "should not load if already loaded and no :reload" do
27
+ collection = Collection.new("uri", mock("session"))
28
+ collection.should_receive(:loaded?).and_return(true)
29
+ collection.load(:reload => false).should == collection
30
+ end
31
+ it "should load when :reload param is true [already loaded]" do
32
+ collection = Collection.new("uri", session=mock("session", :logger => Logger.new(STDOUT)))
33
+ collection.should_receive(:loaded?).and_return(true)
34
+ session.should_receive(:get).and_return(@response_200)
35
+ collection.load(:reload => true).should == collection
36
+ end
37
+ it "should load when force_reload is true [not loaded]" do
38
+ collection = Collection.new("uri", session=mock("session", :logger => Logger.new(STDOUT)))
39
+ collection.should_receive(:loaded?).and_return(false)
40
+ session.should_receive(:get).and_return(@response_200)
41
+ collection.load(:reload => true).should == collection
42
+ end
43
+ it "should force reload when query parameters are given" do
44
+ collection = Collection.new("uri", session=mock("session", :logger => Logger.new(STDOUT)))
45
+ session.should_receive(:get).and_return(@response_200)
46
+ collection.load(:query => {:q1 => 'v1'}).should == collection
47
+ end
48
+ it "should not initialize resources lacking a self link" do
49
+ collection = Collection.new("uri", session = mock("session", :get => mock("restfully response", :body => {
50
+ 'rennes' => {
51
+ 'links' => [
52
+ {'rel' => 'collection', 'href' => '/grid5000/sites/rennes/versions', 'resolvable' => false, 'title' => 'versions'}
53
+ ],
54
+ 'uid' => 'rennes'
55
+ }
56
+ }), :logger => @logger))
57
+ Resource.should_not_receive(:new)
58
+ collection.load
59
+ collection['rennes'].should be_nil
60
+ end
61
+ it "should initialize resources having a self link" do
62
+ collection = Collection.new("uri", session = mock("session", :get => mock("restfully response", :body => {
63
+ 'rennes' => {
64
+ 'links' => [
65
+ {'rel' => 'self', 'href' => '/grid5000/sites/rennes'},
66
+ {'rel' => 'collection', 'href' => '/grid5000/sites/rennes/versions', 'resolvable' => false, 'title' => 'versions'}
67
+ ],
68
+ 'uid' => 'rennes'
69
+ }
70
+ }), :logger => @logger))
71
+ Resource.should_receive(:new).with('/grid5000/sites/rennes', session, :raw => {
72
+ 'links' => [
73
+ {'rel' => 'self', 'href' => '/grid5000/sites/rennes'},
74
+ {'rel' => 'collection', 'href' => '/grid5000/sites/rennes/versions', 'resolvable' => false, 'title' => 'versions'}
75
+ ],
76
+ 'uid' => 'rennes'
77
+ }).and_return(resource=mock("restfully resource"))
78
+ resource.should_receive(:load).and_return(resource)
79
+ collection.load
80
+ collection['rennes'].should == resource
81
+ end
82
+ it "should correctly initialize its resources [integration test]" do
83
+ collection = Collection.new("uri", session=mock("session", :logger => Logger.new(STDOUT), :get => @response_200))
84
+ collection.load
85
+ collection.should be_loaded
86
+ collection.uri.should == "uri"
87
+ collection['rennes'].uid.should == 'rennes'
88
+ collection['rennes'].type.should == 'site'
89
+ collection.keys.should =~ ['rennes', 'lille', 'bordeaux', 'nancy', 'sophia', 'toulouse', 'lyon', 'grenoble', 'orsay']
90
+ end
91
+ end
92
+
93
+ end
@@ -0,0 +1,489 @@
1
+ {
2
+ "lille": {
3
+ "name": "Lille",
4
+ "latitude": 50.65,
5
+ "location": "Lille, France",
6
+ "security_contact": null,
7
+ "uid": "lille",
8
+ "type": "site",
9
+ "user_support_contact": null,
10
+ "version": "747d2af24c5f3c2578a96626e0b00413c8484f04",
11
+ "links": [
12
+ {
13
+ "href": "\/grid5000\/sites\/lille",
14
+ "rel": "self"
15
+ },
16
+ {
17
+ "href": "\/grid5000\/sites\/lille\/versions\/747d2af24c5f3c2578a96626e0b00413c8484f04",
18
+ "title": "version",
19
+ "rel": "member"
20
+ },
21
+ {
22
+ "href": "\/grid5000\/sites\/lille\/versions",
23
+ "title": "versions",
24
+ "rel": "collection"
25
+ },
26
+ {
27
+ "href": "\/grid5000\/sites\/lille\/clusters",
28
+ "resolvable": true,
29
+ "title": "clusters",
30
+ "rel": "collection",
31
+ "resolved": false
32
+ },
33
+ {
34
+ "href": "\/grid5000\/sites\/lille\/environments",
35
+ "resolvable": true,
36
+ "title": "environments",
37
+ "rel": "collection",
38
+ "resolved": false
39
+ },
40
+ {
41
+ "href": "\/grid5000",
42
+ "rel": "parent"
43
+ },
44
+ {
45
+ "href": "\/grid5000\/sites\/lille\/status",
46
+ "title": "status",
47
+ "rel": "collection"
48
+ }
49
+ ],
50
+ "description": "",
51
+ "longitude": 3.0833,
52
+ "email_contact": null,
53
+ "web": null,
54
+ "sys_admin_contact": null
55
+ },
56
+ "grenoble": {
57
+ "name": "Grenoble",
58
+ "latitude": 45.1833,
59
+ "location": "Grenoble, France",
60
+ "security_contact": null,
61
+ "uid": "grenoble",
62
+ "type": "site",
63
+ "user_support_contact": null,
64
+ "version": "747d2af24c5f3c2578a96626e0b00413c8484f04",
65
+ "links": [
66
+ {
67
+ "href": "\/grid5000\/sites\/grenoble",
68
+ "rel": "self"
69
+ },
70
+ {
71
+ "href": "\/grid5000\/sites\/grenoble\/versions\/747d2af24c5f3c2578a96626e0b00413c8484f04",
72
+ "title": "version",
73
+ "rel": "member"
74
+ },
75
+ {
76
+ "href": "\/grid5000\/sites\/grenoble\/versions",
77
+ "title": "versions",
78
+ "rel": "collection"
79
+ },
80
+ {
81
+ "href": "\/grid5000\/sites\/grenoble\/clusters",
82
+ "resolvable": true,
83
+ "title": "clusters",
84
+ "rel": "collection",
85
+ "resolved": false
86
+ },
87
+ {
88
+ "href": "\/grid5000\/sites\/grenoble\/environments",
89
+ "resolvable": true,
90
+ "title": "environments",
91
+ "rel": "collection",
92
+ "resolved": false
93
+ },
94
+ {
95
+ "href": "\/grid5000",
96
+ "rel": "parent"
97
+ },
98
+ {
99
+ "href": "\/grid5000\/sites\/grenoble\/status",
100
+ "title": "status",
101
+ "rel": "collection"
102
+ }
103
+ ],
104
+ "description": "",
105
+ "longitude": 5.7167,
106
+ "email_contact": null,
107
+ "web": null,
108
+ "sys_admin_contact": null
109
+ },
110
+ "toulouse": {
111
+ "name": "Toulouse",
112
+ "latitude": 43.6167,
113
+ "location": "Toulouse, France",
114
+ "security_contact": null,
115
+ "uid": "toulouse",
116
+ "type": "site",
117
+ "user_support_contact": null,
118
+ "version": "747d2af24c5f3c2578a96626e0b00413c8484f04",
119
+ "links": [
120
+ {
121
+ "href": "\/grid5000\/sites\/toulouse",
122
+ "rel": "self"
123
+ },
124
+ {
125
+ "href": "\/grid5000\/sites\/toulouse\/versions\/747d2af24c5f3c2578a96626e0b00413c8484f04",
126
+ "title": "version",
127
+ "rel": "member"
128
+ },
129
+ {
130
+ "href": "\/grid5000\/sites\/toulouse\/versions",
131
+ "title": "versions",
132
+ "rel": "collection"
133
+ },
134
+ {
135
+ "href": "\/grid5000\/sites\/toulouse\/clusters",
136
+ "resolvable": true,
137
+ "title": "clusters",
138
+ "rel": "collection",
139
+ "resolved": false
140
+ },
141
+ {
142
+ "href": "\/grid5000\/sites\/toulouse\/environments",
143
+ "resolvable": true,
144
+ "title": "environments",
145
+ "rel": "collection",
146
+ "resolved": false
147
+ },
148
+ {
149
+ "href": "\/grid5000",
150
+ "rel": "parent"
151
+ },
152
+ {
153
+ "href": "\/grid5000\/sites\/toulouse\/status",
154
+ "title": "status",
155
+ "rel": "collection"
156
+ }
157
+ ],
158
+ "description": "",
159
+ "longitude": 1.4333,
160
+ "email_contact": null,
161
+ "web": null,
162
+ "sys_admin_contact": null
163
+ },
164
+ "sophia": {
165
+ "name": "Sophia-Antipolis",
166
+ "latitude": 43.6161,
167
+ "location": "Sophia-Antipolis, France",
168
+ "security_contact": "sophia-staff@lists.grid5000.fr",
169
+ "uid": "sophia",
170
+ "type": "site",
171
+ "user_support_contact": "sophia-staff@lists.grid5000.fr",
172
+ "version": "747d2af24c5f3c2578a96626e0b00413c8484f04",
173
+ "links": [
174
+ {
175
+ "href": "\/grid5000\/sites\/sophia",
176
+ "rel": "self"
177
+ },
178
+ {
179
+ "href": "\/grid5000\/sites\/sophia\/versions\/747d2af24c5f3c2578a96626e0b00413c8484f04",
180
+ "title": "version",
181
+ "rel": "member"
182
+ },
183
+ {
184
+ "href": "\/grid5000\/sites\/sophia\/versions",
185
+ "title": "versions",
186
+ "rel": "collection"
187
+ },
188
+ {
189
+ "href": "\/grid5000\/sites\/sophia\/clusters",
190
+ "resolvable": true,
191
+ "title": "clusters",
192
+ "rel": "collection",
193
+ "resolved": false
194
+ },
195
+ {
196
+ "href": "\/grid5000\/sites\/sophia\/environments",
197
+ "resolvable": true,
198
+ "title": "environments",
199
+ "rel": "collection",
200
+ "resolved": false
201
+ },
202
+ {
203
+ "href": "\/grid5000",
204
+ "rel": "parent"
205
+ },
206
+ {
207
+ "href": "\/grid5000\/sites\/sophia\/status",
208
+ "title": "status",
209
+ "rel": "collection"
210
+ }
211
+ ],
212
+ "description": "",
213
+ "longitude": 7.0678,
214
+ "compilation_server": true,
215
+ "email_contact": "sophia-staff@lists.grid5000.fr",
216
+ "web": null,
217
+ "sys_admin_contact": "sophia-staff@lists.grid5000.fr"
218
+ },
219
+ "rennes": {
220
+ "name": "Rennes",
221
+ "latitude": 48.1,
222
+ "location": "Rennes, France",
223
+ "security_contact": "rennes-staff@lists.grid5000.fr",
224
+ "uid": "rennes",
225
+ "type": "site",
226
+ "user_support_contact": "rennes-staff@lists.grid5000.fr",
227
+ "version": "747d2af24c5f3c2578a96626e0b00413c8484f04",
228
+ "links": [
229
+ {
230
+ "href": "\/grid5000\/sites\/rennes",
231
+ "rel": "self"
232
+ },
233
+ {
234
+ "href": "\/grid5000\/sites\/rennes\/versions\/747d2af24c5f3c2578a96626e0b00413c8484f04",
235
+ "title": "version",
236
+ "rel": "member"
237
+ },
238
+ {
239
+ "href": "\/grid5000\/sites\/rennes\/versions",
240
+ "title": "versions",
241
+ "rel": "collection"
242
+ },
243
+ {
244
+ "href": "\/grid5000\/sites\/rennes\/clusters",
245
+ "resolvable": true,
246
+ "title": "clusters",
247
+ "rel": "collection",
248
+ "resolved": false
249
+ },
250
+ {
251
+ "href": "\/grid5000\/sites\/rennes\/environments",
252
+ "resolvable": true,
253
+ "title": "environments",
254
+ "rel": "collection",
255
+ "resolved": false
256
+ },
257
+ {
258
+ "href": "\/grid5000",
259
+ "rel": "parent"
260
+ },
261
+ {
262
+ "href": "\/grid5000\/sites\/rennes\/status",
263
+ "title": "status",
264
+ "rel": "collection"
265
+ }
266
+ ],
267
+ "description": "",
268
+ "longitude": -1.6667,
269
+ "compilation_server": false,
270
+ "email_contact": "rennes-staff@lists.grid5000.fr",
271
+ "web": "http:\/\/www.irisa.fr",
272
+ "sys_admin_contact": "rennes-staff@lists.grid5000.fr"
273
+ },
274
+ "lyon": {
275
+ "name": "Lyon",
276
+ "latitude": 45.7667,
277
+ "location": "Lyon, France",
278
+ "security_contact": null,
279
+ "uid": "lyon",
280
+ "type": "site",
281
+ "user_support_contact": null,
282
+ "version": "747d2af24c5f3c2578a96626e0b00413c8484f04",
283
+ "links": [
284
+ {
285
+ "href": "\/grid5000\/sites\/lyon",
286
+ "rel": "self"
287
+ },
288
+ {
289
+ "href": "\/grid5000\/sites\/lyon\/versions\/747d2af24c5f3c2578a96626e0b00413c8484f04",
290
+ "title": "version",
291
+ "rel": "member"
292
+ },
293
+ {
294
+ "href": "\/grid5000\/sites\/lyon\/versions",
295
+ "title": "versions",
296
+ "rel": "collection"
297
+ },
298
+ {
299
+ "href": "\/grid5000\/sites\/lyon\/clusters",
300
+ "resolvable": true,
301
+ "title": "clusters",
302
+ "rel": "collection",
303
+ "resolved": false
304
+ },
305
+ {
306
+ "href": "\/grid5000\/sites\/lyon\/environments",
307
+ "resolvable": true,
308
+ "title": "environments",
309
+ "rel": "collection",
310
+ "resolved": false
311
+ },
312
+ {
313
+ "href": "\/grid5000",
314
+ "rel": "parent"
315
+ },
316
+ {
317
+ "href": "\/grid5000\/sites\/lyon\/status",
318
+ "title": "status",
319
+ "rel": "collection"
320
+ }
321
+ ],
322
+ "description": "",
323
+ "longitude": 4.8333,
324
+ "email_contact": null,
325
+ "web": null,
326
+ "sys_admin_contact": null
327
+ },
328
+ "nancy": {
329
+ "name": "Nancy",
330
+ "latitude": 48.7,
331
+ "location": "Nancy, France",
332
+ "security_contact": null,
333
+ "uid": "nancy",
334
+ "type": "site",
335
+ "user_support_contact": null,
336
+ "version": "747d2af24c5f3c2578a96626e0b00413c8484f04",
337
+ "links": [
338
+ {
339
+ "href": "\/grid5000\/sites\/nancy",
340
+ "rel": "self"
341
+ },
342
+ {
343
+ "href": "\/grid5000\/sites\/nancy\/versions\/747d2af24c5f3c2578a96626e0b00413c8484f04",
344
+ "title": "version",
345
+ "rel": "member"
346
+ },
347
+ {
348
+ "href": "\/grid5000\/sites\/nancy\/versions",
349
+ "title": "versions",
350
+ "rel": "collection"
351
+ },
352
+ {
353
+ "href": "\/grid5000\/sites\/nancy\/clusters",
354
+ "resolvable": true,
355
+ "title": "clusters",
356
+ "rel": "collection",
357
+ "resolved": false
358
+ },
359
+ {
360
+ "href": "\/grid5000\/sites\/nancy\/environments",
361
+ "resolvable": true,
362
+ "title": "environments",
363
+ "rel": "collection",
364
+ "resolved": false
365
+ },
366
+ {
367
+ "href": "\/grid5000",
368
+ "rel": "parent"
369
+ },
370
+ {
371
+ "href": "\/grid5000\/sites\/nancy\/status",
372
+ "title": "status",
373
+ "rel": "collection"
374
+ }
375
+ ],
376
+ "description": "",
377
+ "longitude": 6.2,
378
+ "web": null,
379
+ "sys_admin_contact": null
380
+ },
381
+ "bordeaux": {
382
+ "name": "Bordeaux",
383
+ "latitude": 44.833333,
384
+ "location": "Bordeaux, France",
385
+ "security_contact": "bordeaux-staff@lists.grid5000.fr",
386
+ "uid": "bordeaux",
387
+ "type": "site",
388
+ "user_support_contact": "bordeaux-staff@lists.grid5000.fr",
389
+ "version": "747d2af24c5f3c2578a96626e0b00413c8484f04",
390
+ "links": [
391
+ {
392
+ "href": "\/grid5000\/sites\/bordeaux",
393
+ "rel": "self"
394
+ },
395
+ {
396
+ "href": "\/grid5000\/sites\/bordeaux\/versions\/747d2af24c5f3c2578a96626e0b00413c8484f04",
397
+ "title": "version",
398
+ "rel": "member"
399
+ },
400
+ {
401
+ "href": "\/grid5000\/sites\/bordeaux\/versions",
402
+ "title": "versions",
403
+ "rel": "collection"
404
+ },
405
+ {
406
+ "href": "\/grid5000\/sites\/bordeaux\/clusters",
407
+ "resolvable": true,
408
+ "title": "clusters",
409
+ "rel": "collection",
410
+ "resolved": false
411
+ },
412
+ {
413
+ "href": "\/grid5000\/sites\/bordeaux\/environments",
414
+ "resolvable": true,
415
+ "title": "environments",
416
+ "rel": "collection",
417
+ "resolved": false
418
+ },
419
+ {
420
+ "href": "\/grid5000",
421
+ "rel": "parent"
422
+ },
423
+ {
424
+ "href": "\/grid5000\/sites\/bordeaux\/status",
425
+ "title": "status",
426
+ "rel": "collection"
427
+ }
428
+ ],
429
+ "description": "Grid5000 Bordeaux site",
430
+ "longitude": -0.566667,
431
+ "email_contact": "bordeaux-staff@lists.grid5000.fr",
432
+ "web": "http:\/\/www.grid5000.fr\/mediawiki\/index.php\/Bordeaux:Home",
433
+ "sys_admin_contact": "bordeaux-staff@lists.grid5000.fr"
434
+ },
435
+ "orsay": {
436
+ "name": "Orsay",
437
+ "latitude": 48.7,
438
+ "location": "Orsay, France",
439
+ "security_contact": null,
440
+ "uid": "orsay",
441
+ "type": "site",
442
+ "user_support_contact": null,
443
+ "version": "747d2af24c5f3c2578a96626e0b00413c8484f04",
444
+ "links": [
445
+ {
446
+ "href": "\/grid5000\/sites\/orsay",
447
+ "rel": "self"
448
+ },
449
+ {
450
+ "href": "\/grid5000\/sites\/orsay\/versions\/747d2af24c5f3c2578a96626e0b00413c8484f04",
451
+ "title": "version",
452
+ "rel": "member"
453
+ },
454
+ {
455
+ "href": "\/grid5000\/sites\/orsay\/versions",
456
+ "title": "versions",
457
+ "rel": "collection"
458
+ },
459
+ {
460
+ "href": "\/grid5000\/sites\/orsay\/clusters",
461
+ "resolvable": true,
462
+ "title": "clusters",
463
+ "rel": "collection",
464
+ "resolved": false
465
+ },
466
+ {
467
+ "href": "\/grid5000\/sites\/orsay\/environments",
468
+ "resolvable": true,
469
+ "title": "environments",
470
+ "rel": "collection",
471
+ "resolved": false
472
+ },
473
+ {
474
+ "href": "\/grid5000",
475
+ "rel": "parent"
476
+ },
477
+ {
478
+ "href": "\/grid5000\/sites\/orsay\/status",
479
+ "title": "status",
480
+ "rel": "collection"
481
+ }
482
+ ],
483
+ "description": "",
484
+ "longitude": 2.2,
485
+ "email_contact": null,
486
+ "web": null,
487
+ "sys_admin_contact": null
488
+ }
489
+ }