engine2 1.0.8 → 1.0.9

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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +3 -3
  3. data/app/actions.coffee +87 -50
  4. data/app/app.css +2 -1
  5. data/app/engine2.coffee +37 -19
  6. data/app/modal.coffee +8 -3
  7. data/bower.json +3 -2
  8. data/conf/message.yaml +3 -0
  9. data/conf/message_pl.yaml +5 -2
  10. data/config.coffee +16 -15
  11. data/engine2.gemspec +1 -1
  12. data/lib/engine2/action.rb +51 -23
  13. data/lib/engine2/action/array.rb +94 -1
  14. data/lib/engine2/action/decode.rb +29 -3
  15. data/lib/engine2/action/delete.rb +1 -2
  16. data/lib/engine2/action/infra.rb +5 -4
  17. data/lib/engine2/action/list.rb +70 -25
  18. data/lib/engine2/action/save.rb +0 -2
  19. data/lib/engine2/action_node.rb +2 -4
  20. data/lib/engine2/core.rb +30 -21
  21. data/lib/engine2/handler.rb +5 -5
  22. data/lib/engine2/model.rb +18 -9
  23. data/lib/engine2/models/Files.rb +2 -0
  24. data/lib/engine2/scheme.rb +1 -6
  25. data/lib/engine2/templates.rb +32 -6
  26. data/lib/engine2/type_info.rb +9 -5
  27. data/lib/engine2/version.rb +1 -1
  28. data/package.json +9 -8
  29. data/views/fields/checkbox_button.slim +6 -0
  30. data/views/fields/checkbox_buttons.slim +1 -1
  31. data/views/fields/list_mbuttons.slim +9 -0
  32. data/views/fields/list_mbuttons_opt.slim +11 -0
  33. data/views/fields/list_mselect.slim +12 -0
  34. data/views/fields/scaffold.slim +1 -1
  35. data/views/fields/typeahead_picker.slim +8 -5
  36. data/views/infra/inspect.slim +19 -16
  37. data/views/scaffold/fields.slim +3 -1
  38. data/views/scaffold/search.slim +2 -2
  39. data/views/scaffold/search_collapse.slim +2 -2
  40. data/views/scaffold/search_tabs.slim +2 -2
  41. data/views/search_fields/date.slim +20 -0
  42. data/views/search_fields/list_mbuttons.slim +12 -0
  43. data/views/search_fields/typeahead_picker.slim +4 -3
  44. metadata +11 -5
@@ -57,6 +57,10 @@ module Engine2
57
57
  def invoke handler
58
58
  {entries: get_query.limit(200).load_all}
59
59
  end
60
+
61
+ def order *fields
62
+ @query = get_query.order *fields
63
+ end
60
64
  end
61
65
 
62
66
  class TypeAheadAction < DecodeAction
@@ -71,17 +75,39 @@ module Engine2
71
75
  @limit = lmt
72
76
  end
73
77
 
78
+ def get_limit
79
+ @limit
80
+ end
81
+
82
+ def get_case_insensitive
83
+ @case_insensitive
84
+ end
85
+
74
86
  def case_insensitive
75
87
  @case_insensitive = true
76
88
  end
77
89
 
90
+ def order *fields
91
+ @query = get_query.order *fields
92
+ end
93
+
78
94
  def invoke handler
95
+ model = assets[:model]
79
96
  if query = handler.params[:query]
80
- condition = @meta[:decode_fields].map{|f|f.like("%#{query}%", case_insensitive: @case_insensitive)}.reduce{|q, f| q | f}
81
- {entries: get_query.where(condition).limit(@limit).load_all}
97
+ fields = @meta[:decode_fields] || static.meta[:decode_fields]
98
+
99
+ entries = if query.to_s.empty?
100
+ get_query
101
+ else
102
+ table_name = model.table_name
103
+ condition = fields.map{|f|table_name.q(f).like("%#{query}%", case_insensitive: @case_insensitive || static.get_case_insensitive)}.reduce{|q, f| q | f}
104
+ get_query.where(condition)
105
+ end.limit(@limit || static.get_limit).load_all
106
+
107
+ {entries: entries}
82
108
  else
