ezframe 0.0.4 → 0.4.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 (54) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -0
  3. data/README.md +1 -1
  4. data/asset/css/materialize.min.css +13 -0
  5. data/asset/css/style.css +3 -0
  6. data/asset/html/index.html +1 -0
  7. data/{app_template/asset/image/favicon.ico → asset/image/c_e.ico} +0 -0
  8. data/asset/js/ezframe.js +387 -0
  9. data/exe/check_column_yml +64 -0
  10. data/exe/console +2 -2
  11. data/exe/{create_table.rb → create_table} +7 -4
  12. data/exe/dbmigrate +174 -0
  13. data/exe/html2ruby +61 -0
  14. data/ezframe.gemspec +10 -8
  15. data/lib/ezframe.rb +9 -3
  16. data/lib/ezframe/auth.rb +50 -31
  17. data/lib/ezframe/column_set.rb +314 -103
  18. data/lib/ezframe/column_type.rb +456 -99
  19. data/lib/ezframe/config.rb +27 -6
  20. data/lib/ezframe/controller.rb +41 -38
  21. data/lib/ezframe/database.rb +171 -52
  22. data/lib/ezframe/editor_common.rb +74 -0
  23. data/lib/ezframe/email.rb +34 -0
  24. data/lib/ezframe/ezlog.rb +40 -0
  25. data/lib/ezframe/ht.rb +42 -17
  26. data/lib/ezframe/html.rb +47 -31
  27. data/lib/ezframe/japanese_utils.rb +15 -0
  28. data/lib/ezframe/jquery-ui.rb +29 -0
  29. data/lib/ezframe/loader.rb +4 -4
  30. data/lib/ezframe/main_editor.rb +19 -0
  31. data/lib/ezframe/main_page_kit.rb +226 -0
  32. data/lib/ezframe/materialize.rb +10 -14
  33. data/lib/ezframe/message.rb +46 -0
  34. data/lib/ezframe/page_base.rb +59 -71
  35. data/lib/ezframe/route.rb +126 -0
  36. data/lib/ezframe/server.rb +16 -5
  37. data/lib/ezframe/single_page_editor.rb +22 -0
  38. data/lib/ezframe/single_page_kit.rb +199 -0
  39. data/lib/ezframe/sub_editor.rb +25 -0
  40. data/lib/ezframe/sub_page_kit.rb +213 -0
  41. data/lib/ezframe/template.rb +5 -4
  42. data/lib/ezframe/util.rb +45 -23
  43. data/lib/ezframe/version.rb +1 -1
  44. metadata +74 -34
  45. data/.rubocop.yml +0 -44
  46. data/app_template/asset/js/ezframe.js +0 -288
  47. data/app_template/config.ru +0 -10
  48. data/app_template/config/generic.yml +0 -3
  49. data/app_template/config/materialize.yml +0 -5
  50. data/app_template/pages/basic.rb +0 -5
  51. data/exe/setup.rb +0 -15
  52. data/lib/ezframe/editor.rb +0 -188
  53. data/lib/ezframe/model.rb +0 -52
  54. data/lib/ezframe/page_kit.rb +0 -63
@@ -2,55 +2,192 @@
2
2
 
3
3
  module Ezframe
4
4
  class ColumnSets
5
- attr_accessor :tables, :model
6
-
7
- def initialize
8
- @tables = {}
9
- end
5
+ class << self
6
+ def init(dir = nil)
7
+ dir ||= "./column"
8
+ unless @colset_h
9
+ @colset_h = {}
10
+ load_files(dir)
11
+ end
12
+ end
10
13
 
