norikra 0.0.1-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/.gitignore +17 -0
- data/.ruby-version +1 -0
- data/Gemfile +4 -0
- data/LICENSE +339 -0
- data/README.md +104 -0
- data/Rakefile +10 -0
- data/bin/norikra +8 -0
- data/esper/changelog.txt +1066 -0
- data/esper/esper-4.9.0.jar +0 -0
- data/esper/esper-license.txt +95 -0
- data/esper/esper/lib/antlr-runtime-3.2.jar +0 -0
- data/esper/esper/lib/cglib-nodep-2.2.jar +0 -0
- data/esper/esper/lib/commons-logging-1.1.1.jar +0 -0
- data/esper/esper/lib/esper_3rdparties.license +299 -0
- data/esper/esper/lib/log4j-1.2.16.jar +0 -0
- data/esper/esper/lib/readme.txt +38 -0
- data/esper/esperio-amqp-4.9.0.jar +0 -0
- data/esper/esperio-amqp/lib/commons-cli-1.1.jar +0 -0
- data/esper/esperio-amqp/lib/commons-io-1.2.jar +0 -0
- data/esper/esperio-amqp/lib/esperio_3rdparties.license +1328 -0
- data/esper/esperio-amqp/lib/esperio_amqp_jars.txt +2 -0
- data/esper/esperio-amqp/lib/rabbitmq-client.jar +0 -0
- data/esper/esperio-csv-4.9.0.jar +0 -0
- data/esper/esperio-csv/lib/esperio_3rdparties.license +1328 -0
- data/esper/esperio-db-4.9.0.jar +0 -0
- data/esper/esperio-db/lib/esperio_3rdparties.license +1328 -0
- data/esper/esperio-http-4.9.0.jar +0 -0
- data/esper/esperio-http/lib/esperio_3rdparties.license +1328 -0
- data/esper/esperio-http/lib/httpclient-4.0.1.jar +0 -0
- data/esper/esperio-http/lib/httpcore-4.0.1.jar +0 -0
- data/esper/esperio-http/lib/httpcore-nio-4.0.1.jar +0 -0
- data/esper/esperio-license.txt +95 -0
- data/esper/esperio-socket-4.9.0.jar +0 -0
- data/esper/esperio-socket/lib/esperio_3rdparties.license +1328 -0
- data/esper/esperio-springjms-4.9.0.jar +0 -0
- data/esper/esperio-springjms/lib/activation-1.1.jar +0 -0
- data/esper/esperio-springjms/lib/activemq-core-5.7.0.jar +0 -0
- data/esper/esperio-springjms/lib/activemq-pool-5.7.0.jar +0 -0
- data/esper/esperio-springjms/lib/commons-pool-1.6.jar +0 -0
- data/esper/esperio-springjms/lib/esperio_3rdparties.license +1328 -0
- data/esper/esperio-springjms/lib/geronimo-j2ee-management_1.1_spec-1.0.1.jar +0 -0
- data/esper/esperio-springjms/lib/geronimo-jms_1.1_spec-1.1.1.jar +0 -0
- data/esper/esperio-springjms/lib/junit-4.8.2.jar +0 -0
- data/esper/esperio-springjms/lib/org.springframework.asm-3.1.1.RELEASE.jar +0 -0
- data/esper/esperio-springjms/lib/org.springframework.beans-3.1.1.RELEASE.jar +0 -0
- data/esper/esperio-springjms/lib/org.springframework.context-3.1.1.RELEASE.jar +0 -0
- data/esper/esperio-springjms/lib/org.springframework.core-3.1.1.RELEASE.jar +0 -0
- data/esper/esperio-springjms/lib/org.springframework.expression-3.1.1.RELEASE.jar +0 -0
- data/esper/esperio-springjms/lib/org.springframework.jms-3.1.1.RELEASE.jar +0 -0
- data/esper/esperio-springjms/lib/org.springframework.transaction-3.1.1.RELEASE.jar +0 -0
- data/esper/esperio-springjms/lib/slf4j-api-1.7.2.jar +0 -0
- data/esper/esperio-springjms/lib/slf4j-log4j12-1.7.2.jar +0 -0
- data/esper/esperio-stax-4.9.0.jar +0 -0
- data/esper/esperio-stax/lib/axiom-api-1.2.9.jar +0 -0
- data/esper/esperio-stax/lib/axiom-c14n-1.2.9.jar +0 -0
- data/esper/esperio-stax/lib/axiom-dom-1.2.9.jar +0 -0
- data/esper/esperio-stax/lib/axiom-impl-1.2.9.jar +0 -0
- data/esper/esperio-stax/lib/commons-logging-1.1.1.jar +0 -0
- data/esper/esperio-stax/lib/commons-logging-LICENSE.txt +203 -0
- data/esper/esperio-stax/lib/esperio_3rdparties.license +1328 -0
- data/esper/esperio-stax/lib/geronimo-activation-LICENSE.txt +203 -0
- data/esper/esperio-stax/lib/geronimo-activation_1.1_spec-1.0.2.jar +0 -0
- data/esper/esperio-stax/lib/geronimo-javamail-LICENSE.txt +203 -0
- data/esper/esperio-stax/lib/geronimo-javamail_1.4_spec-1.6.jar +0 -0
- data/esper/esperio-stax/lib/geronimo-stax-api-LICENSE.txt +203 -0
- data/esper/esperio-stax/lib/geronimo-stax-api_1.0_spec-1.0.1.jar +0 -0
- data/esper/esperio-stax/lib/jaxen-1.1.1.jar +0 -0
- data/esper/esperio-stax/lib/jaxen-LICENSE.txt +33 -0
- data/esper/esperio-stax/lib/wstx-LICENSE.txt +203 -0
- data/esper/esperio-stax/lib/wstx-asl-3.2.9.jar +0 -0
- data/junks/esper-test.rb +79 -0
- data/junks/glassfish.rb +17 -0
- data/junks/mizuno.rb +28 -0
- data/junks/simple_test.rb +35 -0
- data/junks/type_confliction.rb +46 -0
- data/junks/type_conversion.rb +49 -0
- data/junks/type_inherit.rb +67 -0
- data/lib/norikra.rb +13 -0
- data/lib/norikra/cli.rb +24 -0
- data/lib/norikra/engine.rb +231 -0
- data/lib/norikra/output_pool.rb +45 -0
- data/lib/norikra/query.rb +173 -0
- data/lib/norikra/rpc.rb +6 -0
- data/lib/norikra/rpc/handler.rb +57 -0
- data/lib/norikra/rpc/http.rb +36 -0
- data/lib/norikra/server.rb +41 -0
- data/lib/norikra/typedef.rb +307 -0
- data/lib/norikra/typedef_manager.rb +91 -0
- data/lib/norikra/version.rb +3 -0
- data/norikra.gemspec +33 -0
- data/script/spec_server_pry +45 -0
- data/spec/field_spec.rb +141 -0
- data/spec/fieldset_spec.rb +172 -0
- data/spec/output_pool_spec.rb +75 -0
- data/spec/query_spec.rb +82 -0
- data/spec/spec_helper.rb +55 -0
- data/spec/typedef_manager_spec.rb +128 -0
- data/spec/typedef_spec.rb +248 -0
- metadata +328 -0
data/lib/norikra/rpc.rb
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
class Norikra::RPC::Handler
|
|
2
|
+
def initialize(engine)
|
|
3
|
+
@engine = engine
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
def targets
|
|
7
|
+
@engine.targets
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def open(target, fields)
|
|
11
|
+
r = @engine.open(target, fields)
|
|
12
|
+
!!r
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def close(target)
|
|
16
|
+
r = @engine.close(target)
|
|
17
|
+
!!r
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def queries
|
|
21
|
+
@engine.queries.map(&:to_hash)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def register(query_name, expression)
|
|
25
|
+
r = @engine.register(Norikra::Query.new(:name => query_name, :expression => expression))
|
|
26
|
+
!!r
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def deregister(query_name)
|
|
30
|
+
#TODO: write!
|
|
31
|
+
raise NotImplementedError
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def fields(target)
|
|
35
|
+
@engine.typedef_manager.field_list(target)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def reserve(target, fieldname, type)
|
|
39
|
+
r = @engine.reserve(target, fieldname, type)
|
|
40
|
+
!!r
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def send(target, events)
|
|
44
|
+
r = @engine.send(target, events)
|
|
45
|
+
!!r
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def event(query_name)
|
|
49
|
+
@engine.output_pool.pop(query_name)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def sweep
|
|
53
|
+
@engine.output_pool.sweep
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# post('/listen') # get all events as stream, during connection keepaliving
|
|
57
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
require 'mizuno/server'
|
|
2
|
+
require 'rack/builder'
|
|
3
|
+
require 'msgpack-rpc-over-http-jruby'
|
|
4
|
+
|
|
5
|
+
require_relative 'handler'
|
|
6
|
+
|
|
7
|
+
module Norikra::RPC
|
|
8
|
+
class HTTP
|
|
9
|
+
#TODO Xmx of mizuno/jetty
|
|
10
|
+
attr_accessor :host, :port, :threads
|
|
11
|
+
attr_accessor :engine, :mizuno, :thread
|
|
12
|
+
|
|
13
|
+
def initialize(opts={})
|
|
14
|
+
@engine = opts[:engine]
|
|
15
|
+
@host = opts[:host]
|
|
16
|
+
@port = opts[:port]
|
|
17
|
+
handler = Norikra::RPC::Handler.new(@engine)
|
|
18
|
+
@app = Rack::Builder.new {
|
|
19
|
+
run MessagePack::RPCOverHTTP::Server.app(handler)
|
|
20
|
+
}
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def start
|
|
24
|
+
@thread = Thread.new do
|
|
25
|
+
@mizuno = Mizuno::Server.new
|
|
26
|
+
@mizuno.run(@app, :embedded => true, :threads => 5, :port => @port, :host => @host)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def stop
|
|
31
|
+
@mizuno.stop
|
|
32
|
+
@thread.kill
|
|
33
|
+
@thread.join
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
require 'norikra/engine'
|
|
2
|
+
|
|
3
|
+
require 'norikra/typedef_manager'
|
|
4
|
+
require 'norikra/output_pool'
|
|
5
|
+
require 'norikra/typedef'
|
|
6
|
+
require 'norikra/query'
|
|
7
|
+
|
|
8
|
+
require 'norikra/rpc'
|
|
9
|
+
|
|
10
|
+
module Norikra
|
|
11
|
+
class Server
|
|
12
|
+
RPC_DEFAULT_HOST = '0.0.0.0'
|
|
13
|
+
RPC_DEFAULT_PORT = 26571
|
|
14
|
+
# 26571 = 3026 + 3014 + 2968 + 2950 + 2891 + 2896 + 2975 + 2979 + 2872
|
|
15
|
+
|
|
16
|
+
def initialize(host=RPC_DEFAULT_HOST, port=RPC_DEFAULT_PORT, configuration={})
|
|
17
|
+
#TODO: initial configuration
|
|
18
|
+
@typedef_manager = Norikra::TypedefManager.new
|
|
19
|
+
|
|
20
|
+
@output_pool = Norikra::OutputPool.new
|
|
21
|
+
|
|
22
|
+
@engine = Norikra::Engine.new(@output_pool, @typedef_manager)
|
|
23
|
+
@rpcserver = Norikra::RPC::HTTP.new(:engine => @engine, :port => port)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def run
|
|
27
|
+
@engine.start
|
|
28
|
+
@rpcserver.start
|
|
29
|
+
p "Norikra server started."
|
|
30
|
+
#TODO: main loop and signal traps
|
|
31
|
+
#TODO: loggings
|
|
32
|
+
sleep 50
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def shutdown
|
|
36
|
+
#TODO: stop order
|
|
37
|
+
@rpcserver.stop
|
|
38
|
+
@engine.stop
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
require 'digest'
|
|
2
|
+
require 'json'
|
|
3
|
+
|
|
4
|
+
# Norikra::Field, Norikra::FieldSet, Norikra::Typedef
|
|
5
|
+
|
|
6
|
+
module Norikra
|
|
7
|
+
class Field
|
|
8
|
+
attr_accessor :name, :type, :optional
|
|
9
|
+
|
|
10
|
+
def initialize(name, type, optional=nil)
|
|
11
|
+
@name = name.to_s
|
|
12
|
+
@type = self.class.valid_type?(type)
|
|
13
|
+
@optional = optional
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def to_hash
|
|
17
|
+
{'name' => @name, 'type' => @type, 'optional' => @optional}
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def dup(optional=nil)
|
|
21
|
+
self.class.new(@name, @type, optional.nil? ? @optional : optional)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def ==(other)
|
|
25
|
+
self.name == other.name && self.type == other.type && self.optional == other.optional
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def optional? # used outside of FieldSet
|
|
29
|
+
@optional
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
### esper types
|
|
33
|
+
### http://esper.codehaus.org/esper-4.9.0/doc/reference/en-US/html/epl_clauses.html#epl-syntax-datatype
|
|
34
|
+
# string A single character to an unlimited number of characters.
|
|
35
|
+
# boolean A boolean value.
|
|
36
|
+
# integer An integer value (4 byte).
|
|
37
|
+
# long A long value (8 byte). Use the "L" or "l" (lowercase L) suffix. # select 1L as field1, 1l as field2
|
|
38
|
+
# double A double-precision 64-bit IEEE 754 floating point. # select 1.67 as field1, 167e-2 as field2, 1.67d as field3
|
|
39
|
+
# float A single-precision 32-bit IEEE 754 floating point. Use the "f" suffix. # select 1.2f as field1, 1.2F as field2
|
|
40
|
+
# byte A 8-bit signed two's complement integer. # select 0x10 as field1
|
|
41
|
+
#
|
|
42
|
+
#### 'integer' in epser document IS WRONG.
|
|
43
|
+
#### If 'integer' specified, esper raises this exception:
|
|
44
|
+
### Exception: Nestable type configuration encountered an unexpected property type name 'integer' for property 'status',
|
|
45
|
+
### expected java.lang.Class or java.util.Map or the name of a previously-declared Map or ObjectArray type
|
|
46
|
+
#### Correct type name is 'int'. see and run 'junks/esper-test.rb'
|
|
47
|
+
def self.valid_type?(type)
|
|
48
|
+
case type.to_s.downcase
|
|
49
|
+
when 'string' then 'string'
|
|
50
|
+
when 'boolean' then 'boolean'
|
|
51
|
+
when 'int' then 'int'
|
|
52
|
+
when 'long' then 'long'
|
|
53
|
+
when 'float' then 'float'
|
|
54
|
+
when 'double' then 'double'
|
|
55
|
+
else
|
|
56
|
+
raise ArgumentError, "invalid field type #{type}"
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def format(value)
|
|
61
|
+
case @type
|
|
62
|
+
when 'string' then value.to_s
|
|
63
|
+
when 'boolean' then value =~ /^(true|false)$/i ? ($1.downcase == 'true') : (!!value)
|
|
64
|
+
when 'long','int' then value.to_i
|
|
65
|
+
when 'double','float' then value.to_f
|
|
66
|
+
else
|
|
67
|
+
raise RuntimeError, "unknown field type (in format), maybe BUG. name:#{@name},type:#{@type}"
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
class FieldSet
|
|
73
|
+
attr_accessor :summary, :fields
|
|
74
|
+
attr_accessor :target, :level
|
|
75
|
+
|
|
76
|
+
def initialize(fields, default_optional=nil)
|
|
77
|
+
@fields = {}
|
|
78
|
+
fields.keys.each do |key|
|
|
79
|
+
data = fields[key]
|
|
80
|
+
type,optional = if data.is_a?(Hash)
|
|
81
|
+
[data[:type], (data.has_key?(:optional) ? data[:optional] : default_optional)]
|
|
82
|
+
elsif data.is_a?(String)
|
|
83
|
+
[data, default_optional]
|
|
84
|
+
else
|
|
85
|
+
raise ArgumentError, "FieldSet.new argument class unknown: #{fields.class}"
|
|
86
|
+
end
|
|
87
|
+
@fields[key.to_s] = Field.new(key, type, optional)
|
|
88
|
+
end
|
|
89
|
+
self.update_summary
|
|
90
|
+
|
|
91
|
+
@target = nil
|
|
92
|
+
@level = nil
|
|
93
|
+
@event_type_name = nil
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def dup
|
|
97
|
+
fields = Hash[@fields.map{|key,field| [key, {:type => field.type, :optional => field.optional}]}]
|
|
98
|
+
self.class.new(fields)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def field_names_key
|
|
102
|
+
@fields.keys.sort.join(',')
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def update_summary
|
|
106
|
+
@summary = @fields.keys.sort.map{|k| @fields[k].name + ':' + @fields[k].type}.join(',')
|
|
107
|
+
self
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def update(fields, optional_flag)
|
|
111
|
+
fields.each do |field|
|
|
112
|
+
@fields[field.name] = field.dup(optional_flag)
|
|
113
|
+
end
|
|
114
|
+
self.update_summary
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
#TODO: have a bug?
|
|
118
|
+
def ==(other)
|
|
119
|
+
return false unless self.class != other.class
|
|
120
|
+
self.summary == other.summary
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def definition
|
|
124
|
+
d = {}
|
|
125
|
+
@fields.each do |key, field|
|
|
126
|
+
d[field.name] = field.type
|
|
127
|
+
end
|
|
128
|
+
d
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def subset?(other) # self is subset of other (or not)
|
|
132
|
+
(self.fields.keys - other.fields.keys).size == 0
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def event_type_name
|
|
136
|
+
@event_type_name.dup
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def bind(target, level)
|
|
140
|
+
@target = target
|
|
141
|
+
@level = level
|
|
142
|
+
prefix = case level
|
|
143
|
+
when :base then 'b_'
|
|
144
|
+
when :query then 'q_'
|
|
145
|
+
else 'e_'
|
|
146
|
+
end
|
|
147
|
+
@event_type_name = prefix + Digest::MD5.hexdigest(target + "\t" + level.to_s + "\t" + @summary)
|
|
148
|
+
self
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def self.simple_guess(data, optional=true)
|
|
152
|
+
mapping = Hash[
|
|
153
|
+
data.map{|key,value|
|
|
154
|
+
type = case value
|
|
155
|
+
when TrueClass,FalseClass then 'boolean'
|
|
156
|
+
when Integer then 'long'
|
|
157
|
+
when Float then 'double'
|
|
158
|
+
else
|
|
159
|
+
'string'
|
|
160
|
+
end
|
|
161
|
+
[key,type]
|
|
162
|
+
}
|
|
163
|
+
]
|
|
164
|
+
self.new(mapping, optional)
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# def self.guess(data, optional=true)
|
|
168
|
+
# mapping = Hash[
|
|
169
|
+
# data.map{|key,value|
|
|
170
|
+
# sval = value.to_s
|
|
171
|
+
# type = case
|
|
172
|
+
# when val.is_a?(TrueClass) || val.is_a?(FalseClass) || sval =~ /^(?:true|false)$/i
|
|
173
|
+
# 'boolean'
|
|
174
|
+
# when val.is_a?(Integer) || sval =~ /^-?\d+[lL]?$/
|
|
175
|
+
# 'long'
|
|
176
|
+
# when val.is_a?(Float) || sval =~ /^-?\d+\.\d+(?:[eE]-?\d+|[dDfF])?$/
|
|
177
|
+
# 'double'
|
|
178
|
+
# else
|
|
179
|
+
# 'string'
|
|
180
|
+
# end
|
|
181
|
+
# [key,type]
|
|
182
|
+
# }
|
|
183
|
+
# ]
|
|
184
|
+
# self.new(mapping, optional)
|
|
185
|
+
# end
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
# Typedef is
|
|
189
|
+
# * known field list of target (and these are optional or not)
|
|
190
|
+
# * known field-set list of a target
|
|
191
|
+
# * base set of a target
|
|
192
|
+
class Typedef
|
|
193
|
+
attr_accessor :fields, :baseset, :queryfieldsets, :datafieldsets
|
|
194
|
+
|
|
195
|
+
def initialize(fields=nil)
|
|
196
|
+
if fields
|
|
197
|
+
@baseset = FieldSet.new(fields, false) # all fields are required
|
|
198
|
+
@fields = @baseset.fields.dup
|
|
199
|
+
else
|
|
200
|
+
@baseset = nil
|
|
201
|
+
@fields = {}
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
@queryfieldsets = []
|
|
205
|
+
@datafieldsets = []
|
|
206
|
+
|
|
207
|
+
@set_map = {} # fieldname.sort.join(',') => data_fieldset
|
|
208
|
+
|
|
209
|
+
@mutex = Mutex.new
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def field_defined?(list)
|
|
213
|
+
list.reduce(true){|r,f| r && @fields[f]}
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
def lazy?
|
|
217
|
+
@baseset.nil?
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
def activate(fieldset)
|
|
221
|
+
@mutex.synchronize do
|
|
222
|
+
set = fieldset.dup
|
|
223
|
+
fieldset.fields.dup.each do |fieldname, field|
|
|
224
|
+
set.fields[fieldname] = field.dup(false)
|
|
225
|
+
end
|
|
226
|
+
@baseset = set
|
|
227
|
+
@fields = @baseset.fields.merge(@fields)
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
def reserve(fieldname, type, optional=true)
|
|
232
|
+
fieldname = fieldname.to_s
|
|
233
|
+
@mutex.synchronize do
|
|
234
|
+
return false if @fields[fieldname]
|
|
235
|
+
@fields[fieldname] = Field.new(fieldname, type, optional)
|
|
236
|
+
end
|
|
237
|
+
true
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
def consistent?(fieldset)
|
|
241
|
+
fields = fieldset.fields
|
|
242
|
+
@baseset.subset?(fieldset) &&
|
|
243
|
+
@fields.values.select{|f| !f.optional? }.reduce(true){|r,f| r && fields[f.name] && fields[f.name].type == f.type} &&
|
|
244
|
+
fields.values.reduce(true){|r,f| r && (@fields[f.name].nil? || @fields[f.name].type == f.type)}
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
def push(level, fieldset)
|
|
248
|
+
unless self.consistent?(fieldset)
|
|
249
|
+
raise ArgumentError, "inconsistent field set for this typedef"
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
@mutex.synchronize do
|
|
253
|
+
case level
|
|
254
|
+
when :base
|
|
255
|
+
unless @baseset.object_id == fieldset.object_id
|
|
256
|
+
raise RuntimeError, "baseset mismatch"
|
|
257
|
+
end
|
|
258
|
+
when :query
|
|
259
|
+
unless @queryfieldsets.include?(fieldset)
|
|
260
|
+
@queryfieldsets.push(fieldset)
|
|
261
|
+
|
|
262
|
+
fieldset.fields.each do |fieldname,field|
|
|
263
|
+
@fields[fieldname] = field.dup(true) unless @fields[fieldname]
|
|
264
|
+
end
|
|
265
|
+
end
|
|
266
|
+
when :data
|
|
267
|
+
unless @datafieldsets.include?(fieldset)
|
|
268
|
+
@datafieldsets.push(fieldset)
|
|
269
|
+
@set_map[fieldset.field_names_key] = fieldset
|
|
270
|
+
|
|
271
|
+
fieldset.fields.each do |fieldname,field|
|
|
272
|
+
@fields[fieldname] = field.dup(true) unless @fields[fieldname]
|
|
273
|
+
end
|
|
274
|
+
end
|
|
275
|
+
else
|
|
276
|
+
raise ArgumentError, "unknown level #{level}"
|
|
277
|
+
end
|
|
278
|
+
end
|
|
279
|
+
true
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
def refer(data)
|
|
283
|
+
field_names_key = data.keys.sort.join(',')
|
|
284
|
+
return @set_map[field_names_key] if @set_map.has_key?(field_names_key)
|
|
285
|
+
|
|
286
|
+
guessed = FieldSet.simple_guess(data)
|
|
287
|
+
guessed_fields = guessed.fields
|
|
288
|
+
@fields.each do |key,field|
|
|
289
|
+
if guessed_fields.has_key?(key)
|
|
290
|
+
guessed_fields[key].type = field.type if guessed_fields[key].type != field.type
|
|
291
|
+
else
|
|
292
|
+
guessed_fields[key] = field unless field.optional?
|
|
293
|
+
end
|
|
294
|
+
end
|
|
295
|
+
guessed
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
def format(data)
|
|
299
|
+
# all keys of data should be already known at #format (before #format, do #refer)
|
|
300
|
+
ret = {}
|
|
301
|
+
data.each do |key, value|
|
|
302
|
+
ret[key] = @fields[key].format(value)
|
|
303
|
+
end
|
|
304
|
+
ret
|
|
305
|
+
end
|
|
306
|
+
end
|
|
307
|
+
end
|