lux-fw 0.2.3 → 0.5.32
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.version +1 -1
- data/bin/README.md +33 -0
- data/bin/build_gem +76 -0
- data/bin/cli/config.rb +44 -0
- data/bin/cli/console.rb +61 -0
- data/bin/cli/dbconsole.rb +8 -0
- data/bin/cli/eval.rb +32 -0
- data/bin/cli/generate.rb +88 -0
- data/bin/cli/get.rb +12 -0
- data/bin/cli/new.rb +22 -0
- data/bin/cli/routes.rb +90 -0
- data/bin/cli/secrets.rb +40 -0
- data/bin/cli/server.rb +24 -0
- data/bin/cli/stats.rb +133 -0
- data/bin/lux +24 -65
- data/lib/common/class_attributes.rb +40 -64
- data/lib/common/class_callbacks.rb +34 -51
- data/lib/common/crypt.rb +10 -8
- data/lib/common/free_struct.rb +42 -0
- data/lib/common/hash_with_indifferent_access.rb +1 -1
- data/lib/common/html_tag_builder.rb +26 -2
- data/lib/common/url.rb +58 -40
- data/lib/lux/application/application.rb +290 -117
- data/lib/lux/application/lib/nav.rb +64 -38
- data/lib/lux/application/lib/render.rb +2 -1
- data/lib/lux/cache/cache.rb +87 -62
- data/lib/lux/cache/lib/{ram.rb → memory.rb} +5 -7
- data/lib/lux/cache/lib/null.rb +6 -8
- data/lib/lux/config/config.rb +109 -52
- data/lib/lux/config/lib/plugin.rb +65 -0
- data/lib/lux/config/lib/secrets.rb +48 -0
- data/lib/lux/controller/controller.rb +241 -0
- data/lib/lux/current/current.rb +33 -47
- data/lib/lux/current/lib/session.rb +72 -0
- data/lib/lux/delayed_job/delayed_job.rb +14 -7
- data/lib/lux/delayed_job/lib/memory.rb +7 -5
- data/lib/lux/delayed_job/lib/redis.rb +1 -1
- data/lib/lux/error/error.rb +164 -62
- data/lib/lux/event_bus/event_bus.rb +27 -0
- data/lib/lux/lux.rb +103 -66
- data/lib/lux/mailer/mailer.rb +23 -25
- data/lib/lux/response/lib/file.rb +81 -0
- data/lib/lux/response/lib/header.rb +14 -1
- data/lib/lux/response/response.rb +64 -56
- data/lib/lux/view/cell.rb +102 -0
- data/lib/lux/{helper → view}/helper.rb +39 -23
- data/lib/lux/view/lib/cell_helpers.rb +29 -0
- data/lib/lux/{helper/helpers/mailer_helper.rb → view/lib/helper_modules.rb} +7 -1
- data/lib/lux/{template/template.rb → view/view.rb} +21 -24
- data/lib/lux-fw.rb +4 -2
- data/lib/overload/array.rb +12 -6
- data/lib/overload/blank.rb +0 -1
- data/lib/overload/dir.rb +18 -0
- data/lib/overload/file.rb +1 -6
- data/lib/overload/hash.rb +56 -13
- data/lib/overload/integer.rb +2 -2
- data/lib/overload/it.rb +4 -4
- data/lib/overload/object.rb +37 -8
- data/lib/overload/{r.rb → raise_variants.rb} +23 -4
- data/lib/overload/string.rb +22 -6
- data/misc/demo/app/cells/demo_cell.rb +12 -0
- data/misc/demo/app/controllers/application_controller.rb +7 -0
- data/misc/demo/app/controllers/main/root_controller.rb +9 -0
- data/misc/demo/app/routes.rb +5 -0
- data/misc/demo/config/application.rb +14 -0
- data/misc/demo/config/assets.rb +6 -0
- data/misc/demo/config/environment.rb +7 -0
- data/misc/puma_auto_tune.rb +43 -0
- data/misc/unicorn.rb +37 -0
- data/{lib/lux → plugins}/api/api.rb +46 -29
- data/plugins/api/lib/attr.rb +31 -0
- data/{lib/lux → plugins}/api/lib/dsl.rb +3 -6
- data/{lib/lux → plugins}/api/lib/error.rb +0 -0
- data/{lib/lux → plugins}/api/lib/model_api.rb +51 -12
- data/{lib/lux → plugins}/api/lib/response.rb +31 -17
- data/{bin/cli/am → plugins/db/auto_migrate/auto_migrate.rb} +18 -35
- data/plugins/db/helpers/array_search.rb +27 -0
- data/plugins/db/helpers/before_save_filters.rb +32 -0
- data/plugins/db/helpers/composite_primary_keys.rb +36 -0
- data/plugins/db/helpers/core.rb +94 -0
- data/plugins/db/helpers/dataset_methods.rb +138 -0
- data/plugins/db/helpers/enums_plugin.rb +52 -0
- data/plugins/db/helpers/find_precache.rb +31 -0
- data/plugins/db/helpers/link_objects.rb +84 -0
- data/plugins/db/helpers/schema_checks.rb +83 -0
- data/plugins/db/helpers/typero_adapter.rb +71 -0
- data/plugins/db/logger/config.rb +22 -0
- data/plugins/db/logger/lux_response_adapter.rb +10 -0
- data/plugins/db/paginate/helper.rb +32 -0
- data/plugins/db/paginate/sequel_adapter.rb +23 -0
- data/plugins/exceptions/simple_exception.rb +64 -0
- data/plugins/favicon/favicon.rb +10 -0
- data/plugins/html/html_form.rb +118 -0
- data/plugins/html/html_input.rb +98 -0
- data/plugins/html/html_menu.rb +79 -0
- data/plugins/html/input_types.rb +346 -0
- data/plugins/js_widgets/js_widgets.rb +15 -0
- data/plugins/oauth/lib/facebook.rb +35 -0
- data/plugins/oauth/lib/github.rb +38 -0
- data/plugins/oauth/lib/google.rb +41 -0
- data/plugins/oauth/lib/linkedin.rb +41 -0
- data/plugins/oauth/lib/stackexchange.rb +41 -0
- data/plugins/oauth/lib/twitter.rb +38 -0
- data/plugins/oauth/oauth.rb +42 -0
- data/{lib/common → plugins/policy}/policy.rb +6 -7
- data/tasks/loader.rb +49 -0
- metadata +151 -49
- data/bin/cli/assets +0 -41
- data/bin/cli/console +0 -51
- data/bin/cli/dev +0 -1
- data/bin/cli/eval +0 -24
- data/bin/cli/exceptions +0 -62
- data/bin/cli/generate +0 -86
- data/bin/cli/get +0 -5
- data/bin/cli/nginx +0 -34
- data/bin/cli/production +0 -1
- data/bin/cli/render +0 -18
- data/bin/cli/routes +0 -14
- data/bin/cli/server +0 -4
- data/bin/cli/stat +0 -1
- data/bin/cli/systemd +0 -36
- data/bin/txt/nginx.conf +0 -46
- data/bin/txt/siege-and-puma.txt +0 -3
- data/lib/common/base32.rb +0 -47
- data/lib/common/dynamic_class.rb +0 -28
- data/lib/common/folder_model.rb +0 -50
- data/lib/common/generic_model.rb +0 -62
- data/lib/lux/application/lib/plugs.rb +0 -10
- data/lib/lux/application/lib/route_test.rb +0 -64
- data/lib/lux/cache/lib/memcached.rb +0 -3
- data/lib/lux/cell/cell.rb +0 -261
- data/lib/lux/current/lib/static_file.rb +0 -103
- data/lib/lux/helper/helpers/application_helper.rb +0 -3
- data/lib/lux/helper/helpers/html_helper.rb +0 -3
- data/lib/overload/auto_loader.rb +0 -27
- data/lib/overload/module.rb +0 -10
- data/lib/overload/string_inflections.rb +0 -8
@@ -0,0 +1,31 @@
|
|
1
|
+
# @list = LinkedUser
|
2
|
+
# .order(Sequel.desc(:updated_at))
|
3
|
+
# .where(user_id: user.id)
|
4
|
+
# .limit(20)
|
5
|
+
# .all
|
6
|
+
# .precache(:job_id)
|
7
|
+
# .precache(:org_id)
|
8
|
+
|
9
|
+
class Sequel::Model
|
10
|
+
module ClassMethods
|
11
|
+
# find will cache all finds in a scope
|
12
|
+
def find id
|
13
|
+
return nil if id.blank?
|
14
|
+
Lux.current.cache("#{self}/#{id}") { where(id:id).first }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class Array
|
20
|
+
# we have to call all on set and then precache
|
21
|
+
def precache field, klass=nil
|
22
|
+
list = self.map(&field).uniq.sort
|
23
|
+
klass ||= field.to_s.sub(/_ids?$/, '').classify.constantize
|
24
|
+
|
25
|
+
for el in klass.where(id: list).all
|
26
|
+
Lux.current.cache("#{klass}/#{el.id}") { el.dup }
|
27
|
+
end
|
28
|
+
|
29
|
+
self
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# @notes.preload(:bucket)
|
2
|
+
|
3
|
+
# class Array
|
4
|
+
# def preload(name)
|
5
|
+
# field = "#{name.to_s.singularize}_id".to_sym
|
6
|
+
# ids = self.map{ |o| o[field] }.uniq
|
7
|
+
# buckets = name.to_s.classify.constantize.where(id:ids).to_a
|
8
|
+
|
9
|
+
# for o in self
|
10
|
+
# o.send "#{name}=", buckets.select{ |el| o.id==el.id }[0]
|
11
|
+
# end
|
12
|
+
# end
|
13
|
+
# end
|
14
|
+
|
15
|
+
class Sequel::Model
|
16
|
+
module ClassMethods
|
17
|
+
|
18
|
+
# Object.genders
|
19
|
+
# @object.gender
|
20
|
+
def link name, opts={}
|
21
|
+
name_s = name.to_s.singularize
|
22
|
+
name_p = name.to_s.pluralize
|
23
|
+
|
24
|
+
raise 'Please use enums plugin' if opts[:collection]
|
25
|
+
|
26
|
+
klass = opts[:class] ? opts[:class].to_s : name.to_s.singularize.classify
|
27
|
+
|
28
|
+
# link :country, class: Country -> many_to_one :country
|
29
|
+
if name.to_s == name_s
|
30
|
+
class_eval %[
|
31
|
+
def #{name_s}
|
32
|
+
@#{name_s}_cached ||= #{klass}.find(#{name_s}_id)
|
33
|
+
end
|
34
|
+
def #{name_s}=(object)
|
35
|
+
@#{name_s}_cached = object
|
36
|
+
end
|
37
|
+
]
|
38
|
+
|
39
|
+
# link :cities
|
40
|
+
# postgre integer array, has to be in form #{name.singularize}_ids
|
41
|
+
# city_ids = [1,2,4]
|
42
|
+
# cities -> [<City:1>, <City:2>, <City:4>]
|
43
|
+
elsif db_schema["#{name_s}_ids".to_sym]
|
44
|
+
# puts "* #{to_s}.link :#{name} -> inline #{name_s}_ids".yellow
|
45
|
+
class_eval %[
|
46
|
+
def #{name_p}
|
47
|
+
return [] if #{name_s}_ids.blank?
|
48
|
+
ids = #{name_s}_ids.uniq
|
49
|
+
|
50
|
+
if !ids.first
|
51
|
+
[]
|
52
|
+
elsif !ids[1]
|
53
|
+
[#{klass}.find(ids.first)]
|
54
|
+
else
|
55
|
+
#{klass}.where(Sequel.lit('id in ('+ids.join(',')+')')).all
|
56
|
+
end
|
57
|
+
end
|
58
|
+
]
|
59
|
+
|
60
|
+
# link :countries -> one_to_many :countries
|
61
|
+
else
|
62
|
+
# one_to_many name, opts
|
63
|
+
field = "#{to_s.tableize.singularize}_id"
|
64
|
+
|
65
|
+
if klass.constantize.db_schema[field.to_sym]
|
66
|
+
comm = "#{klass}.where(#{field}:id).default"
|
67
|
+
else
|
68
|
+
# we have a link table
|
69
|
+
cname = klass[0,1] > to_s[0,1] ? "#{to_s}#{klass}" : "#{klass}#{to_s}"
|
70
|
+
comm = "#{klass}.xwhere('id in (select #{klass.tableize.singularize}_id from #{cname.tableize} where #{to_s.tableize.singularize}_id=?)', id)"
|
71
|
+
puts "* #{to_s}.link :#{name} -> #{comm}"
|
72
|
+
end
|
73
|
+
|
74
|
+
class_eval %[
|
75
|
+
def #{name_p}
|
76
|
+
#{comm}
|
77
|
+
end
|
78
|
+
]
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# inits and saves postgre
|
2
|
+
# string and integer arrays
|
3
|
+
# hstore as hash with indifferent access to keys
|
4
|
+
|
5
|
+
module Sequel::Plugins::LuxInitAndSaveSchemaCheck
|
6
|
+
module ClassMethods
|
7
|
+
end
|
8
|
+
|
9
|
+
module DatasetMethods
|
10
|
+
end
|
11
|
+
|
12
|
+
module InstanceMethods
|
13
|
+
def validate
|
14
|
+
for field in columns
|
15
|
+
schema = db_schema[field]
|
16
|
+
|
17
|
+
# alert, no trim on to big field length
|
18
|
+
if schema[:max_length] && self[field] && self[field].length > schema[:max_length]
|
19
|
+
msg = 'Field "%s" max length is %s, got %d' % [field, schema[:max_length], self[field].length]
|
20
|
+
errors.add(field, msg)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
super
|
25
|
+
end
|
26
|
+
|
27
|
+
# set right values on
|
28
|
+
def after_initialize
|
29
|
+
new_vals = {}
|
30
|
+
# DB.extension :connection_validator, :pg_array, :pg_json
|
31
|
+
db_schema.each do |field, schema|
|
32
|
+
type = schema[:db_type]
|
33
|
+
|
34
|
+
if type.include?('[]') && ![Array, Sequel::Postgres::PGArray].include?(self[field].class)
|
35
|
+
data = self[field].to_s.gsub(/^\{|\}$/, '').split(',')
|
36
|
+
data = data.map(&:to_i) if schema[:type] == :integer
|
37
|
+
self[field] = data
|
38
|
+
elsif type == 'jsonb' && self[field].class != Sequel::Postgres::JSONBHash
|
39
|
+
self[field] = HashWithIndifferentAccess.new(JSON.load(self[field]) || {})
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
super
|
44
|
+
end
|
45
|
+
|
46
|
+
def before_save
|
47
|
+
@_array_hash_cache_fields = {}
|
48
|
+
|
49
|
+
db_schema.each do |field, schema|
|
50
|
+
if schema[:db_type].include?('[]')
|
51
|
+
@_array_hash_cache_fields[field] = self[field].dup
|
52
|
+
|
53
|
+
data = self[field].to_a
|
54
|
+
data = data.map(&:to_i) if schema[:type] == :integer
|
55
|
+
|
56
|
+
db_data = data.to_json
|
57
|
+
db_data[0,1] = '{'
|
58
|
+
db_data[-1,1] = '}'
|
59
|
+
self[field] = db_data
|
60
|
+
elsif ['json', 'jsonb'].index(schema[:db_type])
|
61
|
+
@_array_hash_cache_fields[field] = self[field].dup
|
62
|
+
|
63
|
+
# we use this to convert symbols in keys to strings
|
64
|
+
self[field] = JSON.load(self[field].to_json).to_json
|
65
|
+
elsif schema[:db_type] == 'boolean'
|
66
|
+
self[field] = false if self[field] == 0
|
67
|
+
self[field] = true if self[field] == 1
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
super
|
72
|
+
end
|
73
|
+
|
74
|
+
def after_save
|
75
|
+
@_array_hash_cache_fields.each { |k, v| self[k] = v }
|
76
|
+
|
77
|
+
super
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
Sequel::Model.plugin :lux_init_and_save_schema_check
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'typero'
|
4
|
+
|
5
|
+
module Sequel::Plugins::TyperoAttributes
|
6
|
+
module ClassMethods
|
7
|
+
def attributes(&block)
|
8
|
+
self.instance_variable_set :@typero, Typero.new(&block)
|
9
|
+
end
|
10
|
+
|
11
|
+
def typero
|
12
|
+
self.instance_variable_get :@typero
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
module InstanceMethods
|
17
|
+
# calling typero! on any object will validate all fields
|
18
|
+
def typero! field_name=nil
|
19
|
+
typero = self.class.typero || return
|
20
|
+
|
21
|
+
typero.validate(self) do |name, err|
|
22
|
+
errors.add(name, err) unless (errors.on(name) || []).include?(err)
|
23
|
+
end
|
24
|
+
|
25
|
+
# this are rules unique to database, so we check them here
|
26
|
+
typero.rules.each do |field, rule|
|
27
|
+
# check uniqe fields
|
28
|
+
if rule[:uniq]
|
29
|
+
id = self[:id] || 0
|
30
|
+
value = self[field]
|
31
|
+
|
32
|
+
# we only check if field is changed
|
33
|
+
if value.present? && column_changed?(field) && self.class.xwhere('LOWER(%s)=LOWER(?) and id<>?' % field, value, id).first
|
34
|
+
error = rule[:uniq].class == TrueClass ? %[Value '"#{value}"' for #{field} allready exists] : rule[:uniq]
|
35
|
+
errors.add(field, error) unless (errors.on(field) || []).include?(error)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# check protected fields
|
40
|
+
if rule[:protected] && self[:id]
|
41
|
+
if column_changed?(field)
|
42
|
+
error = rule[:protected].class == TrueClass ? "value once defined can't be overwritten." : rule[:protected]
|
43
|
+
errors.add(field, error) unless (errors.on(field) || []).include?(error)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# check single field if single field given
|
49
|
+
if field_name
|
50
|
+
raise ArgumentError.new 'Field :%s not found in %s' % [field_name, self] unless self[field_name]
|
51
|
+
return unless errors.on(field_name)
|
52
|
+
|
53
|
+
errors.on(field_name).join(', ')
|
54
|
+
end
|
55
|
+
|
56
|
+
true
|
57
|
+
end
|
58
|
+
|
59
|
+
def validate
|
60
|
+
typero!
|
61
|
+
super
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
module DatasetMethods
|
66
|
+
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
Sequel::Model.plugin :typero_attributes
|
71
|
+
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# Logs DB queries in console
|
2
|
+
# to active just load the file
|
3
|
+
if Lux.config(:log_to_stdout)
|
4
|
+
logger = Logger.new(STDOUT)
|
5
|
+
|
6
|
+
logger.formatter = proc { |severity, datetime, progname, msg|
|
7
|
+
elms = msg.split(/\(|s\)\s/, 3)
|
8
|
+
time = (elms[1].to_f * 1000).round(1)
|
9
|
+
|
10
|
+
if c = Thread.current[:db_q]
|
11
|
+
if c && c[:last] != msg
|
12
|
+
c[:last] = msg
|
13
|
+
c[:time] += elms[1].to_f
|
14
|
+
c[:cnt] += 1
|
15
|
+
|
16
|
+
Lux.log " #{c[:cnt].to_s.rjust(2)}. #{time} : #{elms[2].to_s.cyan}\n"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
}
|
20
|
+
|
21
|
+
DB.loggers << logger
|
22
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
Lux.app do
|
2
|
+
before do
|
3
|
+
Thread.current[:db_q] = { time: 0.0, cnt: 0 }
|
4
|
+
end
|
5
|
+
|
6
|
+
after do
|
7
|
+
next unless Thread.current[:db_q]
|
8
|
+
Lux.log " #{Thread.current[:db_q][:cnt]} DB queries, #{(Thread.current[:db_q][:time]*1000).round(1)} ms" if Thread.current[:db_q][:cnt] > 0
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module HtmlHelper
|
2
|
+
|
3
|
+
def paginate list
|
4
|
+
return unless list.respond_to?(:paginate_next)
|
5
|
+
return nil if list.paginate_page == 1 && !list.paginate_next
|
6
|
+
|
7
|
+
ret = ['<div class="paginate"><div>']
|
8
|
+
|
9
|
+
if list.paginate_page > 1
|
10
|
+
url = Url.current
|
11
|
+
list.paginate_page == 1 ? url.delete(list.paginate_param) : url.qs(list.paginate_param, list.paginate_page-1)
|
12
|
+
ret.push %[<a href="#{url.relative}">←</a>]
|
13
|
+
else
|
14
|
+
ret.push %[<span>←</span>]
|
15
|
+
end
|
16
|
+
|
17
|
+
ret.push %[<i>#{list.paginate_page == 1 ? '•' : list.paginate_page}</i>]
|
18
|
+
|
19
|
+
if list.paginate_next
|
20
|
+
url = Url.current
|
21
|
+
url.qs(list.paginate_param, list.paginate_page+1)
|
22
|
+
ret.push %[<a href="#{url.relative}">→</a>]
|
23
|
+
else
|
24
|
+
ret.push %[<span>→</span>]
|
25
|
+
end
|
26
|
+
|
27
|
+
ret.push '</div></div>'
|
28
|
+
ret.join('')
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Sequel::Plugins::LuxSimplePaginate
|
2
|
+
module DatasetMethods
|
3
|
+
def page size: 20, param: :page, page: nil, count: nil
|
4
|
+
page = (page || Lux.current.request.params[param]).to_i
|
5
|
+
page = 1 if page < 1
|
6
|
+
|
7
|
+
# ret = paginate(page, size).all
|
8
|
+
ret = offset((page-1) * size).limit(size+1).all
|
9
|
+
|
10
|
+
has_next = ret.length == size + 1
|
11
|
+
ret.pop if has_next
|
12
|
+
|
13
|
+
ret.define_singleton_method(:paginate_param) do; param ;end
|
14
|
+
ret.define_singleton_method(:paginate_page) do; page; end
|
15
|
+
ret.define_singleton_method(:paginate_next) do; has_next; end
|
16
|
+
|
17
|
+
ret
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
Sequel::Model.db.extension :pagination
|
23
|
+
Sequel::Model.plugin :lux_simple_paginate
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module SimpleException
|
2
|
+
extend self
|
3
|
+
|
4
|
+
ERROR_FOLDER ||= './log/exceptions'
|
5
|
+
|
6
|
+
def log exception
|
7
|
+
return if Lux.env == 'test'
|
8
|
+
# return if exception.class == LocalRaiseError
|
9
|
+
return unless Lux.current
|
10
|
+
|
11
|
+
history = exception.backtrace || []
|
12
|
+
history = history
|
13
|
+
.map{ |el| el.sub(Lux.root.to_s, '') }
|
14
|
+
.join("\n")
|
15
|
+
|
16
|
+
data = '%s in %s (user: %s)' % [exception.class, Lux.current.request.url, (Lux.current.var.user.email rescue 'guest')]
|
17
|
+
data = [data, exception.message, history].join("\n\n")
|
18
|
+
key = Digest::SHA1.hexdigest history
|
19
|
+
|
20
|
+
folder = Lux.root.join('log/exceptions').to_s
|
21
|
+
Dir.mkdir(folder) unless Dir.exists?(folder)
|
22
|
+
|
23
|
+
File.write("#{folder}/#{key}.txt", data)
|
24
|
+
|
25
|
+
key
|
26
|
+
end
|
27
|
+
|
28
|
+
def list
|
29
|
+
error_files = Dir['%s/*.txt' % ERROR_FOLDER].sort_by { |x| File.mtime(x) }.reverse
|
30
|
+
|
31
|
+
error_files.map do |file|
|
32
|
+
last_update = (Time.now - File.mtime(file)).to_i
|
33
|
+
|
34
|
+
age = if last_update < 60
|
35
|
+
'%s sec ago' % last_update
|
36
|
+
elsif last_update < 60*60
|
37
|
+
'%s mins ago' % (last_update/60).to_i
|
38
|
+
elsif last_update < 60*60*24
|
39
|
+
'%s hours ago' % (last_update/(60*60)).to_i
|
40
|
+
else
|
41
|
+
'%s days ago' % (last_update/(60*60*24)).to_i
|
42
|
+
end
|
43
|
+
|
44
|
+
{
|
45
|
+
file: file,
|
46
|
+
last_update: last_update,
|
47
|
+
desc: File.read(file).split("\n").first,
|
48
|
+
code: file.split('/').last.split('.').first,
|
49
|
+
age: age
|
50
|
+
}
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def get code
|
55
|
+
for el in list
|
56
|
+
return el if el[:code] == code
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def clear
|
61
|
+
system 'rm -rf "%s"' % ERROR_FOLDER
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
@@ -0,0 +1,10 @@
|
|
1
|
+
favicon = Lux.root.join('./public/favicon.png')
|
2
|
+
|
3
|
+
die './public/favicon.png not found' unless favicon.exist?
|
4
|
+
|
5
|
+
favicon = favicon.read
|
6
|
+
|
7
|
+
Lux.app.before do
|
8
|
+
response.send_file('favicon.png', inline: true, content: favicon) if
|
9
|
+
['/favicon.ico', '/apple-touch-icon.png'].include?(request.path.downcase)
|
10
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# = form Lux.current.var.user do |f|
|
4
|
+
# = f.hidden :company_id
|
5
|
+
# = f.row :name
|
6
|
+
# = f.row :counrty_id, :as=>:country
|
7
|
+
# .custom= f.input :email
|
8
|
+
# = f.submit 'Save'
|
9
|
+
|
10
|
+
Lux.plugin 'api'
|
11
|
+
|
12
|
+
ApplicationApi.before do
|
13
|
+
name = '[protected]:'
|
14
|
+
params.each do |k,v|
|
15
|
+
params[k] = Crypt.decrypt(v.split(name, 2)[1]) if v.is_a?(String) && v.starts_with?(name)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class HtmlForm
|
20
|
+
def initialize target, opts={}
|
21
|
+
opts[:method] = 'get' if opts.delete(:get)
|
22
|
+
opts[:method] ||= 'get' unless @target
|
23
|
+
opts[:method] ||= 'post'
|
24
|
+
opts[:method] = opts[:method].upcase
|
25
|
+
|
26
|
+
opts[:id] ||= 'form-%s' % Lux.current.uid
|
27
|
+
|
28
|
+
@target = target
|
29
|
+
@opts = opts
|
30
|
+
@before = [] # add stuff to the begining of the form block
|
31
|
+
|
32
|
+
if @target.respond_to?(:update)
|
33
|
+
@object = @target
|
34
|
+
@opts['data-model'] = @object.class.to_s.underscore.singularize
|
35
|
+
end
|
36
|
+
|
37
|
+
before
|
38
|
+
|
39
|
+
@opts[:action] = @target if @target
|
40
|
+
end
|
41
|
+
|
42
|
+
# render full form, accepts block
|
43
|
+
def render
|
44
|
+
data = @before.join('')
|
45
|
+
data += yield self
|
46
|
+
data = form data
|
47
|
+
|
48
|
+
@opts.tag(:form, data)
|
49
|
+
end
|
50
|
+
|
51
|
+
# run stuff after block initialize
|
52
|
+
def before
|
53
|
+
true
|
54
|
+
end
|
55
|
+
|
56
|
+
# hidden filed
|
57
|
+
def hidden name, opts={}
|
58
|
+
fname = @object.class.name.tableize.singularize rescue nil
|
59
|
+
|
60
|
+
if name.respond_to?(:update)
|
61
|
+
oname = name.class.name.tableize.singularize
|
62
|
+
if @object && @object.respond_to?("#{oname}_id") # grp
|
63
|
+
Lux::Current::EncryptParams.hidden_input "#{fname}[#{oname}_id]", name.id
|
64
|
+
else
|
65
|
+
[
|
66
|
+
Lux::Current::EncryptParams.hidden_input("#{fname}[model_id]", name.id),
|
67
|
+
Lux::Current::EncryptParams.hidden_input("#{fname}[model_type]", name.class.name),
|
68
|
+
].join('')
|
69
|
+
end
|
70
|
+
else
|
71
|
+
opts[:value] ||= @object[name] if @object && name.is_a?(Symbol)
|
72
|
+
name = '%s[%s]' % [fname, name] if fname && name.is_a?(Symbol)
|
73
|
+
|
74
|
+
if opts[:value].present?
|
75
|
+
Lux::Current::EncryptParams.hidden_input(name, opts[:value])
|
76
|
+
else
|
77
|
+
''
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# standard input linked to HtmlInput class
|
83
|
+
def input name, opts={}
|
84
|
+
@name = name
|
85
|
+
opts[:id] ||= Lux.current.uid
|
86
|
+
opts[:value] ||= Lux.current.request.params[name] if @opts[:method] == 'GET'
|
87
|
+
input_object = HtmlInput.new(@object)
|
88
|
+
data = input_object.render(name, opts)
|
89
|
+
@type = input_object.type
|
90
|
+
data
|
91
|
+
end
|
92
|
+
|
93
|
+
# submit button
|
94
|
+
def submit name=nil
|
95
|
+
name ||= 'Submit'
|
96
|
+
%[<li><button type="submit">#{name}</button></li>]
|
97
|
+
end
|
98
|
+
|
99
|
+
# render simple row
|
100
|
+
def row name, opts={}
|
101
|
+
node = input(name, opts)
|
102
|
+
label = %[<label for="#{opts[:id]}">#{opts[:label] || name.to_s.humanize}</label>]
|
103
|
+
%[<li class="as-#{opts[:as]}">#{label}#{node}<span class="error"></span></li>]
|
104
|
+
end
|
105
|
+
|
106
|
+
private
|
107
|
+
|
108
|
+
def form data
|
109
|
+
@opts[:class] = 'ul-form'
|
110
|
+
%[<ul>#{data}</ul>]
|
111
|
+
end
|
112
|
+
|
113
|
+
def encrypt data
|
114
|
+
return unless data
|
115
|
+
'[protected]:%s' % Crypt.encrypt(data)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
@@ -0,0 +1,98 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# input = HtmlInput.new(User.first)
|
4
|
+
# input.render :email
|
5
|
+
|
6
|
+
class HtmlInput
|
7
|
+
attr_accessor :type
|
8
|
+
|
9
|
+
def self.register name, &block
|
10
|
+
define_method "as_#{name}", &block
|
11
|
+
end
|
12
|
+
|
13
|
+
###
|
14
|
+
|
15
|
+
def initialize(obj=nil, opts={})
|
16
|
+
@object = obj
|
17
|
+
@globals = opts.dup
|
18
|
+
end
|
19
|
+
|
20
|
+
# if type is written in parameter :as=> use this helper function
|
21
|
+
def render(name, opts={})
|
22
|
+
if name.is_hash?
|
23
|
+
opts = name
|
24
|
+
name = :null
|
25
|
+
end
|
26
|
+
|
27
|
+
opts = opts_prepare name, opts.dup
|
28
|
+
@type = opts.delete(:as) || :text
|
29
|
+
send("as_#{@type}") rescue Lux::Error.inline("as_#{@type} (HtmlInput)")
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
# exports @name and @opts globals
|
35
|
+
def opts_prepare name, opts={}
|
36
|
+
|
37
|
+
opts[:as] ||= lambda do
|
38
|
+
return :select if opts[:collection]
|
39
|
+
|
40
|
+
data_type = @object[name].class.name rescue 'String'
|
41
|
+
|
42
|
+
if ['TrueClass','FalseClass'].index(data_type)
|
43
|
+
:checkbox
|
44
|
+
else
|
45
|
+
:string
|
46
|
+
end
|
47
|
+
end.call
|
48
|
+
|
49
|
+
# experimental, figure out collection unless defined
|
50
|
+
if name =~ /_id$/ && opts[:as] == :select && !opts[:collection]
|
51
|
+
class_name = name.to_s.split('_id')[0].capitalize
|
52
|
+
opts[:collection] = eval "#{class_name}.order('name').all"
|
53
|
+
end
|
54
|
+
|
55
|
+
opts[:as] ||= :select if opts[:collection]
|
56
|
+
opts[:id] ||= Lux.current.uid
|
57
|
+
opts[:value] ||= @object.send(name) if @object
|
58
|
+
opts[:value] = opts[:default] if opts[:value].blank?
|
59
|
+
opts[:name] = name.kind_of?(Symbol) && @object ? "#{@object.class.name.underscore}[#{name}]" : name
|
60
|
+
|
61
|
+
# convert decimal numbers to float
|
62
|
+
opts[:value] = opts[:value].to_f if opts[:value].class == BigDecimal
|
63
|
+
|
64
|
+
@label = opts.delete :label
|
65
|
+
@wrap = opts.delete(:wrap) || @globals[:wrap]
|
66
|
+
@name = name
|
67
|
+
@opts = opts
|
68
|
+
end
|
69
|
+
|
70
|
+
def prepare_collection data
|
71
|
+
ret = []
|
72
|
+
|
73
|
+
if data.is_hash?
|
74
|
+
# { id: {name:'Foo'} } : { id: 'Foo' }
|
75
|
+
for id, name in data
|
76
|
+
name = name[:name] if name.is_hash?
|
77
|
+
ret.push [id.to_s, name]
|
78
|
+
end
|
79
|
+
else
|
80
|
+
for el in data
|
81
|
+
if data[0].respond_to?(:select_name)
|
82
|
+
ret.push [el.id.to_s, el.select_name]
|
83
|
+
elsif data[0].respond_to?(:name)
|
84
|
+
ret.push [el.id.to_s, el.name]
|
85
|
+
elsif data[0].kind_of?(Array)
|
86
|
+
ret.push [el[0].to_s, el[1]]
|
87
|
+
elsif data.is_hash?
|
88
|
+
ret.push el
|
89
|
+
else
|
90
|
+
ret.push [el.to_s, el]
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
ret
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|