11
- def load_files(dir)
12
- Dir["#{dir}/*.yml"].each do |filename|
13
- load_one_file(filename)
14
+ def load_files(dir)
15
+ Dir["#{dir}/*.yml"].each do |filename|
16
+ load_one_file(filename)
17
+ end
18
+ end
19
+
20
+ def load_one_file(filename)
21
+ colset_name = $1 if filename =~ /(\w+).ya?ml$/
22
+ yaml = YAML.load(File.open(filename), symbolize_names: true)
23
+ if yaml.length == 0
24
+ EzLog.error("[ERROR] columns file is empty: #{filename}")
25
+ return
26
+ end
27
+ column_info = yaml # .recursively_symbolize_keys
28
+ # puts "load_one_file: filename=#{filename} column_info=#{column_info.inspect}"
29
+ add(colset_name, column_info)
30
+ end
31
+
32
+ def add(colset_name, columns)
33
+ @colset_h[colset_name.to_sym] = cs = ColumnSet.new(parent: self, name: colset_name, columns: columns)
34
+ cs.set(columns)
35
+ return cs
36
+ end
37
+
38
+ def clone
39
+ @colset_h.deep_dup
40
+ end
41
+
42
+ def has_key?(key)
43
+ return nil unless key
44
+ return @colset_h[key.to_sym]
45
+ end
46
+
47
+ def get(colset_name)
48
+ return nil unless colset_name
49
+ return @colset_h[colset_name.to_sym].deep_dup
50
+ end
51
+
52
+ def refer(colset_name)
53
+ return nil unless colset_name
54
+ return @colset_h[colset_name.to_sym]
55
+ end
56
+
57
+ def [](colset_name)
58
+ return get(colset_name)
59
+ end
60
+
61
+ def each
62
+ @colset_h.each {|k, v| yield(k, v) }
63
+ end
64
+
65
+ def inspect
66
+ return @colset_h.map do |name, colset|
67
+ # "[#{name}]:#{colset.inspect}"
68
+ "[#{name}]:\n"
69
+ end.join
14
70
  end
15
- end
16
71
 
17
- def load_one_file(filename)
18
- table_name = $1 if filename =~ /(\w+).ya?ml$/
19
- begin
20
- yaml = YAML.load_file(filename)
21
- rescue
22
- mylog("YAML load error: #{filename}")
23
- return
72
+ def create_tables
73
+ self.each do |table_name, column_set|
74
+ begin
75
+ create_one_table(table_name, column_set)
76
+ rescue => e
77
+ EzLog.error("create_tables: #{e.inspect}\n#{$@.inspect}")
78
+ end
79
+ end
24
80
  end
25
- if yaml.length == 0
26
- mylog("[ERROR] columns file is empty: #{filename}")
27
- return
81
+
82
+ def create_one_table(table_name, column_set)
83
+ col_h = column_set.get_hash(:db_type)
84
+ EzLog.info "create_one_table: col_h=#{col_h.inspect}"
85
+ DB.create_table(table_name, col_h)
86
+ end
87
+
88
+ # foreignから生成したテーブル連結情報を返す
89
+ def full_join_structure(colset_id)
90
+ struct = { tables: [colset_id] }
91
+ colset = @colset_h[colset_id.to_sym]
92
+ colset_keys = colset.keys
93
+ struct[:column_list] = colset_keys.map { |k| "#{colset_id}.#{k}" }
94
+ join_cond_h = {}
95
+ colset_keys.each do |key|
96
+ column = colset[key]
97
+ if column.type.to_s == "foreign"
98
+ # 連結するテーブル名をtable: で指定する。
99
+ foreign_table = column.attribute[:table]
100
+ # 指定されてなければ、キーの名称をテーブル名とする
101
+ # そのテーブルが定義されてなければ、エラーとしてログに残す。
102
+ unless foreign_table
103
+ if @colset_h[key]
104
+ foreign_table = key
105
+ else
106
+ EzLog.error "There is no related table: #{key}"
107
+ next
108
+ end
109
+ end
110
+ raise "no table: key=#{key}" unless foreign_table
111
+ foreign_column = column.attribute[:column]&.to_sym || :id
112
+ foreign_table = foreign_table.to_sym
113
+ next if struct[:tables].include?(foreign_table)
114
+ # join_cond_h["#{colset_id}.#{key}"] = "#{colset_id}.#{key} = #{foreign_table}.#{foreign_column}"
115
+ join_cond_h[foreign_table] = "#{colset_id}.#{key} = #{foreign_table}.#{foreign_column}"
116
+ struct[:tables].push(foreign_table)
117
+ struct[:column_list] += ColumnSets.refer(foreign_table).keys.map {|k| "#{foreign_table}.#{k}" }
118
+ end
119
+ end
120
+ struct[:join_condition] = join_cond_h
121
+ return struct
122
+ end
123
+
124
+ def join_complex_column
125
+
28
126
  end
29
- column_info = yaml.recursively_symbolize_keys
30
- puts "load_one_file: filename=#{filename} column_info=#{column_info.inspect}"
31
- add(table_name, column_info)
32
127
  end
128
+ end
33
129
 
34
- def add(table_name, columns)
35
- @tables[table_name.to_sym] = tb = ColumnSet.new(parent: self, name: table_name, columns: columns)
36
- tb.set(columns)
130
+ # ColumnSetを複数組み合わせて扱う
131
+ class ColumnSetCollection
132
+ attr_accessor :colset_list
133
+
134
+ def initialize(default_table=nil)
135
+ @colset_h = {}
136
+ @default_table = default_table
37
137
  end
