kwatable 0.2.0 → 0.3.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.
- data/ChangeLog +46 -0
- data/MIT-LICENSE +20 -0
- data/README.txt +4 -2
- data/bin/kwatable +4 -4
- data/examples/ex1/Makefile +40 -14
- data/examples/ex1/{example1.yaml → tabledef.yaml} +42 -11
- data/examples/ex2/Makefile +41 -14
- data/examples/ex2/{example2.yaml → tabledef.yaml} +45 -30
- data/examples/ex3/Makefile +52 -0
- data/examples/ex3/tabledef.yaml +136 -0
- data/kwatable.gemspec +11 -10
- data/lib/kwatable.rb +24 -18
- data/lib/kwatable/kwatable.schema.yaml +95 -5
- data/lib/kwatable/main.rb +331 -0
- data/lib/kwatable/manipulator.rb +320 -192
- data/lib/kwatable/messages.rb +59 -0
- data/lib/kwatable/template/ddl-mysql.eruby +202 -0
- data/lib/kwatable/{templates → template}/ddl-postgresql.eruby +71 -45
- data/lib/kwatable/{templates → template}/defaults.yaml +2 -2
- data/lib/kwatable/template/dictionary.en.yaml +70 -0
- data/lib/kwatable/template/dictionary.ja.yaml +165 -0
- data/lib/kwatable/template/dto-java.eruby +77 -0
- data/lib/kwatable/template/dto-java.sub.eruby +259 -0
- data/lib/kwatable/template/dto-ruby.eruby +63 -0
- data/lib/kwatable/template/dto-ruby.sub.eruby +213 -0
- data/lib/kwatable/template/helper/column.rb +70 -0
- data/lib/kwatable/template/helper/common.rb +151 -0
- data/lib/kwatable/template/helper/java.rb +83 -0
- data/lib/kwatable/template/helper/label.rb +90 -0
- data/lib/kwatable/template/helper/ruby.rb +36 -0
- data/lib/kwatable/template/helper/table.rb +62 -0
- data/lib/kwatable/template/hibernate.eruby +139 -0
- data/lib/kwatable/template/rails-controller.eruby +66 -0
- data/lib/kwatable/template/rails-controller.sub.eruby +114 -0
- data/lib/kwatable/template/rails-kwartz.eruby +164 -0
- data/lib/kwatable/template/rails-kwartz/_attr.plogic.eruby +56 -0
- data/lib/kwatable/template/rails-kwartz/_form.plogic.eruby +81 -0
- data/lib/kwatable/template/rails-kwartz/_link.plogic.eruby +36 -0
- data/lib/kwatable/template/rails-kwartz/edit.cfg.yaml.eruby +16 -0
- data/lib/kwatable/template/rails-kwartz/edit.html.eruby +46 -0
- data/lib/kwatable/template/rails-kwartz/edit.plogic.eruby +20 -0
- data/lib/kwatable/template/rails-kwartz/layout.html.eruby +39 -0
- data/lib/kwatable/template/rails-kwartz/layout.plogic.eruby +32 -0
- data/lib/kwatable/template/rails-kwartz/list.html.eruby +94 -0
- data/lib/kwatable/template/rails-kwartz/list.plogic.eruby +41 -0
- data/lib/kwatable/template/rails-kwartz/new.html.eruby +100 -0
- data/lib/kwatable/template/rails-kwartz/new.plogic.eruby +26 -0
- data/lib/kwatable/template/rails-kwartz/show.html.eruby +51 -0
- data/lib/kwatable/template/rails-kwartz/show.plogic.eruby +9 -0
- data/lib/kwatable/template/rails-model.eruby +35 -0
- data/lib/kwatable/template/rails-model.sub.eruby +136 -0
- data/lib/kwatable/{templates → template}/validator-ruby.eruby +18 -11
- data/lib/kwatable/util.rb +133 -0
- data/lib/kwatable/util/assert-text-equal.rb +47 -0
- data/lib/kwatable/util/assertion.rb +115 -0
- data/lib/kwatable/validator.rb +50 -0
- data/test/assert-diff.rb +1 -1
- data/test/test-ex.rb +306 -0
- data/test/test.rb +37 -127
- metadata +66 -17
- data/COPYING +0 -340
- data/ChangeLog.txt +0 -65
- data/lib/kwatable/error-msg.rb +0 -38
- data/lib/kwatable/main-program.rb +0 -216
- data/lib/kwatable/templates/ddl-mysql.eruby +0 -172
- data/lib/kwatable/templates/dto-java.eruby +0 -260
- data/lib/kwatable/templates/dto-ruby.eruby +0 -185
@@ -0,0 +1,331 @@
|
|
1
|
+
###
|
2
|
+
### copyright(c) 2005 kuwata-lab.com all rights reserved.
|
3
|
+
### $Release: 0.3.0 $
|
4
|
+
### $Rev: 43 $
|
5
|
+
###
|
6
|
+
|
7
|
+
require 'yaml'
|
8
|
+
require 'erb'
|
9
|
+
require 'fileutils'
|
10
|
+
|
11
|
+
begin
|
12
|
+
require 'rubygems'
|
13
|
+
require_gem 'activesupport'
|
14
|
+
rescue LoadError => ex
|
15
|
+
# ignore
|
16
|
+
end
|
17
|
+
|
18
|
+
require 'kwatable'
|
19
|
+
require 'kwatable/util'
|
20
|
+
require 'kwatable/util/assertion'
|
21
|
+
require 'kwatable/validator'
|
22
|
+
|
23
|
+
|
24
|
+
module Kwatable
|
25
|
+
|
26
|
+
class CommandOptionError < KwatableError
|
27
|
+
end
|
28
|
+
|
29
|
+
class Main
|
30
|
+
include Assertion
|
31
|
+
|
32
|
+
def initialize(argv=ARGV)
|
33
|
+
@argv = argv
|
34
|
+
end
|
35
|
+
|
36
|
+
def execute()
|
37
|
+
options, properties, filenames = _parse_options(@argv)
|
38
|
+
|
39
|
+
## help or version
|
40
|
+
if options[?h] || options[?v]
|
41
|
+
puts _version() if options[?v]
|
42
|
+
return unless options[?h]
|
43
|
+
puts _template_info(options) if options[?t]
|
44
|
+
puts _usage() if !options[?t]
|
45
|
+
puts _available_template(options) if !options[?t]
|
46
|
+
return
|
47
|
+
end
|
48
|
+
|
49
|
+
## option check
|
50
|
+
if dir = options[?d]
|
51
|
+
#* key=:dir_notfound msg="-d %s: directory not found."
|
52
|
+
_option_error(:dir_notfound, dir) unless test(?e, dir)
|
53
|
+
#* key=:dir_notadir msg="-d %s: not a directory."
|
54
|
+
_option_error(:dir_notadir, dir) unless test(?d, dir)
|
55
|
+
end
|
56
|
+
|
57
|
+
## load and validate datafile
|
58
|
+
tabledefs = []
|
59
|
+
check_only = options[?c]
|
60
|
+
s = '' if check_only
|
61
|
+
filenames.each do |filename|
|
62
|
+
tabledef, content = _load_yaml(filename, options[?T])
|
63
|
+
tabledefs << tabledef
|
64
|
+
unless options[?u]
|
65
|
+
errors = Kwatable.validate(tabledef, content)
|
66
|
+
if !errors || errors.empty?
|
67
|
+
s << "#{filename}: valid.\n" if check_only
|
68
|
+
else
|
69
|
+
s << "#{filename}: INVALID!\n" if check_only
|
70
|
+
s2 = errors.sort.collect { |err|
|
71
|
+
"%s:%d: %s: %s" % [filename, err.linenum, err.path, err.message]
|
72
|
+
}.join("\n")
|
73
|
+
if check_only
|
74
|
+
s << s2 << "\n"
|
75
|
+
else
|
76
|
+
#* key=:tabledef_validation_error msg="schema validation error.\n%s"
|
77
|
+
_option_error(:tabledef_validation_error, s2)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
return s if check_only
|
83
|
+
|
84
|
+
## merge tabledefs
|
85
|
+
tabledef = tabledefs.shift
|
86
|
+
tabledefs.each do |tdef|
|
87
|
+
tabledef = _merge_tabledefs(tabledef, tdef)
|
88
|
+
end
|
89
|
+
|
90
|
+
## manipulation
|
91
|
+
manipulator = Manipulator.new()
|
92
|
+
manipulator.manipulate(tabledef)
|
93
|
+
$stderr.puts tabledef.to_yaml if options[?D]
|
94
|
+
|
95
|
+
## template filename
|
96
|
+
filename = options[?t]
|
97
|
+
unless filename
|
98
|
+
return nil if options[?D]
|
99
|
+
#* key=:template_notspecified msg="template is not specified."
|
100
|
+
_option_error(:template_notspecified)
|
101
|
+
end
|
102
|
+
template_pathlist = _template_pathlist(options[?I])
|
103
|
+
template_filename = _find_template(filename, template_pathlist)
|
104
|
+
|
105
|
+
## apply template
|
106
|
+
hash = { :options=>options, :properties=>properties,
|
107
|
+
:template_filename=>template_filename,
|
108
|
+
:template_pathlist=>template_pathlist,
|
109
|
+
}
|
110
|
+
hash[:tables] = tabledef['tables']
|
111
|
+
context = _create_context(hash)
|
112
|
+
output = Util.eval_template(template_filename, context)
|
113
|
+
output_files = context.instance_variable_get("@output_files")
|
114
|
+
if output_files
|
115
|
+
output_files.each do |filename, content, message_key|
|
116
|
+
_write_file(filename, content, message_key, options)
|
117
|
+
end
|
118
|
+
output = nil
|
119
|
+
end
|
120
|
+
return output
|
121
|
+
end
|
122
|
+
|
123
|
+
def self.main(argv=ARGV)
|
124
|
+
begin
|
125
|
+
main = Main.new(ARGV)
|
126
|
+
output = main.execute()
|
127
|
+
print output if output
|
128
|
+
rescue KwatableError => ex
|
129
|
+
$stderr.puts "[ERROR] #{ex.message()}"
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
private
|
134
|
+
|
135
|
+
def _write_file(filepath, content, message_key, options)
|
136
|
+
flag_quiet = options[?q]
|
137
|
+
#dir = options[?d]
|
138
|
+
#filepath = "#{dir}/#{filepath}" if dir
|
139
|
+
unless message_key == :identical
|
140
|
+
path = File.dirname(filepath)
|
141
|
+
FileUtils.mkdir_p(path) unless test(?d, path)
|
142
|
+
File.open(filepath, 'w') { |f| f.write(content) }
|
143
|
+
end
|
144
|
+
unless flag_quiet
|
145
|
+
message = _output_message(message_key)
|
146
|
+
$stderr.puts(message % filepath)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def _output_message(message_key)
|
151
|
+
unless @msg_table
|
152
|
+
table = {}
|
153
|
+
#* key=:file_create msg="create: %s"
|
154
|
+
table[:create] = Kwatable.msg(:file_create)
|
155
|
+
#* key=:file_update msg="update: %s"
|
156
|
+
table[:update] = Kwatable.msg(:file_update)
|
157
|
+
#* key=:file_delete msg="delete: %s"
|
158
|
+
table[:delete] = Kwatable.msg(:file_delete)
|
159
|
+
#* key=:file_identical msg="identical: %s"
|
160
|
+
table[:identical] = Kwatable.msg(:file_identical)
|
161
|
+
table[nil] = table[:create]
|
162
|
+
@msg_table = table
|
163
|
+
end
|
164
|
+
message = @msg_table[message_key]
|
165
|
+
assert! "message_key=#{message_key.inspect}"unless message
|
166
|
+
return message
|
167
|
+
end
|
168
|
+
|
169
|
+
def _load_yaml(filename, not_untabify)
|
170
|
+
#* key=:datafile_notfound msg="%s: datafile notfound."
|
171
|
+
_option_error(:datafile_notfound, filename) unless test(?e, filename)
|
172
|
+
#* key=:datafile_notafile msg="%s: not a file."
|
173
|
+
_option_error(:datafile_notafile, filename) unless test(?f, filename)
|
174
|
+
content = File.read(filename)
|
175
|
+
content = Util.untabify(content) unless not_untabify
|
176
|
+
ydoc = YAML.load(content)
|
177
|
+
#* key=:tabledef_empty msg="table definition file is empty."
|
178
|
+
_option_error(:tabledef_empty) unless ydoc
|
179
|
+
#* key=:tabledef_notmap msg="table definition is not a mapping."
|
180
|
+
_option_error(:tabledef_notmap) unless ydoc.is_a?(Hash)
|
181
|
+
return ydoc, content
|
182
|
+
end
|
183
|
+
|
184
|
+
def _merge_tabledefs(base_tabledef, other_tabledef)
|
185
|
+
base, other = base_tabledef, other_tabledef
|
186
|
+
return other unless base
|
187
|
+
other.each do |key, value|
|
188
|
+
if base.key?(key)
|
189
|
+
case v = base[key]
|
190
|
+
when Array ; v.concat(value)
|
191
|
+
when Hash ; v.update(value)
|
192
|
+
else ; base[key] = v
|
193
|
+
end
|
194
|
+
else
|
195
|
+
base[key] = value
|
196
|
+
end
|
197
|
+
end
|
198
|
+
return base
|
199
|
+
end
|
200
|
+
|
201
|
+
def _create_context(vars={})
|
202
|
+
context = Object.new
|
203
|
+
vars.each do |key, val|
|
204
|
+
context.instance_variable_set("@#{key.to_s}", val)
|
205
|
+
end
|
206
|
+
context.extend Assertion
|
207
|
+
return context
|
208
|
+
end
|
209
|
+
|
210
|
+
def _find_template(filename, pathlist)
|
211
|
+
filename += '.eruby' unless filename =~ /\.eruby$/
|
212
|
+
filepath = Util.find_file(filename, pathlist)
|
213
|
+
unless filepath
|
214
|
+
#* key=:template_notfound msg="`%s': template file not found."
|
215
|
+
_option_error(:template_notfound, filename)
|
216
|
+
end
|
217
|
+
return filepath
|
218
|
+
end
|
219
|
+
|
220
|
+
def _template_info(options)
|
221
|
+
filename = options[?t]
|
222
|
+
template_pathlist = _template_pathlist(options[?I])
|
223
|
+
template_filename = _find_template(filename, template_pathlist)
|
224
|
+
s = ''
|
225
|
+
s << "Filename: #{template_filename}\n"
|
226
|
+
content = File.read(template_filename)
|
227
|
+
if content =~ /<template-desc>(.*?)<\/template-desc>/m
|
228
|
+
s << "Description: #{$1}\n"
|
229
|
+
end
|
230
|
+
if content =~ /<template-properties>(.*?)<\/template-properties>/m
|
231
|
+
lines = $1.to_a
|
232
|
+
lines.shift; lines.pop
|
233
|
+
s << "Properties:\n"
|
234
|
+
s << lines.collect { |line| line.sub(/^[ \t]*\#+/, '') }.join
|
235
|
+
end
|
236
|
+
if content =~ /<template-details>(.*?)<\/template-details>/m
|
237
|
+
lines = $1.to_a
|
238
|
+
lines.shift; lines.pop
|
239
|
+
s << "Details:\n"
|
240
|
+
s << lines.collect { |line| line.sub(/^[ \t]*\#+/, '') }.join
|
241
|
+
end
|
242
|
+
return s
|
243
|
+
end
|
244
|
+
|
245
|
+
def _available_template(options)
|
246
|
+
path_list = _template_pathlist(options[?I])
|
247
|
+
filenames = path_list.collect { |path| Dir.glob("#{path}/*.eruby") }.flatten
|
248
|
+
filenames.delete_if { |filename| filename =~ /\.sub\.eruby$/ }
|
249
|
+
s = "Templates:\n"
|
250
|
+
filenames.each do |filename|
|
251
|
+
content = File.read(filename)
|
252
|
+
desc = (content =~ /<template-desc>(.*?)<\/template-desc>/) ? $1 : ''
|
253
|
+
name = File.basename(filename).sub(/\.eruby$/, '')
|
254
|
+
s << " %-18s: %s\n" % [name, desc]
|
255
|
+
end
|
256
|
+
return s
|
257
|
+
end
|
258
|
+
|
259
|
+
def _template_pathlist(option_I)
|
260
|
+
path_list = []
|
261
|
+
path_list.concat(option_I.split(/,/)) if option_I
|
262
|
+
path_list.concat(Kwatable.template_path)
|
263
|
+
return path_list
|
264
|
+
end
|
265
|
+
|
266
|
+
def _option_error(message_key, *args)
|
267
|
+
msg = Kwatable.msg(message_key)
|
268
|
+
assert! "massage not found. (message_key=#{message_key.inspect})" unless msg
|
269
|
+
err = CommandOptionError.new(msg % args)
|
270
|
+
err.set_backtrace(caller())
|
271
|
+
raise err
|
272
|
+
end
|
273
|
+
|
274
|
+
def _parse_options(argv)
|
275
|
+
begin
|
276
|
+
options, properties, filenames = Util.parse_argv(argv, "hvqcuD", "Iftd")
|
277
|
+
options[?h] = true if properties[:help]
|
278
|
+
options[?t] ||= options[?f] if options[?f]
|
279
|
+
return options, properties, filenames
|
280
|
+
rescue => ex
|
281
|
+
msg = ex.message
|
282
|
+
if msg =~ /\A-(.+?): unknown option.\z/
|
283
|
+
optchar = $1
|
284
|
+
#* key=:option_unknown msg="-%s: unknown option."
|
285
|
+
_option_error(:option_unknown, optchar)
|
286
|
+
elsif msg =~ /\A-(.+?): argument required.\z/
|
287
|
+
case optchar = $1
|
288
|
+
when 'f', 't'
|
289
|
+
#* key=:template_required msg="-%s: template filename required."
|
290
|
+
_option_error(:template_required, optchar)
|
291
|
+
when 'd'
|
292
|
+
#* key=:outdir_required msg="-%s: output directory required."
|
293
|
+
_option_error(:outdir_required, optchar)
|
294
|
+
when 'I'
|
295
|
+
#* key=:directory_required msg="-%s: directory required."
|
296
|
+
_option_error(:directory_required, optchar)
|
297
|
+
else
|
298
|
+
raise "*** internal error: optchar=#{optchar.inspect}"
|
299
|
+
end
|
300
|
+
else
|
301
|
+
raise "*** internal error: msg=#{msg.inspect}"
|
302
|
+
end
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
def _usage(command=nil)
|
307
|
+
command ||= File::basename($0)
|
308
|
+
s = ""
|
309
|
+
s << "Usage: #{command} [-hvmqcuT] [-I path] [-d dir] -t template datafile [datafile2 ...]\n"
|
310
|
+
s << " -h, --help : show help\n"
|
311
|
+
s << " -v : show version\n"
|
312
|
+
s << " -I path : template directory path\n"
|
313
|
+
s << " -t template : template filename\n"
|
314
|
+
#s << " -f template : template filename\n"
|
315
|
+
s << " -ht template : show template info(filename, desc, and properties)\n"
|
316
|
+
s << " -d dir : output file directory\n"
|
317
|
+
#s << " -m : multiple output file mode\n"
|
318
|
+
s << " -q : quiet mode\n"
|
319
|
+
s << " -c : validation only\n"
|
320
|
+
s << " -u : skip validation of datafile\n"
|
321
|
+
s << " -T : not expand tab characters in datafile\n"
|
322
|
+
return s
|
323
|
+
end
|
324
|
+
|
325
|
+
def _version()
|
326
|
+
return RELEASE
|
327
|
+
end
|
328
|
+
|
329
|
+
end
|
330
|
+
|
331
|
+
end
|
data/lib/kwatable/manipulator.rb
CHANGED
@@ -1,222 +1,350 @@
|
|
1
1
|
###
|
2
2
|
### copyright(c) 2005 kuwata-lab.com all rights reserved.
|
3
|
-
### $Release: 0.
|
4
|
-
### $Rev:
|
3
|
+
### $Release: 0.3.0 $
|
4
|
+
### $Rev: 44 $
|
5
5
|
###
|
6
6
|
|
7
7
|
require 'yaml'
|
8
|
+
require 'kwatable/util/assertion'
|
8
9
|
|
9
10
|
module Kwatable
|
10
11
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
12
|
+
class ManipulationError < KwatableError
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
##
|
17
|
+
## ex.
|
18
|
+
## tabledef = YAML.load_file('tabledef.yaml')
|
19
|
+
## manipulator = Kwatable::Manipulator.new
|
20
|
+
## tabledef = manipulator.manipulate(tabledef)
|
21
|
+
## p tabledef[:columns]
|
22
|
+
## p tabledef[:tables]
|
23
|
+
##
|
24
|
+
class Manipulator
|
25
|
+
include Assertion
|
26
|
+
|
27
|
+
def initialize(options={})
|
28
|
+
@overridable = options[:overridable]
|
29
|
+
end
|
30
|
+
|
31
|
+
#def parse(input)
|
32
|
+
# str = ''
|
33
|
+
# input.each_line do |line|
|
34
|
+
# str << line.gsub(/([^\t]{8})|([^\t]*)\t/n){[$+].pack("A8")} ## expand tab
|
35
|
+
# end
|
36
|
+
# tabledef = YAML.load(str)
|
37
|
+
# manipulate(tabledef)
|
38
|
+
# return tabledef
|
39
|
+
#end
|
40
|
+
|
41
|
+
def manipulate(tabledef)
|
42
|
+
#assert unless tabledef.is_a?(Hash)
|
43
|
+
return tabledef unless tabledef.is_a?(Hash)
|
44
|
+
|
45
|
+
column_map, patterned_columns = _manipulate_columns(tabledef['columns'])
|
46
|
+
#assert unless column_map.is_a?(Hash)
|
47
|
+
#assert unless patterned_columns.is_a?(Array)
|
48
|
+
|
49
|
+
table_map = _manipulate_tables(tabledef['tables'], column_map, patterned_columns)
|
50
|
+
#assert unless table_map.is_a?(Hash)
|
51
|
+
|
52
|
+
tabledef['column_map'] = column_map # Hash
|
53
|
+
tabledef['table_map'] = table_map # Hash
|
54
|
+
return tabledef
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def _error(message_key, *args)
|
60
|
+
msg = Kwatable.msg(message_key)
|
61
|
+
assert! "message_key=#{message_key}" unless msg
|
62
|
+
return ManipulationError.new(msg % args)
|
63
|
+
end
|
64
|
+
|
65
|
+
def _manipulate_columns(columns)
|
66
|
+
column_map = {}
|
67
|
+
patterned_columns = []
|
68
|
+
columns.each_with_index do |column, i|
|
69
|
+
name = column['name']
|
70
|
+
unless name
|
71
|
+
#* key=:colname_required msg="column name required (index=%d)."
|
72
|
+
raise _error(:colname_required, i)
|
73
|
+
end
|
74
|
+
if name =~ /\A\/(.*)\/\z/
|
75
|
+
pattern = $1
|
76
|
+
begin
|
77
|
+
#namepatetrn = eval(name)
|
78
|
+
namepattern = Regexp.compile(pattern)
|
79
|
+
rescue RegexpError => ex
|
80
|
+
#* key=:regexp_invalid msg="column '%s': pattern %s: %s"
|
81
|
+
raise _error(:regexp_invalid, column['name'], column['pattern'], ex.message)
|
82
|
+
end
|
83
|
+
column['namepattern'] = namepatntern
|
84
|
+
patterned_columns << column
|
85
|
+
else
|
86
|
+
if column_map.key?(name)
|
87
|
+
#* key=:coldef_duplicated msg="column '%s': column name is duplicated."
|
88
|
+
raise _error(:coldef_duplicated, column['name']) unless @overridable
|
89
|
+
end
|
90
|
+
column_map[name] = column
|
91
|
+
end
|
92
|
+
#name = column['name'].strip
|
93
|
+
#namepattern = column['namepattern']
|
94
|
+
#unless name || namepattern
|
95
|
+
# #* key=:colname_required msg="column definition doesn't have a name nor namepattern."
|
96
|
+
# raise ManipulationError.new(Kwatable.msg(:colname_required))
|
97
|
+
#end
|
98
|
+
#if name
|
99
|
+
# if column_map.key?(name)
|
100
|
+
# #* key=:coldef_duplicated msg="column definition `%s' is duplicated."
|
101
|
+
# raise ManipulationError.new(Kwatable.msg(:coldef_duplicated) % [name]) unless @overridable
|
102
|
+
# end
|
103
|
+
# column_map[name] = column
|
104
|
+
#end
|
105
|
+
#if namepattern
|
106
|
+
# pattern = namepattern.strip
|
107
|
+
# pattern = $1 if pattern =~ /^\/(.*)\/$/
|
108
|
+
# column['namepattern'] = Regexp.compile(pattern)
|
109
|
+
# patterned_columns << column
|
110
|
+
#end
|
111
|
+
end if columns
|
112
|
+
return column_map, patterned_columns
|
113
|
+
end
|
114
|
+
|
115
|
+
def _manipulate_tables(tables, column_map, patterned_columns)
|
116
|
+
#assert unless tables.is_a?(Array)
|
117
|
+
#assert unless column_map.is_a?(Hash)
|
118
|
+
#assert unless patterned_columns.is_a?(Array)
|
119
|
+
|
120
|
+
## create table_map
|
121
|
+
table_map = {}
|
122
|
+
tables.each_with_index do |table, i|
|
123
|
+
name = table['name']
|
124
|
+
unless name
|
125
|
+
#* key=:tablename_required msg="table definition doesn't have a name (index=%d)."
|
126
|
+
raise _error(:tablename_required, i)
|
127
|
+
end
|
128
|
+
if table_map.key?(name)
|
129
|
+
#* key=:tabledef_duplicated msg="table '%s': table name is duplicated."
|
130
|
+
raise _error(:tabledef_duplicated, name) unless @overridable
|
131
|
+
end
|
132
|
+
unless table['columns']
|
133
|
+
#* key=:tablecolumns_required msg="table '%s': columns requried."
|
134
|
+
raise _error(:tablecolumns_required, table['name'])
|
135
|
+
end
|
136
|
+
table_map[name] = table
|
49
137
|
end
|
50
138
|
|
51
|
-
|
139
|
+
## manipulate table columns
|
140
|
+
tables.each do |table|
|
141
|
+
_alias_table_key(table, 'ident-columns', 'primary-keys')
|
142
|
+
_manipulate_table_columns(table, table_map, column_map, patterned_columns)
|
143
|
+
end
|
52
144
|
|
53
|
-
|
54
|
-
|
55
|
-
|
145
|
+
## manipulate table column ref
|
146
|
+
tables.each do |table|
|
147
|
+
_manipulate_table_column_ref(table, table_map)
|
56
148
|
end
|
57
149
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
unless
|
64
|
-
|
65
|
-
|
66
|
-
end
|
67
|
-
if name =~ /\A\/(.*)\/\z/
|
68
|
-
pattern = $1
|
69
|
-
begin
|
70
|
-
namepattern = Regexp.compile(pattern)
|
71
|
-
rescue RegexpError => ex
|
72
|
-
#* key=:regexp_invalid msg="table '%s': column '%s': pattern %s: %s"
|
73
|
-
raise _error(:regexp_invalid, table['name'], column['name'], column['pattern'], ex.message)
|
74
|
-
end
|
75
|
-
column['namepattern'] = namepattern
|
76
|
-
patterned_columns << column
|
77
|
-
else
|
78
|
-
if column_map.key?(name)
|
79
|
-
#* key=:coldef_duplicated msg="table '%s': column '%s': column name is duplicated."
|
80
|
-
raise _error(:coldef_duplicated, table['name'], column['name'])
|
81
|
-
end
|
82
|
-
column_map[name] = column
|
150
|
+
## ident-columns (primary-keys)
|
151
|
+
tables.each do |table|
|
152
|
+
if table['ident-columns']
|
153
|
+
ident_columns = table['ident-columns'].collect do |colname|
|
154
|
+
column = table['columns'].find { |col| col['name'] == colname }
|
155
|
+
unless column
|
156
|
+
#* key=:table_identcol_missing msg="table '%s': ident-column '%s': no such column."
|
157
|
+
raise _error(:table_identcol_missing, table['name'], colname)
|
83
158
|
end
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
end if columns
|
104
|
-
return column_map, patterned_columns
|
159
|
+
column
|
160
|
+
end
|
161
|
+
table['ident-columns'] = table['primary-keys'] = ident_columns
|
162
|
+
end
|
163
|
+
## detect ident-column, and column type checking
|
164
|
+
ident_columns = []
|
165
|
+
table['columns'].each do |column|
|
166
|
+
ident_columns << column if column['ident']
|
167
|
+
unless column['type']
|
168
|
+
#* key=:tabletype_required msg="table '%s': column '%s': type is not determined.."
|
169
|
+
raise _error(:tabletype_required, table['name'], column['name'])
|
170
|
+
end
|
171
|
+
end
|
172
|
+
table['ident-columns'] ||= ident_columns
|
173
|
+
table['primary-keys'] ||= ident_columns
|
174
|
+
## check modeling
|
175
|
+
#if table['ident-columns'].length != 1 && table['modeling'] != false
|
176
|
+
# warn("table `%s': may be `modeling: no' ? " % [table['name']])
|
177
|
+
#end
|
105
178
|
end
|
106
179
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
#assert unless patterned_columns.is_a?(Array)
|
111
|
-
|
112
|
-
## create table_map
|
113
|
-
table_map = {}
|
114
|
-
tables.each_with_index do |table, i|
|
115
|
-
name = table['name']
|
116
|
-
unless name
|
117
|
-
#* key=:tablename_required msg="table definition doesn't have a name (index=%d)."
|
118
|
-
raise _error(:tablename_required, i)
|
119
|
-
end
|
120
|
-
if table_map.key?(name)
|
121
|
-
#* key=:tabledef_duplicated msg="table '%s': table name is duplicated."
|
122
|
-
raise _error(:tabledef_duplicated, name)
|
123
|
-
end
|
124
|
-
table_map[name] = table
|
125
|
-
end
|
126
|
-
|
127
|
-
## manipulate table columns
|
128
|
-
tables.each do |table|
|
129
|
-
colname_map = {}
|
130
|
-
table['columns'].each_with_index do |column, i|
|
131
|
-
column['table'] = table
|
132
|
-
colname = column['name']
|
133
|
-
unless colname
|
134
|
-
#* key=:tablecolumn_required msg="table '%s': column name requried (index=%d)."
|
135
|
-
raise _error(:tablecolumn_required, table['name'], i)
|
136
|
-
end
|
137
|
-
if colname_map[colname]
|
138
|
-
#* key=:tablecolumn_duplicated msg="table '%s': column '%s': column name is duplicated."
|
139
|
-
raise _error(:tablecolumn_duplicated, table['name'], colname)
|
140
|
-
end
|
141
|
-
colname_map[colname] = true
|
142
|
-
_set_defaults(column, column_map, patterned_columns)
|
143
|
-
#_alias_keys(column, "primary-key", "primarykey", "identifier")
|
144
|
-
#_alias_keys(column, "not-null", "notnull", "required")
|
145
|
-
_alias_key(column, table, "ident", "primary-key")
|
146
|
-
_alias_key(column, table, "required", "not-null")
|
147
|
-
_handle_ref(column, table, table_map) if column['ref']
|
148
|
-
_handle_enum(column) if column['enum']
|
149
|
-
unless column['type']
|
150
|
-
#* key=:tabletype_required msg="table `%s': column `%s': type is not determined."
|
151
|
-
raise _error(:tabletype_required, table['name'], column['name'])
|
152
|
-
end
|
153
|
-
end if table['columns']
|
154
|
-
end if tables
|
155
|
-
|
156
|
-
return table_map
|
180
|
+
## manipulate table relations
|
181
|
+
tables.each do |table|
|
182
|
+
_manipulate_table_relations(table, table_map) if table['relations']
|
157
183
|
end
|
158
184
|
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
185
|
+
return table_map
|
186
|
+
end
|
187
|
+
|
188
|
+
def _manipulate_table_columns(table, table_map, column_map, patterned_columns)
|
189
|
+
colname_map = {}
|
190
|
+
table['columns'].each_with_index do |column, i|
|
191
|
+
column['table'] = table
|
192
|
+
colname = column['name']
|
193
|
+
unless colname
|
194
|
+
#* key=:tablecolumn_required msg="table '%s': column name requried (index=%d)."
|
195
|
+
raise _error(:tablecolumn_required, table['name'], i)
|
196
|
+
end
|
197
|
+
if colname_map[colname]
|
198
|
+
#* key=:tablecolumn_duplicated msg="table '%s': column '%s': column name is duplicated."
|
199
|
+
raise _error(:tablecolumn_duplicated, table['name'], colname)
|
200
|
+
end
|
201
|
+
colname_map[colname] = true
|
202
|
+
_set_defaults(column, column_map, patterned_columns)
|
203
|
+
_alias_column_key(column, table, "ident", "primary-key")
|
204
|
+
_alias_column_key(column, table, "required", "not-null")
|
205
|
+
_manipulate_column_enum(column) if column['enum']
|
166
206
|
end
|
207
|
+
end
|
167
208
|
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
end if old_keys
|
209
|
+
def _manipulate_table_column_ref(table, table_map)
|
210
|
+
table['columns'].each do |column|
|
211
|
+
_manipulate_column_ref(column, table, table_map) if column['ref']
|
172
212
|
end
|
213
|
+
end
|
214
|
+
|
215
|
+
def _set_defaults(column, column_map, patterned_columns)
|
216
|
+
colname = column['name']
|
217
|
+
defaults = column_map[colname]
|
218
|
+
defaults ||= patterned_columns.find { |col| colname =~ col['namepattern'] }
|
219
|
+
defaults.each do |key, val|
|
220
|
+
column[key] = val if !column.key?(key) && key != 'namepattern'
|
221
|
+
end if defaults
|
222
|
+
end
|
173
223
|
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
end
|
179
|
-
column[key2] = column[key1] if column.key?(key1)
|
180
|
-
column[key1] = column[key2] if column.key?(key2)
|
224
|
+
def _alias_table_key(table, key1, key2)
|
225
|
+
if table.key?(key1) && table.key?(key2)
|
226
|
+
#* key=:alias_tablekey_conflict msg="table '%s': alias key '%1:' and '%2:' are not allowed to use in the same time."
|
227
|
+
raise _error(:alias_tablekey_conflict, table['name'], nil, key1, key2)
|
181
228
|
end
|
229
|
+
table[key2] = table[key1] if table.key?(key1)
|
230
|
+
table[key1] = table[key2] if table.key?(key2)
|
231
|
+
end
|
182
232
|
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
len = value.to_s.length
|
188
|
-
width = len if len > width
|
189
|
-
end
|
190
|
-
column['type'] ||= 'str'
|
191
|
-
column['width'] ||= width
|
233
|
+
def _alias_column_key(column, table, key1, key2)
|
234
|
+
if column.key?(key1) && column.key?(key2)
|
235
|
+
#* key=:alias_columnkey_conflict msg="table '%s', column '%s': alias key '%1:' and '%2:' are not allowed to use in the same time."
|
236
|
+
raise _error(:alias_columnkey_conflict, table['name'], column['name'], key1, key2)
|
192
237
|
end
|
238
|
+
column[key2] = column[key1] if column.key?(key1)
|
239
|
+
column[key1] = column[key2] if column.key?(key2)
|
240
|
+
end
|
193
241
|
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
ref_column_name = $2
|
201
|
-
ref_table = table_map[ref_table_name]
|
202
|
-
unless ref_table
|
203
|
-
#* key=:reftable_notfound msg="`table '%s': column '%s': 'ref: %s': reference table not found."
|
204
|
-
raise _error(:reftable_notfound, table['name'], column['name'], column['ref'])
|
205
|
-
end
|
206
|
-
cols = ref_table['columns']
|
207
|
-
ref_column = cols ? cols.find { |col| col['name'] == ref_column_name } : nil
|
208
|
-
unless ref_column
|
209
|
-
#* key=:refcolumn_notfound msg="`table '%s': column '%s': ref: %s': reference column not found in the table."
|
210
|
-
raise _error(:refcolumn_notfound, table['name'], column['name'], column['ref'])
|
211
|
-
end
|
212
|
-
#column['ref-table'] = ref_table
|
213
|
-
#column['ref-column'] = ref_column
|
214
|
-
column['ref'] = ref_column
|
215
|
-
column['ref-name'] ||= column['name'].sub(/_#{ref_column['name']}$/, '')
|
216
|
-
column['type'] = ref_column['type']
|
217
|
-
column['width'] ||= ref_column['width'] if ref_column.key?('width')
|
242
|
+
def _manipulate_column_enum(column)
|
243
|
+
return unless column['enum']
|
244
|
+
width = 0
|
245
|
+
column['enum'].each do |value|
|
246
|
+
len = value.to_s.length
|
247
|
+
width = len if len > width
|
218
248
|
end
|
249
|
+
column['type'] ||= 'str'
|
250
|
+
column['width'] ||= width
|
251
|
+
end
|
252
|
+
|
253
|
+
def _manipulate_column_ref(column, table, table_map)
|
254
|
+
ref = column['ref']
|
255
|
+
return unless ref
|
256
|
+
ref = ref.strip
|
257
|
+
return unless ref =~ /\A(\w+)\.(\w+)\z/ || ref =~ /\A(\w+)\((\w+)\)\z/
|
258
|
+
ref_table_name = $1
|
259
|
+
ref_column_name = $2
|
260
|
+
ref_table = table_map[ref_table_name]
|
261
|
+
unless ref_table
|
262
|
+
#* key=:reftable_notfound msg="`table '%s': column '%s': 'ref: %s': reference table not found."
|
263
|
+
raise _error(:reftable_notfound, table['name'], column['name'], column['ref'])
|
264
|
+
end
|
265
|
+
cols = ref_table['columns']
|
266
|
+
ref_column = cols ? cols.find { |col| col['name'] == ref_column_name } : nil
|
267
|
+
unless ref_column
|
268
|
+
#* key=:refcolumn_notfound msg="`table '%s': column '%s': ref: %s': reference column not found in the table."
|
269
|
+
raise _error(:refcolumn_notfound, table['name'], column['name'], column['ref'])
|
270
|
+
end
|
271
|
+
#column['ref-table'] = ref_table
|
272
|
+
#column['ref-column'] = ref_column
|
273
|
+
column['_ref'] = column['ref']
|
274
|
+
column['ref'] = ref_column
|
275
|
+
#column['ref-name'] ||= column['name'].sub(/_#{ref_column['name']}$/, '')
|
276
|
+
column['type'] = ref_column['type']
|
277
|
+
column['width'] ||= ref_column['width'] if ref_column.key?('width')
|
278
|
+
end
|
279
|
+
|
280
|
+
def _manipulate_table_relations(table, table_map)
|
281
|
+
return unless table['relations']
|
282
|
+
table['relations'].each do |relation|
|
283
|
+
unless relation['kind']
|
284
|
+
#* key=:relation_kind_required msg="table `%s': relation kind required."
|
285
|
+
raise _error(:relation_kind_required, table['name'])
|
286
|
+
end
|
287
|
+
unless relation['referrer']
|
288
|
+
#* key=:relation_referrer_required msg="table `%s': relation referrer required."
|
289
|
+
raise _error(:relation_referrer_required, table['name'])
|
290
|
+
end
|
291
|
+
referrer = relation['referrer']
|
292
|
+
unless referrer =~ /\A(\w+)\.(\w+)\z/ || referrer =~ /\A(\w+)\((\w+(?:,\s*\w+)*)\)\z/
|
293
|
+
#* key=:referrer_invalid msg="table `%s': referrer `%s': invalid pattern."
|
294
|
+
raise _error(:referrer_invalid, table['name'], referrer)
|
295
|
+
end
|
296
|
+
reftable_name, refcolumn_names = $1, $2
|
297
|
+
unless table_map[reftable_name]
|
298
|
+
#* key=:referrer_table_notfound msg="table `%s': referrer `%s': table not found."
|
299
|
+
raise _error(:referrer_table_notfound, table['name'], referrer)
|
300
|
+
end
|
301
|
+
reftable = table_map[reftable_name]
|
302
|
+
refcolumns = refcolumn_names.split(/,\s*/).collect do |refcolumn_name|
|
303
|
+
refcolumn = reftable['columns'].find { |col| col['name'] == refcolumn_name }
|
304
|
+
unless refcolumn
|
305
|
+
#* key=:referrer_column_notfound msg="table `%s': referrer `%s': column not found."
|
306
|
+
raise _error(:referrer_column_notfound, table['name'], referrer)
|
307
|
+
end
|
308
|
+
refcolumn
|
309
|
+
end
|
310
|
+
relation['table'] = reftable
|
311
|
+
relation['columns'] = refcolumns
|
312
|
+
#
|
313
|
+
if relation['kind'] == 'n:n'
|
314
|
+
unless relation['join-table']
|
315
|
+
#* key=:referrer_jointable_required msg="table `%s': referrer `%s': `join-table:' is required when n:n relationship."
|
316
|
+
raise _error(:referrer_jointable_required, table['name'], referrer)
|
317
|
+
end
|
318
|
+
join_table_name = relation['join-table']
|
319
|
+
join_table = table_map[join_table_name]
|
320
|
+
unless join_table
|
321
|
+
#* key=:referrer_jointable_notfound msg="table `%s': referrer `%s': join-table '%s' not found."
|
322
|
+
raise _error(:referrer_jointable_notfound, table['name'], referrer, join_table_name)
|
323
|
+
end
|
324
|
+
relation['join-table'] = join_table
|
325
|
+
ident_column = table['columns'].find { |col| col['ident'] }
|
326
|
+
unless ident_column
|
327
|
+
#* key=:referrer_identcolumn_notfound msg="table `%s': referrer `%s': table `%s' does not have ident column (it is required for have n:n relationship)."
|
328
|
+
raise _error(:referrer_identcolumn_notfound, table['name'], referrer, table['name'])
|
329
|
+
end
|
330
|
+
unless _find_ref_column(join_table, ident_column)
|
331
|
+
#* key=:referrer_joincolumn_notfound msg="table `%s': referrer `%s': join-table '%s' does not have column to link to %s table."
|
332
|
+
raise _error(:referrer_joincolumn_notfound, table['name'], referrer, join_table['name'], table['name'])
|
333
|
+
end
|
334
|
+
refcolumns.each do |refcolumn|
|
335
|
+
unless _find_ref_column(join_table, refcolumn)
|
336
|
+
#* key=:referrer_joincolumn2_notfound msg="table `%s': referrer `%s': join-table '%s' does not have column to link to table '%s'."
|
337
|
+
raise _error(:referrer_joincolumn2_notfound, table['name'], referrer, join_table['name'], reftable['name'])
|
338
|
+
end
|
339
|
+
end
|
340
|
+
end
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
344
|
+
def _find_ref_column(table, refcolumn)
|
345
|
+
return table['columns'].find { |col| col['ref'] && col['ref'].__id__ == refcolumn.__id__ }
|
346
|
+
end
|
219
347
|
|
220
|
-
|
348
|
+
end
|
221
349
|
|
222
350
|
end
|