ezframe 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.
@@ -4,6 +4,9 @@ module Ezframe
4
4
  def choose(request, route_h = nil)
5
5
  path_parts = request.path_info.split("/").drop(1)
6
6
  route_h ||= Config[:route].deep_dup
7
+ unless route_h
8
+ raise "Config[:route] is not defined. It should be defined in config/route.yml"
9
+ end
7
10
  # puts "config=#{Config[:route]}, route_h=#{route_h}"
8
11
  args = {}
9
12
  opts = {}
@@ -22,13 +25,13 @@ module Ezframe
22
25
  while path_parts.length > 0
23
26
  part = path_parts.shift
24
27
  # break if part.empty?
25
- # Logger.info "part=#{part}, route_h=#{route_h.inspect}"
28
+ # EzLog.info "part=#{part}, route_h=#{route_h.inspect}"
26
29
  if route_h.has_key?(part.to_sym)
27
- # Logger.info "has_route: #{part}"
30
+ # EzLog.info "has_route: #{part}"
28
31
  class_a.push(part)
29
32
  if path_parts[0].to_i > 0
30
33
  args[part.to_sym] = val = path_parts.shift
31
- # Logger.info "value: part=#{part}, val=#{val}"
34
+ # EzLog.info "value: part=#{part}, val=#{val}"
32
35
  end
33
36
  route_h = route_h[part.to_sym]
34
37
  break if route_h.nil?
@@ -41,7 +44,7 @@ module Ezframe
41
44
  end
42
45
  else
43
46
  # routeに無ければ、メソッドを探す
44
- # Logger.info "no_route: #{part}"
47
+ # EzLog.info "no_route: #{part}"
45
48
  klass = get_class(class_a[-1])
46
49
  return [ 404 ] unless klass
47
50
  instance = klass.new
@@ -49,7 +52,7 @@ module Ezframe
49
52
  if instance.respond_to?(method_name)
50
53
  return [instance, method_name, args, opts]
51
54
  else
52
- Logger.info "undefined method: #{klass}.#{method_name}: full path=#{request.path_info}"
55
+ EzLog.info "undefined method: #{klass}.#{method_name}: full path=#{request.path_info}"
53
56
  end
54
57
  end
55
58
  end
@@ -63,7 +66,7 @@ module Ezframe
63
66
  part = "default"
64
67
  end
65
68
  method_name = make_method_name(part, request.request_method)
66
- #Logger.info "method_name=#{method_name}"
69
+ #EzLog.info "method_name=#{method_name}"
67
70
  instance = klass.new
68
71
  if instance.respond_to?(method_name)
69
72
  return [instance, method_name, args, opts]
@@ -74,7 +77,7 @@ module Ezframe
74
77
  # ページクラスの階層を辿る
75
78
  def get_path(class_snake, route_h = nil)
76
79
  route_h = Config[:route] unless route_h
77
- # Logger.info "get_path: route_h=#{route_h}"
80
+ # EzLog.info "get_path: route_h=#{route_h}"
78
81
  @get_path_found_it = nil
79
82
  route =_scan_route(class_snake, route_h.deep_dup)
80
83
  return route.reverse if route
@@ -106,11 +109,11 @@ module Ezframe
106
109
  end
107
110
 
108
111
  def get_class(keys)
109
- # Logger.info "get_class: #{keys.inspect}"
112
+ # EzLog.info "get_class: #{keys.inspect}"
110
113
  return nil unless keys
111
114
  keys = [ keys ] if keys.is_a?(String)
112
115
  klass = (%w[Ezframe] + keys.map { |k| k.to_s.to_camel }).join("::")
113
- # Logger.info "get_class: #{klass}"
116
+ # EzLog.info "get_class: #{klass}"
114
117
  if Object.const_defined?(klass)
115
118
  return Object.const_get(klass)
116
119
  else
@@ -9,10 +9,17 @@ module Ezframe
9
9
  def call(env)
10
10
  req = Rack::Request.new(env)
11
11
  res = Rack::Response.new
12
- Controller.exec(req, res)
13
- if res.body.empty?
14
- raise "no body in response"
12
+ begin
13
+ Controller.exec(req, res)
14
+ rescue => e
15
+ EzLog.error("Controller.exec: exception: #{e.message}:\n#{e.backtrace}")
16
+ res.status = 500
17
+ res.headers["Content-Type"] = "text/plain"
18
+ res.body = [ "Internal server error" ]
15
19
  end
