safrano 0.0.6 → 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a69035d5b1015e36842490919163234c4ae55591657fb7d3b4b730b871c0035b
4
- data.tar.gz: 5c0024539a67f37cbf397975315066b1df803dd826b242b873147bbd1da60062
3
+ metadata.gz: cd0dda1e8d7fc80e01fc15b50b0893a31b189bb95e0138c8c1e49f4d3cdb11d5
4
+ data.tar.gz: 4155a3d6ca1dcd2817aab51bc85208ada311b8a120d4ed90e5a8fcd6c05d23b7
5
5
  SHA512:
6
- metadata.gz: 7538b83433db7381e77a34415e9cd4887cd2db20bfe8ba05db66e6d3703a0358e9f3024f137bddfca3b9b490caf3afbc2262b6b26514a36efeae792c8983a670
7
- data.tar.gz: b12e002bdbdeb826678d9cd1f8c1c9dce8bd11791adf8205464d12b115551bebbc59ddf4635d4406e93c4f5cee955f5356fea3d373857bac662ddbe5dd098ef7
6
+ metadata.gz: e1b61fcb3776b7435821121eb41b21afcf5fba422cb7665182ca7dc4d5e11155c3ba92a41eee7c9dc20730728c838033aa4b30367fd0e518d108547b7fbb7bf5
7
+ data.tar.gz: 270e6f1942254b239aed16b0bdb4ba8c5f48eba6fa676f9e9da9758e984c1cdd37a72cd355ed30326881013342538bf59b4de7b9143aabd2104d4c3fdf7faa0d
@@ -50,6 +50,7 @@ module OData
50
50
  attr_reader :nav_collection_attribs
51
51
  attr_reader :nav_entity_attribs
52
52
  attr_reader :data_fields
53
+ attr_reader :inlinecount
53
54
 
54
55
  attr_accessor :namespace
55
56
 
@@ -131,12 +132,25 @@ module OData
131
132
  @cx = @cx.select_all(entity_set_name.to_sym)
132
133
  end
133
134
 
135
+ def odata_get_inlinecount_w_sequel
136
+ return unless (icp = @params['$inlinecount'])
137
+
138
+ @inlinecount = if icp == 'allpages'
139
+ if is_a? Sequel::Model::ClassMethods
140
+ @cx.count
141
+ else
142
+ @cx.dataset.count
143
+ end
144
+ end
145
+ end
146
+
134
147
  def odata_get_apply_params_w_sequel
135
148
  @left_assocs = Set.new
136
149
  @right_assocs = Set.new
137
150
  odata_get_apply_filter_w_sequel
138
151
  odata_get_apply_order_w_sequel
139
152
  odata_get_do_assoc_joins_w_sequel
153
+ odata_get_inlinecount_w_sequel
140
154
 
141
155
  @cx = @cx.offset(@params['$skip']) if @params['$skip']
142
156
  @cx = @cx.limit(@params['$top']) if @params['$top']
@@ -169,6 +183,16 @@ module OData
169
183
  return BadRequestError if iskip.nil? || (iskip < 0)
170
184
  end
171
185
 
186
+ def check_u_p_inlinecount
187
+ return unless (icp = @params['$inlinecount'])
188
+
189
+ unless (icp == 'allpages') || (icp == 'none')
190
+ return BadRequestInlineCountParamError
191
+ end
192
+
193
+ nil
194
+ end
195
+
172
196
  def check_u_p_filter
173
197
  return unless @params['$filter']
174
198
 
@@ -182,7 +206,8 @@ module OData
182
206
  end
183
207
 
184
208
  def check_u_p_orderby
185
- # TODO: this should be moved into OData::Order somehow, at least partly
209
+ # TODO: this should be moved into OData::Order somehow,
210
+ # at least partly
186
211
  return unless (pordlist = @params['$orderby'].dup)
187
212
 
188
213
  pordlist.split(',').each do |pord|
@@ -224,7 +249,7 @@ module OData
224
249
  def check_url_params
225
250
  return nil unless @params
226
251
 
