safrano 0.4.2 → 0.4.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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)