20
+ # if res.body.empty?
21
+ # raise "no body in response"
22
+ # end
16
23
  return res.finish
17
24
  end
18
25
  end
@@ -0,0 +1,205 @@
1
+ require_relative "editor_common"
2
+
3
+ module Ezframe
4
+ # ページ遷移無しでデータを編集する仕組み
5
+ class SinglePageEditor < PageBase
6
+ include EditorCommon
7
+
8
+ def init_vars
9
+ super
10
+ @sort_key = :id
11
+ @event = @parsed_body[:event] if @parsed_body
12
+ @dom_id = { create: "create-area", edit: "edit-area", index: "index-area", detail: "detail-area"}
13
+ # @show_delete_button = nil
14
+ end
15
+
16
+ def public_default_get
17
+ @id = get_id
18
+ # if @id
19
+ # return public_detail_post
20
+ # else
21
+ div = [ Ht.div(id: @dom_id[:create], child: make_index_top), Ht.div(id: @dom_id[:index], child: make_index_table) ]
22
+ layout = index_layout(center: Ht.form(child: div))
23
+ return show_base_template(title: Message[:index_page_title], body: Html.convert(layout))
24
+ # end
25
+ end
26
+
27
+ def make_index_top
28
+ make_create_button("on=click:url=#{make_base_url}/create")
29
+ end
30
+
31
+ def public_default_post
32
+ return { inject: "##{@dom_id[:index]}", body: Html.convert(make_index_table) }
33
+ end
34
+
35
+ # 新規データ登録
36
+ def public_create_post
37
+ @form = @event[:form]
38
+ EzLog.debug("public_create_post: event=#{@event}")
39
+ if @event[:cancel]
40
+ return { inject: "##{@dom_id[:create]}", body: Html.convert(make_index_top) }
41
+ elsif !@form
42
+ return { inject: "##{@dom_id[:create]}", body: Html.convert(make_edit_form(:create)) }
43
+ else
44
+ # 値の保存
45
+ @column_set.clear
46
+ form_values = @form
47
+ form_values.update(@env["url_params"])
48
+ # @column_set.values = form_values
49
+ @column_set[:id].value = @id = @column_set.create(form_values)
50
+ return { redirect: make_base_url(@id) }
51
+ # return public_default_post
52
+ end
53
+ end
54
+
55
+ # データ編集受信
56
+ def public_edit_post
57
+ @id = get_id
58
+ unless @event[:form]
59
+ data = @column_set.set_from_db(@id)
60
+ return show_message_page("no data", "data is not defined: #{@id}") unless data
61
+ # フォームの表示
62
+ form = make_edit_form
63
+ found_a = Ht.search(form, tag: "input")
64
+ found_a.each { |h| h.add_class("#{@class_snake}-edit-box") if h[:size] }
65
+ return { inject: "##{@dom_id[:detail]}", body: Html.convert(form) }
66
+ else
67
+ if @event[:cancel]
68
+ data = @column_set.set_from_db(@id)
69
+ return act_after_cancel
70
+ else
71
+ # 値を保存
72
+ @column_set.update(@id, @event[:form])
73
+ end
74
+ return act_after_edit
75
+ end
76
+ end
77
+
78
+ # データ詳細表示
79
+ def public_detail_post
80
+ @id ||= get_id
81
+ data = @column_set.set_from_db(@id)
82
+ target_keys = @detail_keys || @column_set.keys.select { |key| !@column_set[key].attribute[:no_view] }
83
+ line_a = []
84
+ target_keys.each do |key|
85
+ column = @column_set[key]
86
+ v = make_detail_line(column)
87
+ line_a.push(v) if v
88
+ end
89
+ table = Ht.div(line_a)
90
+ collection = Materialize::Collection.new
91
+ # 詳細表示用のblockを追加
92
+ collection.push(Ht.div(id: @dom_id[:detail], child: [button_for_detail_box(data), table]))
93
+ return { inject: "##{@dom_id[:detail]}", body: Html.convert(collection.to_h) }
94
+ end
95
+
96
+ def public_delete_post
97
+ @id = get_id
98
+ dataset = DB.dataset(@column_set.name)
99
+ DB.delete(dataset, @id)
100
+ return public_default_post
101
+ end
102
+
103
+ # 詳細表示欄の一行を生成
104
+ def make_detail_line(column)
105
+ view = column.view
106
+ if view
107
+ view = Ht.pre(view) if view.strip.index("\n")
108
+ return Ht.p([Ht.small(column.label), view])
109
+ end
110
+ return nil
111
+ end
112
+
113
+ def act_after_edit
114
+ return [public_default_post, public_detail_post]
115
+ end
116
+
117
+ def act_after_cancel
118
+ return public_detail_post
119
+ end
120
+
121
+ # 一覧表の生成
122
+ def make_index_table
123
+ list = list_for_index
124
+ target_keys = @index_keys
125
+ unless target_keys
126
+ target_keys = @column_set.keys.select {|k| !@column_set[k].no_view?}
127
+ end
128
+ labels = @table_labels
129
+ unless labels
130
+ labels = target_keys.map {|k| @column_set[k].label(force: true) || " "}
131
+ end
132
+ # 項目名欄の生成
133
+ thead = Ht.thead(Ht.tr(labels.map {|label| Ht.th(label||" ")}))
134
+
135
+ tr_a = list.map do |data|
136
+ view_a = make_index_line(target_keys, data)
137
+ td_a = view_a.map {|view| Ht.td(view)}
138
+ Ht.tr(id: "tr-#{@class_snake}-#{data[:id]}", child: td_a, event: "on=click:url=#{make_base_url(data[:id])}/detail")
139
+ end
140
+ tbody = Ht.tbody(tr_a)
141
+ return [
142
+ Ht.table(id: "enable_datatable_#{@class_snake}", child: [thead, tbody], ezload: "command=enable_datatable:target=#enable_datatable_#{@class_snake}"),
143
+ Ht.div(id: @dom_id[:detail], child: ""),
144
+ ]
145
+ end
146
+
147
+ # 一覧表示の1行を生成
148
+ def make_index_line(target_keys, data)
149
+ @column_set.clear
150
+ @column_set.values = data
151
+ return target_keys.map { |key| make_index_column(key) }
152
+ end
153
+
154
+ # 一覧表示の1カラムを生成
155
+ def make_index_column(key)
156
+ column = @column_set[key.to_sym]
157
+ # if @with_label
158
+ # child = [Ht.small(column.label), column.view(force: true)]
159
+ # return Ht.p(id: "edit-#{@class_snake}-#{@column_set[:id].value}-column-#{column.key}", child: child)
160
+ # else
161
+ return column.view(force: true)
162
+ # end
163
+ end
164
+
165
+ # 一覧ページ用のデータリスト生成
166
+ def list_for_index
167
+ return @column_set.dataset.where(deleted_at: nil).order(@sort_key).all
168
+ end
169
+
170
+ # 一覧ページ用ボタン
171
+ def button_for_index_line(data)
172
+ Ht.button(class: %w[btn right], event: "on=click:url=#{make_base_url(data[:id])}/edit", child: [Ht.icon("edit"), Message[:edit_button_label]])
173
+ end
174
+
175
+ # 詳細ページ用ボタン
176
+ def button_for_detail_box(data)
177
+ buttons = [Ht.button(class: %w[btn right], event: "on=click:url=#{make_base_url(data[:id])}/edit", child: [Ht.icon("edit"), Message[:edit_button_label]]) ]
178
+ if @show_delete_button
179
+ buttons.push(make_delete_button)
180
+ end
181
+ return Ht.div(class: %w[button-box], child: buttons)
182
+ end
183
+
184
+ # 編集フォームの生成
185
+ def make_edit_form(command = :edit)
186
+ @id ||= get_id
187
+ target_keys = @edit_keys || @column_set.keys
188
+ list = target_keys.map do |colkey|
189
+ column = @column_set[colkey.to_sym]
190
+ unless column
191
+ EzLog.error("undefined column entry: #{colkey}")
192
+ next
193
+ end
194
+ form = column.form
195
+ Ht.p(class: %w[form-line], child: [Ht.small(column.label), form]) if form
196
+ end
197
+ send_button = Ht.button(id: "#{@class_snake}-#{command}-finish-button", class: %w[btn], child: [Ht.icon("check"), Message[:edit_finish_button_label]], event: "on=click:url=#{make_base_url(@id)}/#{command}:with=form")
198
+ cancel_button = make_cancel_button("on=click:url=#{make_base_url(@id)}/#{command}:cancel=true:with=form")
199
+ list.push(Ht.p(class: %w[edit-finish-buttons], child: [send_button, cancel_button]))
200
+ return Ht.form(list)
201
+ end
202
+
203
+
204
+ end
205
+ end
@@ -0,0 +1,222 @@
1
+ module Ezframe
2
+ # 各顧客に関連づいた情報の編集を一般化したクラス
3
+ class SubEditor < PageBase
4
+ include EditorCommon
5
+
6
+ def init_vars
7
+ super
8
+ @sort_key = :id
9
+ # @parent_key = :customer
10
+ @event = @parsed_body[:event] if @parsed_body
11
+ # @use_detail_box = true
12
+ end
13
+
14
+ def get_parent_id
15
+ params = @request.env["url_params"]
16
+ unless params
17
+ EzLog.info "[WARN] no url_params"
18
+ return nil
19
+ end
20
+ return params[@parent_key.to_sym]
21
+ end
22
+
23
+ def public_default_post
24
+ return { inject: "##{@class_snake}_tab", body: Html.convert(make_index_page) }
25
+ end
26
+
27
+ # 新規データ登録
28
+ def public_create_post
29
+ @form = @event[:form]
30
+ # EzLog.debug("public_create_post: form=#{@form}")
31
+ unless @form
32
+ { inject: "##{@class_snake}-create-area", body: Html.convert(make_edit_form(:create)) }
33
+ else
34
+ # 値の保存
35
+ @column_set.clear
36
+ form_values = @form
37
+ form_values.update(@env["url_params"])
38
+ # @column_set.values = form_values
39
+ @column_set[:id].value = @id = @column_set.create(form_values)
40
+ # return { redirect: "#{make_base_url}/#{@id}" }
41
+ return public_default_post
42
+ end
43
+ end
44
+
45
+ # データ編集受信
46
+ def public_edit_post
47
+ @id ||= get_id
48
+ unless @event[:form]
49
+ data = @column_set.set_from_db(@id)
50
+ # データが空ならエラーページ
51
+ return { inject: "##{edit_inject_element}", body: "データがありません: #{@id}"} unless data
52
+ # フォームの表示
53
+ form = make_edit_form
54
+ found_a = Ht.search(form, tag: "input")
55
+ found_a.each { |h| h.add_class("#{@class_snake}-edit-box") if h[:size] }
56
+ return { inject: "##{edit_inject_element}", body: Html.convert(form) }
57
+ else
58
+ if @event[:cancel]
59
+ data = @column_set.set_from_db(@id)
60
+ return act_after_cancel
61
+ else
62
+ # 値を保存
63
+ @column_set.update(@id, @event[:form])
64
+ end
65
+ return act_after_edit
66
+ end
67
+ end
68
+
69
+ # データ詳細表示
70
+ def public_detail_post
71
+ @id = get_id
72
+ data = @column_set.set_from_db(@id)
73
+ target_keys = @show_keys || @column_set.keys.select { |key| !@column_set[key].attribute[:no_view] }
74
+ line_a = []
75
+ target_keys.each do |key|
76
+ column = @column_set[key]
77
+ v = make_detail_line(column)
78
+ line_a.push(v) if v
79
+ end
80
+ table = Ht.div(line_a)
81
+ collection = Materialize::Collection.new
82
+ # 詳細表示用のblockを追加
83
+ collection.push(Ht.div(class: "detail-box", child: [button_for_detail_box(data), table]))
84
+ return { inject: "##{edit_inject_element}", body: Html.convert(collection.to_h) }
85
+ end
86
+
87
+ # 削除ボタン押下時の処理
88
+ def public_delete_post
89
+ @id = get_id
90
+ dataset = DB.dataset(@column_set.name)
91
+ DB.delete(dataset, @id)
92
+ return public_default_post
93
+ end
94
+
95
+ # 詳細表示欄の一行を生成
96
+ def make_detail_line(column)
97
+ view = column.view
98
+ if view
99
+ view = Ht.pre(view) if view.index("\n")
100
+ return Ht.p([Ht.small(column.label), view])
101
+ end
102
+ return nil
103
+ end
104
+
105
+ def edit_inject_element
106
+ return "#{@class_snake}_show"
107
+ end
108
+
109
+ def act_after_edit
110
+ return [public_default_post, public_detail_post]
111
+ # return { inject: edit_inject_element, body: Html.convert(make_index_line(@column_set.get_hash(:value))) }
112
+ end
113
+
114
+ def act_after_cancel
115
+ return public_detail_post
116
+ end
117
+
118
+ # 一覧表の生成
119
+ def make_index_page
120
+ list = list_for_index
121
+ target_keys = @index_keys
122
+ unless target_keys
123
+ target_keys = @column_set.keys.select {|k| !@column_set[k].no_view?}
124
+ end
125
+ # 項目名欄の生成
126
+ if @table_labels
127
+ thead = Ht.thead(Ht.tr(@table_labels.map {|label| Ht.th(label)}))
128
+ else
129
+ thead = Ht.thead(Ht.tr(target_keys.map {|key|
130
+ if @column_set[key].respond_to?(:label)
131
+ Ht.th(@column_set[key].label(force: true))
132
+ else
133
+ nil
134
+ end
135
+ })).compact
136
+ end
137
+
138
+ tr_a = list.map do |data|
139
+ view_a = make_index_line(target_keys, data)
140
+ td_a = view_a.map {|view| Ht.td(view)}
141
+ Ht.tr(id: "tr-#{@class_snake}-#{data[:id]}", child: td_a, event: "on=click:url=#{make_base_url(data[:id])}/detail")
142
+ end
143
+ tbody = Ht.tbody(tr_a)
144
+ return [
145
+ area_for_create,
146
+ Ht.table(id: "enable_datatable_#{@class_snake}", child: [thead, tbody], ezload: "command=enable_datatable:target=#enable_datatable_#{@class_snake}"),
147
+ Ht.div(id: edit_inject_element),
148
+ ]
149
+ end
150
+
151
+ # 一覧表示の1行を生成
152
+ def make_index_line(target_keys, data)
153
+ @column_set.clear
154
+ @column_set.set_values(data, from_db: true)
155
+ return target_keys.map { |key| make_index_column(key) }
156
+ end
157
+
158
+ # 一覧表示の1カラムを生成
159
+ def make_index_column(key)
160
+ column = @column_set[key.to_sym]
161
+ return column.view(force: true)
162
+ end
163
+
164
+ # 一覧ページ用のデータリスト生成
165
+ def list_for_index
166
+ return @column_set.dataset.where(@parent_key.to_sym => get_parent_id, deleted_at: nil).order(@sort_key).all
167
+ end
168
+
169
+ # 一覧ページ用ボタン
170
+ def button_for_index_line(data)
171
+ Ht.button(class: %w[btn right], event: "on=click:url=#{make_base_url(data[:id])}/edit", child: [Ht.icon("edit"), Message[:edit_button_label]])
172
+ end
173
+
174
+ # 詳細ページ用ボタン
175
+ def button_for_detail_box(data)
176
+ buttons = [Ht.button(class: %w[btn right], event: "on=click:url=#{make_base_url(data[:id])}/edit", child: [Ht.icon("edit"), Message[:edit_button_label]]) ]
177
+ if @show_delete_button
178
+ buttons.push(make_delete_button)
179
+ end
180
+ return Ht.div(class: %w[button-box], child: buttons)
181
+ end
182
+
183
+ #--------------------------------------------------------------------------------------------------------
184
+ # 新規データ追加欄
185
+ def area_for_create(extra_buttons = nil)
186
+ create_button = make_create_button
187
+ create_button[:event] = "on=click:url=#{make_base_url}/create"
188
+ return Ht.div(id: "#{@class_snake}-create-area", child: [ create_button, extra_buttons ].compact)
189
+ end
190
+
191
+ # 編集フォームの生成
192
+ def make_edit_form(command = :edit)
193
+ # @id ||= get_id
194
+ if command == :edit && !@id
195
+ EzLog.error "make_edit_form: @id is not defined"
196
+ end
197
+ target_keys = @edit_keys || @column_set.keys
198
+ list = target_keys.map do |colkey|
199
+ column = @column_set[colkey.to_sym]
200
+ unless column
201
+ EzLog.error("undefined column entry: #{colkey}")
202
+ next
203
+ end
204
+ make_edit_line(column)
205
+ end.compact
206
+ event = "on=click:url=#{make_base_url(@id)}/#{command}:with=form"
207
+ send_button = Ht.button(id: "#{@class_snake}-#{command}-finish-button", class: %w[btn], child: [Ht.icon("check"), Message[:edit_finish_button_label]], event: event)
208
+ cancel_button = make_cancel_button("on=click:url=#{make_base_url(@id)}/#{command}:cancel=true:with=form")
209
+ list.push(Ht.p(class: %w[edit-finish-buttons], child: [send_button, cancel_button]))
210
+ return Ht.form(list)
211
+ end
212
+
213
+ def make_edit_line(column)
214
+ form = column.form
215
+ if form
216
+ return Ht.p(class: %w[form-line], child: [ Ht.small(column.label), form ])
217
+ else
218
+ return nil
219
+ end
220
+ end
221
+ end
222
+ end