227
- check_u_p_top || check_u_p_skip || check_u_p_orderby || check_u_p_filter
252
+ check_u_p_top || check_u_p_skip || check_u_p_orderby || check_u_p_filter || check_u_p_inlinecount
228
253
  end
229
254
 
230
255
  def initialize_dataset
@@ -248,19 +273,29 @@ module OData
248
273
  [200, { 'Content-Type' => 'text/plain;charset=utf-8' },
249
274
  @cx.count.to_s]
250
275
  elsif req.accept?('application/json')
251
- [200, { 'Content-Type' => 'application/json;charset=utf-8' },
252
- to_odata_json(service: req.service)]
276
+ if req.walker.do_links
277
+ [200, { 'Content-Type' => 'application/json;charset=utf-8' },
278
+ to_odata_links_json(service: req.service)]
279
+ else
280
+ [200, { 'Content-Type' => 'application/json;charset=utf-8' },
281
+ to_odata_json(service: req.service)]
282
+ end
253
283
  else # TODO: other formats
254
284
  406
255
285
  end
256
286
  end
257
287
  end
258
288
 
289
+ def to_odata_links_json(service:)
290
+ { 'd' => service.get_coll_odata_links_h(array: get_a,
291
+ uribase: @uribase) }.to_json
292
+ end
293
+
259
294
  def to_odata_json(service:)
260
- expand = @params['$expand']
261
295
  { 'd' => service.get_coll_odata_h(array: get_a,
262
- expand: expand,
263
- uribase: @uribase) }.to_json
296
+ expand: @params['$expand'],
297
+ uribase: @uribase,
298
+ icount: @inlinecount) }.to_json
264
299
  end
265
300
 
266
301
  def get_a
@@ -40,32 +40,6 @@ class String
40
40
  end
41
41
  end
42
42
 
43
- # Handles filtering with ruby expressions
44
- # (eval and DB full scan used --> better avoid using this)
45
- module FilterWithRuby
46
- # this module requires the @fn attribute to exist where it is used
47
- def fn=(farg)
48
- @fn = farg
49
- @fn_tab = farg.split('/').map(&:to_sym)
50
- end
51
-
52
- # returns the attribute named "@fn" of object inp. This version assumes that
53
- # @fn can be a path like "address/city"
54
- def get_value(inp, colescev = '')
55
- obj = inp
56
- @fn_tab.each do |csymb|
57
- tmp = obj.send(csymb)
58
- if tmp.nil?
59
- obj = colescev
60
- break
61
- else
62
- obj = tmp
63
- end
64
- end
65
- obj
66
- end
67
- end
68
-
69
43
  # filter base class and subclass in our OData namespace
70
44
  module OData
71
45
  # handles associations in filter arguments
@@ -120,11 +94,6 @@ module OData
120
94
  new(matx)
121
95
  end
122
96
 
123
- # handle complex deep (relations) expressions with Ruby
124
- def self.new_full_match_complexpr_by_ruby(filterstr)
125
- ComplexFilterByRuby.new(filterstr)
126
- end
127
-
128
97
  def apply_to_dataset(dtcx)
129
98
  dtcx
130
99
  end
@@ -137,7 +106,6 @@ module OData
137
106
 
138
107
  # well...
139
108
  class EqualFilter < Filter
140
- include FilterWithRuby
141
109
  include FilterWithAssoc
142
110
  EQL = '[eE][qQ]|[LlgGNn][eETt]'.freeze
143
111
 
@@ -147,7 +115,7 @@ module OData
147
115
 
148
116
  def initialize(matx)
149
117
  super(matx)
150
- self.fn = matx[1]
118
+ @fn = matx[1]
151
119
  get_assoc if @fn
152
120
 
153
121
  @val = matx[3] ? matx[3].gsub("''", "'") : matx[4]
@@ -172,29 +140,34 @@ module OData
172
140
  y
173
141
  end
174
142
 
175
- def apply_to_dataset(dtcx)
176
- seqfn = get_qualified_fn(dtcx)
177
- # using @val in Procs below does not work reliably. Using a local var
178
- # seems to work better
179
- argval = @val
143
+ def apply_op_to_dataset(dtcx, lefval, rightval)
180
144
  case @op
