norikra 0.0.13-java → 0.0.14-java
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.
- data/lib/norikra/engine.rb +101 -80
- data/lib/norikra/field.rb +204 -0
- data/lib/norikra/fieldset.rb +167 -0
- data/lib/norikra/logger.rb +37 -22
- data/lib/norikra/query.rb +152 -14
- data/lib/norikra/query/ast.rb +65 -22
- data/lib/norikra/rpc/handler.rb +10 -3
- data/lib/norikra/server.rb +8 -2
- data/lib/norikra/target.rb +24 -0
- data/lib/norikra/typedef.rb +117 -234
- data/lib/norikra/typedef_manager.rb +22 -16
- data/lib/norikra/version.rb +1 -1
- data/norikra.gemspec +1 -1
- data/script/spec_server_pry +31 -0
- data/spec/field_spec.rb +131 -2
- data/spec/fieldset_spec.rb +92 -23
- data/spec/query_spec.rb +204 -29
- data/spec/spec_helper.rb +39 -3
- data/spec/target_spec.rb +18 -0
- data/spec/typedef_manager_spec.rb +35 -12
- data/spec/typedef_spec.rb +235 -24
- metadata +6 -4
data/lib/norikra/query/ast.rb
CHANGED
@@ -52,6 +52,33 @@ module Norikra
|
|
52
52
|
# "double"],
|
53
53
|
# "10.5"]]]]
|
54
54
|
|
55
|
+
### SELECT count(*) AS cnt
|
56
|
+
### FROM TestTable.win:time_batch(10 sec)
|
57
|
+
### WHERE params.$$path.$1="/" AND size.$0.bytes > 100 and opts.num.seq.length() > 0
|
58
|
+
|
59
|
+
# ["EPL_EXPR",
|
60
|
+
# ["SELECTION_EXPR", ["SELECTION_ELEMENT_EXPR", "count", "cnt"]],
|
61
|
+
# ["STREAM_EXPR",
|
62
|
+
# ["EVENT_FILTER_EXPR", "TestTable"],
|
63
|
+
# ["VIEW_EXPR", "win", "time_batch", ["TIME_PERIOD", ["SECOND_PART", "10"]]]],
|
64
|
+
# ["WHERE_EXPR",
|
65
|
+
# ["EVAL_AND_EXPR",
|
66
|
+
# ["EVAL_EQUALS_EXPR",
|
67
|
+
# ["EVENT_PROP_EXPR",
|
68
|
+
# ["EVENT_PROP_SIMPLE", "params"],
|
69
|
+
# ["EVENT_PROP_SIMPLE", "$$path"],
|
70
|
+
# ["EVENT_PROP_SIMPLE", "$1"]],
|
71
|
+
# "\"/\""],
|
72
|
+
# [">",
|
73
|
+
# ["EVENT_PROP_EXPR",
|
74
|
+
# ["EVENT_PROP_SIMPLE", "size"],
|
75
|
+
# ["EVENT_PROP_SIMPLE", "$0"],
|
76
|
+
# ["EVENT_PROP_SIMPLE", "bytes"]],
|
77
|
+
# "100"],
|
78
|
+
# [">",
|
79
|
+
# ["LIB_FUNC_CHAIN", ["LIB_FUNCTION", "opts.num.seq", "length", "("]],
|
80
|
+
# "0"]]]]
|
81
|
+
|
55
82
|
def astnode(tree)
|
56
83
|
children = if tree.children
|
57
84
|
tree.children.map{|c| astnode(c)}
|
@@ -114,23 +141,28 @@ module Norikra
|
|
114
141
|
result
|
115
142
|
end
|
116
143
|
|
117
|
-
def fields(default_target=nil)
|
118
|
-
@children.map{|c| c.nodetype?(:subquery) ? [] : c.fields(default_target)}.reduce(&:+) || []
|
144
|
+
def fields(default_target=nil, known_targets_aliases=[])
|
145
|
+
@children.map{|c| c.nodetype?(:subquery) ? [] : c.fields(default_target, known_targets_aliases)}.reduce(&:+) || []
|
119
146
|
end
|
120
147
|
end
|
121
148
|
|
122
149
|
class ASTEventPropNode < ASTNode # EVENT_PROP_EXPR
|
123
|
-
# ["EVENT_PROP_EXPR", ["EVENT_PROP_SIMPLE", "bbb"]]
|
124
|
-
# ["EVENT_PROP_EXPR", ["EVENT_PROP_SIMPLE", "fraud"], ["EVENT_PROP_SIMPLE", "aaa"]]
|
150
|
+
# "bbb" => ["EVENT_PROP_EXPR", ["EVENT_PROP_SIMPLE", "bbb"]]
|
151
|
+
# "fraud.aaa" => ["EVENT_PROP_EXPR", ["EVENT_PROP_SIMPLE", "fraud"], ["EVENT_PROP_SIMPLE", "aaa"]]
|
152
|
+
# "size.$0.bytes" => ["EVENT_PROP_EXPR", ["EVENT_PROP_SIMPLE", "size"], ["EVENT_PROP_SIMPLE", "$0"], ["EVENT_PROP_SIMPLE", "bytes"]]
|
125
153
|
|
126
154
|
def nodetype?(*sym)
|
127
155
|
sym.include?(:prop) || sym.include?(:property)
|
128
156
|
end
|
129
157
|
|
130
|
-
def fields(default_target=nil)
|
158
|
+
def fields(default_target=nil, known_targets_aliases=[])
|
131
159
|
props = self.listup('EVENT_PROP_SIMPLE')
|
132
|
-
if props.size > 1 # alias.fieldname
|
133
|
-
|
160
|
+
if props.size > 1 # alias.fieldname or container_fieldname.key.$1
|
161
|
+
if known_targets_aliases.include?(props[0].child.name)
|
162
|
+
[ {:f => props[1..-1].map{|n| n.child.name}.join("."), :t => props[0].child.name} ]
|
163
|
+
else
|
164
|
+
[ {:f => props.map{|n| n.child.name}.join("."), :t => default_target} ]
|
165
|
+
end
|
134
166
|
else # fieldname (default target)
|
135
167
|
[ {:f => props[0].child.name, :t => default_target } ]
|
136
168
|
end
|
@@ -138,42 +170,53 @@ module Norikra
|
|
138
170
|
end
|
139
171
|
|
140
172
|
class ASTLibFunctionNode < ASTNode # LIB_FUNCTION
|
141
|
-
# ["LIB_FUNCTION", "now", "("]
|
142
|
-
# ["LIB_FUNCTION", "hoge", "length", "("]
|
143
|
-
# ["LIB_FUNCTION", "hoge", "substr", "0", "("]
|
144
|
-
# ["LIB_FUNCTION", "substr", "10", "0", "("]
|
145
|
-
# ["LIB_FUNCTION", "hoge", "substr", "0", "8", "("]
|
146
|
-
|
173
|
+
# "now()" => ["LIB_FUNCTION", "now", "("]
|
174
|
+
# "hoge.length()" => ["LIB_FUNCTION", "hoge", "length", "("]
|
175
|
+
# "hoge.substr(0)" => ["LIB_FUNCTION", "hoge", "substr", "0", "("]
|
176
|
+
# "substr(10,0)" => ["LIB_FUNCTION", "substr", "10", "0", "("]
|
177
|
+
# "hoge.substr(0,8)" => ["LIB_FUNCTION", "hoge", "substr", "0", "8", "("]
|
178
|
+
|
179
|
+
# "opts.num.$0.length()" => ["LIB_FUNCTION", "opts.num.$0", "length", "("]
|
180
|
+
|
181
|
+
# "max(size)" => ["LIB_FUNCTION", "max", ["EVENT_PROP_EXPR", ["EVENT_PROP_SIMPLE", "size"]], "("]
|
147
182
|
|
148
183
|
def nodetype?(*sym)
|
149
184
|
sym.include?(:lib) || sym.include?(:libfunc)
|
150
185
|
end
|
151
186
|
|
152
|
-
def fields(default_target=nil)
|
187
|
+
def fields(default_target=nil, known_targets_aliases=[])
|
153
188
|
if @children.size <= 2
|
154
189
|
# single function like 'now()', function-name and "("
|
155
190
|
[]
|
156
191
|
|
157
192
|
elsif @children[1].nodetype?(:prop, :lib, :subquery)
|
158
193
|
# first element should be func name if second element is property, library call or subqueries
|
159
|
-
self.listup('EVENT_PROP_EXPR').map{|c| c.fields(default_target)}.reduce(&:+) || []
|
194
|
+
self.listup('EVENT_PROP_EXPR').map{|c| c.fields(default_target, known_targets_aliases)}.reduce(&:+) || []
|
160
195
|
|
161
196
|
elsif @children[1].name =~ /^(-)?\d+(\.\d+)$/ || @children[1].name =~ /^'[^']*'$/ || @children[1].name =~ /^"[^"]*"$/
|
162
197
|
# first element should be func name if secod element is number/string literal
|
163
|
-
self.listup('EVENT_PROP_EXPR').map{|c| c.fields(default_target)}.reduce(&:+) || []
|
198
|
+
self.listup('EVENT_PROP_EXPR').map{|c| c.fields(default_target, known_targets_aliases)}.reduce(&:+) || []
|
164
199
|
|
165
200
|
elsif Norikra::Query.imported_java_class?(@children[0].name)
|
166
201
|
# Java imported class name (ex: 'Math.abs(-1)')
|
167
|
-
self.listup('EVENT_PROP_EXPR').map{|c| c.fields(default_target)}.reduce(&:+) || []
|
202
|
+
self.listup('EVENT_PROP_EXPR').map{|c| c.fields(default_target, known_targets_aliases)}.reduce(&:+) || []
|
168
203
|
|
169
204
|
else
|
170
|
-
# first element may be property
|
205
|
+
# first element may be property
|
206
|
+
# * simple 'fieldname.funcname()'
|
207
|
+
# * fully qualified 'target.fieldname.funcname()'
|
208
|
+
# * simple/fully-qualified container field access 'fieldname.key.$0.funcname()' or 'target.fieldname.$1.funcname()'
|
171
209
|
target,fieldname = if @children[0].name.include?('.')
|
172
|
-
@children[0].name.split('.'
|
210
|
+
parts = @children[0].name.split('.')
|
211
|
+
if known_targets_aliases.include?(parts[0])
|
212
|
+
[ parts[0], parts[1..-1].join(".") ]
|
213
|
+
else
|
214
|
+
[ default_target, @children[0].name ]
|
215
|
+
end
|
173
216
|
else
|
174
217
|
[default_target,@children[0].name]
|
175
218
|
end
|
176
|
-
children_list = self.listup('EVENT_PROP_EXPR').map{|c| c.fields(default_target)}.reduce(&:+) || []
|
219
|
+
children_list = self.listup('EVENT_PROP_EXPR').map{|c| c.fields(default_target, known_targets_aliases)}.reduce(&:+) || []
|
177
220
|
[{:f => fieldname, :t => target}] + children_list
|
178
221
|
end
|
179
222
|
end
|
@@ -202,9 +245,9 @@ module Norikra
|
|
202
245
|
@children.last.children.size < 1 ? @children.last.name : nil
|
203
246
|
end
|
204
247
|
|
205
|
-
def fields(default_target=nil)
|
248
|
+
def fields(default_target=nil, known_targets_aliases=[])
|
206
249
|
this_target = self.target
|
207
|
-
self.listup('EVENT_PROP_EXPR').map{|p| p.fields(this_target)}.reduce(&:+) || []
|
250
|
+
self.listup('EVENT_PROP_EXPR').map{|p| p.fields(this_target,known_targets_aliases)}.reduce(&:+) || []
|
208
251
|
end
|
209
252
|
end
|
210
253
|
|
data/lib/norikra/rpc/handler.rb
CHANGED
@@ -32,13 +32,13 @@ class Norikra::RPC::Handler
|
|
32
32
|
|
33
33
|
def targets
|
34
34
|
logging(:show, :targets){
|
35
|
-
@engine.targets
|
35
|
+
@engine.targets.map(&:to_hash)
|
36
36
|
}
|
37
37
|
end
|
38
38
|
|
39
|
-
def open(target, fields)
|
39
|
+
def open(target, fields, auto_field)
|
40
40
|
logging(:manage, :open, target, fields){
|
41
|
-
r = @engine.open(target, fields)
|
41
|
+
r = @engine.open(target, fields, auto_field)
|
42
42
|
!!r
|
43
43
|
}
|
44
44
|
end
|
@@ -50,6 +50,13 @@ class Norikra::RPC::Handler
|
|
50
50
|
}
|
51
51
|
end
|
52
52
|
|
53
|
+
def modify(target, auto_field)
|
54
|
+
logging(:manage, :modify, target, auto_field){
|
55
|
+
r = @engine.modify(target, auto_field)
|
56
|
+
!!r
|
57
|
+
}
|
58
|
+
end
|
59
|
+
|
53
60
|
def queries
|
54
61
|
logging(:show, :queries){
|
55
62
|
@engine.queries.map(&:to_hash)
|
data/lib/norikra/server.rb
CHANGED
@@ -108,7 +108,7 @@ module Norikra
|
|
108
108
|
info "loading from stats file"
|
109
109
|
if @stats.targets && @stats.targets.size > 0
|
110
110
|
@stats.targets.each do |target|
|
111
|
-
@engine.open(target[:name], target[:fields])
|
111
|
+
@engine.open(target[:name], target[:fields], target[:auto_field])
|
112
112
|
end
|
113
113
|
end
|
114
114
|
if @stats.queries && @stats.queries.size > 0
|
@@ -163,7 +163,13 @@ module Norikra
|
|
163
163
|
port: @port,
|
164
164
|
threads: @thread_conf,
|
165
165
|
log: @log_conf,
|
166
|
-
targets: @engine.
|
166
|
+
targets: @engine.targets.map{|t|
|
167
|
+
{
|
168
|
+
:name => t.name,
|
169
|
+
:fields => @engine.typedef_manager.dump_target(t.name),
|
170
|
+
:auto_field => t.auto_field
|
171
|
+
}
|
172
|
+
},
|
167
173
|
queries: @engine.queries.map{|q| {:name => q.name, :expression => q.expression}}
|
168
174
|
)
|
169
175
|
stats.dump(@stats_path)
|
data/lib/norikra/target.rb
CHANGED
@@ -1,7 +1,31 @@
|
|
1
1
|
module Norikra
|
2
2
|
class Target
|
3
|
+
attr_accessor :name, :fields, :auto_field
|
4
|
+
|
3
5
|
def self.valid?(target_name)
|
4
6
|
target_name =~ /^[a-zA-Z]([_a-zA-Z0-9]*[a-zA-Z0-9])?$/
|
5
7
|
end
|
8
|
+
|
9
|
+
def initialize(name, fields=[], auto_field=true)
|
10
|
+
@name = name
|
11
|
+
@fields = fields
|
12
|
+
@auto_field = !!auto_field
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_s
|
16
|
+
@name
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_hash
|
20
|
+
{:name => @name, :auto_field => @auto_field}
|
21
|
+
end
|
22
|
+
|
23
|
+
def ==(other)
|
24
|
+
self.class == other.class ? self.name == other.name : self.name == other.to_s
|
25
|
+
end
|
26
|
+
|
27
|
+
def auto_field?
|
28
|
+
@auto_field
|
29
|
+
end
|
6
30
|
end
|
7
31
|
end
|
data/lib/norikra/typedef.rb
CHANGED
@@ -1,238 +1,41 @@
|
|
1
|
-
require 'digest'
|
2
1
|
require 'json'
|
3
2
|
|
4
3
|
require 'norikra/error'
|
5
4
|
|
6
|
-
|
5
|
+
require 'norikra/field'
|
6
|
+
require 'norikra/fieldset'
|
7
7
|
|
8
8
|
module Norikra
|
9
|
-
class Field
|
10
|
-
attr_accessor :name, :type, :optional
|
11
|
-
|
12
|
-
def initialize(name, type, optional=nil)
|
13
|
-
@name = name.to_s
|
14
|
-
@type = self.class.valid_type?(type)
|
15
|
-
@optional = optional
|
16
|
-
end
|
17
|
-
|
18
|
-
def to_hash(sym=false)
|
19
|
-
if sym
|
20
|
-
{name: @name, type: @type, optional: @optional}
|
21
|
-
else
|
22
|
-
{'name' => @name, 'type' => @type, 'optional' => @optional}
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
def dup(optional=nil)
|
27
|
-
self.class.new(@name, @type, optional.nil? ? @optional : optional)
|
28
|
-
end
|
29
|
-
|
30
|
-
def ==(other)
|
31
|
-
self.name == other.name && self.type == other.type && self.optional == other.optional
|
32
|
-
end
|
33
|
-
|
34
|
-
def optional? # used outside of FieldSet
|
35
|
-
@optional
|
36
|
-
end
|
37
|
-
|
38
|
-
### esper types
|
39
|
-
### http://esper.codehaus.org/esper-4.9.0/doc/reference/en-US/html/epl_clauses.html#epl-syntax-datatype
|
40
|
-
# string A single character to an unlimited number of characters.
|
41
|
-
# boolean A boolean value.
|
42
|
-
# integer An integer value (4 byte).
|
43
|
-
# long A long value (8 byte). Use the "L" or "l" (lowercase L) suffix. # select 1L as field1, 1l as field2
|
44
|
-
# double A double-precision 64-bit IEEE 754 floating point. # select 1.67 as field1, 167e-2 as field2, 1.67d as field3
|
45
|
-
# float A single-precision 32-bit IEEE 754 floating point. Use the "f" suffix. # select 1.2f as field1, 1.2F as field2
|
46
|
-
# byte A 8-bit signed two's complement integer. # select 0x10 as field1
|
47
|
-
#
|
48
|
-
#### 'integer' in epser document IS WRONG.
|
49
|
-
#### If 'integer' specified, esper raises this exception:
|
50
|
-
### Exception: Nestable type configuration encountered an unexpected property type name 'integer' for property 'status',
|
51
|
-
### expected java.lang.Class or java.util.Map or the name of a previously-declared Map or ObjectArray type
|
52
|
-
#### Correct type name is 'int'. see and run 'junks/esper-test.rb'
|
53
|
-
def self.valid_type?(type)
|
54
|
-
case type.to_s.downcase
|
55
|
-
when 'string' then 'string'
|
56
|
-
when 'boolean' then 'boolean'
|
57
|
-
when 'int' then 'int'
|
58
|
-
when 'long' then 'long'
|
59
|
-
when 'float' then 'float'
|
60
|
-
when 'double' then 'double'
|
61
|
-
else
|
62
|
-
raise Norikra::ArgumentError, "invalid field type '#{type}'"
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
def format(value)
|
67
|
-
case @type
|
68
|
-
when 'string' then value.to_s
|
69
|
-
when 'boolean' then value =~ /^(true|false)$/i ? ($1.downcase == 'true') : (!!value)
|
70
|
-
when 'long','int' then value.to_i
|
71
|
-
when 'double','float' then value.to_f
|
72
|
-
else
|
73
|
-
raise RuntimeError, "unknown field type (in format), maybe BUG. name:#{@name},type:#{@type}"
|
74
|
-
end
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
class FieldSet
|
79
|
-
attr_accessor :summary, :fields
|
80
|
-
attr_accessor :target, :level
|
81
|
-
|
82
|
-
def initialize(fields, default_optional=nil, rebounds=0)
|
83
|
-
@fields = {}
|
84
|
-
fields.keys.each do |key|
|
85
|
-
data = fields[key]
|
86
|
-
type,optional = if data.is_a?(Hash)
|
87
|
-
[data[:type].to_s, (data.has_key?(:optional) ? data[:optional] : default_optional)]
|
88
|
-
elsif data.is_a?(String) || data.is_a?(Symbol)
|
89
|
-
[data.to_s, default_optional]
|
90
|
-
else
|
91
|
-
raise ArgumentError, "FieldSet.new argument class unknown: #{fields.class}"
|
92
|
-
end
|
93
|
-
@fields[key.to_s] = Field.new(key.to_s, type, optional)
|
94
|
-
end
|
95
|
-
self.update_summary
|
96
|
-
|
97
|
-
@target = nil
|
98
|
-
@level = nil
|
99
|
-
@rebounds = rebounds
|
100
|
-
@event_type_name = nil
|
101
|
-
end
|
102
|
-
|
103
|
-
def dup
|
104
|
-
fields = Hash[@fields.map{|key,field| [key, {:type => field.type, :optional => field.optional}]}]
|
105
|
-
self.class.new(fields, nil, @rebounds)
|
106
|
-
end
|
107
|
-
|
108
|
-
def self.field_names_key(data, fieldset=nil)
|
109
|
-
if fieldset
|
110
|
-
keys = []
|
111
|
-
fieldset.fields.each do |key,field|
|
112
|
-
unless field.optional?
|
113
|
-
keys.push(key)
|
114
|
-
end
|
115
|
-
end
|
116
|
-
data.keys.each do |key|
|
117
|
-
keys.push(key) unless keys.include?(key)
|
118
|
-
end
|
119
|
-
keys.sort.join(',')
|
120
|
-
else
|
121
|
-
data.keys.sort.join(',')
|
122
|
-
end
|
123
|
-
end
|
124
|
-
|
125
|
-
def field_names_key
|
126
|
-
self.class.field_names_key(@fields)
|
127
|
-
end
|
128
|
-
|
129
|
-
def update_summary
|
130
|
-
@summary = @fields.keys.sort.map{|k| @fields[k].name + ':' + @fields[k].type}.join(',')
|
131
|
-
self
|
132
|
-
end
|
133
|
-
|
134
|
-
def update(fields, optional_flag)
|
135
|
-
fields.each do |field|
|
136
|
-
@fields[field.name] = field.dup(optional_flag)
|
137
|
-
end
|
138
|
-
self.update_summary
|
139
|
-
end
|
140
|
-
|
141
|
-
#TODO: have a bug?
|
142
|
-
def ==(other)
|
143
|
-
return false if self.class != other.class
|
144
|
-
self.summary == other.summary
|
145
|
-
end
|
146
|
-
|
147
|
-
def definition
|
148
|
-
d = {}
|
149
|
-
@fields.each do |key, field|
|
150
|
-
d[field.name] = field.type
|
151
|
-
end
|
152
|
-
d
|
153
|
-
end
|
154
|
-
|
155
|
-
def subset?(other) # self is subset of other (or not)
|
156
|
-
(self.fields.keys - other.fields.keys).size == 0
|
157
|
-
end
|
158
|
-
|
159
|
-
def event_type_name
|
160
|
-
@event_type_name.dup
|
161
|
-
end
|
162
|
-
|
163
|
-
def bind(target, level, update_type_name=false)
|
164
|
-
@target = target
|
165
|
-
@level = level
|
166
|
-
prefix = case level
|
167
|
-
when :base then 'b_'
|
168
|
-
when :query then 'q_'
|
169
|
-
when :data then 'e_' # event
|
170
|
-
else
|
171
|
-
raise ArgumentError, "unknown fieldset bind level: #{level}, for target #{target}"
|
172
|
-
end
|
173
|
-
@rebounds += 1 if update_type_name
|
174
|
-
|
175
|
-
@event_type_name = prefix + Digest::MD5.hexdigest([target, level.to_s, @rebounds.to_s, @summary].join("\t"))
|
176
|
-
self
|
177
|
-
end
|
178
|
-
|
179
|
-
def rebind(update_type_name)
|
180
|
-
self.dup.bind(@target, @level, update_type_name)
|
181
|
-
end
|
182
|
-
|
183
|
-
def self.simple_guess(data, optional=true)
|
184
|
-
mapping = Hash[
|
185
|
-
data.map{|key,value|
|
186
|
-
type = case value
|
187
|
-
when TrueClass,FalseClass then 'boolean'
|
188
|
-
when Integer then 'long'
|
189
|
-
when Float then 'double'
|
190
|
-
else
|
191
|
-
'string'
|
192
|
-
end
|
193
|
-
[key,type]
|
194
|
-
}
|
195
|
-
]
|
196
|
-
self.new(mapping, optional)
|
197
|
-
end
|
198
|
-
|
199
|
-
# def self.guess(data, optional=true)
|
200
|
-
# mapping = Hash[
|
201
|
-
# data.map{|key,value|
|
202
|
-
# sval = value.to_s
|
203
|
-
# type = case
|
204
|
-
# when val.is_a?(TrueClass) || val.is_a?(FalseClass) || sval =~ /^(?:true|false)$/i
|
205
|
-
# 'boolean'
|
206
|
-
# when val.is_a?(Integer) || sval =~ /^-?\d+[lL]?$/
|
207
|
-
# 'long'
|
208
|
-
# when val.is_a?(Float) || sval =~ /^-?\d+\.\d+(?:[eE]-?\d+|[dDfF])?$/
|
209
|
-
# 'double'
|
210
|
-
# else
|
211
|
-
# 'string'
|
212
|
-
# end
|
213
|
-
# [key,type]
|
214
|
-
# }
|
215
|
-
# ]
|
216
|
-
# self.new(mapping, optional)
|
217
|
-
# end
|
218
|
-
end
|
219
|
-
|
220
9
|
# Typedef is
|
221
|
-
# * known field list of target (and these are optional or not)
|
10
|
+
# * known field list of target (and these are optional or not), and container fields
|
222
11
|
# * known field-set list of a target
|
223
12
|
# * base set of a target
|
224
13
|
class Typedef
|
225
|
-
attr_accessor :fields, :baseset, :queryfieldsets, :datafieldsets
|
14
|
+
attr_accessor :fields, :container_fields, :waiting_fields ,:baseset, :queryfieldsets, :datafieldsets
|
226
15
|
|
227
16
|
def initialize(fields=nil)
|
17
|
+
@container_fields = {}
|
18
|
+
|
228
19
|
if fields && !fields.empty?
|
229
|
-
|
230
|
-
@fields =
|
20
|
+
given_set = FieldSet.new(fields, false) # default_optional=false, but chained access fields are always optional
|
21
|
+
@fields = given_set.fields.dup
|
22
|
+
base_fields = {}
|
23
|
+
@fields.values.each do |field|
|
24
|
+
if !field.optional?
|
25
|
+
base_fields[field.name] = field
|
26
|
+
elsif field.chained_access?
|
27
|
+
cname = field.container_name
|
28
|
+
@container_fields[cname] = Norikra::Field.new(cname, field.container_type, true)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
@baseset = FieldSet.new(base_fields, false)
|
231
32
|
else
|
232
33
|
@baseset = nil
|
233
34
|
@fields = {}
|
234
35
|
end
|
235
36
|
|
37
|
+
@waiting_fields = []
|
38
|
+
|
236
39
|
@queryfieldsets = []
|
237
40
|
@datafieldsets = []
|
238
41
|
|
@@ -242,6 +45,8 @@ module Norikra
|
|
242
45
|
end
|
243
46
|
|
244
47
|
def field_defined?(list)
|
48
|
+
# used for only queries: TypedefManager#ready?(query) and TypedefManager#generate_field_mapping(query)
|
49
|
+
# be not needed to think about containers
|
245
50
|
list.reduce(true){|r,f| r && @fields[f]}
|
246
51
|
end
|
247
52
|
|
@@ -256,6 +61,9 @@ module Norikra
|
|
256
61
|
set.fields[fieldname] = field.dup(false)
|
257
62
|
end
|
258
63
|
@baseset = set
|
64
|
+
@baseset.fields.each do |name,f|
|
65
|
+
@waiting_fields.delete(name) if @waiting_fields.include?(name)
|
66
|
+
end
|
259
67
|
@fields = @baseset.fields.merge(@fields)
|
260
68
|
end
|
261
69
|
end
|
@@ -264,7 +72,15 @@ module Norikra
|
|
264
72
|
fieldname = fieldname.to_s
|
265
73
|
@mutex.synchronize do
|
266
74
|
return false if @fields[fieldname]
|
267
|
-
|
75
|
+
field = Norikra::Field.new(fieldname, type, optional)
|
76
|
+
if @waiting_fields.include?(fieldname)
|
77
|
+
@waiting_fields.delete(fieldname)
|
78
|
+
end
|
79
|
+
@fields[fieldname] = field
|
80
|
+
if field.chained_access? && !@container_fields[field.container_name]
|
81
|
+
container = Norikra::Field.new(field.container_name, field.container_type, true)
|
82
|
+
@container_fields[field.container_name] = container
|
83
|
+
end
|
268
84
|
end
|
269
85
|
true
|
270
86
|
end
|
@@ -272,12 +88,13 @@ module Norikra
|
|
272
88
|
def consistent?(fieldset)
|
273
89
|
fields = fieldset.fields
|
274
90
|
@baseset.subset?(fieldset) &&
|
275
|
-
@fields.values.select{|f| !f.optional? }.reduce(true){|r,f| r && fields
|
276
|
-
fields.values.reduce(true){|r,f| r && (
|
91
|
+
@fields.values.select{|f| !f.optional? }.reduce(true){|r,f| r && fields.has_key?(f.name) && fields[f.name].type == f.type} &&
|
92
|
+
fields.values.reduce(true){|r,f| r && (!@fields.has_key?(f.name) || @fields[f.name].type == f.type)}
|
277
93
|
end
|
278
94
|
|
279
95
|
def push(level, fieldset)
|
280
96
|
unless self.consistent?(fieldset)
|
97
|
+
warn "fieldset mismatch", :self => self, :with => fieldset
|
281
98
|
raise Norikra::ArgumentError, "field definition mismatch with already defined fields"
|
282
99
|
end
|
283
100
|
|
@@ -292,6 +109,7 @@ module Norikra
|
|
292
109
|
@queryfieldsets.push(fieldset)
|
293
110
|
|
294
111
|
fieldset.fields.each do |fieldname,field|
|
112
|
+
@waiting_fields.delete(fieldname) if @waiting_fields.include?(fieldname)
|
295
113
|
@fields[fieldname] = field.dup(true) unless @fields[fieldname]
|
296
114
|
end
|
297
115
|
end
|
@@ -301,7 +119,15 @@ module Norikra
|
|
301
119
|
@set_map[fieldset.field_names_key] = fieldset
|
302
120
|
|
303
121
|
fieldset.fields.each do |fieldname,field|
|
304
|
-
@
|
122
|
+
if @waiting_fields.include?(fieldname)
|
123
|
+
@waiting_fields.delete(fieldname)
|
124
|
+
end
|
125
|
+
unless @fields[fieldname]
|
126
|
+
@fields[fieldname] = field.dup(true)
|
127
|
+
if field.chained_access? && !@container_fields[field.container_name]
|
128
|
+
@container_fields[field.container_name] = Norikra::Field.new(field.container_name, field.container_type, true)
|
129
|
+
end
|
130
|
+
end
|
305
131
|
end
|
306
132
|
end
|
307
133
|
else
|
@@ -329,6 +155,7 @@ module Norikra
|
|
329
155
|
|
330
156
|
def replace(level, old_fieldset, fieldset)
|
331
157
|
unless self.consistent?(fieldset)
|
158
|
+
warn "fieldset mismatch", :self => self, :with => fieldset
|
332
159
|
raise Norikra::ArgumentError, "field definition mismatch with already defined fields"
|
333
160
|
end
|
334
161
|
if level != :data
|
@@ -345,11 +172,65 @@ module Norikra
|
|
345
172
|
true
|
346
173
|
end
|
347
174
|
|
348
|
-
def
|
349
|
-
|
175
|
+
def simple_guess(data, optional=true, strict=false)
|
176
|
+
flatten_key_value_pairs = []
|
177
|
+
|
178
|
+
data.each do |key,value|
|
179
|
+
next if strict && !(@fields.has_key?(key) || @waiting_fields.include?(key) || value.is_a?(Hash) || value.is_a?(Array))
|
180
|
+
|
181
|
+
if value.is_a?(Hash) || value.is_a?(Array)
|
182
|
+
Norikra::FieldSet.leaves(value).map{|chain| [key] + chain}.each do |chain|
|
183
|
+
value = chain.pop
|
184
|
+
key = Norikra::Field.regulate_key_chain(chain).join('.')
|
185
|
+
next unless @fields.has_key?(key) || @waiting_fields.include?(key)
|
186
|
+
flatten_key_value_pairs.push([key, value])
|
187
|
+
end
|
188
|
+
else
|
189
|
+
flatten_key_value_pairs.push([key, value])
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
mapping = Hash[
|
194
|
+
flatten_key_value_pairs.map{|key,value|
|
195
|
+
type = case value
|
196
|
+
when TrueClass,FalseClass then 'boolean'
|
197
|
+
when Integer then 'long'
|
198
|
+
when Float then 'double'
|
199
|
+
else
|
200
|
+
'string'
|
201
|
+
end
|
202
|
+
[key,type]
|
203
|
+
}
|
204
|
+
]
|
205
|
+
|
206
|
+
FieldSet.new(mapping, optional)
|
207
|
+
end
|
208
|
+
|
209
|
+
# def self.guess(data, optional=true)
|
210
|
+
# mapping = Hash[
|
211
|
+
# data.map{|key,value|
|
212
|
+
# sval = value.to_s
|
213
|
+
# type = case
|
214
|
+
# when val.is_a?(TrueClass) || val.is_a?(FalseClass) || sval =~ /^(?:true|false)$/i
|
215
|
+
# 'boolean'
|
216
|
+
# when val.is_a?(Integer) || sval =~ /^-?\d+[lL]?$/
|
217
|
+
# 'long'
|
218
|
+
# when val.is_a?(Float) || sval =~ /^-?\d+\.\d+(?:[eE]-?\d+|[dDfF])?$/
|
219
|
+
# 'double'
|
220
|
+
# else
|
221
|
+
# 'string'
|
222
|
+
# end
|
223
|
+
# [key,type]
|
224
|
+
# }
|
225
|
+
# ]
|
226
|
+
# self.new(mapping, optional)
|
227
|
+
# end
|
228
|
+
|
229
|
+
def refer(data, strict=false)
|
230
|
+
field_names_key = FieldSet.field_names_key(data, self, strict, @waiting_fields)
|
350
231
|
return @set_map[field_names_key] if @set_map.has_key?(field_names_key)
|
351
232
|
|
352
|
-
guessed =
|
233
|
+
guessed = self.simple_guess(data, false, strict)
|
353
234
|
guessed_fields = guessed.fields
|
354
235
|
@fields.each do |key,field|
|
355
236
|
if guessed_fields.has_key?(key)
|
@@ -359,23 +240,25 @@ module Norikra
|
|
359
240
|
guessed_fields[key] = field unless field.optional?
|
360
241
|
end
|
361
242
|
end
|
362
|
-
guessed.update_summary
|
243
|
+
guessed.update_summary #=> guessed
|
363
244
|
end
|
364
245
|
|
365
|
-
def
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
ret[key] = @fields[key].format(value)
|
246
|
+
def dump_all
|
247
|
+
fields = {}
|
248
|
+
@fields.each do |key,field|
|
249
|
+
fields[field.name] = field.to_hash(true)
|
370
250
|
end
|
371
|
-
|
251
|
+
fields
|
372
252
|
end
|
373
253
|
|
374
|
-
def dump
|
254
|
+
def dump # to cli display
|
375
255
|
fields = {}
|
376
|
-
@fields.
|
256
|
+
@fields.each do |key,field|
|
257
|
+
fields[key.to_sym] = field.to_hash(true) unless field.chained_access?
|
258
|
+
end
|
259
|
+
@container_fields.each do |key, field|
|
377
260
|
fields[key.to_sym] = field.to_hash(true)
|
378
|
-
|
261
|
+
end
|
379
262
|
fields
|
380
263
|
end
|
381
264
|
end
|