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.
- checksums.yaml +4 -4
- data/Gemfile +2 -0
- data/README.md +1 -1
- data/asset/css/materialize.min.css +13 -0
- data/asset/css/style.css +3 -0
- data/asset/html/index.html +1 -0
- data/{app_template/asset/image/favicon.ico → asset/image/c_e.ico} +0 -0
- data/asset/js/ezframe.js +387 -0
- data/exe/check_column_yml +64 -0
- data/exe/console +2 -2
- data/exe/{create_table.rb → create_table} +7 -4
- data/exe/dbmigrate +174 -0
- data/exe/html2ruby +61 -0
- data/ezframe.gemspec +10 -8
- data/lib/ezframe.rb +9 -3
- data/lib/ezframe/auth.rb +50 -31
- data/lib/ezframe/column_set.rb +314 -103
- data/lib/ezframe/column_type.rb +456 -99
- data/lib/ezframe/config.rb +27 -6
- data/lib/ezframe/controller.rb +41 -38
- data/lib/ezframe/database.rb +171 -52
- data/lib/ezframe/editor_common.rb +74 -0
- data/lib/ezframe/email.rb +34 -0
- data/lib/ezframe/ezlog.rb +40 -0
- data/lib/ezframe/ht.rb +42 -17
- data/lib/ezframe/html.rb +47 -31
- data/lib/ezframe/japanese_utils.rb +15 -0
- data/lib/ezframe/jquery-ui.rb +29 -0
- data/lib/ezframe/loader.rb +4 -4
- data/lib/ezframe/main_editor.rb +19 -0
- data/lib/ezframe/main_page_kit.rb +226 -0
- data/lib/ezframe/materialize.rb +10 -14
- data/lib/ezframe/message.rb +46 -0
- data/lib/ezframe/page_base.rb +59 -71
- data/lib/ezframe/route.rb +126 -0
- data/lib/ezframe/server.rb +16 -5
- data/lib/ezframe/single_page_editor.rb +22 -0
- data/lib/ezframe/single_page_kit.rb +199 -0
- data/lib/ezframe/sub_editor.rb +25 -0
- data/lib/ezframe/sub_page_kit.rb +213 -0
- data/lib/ezframe/template.rb +5 -4
- data/lib/ezframe/util.rb +45 -23
- data/lib/ezframe/version.rb +1 -1
- metadata +74 -34
- data/.rubocop.yml +0 -44
- data/app_template/asset/js/ezframe.js +0 -288
- data/app_template/config.ru +0 -10
- data/app_template/config/generic.yml +0 -3
- data/app_template/config/materialize.yml +0 -5
- data/app_template/pages/basic.rb +0 -5
- data/exe/setup.rb +0 -15
- data/lib/ezframe/editor.rb +0 -188
- data/lib/ezframe/model.rb +0 -52
- data/lib/ezframe/page_kit.rb +0 -63
data/lib/ezframe/config.rb
CHANGED
@@ -3,23 +3,40 @@ module Ezframe
|
|
3
3
|
class << self
|
4
4
|
attr_accessor :value_h
|
5
5
|
|
6
|
+
def init(dir = "./config")
|
7
|
+
load_files(dir)
|
8
|
+
end
|
9
|
+
|
6
10
|
def load_files(dir)
|
7
11
|
unless @value_h
|
8
|
-
|
9
|
-
|
12
|
+
load_dir(dir)
|
13
|
+
rack_env = ENV['RACK_ENV']
|
14
|
+
env_dir = "#{dir}/#{rack_env}"
|
15
|
+
if rack_env && File.directory?(env_dir)
|
16
|
+
load_dir(env_dir)
|
10
17
|
end
|
11
18
|
end
|
12
19
|
end
|
13
20
|
|
21
|
+
def load_dir(dir)
|
22
|
+
Dir["#{dir}/*.yml"].each do |file|
|
23
|
+
load_one_file(file)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
14
27
|
def load_one_file(filename)
|
28
|
+
instr = File.open(filename, &:read)
|
29
|
+
if instr.index("\#{")
|
30
|
+
instr = Template.fill_in_text(instr)
|
31
|
+
end
|
15
32
|
begin
|
16
|
-
yaml = YAML.
|
17
|
-
rescue
|
18
|
-
|
33
|
+
yaml = YAML.load(instr, symbolize_names: true)
|
34
|
+
rescue => e
|
35
|
+
EzLog.info("YAML load error: #{filename}:#{e}")
|
19
36
|
return
|
20
37
|
end
|
21
38
|
@value_h ||={}
|
22
|
-
@value_h.update(yaml
|
39
|
+
@value_h.update(yaml) if yaml.length>0 # .recursively_symbolize_keys
|
23
40
|
end
|
24
41
|
|
25
42
|
def [](k)
|
@@ -31,6 +48,10 @@ module Ezframe
|
|
31
48
|
@value_h[k]=v
|
32
49
|
end
|
33
50
|
|
51
|
+
def delete(k)
|
52
|
+
@value_h.delete(k) if @value_h[k]
|
53
|
+
end
|
54
|
+
|
34
55
|
def inspect
|
35
56
|
@value_h.inspect
|
36
57
|
end
|
data/lib/ezframe/controller.rb
CHANGED
@@ -1,60 +1,63 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
3
2
|
module Ezframe
|
4
|
-
class
|
3
|
+
class Controller
|
5
4
|
class << self
|
5
|
+
def init
|
6
|
+
Config.init
|
7
|
+
ColumnSets.init
|
8
|
+
DB.init
|
9
|
+
Message.init
|
10
|
+
Auth.init if Config[:auth]
|
11
|
+
end
|
12
|
+
|
6
13
|
def exec(request, response)
|
7
14
|
@request = request
|
8
|
-
|
9
|
-
Model.init
|
10
|
-
model = Model.get_clone
|
11
|
-
Auth.init_warden if defined?(Warden)
|
12
|
-
@request.env["model"] = model
|
15
|
+
page_instance, method, url_params, class_opts = Route::choose(request)
|
13
16
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
response.status = 404
|
18
|
-
response['Content-Type'] = 'text/html; charset=utf-8'
|
19
|
-
response.body = [ Html.convert(Ht.p("file not found")) ]
|
17
|
+
EzLog.debug("Controller.exec: path=#{request.path_info}, params=#{request.params}, class=#{page_instance.class}, method=#{method}, url_params=#{url_params}, class_opts=#{class_opts}")
|
18
|
+
if !page_instance || page_instance == 404
|
19
|
+
file_not_found(response)
|
20
20
|
return
|
21
21
|
end
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
method_full_name = "public_#{method}_page"
|
28
|
-
end
|
29
|
-
if page.auth
|
22
|
+
@request.env["url_params"] = url_params
|
23
|
+
opt_auth = class_opts[:auth]
|
24
|
+
@session = @request.env['rack.session']
|
25
|
+
if !@session[:user] && Config[:auth] && (!opt_auth || opt_auth != "disable")
|
26
|
+
EzLog.debug("authenticate!")
|
30
27
|
warden.authenticate!
|
28
|
+
EzLog.info "Controller.exec: warden.options = #{@request.env['warden.options']}"
|
31
29
|
end
|
32
|
-
#
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
body = if page.respond_to?(method_full_name)
|
39
|
-
page.send(method_full_name)
|
40
|
-
else
|
41
|
-
mylog "no such method: #{method_full_name}"
|
42
|
-
page.public_default_page
|
43
|
-
end
|
30
|
+
# session["in_controller"] = "set in controller"
|
31
|
+
EzLog.debug "rack.session.keys=#{@session.keys}" if @session
|
32
|
+
page_instance.set_request(@request)
|
33
|
+
body = page_instance.send(method)
|
34
|
+
|
35
|
+
# 戻り値によるレスポンス生成
|
44
36
|
if body.is_a?(Hash) || body.is_a?(Array)
|
45
|
-
|
37
|
+
# EzLog.debug("Controller: body = #{body}")
|
38
|
+
json = JSON.generate(body)
|
39
|
+
response.body = [ json ]
|
46
40
|
response['Content-Type'] = 'application/json; charset=utf-8'
|
47
41
|
else
|
48
42
|
response.body = [ body ]
|
49
43
|
response['Content-Type'] = 'text/html; charset=utf-8'
|
50
44
|
end
|
51
45
|
response.status = 200
|
46
|
+
# EzLog.debug("Controller.exec: response.body=#{response.body}")
|
52
47
|
end
|
53
48
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
#
|
49
|
+
def file_not_found(response)
|
50
|
+
response.status = 404
|
51
|
+
response['Content-Type'] = 'text/html; charset=utf-8'
|
52
|
+
template_file = ("#{Config[:template_dir]}/404.html")
|
53
|
+
# puts template_file
|
54
|
+
if File.exist?(template_file)
|
55
|
+
body = File.read(template_file)
|
56
|
+
else
|
57
|
+
body = Html.convert(Ht.p("file not found"))
|
58
|
+
end
|
59
|
+
response.body = [ body ]
|
60
|
+
end
|
58
61
|
|
59
62
|
def warden
|
60
63
|
@request.env["warden"]
|
data/lib/ezframe/database.rb
CHANGED
@@ -1,62 +1,181 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
require "logger"
|
3
|
-
|
4
2
|
module Ezframe
|
5
|
-
class
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
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
|
-
|
3
|
+
class DB
|
4
|
+
class << self
|
5
|
+
attr_accessor :sequel, :pool
|
6
|
+
|
7
|
+
def init(dbfile = nil, opts = {})
|
8
|
+
@dbfile = dbfile || ENV["EZFRAME_DB"] || Config[:database]
|
9
|
+
unless @dbfile
|
10
|
+
raise "database settings error: dbfile=#{Config[:database]}"
|
11
|
+
end
|
12
|
+
#if Config[:use_connection_pool] || opts[:use_connection_pool]
|
13
|
+
#@pool = Sequel::ConnectionPool.new(max_connections: 10) do
|
14
|
+
# Sequel.connect(@dbfile, loggers: [EzLog])
|
15
|
+
#end
|
16
|
+
#else
|
17
|
+
connect(@dbfile)
|
18
|
+
#end
|
19
|
+
end
|
20
|
+
|
21
|
+
def connect(dbfile = nil)
|
22
|
+
dbfile ||= @dbfile
|
23
|
+
@sequel = Sequel.connect(dbfile, EzLogs: [EzLog])
|
24
|
+
return @sequel
|
25
|
+
end
|
26
|
+
|
27
|
+
def disconnect
|
28
|
+
@sequel.disconnect
|
29
|
+
end
|
30
|
+
|
31
|
+
def get_conn
|
32
|
+
if @pool
|
33
|
+
@pool.hold {|conn| return conn }
|
34
|
+
else
|
35
|
+
@sequel
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def exec(sql, first: nil)
|
40
|
+
conn = get_conn
|
41
|
+
if first
|
42
|
+
return conn[sql].first
|
43
|
+
else
|
44
|
+
return conn[sql].all
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def run(sql)
|
49
|
+
conn = get_conn
|
50
|
+
conn.run(sql)
|
51
|
+
end
|
52
|
+
|
53
|
+
def dataset(table_name)
|
54
|
+
@sequel[table_name.to_sym]
|
55
|
+
end
|
56
|
+
|
57
|
+
class JointHash < Hash
|
58
|
+
def initialize(default_table, values = {})
|
59
|
+
@default_table = default_table
|
60
|
+
self.update(values)
|
61
|
+
end
|
62
|
+
|
63
|
+
def []=(key, value)
|
64
|
+
super(key.to_s, value)
|
65
|
+
end
|
66
|
+
|
67
|
+
def [](key)
|
68
|
+
key = key.to_s
|
69
|
+
return fetch(key) if has_key?(key)
|
70
|
+
alt_key = "#{@default_table}.#{key}"
|
71
|
+
return fetch(alt_key) if has_key?(alt_key)
|
72
|
+
return nil
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# テーブルを連結して、全てのデータを返す。
|
77
|
+
def get_join_table(structure, opts = {})
|
78
|
+
col_h = {}
|
79
|
+
reverse_col_h = {}
|
80
|
+
query_a = []
|
81
|
+
table_a = []
|
82
|
+
prefix="_x_"
|
83
|
+
structure[:column_list].each_with_index do |k, i|
|
84
|
+
key = "#{prefix}#{i+1}"
|
85
|
+
col_h[k.to_sym] = key.to_sym
|
86
|
+
reverse_col_h[key.to_sym] = k
|
87
|
+
query_a.push "#{k} AS #{key}"
|
88
|
+
end
|
89
|
+
tables = structure[:tables].clone
|
90
|
+
join_cond = structure[:join_condition]
|
91
|
+
tb = tables.shift
|
92
|
+
table_part = [ tb ]
|
93
|
+
tables.each do |table|
|
94
|
+
cond = join_cond[table.to_sym]
|
95
|
+
if cond
|
96
|
+
table_part.push " LEFT JOIN #{table} ON #{cond}"
|
97
|
+
else
|
98
|
+
table_part.push " LEFT JOIN #{table} ON #{tb}.#{table} = #{table}.id"
|
37
99
|
end
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
100
|
+
end
|
101
|
+
sql = "SELECT #{query_a.join(', ')} FROM #{table_part.join(' ')}"
|
102
|
+
sql += " WHERE #{opts[:where]}" if opts[:where]
|
103
|
+
sql += " ORDER BY #{opts[:order]}" if opts[:order]
|
104
|
+
sql += " LIMIT #{opts[:limit]}" if opts[:limit]
|
105
|
+
data_a = self.exec(sql)
|
106
|
+
res_a = []
|
107
|
+
data_a.each do |data|
|
108
|
+
new_data = JointHash.new(tb)
|
109
|
+
data.each do |k, v|
|
110
|
+
orig_key = reverse_col_h[k.to_sym]
|
111
|
+
next unless orig_key
|
112
|
+
new_data[orig_key] = v
|
46
113
|
end
|
47
|
-
|
48
|
-
column(:updated_at, :timestamp, default: Sequel::CURRENT_TIMESTAMP)
|
114
|
+
res_a.push(new_data)
|
49
115
|
end
|
116
|
+
return res_a
|
117
|
+
end
|
118
|
+
|
119
|
+
# テーブル生成
|
120
|
+
def create_table(table_name, dbtype_h)
|
121
|
+
%w[id created_at updated_at deleted_at].each do |key|
|
122
|
+
dbtype_h.delete(key.to_sym)
|
123
|
+
end
|
124
|
+
# puts "create_table: #{table_name}"
|
125
|
+
if @dbfile.index("postgres")
|
126
|
+
@sequel.create_table(table_name) do
|
127
|
+
primary_key :id, identity: true
|
128
|
+
dbtype_h.each do |key, dbtype|
|
129
|
+
column(key, dbtype)
|
130
|
+
end
|
131
|
+
column(:created_at, :timestamp, default: Sequel::CURRENT_TIMESTAMP)
|
132
|
+
column(:updated_at, :timestamp)
|
133
|
+
column(:deleted_at, :timestamp)
|
134
|
+
end
|
135
|
+
else
|
136
|
+
@sequel.create_table(table_name) do
|
137
|
+
primary_key :id, auto_increment: true
|
138
|
+
dbtype_h.each do |key, dbtype|
|
139
|
+
column(key, dbtype)
|
140
|
+
end
|
141
|
+
column(:created_at, :timestamp, default: Sequel::CURRENT_TIMESTAMP)
|
142
|
+
column(:updated_at, :timestamp)
|
143
|
+
column(:deleted_at, :timestamp)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def insert(table_name, val_h)
|
149
|
+
dataset(table_name).insert(val_h)
|
150
|
+
end
|
151
|
+
|
152
|
+
def update(dataset, id, val_h)
|
153
|
+
val_h.update({ updated_at: Time.now })
|
154
|
+
dataset.where(id: id).update(val_h)
|
155
|
+
end
|
156
|
+
|
157
|
+
def delete(dataset, id)
|
158
|
+
dataset.where(id: id).update({ deleted_at: Time.now })
|
50
159
|
end
|
51
|
-
end
|
52
|
-
|
53
|
-
def insert(table_name, val_h)
|
54
|
-
dataset(table_name).insert(val_h)
|
55
160
|
end
|
56
161
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
162
|
+
class Cache
|
163
|
+
class << self
|
164
|
+
|
165
|
+
def [](table)
|
166
|
+
@store ||= {}
|
167
|
+
dataset = DB.dataset(table.to_sym)
|
168
|
+
# EzLog.debug("DB::Cache: #{table}")
|
169
|
+
unless @store[table.to_sym]
|
170
|
+
data_a = dataset.where(deleted_at: nil).all
|
171
|
+
h = {}
|
172
|
+
data_a.each {|data| h[data[:id]] = data }
|
173
|
+
@store[table.to_sym] = h
|
174
|
+
end
|
175
|
+
# EzLog.debug(@store[table.to_sym])
|
176
|
+
return @store[table.to_sym]
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
61
180
|
end
|
62
181
|
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module Ezframe
|
2
|
+
module EditorCommon
|
3
|
+
def get_id(class_name = nil)
|
4
|
+
class_name ||= @class_snake
|
5
|
+
params = @request.env['url_params']
|
6
|
+
return nil unless params
|
7
|
+
# EzLog.info "get_id: #{params.inspect}, #{class_name}"
|
8
|
+
return params[class_name.to_sym]
|
9
|
+
end
|
10
|
+
|
11
|
+
# 新規データの生成
|
12
|
+
def create_data(form)
|
13
|
+
@column_set.clear
|
14
|
+
@column_set[:id].value = id = @column_set.create(form)
|
15
|
+
return id
|
16
|
+
end
|
17
|
+
|
18
|
+
# データの更新
|
19
|
+
def update_data(id, form)
|
20
|
+
@column_set.update(id, form)
|
21
|
+
end
|
22
|
+
|
23
|
+
def make_form(url, child)
|
24
|
+
return Ht.form(ezload: "command=set_validation:validate_url=#{url}", child: child)
|
25
|
+
end
|
26
|
+
|
27
|
+
# 新規登録ボタンの生成
|
28
|
+
def make_create_button(event = nil)
|
29
|
+
event ||= "on=click:url=#{make_base_url(@id)}/create"
|
30
|
+
return Ht.button(id: "#{@class_snake}-create-button", class: %[btn], child: [Ht.icon("add"), Message[:create_button_label]], ezevent: event)
|
31
|
+
end
|
32
|
+
|
33
|
+
# 編集ボタンの生成
|
34
|
+
def make_edit_button(event = nil)
|
35
|
+
event ||= "on=click:url=#{make_base_url(@id)}/edit"
|
36
|
+
return Ht.button(class: %w[btn], ezevent: event, child: [ Ht.icon("edit"), Message[:edit_button_label]])
|
37
|
+
end
|
38
|
+
|
39
|
+
# 削除ボタンの生成
|
40
|
+
def make_delete_button(event = nil)
|
41
|
+
event ||= "on=click:url=#{make_base_url(@id)}/delete"
|
42
|
+
return Ht.button(class: %w[btn right red], ezevent: event, child: [Ht.icon("delete"), Message[:delete_button_label]])
|
43
|
+
end
|
44
|
+
|
45
|
+
# キャンセルボタンの生成
|
46
|
+
def make_cancel_button(event = nil)
|
47
|
+
event ||= "on=click:url=#{make_base_url(@id)}/detail:cancel=true:with=form"
|
48
|
+
return Ht.button(class: %w[btn red], child: [Ht.icon("cancel"), Message[:cancel_button_label]], ezevent: event)
|
49
|
+
end
|
50
|
+
|
51
|
+
# 値の更新
|
52
|
+
# def update_value
|
53
|
+
# form = @event[:form]
|
54
|
+
# @column_set.update(get_id, form)
|
55
|
+
# end
|
56
|
+
|
57
|
+
# ラベル付きで1カラムのviewを表示
|
58
|
+
def show_label_view(key)
|
59
|
+
col = @column_set[key]
|
60
|
+
Ht.span([Ht.small(col.label), col.view(force: true)])
|
61
|
+
end
|
62
|
+
|
63
|
+
# ラベル付きで1カラムのformを表示
|
64
|
+
def show_label_edit(key)
|
65
|
+
col = @column_set[key]
|
66
|
+
Ht.span([Ht.small(col.label), col.form(force: true)])
|
67
|
+
end
|
68
|
+
|
69
|
+
# エラーメッセージだけを表示するページを生成
|
70
|
+
def show_message_page(title, body)
|
71
|
+
return show_base_template(title: title, body: Html.convert(body))
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|