dataMetaDom 1.0.6 → 1.0.7

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 00be7044d2c3f2305152056593942faaa2d606ce
4
- data.tar.gz: d067f88807ff480fa15e59a0a5139ecc08bd2721
3
+ metadata.gz: 4d2947d7b7e267278ef3748b7ec57d59d3db9cff
4
+ data.tar.gz: d9b8bb9b537bf46cfd9e352bc83a285a7051f814
5
5
  SHA512:
6
- metadata.gz: 8389e73f9f9915da9c584e0c989b238e926ef3c7a206c302ab7bbfc6b45f507f2363356ba1c8285ccd6d21730e4245ba2a34e4ad7d7ab22b3f31f57376c38793
7
- data.tar.gz: cfe41be39d9a0e0a04970c7cde983d60cc2a960c44bdce55fbeb4cfa4c15ff3dbef161951ffa0229e2a150cc17db04338a35981f1c7c5e2c66e1145cdd815733
6
+ metadata.gz: 5a0b28437e94ef6e5ddca7c1226d689faa909d091f1431c180e4302fc710ffec7ba16a21e88ed448cf09fe2eaa8b9780c67dbd7db216477432f48ab603abae44
7
+ data.tar.gz: 99e2c46dea64d691d7595dd035003d952d86d037330d79f180a3244efa759b08fae97da41ad901230c1e6c5c41e6f37480246dba98d92e248410eb87393d1bad
data/History.md CHANGED
@@ -1,6 +1,10 @@
1
1
 
2
2
  # `dataMetaDom` Release history:
3
3
 
4
+ ## `1.0.7` - `2017-04-17 Mon` by [`mub`](https://github.com/mub)
5
+ * New feature
6
+ * Scala Case Classes generation
7
+
4
8
  ## `1.0.6` - `2017-04-03 Mon` by [`mub`](https://github.com/mub)
5
9
  * Bug fix:
6
10
  * Pojo Generator had autoversion parameter misspelled.
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env ruby
2
+ # this script generates Java POJOs and the SQL DDL for the DataMeta DOM model
3
+ # Sample:
4
+ # mkdir ../../../../../target/pojo
5
+ # dataMetaPojo.rb ../../../dataMeta/showCase.dmDom ../../../../../target/pojo
6
+
7
+ %w(dataMetaDom dataMetaDom/scala dataMetaDom/help).each(&method(:require))
8
+
9
+ include DataMetaDom, DataMetaDom::ScalaLexer
10
+
11
+ @source, @target = ARGV
12
+ DataMetaDom::helpScalaGen __FILE__ unless @source && @target
13
+ DataMetaDom::helpScalaGen(__FILE__, "DataMeta DOM source #{@source} is not a file") unless File.file?(@source)
14
+ DataMetaDom::helpScalaGen(__FILE__, "Case Classes destination directory #{@target} is not a dir") unless File.directory?(@target)
15
+
16
+ @parser = Model.new
17
+ begin
18
+ @parser.parse(@source, options={autoVerNs: true})
19
+ genCaseClasses(@parser, @target)
20
+ puts "Scala Case Classes written to #{@target}. Done."
21
+ rescue Exception => e
22
+ $stderr.puts "ERROR #{e.message}; #{@parser.diagn}"
23
+ $stderr.puts e.backtrace.inspect
24
+ exit 1
25
+ end
data/lib/dataMetaDom.rb CHANGED
@@ -25,7 +25,7 @@ For command line details either check the new method's source or the README.rdoc
25
25
  module DataMetaDom
26
26
 
27
27
  # Current version
28
- VERSION = '1.0.6'
28
+ VERSION = '1.0.7'
29
29
 
30
30
  =begin rdoc
31
31
  Quick and dirty turning a Windows path into a path of the platform on which this script is running.
@@ -28,6 +28,11 @@ def helpPojoGen(file, errorText=nil)
28
28
  help(file, 'POJO generator', '<DataMeta DOM source> <target directory>', errorText)
29
29
  end
30
30
 
