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,68 @@
|
|
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
|
+
All sources including all includes from the master file.
|
9
|
+
|
10
|
+
For command line details either check the new method's source or the README.rdoc file, the usage section.
|
11
|
+
=end
|
12
|
+
class Sources
|
13
|
+
|
14
|
+
=begin rdoc
|
15
|
+
Start parsing from the master file, collect all the files that are included.
|
16
|
+
=end
|
17
|
+
def initialize(masterFile)
|
18
|
+
masterPath = File.dirname(masterFile)
|
19
|
+
@todo = {}; @done = {}
|
20
|
+
libSpec = ENV[DATAMETA_LIB]
|
21
|
+
@paths = libSpec ? libSpec.split(File::PATH_SEPARATOR).map { |e| uniPath(e) } : []
|
22
|
+
@paths.unshift(masterPath).flatten! if masterPath
|
23
|
+
@paths.unshift '.' # start looking in the current directory and then in the rest of the path
|
24
|
+
src = SourceFile.new(masterPath, File.basename(masterFile))
|
25
|
+
@todo[src.key] = src
|
26
|
+
end
|
27
|
+
|
28
|
+
=begin rdoc
|
29
|
+
Returns the set of the keys of the source files alredy parsed.
|
30
|
+
=end
|
31
|
+
def doneKeys; @done.keys end
|
32
|
+
|
33
|
+
=begin rdoc
|
34
|
+
Fetches the instance of SourceFile by its key.
|
35
|
+
=end
|
36
|
+
def [](key); @done[key] end
|
37
|
+
|
38
|
+
# Queue a source file for parsing
|
39
|
+
def queue(name)
|
40
|
+
# need to resolve the name to the path first
|
41
|
+
includeDir = nil
|
42
|
+
@paths.each { |m|
|
43
|
+
fullName = "#{m}#{File::SEPARATOR}#{name}"
|
44
|
+
if File.exist?(fullName)
|
45
|
+
includeDir = m
|
46
|
+
break
|
47
|
+
end
|
48
|
+
}
|
49
|
+
raise "Missing include '#{name}' in the path #{@paths.join(File::PATH_SEPARATOR)}" unless includeDir
|
50
|
+
src = SourceFile.new(includeDir, name)
|
51
|
+
@todo[src.key]=src unless @todo[src.key] || @done[src.key]
|
52
|
+
self
|
53
|
+
end
|
54
|
+
|
55
|
+
=begin rdoc
|
56
|
+
Returns next source file in queue if any, returns +nil+ if no more source files left to parse.
|
57
|
+
=end
|
58
|
+
def next
|
59
|
+
return nil if @todo.empty?
|
60
|
+
@key = nil
|
61
|
+
@todo.each_key { |k| @key = k; break }
|
62
|
+
@val = @todo[@key]
|
63
|
+
@todo.delete @key; @done[@key] = @val
|
64
|
+
@val
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
@@ -0,0 +1,279 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__)) unless $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
2
|
+
|
3
|
+
require 'set'
|
4
|
+
require 'logger'
|
5
|
+
|
6
|
+
module DataMetaDom
|
7
|
+
|
8
|
+
# Logger set to WARN, daily rollover and max size 10M
|
9
|
+
# Feel free to change any of it.
|
10
|
+
L = Logger.new('dataMetaDom.log', 'daily', 10*1024*1024)
|
11
|
+
L.level = Logger::WARN
|
12
|
+
|
13
|
+
# Keyword: namespace
|
14
|
+
NAMESPACE = :namespace
|
15
|
+
|
16
|
+
# Keyword: include
|
17
|
+
INCLUDE = :include
|
18
|
+
|
19
|
+
# Keyword, data type: string with fixed length
|
20
|
+
CHAR = :char
|
21
|
+
|
22
|
+
# Keyword, data type: string with variable length
|
23
|
+
STRING = :string
|
24
|
+
|
25
|
+
# Keyword, data type: integer
|
26
|
+
INT = :int
|
27
|
+
|
28
|
+
# Keyword, data type: float (real numbers)
|
29
|
+
FLOAT = :float
|
30
|
+
|
31
|
+
# Keyword, data type: boolean
|
32
|
+
BOOL = :bool
|
33
|
+
|
34
|
+
# Keyword, data type: bitset
|
35
|
+
BITSET = :bitset
|
36
|
+
|
37
|
+
# Keyword, data type: URL
|
38
|
+
URL = :url
|
39
|
+
|
40
|
+
# Keyword, data type: map
|
41
|
+
MAPPING = :mapping
|
42
|
+
|
43
|
+
# Keyword, data type: datetime
|
44
|
+
DATETIME = :datetime
|
45
|
+
|
46
|
+
# Keyword, data type: numeric
|
47
|
+
NUMERIC = :numeric
|
48
|
+
|
49
|
+
# Keyword, identity
|
50
|
+
IDENTITY = :identity
|
51
|
+
|
52
|
+
# Keyword, unique
|
53
|
+
UNIQUE = :unique
|
54
|
+
|
55
|
+
# Keyword, matches
|
56
|
+
MATCHES = :matches
|
57
|
+
|
58
|
+
# Keyword, index
|
59
|
+
INDEX = :index
|
60
|
+
|
61
|
+
# Key to a no-namespace
|
62
|
+
NO_NAMESPACE = ''.to_sym
|
63
|
+
|
64
|
+
# Wiki for DataMeta DOM
|
65
|
+
WIKI = 'https://github.com/eBayDataMeta/DataMeta'
|
66
|
+
|
67
|
+
# HTML tag referencing the WIKI
|
68
|
+
WIKI_REF_HTML = "<a href='#{WIKI}'>DataMeta</a>"
|
69
|
+
|
70
|
+
# Keyword, data type, the RAW type refers to raw data, like a byte array
|
71
|
+
RAW = :raw
|
72
|
+
|
73
|
+
# the reference keyword was not a good idea, too much confusion and dupe functionality
|
74
|
+
# with the better way, namely referencing an object by name
|
75
|
+
#REFERENCE=:reference
|
76
|
+
|
77
|
+
=begin rdoc
|
78
|
+
Keyword +doc+, documentation.
|
79
|
+
=end
|
80
|
+
DOC = :doc
|
81
|
+
|
82
|
+
=begin rdoc
|
83
|
+
Keyword +ver+, version info.
|
84
|
+
=end
|
85
|
+
VER_KW = :ver
|
86
|
+
|
87
|
+
# Keyword, data type, enum
|
88
|
+
ENUM = :enum
|
89
|
+
|
90
|
+
# Keyword, end
|
91
|
+
END_KW = :end
|
92
|
+
|
93
|
+
# Keyword, record
|
94
|
+
RECORD = :record
|
95
|
+
|
96
|
+
# Package separator in a namespace.
|
97
|
+
PACK_SEPARATOR = '.'
|
98
|
+
|
99
|
+
# Environment variable for DataMeta DOM library path.
|
100
|
+
DATAMETA_LIB = 'DATAMETA_LIB'
|
101
|
+
|
102
|
+
# for source code generation, 2 spaces
|
103
|
+
SOURCE_INDENT = ' ' * 2
|
104
|
+
|
105
|
+
# Prefix for a required field
|
106
|
+
REQUIRED_PFX = '+'.to_sym
|
107
|
+
|
108
|
+
# Prefix for an optional field
|
109
|
+
OPTIONAL_PFX = '-'.to_sym
|
110
|
+
|
111
|
+
# DataMeta DOM standard types that have a dimension with a scale
|
112
|
+
SCALE_TYPES = Set.new [NUMERIC]
|
113
|
+
|
114
|
+
=begin rdoc
|
115
|
+
Data Types that must be dimensioned, with a length or with a length and a scale, as
|
116
|
+
it includes all the SCALE_TYPES too..
|
117
|
+
=end
|
118
|
+
DIMMED_TYPES = Set.new ([FLOAT, INT, CHAR, RAW] << SCALE_TYPES.to_a).flatten
|
119
|
+
|
120
|
+
# Optionally dimmable types - may have a dim or may have not
|
121
|
+
OPT_DIMMABLE = Set.new ([STRING]).flatten
|
122
|
+
|
123
|
+
# standard types is a superset of dimmensionable types, adding BOOL and DATETIME
|
124
|
+
STANDARD_TYPES = Set.new(([BOOL, DATETIME, URL] << DIMMED_TYPES.to_a << OPT_DIMMABLE.to_a << SCALE_TYPES.to_a).flatten)
|
125
|
+
|
126
|
+
=begin rdoc
|
127
|
+
Record attribute keywords:
|
128
|
+
* +identity+
|
129
|
+
* +unique+
|
130
|
+
* +index+
|
131
|
+
=end
|
132
|
+
REC_ATTR_KEYWORDS = Set.new [IDENTITY, UNIQUE, INDEX]
|
133
|
+
# Valid first symbol of a DataMeta DOM idenifier such as entity or enum name, field name, enum item.
|
134
|
+
ID_START = '[A-Za-z_]'
|
135
|
+
|
136
|
+
# Valid first symbol for a DataMeta DOM Type
|
137
|
+
TYPE_START = '[A-Z]'
|
138
|
+
|
139
|
+
puts "Standard types: #{STANDARD_TYPES.to_a.map { |k| k.to_s }.sort.join(', ')}" if $DEBUG
|
140
|
+
|
141
|
+
# Migration context - for a record
|
142
|
+
class MigrCtx
|
143
|
+
attr_accessor :rec, :canAuto, :isSkipped
|
144
|
+
def initialize(name)
|
145
|
+
@rec = name
|
146
|
+
@canAuto = true # assume we can automigrate unless encounter problems that would require a HIT
|
147
|
+
@isSkipped = false
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
=begin rdoc
|
152
|
+
Suffix for the java source files for the implementors of the DataMetaSame interface by all the fields on the class.
|
153
|
+
=end
|
154
|
+
SAME_FULL_SFX = '_DmSameFull'
|
155
|
+
|
156
|
+
=begin rdoc
|
157
|
+
Suffix for the java source files for the implementors of the DataMetaSame interface by identity field(s) on the class.
|
158
|
+
=end
|
159
|
+
SAME_ID_SFX = '_DmSameId'
|
160
|
+
|
161
|
+
# +DataMetaSame+ generation style: Full Compare, compare by all the fields defined in the class
|
162
|
+
FULL_COMPARE = :full
|
163
|
+
|
164
|
+
=begin rdoc
|
165
|
+
+DataMetaSame+ generation style: Compare by the identity fields only as defined on DataMetaDom::Record
|
166
|
+
=end
|
167
|
+
ID_ONLY_COMPARE = :id
|
168
|
+
|
169
|
+
# One indent step for java classes, spaces.
|
170
|
+
INDENT = ' ' * 4
|
171
|
+
|
172
|
+
# keep in sync with generated classes such as the Java class `CannedRegexUtil` in DataMeta DOM Core/Java etc.
|
173
|
+
CANNED_RX = Set.new [:email, :phone]
|
174
|
+
|
175
|
+
# holds a custom regex symbol and the variables that use this regex
|
176
|
+
class RegExEntry
|
177
|
+
attr_reader :r, :vars, :req
|
178
|
+
# initializes interna variables
|
179
|
+
def initialize(regex, var, req)
|
180
|
+
@r = regex
|
181
|
+
@vars = Set.new [var]
|
182
|
+
@req = req
|
183
|
+
end
|
184
|
+
# adds the variable to the instance
|
185
|
+
def <<(var)
|
186
|
+
@vars << var
|
187
|
+
end
|
188
|
+
|
189
|
+
def req?; @req end
|
190
|
+
end
|
191
|
+
|
192
|
+
# Registry for the regexes so we don't repeat those
|
193
|
+
class RegExRoster
|
194
|
+
attr_reader :i_to_r, :r_to_i, :canned
|
195
|
+
|
196
|
+
class << self
|
197
|
+
# Converts the given custom RegEx index to the matching Pattern static final variable name
|
198
|
+
def ixToVarName(index)
|
199
|
+
"REGEX___#{index}___"
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
# sets index to 0, initializes hashes
|
204
|
+
def initialize
|
205
|
+
@index = 0
|
206
|
+
@i_to_r = {}
|
207
|
+
@r_to_i = {}
|
208
|
+
@canned = {}
|
209
|
+
end
|
210
|
+
|
211
|
+
# adds a new regex to the registry
|
212
|
+
def register(f)
|
213
|
+
var = f.name
|
214
|
+
rx = f.regex
|
215
|
+
rx = rx[1..-2] if rx.length > 2 && rx.start_with?('/') && rx.end_with?('/')
|
216
|
+
k = rx.to_sym
|
217
|
+
if CANNED_RX.member?(k)
|
218
|
+
if @canned.has_key?(k)
|
219
|
+
@canned[k] << var
|
220
|
+
else
|
221
|
+
@canned[k] = RegExEntry.new(k, var, f.isRequired)
|
222
|
+
end
|
223
|
+
elsif @r_to_i.has_key?(k)
|
224
|
+
# this regex is already registered, just add the variable
|
225
|
+
@i_to_r[@index] << var
|
226
|
+
else
|
227
|
+
@index += 1
|
228
|
+
@i_to_r[@index] = RegExEntry.new(k, var, f.isRequired)
|
229
|
+
@r_to_i[k] = @index
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
end
|
234
|
+
|
235
|
+
=begin rdoc
|
236
|
+
With the given full type including the namespace if any and the given namespace (java package, python package etc),
|
237
|
+
figures out whether the full type has to be reference in full, if it belongs to a different namespace,
|
238
|
+
or just by the base name if it belongs to the same package.
|
239
|
+
|
240
|
+
* Parameters
|
241
|
+
* +fullType+ - full data type including the namespace if any
|
242
|
+
* +namespace+ - reference namespace.
|
243
|
+
|
244
|
+
For example, passed:
|
245
|
+
"com.acme.proj.Klass", "com.acme.lib"
|
246
|
+
will return
|
247
|
+
"com.acme.proj.Klass"
|
248
|
+
|
249
|
+
but when passed:
|
250
|
+
"com.acme.proj.Klass", "com.acme.proj"
|
251
|
+
will return
|
252
|
+
"Klass"
|
253
|
+
|
254
|
+
This is to avoid excessive verbosity when referencing entities in the same package.
|
255
|
+
=end
|
256
|
+
def condenseType(fullType, ref_namespace)
|
257
|
+
ns, base = DataMetaDom.splitNameSpace(fullType)
|
258
|
+
# noinspection RubyNestedTernaryOperatorsInspection
|
259
|
+
DataMetaDom.validNs?(ns, base) ? ( ns == ref_namespace ? base : fullType) : fullType
|
260
|
+
end
|
261
|
+
|
262
|
+
# Migrator implementor name
|
263
|
+
def migrClass(base, ver1, ver2); "Migrate_#{base}_v#{ver1.toVarName}_to_v#{ver2.toVarName}" end
|
264
|
+
|
265
|
+
=begin rdoc
|
266
|
+
Builds and returns the Java-style getter name for the given field. This style is used in other platforms such as
|
267
|
+
Python, for consistency.
|
268
|
+
=end
|
269
|
+
def getterName(f); "get#{DataMetaXtra::Str.capFirst(f.name.to_s)}" end
|
270
|
+
|
271
|
+
=begin rdoc
|
272
|
+
Builds and returns the Java-setter setter name for the given field. This style is used in other platforms such as
|
273
|
+
Python, for consistency.
|
274
|
+
=end
|
275
|
+
def setterName(f); "set#{DataMetaXtra::Str.capFirst(f.name.to_s)}" end
|
276
|
+
|
277
|
+
module_function :setterName, :getterName
|
278
|
+
|
279
|
+
end
|
@@ -0,0 +1,244 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__)) unless $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
2
|
+
|
3
|
+
require 'fileutils'
|
4
|
+
require 'typesafe_enum'
|
5
|
+
require 'set'
|
6
|
+
require 'dataMetaDom/docs'
|
7
|
+
|
8
|
+
module DataMetaDom
|
9
|
+
|
10
|
+
=begin
|
11
|
+
Semantic Version implementation.
|
12
|
+
|
13
|
+
See {http://semver.org this page} for details
|
14
|
+
=end
|
15
|
+
class SemVer
|
16
|
+
attr_reader :source, :semanticPartsOnly, :items
|
17
|
+
# Version difference levels as an enum
|
18
|
+
class DiffLevel < TypesafeEnum::Base
|
19
|
+
new :NONE # Versions are equal
|
20
|
+
new :MAJOR # Difference in the Major part
|
21
|
+
new :MINOR # Difference in the Minor part
|
22
|
+
new :UPDATE # Difference in the Update (Patch) part
|
23
|
+
new :BUILD # Difference in the Build part
|
24
|
+
end
|
25
|
+
|
26
|
+
# Split by dots pattern
|
27
|
+
DOTS_SPLIT = %r<\.>
|
28
|
+
# Any Integral part of the Version - just digits
|
29
|
+
DIGITS = %r<^[0-9]+$>
|
30
|
+
|
31
|
+
# Major part index in the @items array
|
32
|
+
MAJOR_INDEX = 0
|
33
|
+
# Minor part index in the @items array
|
34
|
+
MINOR_INDEX = MAJOR_INDEX + 1
|
35
|
+
# Update part index in the @items array
|
36
|
+
UPDATE_INDEX = MINOR_INDEX + 1
|
37
|
+
# Build part index in the @items array
|
38
|
+
BUILD_INDEX = UPDATE_INDEX + 1
|
39
|
+
# Minimal size of the @items array
|
40
|
+
ITEMS_MIN_SIZE = UPDATE_INDEX + 1
|
41
|
+
# Max size of the @items array
|
42
|
+
ITEMS_MAX_SIZE = BUILD_INDEX + 1
|
43
|
+
|
44
|
+
# Equality for the saucer operator <=>
|
45
|
+
EQ = 0
|
46
|
+
# Strictly "greater than" for the saucer operator <=>
|
47
|
+
GT = 1
|
48
|
+
# Strictly "lesser than" for the saucer operator <=>
|
49
|
+
LT = -1
|
50
|
+
|
51
|
+
# Parsing constructor
|
52
|
+
def initialize(src)
|
53
|
+
raise ArgumentError, "Attempted to create an instance of #{self.class.name} from a nil" if src.nil?
|
54
|
+
@source = src
|
55
|
+
# put everything in an array -- this provides free eql? and hash() methods
|
56
|
+
@items = []
|
57
|
+
src.split(DOTS_SPLIT).each { |i|
|
58
|
+
if i =~ DIGITS
|
59
|
+
@items << i.to_i
|
60
|
+
else
|
61
|
+
break
|
62
|
+
end
|
63
|
+
}
|
64
|
+
raise ArgumentError, %<Invalid semantic version format: #{src}> if items.size < ITEMS_MIN_SIZE ||
|
65
|
+
items.size > ITEMS_MAX_SIZE
|
66
|
+
|
67
|
+
raise ArgumentError,
|
68
|
+
%<Invalid semantic version format: "#{src}": build version can not be zero.> unless build.nil? ||
|
69
|
+
build != 0
|
70
|
+
|
71
|
+
@semanticPartsOnly = @items.map{|i| i.to_s}.join('.')
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
# Major part of the version
|
76
|
+
def major; @items[MAJOR_INDEX] end
|
77
|
+
|
78
|
+
# Minor part of the version
|
79
|
+
def minor; @items[MINOR_INDEX] end
|
80
|
+
|
81
|
+
# Update part of the version
|
82
|
+
def update; @items[UPDATE_INDEX] end
|
83
|
+
|
84
|
+
# Build part of the version or nil
|
85
|
+
def build; items.size > BUILD_INDEX ? @items[BUILD_INDEX] : nil end
|
86
|
+
|
87
|
+
# Difference Level, computes one of the DiffLevel values
|
88
|
+
def diffLevel(other)
|
89
|
+
return DiffLevel::MAJOR if major != other.major
|
90
|
+
return DiffLevel::MINOR if minor != other.minor
|
91
|
+
return DiffLevel::UPDATE if update != other.update
|
92
|
+
if (
|
93
|
+
!build.nil? && !other.build.nil? && (build <=> other.build) != EQ
|
94
|
+
) || (
|
95
|
+
build.nil? && !other.build.nil?
|
96
|
+
) || ( !build.nil? && other.build.nil? )
|
97
|
+
return DiffLevel::BUILD
|
98
|
+
end
|
99
|
+
DiffLevel::NONE
|
100
|
+
end
|
101
|
+
|
102
|
+
# Override the eql? for the == operator to work
|
103
|
+
def eql?(o); @items == o.items end
|
104
|
+
|
105
|
+
# Override the hash() method for the sets and maps to work
|
106
|
+
def hash; @items.hash end
|
107
|
+
|
108
|
+
# The Saucer Operator, Ruby equivalent of Java's compareTo(...)
|
109
|
+
def <=>(o)
|
110
|
+
raise ArgumentError, %<Attempt to compare #{self.class.name} "#{self}" to a nil> if o.nil?
|
111
|
+
|
112
|
+
0.upto(UPDATE_INDEX) { |x|
|
113
|
+
cmp = items[x] <=> o.items[x]
|
114
|
+
return cmp unless cmp == EQ # not equal: end of the story, that's the comparison result
|
115
|
+
}
|
116
|
+
# if we are here, the Minor, Major and the Update are equal. See what's up with the build if any:
|
117
|
+
|
118
|
+
# this object is newer (version bigger) because it has a build number but the other does not
|
119
|
+
return GT if items.size > o.items.size
|
120
|
+
|
121
|
+
# this object is older (version lesser) because it does not have a build number but the other does
|
122
|
+
return LT if items.size < o.items.size
|
123
|
+
|
124
|
+
# We got build part in self and the other, return the build part comparison:
|
125
|
+
build <=> o.build
|
126
|
+
end
|
127
|
+
|
128
|
+
# For a simple string representation, just show the source
|
129
|
+
def to_s; @source end
|
130
|
+
|
131
|
+
# Long string for debugging and detailed logging - shows semantic parts as parsed
|
132
|
+
def to_long_s; "#{self.class.name}{#{@source}(#{@semanticPartsOnly})}" end
|
133
|
+
|
134
|
+
=begin
|
135
|
+
Consistently and reproducibly convert the version specs to the text suitable for making it a part of a class name or a
|
136
|
+
variable name
|
137
|
+
=end
|
138
|
+
def toVarName; @items.join('_') end
|
139
|
+
|
140
|
+
# Overload the equals operator
|
141
|
+
def ==(other); self.<=>(other) == EQ end
|
142
|
+
# Overload the greater-than operator
|
143
|
+
def >(other); self.<=>(other) == GT end
|
144
|
+
# Overload the lesser-than operator
|
145
|
+
def <(other); self.<=>(other) == LT end
|
146
|
+
# Overload the greater-than-or-equals operator
|
147
|
+
def >=(other); self.<=>(other) >= EQ end
|
148
|
+
# Overload the lesser-than-or-equals operator
|
149
|
+
def <=(other); self.<=>(other) <= EQ end
|
150
|
+
|
151
|
+
class << self
|
152
|
+
# Builds an instance of SemVer from the given specs whatever they are
|
153
|
+
def fromSpecs(specs)
|
154
|
+
case specs
|
155
|
+
when SemVer
|
156
|
+
specs
|
157
|
+
when String
|
158
|
+
SemVer.new(specs)
|
159
|
+
else
|
160
|
+
raise ArgumentError, %<Unsupported SemVer specs type #{specs}==#{specs.inspect}>
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
=begin rdoc
|
167
|
+
Version info.
|
168
|
+
=end
|
169
|
+
class Ver
|
170
|
+
=begin rdoc
|
171
|
+
Full version info.
|
172
|
+
=end
|
173
|
+
attr_accessor :full
|
174
|
+
|
175
|
+
=begin rdoc
|
176
|
+
Creates an instance with the given full version.
|
177
|
+
=end
|
178
|
+
def initialize(specs)
|
179
|
+
@full = if specs.kind_of?(Integer)
|
180
|
+
raise ArgumentError,
|
181
|
+
%|Invalid version specs: "#{specs
|
182
|
+
}"; a version must be of a valid Semantic format|
|
183
|
+
else
|
184
|
+
SemVer.fromSpecs(specs)
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
class << self
|
189
|
+
# Reversions all the files in the given paths recursively
|
190
|
+
def reVersion(path, namespace, globs, srcVer, trgVer)
|
191
|
+
vPat = srcVer ? srcVer.toVarName : '\d+_\d+_\d+'
|
192
|
+
globs.each { |g|
|
193
|
+
Dir.glob("#{path}/#{g}").each { |f|
|
194
|
+
origLines = IO.read(f).split("\n")
|
195
|
+
newLines = []
|
196
|
+
origLines.each { |line|
|
197
|
+
newLines << (line.end_with?('KEEP') ? line :
|
198
|
+
line.gsub(%r~#{namespace.gsub(/\./, '\.')}\.v#{vPat}~, "#{namespace}.v#{trgVer.toVarName}"))
|
199
|
+
}
|
200
|
+
IO.write(f, newLines.join("\n"), mode: 'wb')
|
201
|
+
}
|
202
|
+
}
|
203
|
+
Dir.entries(path).select{|e| File.directory?(File.join(path, e))}.reject{|e| e.start_with?('.')}.each {|d|
|
204
|
+
reVersion File.join(path, d), namespace, globs, srcVer, trgVer
|
205
|
+
}
|
206
|
+
|
207
|
+
end
|
208
|
+
|
209
|
+
end
|
210
|
+
|
211
|
+
=begin rdoc
|
212
|
+
Textual presentation for the instance.
|
213
|
+
=end
|
214
|
+
def to_s; "ver #{full}" end
|
215
|
+
end
|
216
|
+
|
217
|
+
=begin rdoc
|
218
|
+
Anything having a version. It must be also documentable, but not everything documentable is also versionable,
|
219
|
+
like, for example, Record Field or an Enum part.
|
220
|
+
=end
|
221
|
+
class VerDoccable < Documentable
|
222
|
+
=begin rdoc
|
223
|
+
The version info, an instance of Ver.
|
224
|
+
=end
|
225
|
+
attr_accessor :ver
|
226
|
+
=begin rdoc
|
227
|
+
Resets stateful information on the entity level, like docs that should not apply to the next entity if missing.
|
228
|
+
=end
|
229
|
+
def resetEntity
|
230
|
+
docs.clear
|
231
|
+
end
|
232
|
+
|
233
|
+
=begin rdoc
|
234
|
+
Attempts to parse an instance of Ver from the current line on the given instance of SourceFile.
|
235
|
+
Returns the instance of Ver if successful, nil otherwise.
|
236
|
+
Parameter:
|
237
|
+
* +src+ - the instance of SourceFile to parse the version info from.
|
238
|
+
=end
|
239
|
+
def self.verConsumed?(src)
|
240
|
+
src.line =~ /^\s*#{VER_KW}\s+(\S+)\s*$/ ? Ver.new($1) : nil
|
241
|
+
end
|
242
|
+
|
243
|
+
end
|
244
|
+
end
|