openbel-api 0.4.0-java
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.
- checksums.yaml +7 -0
- data/.gemspec +65 -0
- data/CHANGELOG.md +22 -0
- data/INSTALL.md +19 -0
- data/INSTALL_RUBY.md +107 -0
- data/LICENSE +191 -0
- data/README.md +208 -0
- data/app/openbel/api/app.rb +83 -0
- data/app/openbel/api/config.rb +45 -0
- data/app/openbel/api/config.ru +3 -0
- data/app/openbel/api/helpers/pager.rb +109 -0
- data/app/openbel/api/middleware/auth.rb +112 -0
- data/app/openbel/api/resources/adapters/basic_json.rb +52 -0
- data/app/openbel/api/resources/annotation.rb +141 -0
- data/app/openbel/api/resources/base.rb +16 -0
- data/app/openbel/api/resources/completion.rb +89 -0
- data/app/openbel/api/resources/evidence.rb +115 -0
- data/app/openbel/api/resources/evidence_transform.rb +143 -0
- data/app/openbel/api/resources/function.rb +98 -0
- data/app/openbel/api/resources/match_result.rb +79 -0
- data/app/openbel/api/resources/namespace.rb +174 -0
- data/app/openbel/api/routes/annotations.rb +168 -0
- data/app/openbel/api/routes/authenticate.rb +108 -0
- data/app/openbel/api/routes/base.rb +326 -0
- data/app/openbel/api/routes/datasets.rb +519 -0
- data/app/openbel/api/routes/evidence.rb +330 -0
- data/app/openbel/api/routes/expressions.rb +560 -0
- data/app/openbel/api/routes/functions.rb +41 -0
- data/app/openbel/api/routes/namespaces.rb +382 -0
- data/app/openbel/api/routes/root.rb +39 -0
- data/app/openbel/api/schemas.rb +34 -0
- data/app/openbel/api/schemas/annotation_collection.schema.json +20 -0
- data/app/openbel/api/schemas/annotation_resource.schema.json +36 -0
- data/app/openbel/api/schemas/annotation_value_collection.schema.json +21 -0
- data/app/openbel/api/schemas/annotation_value_resource.schema.json +35 -0
- data/app/openbel/api/schemas/completion_collection.schema.json +21 -0
- data/app/openbel/api/schemas/completion_resource.schema.json +146 -0
- data/app/openbel/api/schemas/evidence.schema.json +198 -0
- data/app/openbel/api/schemas/evidence_collection.schema.json +98 -0
- data/app/openbel/api/schemas/evidence_resource.schema.json +29 -0
- data/app/openbel/api/schemas/namespace_value_collection.schema.json +21 -0
- data/app/openbel/api/schemas/namespace_value_resource.schema.json +43 -0
- data/app/openbel/api/util.rb +11 -0
- data/bin/openbel-api +78 -0
- data/bin/openbel-config +46 -0
- data/config/async_evidence.rb +12 -0
- data/config/async_jena.rb +14 -0
- data/config/config.yml +31 -0
- data/config/server_config.rb +184 -0
- data/lib/openbel/api/cache/cache.rb +30 -0
- data/lib/openbel/api/config/config.rb +33 -0
- data/lib/openbel/api/evidence/api.rb +39 -0
- data/lib/openbel/api/evidence/facet_api.rb +18 -0
- data/lib/openbel/api/evidence/facet_filter.rb +83 -0
- data/lib/openbel/api/evidence/mongo.rb +247 -0
- data/lib/openbel/api/evidence/mongo_facet.rb +105 -0
- data/lib/openbel/api/helpers/dependency_graph.rb +52 -0
- data/lib/openbel/api/model/rdf_resource.rb +74 -0
- data/lib/openbel/api/plugin/cache/kyotocabinet.rb +85 -0
- data/lib/openbel/api/plugin/configure_plugins.rb +97 -0
- data/lib/openbel/api/plugin/evidence/evidence.rb +58 -0
- data/lib/openbel/api/plugin/plugin.rb +99 -0
- data/lib/openbel/api/plugin/plugin_manager.rb +20 -0
- data/lib/openbel/api/plugin/plugin_repository.rb +60 -0
- data/lib/openbel/api/storage/cache_proxy.rb +74 -0
- data/lib/openbel/api/storage/triple_storage.rb +43 -0
- metadata +379 -0
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'bel'
|
2
|
+
|
3
|
+
module OpenBEL
|
4
|
+
module Routes
|
5
|
+
|
6
|
+
class Functions < Base
|
7
|
+
include BEL::Language
|
8
|
+
|
9
|
+
SORTED_FUNCTIONS = FUNCTIONS.values.uniq.sort_by { |fx|
|
10
|
+
fx[:short_form]
|
11
|
+
}
|
12
|
+
|
13
|
+
options '/api/functions' do
|
14
|
+
response.headers['Allow'] = 'OPTIONS,GET'
|
15
|
+
status 200
|
16
|
+
end
|
17
|
+
|
18
|
+
options '/api/functions/:fx' do
|
19
|
+
response.headers['Allow'] = 'OPTIONS,GET'
|
20
|
+
status 200
|
21
|
+
end
|
22
|
+
|
23
|
+
get '/api/functions' do
|
24
|
+
render(SORTED_FUNCTIONS, :function_collection)
|
25
|
+
end
|
26
|
+
|
27
|
+
# BEL Completion
|
28
|
+
get '/api/functions/:fx' do
|
29
|
+
fx_match = FUNCTIONS[params[:fx].to_sym]
|
30
|
+
halt 404 unless fx_match
|
31
|
+
|
32
|
+
render(
|
33
|
+
[fx_match],
|
34
|
+
:function_collection
|
35
|
+
)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
# vim: ts=2 sw=2:
|
41
|
+
# encoding: utf-8
|
@@ -0,0 +1,382 @@
|
|
1
|
+
require 'bel'
|
2
|
+
require 'cgi'
|
3
|
+
require 'multi_json'
|
4
|
+
require 'uri'
|
5
|
+
|
6
|
+
module OpenBEL
|
7
|
+
module Routes
|
8
|
+
|
9
|
+
# REST API for retrieving namespaces and values. Provides the following capabilities:
|
10
|
+
#
|
11
|
+
# * Retrieve namespaces.
|
12
|
+
# * Retrieve values for a namespace using the identifier, preferred name, or title.
|
13
|
+
# * Retrieve equivalences for one or more values.
|
14
|
+
# * Retrieve equivalences in a target namespace for one or more values.
|
15
|
+
# * Retrieve orthologs for one or more values.
|
16
|
+
# * Retrieve orthologs in a target namespace for one or more values.
|
17
|
+
class Namespaces < Base
|
18
|
+
|
19
|
+
RESULT_TYPES = {
|
20
|
+
:resource => :all,
|
21
|
+
:name => :prefLabel,
|
22
|
+
:identifier => :identifier,
|
23
|
+
:title => :title
|
24
|
+
}
|
25
|
+
|
26
|
+
def initialize(app)
|
27
|
+
super
|
28
|
+
|
29
|
+
# RdfRepository using Jena.
|
30
|
+
@rr = BEL::RdfRepository.plugins[:jena].create_repository(
|
31
|
+
:tdb_directory => OpenBEL::Settings[:resource_rdf][:jena][:tdb_directory]
|
32
|
+
)
|
33
|
+
|
34
|
+
# Namespaces using RdfRepository
|
35
|
+
@namespaces = BEL::Resource::Namespaces.new(@rr)
|
36
|
+
|
37
|
+
# Resource Search using SQLite.
|
38
|
+
@search = BEL::Resource::Search.plugins[:sqlite].create_search(
|
39
|
+
:database_file => OpenBEL::Settings[:resource_search][:sqlite][:database_file]
|
40
|
+
)
|
41
|
+
end
|
42
|
+
|
43
|
+
options '/api/namespaces' do
|
44
|
+
response.headers['Allow'] = 'OPTIONS,GET'
|
45
|
+
status 200
|
46
|
+
end
|
47
|
+
|
48
|
+
options '/api/namespaces/:namespace' do
|
49
|
+
response.headers['Allow'] = 'OPTIONS,GET'
|
50
|
+
status 200
|
51
|
+
end
|
52
|
+
|
53
|
+
options '/api/namespaces/:namespace/equivalents' do
|
54
|
+
response.headers['Allow'] = 'OPTIONS,POST,GET'
|
55
|
+
status 200
|
56
|
+
end
|
57
|
+
|
58
|
+
options '/api/namespaces/:namespace/orthologs' do
|
59
|
+
response.headers['Allow'] = 'OPTIONS,POST,GET'
|
60
|
+
status 200
|
61
|
+
end
|
62
|
+
|
63
|
+
options '/api/namespaces/:namespace/values/:value' do
|
64
|
+
response.headers['Allow'] = 'OPTIONS,GET'
|
65
|
+
status 200
|
66
|
+
end
|
67
|
+
|
68
|
+
options '/api/namespaces/:namespace/values/:value/equivalents' do
|
69
|
+
response.headers['Allow'] = 'OPTIONS,GET'
|
70
|
+
status 200
|
71
|
+
end
|
72
|
+
|
73
|
+
options '/api/namespaces/:namespace/values/:value/equivalents/:target' do
|
74
|
+
response.headers['Allow'] = 'OPTIONS,GET'
|
75
|
+
status 200
|
76
|
+
end
|
77
|
+
|
78
|
+
options '/api/namespaces/:namespace/values/:value/orthologs' do
|
79
|
+
response.headers['Allow'] = 'OPTIONS,GET'
|
80
|
+
status 200
|
81
|
+
end
|
82
|
+
|
83
|
+
options '/api/namespaces/:namespace/values/:value/orthologs/:target' do
|
84
|
+
response.headers['Allow'] = 'OPTIONS,GET'
|
85
|
+
status 200
|
86
|
+
end
|
87
|
+
|
88
|
+
get '/api/namespaces' do
|
89
|
+
namespaces = @namespaces.each.to_a
|
90
|
+
|
91
|
+
halt 404 if not namespaces or namespaces.empty?
|
92
|
+
|
93
|
+
render_collection(
|
94
|
+
namespaces.sort { |x,y|
|
95
|
+
x.prefLabel.to_s <=> y.prefLabel.to_s
|
96
|
+
},
|
97
|
+
:namespace
|
98
|
+
)
|
99
|
+
end
|
100
|
+
|
101
|
+
get '/api/namespaces/values' do
|
102
|
+
start = (params[:start] || 0).to_i
|
103
|
+
size = (params[:size] || -1).to_i
|
104
|
+
size = -1 if size <= 0
|
105
|
+
faceted = as_bool(params[:faceted])
|
106
|
+
halt 501 if faceted
|
107
|
+
|
108
|
+
filter_hash = Hash.new{ |h,k| h[k] = Hash.new(&h.default_proc) }
|
109
|
+
filter_params = CGI::parse(env["QUERY_STRING"])['filter']
|
110
|
+
filter_params.each do |filter|
|
111
|
+
filter = read_filter(filter)
|
112
|
+
halt 400 unless ['category', 'name', 'value'].all? { |f| filter.include? f}
|
113
|
+
filter_hash[filter['category']][filter['name']] = filter['value']
|
114
|
+
end
|
115
|
+
|
116
|
+
halt 404 unless filter_hash['fts']['search'].is_a?(String)
|
117
|
+
match = filter_hash['fts']['search']
|
118
|
+
halt 404 unless match.length > 1
|
119
|
+
|
120
|
+
match_results = @search.search(wildcard_match(match), :namespace_concept, nil, nil,
|
121
|
+
:start => start,
|
122
|
+
:size => size
|
123
|
+
).map { |result|
|
124
|
+
value = OpenBEL::Resource::Namespaces::NamespaceValueSearchResult.new(@rr, result.uri)
|
125
|
+
value.match_text = result.snippet
|
126
|
+
value
|
127
|
+
}.to_a
|
128
|
+
|
129
|
+
halt 404 if not match_results or match_results.empty?
|
130
|
+
render_collection(
|
131
|
+
match_results,
|
132
|
+
:namespace_value,
|
133
|
+
:adapter => Oat::Adapters::BasicJson
|
134
|
+
)
|
135
|
+
end
|
136
|
+
|
137
|
+
get '/api/namespaces/:namespace' do |namespace|
|
138
|
+
namespace = @namespaces.find(namespace).first
|
139
|
+
|
140
|
+
halt 404 unless namespace
|
141
|
+
|
142
|
+
status 200
|
143
|
+
render_resource(
|
144
|
+
namespace,
|
145
|
+
:namespace
|
146
|
+
)
|
147
|
+
end
|
148
|
+
|
149
|
+
get '/api/namespaces/:namespace/values' do |namespace|
|
150
|
+
namespace = @namespaces.find(namespace).first
|
151
|
+
halt 404 unless namespace
|
152
|
+
|
153
|
+
start = (params[:start] || 0).to_i
|
154
|
+
size = (params[:size] || -1).to_i
|
155
|
+
size = -1 if size <= 0
|
156
|
+
faceted = as_bool(params[:faceted])
|
157
|
+
halt 501 if faceted
|
158
|
+
|
159
|
+
filter_hash = Hash.new{ |h,k| h[k] = Hash.new(&h.default_proc) }
|
160
|
+
filter_params = CGI::parse(env["QUERY_STRING"])['filter']
|
161
|
+
filter_params.each do |filter|
|
162
|
+
filter = read_filter(filter)
|
163
|
+
halt 400 unless ['category', 'name', 'value'].all? { |f| filter.include? f}
|
164
|
+
filter_hash[filter['category']][filter['name']] = filter['value']
|
165
|
+
end
|
166
|
+
|
167
|
+
halt 404 unless filter_hash['fts']['search'].is_a?(String)
|
168
|
+
match = filter_hash['fts']['search']
|
169
|
+
halt 404 unless match.length > 1
|
170
|
+
|
171
|
+
match_results = @search.search(wildcard_match(match), :namespace_concept, namespace.uri.to_s, nil,
|
172
|
+
:start => start,
|
173
|
+
:size => size
|
174
|
+
).map { |result|
|
175
|
+
value = OpenBEL::Resource::Namespaces::NamespaceValueSearchResult.new(@rr, result.uri)
|
176
|
+
value.match_text = result.snippet
|
177
|
+
value
|
178
|
+
}.to_a
|
179
|
+
|
180
|
+
halt 404 if not match_results or match_results.empty?
|
181
|
+
render_collection(
|
182
|
+
match_results,
|
183
|
+
:namespace_value,
|
184
|
+
:adapter => Oat::Adapters::BasicJson
|
185
|
+
)
|
186
|
+
end
|
187
|
+
|
188
|
+
# TODO Requires a Namespace API to retrieve equivalents for matched values.
|
189
|
+
# get '/api/namespaces/:namespace/equivalents' do |namespace|
|
190
|
+
# halt 400 unless request.params['value']
|
191
|
+
|
192
|
+
# values = CGI::parse(env["QUERY_STRING"])['value']
|
193
|
+
# options = {}
|
194
|
+
# if request.params['namespace']
|
195
|
+
# options[:target] = request.params['namespace']
|
196
|
+
# end
|
197
|
+
|
198
|
+
# if request.params['result']
|
199
|
+
# result = request.params['result'].to_sym
|
200
|
+
# halt 400 unless RESULT_TYPES.include? result
|
201
|
+
# options[:result] = RESULT_TYPES[result]
|
202
|
+
# end
|
203
|
+
|
204
|
+
# eq_mapping = @api.find_equivalents(namespace, values, options)
|
205
|
+
# halt 404 if eq_mapping.values.all? { |v| v == nil }
|
206
|
+
# response.headers['Content-Type'] = 'application/json'
|
207
|
+
# MultiJson.dump eq_mapping
|
208
|
+
# end
|
209
|
+
|
210
|
+
# TODO Requires a Namespace API to retrieve equivalents for matched values.
|
211
|
+
# post '/api/namespaces/:namespace/equivalents' do |namespace|
|
212
|
+
# halt 400 unless request.media_type == 'application/x-www-form-urlencoded'
|
213
|
+
|
214
|
+
# content = request.body.read
|
215
|
+
# halt 400 if content.empty?
|
216
|
+
|
217
|
+
# params = Hash[
|
218
|
+
# URI.decode_www_form(content).group_by(&:first).map{
|
219
|
+
# |k,a| [k,a.map(&:last)]
|
220
|
+
# }
|
221
|
+
# ]
|
222
|
+
|
223
|
+
# halt 400 unless params['value']
|
224
|
+
|
225
|
+
# options = {}
|
226
|
+
# if params['namespace']
|
227
|
+
# options[:target] = params['namespace'].first
|
228
|
+
# end
|
229
|
+
|
230
|
+
# if params['result']
|
231
|
+
# result = params['result'].first.to_sym
|
232
|
+
# halt 400 unless RESULT_TYPES.include? result
|
233
|
+
# options[:result] = RESULT_TYPES[result]
|
234
|
+
# end
|
235
|
+
|
236
|
+
# eq_mapping = @api.find_equivalents(namespace, params['value'], options)
|
237
|
+
# response.headers['Content-Type'] = 'application/json'
|
238
|
+
# MultiJson.dump eq_mapping
|
239
|
+
# end
|
240
|
+
|
241
|
+
# TODO Requires a Namespace API to retrieve orthologs for matched values.
|
242
|
+
# get '/api/namespaces/:namespace/orthologs' do |namespace|
|
243
|
+
# halt 400 unless request.params['value']
|
244
|
+
|
245
|
+
# values = CGI::parse(env["QUERY_STRING"])['value']
|
246
|
+
# options = {}
|
247
|
+
# if request.params['namespace']
|
248
|
+
# options[:target] = request.params['namespace']
|
249
|
+
# end
|
250
|
+
|
251
|
+
# if request.params['result']
|
252
|
+
# result = request.params['result'].to_sym
|
253
|
+
# halt 400 unless RESULT_TYPES.include? result
|
254
|
+
# options[:result] = RESULT_TYPES[result]
|
255
|
+
# end
|
256
|
+
|
257
|
+
# orth_mapping = @api.find_orthologs(namespace, values, options)
|
258
|
+
# halt 404 if orth_mapping.values.all? { |v| v == nil }
|
259
|
+
# response.headers['Content-Type'] = 'application/json'
|
260
|
+
# MultiJson.dump orth_mapping
|
261
|
+
# end
|
262
|
+
|
263
|
+
# TODO Requires a Namespace API to retrieve orthologs for matched values.
|
264
|
+
# post '/api/namespaces/:namespace/orthologs' do |namespace|
|
265
|
+
# halt 400 unless request.media_type == 'application/x-www-form-urlencoded'
|
266
|
+
|
267
|
+
# content = request.body.read
|
268
|
+
# halt 400 if content.empty?
|
269
|
+
|
270
|
+
# params = Hash[
|
271
|
+
# URI.decode_www_form(content).group_by(&:first).map{
|
272
|
+
# |k,a| [k,a.map(&:last)]
|
273
|
+
# }
|
274
|
+
# ]
|
275
|
+
|
276
|
+
# halt 400 unless params['value']
|
277
|
+
|
278
|
+
# options = {}
|
279
|
+
# if params['namespace']
|
280
|
+
# options[:target] = params['namespace'].first
|
281
|
+
# end
|
282
|
+
|
283
|
+
# if params['result']
|
284
|
+
# result = params['result'].first.to_sym
|
285
|
+
# halt 400 unless RESULT_TYPES.include? result
|
286
|
+
# options[:result] = RESULT_TYPES[result]
|
287
|
+
# end
|
288
|
+
|
289
|
+
# orth_mapping = @api.find_orthologs(namespace, params['value'], options)
|
290
|
+
# response.headers['Content-Type'] = 'application/json'
|
291
|
+
# MultiJson.dump orth_mapping
|
292
|
+
# end
|
293
|
+
|
294
|
+
get '/api/namespaces/:namespace/values/:value' do |namespace, value|
|
295
|
+
namespace = @namespaces.find(namespace).first
|
296
|
+
halt 404 unless namespace
|
297
|
+
|
298
|
+
value = namespace.find(value).first
|
299
|
+
halt 404 unless value
|
300
|
+
|
301
|
+
status 200
|
302
|
+
render_resource(
|
303
|
+
value,
|
304
|
+
:namespace_value,
|
305
|
+
:adapter => Oat::Adapters::BasicJson
|
306
|
+
)
|
307
|
+
end
|
308
|
+
|
309
|
+
get '/api/namespaces/:namespace/values/:value/equivalents' do |namespace, value|
|
310
|
+
namespace = @namespaces.find(namespace).first
|
311
|
+
halt 404 unless namespace
|
312
|
+
|
313
|
+
value = namespace.find(value).first
|
314
|
+
halt 404 unless value
|
315
|
+
|
316
|
+
equivalents = value.equivalents.to_a
|
317
|
+
halt 404 if not equivalents or equivalents.empty?
|
318
|
+
|
319
|
+
render_collection(
|
320
|
+
equivalents,
|
321
|
+
:namespace_value,
|
322
|
+
:adapter => Oat::Adapters::BasicJson
|
323
|
+
)
|
324
|
+
end
|
325
|
+
|
326
|
+
get '/api/namespaces/:namespace/values/:value/equivalents/:target' do |namespace, value, target|
|
327
|
+
namespace = @namespaces.find(namespace).first
|
328
|
+
halt 404 unless namespace
|
329
|
+
halt 404 unless @namespaces.find(target).first
|
330
|
+
|
331
|
+
value = namespace.find(value).first
|
332
|
+
halt 404 unless value
|
333
|
+
|
334
|
+
target_equivalents = value.equivalents(target).to_a
|
335
|
+
halt 404 if not target_equivalents or target_equivalents.empty?
|
336
|
+
|
337
|
+
render_collection(
|
338
|
+
target_equivalents,
|
339
|
+
:namespace_value,
|
340
|
+
:adapter => Oat::Adapters::BasicJson
|
341
|
+
)
|
342
|
+
end
|
343
|
+
|
344
|
+
get '/api/namespaces/:namespace/values/:value/orthologs' do |namespace, value|
|
345
|
+
namespace = @namespaces.find(namespace).first
|
346
|
+
halt 404 unless namespace
|
347
|
+
|
348
|
+
value = namespace.find(value).first
|
349
|
+
halt 404 unless value
|
350
|
+
|
351
|
+
orthologs = value.orthologs.to_a
|
352
|
+
halt 404 if not orthologs or orthologs.empty?
|
353
|
+
|
354
|
+
render_collection(
|
355
|
+
orthologs,
|
356
|
+
:namespace_value,
|
357
|
+
:adapter => Oat::Adapters::BasicJson
|
358
|
+
)
|
359
|
+
end
|
360
|
+
|
361
|
+
get '/api/namespaces/:namespace/values/:value/orthologs/:target' do |namespace, value, target|
|
362
|
+
namespace = @namespaces.find(namespace).first
|
363
|
+
halt 404 unless namespace
|
364
|
+
halt 404 unless @namespaces.find(target).first
|
365
|
+
|
366
|
+
value = namespace.find(value).first
|
367
|
+
halt 404 unless value
|
368
|
+
|
369
|
+
target_orthologs = value.orthologs(target).to_a
|
370
|
+
halt 404 if not target_orthologs or target_orthologs.empty?
|
371
|
+
|
372
|
+
render_collection(
|
373
|
+
target_orthologs,
|
374
|
+
:namespace_value,
|
375
|
+
:adapter => Oat::Adapters::BasicJson
|
376
|
+
)
|
377
|
+
end
|
378
|
+
end
|
379
|
+
end
|
380
|
+
end
|
381
|
+
# vim: ts=2 sw=2:
|
382
|
+
# encoding: utf-8
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module OpenBEL
|
2
|
+
module Routes
|
3
|
+
|
4
|
+
class Root < Base
|
5
|
+
|
6
|
+
options '/api' do
|
7
|
+
response.headers['Allow'] = 'OPTIONS,GET'
|
8
|
+
status 200
|
9
|
+
end
|
10
|
+
|
11
|
+
get '/api' do
|
12
|
+
response.headers['Content-Type'] = 'application/hal+json'
|
13
|
+
MultiJson.dump({
|
14
|
+
:_links => {
|
15
|
+
:item => [
|
16
|
+
{
|
17
|
+
:href => "#{base_url}/api/annotations"
|
18
|
+
},
|
19
|
+
{
|
20
|
+
:href => "#{base_url}/api/evidence"
|
21
|
+
},
|
22
|
+
{
|
23
|
+
:href => "#{base_url}/api/expressions"
|
24
|
+
},
|
25
|
+
{
|
26
|
+
:href => "#{base_url}/api/functions"
|
27
|
+
},
|
28
|
+
{
|
29
|
+
:href => "#{base_url}/api/namespaces"
|
30
|
+
}
|
31
|
+
]
|
32
|
+
}
|
33
|
+
})
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
# vim: ts=2 sw=2:
|
39
|
+
# encoding: utf-8
|