dataMetaDom 1.0.0
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.
- checksums.yaml +7 -0
- data/.yardopts +1 -0
- data/History.md +6 -0
- data/PostInstall.txt +1 -0
- data/README.md +137 -0
- data/Rakefile +13 -0
- data/bin/dataMetaGvExport.rb +97 -0
- data/bin/dataMetaMySqlDdl.rb +27 -0
- data/bin/dataMetaOracleDdl.rb +26 -0
- data/bin/dataMetaPojo.rb +27 -0
- data/bin/dataMetaReVersion.rb +66 -0
- data/bin/dataMetaSameFullJ.rb +13 -0
- data/bin/dataMetaSameIdJ.rb +12 -0
- data/lib/dataMetaDom/converter.rb +70 -0
- data/lib/dataMetaDom/dataType.rb +112 -0
- data/lib/dataMetaDom/docs.rb +122 -0
- data/lib/dataMetaDom/enum.rb +125 -0
- data/lib/dataMetaDom/field.rb +182 -0
- data/lib/dataMetaDom/help.rb +41 -0
- data/lib/dataMetaDom/model.rb +274 -0
- data/lib/dataMetaDom/mySql.rb +256 -0
- data/lib/dataMetaDom/ora.rb +295 -0
- data/lib/dataMetaDom/pojo.rb +1056 -0
- data/lib/dataMetaDom/python.rb +168 -0
- data/lib/dataMetaDom/recAttr.rb +271 -0
- data/lib/dataMetaDom/record.rb +397 -0
- data/lib/dataMetaDom/ref.rb +127 -0
- data/lib/dataMetaDom/sourceFile.rb +150 -0
- data/lib/dataMetaDom/sources.rb +68 -0
- data/lib/dataMetaDom/util.rb +279 -0
- data/lib/dataMetaDom/ver.rb +244 -0
- data/lib/dataMetaDom.rb +141 -0
- data/test/test_dataMetaDom.rb +130 -0
- data/test/test_helper.rb +6 -0
- data/tmpl/java/migrationEntityEnums.erb +172 -0
- data/tmpl/python/entity.erb +50 -0
- metadata +126 -0
@@ -0,0 +1,168 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__)) unless $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
2
|
+
|
3
|
+
require 'set'
|
4
|
+
require 'fileutils'
|
5
|
+
require 'dataMetaDom/help'
|
6
|
+
require 'dataMetaDom/field'
|
7
|
+
require 'dataMetaDom/pojo' # we borrow some useful methods from the POJO Lexer
|
8
|
+
require 'erb'
|
9
|
+
require 'ostruct'
|
10
|
+
|
11
|
+
module DataMetaDom
|
12
|
+
=begin rdoc
|
13
|
+
Definition for generating Python basic classes that do not relate to serialization layer.
|
14
|
+
|
15
|
+
For command line details either check the new method's source or the README.rdoc file, the usage section.
|
16
|
+
=end
|
17
|
+
module PythonLexer
|
18
|
+
include DataMetaDom
|
19
|
+
|
20
|
+
# Standard header for every __init__.py file
|
21
|
+
INIT_PY_HEADER = %q<
|
22
|
+
# see https://docs.python.org/3/library/pkgutil.html
|
23
|
+
# without this, Python will have trouble finding packages that share some common tree off the root
|
24
|
+
from pkgutil import extend_path
|
25
|
+
__path__ = extend_path(__path__, __name__)
|
26
|
+
|
27
|
+
>
|
28
|
+
|
29
|
+
# The top of each model file.
|
30
|
+
MODEL_HEADER = %q|# This file is generated by DataMeta DOM. Do not edit manually!
|
31
|
+
# package %s
|
32
|
+
|
33
|
+
import re
|
34
|
+
|
35
|
+
from ebay_datameta_core.base import Verifiable, DateTime, Migrator, SemVer
|
36
|
+
from ebay_datameta_core.canned_re import CannedRe
|
37
|
+
|
38
|
+
# noinspection PyCompatibility
|
39
|
+
from enum import Enum
|
40
|
+
|
41
|
+
|
|
42
|
+
|
43
|
+
# Augment the RegExRoster class with Python specifics
|
44
|
+
class PyRegExRoster < RegExRoster
|
45
|
+
# converts the registry to the Python variable -- compiled Re
|
46
|
+
def to_patterns
|
47
|
+
i_to_r.keys.map { |ix|
|
48
|
+
r = i_to_r[ix]
|
49
|
+
rx = r.r.to_s
|
50
|
+
%<#{RegExRoster.ixToVarName(ix)} = re.compile(#{rx.inspect}) # #{r.vars.to_a.sort.join(', ')}>
|
51
|
+
}.join("\n#{INDENT}")
|
52
|
+
end
|
53
|
+
|
54
|
+
# converts the registry to the verification code for the verify() method
|
55
|
+
def to_verifications(baseName)
|
56
|
+
result = (canned.keys.map { |r|
|
57
|
+
r = canned[r]
|
58
|
+
vs = r.vars.to_a.sort
|
59
|
+
vs.map { |v|
|
60
|
+
rx = r.r.to_s
|
61
|
+
%<\n#{INDENT*2}if(#{r.req? ? '' : "self.__#{v} is not None and "}CannedRe.CANNED_RES[#{rx.inspect}].match(self.__#{v}) is None):
|
62
|
+
#{INDENT*3}raise AttributeError("Property \\"#{v}\\" == {{%s}} didn't match canned expression \\"#{rx}\\"" % self.__#{v} )>
|
63
|
+
}
|
64
|
+
}).flatten
|
65
|
+
(result << i_to_r.keys.map { |ix|
|
66
|
+
r = i_to_r[ix]
|
67
|
+
vs = r.vars.to_a.sort
|
68
|
+
rv = "#{baseName}.#{RegExRoster.ixToVarName(ix)}"
|
69
|
+
vs.map { |v|
|
70
|
+
%<\n#{INDENT*2}if(#{r.req? ? '' : "self.__#{v} is not None and "}#{rv}.match(self.__#{v}) is None):
|
71
|
+
#{INDENT*3}raise AttributeError("Property \\"#{v}\\" == {{%s}} didn't match custom expression {{%s}}" %(self.__#{v}, #{rv}))>
|
72
|
+
}
|
73
|
+
}).flatten
|
74
|
+
result.join("\n")
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
=begin rdoc
|
79
|
+
Generates Python sources for the model, the "plain" Python part, without fancy dependencies.
|
80
|
+
* Parameters
|
81
|
+
* +parser+ - instance of Model
|
82
|
+
* +outRoot+ - output directory
|
83
|
+
=end
|
84
|
+
def genPy(model, outRoot)
|
85
|
+
firstRecord = model.records.values.first
|
86
|
+
@pyPackage, baseName, packagePath = DataMetaDom::PojoLexer::assertNamespace(firstRecord.name)
|
87
|
+
|
88
|
+
# Next: replace dots with underscores.
|
89
|
+
# The path also adjusted accordingly.
|
90
|
+
#
|
91
|
+
# Rationale for this, quoting PEP 8:
|
92
|
+
#
|
93
|
+
# Package and Module Names
|
94
|
+
#
|
95
|
+
# Modules should have short, all-lowercase names. Underscores can be used in the module name if it improves
|
96
|
+
# readability. Python packages should also have short, all-lowercase names, although the use of underscores
|
97
|
+
# is discouraged.
|
98
|
+
#
|
99
|
+
# Short and all-lowercase names, and improving readability if you have complex system and need long package names,
|
100
|
+
# is "discouraged". Can't do this here, our system is more complicated for strictly religous, "pythonic" Python.
|
101
|
+
# A tool must be enabling, and in this case, this irrational ruling gets in the way.
|
102
|
+
# And dots are a no-no, Python can't find packages with complicated package structures and imports.
|
103
|
+
#
|
104
|
+
# Hence, we opt for long package names with underscores for distinctiveness and readability:
|
105
|
+
@pyPackage = @pyPackage.gsub('.', '_')
|
106
|
+
packagePath = packagePath.gsub('/', '_')
|
107
|
+
destDir = File.join(outRoot, packagePath)
|
108
|
+
FileUtils.mkdir_p destDir
|
109
|
+
# build the package path and create __init__.py files in each package's dir if it's not there.
|
110
|
+
@destDir = packagePath.split(File::SEPARATOR).reduce(outRoot){ |s,v|
|
111
|
+
r = s.empty? ? v : File.join(s, v) # next package dir
|
112
|
+
FileUtils.mkdir_p r # create if not there
|
113
|
+
# create the __init__.py with proper content if it's not there
|
114
|
+
df = File.join(r, '__init__.py'); IO.write(df, INIT_PY_HEADER, mode: 'wb') unless r == outRoot || File.file?(df)
|
115
|
+
r # pass r for the next reduce iteration
|
116
|
+
}
|
117
|
+
vars = OpenStruct.new # for template's local variables. ERB does not make them visible to the binding
|
118
|
+
modelFile = File.join(@destDir, 'model.py')
|
119
|
+
[modelFile].each{|f| FileUtils.rm f if File.file?(f)}
|
120
|
+
IO.write(modelFile, MODEL_HEADER % @pyPackage, mode: 'wb')
|
121
|
+
(model.records.values + model.enums.values).each { |srcE| # it is important that the records render first
|
122
|
+
_, baseName, _ = DataMetaDom::PojoLexer::assertNamespace(srcE.name)
|
123
|
+
|
124
|
+
pyClassName = baseName
|
125
|
+
|
126
|
+
case
|
127
|
+
when srcE.kind_of?(DataMetaDom::Record)
|
128
|
+
fields = srcE.fields
|
129
|
+
rxRoster = PyRegExRoster.new
|
130
|
+
eqHashFields = srcE.identity ? srcE.identity.args : fields.keys.sort
|
131
|
+
reqFields = fields.values.select{|f| f.isRequired }.map{|f| f.name}
|
132
|
+
verCalls = reqFields.map{|r| %<if(self.__#{r} is None): missingFields.append("#{r}");>}.join("\n#{INDENT * 2}")
|
133
|
+
fieldVerifications = ''
|
134
|
+
fields.each_key { |k|
|
135
|
+
f = fields[k]
|
136
|
+
dt = f.dataType
|
137
|
+
rxRoster.register(f) if f.regex
|
138
|
+
if f.trgType # Maps: if either the key or the value is verifiable, do it
|
139
|
+
mainVf = model.records[dt.type] # main data type is verifiable
|
140
|
+
trgVf = model.records[f.trgType.type] # target type is verifiable
|
141
|
+
if mainVf || trgVf
|
142
|
+
fieldVerifications << "\n#{INDENT*2}#{!f.isRequired ? "if(self.__#{f.name} is not None):\n#{INDENT*3}" : '' }for k, v in self.__#{f.name}.iteritems():#{mainVf ? 'k.verify();' : ''} #{trgVf ? 'v.verify()' :''}\n"
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
if model.records[dt.type] && !f.trgType # maps handled separately
|
147
|
+
fieldVerifications << "\n#{INDENT*2}#{!f.isRequired ? "if(self.__#{f.name} is not None): " : '' }#{f.aggr ? "[v___#{f.name}.verify() for v___#{f.name} in self.__#{f.name} ]" : "self.__#{f.name}.verify()"}"
|
148
|
+
# the Verifiable::verify method reference works just fine, tested it: Java correctly calls the method on the object
|
149
|
+
end
|
150
|
+
}
|
151
|
+
IO::write(File.join(@destDir, 'model.py'),
|
152
|
+
ERB.new(IO.read(File.join(File.dirname(__FILE__), '../../tmpl/python/entity.erb')),
|
153
|
+
$SAFE, '%<>').result(binding), mode: 'ab')
|
154
|
+
when srcE.kind_of?(DataMetaDom::Mappings)
|
155
|
+
# FIXME -- implement!!!
|
156
|
+
when srcE.kind_of?(DataMetaDom::Enum)
|
157
|
+
IO.write(modelFile, %|#{baseName} = Enum("#{baseName}", "#{srcE.rawVals.join(' ')}")\n\n|, mode: 'ab')
|
158
|
+
# # handled below, bundled in one huge file
|
159
|
+
when srcE.kind_of?(DataMetaDom::BitSet)
|
160
|
+
# FIXME -- implement!!!
|
161
|
+
else
|
162
|
+
raise "Unsupported Entity: #{srcE.inspect}"
|
163
|
+
end
|
164
|
+
}
|
165
|
+
end
|
166
|
+
|
167
|
+
end
|
168
|
+
end
|
@@ -0,0 +1,271 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__)) unless $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
2
|
+
|
3
|
+
%w(fileutils set).each { |r| require r }
|
4
|
+
|
5
|
+
module DataMetaDom
|
6
|
+
|
7
|
+
=begin rdoc
|
8
|
+
Record Attribute such as unique fields set, identity information, indexes, references etc
|
9
|
+
the common structure is like this:
|
10
|
+
keyword (hint1, hint2, hint3...) arg1, arg2
|
11
|
+
|
12
|
+
For command line details either check the new method's source or the README.rdoc file, the usage section.
|
13
|
+
=end
|
14
|
+
class RecAttr
|
15
|
+
|
16
|
+
=begin rdoc
|
17
|
+
The keyword for the attribute.
|
18
|
+
=end
|
19
|
+
attr_reader :keyword
|
20
|
+
|
21
|
+
=begin rdoc
|
22
|
+
A Set of hints, empty set if there are no hints on this attribute.
|
23
|
+
=end
|
24
|
+
attr_reader :hints
|
25
|
+
|
26
|
+
=begin rdoc
|
27
|
+
Arguments on this attribute if any, an array in the order as listed in the DataMeta DOM source. Order is important,
|
28
|
+
for example, for an identity.
|
29
|
+
=end
|
30
|
+
attr_reader :args
|
31
|
+
|
32
|
+
=begin rdoc
|
33
|
+
Unique key for the given attribute to distinguish between those and use in a map. Rebuilt by getKey method
|
34
|
+
defined on the subclasses.
|
35
|
+
=end
|
36
|
+
attr_reader :key
|
37
|
+
|
38
|
+
=begin rdoc
|
39
|
+
Determines if this attribute has the given hint.
|
40
|
+
=end
|
41
|
+
def hasHint?(hint)
|
42
|
+
@hints.member?(hint)
|
43
|
+
end
|
44
|
+
|
45
|
+
=begin rdoc
|
46
|
+
Creates an instance for the given keyword.
|
47
|
+
=end
|
48
|
+
def initialize(keyword)
|
49
|
+
@keyword = keyword.to_sym
|
50
|
+
raise "Unsupported keyword #@keyword" unless REC_ATTR_KEYWORDS.member?(@keyword)
|
51
|
+
@args = []; @hints = Set.new
|
52
|
+
end
|
53
|
+
|
54
|
+
=begin rdoc
|
55
|
+
Adds the given argument, updates the key
|
56
|
+
=end
|
57
|
+
def addArg(val)
|
58
|
+
@args << val.to_sym
|
59
|
+
updateKey
|
60
|
+
self
|
61
|
+
end
|
62
|
+
|
63
|
+
=begin rdoc
|
64
|
+
Adds the given hint.
|
65
|
+
=end
|
66
|
+
def addHint(val); @hints << val end
|
67
|
+
|
68
|
+
=begin rdoc
|
69
|
+
Adds an array of arguments.
|
70
|
+
=end
|
71
|
+
def addArgs(vals); vals.each { |v| addArg v }; self end
|
72
|
+
|
73
|
+
=begin rdoc
|
74
|
+
Adds a collection of hints.
|
75
|
+
=end
|
76
|
+
def addHints(vals); vals.each { |h| addHint h }; self end
|
77
|
+
|
78
|
+
=begin rdoc
|
79
|
+
Updates the key, returns self for call chaining
|
80
|
+
=end
|
81
|
+
def updateKey; @key = getKey; self end
|
82
|
+
|
83
|
+
=begin rdoc
|
84
|
+
Returns the count of arguments.
|
85
|
+
=end
|
86
|
+
def length; @args.length end
|
87
|
+
|
88
|
+
=begin rdoc
|
89
|
+
Returns the arguments in the given position, zero-based.
|
90
|
+
=end
|
91
|
+
def [](index); @args[index] end
|
92
|
+
|
93
|
+
|
94
|
+
=begin rdoc
|
95
|
+
Joins the arguments with the given delimiter.
|
96
|
+
=end
|
97
|
+
def join(delimiter); @args.join(delimiter) end
|
98
|
+
|
99
|
+
=begin rdoc
|
100
|
+
Parses this instance from the given source.
|
101
|
+
* Parameter:
|
102
|
+
* +source+ - an instance of SourceFile
|
103
|
+
=end
|
104
|
+
def parse(source)
|
105
|
+
@sourceRef = source.snapshot
|
106
|
+
line = source.line
|
107
|
+
recAttrMatch = line.scan(/^\s*(\w*)\s*(\([^\)]+\))?\s+(.+)$/)
|
108
|
+
raise "Invalid record attribute spec '#{line}'" unless recAttrMatch
|
109
|
+
keyw, hintList, argList = recAttrMatch[0]
|
110
|
+
raise "Wrong keyword '#{keyw}', '#@keyword' expected instead" unless keyw && keyw.to_sym == @keyword
|
111
|
+
@args = argList.split(/[\(\)\s\,]+/).map { |a| a.to_sym }
|
112
|
+
if hintList
|
113
|
+
@hints = Set.new hintList.split(/[\(\)\s\,]+/).select { |h| !h.strip.empty? }.map { |h| h.strip.to_sym }
|
114
|
+
else
|
115
|
+
@hints = Set.new
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
# textual representation of this instance
|
120
|
+
def to_s; "#@keyword:#@key; #@sourceRef" end
|
121
|
+
|
122
|
+
private :initialize
|
123
|
+
end
|
124
|
+
|
125
|
+
=begin rdoc
|
126
|
+
The record attribute with the unordered set of arguments.
|
127
|
+
See the RecAttrList for the ordered list implementation.
|
128
|
+
=end
|
129
|
+
class RecAttrSet < RecAttr
|
130
|
+
|
131
|
+
# Unordered unique set of the arguments
|
132
|
+
attr_reader :argSet
|
133
|
+
|
134
|
+
# Creates an instance with the given keyword
|
135
|
+
def initialize(keyword); super(keyword); @argSet = Set.new end
|
136
|
+
|
137
|
+
# Engages the super's parse method via the alias
|
138
|
+
alias :recAttrParse :parse
|
139
|
+
|
140
|
+
# Determines if the instance has the given argument
|
141
|
+
def hasArg?(arg); argSet.member?(arg) end
|
142
|
+
|
143
|
+
# Builds textual for the set of the arguments, for diagnostics.
|
144
|
+
def argSetTextual; @argSet.map { |a| a.to_s }.sort.join(':') end
|
145
|
+
|
146
|
+
# Builds the unique key for the set of arguments on the instance
|
147
|
+
def getKey; argSetTextual.to_sym end
|
148
|
+
|
149
|
+
# Adds the given argument to the instance
|
150
|
+
def addArg(val)
|
151
|
+
k = val.to_sym
|
152
|
+
raise "Duplicate arg #{k} in the set of #{argSetTextual}" if @argSet.member?(k)
|
153
|
+
@argSet << k
|
154
|
+
#RecAttr.instance_method(:addArg).bind(self).call k - fortunately, overkill in this case, can do with just:
|
155
|
+
super k
|
156
|
+
end
|
157
|
+
|
158
|
+
=begin rdoc
|
159
|
+
Parses the instance from the given source.
|
160
|
+
* Parameters
|
161
|
+
* +src+ - an instance of SourceFile
|
162
|
+
=end
|
163
|
+
def parse(src)
|
164
|
+
recAttrParse(src)
|
165
|
+
# look if there are any duplicates, if there are it's an error:
|
166
|
+
counterHash = Hash.new(0)
|
167
|
+
args.each { |a| k=a.to_sym; counterHash[k] += 1 }
|
168
|
+
dupes = []; counterHash.each { |k, v| dupes << k if v > 1 }
|
169
|
+
raise "Duplicate arguments for #{self} - [#{dupes.join(',')}]" unless dupes.empty?
|
170
|
+
@argSet = Set.new(args)
|
171
|
+
updateKey
|
172
|
+
self
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
=begin rdoc
|
177
|
+
The record attribute with the ordered list of arguments.
|
178
|
+
See RecAttrSet for the unordered set implementation.
|
179
|
+
=end
|
180
|
+
class RecAttrList < RecAttr
|
181
|
+
|
182
|
+
# Engages the super's parse method via the alias
|
183
|
+
alias :recAttrParse :parse
|
184
|
+
|
185
|
+
# Creates an instance with the given keyword
|
186
|
+
def initialize(keyword); super(keyword) end
|
187
|
+
|
188
|
+
# Builds the unique key for the list of arguments on the instance
|
189
|
+
def getKey; @args.map { |a| a.to_s }.join(':').to_sym end
|
190
|
+
|
191
|
+
=begin rdoc
|
192
|
+
Parses the instance from the given source preserving the order of arguments, returns self for call chaining.
|
193
|
+
* Parameters
|
194
|
+
* +src+ - an instance of SourceFile
|
195
|
+
=end
|
196
|
+
def parse(src)
|
197
|
+
recAttrParse(src)
|
198
|
+
updateKey
|
199
|
+
self
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
=begin rdoc
|
204
|
+
Record attrubute "<tt>unique</tt>"
|
205
|
+
=end
|
206
|
+
class RecUnique < RecAttrSet
|
207
|
+
|
208
|
+
# Creates an instance with the keyword "<tt>unique</tt>"
|
209
|
+
def initialize
|
210
|
+
#noinspection RubyArgCount
|
211
|
+
super(UNIQUE)
|
212
|
+
end
|
213
|
+
|
214
|
+
=begin rdoc
|
215
|
+
Attempts to consume the "<tt>unique</tt>" attribute for the given Record from the given source.
|
216
|
+
* Parameters
|
217
|
+
* +source+ - an instance of SourceFile
|
218
|
+
=end
|
219
|
+
def self.consumed?(source, record)
|
220
|
+
source.line =~ /^#{UNIQUE}\W.+$/ ? record.addUnique(RecUnique.new.parse(source)) : nil
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
=begin rdoc
|
225
|
+
Record attrubute "<tt>identity</tt>"
|
226
|
+
=end
|
227
|
+
class RecIdentity < RecAttrSet
|
228
|
+
|
229
|
+
# Creates an instance with the keyword "<tt>identity</tt>"
|
230
|
+
def initialize
|
231
|
+
#noinspection RubyArgCount
|
232
|
+
super(IDENTITY)
|
233
|
+
end
|
234
|
+
|
235
|
+
=begin rdoc
|
236
|
+
Attempts to consume the "<tt>identity</tt>" attribute for the given Record from the given source.
|
237
|
+
* Parameters
|
238
|
+
* +source+ - an instance of SourceFile
|
239
|
+
=end
|
240
|
+
def self.consumed?(source, record)
|
241
|
+
source.line =~ /^#{IDENTITY}\W+.+$/ ? record.identity = RecIdentity.new.parse(source) : nil
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
=begin rdoc
|
246
|
+
Record attrubute "<tt>index</tt>"
|
247
|
+
=end
|
248
|
+
class RecIndex < RecAttrList
|
249
|
+
|
250
|
+
# Creates an instance with the keyword "<tt>index</tt>"
|
251
|
+
def initialize
|
252
|
+
#noinspection RubyArgCount
|
253
|
+
super(INDEX)
|
254
|
+
end
|
255
|
+
|
256
|
+
=begin rdoc
|
257
|
+
Attempts to consume the "<tt>index</tt>" attribute for the given Record from the given source.
|
258
|
+
* Parameters
|
259
|
+
* +source+ - an instance of SourceFile
|
260
|
+
=end
|
261
|
+
def self.consumed?(source, record)
|
262
|
+
source.line =~ /^#{INDEX}\W+.+$/ ? record.addIndex(RecIndex.new.parse(source)) : nil
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
=begin rdoc
|
267
|
+
An array of record level parse token classes, namely RecIdentity, RecIndex, RecUnique
|
268
|
+
=end
|
269
|
+
RECORD_LEVEL_TOKENS=[RecIdentity, RecIndex, RecUnique]
|
270
|
+
|
271
|
+
end
|