31
+ # Shortcut to help for the Pojo Generator.
32
+ def helpScalaGen(file, errorText=nil)
33
+ help(file, 'Scala generator', '<DataMeta DOM source> <target directory>', errorText)
34
+ end
35
+
31
36
  # Shortcut to help for the MySQL DDL Generator.
32
37
  def helpMySqlDdl(file, errorText=nil)
33
38
  help(file, 'MySQL DDL generator', '<DataMeta DOM source> <target directory>', errorText)
@@ -38,5 +43,5 @@ def helpOracleDdl(file, errorText=nil)
38
43
  help(file, 'Oracle DDL generator', '<DataMeta DOM source> <target directory>', errorText)
39
44
  end
40
45
 
41
- module_function :help, :helpPojoGen, :helpMySqlDdl
46
+ module_function :help, :helpPojoGen, :helpMySqlDdl, :helpScalaGen
42
47
  end
@@ -347,7 +347,7 @@ import org.ebay.datameta.dom.VerificationException;
347
347
  import org.ebay.datameta.util.jdk.SemanticVersion;
348
348
  import static org.ebay.datameta.dom.CannedRegexUtil.getCannedRegEx;
349
349
 
350
- #{PojoLexer.classJavaDoc record.docs}public class #{baseName} implements Verifiable {
350
+ #{PojoLexer.classJavaDoc(record.docs)}public class #{baseName} implements Verifiable {
351
351
 
352
352
  ENTITY_CLASS_HEADER
353
353
  if record.ver
