safrano 0.4.2 → 0.4.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/odata/attribute.rb +8 -7
- data/lib/odata/collection.rb +86 -34
- data/lib/odata/entity.rb +65 -69
- data/lib/odata/error.rb +18 -0
- data/lib/odata/filter/base.rb +66 -0
- data/lib/odata/filter/error.rb +27 -0
- data/lib/odata/filter/parse.rb +2 -0
- data/lib/odata/filter/sequel.rb +32 -17
- data/lib/odata/filter/sequel_function_adapter.rb +147 -0
- data/lib/odata/filter/token.rb +5 -1
- data/lib/odata/filter/tree.rb +34 -14
- data/lib/odata/navigation_attribute.rb +4 -2
- data/lib/odata/walker.rb +6 -4
- data/lib/safrano/core.rb +0 -2
- data/lib/safrano/multipart.rb +0 -9
- data/lib/safrano/odata_rack_builder.rb +0 -1
- data/lib/safrano/rack_app.rb +8 -6
- data/lib/safrano/request.rb +0 -7
- data/lib/safrano/service.rb +111 -47
- data/lib/safrano/version.rb +1 -1
- metadata +4 -2
data/lib/odata/error.rb
CHANGED
@@ -129,9 +129,27 @@ module OData
|
|
129
129
|
HTTP_CODE = 501
|
130
130
|
@msg = 'Not implemented: OData batch'
|
131
131
|
end
|
132
|
+
|
132
133
|
# error in filter parsing (Safrano specific)
|
133
134
|
class FilterParseError < BadRequestError
|
134
135
|
extend ErrorClass
|
135
136
|
HTTP_CODE = 400
|
136
137
|
end
|
138
|
+
|
139
|
+
class FilterFunctionNotImplementedError < BadRequestError
|
140
|
+
extend ErrorClass
|
141
|
+
include ErrorInstance
|
142
|
+
@msg = 'the requested $filter function is Not implemented'
|
143
|
+
HTTP_CODE = 400
|
144
|
+
def initialize(exception)
|
145
|
+
@msg = exception.to_s
|
146
|
+
end
|
147
|
+
end
|
148
|
+
class FilterInvalidFunctionError < BadRequestError
|
149
|
+
include ErrorInstance
|
150
|
+
HTTP_CODE = 400
|
151
|
+
def initialize(exception)
|
152
|
+
@msg = exception.to_s
|
153
|
+
end
|
154
|
+
end
|
137
155
|
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module OData
|
4
|
+
module Filter
|
5
|
+
# Base class for Leaves, Trees, RootTrees etc
|
6
|
+
class Node
|
7
|
+
end
|
8
|
+
|
9
|
+
# Leaves are Nodes with a parent but no children
|
10
|
+
class Leave < Node
|
11
|
+
end
|
12
|
+
|
13
|
+
# RootTrees have childrens but no parent
|
14
|
+
class RootTree < Node
|
15
|
+
end
|
16
|
+
|
17
|
+
# Tree's have Parent and children
|
18
|
+
class Tree < RootTree
|
19
|
+
end
|
20
|
+
|
21
|
+
# For functions... should have a single child---> the argument list
|
22
|
+
class FuncTree < Tree
|
23
|
+
end
|
24
|
+
|
25
|
+
# Indentity Func to use as "parent" func of parenthesis expressions
|
26
|
+
# --> allow to handle generically parenthesis always as argument of
|
27
|
+
# some function
|
28
|
+
class IdentityFuncTree < FuncTree
|
29
|
+
end
|
30
|
+
|
31
|
+
# unary op eg. NOT
|
32
|
+
class UnopTree < Tree
|
33
|
+
end
|
34
|
+
|
35
|
+
# Bin ops
|
36
|
+
class BinopTree < Tree
|
37
|
+
end
|
38
|
+
|
39
|
+
class BinopBool < BinopTree
|
40
|
+
end
|
41
|
+
|
42
|
+
class BinopArithm < BinopTree
|
43
|
+
end
|
44
|
+
|
45
|
+
# Arguments or lists
|
46
|
+
class ArgTree < Tree
|
47
|
+
end
|
48
|
+
|
49
|
+
# Numbers (floating point, ints, dec)
|
50
|
+
class FPNumber < Leave
|
51
|
+
end
|
52
|
+
|
53
|
+
# Literals are unquoted words without /
|
54
|
+
class Literal < Leave
|
55
|
+
end
|
56
|
+
|
57
|
+
# Qualit (qualified lits) are words separated by /
|
58
|
+
# path/path/path/attrib
|
59
|
+
class Qualit < Literal
|
60
|
+
end
|
61
|
+
|
62
|
+
# Quoted Strings
|
63
|
+
class QString < Leave
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
data/lib/odata/filter/error.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require_relative '../error'
|
2
|
+
|
1
3
|
module OData
|
2
4
|
class SequelAdapterError < StandardError
|
3
5
|
attr_reader :inner
|
@@ -5,7 +7,22 @@ module OData
|
|
5
7
|
@inner = err
|
6
8
|
end
|
7
9
|
end
|
10
|
+
|
11
|
+
# exception to OData error bridge
|
12
|
+
module ErrorBridge
|
13
|
+
# return an odata error object wrapping the exception
|
14
|
+
# the odata error object should respond to odata_get for output
|
15
|
+
def odata_error
|
16
|
+
self.class.const_get('ODATA_ERROR_KLASS').new(self)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
8
20
|
module Filter
|
21
|
+
class FunctionNotImplemented < StandardError
|
22
|
+
ODATA_ERROR_KLASS = OData::FilterFunctionNotImplementedError
|
23
|
+
include ::OData::ErrorBridge
|
24
|
+
end
|
25
|
+
|
9
26
|
class Parser
|
10
27
|
# Parser errors
|
11
28
|
class Error < StandardError
|
@@ -35,6 +52,16 @@ module OData
|
|
35
52
|
class ErrorWrongColumnName < StandardError
|
36
53
|
end
|
37
54
|
|
55
|
+
# attempt to add a child to a Leave
|
56
|
+
class ErrorLeaveChild < StandardError
|
57
|
+
end
|
58
|
+
|
59
|
+
# invalid function error (literal attach to IdentityFuncTree)
|
60
|
+
class ErrorInvalidFunction < StandardError
|
61
|
+
ODATA_ERROR_KLASS = OData::FilterInvalidFunctionError
|
62
|
+
include ::OData::ErrorBridge
|
63
|
+
end
|
64
|
+
|
38
65
|
# Invalid function arity
|
39
66
|
class ErrorInvalidArity < Error
|
40
67
|
end
|
data/lib/odata/filter/parse.rb
CHANGED
data/lib/odata/filter/sequel.rb
CHANGED
@@ -1,13 +1,17 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative './base.rb'
|
4
|
+
require_relative './sequel_function_adapter.rb'
|
5
|
+
|
2
6
|
module OData
|
3
7
|
module Filter
|
4
8
|
# Base class for Leaves, Trees, RootTrees etc
|
5
|
-
class Node
|
6
|
-
end
|
9
|
+
# class Node
|
10
|
+
# end
|
7
11
|
|
8
12
|
# Leaves are Nodes with a parent but no children
|
9
|
-
class Leave < Node
|
10
|
-
end
|
13
|
+
# class Leave < Node
|
14
|
+
# end
|
11
15
|
|
12
16
|
# RootTrees have childrens but no parent
|
13
17
|
class RootTree
|
@@ -26,6 +30,8 @@ module OData
|
|
26
30
|
end
|
27
31
|
|
28
32
|
# For functions... should have a single child---> the argument list
|
33
|
+
# note: Adapter specific function helpers like year() or substringof_sig2()
|
34
|
+
# need to be mixed in on startup (eg. on publish finalize)
|
29
35
|
class FuncTree < Tree
|
30
36
|
def leuqes(jh)
|
31
37
|
case @value
|
@@ -48,18 +54,7 @@ module OData
|
|
48
54
|
Sequel.like(args[1].leuqes(jh),
|
49
55
|
args[0].leuqes_substringof_sig1(jh))
|
50
56
|
elsif args[1].is_a?(QString)
|
51
|
-
|
52
|
-
# '__Route du Rhum__' contains name as a substring
|
53
|
-
# TODO... check if the database supports instr (how?)
|
54
|
-
# othewise use substr(postgresql) or whatevr?
|
55
|
-
instr_substr_func = if Sequel::Model.db.adapter_scheme == :postgres
|
56
|
-
Sequel.function(:strpos, args[1].leuqes(jh), args[0].leuqes(jh))
|
57
|
-
else
|
58
|
-
Sequel.function(:instr, args[1].leuqes(jh), args[0].leuqes(jh))
|
59
|
-
end
|
60
|
-
|
61
|
-
Sequel::SQL::BooleanExpression.new(:>, instr_substr_func, 0)
|
62
|
-
|
57
|
+
substringof_sig2(jh) # adapter specific
|
63
58
|
else
|
64
59
|
# TODO... actually not supported?
|
65
60
|
raise OData::Filter::Parser::ErrorFunctionArgumentType
|
@@ -75,6 +70,26 @@ module OData
|
|
75
70
|
Sequel.function(:upper, args.first.leuqes(jh))
|
76
71
|
when :tolower
|
77
72
|
Sequel.function(:lower, args.first.leuqes(jh))
|
73
|
+
# all datetime funcs are adapter specific (because sqlite does not have extract)
|
74
|
+
when :year
|
75
|
+
year(jh)
|
76
|
+
when :month
|
77
|
+
month(jh)
|
78
|
+
when :second
|
79
|
+
second(jh)
|
80
|
+
when :minute
|
81
|
+
minute(jh)
|
82
|
+
when :hour
|
83
|
+
hour(jh)
|
84
|
+
when :day
|
85
|
+
day(jh)
|
86
|
+
# math functions
|
87
|
+
when :round
|
88
|
+
Sequel.function(:round, args.first.leuqes(jh))
|
89
|
+
when :floor
|
90
|
+
floor(jh)
|
91
|
+
when :ceiling
|
92
|
+
ceiling(jh)
|
78
93
|
else
|
79
94
|
raise OData::FilterParseError
|
80
95
|
end
|
@@ -0,0 +1,147 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative './tree.rb'
|
4
|
+
require_relative './sequel.rb'
|
5
|
+
|
6
|
+
module OData
|
7
|
+
module Filter
|
8
|
+
# sqlite adapter specific function handler
|
9
|
+
module FuncTreeSqlite
|
10
|
+
def substringof_sig2(jh)
|
11
|
+
# substringof(name, '__Route du Rhum__') -->
|
12
|
+
# '__Route du Rhum__' contains name as a substring
|
13
|
+
# sqlite uses instr()
|
14
|
+
|
15
|
+
substr_func = Sequel.function(:instr, args[1].leuqes(jh), args[0].leuqes(jh))
|
16
|
+
|
17
|
+
Sequel::SQL::BooleanExpression.new(:>, substr_func, 0)
|
18
|
+
end
|
19
|
+
# %d day of month: 00
|
20
|
+
# %f fractional seconds: SS.SSS
|
21
|
+
# %H hour: 00-24
|
22
|
+
# %j day of year: 001-366
|
23
|
+
# %J Julian day number
|
24
|
+
# %m month: 01-12
|
25
|
+
# %M minute: 00-59
|
26
|
+
# %s seconds since 1970-01-01
|
27
|
+
# %S seconds: 00-59
|
28
|
+
# %w day of week 0-6 with Sunday==0
|
29
|
+
# %W week of year: 00-53
|
30
|
+
# %Y year: 0000-9999
|
31
|
+
# %% %
|
32
|
+
|
33
|
+
# sqlite does not have extract but recommends to use strftime
|
34
|
+
def year(jh)
|
35
|
+
Sequel.function(:strftime, '%Y', args.first.leuqes(jh)).cast(:integer)
|
36
|
+
end
|
37
|
+
|
38
|
+
def month(jh)
|
39
|
+
Sequel.function(:strftime, '%m', args.first.leuqes(jh)).cast(:integer)
|
40
|
+
end
|
41
|
+
|
42
|
+
def second(jh)
|
43
|
+
Sequel.function(:strftime, '%S', args.first.leuqes(jh)).cast(:integer)
|
44
|
+
end
|
45
|
+
|
46
|
+
def minute(jh)
|
47
|
+
Sequel.function(:strftime, '%M', args.first.leuqes(jh)).cast(:integer)
|
48
|
+
end
|
49
|
+
|
50
|
+
def hour(jh)
|
51
|
+
Sequel.function(:strftime, '%H', args.first.leuqes(jh)).cast(:integer)
|
52
|
+
end
|
53
|
+
|
54
|
+
def day(jh)
|
55
|
+
Sequel.function(:strftime, '%d', args.first.leuqes(jh)).cast(:integer)
|
56
|
+
end
|
57
|
+
|
58
|
+
def floor(jh)
|
59
|
+
raise OData::Filter::FunctionNotImplemented, "$filter function 'floor' is not implemented in sqlite adapter"
|
60
|
+
end
|
61
|
+
|
62
|
+
def ceiling(jh)
|
63
|
+
raise OData::Filter::FunctionNotImplemented, "$filter function 'ceiling' is not implemented in sqlite adapter"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
# re-useable module with math floor/ceil functions for those adapters having these SQL funcs
|
67
|
+
module MathFloorCeilFuncTree
|
68
|
+
def floor(jh)
|
69
|
+
Sequel.function(:floor, args.first.leuqes(jh))
|
70
|
+
end
|
71
|
+
|
72
|
+
def ceiling(jh)
|
73
|
+
Sequel.function(:ceil, args.first.leuqes(jh))
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# re-useable module with Datetime functions with extract()
|
78
|
+
module DateTimeFuncTreeExtract
|
79
|
+
def year(jh)
|
80
|
+
args.first.leuqes(jh).extract(:year)
|
81
|
+
end
|
82
|
+
|
83
|
+
def year(jh)
|
84
|
+
args.first.leuqes(jh).extract(:year)
|
85
|
+
end
|
86
|
+
|
87
|
+
def month(jh)
|
88
|
+
args.first.leuqes(jh).extract(:month)
|
89
|
+
end
|
90
|
+
|
91
|
+
def second(jh)
|
92
|
+
args.first.leuqes(jh).extract(:second)
|
93
|
+
end
|
94
|
+
|
95
|
+
def minute(jh)
|
96
|
+
args.first.leuqes(jh).extract(:minute)
|
97
|
+
end
|
98
|
+
|
99
|
+
def hour(jh)
|
100
|
+
args.first.leuqes(jh).extract(:hour)
|
101
|
+
end
|
102
|
+
|
103
|
+
def day(jh)
|
104
|
+
args.first.leuqes(jh).extract(:day)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# postgresql adapter specific function handler
|
109
|
+
module FuncTreePostgres
|
110
|
+
def substringof_sig2(jh)
|
111
|
+
# substringof(name, '__Route du Rhum__') -->
|
112
|
+
# '__Route du Rhum__' contains name as a substring
|
113
|
+
# postgres does not know instr() but has strpos
|
114
|
+
substr_func = Sequel.function(:strpos, args[1].leuqes(jh), args[0].leuqes(jh))
|
115
|
+
|
116
|
+
Sequel::SQL::BooleanExpression.new(:>, substr_func, 0)
|
117
|
+
end
|
118
|
+
|
119
|
+
# postgres uses extract()
|
120
|
+
include DateTimeFuncTreeExtract
|
121
|
+
|
122
|
+
# postgres has floor/ceil funcs
|
123
|
+
include MathFloorCeilFuncTree
|
124
|
+
end
|
125
|
+
|
126
|
+
# default adapter function handler for all others... try to use the most common version
|
127
|
+
# :substring --> instr because here is seems Postgres is special
|
128
|
+
# datetime funcs --> exctract, here sqlite is special(uses format)
|
129
|
+
# note: we dont test this, provided as an example/template, might work eg for mysql
|
130
|
+
module FuncTreeDefault
|
131
|
+
def substringof_sig2(jh)
|
132
|
+
# substringof(name, '__Route du Rhum__') -->
|
133
|
+
# '__Route du Rhum__' contains name as a substring
|
134
|
+
# instr() seems to be the most common substring func
|
135
|
+
substr_func = Sequel.function(:instr, args[1].leuqes(jh), args[0].leuqes(jh))
|
136
|
+
|
137
|
+
Sequel::SQL::BooleanExpression.new(:>, substr_func, 0)
|
138
|
+
end
|
139
|
+
|
140
|
+
# XYZ uses extract() ?
|
141
|
+
include DateTimeFuncTreeExtract
|
142
|
+
|
143
|
+
# ... assume SQL
|
144
|
+
include MathFloorCeilFuncTree
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
data/lib/odata/filter/token.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# top level OData namespace
|
2
4
|
module OData
|
3
5
|
module Filter
|
@@ -5,7 +7,9 @@ module OData
|
|
5
7
|
# Input tokenizer
|
6
8
|
module Token
|
7
9
|
FUNCNAMES = %w[concat substringof endswith startswith length indexof
|
8
|
-
replace substring trim toupper tolower
|
10
|
+
replace substring trim toupper tolower
|
11
|
+
day hour minute month second year
|
12
|
+
round floor ceiling].freeze
|
9
13
|
FUNCRGX = FUNCNAMES.join('|').freeze
|
10
14
|
QSTRINGRGX = /'((?:[^']|(?:\'{2}))*)'/.freeze
|
11
15
|
BINOBOOL = '[eE][qQ]|[LlgGNn][eETt]|[aA][nN][dD]|[oO][rR]'.freeze
|
data/lib/odata/filter/tree.rb
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative './base.rb'
|
1
4
|
require_relative './error.rb'
|
2
5
|
|
3
6
|
module OData
|
@@ -16,17 +19,22 @@ module OData
|
|
16
19
|
end
|
17
20
|
|
18
21
|
# Leaves are Nodes with a parent but no children
|
19
|
-
class Leave
|
22
|
+
class Leave
|
20
23
|
attr_accessor :parent
|
21
24
|
def accept?(tok, typ)
|
22
25
|
[false, Parser::ErrorInvalidToken(tok, typ)]
|
23
26
|
end
|
24
27
|
|
25
28
|
def check_types; end
|
29
|
+
|
30
|
+
def attach(child)
|
31
|
+
# TODO better reporting of error infos
|
32
|
+
raise ErrorLeaveChild
|
33
|
+
end
|
26
34
|
end
|
27
35
|
|
28
36
|
# RootTrees have childrens but no parent
|
29
|
-
class RootTree
|
37
|
+
class RootTree
|
30
38
|
attr_reader :children
|
31
39
|
attr_accessor :state
|
32
40
|
def initialize(val: :root, &block)
|
@@ -71,7 +79,7 @@ module OData
|
|
71
79
|
end
|
72
80
|
|
73
81
|
# Tree's have Parent and children
|
74
|
-
class Tree
|
82
|
+
class Tree
|
75
83
|
attr_accessor :parent
|
76
84
|
|
77
85
|
def initialize(val)
|
@@ -80,7 +88,7 @@ module OData
|
|
80
88
|
end
|
81
89
|
|
82
90
|
# For functions... should have a single child---> the argument list
|
83
|
-
class FuncTree
|
91
|
+
class FuncTree
|
84
92
|
def initialize(val)
|
85
93
|
super(val.downcase.to_sym)
|
86
94
|
end
|
@@ -143,7 +151,7 @@ module OData
|
|
143
151
|
# Indentity Func to use as "parent" func of parenthesis expressions
|
144
152
|
# --> allow to handle generically parenthesis always as argument of
|
145
153
|
# some function
|
146
|
-
class IdentityFuncTree
|
154
|
+
class IdentityFuncTree
|
147
155
|
def initialize
|
148
156
|
super(:__indentity)
|
149
157
|
end
|
@@ -160,7 +168,7 @@ module OData
|
|
160
168
|
end
|
161
169
|
|
162
170
|
# unary op eg. NOT
|
163
|
-
class UnopTree
|
171
|
+
class UnopTree
|
164
172
|
def initialize(val)
|
165
173
|
super(val.downcase.to_sym)
|
166
174
|
end
|
@@ -187,7 +195,7 @@ module OData
|
|
187
195
|
end
|
188
196
|
|
189
197
|
# Bin ops
|
190
|
-
class BinopTree
|
198
|
+
class BinopTree
|
191
199
|
def initialize(val)
|
192
200
|
@state = :open
|
193
201
|
super(val.downcase.to_sym)
|
@@ -201,7 +209,7 @@ module OData
|
|
201
209
|
end
|
202
210
|
end
|
203
211
|
|
204
|
-
class BinopBool
|
212
|
+
class BinopBool
|
205
213
|
# reference:
|
206
214
|
# OData v4 par 5.1.1.9 Operator Precedence
|
207
215
|
def precedence
|
@@ -224,7 +232,7 @@ module OData
|
|
224
232
|
end
|
225
233
|
end
|
226
234
|
|
227
|
-
class BinopArithm
|
235
|
+
class BinopArithm
|
228
236
|
# reference:
|
229
237
|
# OData v4 par 5.1.1.9 Operator Precedence
|
230
238
|
def precedence
|
@@ -245,7 +253,7 @@ module OData
|
|
245
253
|
end
|
246
254
|
|
247
255
|
# Arguments or lists
|
248
|
-
class ArgTree
|
256
|
+
class ArgTree
|
249
257
|
attr_reader :type
|
250
258
|
def initialize(val)
|
251
259
|
@type = :expression
|
@@ -307,7 +315,7 @@ module OData
|
|
307
315
|
end
|
308
316
|
|
309
317
|
# Numbers (floating point, ints, dec)
|
310
|
-
class FPNumber
|
318
|
+
class FPNumber
|
311
319
|
def accept?(tok, typ)
|
312
320
|
case typ
|
313
321
|
when :Delimiter, :Separator, :BinopBool, :BinopArithm
|
@@ -324,7 +332,7 @@ module OData
|
|
324
332
|
end
|
325
333
|
|
326
334
|
# Literals are unquoted words without /
|
327
|
-
class Literal
|
335
|
+
class Literal
|
328
336
|
def accept?(tok, typ)
|
329
337
|
case typ
|
330
338
|
when :Delimiter, :Separator, :BinopBool, :BinopArithm
|
@@ -337,11 +345,23 @@ module OData
|
|
337
345
|
def edm_type
|
338
346
|
:any
|
339
347
|
end
|
348
|
+
|
349
|
+
# error, Literal are leaves
|
350
|
+
# when the child is a IdentityFuncTree then this looks like
|
351
|
+
# an attempt to use a unknown function, eg. ceil(Total)
|
352
|
+
# instead of ceiling(Total)
|
353
|
+
def attach(child)
|
354
|
+
if child.kind_of? OData::Filter::IdentityFuncTree
|
355
|
+
raise Parser::ErrorInvalidFunction.new("Error in $filter expr.: invalid function #{self.value}")
|
356
|
+
else
|
357
|
+
super
|
358
|
+
end
|
359
|
+
end
|
340
360
|
end
|
341
361
|
|
342
362
|
# Qualit (qualified lits) are words separated by /
|
343
363
|
# path/path/path/attrib
|
344
|
-
class Qualit
|
364
|
+
class Qualit
|
345
365
|
REGEXP = /((?:\w+\/)+)(\w+)/.freeze
|
346
366
|
attr_reader :path
|
347
367
|
attr_reader :attrib
|
@@ -356,7 +376,7 @@ module OData
|
|
356
376
|
end
|
357
377
|
|
358
378
|
# Quoted Strings
|
359
|
-
class QString
|
379
|
+
class QString
|
360
380
|
DBL_QO = "''".freeze
|
361
381
|
SI_QO = "'".freeze
|
362
382
|
def initialize(val)
|