83
109
  handler.permit id = handler.params[:id]
84
- record = get_query.load Hash[assets[:model].primary_keys.zip(split_keys(id))]
110
+ record = get_query.unordered.load Hash[model.primary_keys_qualified.zip(split_keys(id))]
85
111
  # handler.halt_not_found(LOCS[:no_entry]) unless record
86
112
  {entry: record}
87
113
  end
@@ -61,5 +61,4 @@ module Engine2
61
61
  invoke_delete_db(handler, ids)
62
62
  end
63
63
  end
64
-
65
- end
64
+ end
@@ -24,7 +24,7 @@ module Engine2
24
24
 
25
25
  define_node :inspect_modal, InspectModalAction do
26
26
  access! &:logged_in?
27
- define_node :inspect, WebSocketAction.inherit do
27
+ define_node :"inspect_#{SETTINGS[:name]}", WebSocketAction.inherit do
28
28
  self.* do
29
29
  @action_type = :inspect
30
30
 
@@ -112,7 +112,8 @@ module Engine2
112
112
  temp.close
113
113
  rackname = File.basename(temp.path)
114
114
  info = node.parent.*.model.type_info[node.parent.*.field]
115
- File.rename(temp.path, "#{info[:store][:upload]}/#{rackname}")
115
+ FileUtils.cp(temp.path, "#{info[:store][:upload]}/#{rackname}") # File.rename(temp.path, "#{info[:store][:upload]}/#{rackname}")
116
+ FileUtils.rm(temp.path)
116
117
  {rackname: rackname}
117
118
  end
118
119
  end
@@ -233,9 +234,9 @@ module Engine2
233
234
  {user: user ? user.to_hash : nil}
234
235
  end
235
236
 
236
- def login_meta show_login_otion = 'false', &blk
237
+ def login_meta menu_properties = {show: 'false'}, &blk
237
238
  node.login_form.* &blk
238
- menu(:menu).modify_option :login_form, show: show_login_otion
239
+ menu(:menu).modify_option :login_form, menu_properties
239
240
  node.parent.login_form.* &blk
240
241
  end
241
242
  end
@@ -34,6 +34,13 @@ module Engine2
34
34
  query.where(name => Sequel.string_to_date(value))
35
35
  end
36
36
  },