181
145
  when 'eq'
182
- dtcx.where(seqfn => @val)
146
+ dtcx.where(lefval => rightval)
183
147
  when 'ne'
184
- dtcx.exclude(seqfn => @val)
148
+ dtcx.exclude(lefval => rightval)
185
149
  when 'le'
186
- dtcx.where { seqfn <= argval }
150
+ dtcx.where { lefval <= rightval }
187
151
  when 'ge'
188
- dtcx.where { seqfn >= argval }
152
+ dtcx.where { lefval >= rightval }
189
153
  when 'lt'
190
- dtcx.where { seqfn < argval }
154
+ dtcx.where { lefval < rightval }
191
155
  when 'gt'
192
- dtcx.where { seqfn > argval }
156
+ dtcx.where { lefval > rightval }
193
157
  else
194
158
  raise OData::FilterParseError
195
159
  end
196
160
  end
197
161
 
162
+ def apply_to_dataset(dtcx)
163
+ seqfn = get_qualified_fn(dtcx)
164
+ # using @val in Procs below does not work reliably. Using a local var
165
+ # seems to work better
166
+ argval = @val
167
+
168
+ apply_op_to_dataset(dtcx, seqfn, argval)
169
+ end
170
+
198
171
  def apply_to_object(inp)
199
172
  case @op
200
173
  when 'eq'
@@ -207,7 +180,132 @@ module OData
207
180
  end
208
181
  end
209
182
 
