safrano 0.3.4 → 0.4.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/core_ext/Dir/iter.rb +18 -0
- data/lib/core_ext/Hash/transform.rb +21 -0
- data/lib/core_ext/Integer/edm.rb +13 -0
- data/lib/core_ext/REXML/Document/output.rb +16 -0
- data/lib/core_ext/String/convert.rb +25 -0
- data/lib/core_ext/String/edm.rb +13 -0
- data/lib/core_ext/dir.rb +3 -0
- data/lib/core_ext/hash.rb +3 -0
- data/lib/core_ext/integer.rb +3 -0
- data/lib/core_ext/rexml.rb +3 -0
- data/lib/core_ext/string.rb +5 -0
- data/lib/odata/attribute.rb +15 -10
- data/lib/odata/batch.rb +17 -15
- data/lib/odata/collection.rb +141 -500
- data/lib/odata/collection_filter.rb +44 -37
- data/lib/odata/collection_media.rb +193 -43
- data/lib/odata/collection_order.rb +50 -37
- data/lib/odata/common_logger.rb +39 -12
- data/lib/odata/complex_type.rb +152 -0
- data/lib/odata/edm/primitive_types.rb +184 -0
- data/lib/odata/entity.rb +201 -176
- data/lib/odata/error.rb +186 -33
- data/lib/odata/expand.rb +126 -0
- data/lib/odata/filter/base.rb +69 -0
- data/lib/odata/filter/error.rb +55 -6
- data/lib/odata/filter/parse.rb +38 -36
- data/lib/odata/filter/sequel.rb +121 -67
- data/lib/odata/filter/sequel_function_adapter.rb +148 -0
- data/lib/odata/filter/token.rb +15 -11
- data/lib/odata/filter/tree.rb +110 -60
- data/lib/odata/function_import.rb +166 -0
- data/lib/odata/model_ext.rb +618 -0
- data/lib/odata/navigation_attribute.rb +50 -32
- data/lib/odata/relations.rb +7 -7
- data/lib/odata/select.rb +54 -0
- data/lib/{safrano_core.rb → odata/transition.rb} +14 -60
- data/lib/odata/url_parameters.rb +128 -37
- data/lib/odata/walker.rb +19 -11
- data/lib/safrano.rb +18 -28
- data/lib/safrano/contract.rb +143 -0
- data/lib/safrano/core.rb +43 -0
- data/lib/safrano/core_ext.rb +13 -0
- data/lib/safrano/deprecation.rb +73 -0
- data/lib/{multipart.rb → safrano/multipart.rb} +37 -41
- data/lib/safrano/rack_app.rb +175 -0
- data/lib/{odata_rack_builder.rb → safrano/rack_builder.rb} +18 -2
- data/lib/{request.rb → safrano/request.rb} +102 -50
- data/lib/{response.rb → safrano/response.rb} +5 -4
- data/lib/safrano/sequel_join_by_paths.rb +5 -0
- data/lib/{service.rb → safrano/service.rb} +257 -188
- data/lib/safrano/version.rb +5 -0
- data/lib/sequel/plugins/join_by_paths.rb +17 -29
- metadata +53 -17
- data/lib/rack_app.rb +0 -174
- data/lib/sequel_join_by_paths.rb +0 -5
- data/lib/version.rb +0 -4
@@ -1,11 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'json'
|
2
|
-
require_relative '../
|
3
|
-
require_relative './entity
|
4
|
+
require_relative '../safrano/core'
|
5
|
+
require_relative './entity'
|
6
|
+
|
7
|
+
module Safrano
|
8
|
+
# remove the relation between entity and parent by clearing
|
9
|
+
# the FK field(s) (if allowed)
|
10
|
+
def self.remove_nav_relation(assoc, parent)
|
11
|
+
return unless assoc
|
12
|
+
|
13
|
+
return unless assoc[:type] == :many_to_one
|
14
|
+
|
15
|
+
# removes/clear the FK values in parent
|
16
|
+
# thus deleting the "link" between the entity and the parent
|
17
|
+
# Note: This is called if we have to delete the child--> can only be
|
18
|
+
# done after removing the FK in parent (if allowed!)
|
19
|
+
lks = [assoc[:key]].flatten
|
20
|
+
lks.each do |lk|
|
21
|
+
parent.set(lk => nil)
|
22
|
+
parent.save(transaction: false)
|
23
|
+
end
|
24
|
+
end
|
4
25
|
|
5
|
-
module OData
|
6
26
|
# link newly created entities(child) to an existing parent
|
7
27
|
# by following the association_reflection rules
|
8
|
-
def
|
28
|
+
def self.create_nav_relation(child, assoc, parent)
|
9
29
|
return unless assoc
|
10
30
|
|
11
31
|
# Note: this coding shares some bits from our sequel/plugins/join_by_paths,
|
@@ -24,16 +44,16 @@ module OData
|
|
24
44
|
lks = [leftm.primary_key].flatten
|
25
45
|
rks = [assoc[:key]].flatten
|
26
46
|
join_cond = rks.zip(lks).to_h
|
27
|
-
join_cond.each
|
47
|
+
join_cond.each do |rk, lk|
|
28
48
|
if child.values[rk] # FK in new entity from payload not nil, only check consistency
|
29
49
|
# with the parent - id(s)
|
30
|
-
if (child.values[rk] != parent.pk_hash[lk]) # error...
|
31
|
-
|
32
|
-
end
|
50
|
+
# if (child.values[rk] != parent.pk_hash[lk]) # error...
|
51
|
+
# TODO
|
52
|
+
# end
|
33
53
|
else # we can set the FK value, thus creating the "link"
|
34
54
|
child.set(rk => parent.pk_hash[lk])
|
35
55
|
end
|
36
|
-
|
56
|
+
end
|
37
57
|
when :many_to_one
|
38
58
|
# sets the FK values in parent to corresponding related child key-values
|
39
59
|
# thus creating the "link" between the new entity and the parent
|
@@ -42,36 +62,28 @@ module OData
|
|
42
62
|
lks = [assoc[:key]].flatten
|
43
63
|
rks = [child.class.primary_key].flatten
|
44
64
|
join_cond = rks.zip(lks).to_h
|
45
|
-
join_cond.each
|
65
|
+
join_cond.each do |rk, lk|
|
46
66
|
if parent.values[lk] # FK in parent not nil, only check consistency
|
47
67
|
# with the child - id(s)
|
48
|
-
if
|
49
|
-
|
50
|
-
end
|
68
|
+
# if parent.values[lk] != child.pk_hash[rk] # error...
|
69
|
+
# TODO
|
70
|
+
# end
|
51
71
|
else # we can set the FK value, thus creating the "link"
|
52
72
|
parent.set(lk => child.pk_hash[rk])
|
53
73
|
end
|
54
|
-
|
74
|
+
end
|
55
75
|
end
|
56
76
|
end
|
57
77
|
|
58
78
|
# Represents a named but nil-valued navigation-attribute of an Entity
|
59
79
|
# (usually resulting from a NULL FK db value)
|
60
80
|
class NilNavigationAttribute
|
61
|
-
|
62
|
-
attr_reader :parent
|
63
|
-
def initialize(parent, name)
|
64
|
-
@parent = parent
|
65
|
-
@name = name
|
66
|
-
@navattr_reflection = parent.class.association_reflections[name.to_sym]
|
67
|
-
@klass = @navattr_reflection[:class_name].constantize
|
68
|
-
end
|
69
|
-
|
81
|
+
include Safrano::NavigationInfo
|
70
82
|
def odata_get(req)
|
71
83
|
if req.walker.media_value
|
72
|
-
|
84
|
+
Safrano::ErrorNotFound.odata_get
|
73
85
|
elsif req.accept?(APPJSON)
|
74
|
-
[200,
|
86
|
+
[200, EMPTY_HASH, to_odata_json(service: req.service)]
|
75
87
|
else # TODO: other formats
|
76
88
|
415
|
77
89
|
end
|
@@ -80,38 +92,44 @@ module OData
|
|
80
92
|
# create the nav. entity
|
81
93
|
def odata_post(req)
|
82
94
|
# delegate to the class method
|
83
|
-
@
|
95
|
+
@nav_klass.odata_create_entity_and_relation(req,
|
96
|
+
@navattr_reflection,
|
97
|
+
@nav_parent)
|
84
98
|
end
|
85
99
|
|
86
100
|
# create the nav. entity
|
87
101
|
def odata_put(req)
|
88
102
|
# delegate to the class method
|
89
|
-
@
|
103
|
+
@nav_klass.odata_create_entity_and_relation(req,
|
104
|
+
@navattr_reflection,
|
105
|
+
@nav_parent)
|
90
106
|
end
|
91
107
|
|
92
108
|
# empty output as OData json (v2)
|
93
109
|
def to_odata_json(*)
|
94
|
-
{ 'd' =>
|
110
|
+
{ 'd' => EMPTY_HASH }.to_json
|
95
111
|
end
|
96
112
|
|
97
113
|
# for testing purpose (assert_equal ...)
|
98
114
|
def ==(other)
|
99
|
-
(@
|
115
|
+
(@nav_parent == other.nav_parent) && (@nav_name == other.nav_name)
|
100
116
|
end
|
101
117
|
|
102
118
|
# methods related to transitions to next state (cf. walker)
|
103
119
|
module Transitions
|
104
120
|
def transition_end(_match_result)
|
105
|
-
|
121
|
+
Safrano::Transition::RESULT_END
|
106
122
|
end
|
107
123
|
|
108
124
|
def transition_value(_match_result)
|
109
125
|
[self, :end_with_value]
|
110
126
|
end
|
111
127
|
|
128
|
+
ALLOWED_TRANSITIONS = [Safrano::TransitionEnd,
|
129
|
+
Safrano::TransitionValue].freeze
|
130
|
+
|
112
131
|
def allowed_transitions
|
113
|
-
|
114
|
-
Safrano::TransitionValue]
|
132
|
+
ALLOWED_TRANSITIONS
|
115
133
|
end
|
116
134
|
end
|
117
135
|
include Transitions
|
data/lib/odata/relations.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'set'
|
4
4
|
|
5
5
|
# OData relation related classes/module
|
6
|
-
module
|
6
|
+
module Safrano
|
7
7
|
# we represent a relation as a Set (unordered) of two end elements
|
8
8
|
class Relation < Set
|
9
9
|
# attr_reader :rid
|
@@ -20,7 +20,7 @@ module OData
|
|
20
20
|
|
21
21
|
# we need a from/to order independant ID
|
22
22
|
def rid
|
23
|
-
|
23
|
+
Safrano::RelationManager.build_id(self)
|
24
24
|
end
|
25
25
|
|
26
26
|
# we need a from/to order independant OData like name
|
@@ -76,16 +76,16 @@ module OData
|
|
76
76
|
end
|
77
77
|
|
78
78
|
def get(arg)
|
79
|
-
rid =
|
79
|
+
rid = Safrano::RelationManager.build_id(arg)
|
80
80
|
if @list.key?(rid)
|
81
81
|
@list[rid]
|
82
82
|
else
|
83
|
-
rel =
|
83
|
+
rel = Safrano::Relation.new(arg)
|
84
84
|
@list[rid] = rel
|
85
85
|
end
|
86
86
|
end
|
87
87
|
|
88
|
-
def get_metadata_xml_attribs(from, to, assoc_type, xnamespace)
|
88
|
+
def get_metadata_xml_attribs(from, to, assoc_type, xnamespace, attrname)
|
89
89
|
rel = get([from, to])
|
90
90
|
# use Sequel reflection to get multiplicity (will be used later
|
91
91
|
# in 2. Associations below)
|
@@ -107,7 +107,7 @@ module OData
|
|
107
107
|
# <NavigationProperty Name="Supplier"
|
108
108
|
# Relationship="ODataDemo.Product_Supplier_Supplier_Products"
|
109
109
|
# FromRole="Product_Supplier" ToRole="Supplier_Products"/>
|
110
|
-
{ 'Name' =>
|
110
|
+
{ 'Name' => attrname, 'Relationship' => "#{xnamespace}.#{rel.name}",
|
111
111
|
'FromRole' => from, 'ToRole' => to }
|
112
112
|
end
|
113
113
|
end
|
data/lib/odata/select.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'odata/error'
|
4
|
+
|
5
|
+
# all dataset selecting related classes in our OData module
|
6
|
+
# ie do eager loading
|
7
|
+
module Safrano
|
8
|
+
# base class for selecting. We have to distinguish between
|
9
|
+
# fields of the current entity, and the navigation properties
|
10
|
+
# we can have one special case
|
11
|
+
# empty, ie no $select specified --> return all fields and all nav props
|
12
|
+
# ==> SelectAll
|
13
|
+
|
14
|
+
class SelectBase
|
15
|
+
ALL = new # re-useable selecting-all handler
|
16
|
+
|
17
|
+
def self.factory(selectstr, model)
|
18
|
+
case selectstr&.strip
|
19
|
+
when nil, '', '*'
|
20
|
+
ALL
|
21
|
+
else
|
22
|
+
Select.new(selectstr, model)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def all_props?
|
27
|
+
false
|
28
|
+
end
|
29
|
+
|
30
|
+
def ALL.all_props?
|
31
|
+
true
|
32
|
+
end
|
33
|
+
|
34
|
+
def parse_error?
|
35
|
+
Contract::OK
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# single select
|
40
|
+
class Select < SelectBase
|
41
|
+
COMASPLIT = /\s*,\s*/.freeze
|
42
|
+
attr_reader :props
|
43
|
+
|
44
|
+
def initialize(selstr, model)
|
45
|
+
@model = model
|
46
|
+
@selectp = selstr.strip
|
47
|
+
@props = @selectp.split(COMASPLIT)
|
48
|
+
end
|
49
|
+
|
50
|
+
def parse_error?
|
51
|
+
(invalids = @model.find_invalid_props(props.to_set)) ? BadRequestSelectInvalidProps.new(@model, invalids) : Contract::OK
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -1,63 +1,8 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
module OData
|
5
|
-
# some prominent constants... probably already defined elsewhere eg in Rack
|
6
|
-
# but lets KISS
|
7
|
-
CONTENT_TYPE = 'Content-Type'.freeze
|
8
|
-
CTT_TYPE_LC = 'content-type'.freeze
|
9
|
-
TEXTPLAIN_UTF8 = 'text/plain;charset=utf-8'.freeze
|
10
|
-
APPJSON = 'application/json'.freeze
|
11
|
-
APPXML = 'application/xml'.freeze
|
12
|
-
MP_MIXED = 'multipart/mixed'.freeze
|
13
|
-
APPXML_UTF8 = 'application/xml;charset=utf-8'.freeze
|
14
|
-
APPATOMXML_UTF8 = 'application/atomsvc+xml;charset=utf-8'.freeze
|
15
|
-
APPJSON_UTF8 = 'application/json;charset=utf-8'.freeze
|
16
|
-
|
17
|
-
CT_JSON = { CONTENT_TYPE => APPJSON_UTF8 }.freeze
|
18
|
-
CT_TEXT = { CONTENT_TYPE => TEXTPLAIN_UTF8 }.freeze
|
19
|
-
CT_ATOMXML = { CONTENT_TYPE => APPATOMXML_UTF8 }.freeze
|
20
|
-
CT_APPXML = { CONTENT_TYPE => APPXML_UTF8 }.freeze
|
21
|
-
|
22
|
-
# Type mapping DB --> Edm
|
23
|
-
# TypeMap = {"INTEGER" => "Edm.Int32" , "TEXT" => "Edm.String",
|
24
|
-
# "STRING" => "Edm.String"}
|
25
|
-
# Todo: complete mapping... this is just for the most common ones
|
26
|
-
|
27
|
-
# TODO: use Sequel GENERIC_TYPES: -->
|
28
|
-
# Constants
|
29
|
-
# GENERIC_TYPES = %w'String Integer Float Numeric BigDecimal Date DateTime
|
30
|
-
# Time File TrueClass FalseClass'.freeze
|
31
|
-
# Classes specifying generic types that Sequel will convert to
|
32
|
-
# database-specific types.
|
33
|
-
DB_TYPE_STRING_RGX = /\ACHAR\s*\(\d+\)\z/.freeze
|
34
|
-
|
35
|
-
def self.get_edm_type(db_type:)
|
36
|
-
case db_type
|
37
|
-
when 'INTEGER'
|
38
|
-
'Edm.Int32'
|
39
|
-
when 'TEXT', 'STRING'
|
40
|
-
'Edm.String'
|
41
|
-
else
|
42
|
-
'Edm.String' if DB_TYPE_STRING_RGX =~ db_type
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
3
|
+
require_relative 'error'
|
46
4
|
|
47
|
-
|
48
|
-
# some small extensions
|
49
|
-
class Document
|
50
|
-
def to_pretty_xml
|
51
|
-
formatter = REXML::Formatters::Pretty.new(2)
|
52
|
-
formatter.compact = true
|
53
|
-
strio = ''
|
54
|
-
formatter.write(root, strio)
|
55
|
-
strio
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
# Core
|
5
|
+
# our main namespace
|
61
6
|
module Safrano
|
62
7
|
# represents a state transition when navigating/parsing the url path
|
63
8
|
# from left to right
|
@@ -66,6 +11,15 @@ module Safrano
|
|
66
11
|
attr_accessor :match_result
|
67
12
|
attr_accessor :rgx
|
68
13
|
attr_reader :remain_idx
|
14
|
+
|
15
|
+
EMPTYSTR = ''.freeze
|
16
|
+
SLASH = '/'.freeze
|
17
|
+
|
18
|
+
RESULT_BAD_REQ_ERR = [nil, :error, ::Safrano::BadRequestError].freeze
|
19
|
+
RESULT_NOT_FOUND_ERR = [nil, :error, ::Safrano::ErrorNotFound].freeze
|
20
|
+
RESULT_SERVER_TR_ERR = [nil, :error, ServerTransitionError].freeze
|
21
|
+
RESULT_END = [nil, :end].freeze
|
22
|
+
|
69
23
|
def initialize(arg, trans: nil, remain_idx: 2)
|
70
24
|
@rgx = if arg.respond_to? :each_char
|
71
25
|
Regexp.new(arg)
|
@@ -88,9 +42,9 @@ module Safrano
|
|
88
42
|
|
89
43
|
def path_done
|
90
44
|
if @match_result
|
91
|
-
@match_result[1] ||
|
45
|
+
@match_result[1] || EMPTYSTR
|
92
46
|
else
|
93
|
-
|
47
|
+
EMPTYSTR
|
94
48
|
end
|
95
49
|
end
|
96
50
|
|
data/lib/odata/url_parameters.rb
CHANGED
@@ -1,58 +1,149 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'odata/error'
|
2
4
|
|
3
5
|
# url parameters processing . Mostly delegates to specialised classes
|
4
6
|
# (filter, order...) to convert into Sequel exprs.
|
5
|
-
module
|
6
|
-
class
|
7
|
+
module Safrano
|
8
|
+
class UrlParametersBase
|
9
|
+
attr_reader :expand
|
10
|
+
attr_reader :select
|
11
|
+
|
12
|
+
# url params validation methods.
|
13
|
+
# nil is the expected return for no errors
|
14
|
+
# an error class is returned in case of errors
|
15
|
+
# this way we can combine multiple validation calls with logical ||
|
16
|
+
def check_expand
|
17
|
+
@expand.parse_error?
|
18
|
+
end
|
19
|
+
|
20
|
+
def check_select
|
21
|
+
@select.parse_error?
|
22
|
+
end
|
23
|
+
|
24
|
+
def initialize(dataset, params = {})
|
25
|
+
@model = if dataset.respond_to? :model
|
26
|
+
dataset.model
|
27
|
+
else
|
28
|
+
dataset
|
29
|
+
end
|
30
|
+
@params = params
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# url parameters for a single entity expand/select
|
35
|
+
class UrlParameters4Single < UrlParametersBase
|
36
|
+
def initialize(dataset, params)
|
37
|
+
super
|
38
|
+
@expand = ExpandBase.factory(@params['$expand'], @model)
|
39
|
+
@select = SelectBase.factory(@params['$select'], @model)
|
40
|
+
end
|
41
|
+
|
42
|
+
def check_all
|
43
|
+
return Contract::OK unless @params
|
44
|
+
|
45
|
+
check_expand.if_valid do
|
46
|
+
check_select
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# url parameters for a collection expand/select + filter/order
|
52
|
+
class UrlParameters4Coll < UrlParametersBase
|
7
53
|
attr_reader :filt
|
8
54
|
attr_reader :ordby
|
9
|
-
|
10
|
-
|
55
|
+
|
56
|
+
def initialize(dataset, params = {})
|
57
|
+
super
|
58
|
+
# join helper is only needed for odering or filtering
|
59
|
+
@jh = @model.join_by_paths_helper if params['$orderby'] || params['$filter']
|
11
60
|
@params = params
|
61
|
+
@ordby = OrderBase.factory(@params['$orderby'], @jh)
|
62
|
+
@filt = FilterBase.factory(@params['$filter'])
|
63
|
+
@expand = ExpandBase.factory(@params['$expand'], @model)
|
64
|
+
@select = SelectBase.factory(@params['$select'], @model)
|
12
65
|
end
|
13
66
|
|
14
|
-
def
|
15
|
-
return unless @params['$
|
67
|
+
def check_top
|
68
|
+
return Contract::OK unless @params['$top']
|
16
69
|
|
17
|
-
|
18
|
-
|
70
|
+
itop = number_or_nil(@params['$top'])
|
71
|
+
(itop.nil? || itop.zero?) ? BadRequestError : Contract::OK
|
72
|
+
end
|
73
|
+
|
74
|
+
def check_skip
|
75
|
+
return Contract::OK unless @params['$skip']
|
19
76
|
|
20
|
-
|
21
|
-
nil
|
77
|
+
iskip = number_or_nil(@params['$skip'])
|
78
|
+
(iskip.nil? || iskip.negative?) ? BadRequestError : Contract::OK
|
22
79
|
end
|
23
80
|
|
24
|
-
def
|
25
|
-
return unless @params['$
|
81
|
+
def check_inlinecount
|
82
|
+
return Contract::OK unless (icp = @params['$inlinecount'])
|
26
83
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
return BadRequestError unless @jh.start_model.attrib_path_valid? qualfn
|
34
|
-
return BadRequestError unless [nil, 'asc', 'desc'].include? dir
|
35
|
-
end
|
84
|
+
((icp == 'allpages') || (icp == 'none')) ? Contract::OK : BadRequestInlineCountParamError
|
85
|
+
end
|
86
|
+
|
87
|
+
def check_filter
|
88
|
+
(err = @filt.parse_error?) ? err : Contract::OK
|
89
|
+
end
|
36
90
|
|
37
|
-
|
91
|
+
def check_orderby
|
92
|
+
return Contract::OK if @ordby.empty?
|
93
|
+
return BadRequestOrderParseError if @ordby.parse_error?
|
38
94
|
|
39
|
-
|
40
|
-
nil
|
95
|
+
Contract::OK
|
41
96
|
end
|
42
97
|
|
43
98
|
def apply_to_dataset(dtcx)
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
99
|
+
apply_expand_to_dataset(dtcx).if_valid do |dataset|
|
100
|
+
apply_filter_order_to_dataset(dataset)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def apply_expand_to_dataset(dtcx)
|
105
|
+
return Contract.valid(dtcx) if @expand.empty?
|
106
|
+
|
107
|
+
@expand.apply_to_dataset(dtcx)
|
108
|
+
end
|
109
|
+
|
110
|
+
# Warning, the @ordby and @filt objects are coupled by way of the join helper
|
111
|
+
def apply_filter_order_to_dataset(dtcx)
|
112
|
+
return Contract.valid(dtcx) if @filt.empty? && @ordby.empty?
|
113
|
+
|
114
|
+
# filter object and join-helper need to be finalized after filter
|
115
|
+
# has been parsed and checked
|
116
|
+
@filt.finalize(@jh).if_valid do
|
117
|
+
# start with the join
|
118
|
+
dtcx = @jh.dataset(dtcx)
|
119
|
+
|
120
|
+
@filt.apply_to_dataset(dtcx).map_result! do |dataset|
|
121
|
+
dtcx = dataset
|
122
|
+
dtcx = @ordby.apply_to_dataset(dtcx)
|
123
|
+
dtcx.select_all(@jh.start_model.table_name)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
###########################################################
|
129
|
+
def check_all
|
130
|
+
return Contract::OK unless @params
|
131
|
+
|
132
|
+
# lazy nested proc evaluation.
|
133
|
+
# if one check fails, it will be passed up the chain and the ones
|
134
|
+
# below will not be evaluated
|
135
|
+
check_top.if_valid do
|
136
|
+
check_skip.if_valid do
|
137
|
+
check_orderby.if_valid do
|
138
|
+
check_filter.if_valid do
|
139
|
+
check_expand.if_valid do
|
140
|
+
check_select.if_valid do
|
141
|
+
check_inlinecount
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
56
147
|
end
|
57
148
|
end
|
58
149
|
end
|