38
138
 
39
- def [](table_name)
40
- return @tables[table_name]
139
+ def values=(data)
140
+ @colset_h.each {|key, colset| colset.clear }
141
+ set_values(data)
41
142
  end
42
143
 
43
- def each
44
- @tables.each {|k, v| yield(k, v) }
144
+ def set_values(data)
145
+ data.each do |key, value|
146
+ if key.to_s.index(".")
147
+ table_key, col_key = key.to_s.split(".")
148
+ colset = @colset_h[table_key.to_sym]
149
+ unless colset
150
+ @colset_h[table_key.to_sym] = colset = ColumnSets[table_key]
151
+ end
152
+ elsif @default_table
153
+ col_key = key
154
+ colset = @colset_h[@default_table.to_sym]
155
+ unless colset
156
+ @colset_h[table_key.to_sym] = colset = ColumnSets[@default_table]
157
+ end
158
+ end
159
+ colset[col_key].value = value
160
+ end
161
+ end
162
+
163
+ def get(colset_key, col_key=nil)
164
+ if col_key.nil?
165
+ if colset_key.to_s.index(".")
166
+ colset_key, col_key = colset_key.to_s.split(".")
167
+ elsif @default_table
168
+ colset_key, col_key = @default_table, colset_key
169
+ else
170
+ EzLog.error "ColumnSetCollection.get: illegal arguments: #{colset_key}, #{col_key}"
171
+ return nil
172
+ end
173
+ end
174
+ colset = @colset_h[colset_key.to_sym]
175
+ return nil unless colset
176
+ # EzLog.debug("Collection.get: colset_key=#{colset_key}, col_key=#{col_key}, value=#{colset[col_key].value}")
177
+ return colset[col_key]
178
+ end
179
+
180
+ def [](k)
181
+ return get(k)
45
182
  end
46
183
  end
47
184
 
48
185
  class ColumnSet
49
- attr_accessor :name, :parent, :edit_keys, :view_keys
186
+ attr_accessor :name, :parent # , :edit_keys, :view_keys
50
187
 
51
- def initialize(parent:, name: nil, columns: nil)
188
+ def initialize(parent: nil, name: nil, columns: nil)
52
189
  @parent = parent
53
- @name = name
190
+ @name = name
54
191
  @columns ||= {}
55
192
  set(columns) if columns
56
193
  end
@@ -61,10 +198,23 @@ module Ezframe
61
198
  end
62
199
  end
63
200
 
201
+ def keys
202
+ @columns.keys
203
+ end
204
+
205
+ def edit_keys
206
+ @columns.keys.select {|k| !@columns[k].no_edit? }
207
+ end
208
+
209
+ def view_keys
210
+ @columns.keys.select {|k| !@columns[k].no_view? }
211
+ end
212
+
213
+ # 配列を初期化する
64
214
  def set(attr_a)
65
- @columns[:id] = IdType.new(key: "id", label: "ID", no_edit: true)
66
- attr_a.each do |attributes|
67
- attr = attributes.clone
215
+ @columns[:id] = IdType.new(key: "id", label: "ID", hidden: true)
216
+ attr_a.each do |attribute|
217
+ attr = attribute.clone
68
218
  col_key = attr[:key]
69
219
  raise "no column key: #{attr.inspect}" unless col_key
70
220
  klass = TypeBase.get_class(attr[:type])
@@ -74,97 +224,147 @@ module Ezframe
74
224
  @columns[col_key.to_sym] = klass.new(attr)
75
225
  end
76
226
  end
77
- @columns[:created_at] = DatetimeType.new(type: "datetime", key: "created_at", label: "生成日時", no_edit: true)
78
- @columns[:updated_at] = DatetimeType.new(type: "datetime", key: "updated_at", label: "更新日時", no_edit: true)
79
- # mylog "set: #{@columns.inspect}"
80
- @columns.values.each {|col| col.parent = self }
227
+ @columns[:created_at] = DatetimeType.new(type: "datetime", key: "created_at", label: "生成日時", hidden: true)
228
+ @columns[:updated_at] = DatetimeType.new(type: "datetime", key: "updated_at", label: "更新日時", hidden: true)
229
+ @columns[:deleted_at] = DatetimeType.new(type: "datetime", key: "deleted_at", label: "削除日時", hidden: true)
230
+ @columns.values.each { |col| col.parent = self }
81
231
  return @columns
82
232
  end
83
233
 
84
234
  def dataset