210
- # equality expressions with functions ... length(name) eq 2
183
+ class Func2a_EqualFilter < EqualFilter
184
+ FUNC = 'concat'.freeze
185
+ attr_reader :funcname
186
+
187
+ # like for concat(Name, 'xyz') EQ 'blablux'
188
+ def self.qualified_regexp(pathsrx)
189
+ /\s*(#{FUNC})\((#{pathsrx}),\s*(?:#{QUO})\)\s+(#{EQL})\s+(?:#{QUO})\s*/
190
+ end
191
+
192
+ def initialize(matx)
193
+ # super(matx)
194
+ @funcname = matx[1]
195
+ @fn = matx[2]
196
+ get_assoc if @fn
197
+ @farg = matx[3].gsub("''", "'")
198
+ @op = matx[4].downcase
199
+
200
+ @val = matx[5].gsub("''", "'")
201
+ end
202
+
203
+ def apply_to_dataset(dtcx)
204
+ seqfn = get_qualified_fn(dtcx)
205
+ # using @val in Procs below does not work reliably. Using a local var
206
+ # seems to work better
207
+ argval = @val
208
+ seqfunc = case @funcname
209
+ when 'concat'
210
+ Sequel.join([seqfn, @farg])
211
+ else
212
+ raise OData::FilterParseError
213
+ end
214
+ apply_op_to_dataset(dtcx, seqfunc, argval)
215
+ end
216
+ end
217
+ class Func2b_EqualFilter < EqualFilter
218
+ FUNC = 'concat'.freeze
219
+ attr_reader :funcname
220
+
221
+ # like for concat('xyz', Name) EQ 'blablux'
222
+ def self.qualified_regexp(pathsrx)
223
+ /\s*(#{FUNC})\((?:#{QUO}),\s*(#{pathsrx})\)\s+(#{EQL})\s+(?:#{QUO})\s*/
224
+ end
225
+
226
+ def initialize(matx)
227
+ # super(matx)
228
+ @funcname = matx[1]
229
+ @fn = matx[3]
230
+ get_assoc if @fn
231
+ @farg = matx[2].gsub("''", "'")
232
+ @op = matx[4].downcase
233
+
234
+ @val = matx[5].gsub("''", "'")
235
+ end
236
+
237
+ def apply_to_dataset(dtcx)
238
+ seqfn = get_qualified_fn(dtcx)
239
+ # using @val in Procs below does not work reliably. Using a local var
240
+ # seems to work better
241
+ argval = @val
242
+ seqfunc = case @funcname
243
+ when 'concat'
244
+ Sequel.join([@farg, seqfn])
245
+ else
246
+ raise OData::FilterParseError
247
+ end
248
+ apply_op_to_dataset(dtcx, seqfunc, argval)
249
+ end
250
+ end
251
+
252
+ class Func2c_EqualFilter < EqualFilter
253
+ FUNC = 'concat'.freeze
254
+ attr_reader :funcname
255
+
256
+ # like for concat(first_ame, last_name) EQ 'blablux'
257
+ def self.qualified_regexp(pathsrx)
258
+ /\s*(#{FUNC})\((#{pathsrx}),\s*(#{pathsrx})\)\s+(#{EQL})\s+(?:#{QUO})\s*/
259
+ end
260
+
261
+ def get_assoc
262
+ @assoc, @fn = @fn.split('/') if @fn.include?('/')
263
+ assoc1, @fn1 = @fn1.split('/') if @fn1.include?('/')
264
+ if assoc1 != @assoc
265
+ # TODO... handle this
266
+ raise OData::ServerError
267
+ end
268
+ end
269
+
270
+ def get_qualified_fn1(dtcx)
271
+ seqtn = if dtcx.respond_to? :table_name
272
+ @assoc ? @assoc.to_sym : dtcx.table_name
273
+ else
274
+ @assoc ? @assoc.to_sym : dtcx.model.table_name
275
+ end
276
+ Sequel[seqtn][@fn1.to_sym]
277
+ end
278
+
279
+ def initialize(matx)
280
+ # super(matx)
281
+ @funcname = matx[1]
282
+ @fn = matx[2]
283
+ @fn1 = matx[3]
284
+ get_assoc if @fn
285
+
286
+ @op = matx[4].downcase
287
+
288
+ @val = matx[5].gsub("''", "'")
289
+ end
290
+
291
+ def apply_to_dataset(dtcx)
292
+ seqfn = get_qualified_fn(dtcx)
293
+ seqfn1 = get_qualified_fn1(dtcx)
294
+ # using @val in Procs below does not work reliably. Using a local var
295
+ # seems to work better
296
+ argval = @val
297
+ seqfunc = case @funcname
298
+ when 'concat'
299
+ Sequel.join([seqfn, seqfn1])
300
+ else
301
+ raise OData::FilterParseError
302
+ end
303
+ apply_op_to_dataset(dtcx, seqfunc, argval)
304
+ end
305
+ end
306
+
307
+ # equality expressions with functions having 1 parameter
308
+ # like for length(name) eq 2
211
309
  class FuncEqualFilter < EqualFilter
212
310
  FUNC = 'length|trim|tolower|toupper'.freeze
213
311
 
@@ -220,29 +318,12 @@ module OData
220
318
  def initialize(matx)
221
319
  super(matx)
222
320
  @funcname = matx[1]
223
- self.fn = matx[2]
321
+ @fn = matx[2]
224
322
  get_assoc if @fn
225
323
 
226
- @val = matx[4] ? matx[4].gsub("''", "'") : matx[5]
227
-
228
324
  @op = matx[3].downcase
229
- end
230
-
231
- # ugly hack... but working
232
- def gen_sql(dtcx)
233
- # handle ambiguous column names (joins) by qualifiying the names
234
- # seqfn = @assoc ? Sequel[@assoc.to_sym][@fn.to_sym] : @fn.to_sym
235
- # handle ambiguous column names (joins) by qualifiying the names
236
- # for the main table as well
237
- # TODO: find a better way to differentiate between the two types
238
- # of dtcx : Model or Dataset
239
325
 
240
- # DONE: same handling for order and substring and everywhere where
241
- # qualified fieldnames could be needed (uses attrib path regexp)
242
- x = apply_to_dataset(dtcx)
243
- # ugly ugly hack :-(
244
- y = x.unordered.sql.sub(x.unfiltered.unordered.sql + ' WHERE', '')
245
- y
326
+ @val = matx[4] ? matx[4].gsub("''", "'") : matx[5]
246
327
  end
247
328
 
248
329
  def apply_to_dataset(dtcx)
@@ -264,22 +345,7 @@ module OData
264
345
  raise OData::FilterParseError
265
346
  end
266
347
 
267
- case @op
268
- when 'eq'
269
- dtcx.where(seqfunc => argval)
270
- when 'ne'
271
- dtcx.exclude(seqfunc => argval)
272
- when 'le'
273
- dtcx.where { seqfunc <= argval }
274
- when 'ge'
275
- dtcx.where { seqfunc >= argval }
276
- when 'lt'
277
- dtcx.where { seqfunc < argval }
278
- when 'gt'
279
- dtcx.where { seqfunc > argval }
280
- else
281
- raise OData::FilterParseError
282
- end
348
+ apply_op_to_dataset(dtcx, seqfunc, argval)
283
349
  end
284
350
  end
285
351
 
@@ -290,7 +356,6 @@ module OData
290
356
 
291
357
  # for substringof('value', field)
292
358
  class SubstringOfFilterSig1 < FilterWithKeyword
293
- include FilterWithRuby
294
359
  include FilterWithAssoc
295
360
 
296
361
  def self.qualified_regexp(paths_rx)
@@ -303,8 +368,8 @@ module OData
303
368
 
304
369
  def initialize(matx)
305
370
  super(matx)
306
- # binding.pry
307
- self.fn = matx[4]
371
+
372
+ @fn = matx[4]
308
373
  get_assoc if @fn
309
374
  # @val = matx[2].strip_single_quote
310
375
  @val = matx[2] ? matx[2].gsub("''", "'") : matx[3]
@@ -328,19 +393,14 @@ module OData
328
393
  def apply_to_dataset(dtcx)
329
394
  dtcx.where(whcl(dtcx))
330
395
  end
331
-
332
- def apply_to_object(inp)
333
- get_value(inp).include?(@val)
334
- end
335
396
  end
336
397
  # for substringof(field,'value')
337
398
  class SubstringOfFilterSig2 < FilterWithKeyword
338
- include FilterWithRuby
339
399
  include FilterWithAssoc
340
400
 
341
401
  def initialize(matx)
342
402
  super(matx)
343
- self.fn = matx[2]
403
+ @fn = matx[2]
344
404
  get_assoc if @fn
345
405
  @val = matx[3]
346
406
  @keyword = matx[1]
@@ -351,15 +411,10 @@ module OData
351
411
  def apply_to_dataset(dtcx)
352
412
  dtcx
353
413
  end
354
-
355
- def apply_to_object(inp)
356
- get_value(inp).include?(@val)
357
- end
358
414
  end
359
415
 
360
416
  # Filter by start/end with
361
417
  class StartOrEndsWithFilter < FilterWithKeyword
362
- include FilterWithRuby
363
418
  include FilterWithAssoc
364
419
  # DONE: better handle quotes vs not-quotes. Unblanced quotes are now handled
365
420
  # example : startswith(year, 190')
@@ -372,21 +427,11 @@ module OData
372
427
  super(matx)
373
428
  # DONE: the regexp should only match on known field names...
374
429
  # --> use qualified_regexp(attr_paths_rgx)
375
- self.fn = matx[2]
430
+ @fn = matx[2]
376
431
  get_assoc if @fn
377
432
  # double quotes need to be unescaped
378
433
  @val = matx[3] ? matx[3].gsub("''", "'") : matx[4]
379
434
  @keyword = matx[1]
380
- ve = Regexp.escape(@val)
381
- # used for "filter with ruby"
382
- case @keyword
383
- when 'startswith'
384
- @startend_rgx = Regexp.new("\A#{ve}")
385
- when 'endswith'
386
- @startend_rgx = Regexp.new("#{ve}\z")
387
- else
388
- raise FilterParseError
389
- end
390
435
  end
391
436
 
392
437
  def whcl(dtcx)
@@ -412,10 +457,6 @@ module OData
412
457
  def apply_to_dataset(dtcx)
413
458
  dtcx.where(whcl(dtcx))
414
459
  end
415
-
416
- def apply_to_object(inp)
417
- get_value(inp) =~ @startend_rgx
418
- end
419
460
  end
420
461
 
421
462
  FILTER_CLASSES = [EqualFilter,
@@ -435,6 +476,9 @@ module OData
435
476
  # list of active Subfilter classes. The ordering is important
436
477
  SUBFILTERS = [EqualFilter,
437
478
  FuncEqualFilter,
479
+ Func2a_EqualFilter,
480
+ Func2b_EqualFilter,
481
+ Func2c_EqualFilter,
438
482
  SubstringOfFilterSig1,
439
483
  StartOrEndsWithFilter].freeze
440
484
  # for counting number of AND OR's
@@ -487,39 +531,4 @@ module OData
487
531
  dtcx.where(Sequel.lit(@osql))
488
532
  end
489
533
  end
490
-
491
- # Handle complex filter expression with Ruby
492
- class ComplexFilterByRuby < Filter
493
- def initialize(filterstr)
494
- @subf = []
495
- @f = nil
496
- @rbcode = filterstr.dup
497
- @rbcode.with_mask_quoted_substrings! do |_s|
498
- SUBFILTERS.each { |klass| init_subfilter(klass) }
499
- downcase_and_or
500
- end
501
- end
502
-
503
- # downcase AND OR in @rbcode otherwise it is seen as a Ruby constant
504
- # instead as a ruby langu keyword
505
- def downcase_and_or
506
- @rbcode.gsub!(/(OR|Or|AND|And)/) do |_mx|
507
- Regexp.last_match[1].downcase
508
- end
509
- end
510
-
511
- def init_subfilter(subfiltclass)
512
- @rbcode.gsub!(subfiltclass.regexp) do |_mx|
513
- @f = subfiltclass.new(Regexp.last_match)
514
- @subf << @f
515
- "@subf[#{@subf.size - 1}].apply_to_object(@o)"
516
- end
517
- end
518
-
519
- def apply_to_object(inp)
520
- @o = inp
521
- b = binding
522
- eval(@rbcode, b)
523
- end
524
- end
525
534
  end
data/lib/odata/entity.rb CHANGED
@@ -17,6 +17,7 @@ module OData
17
17
  alltr = [
18
18
  Safrano::TransitionEnd,
19
19
  Safrano::TransitionCount,
20
+ Safrano::TransitionLinks,
20
21
  Safrano::Transition.new(%r{\A/(#{aurgx})(.*)\z},
21
22
  trans: 'transition_attribute')
22
23
  ]
@@ -42,6 +43,10 @@ module OData
42
43
  [self, :end]
43
44
  end
44
45
 
46
+ def transition_links(_match_result)
47
+ [self, :run_with_links]
48
+ end
49
+
45
50
  def transition_attribute(match_result)
46
51
  attrib = match_result[1]
47
52
  # [values[attrib.to_sym], :run]
@@ -97,6 +102,7 @@ module OData
97
102
  def to_odata_json(service:)
98
103
  { 'd' => service.get_entity_odata_h(entity: self,
99
104
  expand: @params['$expand'],
105
+ # links: @do_links,
100
106
  uribase: @uribase) }.to_json
101
107
  end
102
108
 
@@ -120,6 +126,7 @@ module OData
120
126
  def odata_get(req)
121
127
  @params = req.params
122
128
  @uribase = req.uribase
129
+ @do_links = req.walker.do_links
123
130
  if req.accept?('application/json')
124
131
  [200, { 'Content-Type' => 'application/json;charset=utf-8' },
125
132
  to_odata_json(service: req.service)]
data/lib/odata/error.rb CHANGED
@@ -49,6 +49,11 @@ module OData
49
49
  HTTP_CODE = 400
50
50
  @msg = 'Bad Request: Syntax error in Filter'
51
51
  end
52
+ # for $inlinecount error
53
+ class BadRequestInlineCountParamError < BadRequestError
54
+ HTTP_CODE = 400
55
+ @msg = 'Bad Request: wrong $inlinecount parameter'
56
+ end
52
57
  # http not found
53
58
  class ErrorNotFound
54
59
  extend Error
data/lib/odata/walker.rb CHANGED
@@ -25,6 +25,9 @@ module OData
25
25
  # is $value requested?
26
26
  attr_reader :raw_value
27
27
 
28
+ # are $links requested ?
29
+ attr_reader :do_links
30
+
28
31
  def initialize(service, path)
29
32
  path = URI.decode_www_form_component(path)
30
33
  @context = service
@@ -82,13 +85,16 @@ module OData
82
85
  @path_remain = @tr_next.path_remain
83
86
  @path_done << @tr_next.path_done
84
87
  # little hack's
85
- if @status == :end_with_count
88
+ case @status
89
+ when :end_with_count
86
90
  @do_count = true
87
91
  @status == :end
88
- end
89
- if @status == :end_with_value
92
+ when :end_with_value
90
93
  @raw_value = true
91
94
  @status == :end
95
+ when :run_with_links
96
+ @do_links = true
97
+ @status = :run
92
98
  end
93
99
  else
94
100
  @context = nil
data/lib/safrano_core.rb CHANGED
@@ -85,5 +85,7 @@ module Safrano
85
85
  trans: 'transition_count')
86
86
  TransitionValue = Transition.new('(\A\/\$value)(.*)\z',
87
87
  trans: 'transition_value')
88
+ TransitionLinks = Transition.new('(\A\/\$links)(.*)\z',
89
+ trans: 'transition_links')
88
90
  attr_accessor :allowed_transitions
89
91
  end
data/lib/service.rb CHANGED
@@ -74,6 +74,12 @@ module OData
74
74
  end
75
75
  end
76
76
 
77
+ # handle $links ... Note: $expand seems to be ignored when $links
78
+ # are requested
79
+ def get_entity_odata_link_h(entity:, uribase:)
80
+ hres = { uri: entity.uri(uribase) }
81
+ end
82
+
77
83
  def get_entity_odata_h(entity:, expand: nil, uribase:)
78
84
  hres = {}
79
85
  hres['__metadata'] = entity.metadata_h(uribase: uribase)
@@ -511,7 +517,7 @@ module OData
511
517
  @data_service_version = '1.0'
512
518
  end
513
519
 
514
- def get_coll_odata_h(array:, expand: nil, uribase:)
520
+ def get_coll_odata_h(array:, expand: nil, uribase:, icount: nil)
515
521
  array.map do |w|
516
522
  get_entity_odata_h(entity: w,
517
523
  expand: expand,
@@ -530,13 +536,23 @@ module OData
530
536
  @data_service_version = '2.0'
531
537
  end
532
538
 
533
- def get_coll_odata_h(array:, expand: nil, uribase:)
534
- { 'results' =>
535
- array.map do |w|
536
- get_entity_odata_h(entity: w,
537
- expand: expand,
538
- uribase: uribase)
539
- end }
539
+ def get_coll_odata_links_h(array:, uribase:)
540
+ array.map do |w|
541
+ get_entity_odata_link_h(entity: w, uribase: uribase)
542
+ end
543
+ end
544
+
545
+ def get_coll_odata_h(array:, expand: nil, uribase:, icount: nil)
546
+ res = array.map do |w|
547
+ get_entity_odata_h(entity: w,
548
+ expand: expand,
549
+ uribase: uribase)
550
+ end
551
+ if icount
552
+ { 'results' => res, '__count' => icount }
553
+ else
554
+ { 'results' => res }
555
+ end
540
556
  end
541
557
 
542
558
  def get_emptycoll_odata_h
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: safrano
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.6
4
+ version: 0.0.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - D.M.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-04-19 00:00:00.000000000 Z
11
+ date: 2019-04-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sequel
@@ -66,8 +66,7 @@ dependencies:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0.51'
69
- description: Safrano is a small experimental OData server framework based on Ruby,
70
- Rack and Sequel.
69
+ description: Safrano is a small OData server framework based on Ruby, Rack and Sequel.
71
70
  email: dm@0data.dev
72
71
  executables: []
73
72
  extensions: []