@@ -0,0 +1,287 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
2
+
3
+ require 'dataMetaDom/help'
4
+ require 'dataMetaDom/field'
5
+ require 'dataMetaDom/util'
6
+
7
+ module DataMetaDom
8
+
9
+ =begin rdoc
10
+ Definition for generating Scala artifacts such as case classes and everything related that depends on Scala distro only
11
+ witout any other dependencies.
12
+
13
+ For command line details either check the new method's source or the README.rdoc file, the usage section.
14
+ =end
15
+ module ScalaLexer
16
+ include DataMetaDom
17
+
18
+ =begin rdoc
19
+ Scala Data Meta export subpackage, to distinguish between other platforms. Can not make it just ".scala" because if
20
+ someone imports all the data model classes with underscore, they may pick up the "scala" subpackage too.
21
+
22
+ In which case, they will have trouble importing anything from the Scala core by "scala.*",
23
+ that violates the principle of "Least Astonishment", they may dink around till they find out that
24
+ they will have to use the _root_ package to access the Scala core's "scala", not the exported DataMeta "scala".
25
+ =end
26
+ SCALA_SUBPACKAGE = 'scadm'
27
+
28
+ =begin rdoc
29
+ Renderer for the String type.
30
+ =end
31
+ TEXTUAL_TYPER = lambda{|t| 'String'}
32
+ =begin rdoc
33
+ Maps DataMeta DOM datatypes to the matching Scala classes, for those that need to be imported.
34
+ The Scala source generator will import these if they are used in the class.
35
+ =end
36
+ SCALA_IMPORTS = {
37
+ DATETIME => 'java.time.ZonedDateTime',
38
+ NUMERIC => 'scala.math.BigDecimal'
39
+ }
40
+
41
+ =begin rdoc
42
+ DataMeta DOM aggregated field type spec mapped to matching Scala Case class:
43
+ =end
44
+ AGGR_CLASSES = {
45
+ Field::SET => 'scala.collection.Seq', # for Case classes, if the identity is different that full set of fields, Set makes no sense
46
+ # which is a majority of the cases. Wait for full implementation and switch to scala.collection.mutable.Set
47
+ Field::LIST => 'scala.collection.immutable.List',
48
+ Field::DEQUE => 'scala.collection.Seq',
49
+ }
50
+
51
+ =begin rdoc
52
+ A map from DataMeta DOM standard types to the lambdas that render correspondent Scala types per Scala syntax.
53
+
54
+ We used to render the primitives for the required types but the Verifiable interface made it impractical.
55
+ =end
56
+ SCALA_TYPES = {
57
+ DataMetaDom::INT => lambda{ |t|
58
+ len = t.length
59
+ case
60
+ when len <= 4; 'Int'
61
+ when len <=8; 'Long'
62
+ else; raise "Invalid integer length #{len}"
63
+ end
64
+ },
65
+ STRING => TEXTUAL_TYPER,
66
+ DATETIME => lambda{|t| 'ZonedDateTime'},
67
+ BOOL => lambda{|t| 'Boolean'}, # req ? 'boolean' : 'Boolean'},
68
+ CHAR => TEXTUAL_TYPER,
69
+ FLOAT => lambda{|t|
70
+ len = t.length
71
+ case
72
+ when len <= 4; 'Float' # req ? 'float' : 'Float'
73
+ when len <=8; 'Double' #req ? 'double' : 'Double'
74
+ else; raise "Invalid float length #{len}"
75
+ end
76
+ },
77
+ RAW => lambda{|t| 'Array[Byte]'},
78
+ URL => lambda{|t| URL_CLASS},
79
+ NUMERIC => lambda{|t| 'BigDecimal'}
80
+ }
81
+
82
+ =begin rdoc
83
+ Maximum size of a Mapping (Map), rather aribtrary choice, not backed by any big idea.
84
+ =end
85
+ MAX_MAPPING_SIZE = 10000
86
+
87
+ class << self
88
+ =begin rdoc
89
+ Figures out type adjusted for aggregates and maps.
90
+ =end
91
+ def aggrType(aggr, trg, rawType, scalaPackage)
92
+ if aggr
93
+ k = rawType.to_sym
94
+ subType = rawType # PRIMS_TO_WRAP.has_key?(k) ? PRIMS_TO_WRAP[k] :
95
+ "#{aggr}[#{subType}]"
96
+ elsif trg
97
+ k = rawType.to_sym
98
+ srcType = rawType
99
+ typeRenderer = SCALA_TYPES[trg.type]
100
+ rawTrg = typeRenderer ? typeRenderer.call(trg) : self.condenseType(self.scalaNs(trg.type), scalaPackage)
101
+ k = rawTrg.to_sym
102
+ trgType = rawTrg
103
+ "Map[#{srcType}, #{trgType}]"
104
+ else
105
+ rawType
106
+ end
107
+ end
108
+
109
+ end
110
+
111
+ def self.condenseType(fullType, ref_namespace)
112
+ ns, base = DataMetaDom.splitNameSpace(fullType)
113
+ ns = self.scalaNs(ns)
114
+ # noinspection RubyNestedTernaryOperatorsInspection
115
+ DataMetaDom.validNs?(ns, base) ? ( ns == ref_namespace ? base : fullType) : fullType
116
+ end
117
+
118
+ # Unaggregated Scala type
119
+ def self.unaggrScalaType(dt, scalaPackage)
120
+ typeRenderer = SCALA_TYPES[dt.type]
121
+ typeRenderer ? typeRenderer.call(dt) : self.condenseType(self.scalaNs(dt.type), scalaPackage)
122
+ end
123
+
124
+ # aggregated Scala type
125
+ def self.aggrScalaType(f, scalaPackage)
126
+ rawType = self.unaggrScalaType(f.dataType, scalaPackage)
127
+ aggr = f.aggr? ? DataMetaDom.splitNameSpace(AGGR_CLASSES[f.aggr])[1] : nil
128
+ ScalaLexer.aggrType(aggr, f.trgType, rawType, scalaPackage)
129
+ end
130
+ =begin rdoc
131
+ Given the property +docs+ of Documentable, return the SCALA_DOC_TARGET if it is present,
132
+ PLAIN_DOC_TARGET otherwise. Returns empty string if the argument is nil.
133
+ =end
134
+ def scalaDocs(docs)
135
+ return '' unless docs
136
+ case
137
+ when docs[PLAIN_DOC_TARGET]
138
+ docs[PLAIN_DOC_TARGET].text
139
+ else
140
+ ''
141
+ end
142
+ end
143
+ =begin rdoc
144
+ Scala Class ScalaDoc text with the Wiki reference.
145
+ =end
146
+ def classScalaDoc(docs)
147
+ return <<CLASS_SCALADOC
148
+ /**
149
+ #{ScalaLexer.scalaDocs(docs)}
150
+ */
151
+ CLASS_SCALADOC
152
+ end
153
+
154
+ =begin rdoc
155
+ Scala Enum class-level ScalaDoc text with the Wiki reference.
156
+ =end
157
+ def enumScalaDoc(docs)
158
+ return <<ENUM_SCALADOC
159
+ /**
160
+ #{ScalaLexer.scalaDocs(docs)}
161
+ */
162
+ ENUM_SCALADOC
163
+ end
164
+
165
+ =begin rdoc
166
+ For the given DataMeta DOM data type and the isRequired flag, builds and returns the matching Scala data type declaration.
167
+ For standard types, uses the SCALA_TYPES map
168
+ =end
169
+ def getScalaType(dmDomType)
170
+ typeRenderer = SCALA_TYPES[dmDomType.type]
171
+ typeRenderer ? typeRenderer.call(dmDomType) : dmDomType.type
172
+ end
173
+
174
+ =begin rdoc
175
+ Generates Scala source code, the Scala class for a DataMeta DOM Record
176
+
177
+ Parameters:
178
+ * +model+ - the source model to export from
179
+ * +out+ - open output file to write the result to.
180
+ * +record+ - instance of DataMetaDom::Record to export
181
+ * +scalaPackage+ - Scala package to export to
182
+ * +baseName+ - the name of the class to generate.
183
+ =end
184
+ def self.genEntity(model, out, record, scalaPackage)
185
+ baseName = record.baseName
186
+ fields = record.fields
187
+ out.puts <<ENTITY_CLASS_HEADER
188
+
189
+ #{record.docs.empty? ? '' : ScalaLexer.classScalaDoc(record.docs)}case class #{baseName} (
190
+ #{fields.keys.map { |k|
191
+ f = fields[k]
192
+ typeDef = self.aggrScalaType(f, scalaPackage)
193
+ " `#{f.name}`: #{typeDef}#{model.enums.keys.member?(f.dataType.type) ? '.Value' : ''}"
194
+ }.join(",\n ")
195
+ }
196
+ )
197
+ ENTITY_CLASS_HEADER
198
+ end
199
+
200
+ =begin rdoc
201
+ Generates Scala source code for the worded enum, DataMeta DOM keyword "<tt>enum</tt>".
202
+ =end
203
+ def self.genEnumWorded(out, enum)
204
+ values = enum.keys.map{|k| enum[k]} # sort by ordinals to preserve the order
205
+ _, base, _ = assertNamespace(enum.name)
206
+ out.puts %<
207
+
208
+ #{enum.docs.empty? ? '' : enumScalaDoc(enum.docs)}object #{base} extends Enumeration {
209
+ type #{base} = Value
210
+ val #{values.map{|v| "`#{v}`"}.join(', ')} = Value
211
+ }
212
+ >
213
+ end
214
+
215
+ # Distinguish JVM classes by the platform, unless it's Java
216
+ def self.scalaNs(ns)
217
+ "#{ns}.#{SCALA_SUBPACKAGE}"
218
+ end
219
+
220
+ =begin rdoc
221
+ Extracts 3 pieces of information from the given full name:
222
+ * The namespace if any, i.e. Scala package, empty string if none
223
+ * The base name for the type, without the namespace
224
+ * Scala package's relative path, the dots replaced by the file separator.
225
+
226
+ Returns an array of these pieces of info in this exact order as described here.
227
+ =end
228
+ def assertNamespace(name)
229
+ ns, base = DataMetaDom.splitNameSpace(name)
230
+ scalaPackage = DataMetaDom.validNs?(ns, base) ? ns : ''
231
+ packagePath = scalaPackage.empty? ? '' : scalaPackage.gsub('.', File::SEPARATOR)
232
+
233
+ [scalaPackage, base, packagePath]
234
+ end
235
+ =begin rdoc
236
+ Generates scala sources for the model, the POJOs.
237
+ * Parameters
238
+ * +parser+ - instance of Model
239
+ * +outRoot+ - output directory
240
+ =end
241
+ def genCaseClasses(model, outRoot)
242
+ firstRec = model.records.values.first
243
+ raise ArgumentError, "No records defined in the model #{model.sources.masterPath}" unless firstRec
244
+
245
+ scalaPackage, base, packagePath = assertNamespace(firstRec.name)
246
+ scalaPackage = self.scalaNs(scalaPackage)
247
+ destDir = File.join(outRoot, packagePath, SCALA_SUBPACKAGE) # keep this in sync with scalaNs
248
+ FileUtils.mkdir_p destDir
249
+ out = File.open(File.join(destDir, 'Model.scala'), 'wb')
250
+ begin
251
+ out.puts %<package #{scalaPackage}
252
+
253
+ import java.time.ZonedDateTime
254
+ import scala.math.BigDecimal
255
+ import scala.collection.immutable.Set
256
+ import scala.collection.immutable.List
257
+ import scala.collection.Seq
258
+
259
+ /**
260
+ * This content is generated by DataMeta, do not edit manually!
261
+ */
262
+ >
263
+
264
+ (model.enums.values + model.records.values).each {|e|
265
+ case
266
+ when e.kind_of?(DataMetaDom::Record)
267
+ self.genEntity model, out, e, scalaPackage
268
+ when e.kind_of?(DataMetaDom::Mappings)
269
+ raise ArgumentError, "For name #{e.name}: Mappings can not be generated to a case class"
270
+ when e.kind_of?(DataMetaDom::Enum)
271
+ self.genEnumWorded out, e
272
+ when e.kind_of?(DataMetaDom::BitSet)
273
+ raise ArgumentError, "For name #{e.name}: BitsSets can not be generated to a case class"
274
+ else
275
+ raise "Unsupported Entity: #{e.inspect}"
276
+ end
277
+ }
278
+ ensure
279
+ out.close
280
+ end
281
+ end
282
+
283
+ module_function :genCaseClasses
284
+
285
+ end
286
+ end
287
+
@@ -11,17 +11,18 @@ For command line details either check the new method's source or the README.rdoc
11
11
  =end