37
+ datetime: lambda{|query, name, value, type_info, hash|
38
+ if value.is_a? Hash
39
+ DefaultFilters[:date].(query, name, value, type_info, hash)
40
+ else
41
+ query.where(:date.sql_function(name) => Sequel.string_to_date(value))
42
+ end
43
+ },
37
44
  integer: lambda{|query, name, value, type_info, hash|
38
45
  if value.is_a? Hash
39
46
  from, to = value[:from], value[:to]
@@ -82,6 +89,10 @@ module Engine2
82
89
  super
83
90
  end
84
91
 
92
+ def page_frame handler, entries
93
+ entries
94
+ end
95
+
85
96
  def invoke handler
86
97
  params = handler.params
87
98
  model = assets[:model]
@@ -89,31 +100,30 @@ module Engine2
89
100
 
90
101
  if search = params[:search]
91
102
  query = list_search(query, handler, search)
103
+ elsif @filters || static.filters
104
+ static.filters.to_h.merge(@filters.to_h).each do |name, filter|
105
+ query = filter.(handler, query, {})
106
+ handler.permit query
107
+ end
92
108
  end
93
109
 
94
110
  count = query.count if lookup(:config, :use_count)
95
111
 
96
- if order_str = params[:order]
97
- order = order_str.to_sym
112
+ if order = params[:order]
113
+ order = order.to_sym
98
114
  handler.permit lookup(:fields, order, :sort)
99
-
100
- if order_blk = (@orders && @orders[order]) || (dynamic? && (static.orders && static.orders[order]))
101
- query = order_blk.(handler, query)
102
- else
103
- order = model.table_name.q(order) if model.type_info[order]
104
- query = query.order(order)
105
- end
106
-
107
- query = query.reverse if params[:asc] == "true"
115
+ query = list_order(query, handler, order, params[:asc] == "true")
116
+ elsif order = @default_order_field || static.default_order_field
117
+ query = list_order(query, handler, order, true)
108
118
  end
109
119
 
110
120
  per_page = lookup(:config, :per_page)
111
121
  page = params[:page].to_i
112
- handler.permit page >= 0 && page < 1000
122
+ handler.permit page >= 0 && page < 1000000
113
123
 
114
124
  query = query.limit(per_page, page)
115
125
 
116
- res = {entries: query.load_all}
126
+ res = {entries: page_frame(handler, query.load_all)}
117
127
  res[:count] = count if count
118
128
  res
119
129
  end
@@ -123,24 +133,60 @@ module Engine2
123
133
  model = assets[:model]
124
134
  sfields = lookup(:search_field_list)
125
135
  handler.permit sfields
126
- hash.each_pair do |name, value|
127
- handler.permit name = sfields.find{|sf|sf.to_sym == name}
128
136
 
137
+ filters = @filters || static.filters ? static.filters.to_h.merge(@filters.to_h) : {}
138
+
139
+ sfields.each do |name|
129
140
  type_info = model.find_type_info(name)
130
- query = if filter = (@filters && @filters[name]) || (dynamic? && (static.filters && static.filters[name]))
131
- filter.(handler, query, hash)
132
- elsif filter = DefaultFilters[type_info[:otype]]
133
- name = model.type_info[name] ? model.table_name.q(name) : Sequel.expr(name)
134
- filter.(query, name, value, type_info, hash)
141
+ name = name.to_sym
142
+ filter = filters[name]
143
+ if !(value = hash[name]).nil? # use key?
144
+ query = if filter
145
+ filter.(handler, query, hash)
146
+ elsif filter = DefaultFilters[type_info[:otype]]
147
+ name = model.type_info[name] ? model.table_name.q(name) : Sequel.expr(name)
148
+ filter.(query, name, value, type_info, hash)
149
+ else
150
+ raise E2Error.new("Filter not found for field '#{name}' in model '#{model}'") unless filter
151
+ end
152
+ handler.permit query
135
153
  else
136
- raise E2Error.new("Filter not found for field '#{name}' in model '#{model}'") unless filter
154
+ if filter
155
+ query = filter.(handler, query, hash)
156
+ handler.permit query
157
+ end
137
158
  end
138
-
139
- handler.permit query
140
159
  end
160
+
161
+ # hash.each_pair do |name, value|
162
+ # handler.permit name = sfields.find{|sf|sf.to_sym == name}
163
+ # type_info = model.find_type_info(name)
164
+ # query = if filter = (@filters && @filters[name]) || (dynamic? && (static.filters && static.filters[name]))
165
+ # filter.(handler, query, hash)
166
+ # elsif filter = DefaultFilters[type_info[:otype]]
167
+ # name = model.type_info[name] ? model.table_name.q(name) : Sequel.expr(name)
168
+ # filter.(query, name, value, type_info, hash)
169
+ # else
170
+ # raise E2Error.new("Filter not found for field '#{name}' in model '#{model}'") unless filter
171
+ # end
172
+ # handler.permit query
173
+ # end
174
+
141
175
  query
142
176
  end
143
177
 
178
+ def list_order query, handler, order, asc
179
+ model = assets[:model]
180
+ query = if order_blk = (@orders && @orders[order]) || (dynamic? && (static.orders && static.orders[order]))
181
+ order_blk.(handler, query)
182
+ else
183
+ order = model.table_name.q(order) if model.type_info[order]
184
+ query.order(order)
185
+ end
186
+
187
+ asc ? query : query.reverse
188
+ end
189
+
144
190
  def list_context query, handler
145
191
  query
146
192
  end
@@ -165,7 +211,7 @@ module Engine2
165
211
  def pre_run
166
212
  super
167
213
  menu(:panel_menu).option_at 0, :close, icon: "remove"
168
- panel_title "#{:list.icon} #{LOCS[assets[:assoc][:name]]}"
214
+ panel_title "#{assets[:model].model_icon.icon} #{LOCS[assets[:assoc][:name]]}"
169
215
  end
170
216
 
171
217
  def list_context query, handler
@@ -285,4 +331,3 @@ module Engine2
285
331
  end
286
332
  end
287
333
  end
288
-
@@ -2,7 +2,6 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Engine2
5
-
6
5
  class SaveAction < Action
7
6
  include ActionSaveSupport
8
7
  end
@@ -26,5 +25,4 @@ module Engine2
26
25
  self.validate_only = true
27
26
  action_type :star_to_many_field_approve
28
27
  end
29
-
30
28
  end
@@ -2,7 +2,6 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Engine2
5
-
6
5
  class ActionNode < BasicObject
7
6
  ACCESS_FORBIDDEN ||= ->h{false}
8
7
  attr_reader :parent, :name, :number, :nodes, :recheck_access
@@ -201,7 +200,7 @@ module Engine2
201
200
  true
202
201
  end
203
202
 
204
- ::Kernel::puts "ACTION NODES: #{ActionNode.count}, Panels: #{panels}, Time: #{::Time.now - time}, Thefts: #{thefts}"
203
+ ::Kernel::puts "ACTION NODES: #{ActionNode.count}, Panels: #{panels}, Thefts: #{thefts}, Time: #{::Time.now - time}"
205
204
  end
206
205
 
207
206
  def p *args
@@ -209,7 +208,6 @@ module Engine2
209
208
  end
210
209
  end
211
210
 
212
-
213
211
  class ActionNodeBundle
214
212
  def initialize node, node_names
215
213
  @node = node
@@ -220,4 +218,4 @@ module Engine2
220
218
  @node_names.each{|an| @node[an].__send__(name, *args, &blk)}
221
219
  end
222
220
  end
223
- end
221
+ end
@@ -126,6 +126,10 @@ class String
126
126
  s
127
127
  end
128
128
  end
129
+
130
+ def escape_html
131
+ Rack::Utils.escape_html(self)
132
+ end
129
133
  end
130
134
 
131
135
  class Symbol
@@ -138,12 +142,18 @@ class Symbol
138
142
  end
139
143
  end
140
144
 
145
+ def loc
146
+ Engine2::LOCS[self]
147
+ end
148
+
141
149
  def q col
142
150
  col.qualify self
143
151
  end
144
152
 
145
- def loc
146
- Engine2::LOCS[self]
153
+ def html body = '', attrs = {}
154
+ element = self.to_s
155
+ attrs = attrs.map{|k, v| "#{k}=\"#{v}\""}.join(" ")
156
+ "<#{element} #{attrs}>#{body}</#{element}>"
147
157
  end
148
158
  end
149
159
 
@@ -174,7 +184,7 @@ class Sequel::Database
174
184
  attr_accessor :models, :default_schema
175
185
 
176
186
  def cache_file
177
- "#{Engine2::SETTINGS.path_for(:db_path)}/#{opts[:orig_opts][:name]}.dump"
187
+ "#{Engine2::SETTINGS.path_for(:db_path)}/#{opts[:orig_opts][:name]}.schema_cache"
178
188
  end
179
189
 
180
190
  def load_schema_cache_from_file
@@ -282,7 +292,7 @@ module E2Model
282
292
  next if info[:primary_key] && !model.natural_key
283
293
 
284
294
  value = values[name].to_s
285
- value.strip! unless info[:dont_strip]
295
+ value.strip! unless value.frozen? || info[:dont_strip]
286
296
  if value.empty?
287
297
  if req = info[:required]
288
298
  errors.add(name, req[:message]) if !req[:if] || req[:if].(self)
@@ -392,7 +402,6 @@ module E2Model
392
402
  else
393
403
  select(*pk.map{|k| model.table_name.q(k)})
394
404
  end
395
-
396
405
  end
397
406
 
398
407
  def extract_select sel, al = nil, &blk
@@ -413,41 +422,41 @@ module E2Model
413
422
 
414
423
  def setup_query fields
415
424
  joins = {}
416
- type_info = model.type_info
417
425
  model_table_name = model.table_name
418
426
 
419
427
  select = @opts[:select].map do |sel|
420
428
  extract_select sel do |table, name, aliaz|
421
- info = if table
429
+ mdl = if table
422
430
  if table == model_table_name
423
431
  model
424
432
  else
425
433
  assoc = model.many_to_one_associations[table] || model.many_to_many_associations[table]
426
434
  raise Engine2::E2Error.new("Association #{table} not found for model #{model}") unless assoc
427
435
  assoc.associated_class
428
- end.type_info
436
+ end
429
437
  else
430
- type_info
438
+ model
431
439
  end
432
440
 
433
- table ||= model_table_name
434
- if table == model_table_name
441
+ mdl_table_name = mdl.table_name
442
+ table ||= mdl_table_name
443
+ if mdl_table_name == model_table_name
435
444
  fields << name
436
445
  else
437
446
  fields << table.q(name)
438
- joins[table] ||= model.many_to_one_associations[table] || model.many_to_many_associations[table]
447
+ joins[mdl_table_name] ||= model.many_to_one_associations[table] || model.many_to_many_associations[table]
439
448
  end
440
449
 
441
- f_info = info[name]
442
- raise Engine2::E2Error.new("Column #{name} not found for table #{table || model_table_name}") unless f_info
450
+ f_info = mdl.type_info[name]
451
+ raise Engine2::E2Error.new("Column #{name} not found for table #{table}") unless f_info
443
452
  if f_info[:dummy]
444
453
  nil
445
454
  else
446
- qname = table.q(name)
447
- if table != model_table_name
448
- Sequel.alias_columns_in_joins ? qname.as(:"#{table}__#{name}") : qname
449
- else
455
+ qname = mdl_table_name.q(name)
456
+ if table == model_table_name
450
457
  qname
458
+ else
459
+ Sequel.alias_columns_in_joins ? qname.as(:"#{table}__#{name}") : qname
451
460
  end
452
461
  end
453
462
  end
@@ -488,7 +497,6 @@ module Sequel
488
497
  @error = error
489
498
  end
490
499
  end
491
-
492
500
  end
493
501
 
494
502
  module Engine2
@@ -501,7 +509,8 @@ module Engine2
501
509
  model_path: 'models',
502
510
  view_path: 'views',
503
511
  asset_path: 'assets',
504
- conf_path: 'conf'
512
+ conf_path: 'conf',
513
+ log_path: 'log'
505
514
  }
