kwatable 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/COPYING +340 -0
- data/ChangeLog.txt +27 -0
- data/README.txt +81 -0
- data/bin/kwatable +20 -0
- data/examples/ex1/Makefile +34 -0
- data/examples/ex1/example1.yaml +85 -0
- data/examples/ex2/Makefile +34 -0
- data/examples/ex2/example2.yaml +94 -0
- data/kwatable.gemspec +48 -0
- data/lib/kwatable.rb +31 -0
- data/lib/kwatable/error-msg.rb +37 -0
- data/lib/kwatable/kwatable.schema.yaml +133 -0
- data/lib/kwatable/main-program.rb +197 -0
- data/lib/kwatable/manufactory.rb +213 -0
- data/lib/kwatable/templates/ddl-mysql.eruby +169 -0
- data/lib/kwatable/templates/ddl-postgresql.eruby +153 -0
- data/lib/kwatable/templates/defaults.yaml +87 -0
- data/lib/kwatable/templates/dto-java.eruby +204 -0
- data/lib/kwatable/templates/dto-ruby.eruby +180 -0
- data/setup.rb +1331 -0
- data/test/assert-diff.rb +44 -0
- data/test/test.rb +202 -0
- data/test/test1/test1.ddl-mysql.expected +22 -0
- data/test/test1/test1.ddl-postgresql.expected +22 -0
- data/test/test1/test1.dto-java.Group.expected +32 -0
- data/test/test1/test1.dto-java.User.expected +59 -0
- data/test/test1/test1.dto-ruby.Group.expected +21 -0
- data/test/test1/test1.dto-ruby.User.expected +36 -0
- data/test/test1/test1.yaml +85 -0
- data/test/test2/test2.ddl-mysql.expected +49 -0
- data/test/test2/test2.ddl-postgresql.expected +49 -0
- data/test/test2/test2.dto-java.Address.expected +42 -0
- data/test/test2/test2.dto-java.Customer.expected +49 -0
- data/test/test2/test2.dto-java.Item.expected +37 -0
- data/test/test2/test2.dto-java.SalesOrder.expected +50 -0
- data/test/test2/test2.dto-java.SalesOrderLine.expected +56 -0
- data/test/test2/test2.dto-ruby.Address.expected +25 -0
- data/test/test2/test2.dto-ruby.Customer.expected +32 -0
- data/test/test2/test2.dto-ruby.Item.expected +23 -0
- data/test/test2/test2.dto-ruby.SalesOrder.expected +32 -0
- data/test/test2/test2.dto-ruby.SalesOrderLine.expected +39 -0
- data/test/test2/test2.yaml +94 -0
- metadata +91 -0
@@ -0,0 +1,197 @@
|
|
1
|
+
###
|
2
|
+
### copyright(c) 2005 kuwata-lab.com all rights reserved.
|
3
|
+
### $Release: 0.0.1 $
|
4
|
+
### $Rev: 12 $
|
5
|
+
###
|
6
|
+
|
7
|
+
require 'erb'
|
8
|
+
|
9
|
+
module Kwatable
|
10
|
+
|
11
|
+
class CommandOptionError < KwatableError
|
12
|
+
end
|
13
|
+
|
14
|
+
class MainProgram
|
15
|
+
def initialize(argv=ARGV)
|
16
|
+
@argv = argv
|
17
|
+
end
|
18
|
+
|
19
|
+
def execute()
|
20
|
+
options, properties = parse_options(@argv)
|
21
|
+
|
22
|
+
## help or version
|
23
|
+
if options[?h] || options[?v]
|
24
|
+
puts version() if options[?v]
|
25
|
+
puts usage() if options[?h]
|
26
|
+
return
|
27
|
+
end
|
28
|
+
|
29
|
+
## load data file
|
30
|
+
s = ''
|
31
|
+
filenames = @argv
|
32
|
+
filenames.each do |filename|
|
33
|
+
File.open(filename) do |f|
|
34
|
+
f.each_line do |line|
|
35
|
+
s << line.gsub(/([^\t]{8})|([^\t]*)\t/n){[$+].pack("A8")} ## expand tab
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
yaml = YAML.load(s)
|
40
|
+
|
41
|
+
## manufacture
|
42
|
+
manufactory = Manufactory.new()
|
43
|
+
manufactory.manufacture(yaml)
|
44
|
+
$stderr.print yaml.to_yaml if options[?D]
|
45
|
+
|
46
|
+
## template filename
|
47
|
+
template = options[?f]
|
48
|
+
unless template
|
49
|
+
return nil if options[?D]
|
50
|
+
#* key=:template_required msg="template is not specified."
|
51
|
+
raise CommandOptionError.new(Kwatable.msg(:template_required))
|
52
|
+
end
|
53
|
+
|
54
|
+
## template filepath
|
55
|
+
template_filepath = nil
|
56
|
+
if test(?f, template)
|
57
|
+
template_filepath = template
|
58
|
+
elsif options[?I] && t = find_template(template, options[?I])
|
59
|
+
template_filepath = t
|
60
|
+
else
|
61
|
+
template_filepath = find_template(template, Kwatable.template_path)
|
62
|
+
end
|
63
|
+
unless template_filepath
|
64
|
+
#* key=:template_notfound msg="`%s': template file not found."
|
65
|
+
raise CommandOptionError.new(Kwatable.msg(:template_notfound) % template)
|
66
|
+
end
|
67
|
+
|
68
|
+
## apply template
|
69
|
+
if !options[?m]
|
70
|
+
context = { 'tables' => yaml['tables'], 'properties' => properties, }
|
71
|
+
output = apply_template(template_filepath, context)
|
72
|
+
return output
|
73
|
+
else
|
74
|
+
yaml['tables'].each do |table|
|
75
|
+
context = { 'table' => table, 'properties' => properties, }
|
76
|
+
output = apply_template(template_filepath, context)
|
77
|
+
output_filename = context[:output_filename]
|
78
|
+
output_filename = "#{options[?d]}/#{output_filename}" if options[?d]
|
79
|
+
File.open(output_filename, 'w') { |f| f.write(output) }
|
80
|
+
unless options[?s]
|
81
|
+
#* key=:file_generated msg="generated: %s"
|
82
|
+
$stderr.puts(Kwatable.msg(:file_generated) % output_filename)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
return nil
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
def parse_options(argv)
|
92
|
+
options = {}
|
93
|
+
properties = {}
|
94
|
+
while argv[0] && argv[0][0] == ?-
|
95
|
+
optstr = argv.shift
|
96
|
+
if optstr =~ /\A--([-\w]+)(=.*)?/ ## properties
|
97
|
+
key, value = $1, $2
|
98
|
+
key = key.gsub(/-/, '_')
|
99
|
+
if value
|
100
|
+
value.sub!(/\A=/, '')
|
101
|
+
else
|
102
|
+
value = true
|
103
|
+
end
|
104
|
+
case value
|
105
|
+
when "true", "yes" ; value = true
|
106
|
+
when "false", "no" ; value = false
|
107
|
+
when "null", "nil" ; value = nil
|
108
|
+
when /\A\d+\z/ ; value = value.to_i
|
109
|
+
when /\A\d+\.\d+\z/ ; value = value.to_f
|
110
|
+
when /\A'.*'\z/ ; value = eval(value)
|
111
|
+
when /\A".*"\z/ ; value = eval(value)
|
112
|
+
end
|
113
|
+
properties[key.intern] = value
|
114
|
+
else
|
115
|
+
optstr = optstr[1, optstr.length-1]
|
116
|
+
while optstr && !optstr.empty?
|
117
|
+
optchar = optstr[0]
|
118
|
+
optstr = optstr[1, optstr.length-1]
|
119
|
+
case optchar
|
120
|
+
when ?h, ?v, ?m, ?s, ?D
|
121
|
+
options[optchar] = true
|
122
|
+
when ?f, ?t
|
123
|
+
arg = optstr.empty? ? argv.shift : optstr
|
124
|
+
optstr = nil
|
125
|
+
unless arg
|
126
|
+
#* key=:template_required msg="-%s: template filename required."
|
127
|
+
raise CommandOptionError.new(Kwatable.msg(:template_required) % optchar.chr)
|
128
|
+
end
|
129
|
+
options[?f] = arg
|
130
|
+
#options[optchar] = arg
|
131
|
+
when ?d
|
132
|
+
arg = optstr.empty? ? argv.shift : optstr
|
133
|
+
optstr = nil
|
134
|
+
unless arg
|
135
|
+
#* key=:outdir_required msg="-%s: output directory required."
|
136
|
+
raise CommandOptionError.new(Kwatable.msg(:outdir_required) % optchar.chr)
|
137
|
+
end
|
138
|
+
options[optchar] = arg
|
139
|
+
when ?I
|
140
|
+
arg = optstr.empty? ? argv.shift : optstr
|
141
|
+
optstr = nil
|
142
|
+
unless arg
|
143
|
+
#* key=:directory_required msg="-%s: directory required."
|
144
|
+
raise CommandOptionError.new(Kwatable.msg(:directory_required) % optchar.chr)
|
145
|
+
end
|
146
|
+
(options[optchar] ||= []).concat(arg.split(/,/))
|
147
|
+
else
|
148
|
+
#* key=:option_invalid msg="-%s: invalid option."
|
149
|
+
raise CommandOptionError.new(Kwatable.msg(:option_invalid) % optchar.chr)
|
150
|
+
end
|
151
|
+
end # end while
|
152
|
+
end # end if
|
153
|
+
end # end while
|
154
|
+
return options, properties
|
155
|
+
end
|
156
|
+
|
157
|
+
def find_template(template, path_list)
|
158
|
+
path_list.each do |path|
|
159
|
+
t = "#{path}/#{template}"
|
160
|
+
return t if test(?f, t)
|
161
|
+
end
|
162
|
+
return nil
|
163
|
+
end
|
164
|
+
|
165
|
+
def apply_template(filename, context)
|
166
|
+
str = File.open(filename) { |f| f.read() }
|
167
|
+
trim_mode = '>' # or '%'
|
168
|
+
erb = ERB.new(str, $SAFE, trim_mode)
|
169
|
+
result = eval_erb(erb, context)
|
170
|
+
return result
|
171
|
+
end
|
172
|
+
|
173
|
+
def eval_erb(__erb, context)
|
174
|
+
return __erb.result(binding())
|
175
|
+
end
|
176
|
+
|
177
|
+
def usage()
|
178
|
+
command = File::basename($0)
|
179
|
+
s = ""
|
180
|
+
s << "Usage: #{command} [-hvm] [-I path] [-d dir] -f template datafile [datafile2 ...]\n"
|
181
|
+
s << " -h : help\n"
|
182
|
+
s << " -v : version\n"
|
183
|
+
s << " -I path : template directory path\n"
|
184
|
+
s << " -f template : template filename\n"
|
185
|
+
s << " -m : multiple output file\n"
|
186
|
+
s << " -d dir : output file directory (with '-m')\n"
|
187
|
+
s << " -s : silent mode\n"
|
188
|
+
return s
|
189
|
+
end
|
190
|
+
|
191
|
+
def version()
|
192
|
+
return ("$Release: 0.0.1 $" =~ /[\.\d]+/ && $&)
|
193
|
+
end
|
194
|
+
|
195
|
+
end
|
196
|
+
|
197
|
+
end
|
@@ -0,0 +1,213 @@
|
|
1
|
+
###
|
2
|
+
### copyright(c) 2005 kuwata-lab.com all rights reserved.
|
3
|
+
### $Release: 0.0.1 $
|
4
|
+
### $Rev: 11 $
|
5
|
+
###
|
6
|
+
|
7
|
+
require 'yaml'
|
8
|
+
|
9
|
+
module Kwatable
|
10
|
+
|
11
|
+
class ManufactureError < KwatableError
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
##
|
16
|
+
## ex.
|
17
|
+
## manufactory = Kwatable::Manufactory.new
|
18
|
+
## tabledef = manufactory.parse(input) # input is String or File
|
19
|
+
## p tabledef[:columns]
|
20
|
+
## p tabledef[:tables]
|
21
|
+
##
|
22
|
+
class Manufactory
|
23
|
+
|
24
|
+
def parse(input)
|
25
|
+
str = ''
|
26
|
+
input.each_line do |line|
|
27
|
+
str << line.gsub(/([^\t]{8})|([^\t]*)\t/n){[$+].pack("A8")} ## expand tab
|
28
|
+
end
|
29
|
+
ingredient = YAML.load(str)
|
30
|
+
manufacture(ingredient)
|
31
|
+
return ingredient
|
32
|
+
end
|
33
|
+
|
34
|
+
def manufacture(ingredient)
|
35
|
+
#assert unless ingredient.is_a?(Hash)
|
36
|
+
|
37
|
+
column_map, patterned_columns = manufacture_columns(ingredient['columns'])
|
38
|
+
#assert unless column_map.is_a?(Hash)
|
39
|
+
#assert unless patterned_columns.is_a?(Array)
|
40
|
+
|
41
|
+
table_map = manufacture_tables(ingredient['tables'], column_map, patterned_columns)
|
42
|
+
#assert unless table_map.is_a?(Hash)
|
43
|
+
|
44
|
+
ingredient['column_map'] = column_map # Hash
|
45
|
+
ingredient['table_map'] = table_map # Hash
|
46
|
+
return ingredient
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def manufacture_columns(columns)
|
52
|
+
column_map = {}
|
53
|
+
patterned_columns = []
|
54
|
+
columns.each do |column|
|
55
|
+
name = column['name']
|
56
|
+
unless name
|
57
|
+
#* key=:colname_required msg="column definition doesn't have a name."
|
58
|
+
raise ManufactureError.new(Kwatable.msg(:colname_required))
|
59
|
+
end
|
60
|
+
if name =~ /\A\/(.*)\/\z/
|
61
|
+
pattern = $1
|
62
|
+
begin
|
63
|
+
name = Regexp.compile(pattern)
|
64
|
+
rescue RegexpError => ex
|
65
|
+
#* key=:regexp_invalid msg="column %s: %s"
|
66
|
+
raise ManufactureError.new(Kwatable.msg(:regexp_invalid) % [name, ex.message])
|
67
|
+
end
|
68
|
+
column['namepattern'] = name
|
69
|
+
#column.delete('name')
|
70
|
+
patterned_columns << column
|
71
|
+
else
|
72
|
+
if column_map.key?(name)
|
73
|
+
#* key=:coldef_duplicated msg="column definition `%s' is duplicated."
|
74
|
+
raise ManufactureError.new(Kwatable.msg(:coldef_duplicated) % name)
|
75
|
+
end
|
76
|
+
column_map[name] = column
|
77
|
+
end
|
78
|
+
#name = column['name'].strip
|
79
|
+
#namepattern = column['namepattern']
|
80
|
+
#unless name || namepattern
|
81
|
+
# #* key=:colname_required msg="column definition doesn't have a name nor namepattern."
|
82
|
+
# raise ManufactureError.new(Kwatable.msg(:colname_required))
|
83
|
+
#end
|
84
|
+
#if name
|
85
|
+
# if column_map.key?(name)
|
86
|
+
# #* key=:coldef_duplicated msg="column definition `%s' is duplicated."
|
87
|
+
# raise ManufactureError.new(Kwatable.msg(:coldef_duplicated) % [name])
|
88
|
+
# end
|
89
|
+
# column_map[name] = column
|
90
|
+
#end
|
91
|
+
#if namepattern
|
92
|
+
# pattern = namepattern.strip
|
93
|
+
# pattern = $1 if pattern =~ /^\/(.*)\/$/
|
94
|
+
# column['namepattern'] = Regexp.compile(pattern)
|
95
|
+
# patterned_columns << column
|
96
|
+
#end
|
97
|
+
end if columns
|
98
|
+
return column_map, patterned_columns
|
99
|
+
end
|
100
|
+
|
101
|
+
def manufacture_tables(tables, column_map, patterned_columns)
|
102
|
+
#assert unless tables.is_a?(Array)
|
103
|
+
#assert unless column_map.is_a?(Hash)
|
104
|
+
#assert unless patterned_columns.is_a?(Array)
|
105
|
+
|
106
|
+
## create table_map
|
107
|
+
table_map = {}
|
108
|
+
tables.each do |table|
|
109
|
+
name = table['name']
|
110
|
+
unless name
|
111
|
+
#* key=:tablename_required msg="table definition doesn't have a name."
|
112
|
+
raise ManufactureError.new(Kwatable.msg(:tablename_required))
|
113
|
+
end
|
114
|
+
if table_map.key?(name)
|
115
|
+
#* key=:tabledef_duplicated msg="table definition `%s' is duplicated."
|
116
|
+
raise ManufactureError.new(Kwatable.msg(:tabledef_duplicated) % [name])
|
117
|
+
end
|
118
|
+
table_map[name] = table
|
119
|
+
end
|
120
|
+
|
121
|
+
## manufacture table columns
|
122
|
+
tables.each do |table|
|
123
|
+
name_map = {}
|
124
|
+
table['columns'].each do |column|
|
125
|
+
name = column['name']
|
126
|
+
unless name
|
127
|
+
#* key=:tablecolumn_required msg="table '%s': column name requried."
|
128
|
+
raise ManufactureError.new(Kwatable.msg(:tablecolumn_required % [table['name']]))
|
129
|
+
end
|
130
|
+
if name_map[name]
|
131
|
+
#* key=:tablecolumn_duplicated msg="table '%s': column '%s' is duplicated."
|
132
|
+
raise ManufactureError.new(Kwatable.msg(:tablecolumn_duplicated % [table['name'], name]))
|
133
|
+
end
|
134
|
+
name_map[name] = true
|
135
|
+
set_defaults(column, column_map, patterned_columns)
|
136
|
+
#alias_keys(column, "primary-key", "primarykey", "identifier")
|
137
|
+
#alias_keys(column, "not-null", "notnull", "required")
|
138
|
+
alias_key(column, "primary-key", "identifier")
|
139
|
+
alias_key(column, "not-null", "required")
|
140
|
+
handle_ref(column, table_map) if column['ref']
|
141
|
+
handle_values(column) if column['values']
|
142
|
+
unless column['type']
|
143
|
+
#* key=:tabletype_required msg="table `%s': type of column `%s' is not determined."
|
144
|
+
raise ManufactureError.new(Kwatable.msg(:tabletype_required) % [table['name'], column['name']])
|
145
|
+
end
|
146
|
+
end if table['columns']
|
147
|
+
end if tables
|
148
|
+
|
149
|
+
return table_map
|
150
|
+
end
|
151
|
+
|
152
|
+
def set_defaults(column, column_map, patterned_columns)
|
153
|
+
colname = column['name']
|
154
|
+
defaults = column_map[colname]
|
155
|
+
defaults ||= patterned_columns.find { |col| colname =~ col['namepattern'] }
|
156
|
+
defaults.each do |key, val|
|
157
|
+
column[key] = val if !column.key?(key) && key != 'namepattern'
|
158
|
+
end if defaults
|
159
|
+
end
|
160
|
+
|
161
|
+
def alias_keys(column, key, *old_keys) # not used
|
162
|
+
old_keys.each do |old_key|
|
163
|
+
column[key] = column[old_key] if !column.key?(key) && column.key?(old_key)
|
164
|
+
end if old_keys
|
165
|
+
end
|
166
|
+
|
167
|
+
def alias_key(column, key1, key2)
|
168
|
+
if column[key1] && !column.key?(key2)
|
169
|
+
column[key2] = column[key1]
|
170
|
+
elsif column[key2] && !column.key?(key1)
|
171
|
+
column[key1] = column[key2]
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def handle_values(column)
|
176
|
+
return unless column['values']
|
177
|
+
width = 0
|
178
|
+
column['values'].each do |value|
|
179
|
+
len = value.to_s.length
|
180
|
+
width = len if len > width
|
181
|
+
end
|
182
|
+
column['type'] ||= 'string'
|
183
|
+
column['width'] ||= width
|
184
|
+
end
|
185
|
+
|
186
|
+
def handle_ref(column, table_map)
|
187
|
+
ref = column['ref']
|
188
|
+
return unless ref
|
189
|
+
ref = ref.strip
|
190
|
+
return unless ref =~ /\A(\w+)\.(\w+)\z/ || ref =~ /\A(\w+)\((\w+)\)\z/
|
191
|
+
ref_table_name = $1
|
192
|
+
ref_column_name = $2
|
193
|
+
ref_table = table_map[ref_table_name]
|
194
|
+
unless ref_table
|
195
|
+
#* key=:reftable_notfound msg="`ref: %s': table not found."
|
196
|
+
raise ManufactureError.new(Kwatable.msg(:reftable_notfound) % column['ref'])
|
197
|
+
end
|
198
|
+
cols = ref_table['columns']
|
199
|
+
ref_column = cols ? cols.find { |col| col['name'] == ref_column_name } : nil
|
200
|
+
unless ref_column
|
201
|
+
#* key=:refcolumn_notfound msg="`ref: %s': column not found in the table."
|
202
|
+
raise ManufactureError.new(Kwatable.msg(:refcolumn_notfound) % column['ref'])
|
203
|
+
end
|
204
|
+
column['ref-table'] = ref_table
|
205
|
+
column['ref-column'] = ref_column
|
206
|
+
column['ref-name'] ||= column['name'].sub(/_#{ref_column_name}$/, '')
|
207
|
+
column['type'] = ref_column['type']
|
208
|
+
column['width'] ||= ref_column['width'] if ref_column.key?('width')
|
209
|
+
end
|
210
|
+
|
211
|
+
end
|
212
|
+
|
213
|
+
end
|
@@ -0,0 +1,169 @@
|
|
1
|
+
<%
|
2
|
+
|
3
|
+
##
|
4
|
+
## kwatable template file for MySQL
|
5
|
+
##
|
6
|
+
## copyright(c) 2005 kuwata-lab.com all rights reserved.
|
7
|
+
## $Release: 0.0.1 $
|
8
|
+
## $Rev: 12 $
|
9
|
+
##
|
10
|
+
## template properties:
|
11
|
+
## (none)
|
12
|
+
|
13
|
+
|
14
|
+
#
|
15
|
+
# context variables
|
16
|
+
#
|
17
|
+
tables = context['tables']
|
18
|
+
properties = context['properties']
|
19
|
+
raise "don't use '-m' option with 'ddl-mysql.eruby'." unless tables
|
20
|
+
|
21
|
+
|
22
|
+
#
|
23
|
+
# MySQL keywords
|
24
|
+
#
|
25
|
+
keywords = <<-END
|
26
|
+
add all alter analyze and as asc asensitive
|
27
|
+
before between bigint binary blob both by
|
28
|
+
call cascade case change char character check collate column
|
29
|
+
condition connection constraint continue convert create cross
|
30
|
+
current_date current_time current_timestamp current_user cursor
|
31
|
+
database databases day_hour day_microsecond day_minute day_second
|
32
|
+
dec decimal declare default delayed delete desc describe
|
33
|
+
deterministic distinct distinctrow div double drop dual
|
34
|
+
each else elseif enclosed escaped exists exit explain
|
35
|
+
false fetch float for force foreign from fulltext
|
36
|
+
goto grant group
|
37
|
+
having high_priority hour_microsecond hour_minute hour_second
|
38
|
+
if ignore in index infile inner inout insensitive insert
|
39
|
+
int integer interval into is iterate
|
40
|
+
join
|
41
|
+
key keys kill
|
42
|
+
leading leave left like limit lines load localtime
|
43
|
+
localtimestamp lock long longblob longtext loop low_priority
|
44
|
+
match mediumblob mediumint mediumtext middleint
|
45
|
+
minute_microsecond minute_second mod modifies
|
46
|
+
natural not no_write_to_binlog null numeric
|
47
|
+
on optimize option optionally or order out outer outfile
|
48
|
+
precision primary procedure purge
|
49
|
+
read reads real references regexp release rename repeat
|
50
|
+
replace require restrict return revoke right rlike
|
51
|
+
schema schemas second_microsecond select sensitive
|
52
|
+
separator set show smallint soname spatial specific sql
|
53
|
+
sqlexception sqlstate sqlwarning sql_big_result
|
54
|
+
sql_calc_found_rows sql_small_result ssl starting straight_join
|
55
|
+
table terminated then tinyblob tinyint tinytext to
|
56
|
+
trailing trigger true
|
57
|
+
undo union unique unlock unsigned update usage use using
|
58
|
+
utc_date utc_time utc_timestamp
|
59
|
+
values varbinary varchar varcharacter varying
|
60
|
+
when where while with write
|
61
|
+
xor
|
62
|
+
year_month
|
63
|
+
zerofill
|
64
|
+
END
|
65
|
+
KEYWORDS = {}
|
66
|
+
keywords.split(/\s+/).each { |word| KEYWORDS[word] = true }
|
67
|
+
|
68
|
+
|
69
|
+
#
|
70
|
+
# escape keyword
|
71
|
+
#
|
72
|
+
def _(word)
|
73
|
+
return KEYWORDS[word.downcase] ? "`#{word}`" : word
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
#
|
78
|
+
# start output
|
79
|
+
#
|
80
|
+
%>
|
81
|
+
----------------------------------------------------------------------
|
82
|
+
-- DDL for MySQL
|
83
|
+
-- generated by kwatable with template 'ddl-mysql.eruby'
|
84
|
+
-- at <%= Time.now.to_s %>
|
85
|
+
|
86
|
+
----------------------------------------------------------------------
|
87
|
+
<%
|
88
|
+
#
|
89
|
+
# create table statement
|
90
|
+
#
|
91
|
+
%>
|
92
|
+
<% for table in tables %>
|
93
|
+
|
94
|
+
-- <%= table['desc'] %>
|
95
|
+
|
96
|
+
create table <%= _(table['name']) %> (
|
97
|
+
<%
|
98
|
+
n = table['columns'].length
|
99
|
+
i = 0
|
100
|
+
for column in table['columns']
|
101
|
+
i += 1
|
102
|
+
flag_last_loop = (i == n)
|
103
|
+
|
104
|
+
name = column['name']
|
105
|
+
type = column['type']
|
106
|
+
width = column['width']
|
107
|
+
|
108
|
+
#
|
109
|
+
# column type
|
110
|
+
#
|
111
|
+
case type
|
112
|
+
when 'char' ; type = 'tinyint'
|
113
|
+
when 'short' ; type = 'mediumint'
|
114
|
+
when 'int' ; type = 'integer'
|
115
|
+
when 'inteter' ;
|
116
|
+
when 'str' ; type = 'varchar' ; width ||= 255
|
117
|
+
when 'string' ; type = 'varchar' ; width ||= 255
|
118
|
+
when 'text' ;
|
119
|
+
when 'float' ;
|
120
|
+
when 'double' ;
|
121
|
+
when 'bool' ; type = 'boolean'
|
122
|
+
when 'boolean' ;
|
123
|
+
when 'date' ;
|
124
|
+
when 'timestamp' ;
|
125
|
+
when 'money' ; type = 'decimal'
|
126
|
+
end
|
127
|
+
type += "(#{width})" if width
|
128
|
+
|
129
|
+
#
|
130
|
+
# set type with 'enum(...)' if column has values
|
131
|
+
#
|
132
|
+
if column['values']
|
133
|
+
type = "enum(" + column['values'].collect{|v| "'#{v}'"}.join(", ") + ")"
|
134
|
+
width = nil
|
135
|
+
end
|
136
|
+
|
137
|
+
#
|
138
|
+
# constraints
|
139
|
+
#
|
140
|
+
constraints = []
|
141
|
+
constraints << 'auto_increment' if column['serial']
|
142
|
+
constraints << 'not null' if column['not-null'] && !column['serial'] && !column['primary-key']
|
143
|
+
constraints << 'primary key' if column['primary-key']
|
144
|
+
constraints << 'unique' if column['unique']
|
145
|
+
|
146
|
+
#
|
147
|
+
# column definition
|
148
|
+
#
|
149
|
+
name_part = '%-20s' % _(name)
|
150
|
+
type_part = '%-20s' % type
|
151
|
+
const_part = constraints.join(' ')
|
152
|
+
comma = flag_last_loop ? '' : ','
|
153
|
+
comment = column['ref'] ? " -- references #{column['ref']}" : ""
|
154
|
+
%>
|
155
|
+
<%= name_part %> <%= type_part %> <%= const_part %><%= comma %><%= comment %>
|
156
|
+
|
157
|
+
<%
|
158
|
+
end
|
159
|
+
|
160
|
+
#
|
161
|
+
# composite primary key
|
162
|
+
#
|
163
|
+
%>
|
164
|
+
<% if table['primary-keys'] %>
|
165
|
+
<% pkeystr = table['primary-keys'].collect { |pkey| _(pkey) }.join(', ') %>
|
166
|
+
, primary key (<%= pkeystr %>)
|
167
|
+
<% end %>
|
168
|
+
);
|
169
|
+
<% end %>
|