safrano 0.4.2 → 0.4.3

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.
@@ -140,9 +140,11 @@ module OData
140
140
  [self, :end_with_value]
141
141
  end
142
142
 
143
+ ALLOWED_TRANSITIONS = [Safrano::TransitionEnd,
144
+ Safrano::TransitionValue].freeze
145
+
143
146
  def allowed_transitions
144
- [Safrano::TransitionEnd,
145
- Safrano::TransitionValue]
147
+ ALLOWED_TRANSITIONS
146
148
  end
147
149
  end
148
150
  include Transitions
@@ -27,9 +27,11 @@ module OData
27
27
 
28
28
  # are $links requested ?
29
29
  attr_reader :do_links
30
-
30
+ NIL_SERVICE_FATAL = 'Walker is called with a nil service'.freeze
31
+ EMPTYSTR = ''.freeze
32
+ SLASH = '/'.freeze
31
33
  def initialize(service, path, content_id_refs = nil)
32
- raise 'Walker is called with a nil service' unless service
34
+ raise NIL_SERVICE_FATAL unless service
33
35
 
34
36
  path = URI.decode_www_form_component(path)
35
37
  @context = service
@@ -51,12 +53,12 @@ module OData
51
53
  end
52
54
 
53
55
  def unprefixed(prefix, path)
54
- if (prefix == '') || (prefix == '/')
56
+ if (prefix == EMPTYSTR) || (prefix == SLASH)
55
57
  path
56
58
  else
57
59
  # path.sub!(/\A#{prefix}/, '')
58
60
  # TODO check