506
515
 
507
516
  def SETTINGS.path_for path
@@ -549,10 +558,10 @@ module Engine2
549
558
  load "#{Engine2::SETTINGS[:app_path]}/boot.rb"
550
559
 
551
560
  Sequel::DATABASES.each &:load_schema_cache_from_file
552
- @model_boot_blk.() if @model_boot_blk
553
561
  load 'engine2/models/Files.rb'
554
562
  load 'engine2/models/UserInfo.rb'
555
563
  Dir["#{Engine2::SETTINGS.path_for(:model_path)}/*"].each{|m| load m}
564
+ @model_boot_blk.() if @model_boot_blk
556
565
  puts "MODELS: #{Sequel::DATABASES.reduce(0){|s, d|s + d.models.size}}, Time: #{Time.now - t}"
557
566
  Sequel::DATABASES.each do |db|
558
567
  db.dump_schema_cache_to_file
@@ -119,16 +119,16 @@ module Engine2
119
119
  slim name.to_sym
120
120
  end
121
121
 
122
- production = environment == :production
123
-
124
- set :slim, pretty: !production, sort_attrs: false
125
- set :sessions, expire_after: 3600, httponly: production # , secure: production
122
+ set :slim, pretty: !production?, sort_attrs: false
123
+ unless production? # use Engine2::SETTINGS[:reloading] ?
124
+ set :sessions, expire_after: 3600
125
+ # set :session_store, Rack::Session::Pool
126
+ end
126
127
 
127
128
  helpers do
128
129
  def find_template(views, name, engine, &block)
129
130
  views.each{|v| super(v, name, engine, &block)}
130
131
  end
131
132
  end
132
-
133
133
  end
134
134
  end