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.
Files changed (67) hide show
  1. data/ChangeLog +46 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.txt +4 -2
  4. data/bin/kwatable +4 -4
  5. data/examples/ex1/Makefile +40 -14
  6. data/examples/ex1/{example1.yaml → tabledef.yaml} +42 -11
  7. data/examples/ex2/Makefile +41 -14
  8. data/examples/ex2/{example2.yaml → tabledef.yaml} +45 -30
  9. data/examples/ex3/Makefile +52 -0
  10. data/examples/ex3/tabledef.yaml +136 -0
  11. data/kwatable.gemspec +11 -10
  12. data/lib/kwatable.rb +24 -18
  13. data/lib/kwatable/kwatable.schema.yaml +95 -5
  14. data/lib/kwatable/main.rb +331 -0
  15. data/lib/kwatable/manipulator.rb +320 -192
  16. data/lib/kwatable/messages.rb +59 -0
  17. data/lib/kwatable/template/ddl-mysql.eruby +202 -0
  18. data/lib/kwatable/{templates → template}/ddl-postgresql.eruby +71 -45
  19. data/lib/kwatable/{templates → template}/defaults.yaml +2 -2
  20. data/lib/kwatable/template/dictionary.en.yaml +70 -0
  21. data/lib/kwatable/template/dictionary.ja.yaml +165 -0
  22. data/lib/kwatable/template/dto-java.eruby +77 -0
  23. data/lib/kwatable/template/dto-java.sub.eruby +259 -0
  24. data/lib/kwatable/template/dto-ruby.eruby +63 -0
  25. data/lib/kwatable/template/dto-ruby.sub.eruby +213 -0
  26. data/lib/kwatable/template/helper/column.rb +70 -0
  27. data/lib/kwatable/template/helper/common.rb +151 -0
  28. data/lib/kwatable/template/helper/java.rb +83 -0
  29. data/lib/kwatable/template/helper/label.rb +90 -0
  30. data/lib/kwatable/template/helper/ruby.rb +36 -0
  31. data/lib/kwatable/template/helper/table.rb +62 -0
  32. data/lib/kwatable/template/hibernate.eruby +139 -0
  33. data/lib/kwatable/template/rails-controller.eruby +66 -0
  34. data/lib/kwatable/template/rails-controller.sub.eruby +114 -0
  35. data/lib/kwatable/template/rails-kwartz.eruby +164 -0
  36. data/lib/kwatable/template/rails-kwartz/_attr.plogic.eruby +56 -0
  37. data/lib/kwatable/template/rails-kwartz/_form.plogic.eruby +81 -0
  38. data/lib/kwatable/template/rails-kwartz/_link.plogic.eruby +36 -0
  39. data/lib/kwatable/template/rails-kwartz/edit.cfg.yaml.eruby +16 -0
  40. data/lib/kwatable/template/rails-kwartz/edit.html.eruby +46 -0
  41. data/lib/kwatable/template/rails-kwartz/edit.plogic.eruby +20 -0
  42. data/lib/kwatable/template/rails-kwartz/layout.html.eruby +39 -0
  43. data/lib/kwatable/template/rails-kwartz/layout.plogic.eruby +32 -0
  44. data/lib/kwatable/template/rails-kwartz/list.html.eruby +94 -0
  45. data/lib/kwatable/template/rails-kwartz/list.plogic.eruby +41 -0
  46. data/lib/kwatable/template/rails-kwartz/new.html.eruby +100 -0
  47. data/lib/kwatable/template/rails-kwartz/new.plogic.eruby +26 -0
  48. data/lib/kwatable/template/rails-kwartz/show.html.eruby +51 -0
  49. data/lib/kwatable/template/rails-kwartz/show.plogic.eruby +9 -0
  50. data/lib/kwatable/template/rails-model.eruby +35 -0
  51. data/lib/kwatable/template/rails-model.sub.eruby +136 -0
  52. data/lib/kwatable/{templates → template}/validator-ruby.eruby +18 -11
  53. data/lib/kwatable/util.rb +133 -0
  54. data/lib/kwatable/util/assert-text-equal.rb +47 -0
  55. data/lib/kwatable/util/assertion.rb +115 -0
  56. data/lib/kwatable/validator.rb +50 -0
  57. data/test/assert-diff.rb +1 -1
  58. data/test/test-ex.rb +306 -0
  59. data/test/test.rb +37 -127
  60. metadata +66 -17
  61. data/COPYING +0 -340
  62. data/ChangeLog.txt +0 -65
  63. data/lib/kwatable/error-msg.rb +0 -38
  64. data/lib/kwatable/main-program.rb +0 -216
  65. data/lib/kwatable/templates/ddl-mysql.eruby +0 -172
  66. data/lib/kwatable/templates/dto-java.eruby +0 -260
  67. 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