12
12
  class Sources
13
13
 
14
+ attr_reader :masterPath
14
15
  =begin rdoc
15
16
  Start parsing from the master file, collect all the files that are included.
16
17
  =end
17
18
  def initialize(masterFile)
18
- masterPath = File.dirname(masterFile)
19
+ @masterPath = File.dirname(masterFile)
19
20
  @todo = {}; @done = {}
20
21
  libSpec = ENV[DATAMETA_LIB]
21
22
  @paths = libSpec ? libSpec.split(File::PATH_SEPARATOR).map { |e| uniPath(e) } : []
22
- @paths.unshift(masterPath).flatten! if masterPath
23
+ @paths.unshift(@masterPath).flatten! if @masterPath
23
24
  @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
+ src = SourceFile.new(@masterPath, File.basename(masterFile))
25
26
  @todo[src.key] = src
26
27
  end
27
28
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dataMetaDom
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.6
4
+ version: 1.0.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Bergens
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-04-03 00:00:00.000000000 Z
11
+ date: 2017-04-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dataMetaXtra
@@ -60,6 +60,7 @@ executables:
60
60
  - dataMetaGvExport.rb
61
61
  - dataMetaOracleDdl.rb
62
62
  - dataMetaReVersion.rb
63
+ - dataMetaGenScalaCases.rb
63
64
  extensions: []
64
65
  extra_rdoc_files: []
65
66
  files:
@@ -68,6 +69,7 @@ files:
68
69
  - PostInstall.txt
69
70
  - README.md
70
71
  - Rakefile
72
+ - bin/dataMetaGenScalaCases.rb
71
73
  - bin/dataMetaGvExport.rb
72
74
  - bin/dataMetaMySqlDdl.rb
73
75
  - bin/dataMetaOracleDdl.rb
@@ -90,6 +92,7 @@ files:
90
92
  - lib/dataMetaDom/recAttr.rb
91
93
  - lib/dataMetaDom/record.rb
92
94
  - lib/dataMetaDom/ref.rb
95
+ - lib/dataMetaDom/scala.rb
93
96
  - lib/dataMetaDom/sourceFile.rb
94
97
  - lib/dataMetaDom/sources.rb
95
98
  - lib/dataMetaDom/util.rb