85
- # puts "dataset: #{@model.inspect}"
86
- return @parent.model.db.dataset(@name)
235
+ return DB.dataset(@name)
87
236
  end
88
237
 
89
238
  def set_from_db(id)
90
239
  data = dataset.where(id: id).first
91
240
  return nil unless data
92
- self.values = data
241
+ self.set_values(data, from_db: true)
93
242
  return data
94
243
  end
95
244
 
96
- def save
97
- col_h = get_hash(:value)
98
- col_h.delete(:id)
99
- col_h.delete(:created_at)
100
- col_h[:updated_at] = Time.now
101
- mylog "save: #{col_h.inspect}"
102
- id = @columns[:id]
103
- if id.value.to_i > 0
104
- dataset.where(id: id.value).update(col_h)
245
+ def set_from_form(form, key_suffix: nil)
246
+ self.set_values(form)
247
+ end
248
+
249
+ # データベースに新規に値を登録する
250
+ def create(value_h, from_db: nil, key_suffix: nil)
251
+ if from_db
252
+ self.set_values(value_h, from_db: true, key_suffix: key_suffix)
105
253
  else
106
- return dataset.insert(col_h)
254
+ self.set_values(value_h, key_suffix: key_suffix)
107
255
  end
256
+ db_value_h = self.get_hash(:db_value)
257
+ EzLog.debug("column_set.create: #{db_value_h}")
258
+ db_value_h.delete(:id)
259
+ db_value_h[:updated_at] = Time.now
260
+ db_value_h[:created_at] = Time.now
261
+ EzLog.debug("create: sql=#{dataset.insert_sql(db_value_h)}")
262
+ return dataset.insert(db_value_h)
108
263
  end
109
264
 
265
+ # データベース上の値の更新
110
266
  def update(id, value_h)
111
- values = {}
112
- colkeys = @columns.keys
113
- value_h.each do |k, v|
114
- values[k] = v if colkeys.include?(k)
267
+ self.set_from_db(id)
268
+ updated_values = {}
269
+ @columns.each do |colkey, column|
270
+ next if column.no_edit?
271
+ if column.respond_to?(:form_to_value)
272
+ new_value = column.form_to_value(value_h)
273
+ else
274
+ new_value = value_h[colkey]
275
+ end
276
+ prev_value = column.db_value
277
+ column.value = new_value
278
+ # EzLog.debug("key=#{colkey}, pre_value=#{prev_value}, new_value=#{column.db_value}")
279
+ if column.respond_to?("value_equal?")
280
+ unless column.value_equal?(prev_value, column.db_value)
281
+ updated_values[colkey] = column.db_value
282
+ end
283
+ elsif prev_value != column.db_value
284
+ updated_values[colkey] = column.db_value
285
+ end
286
+ end
287
+ if updated_values.length > 0
288
+ updated_values[:updated_at] = Time.now
289
+ sql = dataset.where(id: id).update_sql(updated_values)
290
+ EzLog.debug("update: sql=#{sql}")
291
+ dataset.where(id: id).update(updated_values)
115
292
  end
116
- dataset.where(id: id).update(values)
117
- set_values(values)
118
293
  end
119
294
 
120
- def values=(value_h)
121
- clear
122
- set_values(value_h)
295
+ # 各カラムに値を格納する
296
+ def set_values(value_h, from_db: nil, key_suffix: nil)
297
+ self.clear
298
+ merge_values(value_h, from_db: from_db, key_suffix: key_suffix)
123
299
  end
124
300
 
125
- def set_values(value_h)
126
- return unless value_h
127
- value_h.each do |k, v|
128
- col = @columns[k.to_sym]
129
- next unless col
130
- col.value = v
301
+ def merge_values(value_h, from_db: nil, key_suffix: nil)
302
+ return self unless value_h
303
+ @columns.keys.each do |key|
304
+ next if key.to_s.empty?
305
+ target_key = key
306
+ target_key = "#{key}#{key_suffix}" if key_suffix
307
+ column = @columns[key.to_sym]
308
+ if !from_db && column.respond_to?(:form_to_value) # && !value_h.has_key?(key)
309
+ val = column.form_to_value(value_h, target_key: target_key)
310
+ else
311
+ val = value_h[target_key.to_sym] || value_h[key]
312
+ end
313
+ column.value = val
131
314
  end
315
+ return self
316
+ end
317
+
318
+ def values=(value_h)
319
+ set_values(value_h)
132
320
  end
133
321
 