@@ -1,222 +1,350 @@
1
1
  ###
2
2
  ### copyright(c) 2005 kuwata-lab.com all rights reserved.
3
- ### $Release: 0.2.0 $
4
- ### $Rev: 18 $
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
- class ManipulationError < KwatableError
12
- end
13
-
14
-
15
- ##
16
- ## ex.
17
- ## tabledef = YAML.load_file('tabledef.yaml')
18
- ## manipulator = Kwatable::Manipulator.new
19
- ## tabledef = manipulator.manipulate(tabledef)
20
- ## p tabledef[:columns]
21
- ## p tabledef[:tables]
22
- ##
23
- class Manipulator
24
-
25
- #def parse(input)
26
- # str = ''
27
- # input.each_line do |line|
28
- # str << line.gsub(/([^\t]{8})|([^\t]*)\t/n){[$+].pack("A8")} ## expand tab
29
- # end
30
- # tabledef = YAML.load(str)
31
- # manipulate(tabledef)
32
- # return tabledef
33
- #end
34
-
35
- def manipulate(tabledef)
36
- #assert unless tabledef.is_a?(Hash)
37
- return tabledef unless tabledef.is_a?(Hash)
38
-
39
- column_map, patterned_columns = _manipulate_columns(tabledef['columns'])
40
- #assert unless column_map.is_a?(Hash)
41
- #assert unless patterned_columns.is_a?(Array)
42
-
43
- table_map = _manipulate_tables(tabledef['tables'], column_map, patterned_columns)
44
- #assert unless table_map.is_a?(Hash)
45
-
46
- tabledef['column_map'] = column_map # Hash
47
- tabledef['table_map'] = table_map # Hash
48
- return tabledef
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
- private
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
- def _error(message_key, *args)
54
- msg = Kwatable.msg(message_key) % args
55
- return ManipulationError.new(msg)
145
+ ## manipulate table column ref
146
+ tables.each do |table|
147
+ _manipulate_table_column_ref(table, table_map)
56
148
  end
57
149
 
58
- def _manipulate_columns(columns)
59
- column_map = {}
60
- patterned_columns = []
61
- columns.each_with_index do |column, i|
62
- name = column['name']
63
- unless name
64
- #* key=:colname_required msg="table '%s': column name required (index=%d)."
65
- raise _error(:colname_required, table['name'], i)
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
- #name = column['name'].strip
85
- #namepattern = column['namepattern']
86
- #unless name || namepattern
87
- # #* key=:colname_required msg="column definition doesn't have a name nor namepattern."
88
- # raise ManipulationError.new(Kwatable.msg(:colname_required))
89
- #end
90
- #if name
91
- # if column_map.key?(name)
92
- # #* key=:coldef_duplicated msg="column definition `%s' is duplicated."
93
- # raise ManipulationError.new(Kwatable.msg(:coldef_duplicated) % [name])
94
- # end
95
- # column_map[name] = column
96
- #end
97
- #if namepattern
98
- # pattern = namepattern.strip
99
- # pattern = $1 if pattern =~ /^\/(.*)\/$/
100
- # column['namepattern'] = Regexp.compile(pattern)
101
- # patterned_columns << column
102
- #end
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
- def _manipulate_tables(tables, column_map, patterned_columns)
108
- #assert unless tables.is_a?(Array)
109
- #assert unless column_map.is_a?(Hash)
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
- def _set_defaults(column, column_map, patterned_columns)
160
- colname = column['name']
161
- defaults = column_map[colname]
162
- defaults ||= patterned_columns.find { |col| colname =~ col['namepattern'] }
163
- defaults.each do |key, val|
164
- column[key] = val if !column.key?(key) && key != 'namepattern'
165
- end if defaults
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
- def _alias_keys(column, key, *old_keys) # not used
169
- old_keys.each do |old_key|
170
- column[key] = column[old_key] if !column.key?(key) && column.key?(old_key)
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
- def _alias_key(column, table, key1, key2)
175
- if column.key?(key1) && column.key?(key2)
176
- #* key=:alias_conflict msg="table '%s', column '%s': alias key '%1:' and '%2:' are not allowed to use in the same time."
177
- raise _error(:alias_conflict, table['name'], column['name'], key1, key2)
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
- def _handle_enum(column)
184
- return unless column['enum']
185
- width = 0
186
- column['enum'].each do |value|
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
- def _handle_ref(column, table, table_map)
195
- ref = column['ref']
196
- return unless ref
197
- ref = ref.strip
198
- return unless ref =~ /\A(\w+)\.(\w+)\z/ || ref =~ /\A(\w+)\((\w+)\)\z/
199
- ref_table_name = $1
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
- end
348
+ end
221
349
 
222
350
  end