kwatable 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|