59
- path.sub(/\A#{prefix}/, '')
61
+ path.sub(/\A#{prefix}/, EMPTYSTR)
60
62
  end
61
63
  end
62
64
 
@@ -1,5 +1,3 @@
1
- #!/usr/bin/env ruby
2
-
3
1
  # our main namespace
4
2
  module OData
5
3
  # frozen empty Array/Hash to reduce unncecessary object creation
@@ -15,8 +15,6 @@ module MIME
15
15
  class Parser
16
16
  HMD_RGX = /^([\w-]+)\s*:\s*(.*)/.freeze
17
17
 
18
- CRLF_LINE_RGX = /^#{CRLF}$/.freeze
19
-
20
18
  attr_accessor :lines
21
19
  attr_accessor :target
22
20
  def initialize
@@ -57,7 +55,6 @@ module MIME
57
55
  if (hmd = HMD_RGX.match(line))
58
56
  @target_hd[hmd[1].downcase] = hmd[2].strip
59
57
 
60
- # elsif CRLF_LINE_RGX =~ line
61
58
  elsif CRLF == line
62
59
  @target_ct = @target_hd[CTT_TYPE_LC] || TEXT_PLAIN
63
60
  @state = new_content
@@ -199,7 +196,6 @@ module MIME
199
196
  # Parser for Text::Plain
200
197
  class Parser
201
198
  HMD_RGX = /^([\w-]+)\s*:\s*(.*)/.freeze
202
- CRLF_LINE_RGX = /^#{CRLF}$/.freeze
203
199
  def initialize(target)
204
200
  @state = :h
205
201
  @lines = []
@@ -213,7 +209,6 @@ module MIME
213
209
  def parse_head(line)
214
210
  if (hmd = HMD_RGX.match(line))
215
211
  @target.hd[hmd[1].downcase] = hmd[2].strip
216
- # elsif CRLF_LINE_RGX =~ line
217
212
  elsif CRLF == line
218
213
  @state = :b
219
214
  else
@@ -442,7 +437,6 @@ module MIME
442
437
  def unparse
443
438
  b = "#{@http_method} #{@uri} HTTP/1.1#{CRLF}"
444
439
  @hd.each { |k, v| b << "#{k}: #{v}#{CRLF}" }
445
- # @hd.each { |k, v| b << k.to_s << ': ' << v.to_s << CRLF }
446
440
  b << CRLF
447
441
  b << @content if @content != ''
448
442
  b
@@ -473,7 +467,6 @@ module MIME
473
467
  b = String.new(APPLICATION_HTTP_11)
474
468
  b << "#{@status} #{StatusMessage[@status]} #{CRLF}"
475
469
  @hd.each { |k, v| b << "#{k}: #{v}#{CRLF}" }
476
- # @hd.each { |k, v| b << k.to_s << ': ' << v.to_s << CRLF }
477
470
  b << CRLF
478
471
  b << @content.join if @content
479
472
  b
@@ -490,7 +483,6 @@ module MIME
490
483
  # Parser for Http Media
491
484
  class Parser
492
485
  HMD_RGX = /^([\w-]+)\s*:\s*(.*)/.freeze
493
- CRLF_LINE_RGX = /^#{CRLF}$/.freeze
494
486
 
495
487
  def initialize(target)
496
488
  @state = :http
@@ -523,7 +515,6 @@ module MIME
523
515
  if (hmd = HMD_RGX.match(line))
524
516
  @target.content.hd[hmd[1].downcase] = hmd[2].strip
525
517
  elsif CRLF == line
526
- # elsif CRLF_LINE_RGX =~ line
527
518
  @state = :b
528
519
  else
529
520
  @body_lines << line
@@ -10,7 +10,6 @@ module Rack
10
10
  super(default_app) {}
11
11
  use ::Rack::Cors
12
12
  instance_eval(&block) if block_given?
13
- use ::Rack::Lint
14
13
  use ::Rack::ContentLength
15
14
  end
16
15
  end
@@ -77,13 +77,13 @@ module OData
77
77
  # the main Rack server app. Source: the Rack docu/examples and partly
78
78
  # inspired from Sinatra
79
79
  class ServerApp
80
- METHODS_REGEXP = Regexp.new('HEAD|OPTIONS|GET|POST|PATCH|MERGE|PUT|DELETE')
80
+ METHODS_REGEXP = Regexp.new('HEAD|OPTIONS|GET|POST|PATCH|MERGE|PUT|DELETE').freeze
81
+ NOCACHE_HDRS = { 'Cache-Control' => 'no-cache',
82
+ 'Expires' => '-1',
83
+ 'Pragma' => 'no-cache' }.freeze
84
+ DATASERVICEVERSION = 'DataServiceVersion'.freeze
81
85
  include MethodHandlers
82
86
  def before
83
- headers 'Cache-Control' => 'no-cache'
84
- headers 'Expires' => '-1'
85
- headers 'Pragma' => 'no-cache'
86
-
87
87
  @request.service_base = self.class.get_service_base
88
88
 
89
89
  neg_error = @request.negotiate_service_version
@@ -92,7 +92,9 @@ module OData
92
92
 
93
93
  return false unless @request.service
94
94
 
95
- headers 'DataServiceVersion' => @request.service.data_service_version
95
+ myhdrs = NOCACHE_HDRS.dup
96
+ myhdrs[DATASERVICEVERSION] = @request.service.data_service_version
97
+ headers myhdrs
96
98
  end
97
99
 
98
100
  # dispatch for all methods requiring parsing of the path
@@ -108,13 +108,6 @@ module OData
108
108
  end
109
109
  end
110
110
 
111
- def uribase
112
- return @uribase if @uribase
113
-
114
- @uribase =
115
- "#{env['rack.url_scheme']}://#{env['HTTP_HOST']}#{service.xpath_prefix}"
116
- end
117
-
118
111
  def accept?(type)
119
112
  preferred_type(type).to_s.include?(type)
120
113
  end
@@ -2,14 +2,18 @@ require 'rexml/document'
2
2
  require 'odata/relations.rb'
3
3
  require 'odata/batch.rb'
4
4
  require 'odata/error.rb'
5
+ require 'odata/filter/sequel.rb'
5
6
 
6
7
  module OData
7
8
  # this module has all methods related to expand/defered output preparation
8
9
  # and will be included in Service class
9
10
  module ExpandHandler
10
11
  PATH_SPLITTER = %r{\A(\w+)\/?(.*)\z}.freeze
11
- def get_deferred_odata_h(entity:, attrib:, uribase:)
12
- { '__deferred' => { 'uri' => "#{entity.uri(uribase)}/#{attrib}" } }
12
+ DEFERRED = '__deferred'.freeze
13
+ URI = 'uri'.freeze
14
+
15
+ def get_deferred_odata_h(entity_uri:, attrib:)
16
+ { DEFERRED => { URI => "#{entity_uri}/#{attrib}" } }
13
17
  end
14
18
 
15
19
  # split expand argument into first and rest part
@@ -33,29 +37,30 @@ module OData
33
37
 
34
38
  # default v2
35
39
  # overriden in ServiceV1
36
- def get_coll_odata_h(array:, template:, uribase:, icount: nil)
37
- res = array.map do |w|
38
- get_entity_odata_h(entity: w,
39
- template: template,
40
- uribase: uribase)
40
+ RESULTS_K = 'results'.freeze
41
+ COUNT_K = '__count'.freeze
42
+ def get_coll_odata_h(array:, template:, icount: nil)
43
+ array.map! do |w|
44
+ get_entity_odata_h(entity: w, template: template)
41
45
  end
42
46
  if icount
43
- { 'results' => res, '__count' => icount }
47
+ { RESULTS_K => array, COUNT_K => icount }
44
48
  else
45
- { 'results' => res }
49
+ { RESULTS_K => array }
46
50
  end
47
51
  end
48
52
 
49
53
  # handle $links ... Note: $expand seems to be ignored when $links
50
54
  # are requested
51
- def get_entity_odata_link_h(entity:, uribase:)
52
- { uri: entity.uri(uribase) }
55
+ def get_entity_odata_link_h(entity:)
56
+ { uri: entity.uri }
53
57
  end
54
58
 
55
59
  EMPTYH = {}.freeze
56
- def get_entity_odata_h(entity:, template:, uribase:)
60
+ METADATA_K = '__metadata'.freeze
61
+ def get_entity_odata_h(entity:, template:)
57
62
  # start with metadata
58
- hres = { '__metadata' => entity.metadata_h(uribase: uribase) }
63
+ hres = { METADATA_K => entity.metadata_h }
59
64
 
60
65
  template.each do |elmt, arg|
61
66
  case elmt
@@ -68,10 +73,7 @@ module OData
68
73
  arg.each do |attr, templ|
69
74
  enval = entity.send(attr)
70
75
  hres[attr] = if enval
71
-
72
- get_entity_odata_h(entity: enval,
73
- template: templ,
74
- uribase: uribase)
76
+ get_entity_odata_h(entity: enval, template: templ)
75
77
  else
76
78
  # FK is NULL --> nav_value is nil --> return empty json
77
79
  EMPTYH
@@ -82,16 +84,13 @@ module OData
82
84
  next unless (encoll = entity.send(attr))
83
85
 
84
86
  # nav attributes that are a collection (x..n)
85
- hres[attr] = get_coll_odata_h(array: encoll,
86
- template: templ,
87
- uribase: uribase)
87
+ hres[attr] = get_coll_odata_h(array: encoll, template: templ)
88
88
  # else error ?
89
89
  end
90
90
  when :deferr
91
+ euri = entity.uri
91
92
  arg.each do |attr|
92
- hres[attr] = get_deferred_odata_h(entity: entity,
93
- attrib: attr,
94
- uribase: uribase)
93
+ hres[attr] = get_deferred_odata_h(entity_uri: euri, attrib: attr)
95
94
  end
96
95
  end
97
96
  end
@@ -144,7 +143,8 @@ module OData
144
143
  attr_accessor :xname
145
144
  attr_accessor :xnamespace
146
145
  attr_accessor :xpath_prefix
147
- # attr_accessor :xuribase
146
+ attr_accessor :xserver_url
147
+ attr_accessor :uribase
148
148
  attr_accessor :meta
149
149
  attr_accessor :batch_handler
150
150
  attr_accessor :relman
@@ -170,6 +170,7 @@ module OData
170
170
  DATASERVICEVERSION_RGX = /\A([1234])(?:\.0);*\w*\z/.freeze
171
171
  TRAILING_SLASH = %r{/\z}.freeze
172
172
  DEFAULT_PATH_PREFIX = '/'.freeze
173
+ DEFAULT_SERVER_URL = 'http://localhost:9494'
173
174
 
174
175
  # input is the DataServiceVersion request header string, eg.
175
176
  # '2.0;blabla' ---> Version -> 2
@@ -212,21 +213,51 @@ module OData
212
213
  (@v1.xpath_prefix = @xpath_prefix) if @v1
213
214
  (@v2.xpath_prefix = @xpath_prefix) if @v2
214
215
  end
216
+
217
+ def server_url(surl)
218
+ @xserver_url = surl.sub(TRAILING_SLASH, '')
219
+ (@v1.xserver_url = @xserver_url) if @v1
220
+ (@v2.xserver_url = @xserver_url) if @v2
221
+ end
222
+
215
223
  # end public API
216
224
 
225
+ def set_uribase
226
+ @uribase = if @xpath_prefix.empty?
227
+ @xserver_url
228
+ else
229
+ if @xpath_prefix[0] == '/'
230
+ @xserver_url + @xpath_prefix
231
+ else
232
+ @xserver_url + '/' + @xpath_prefix
233
+ end
234
+ end
235
+ (@v1.uribase = @uribase) if @v1
236
+ (@v2.uribase = @uribase) if @v2
237
+ end
238
+
217
239
  def copy_attribs_to(other)
218
240
  other.cmap = @cmap
219
241
  other.collections = @collections
242
+ other.allowed_transitions = @allowed_transitions
220
243
  other.xtitle = @xtitle
221
244
  other.xname = @xname
222
245
  other.xnamespace = @xnamespace
223
246
  other.xpath_prefix = @xpath_prefix
247
+ other.xserver_url = @xserver_url
248
+ other.uribase = @uribase
224
249
  other.meta = ServiceMeta.new(other) # hum ... #todo: versions as well ?
225
250
  other.relman = @relman
226
251
  other.batch_handler = @batch_handler
227
252
  other
228
253
  end
229
254
 
255
+ # this is a central place. We extend Sequel models with OData functionality
256
+ # The included/extended modules depends on the properties(eg, pks, field types) of the model
257
+ # we differentiate
258
+ # * Single/Multi PK
259
+ # * Media/Non-Media entity
260
+ # Putting this logic here in modules loaded once on start shall result in less runtime overhead
230
261
  def register_model(modelklass, entity_set_name = nil, is_media = false)
231
262
  # check that the provided klass is a Sequel Model
232
263
 
@@ -313,10 +344,36 @@ module OData
313
344
  # set default path prefix if path_prefix was not called
314
345
  path_prefix(DEFAULT_PATH_PREFIX) unless @xpath_prefix
315
346
 
347
+ # set default server url if server_url was not called
348
+ server_url(DEFAULT_SERVER_URL) unless @xserver_url
349
+
350
+ set_uribase
351
+
316
352
  @collections.each(&:finalize_publishing)
317
353
 
318
- # finalize the media handlers
319
- @collections.each { |klass| }
354
+ # finalize the uri's and include NoMappingBeforeOutput or MappingBeforeOutput as needed
355
+ @collections.each { |klass|
356
+ klass.build_uri(@uribase)
357
+ if klass.time_cols.empty?
358
+ klass.include OData::NoMappingBeforeOutput
359
+ else
360
+ klass.include OData::MappingBeforeOutput
361
+ end
362
+ }
363
+
364
+ # build allowed transitions (requires that @collections are filled and sorted for having a
365
+ # correct base_url_regexp)
366
+ build_allowed_transitions
367
+
368
+ # mixin adapter specific modules where needed
369
+ case Sequel::Model.db.adapter_scheme
370
+ when :postgres
371
+ OData::Filter::FuncTree.include OData::Filter::FuncTreePostgres
372
+ when :sqlite
373
+ OData::Filter::FuncTree.include OData::Filter::FuncTreeSqlite
374
+ else
375
+ OData::Filter::FuncTree.include OData::Filter::FuncTreeDefault
376
+ end
320
377
  end
321
378
 
322
379
  def execute_deferred_iblocks
@@ -341,7 +398,7 @@ module OData
341
398
  def service_xml(req)
342
399
  doc = REXML::Document.new
343
400
  # separator required ? ?
344
- root = doc.add_element('service', 'xml:base' => req.uribase)
401
+ root = doc.add_element('service', 'xml:base' => @uribase)
345
402
 
346
403
  root.add_namespace('xmlns:atom', XMLNS::W3_2005_ATOM)
347
404
  root.add_namespace('xmlns:app', XMLNS::W3_2007_APP)
@@ -452,15 +509,22 @@ module OData
452
509
  # methods related to transitions to next state (cf. walker)
453
510
  module Transitions
454
511
  DOLLAR_ID_REGEXP = Regexp.new('\A\/\$').freeze
455
- def allowed_transitions
456
- @allowed_transitions = [
457
- Safrano::TransitionEnd,
458
- Safrano::TransitionMetadata,
459
- Safrano::TransitionBatch,
460
- Safrano::TransitionContentId,
512
+ ALLOWED_TRANSITIONS_FIXED = [
513
+ Safrano::TransitionEnd,
514
+ Safrano::TransitionMetadata,
515
+ Safrano::TransitionBatch,
516
+ Safrano::TransitionContentId
517
+ ].freeze
518
+
519
+ def build_allowed_transitions
520
+ @allowed_transitions = (ALLOWED_TRANSITIONS_FIXED + [
461
521
  Safrano::Transition.new(%r{\A/(#{base_url_regexp})(.*)},
462
522
  trans: 'transition_collection')
463
- ]
523
+ ]).freeze
524
+ end
525
+
526
+ def allowed_transitions
527
+ @allowed_transitions
464
528
  end
465
529
 
466
530
  def transition_collection(match_result)
@@ -507,18 +571,18 @@ module OData
507
571
  @data_service_version = '1.0'
508
572
  end
509
573
 
510
- def get_coll_odata_links_h(array:, uribase:, icount: nil)
574
+ def get_coll_odata_links_h(array:, icount: nil)
511
575
  array.map do |w|
512
- get_entity_odata_link_h(entity: w, uribase: uribase)
576
+ get_entity_odata_link_h(entity: w)
513
577
  end
514
578
  end
515
579
 
516
- def get_coll_odata_h(array:, template:, uribase:, icount: nil)
517
- array.map do |w|
580
+ def get_coll_odata_h(array:, template:, icount: nil)
581
+ array.map! do |w|
518
582
  get_entity_odata_h(entity: w,
519
- template: template,
520
- uribase: uribase)
583
+ template: template)
521
584
  end
585
+ array
522
586
  end
523
587
 
524
588
  def get_emptycoll_odata_h
@@ -532,19 +596,19 @@ module OData
532
596
  @data_service_version = '2.0'
533
597
  end
534
598
 
535
- def get_coll_odata_links_h(array:, uribase:, icount: nil)
599
+ def get_coll_odata_links_h(array:, icount: nil)
536
600
  res = array.map do |w|
537
- get_entity_odata_link_h(entity: w, uribase: uribase)
601
+ get_entity_odata_link_h(entity: w)
538
602
  end
539
603
  if icount
540
- { 'results' => res, '__count' => icount }
604
+ { RESULTS_K => res, COUNT_K => icount }
541
605
  else
542
- { 'results' => res }
606
+ { RESULTS_K => res }
543
607
  end
544
608
  end
545
609
 
546
610
  def get_emptycoll_odata_h
547
- { 'results' => EMPTY_HASH_IN_ARY }
611
+ { RESULTS_K => EMPTY_HASH_IN_ARY }
548
612
  end
549
613
  end
550
614
 
@@ -555,9 +619,9 @@ module OData
555
619
  @service = service
556
620
  end
557
621
 
622
+ ALLOWED_TRANSITIONS_FIXED = [Safrano::TransitionEnd].freeze
558
623
  def allowed_transitions
559
- @allowed_transitions = [Safrano::Transition.new('',
560
- trans: 'transition_end')]
624
+ ALLOWED_TRANSITIONS_FIXED
561
625
  end
562
626
 
563
627
  def transition_end(_match_result)