134
- def validate
322
+ # 各カラムのバリデーション
323
+ # 戻り値は[ 正規化した値, エラーシンボル(Messageのキーと紐づく) ]を値として、
324
+ # カラムキーをキーとするハッシュ
325
+ def validate(value_h)
326
+ return {} unless value_h
135
327
  clear_error
136
- errors = []
137
- @columns.values.each do |col|
138
- err = col.validate
139
- errors.push([ col.key, err ]) if err
328
+ result_h = {}
329
+ @columns.values.each do |col|
330
+ res = []
331
+ if col.respond_to?(:form_to_value) && !value_h.has_key?(col.key)
332
+ orig_val = col.form_to_value(value_h)
333
+ else
334
+ orig_val = value_h[col.key]
335
+ end
336
+ new_val = col.normalize(orig_val)
337
+ res[0] = new_val if orig_val != new_val
338
+ res[1] = col.validate(new_val)
339
+ result_h[col.key] = res if res[0] || res[1]
140
340
  end
141
- return errors
341
+ return result_h
142
342
  end
143
343
 
144
344
  def clear_error
145
- @columns.values.each {|col| col.error = nil }
345
+ @columns.values.each { |col| col.error = nil }
146
346
  end
147
347
 
148
348
  def values
149
- @columns.map {|key, col| col.value}
150
- end
349
+ @columns.map { |key, col| col.value }
350
+ end
151
351
 
152
352
  def each
153
- @columns.values.each {|column| yield(column) }
353
+ @columns.values.each { |column| yield(column) }
154
354
  end
155
355
 
156
356
  def map
157
- @columns.values.map {|column| yield(column) }
357
+ @columns.values.map { |column| yield(column) }
158
358
  end
159
359
 
160
360
  def get_matrix(method_a)
161
361
  return @columns.map do |_key, col|
162
- method_a.map { |method| col.send(method) }
163
- end
362
+ method_a.map { |method| col.send(method) }
363
+ end
164
364
  end
165
365
 
166
366
  def get_hash(method)
167
- res_h = {}
367
+ res_h = {}
168
368
  @columns.map do |key, col|
169
369
  res_h[key.to_sym] = col.send(method)
170
370
  end
@@ -174,41 +374,52 @@ module Ezframe
174
374
  def [](col_key)
175
375
  return @columns[col_key.to_sym]
176
376
  end
177
-
377
+
178
378
  def form
179
379
  if @edit_keys
180
- return @edit_keys.map do |key|
181
- col = @columns[key.to_sym]
182
- unless col
183
- mylog "[ERROR] @edit_keys has unknown column:name=#{@name}:key=#{key}"
184
- next
185
- end
186
- col.form
187
- end
380
+ return @edit_keys.map do |key|
381
+ col = @columns[key.to_sym]
382
+ unless col
383
+ EzLog.info "[ERROR] @edit_keys has unknown column:name=#{@name}:key=#{key}"
384
+ next
385
+ end
386
+ col.form
387
+ end
188
388
  else
189
- return @columns.values.map {|coltype| coltype.form }
190
- end
389
+ return @columns.values.map { |coltype| coltype.form }
390
+ end
191
391
  end
192
392
 
193
393
  def view
194
394
  if @view_keys
195
- return @view_keys.map do |key|
196
- col = @columns[key.to_sym]
197
- unless col
198
- mylog "[ERROR] @view_keys has unknown column:name=#{@name}:key=#{key}"
199
- next
200
- end
201
- col.view
202
- end
395
+ return @view_keys.map do |key|
396
+ col = @columns[key.to_sym]
397
+ unless col
398
+ EzLog.info "[ERROR] @view_keys has unknown column:name=#{@name}:key=#{key}"
399
+ next
400
+ end
401
+ col.view
402
+ end
203
403
  else
204
- return @columns.values.map {|coltype| coltype.view }
404
+ return @columns.values.map { |coltype| coltype.view }
205
405
  end
206
406
  end
207
407
 
408
+ def get_full_join(opts = {})
409
+ struct = ColumnSets.full_join_structure(self.name)
410
+ return DB.get_join_table(struct, opts)
411
+ end
412
+
208
413
  def hidden_form
209
414
  return @columns.map do |colkey, coltype|
210
- { tag: 'input', id: colkey, name: colkey, type: 'hidden', value: coltype.value }
211
- end
415
+ { tag: "input", id: colkey, name: colkey, type: "hidden", value: coltype.value }
416
+ end
417
+ end
418
+
419
+ def inpsect
420
+ @columns.map do |colkey, coltype|
421
+ "#{colkey}=#{coltype.value}"
422
+ end.join(" ")
212
423
  end
213
424
  end
214
425
  end