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.
Files changed (138) hide show
  1. checksums.yaml +4 -4
  2. data/.version +1 -1
  3. data/bin/README.md +33 -0
  4. data/bin/build_gem +76 -0
  5. data/bin/cli/config.rb +44 -0
  6. data/bin/cli/console.rb +61 -0
  7. data/bin/cli/dbconsole.rb +8 -0
  8. data/bin/cli/eval.rb +32 -0
  9. data/bin/cli/generate.rb +88 -0
  10. data/bin/cli/get.rb +12 -0
  11. data/bin/cli/new.rb +22 -0
  12. data/bin/cli/routes.rb +90 -0
  13. data/bin/cli/secrets.rb +40 -0
  14. data/bin/cli/server.rb +24 -0
  15. data/bin/cli/stats.rb +133 -0
  16. data/bin/lux +24 -65
  17. data/lib/common/class_attributes.rb +40 -64
  18. data/lib/common/class_callbacks.rb +34 -51
  19. data/lib/common/crypt.rb +10 -8
  20. data/lib/common/free_struct.rb +42 -0
  21. data/lib/common/hash_with_indifferent_access.rb +1 -1
  22. data/lib/common/html_tag_builder.rb +26 -2
  23. data/lib/common/url.rb +58 -40
  24. data/lib/lux/application/application.rb +290 -117
  25. data/lib/lux/application/lib/nav.rb +64 -38
  26. data/lib/lux/application/lib/render.rb +2 -1
  27. data/lib/lux/cache/cache.rb +87 -62
  28. data/lib/lux/cache/lib/{ram.rb → memory.rb} +5 -7
  29. data/lib/lux/cache/lib/null.rb +6 -8
  30. data/lib/lux/config/config.rb +109 -52
  31. data/lib/lux/config/lib/plugin.rb +65 -0
  32. data/lib/lux/config/lib/secrets.rb +48 -0
  33. data/lib/lux/controller/controller.rb +241 -0
  34. data/lib/lux/current/current.rb +33 -47
  35. data/lib/lux/current/lib/session.rb +72 -0
  36. data/lib/lux/delayed_job/delayed_job.rb +14 -7
  37. data/lib/lux/delayed_job/lib/memory.rb +7 -5
  38. data/lib/lux/delayed_job/lib/redis.rb +1 -1
  39. data/lib/lux/error/error.rb +164 -62
  40. data/lib/lux/event_bus/event_bus.rb +27 -0
  41. data/lib/lux/lux.rb +103 -66
  42. data/lib/lux/mailer/mailer.rb +23 -25
  43. data/lib/lux/response/lib/file.rb +81 -0
  44. data/lib/lux/response/lib/header.rb +14 -1
  45. data/lib/lux/response/response.rb +64 -56
  46. data/lib/lux/view/cell.rb +102 -0
  47. data/lib/lux/{helper → view}/helper.rb +39 -23
  48. data/lib/lux/view/lib/cell_helpers.rb +29 -0
  49. data/lib/lux/{helper/helpers/mailer_helper.rb → view/lib/helper_modules.rb} +7 -1
  50. data/lib/lux/{template/template.rb → view/view.rb} +21 -24
  51. data/lib/lux-fw.rb +4 -2
  52. data/lib/overload/array.rb +12 -6
  53. data/lib/overload/blank.rb +0 -1
  54. data/lib/overload/dir.rb +18 -0
  55. data/lib/overload/file.rb +1 -6
  56. data/lib/overload/hash.rb +56 -13
  57. data/lib/overload/integer.rb +2 -2
  58. data/lib/overload/it.rb +4 -4
  59. data/lib/overload/object.rb +37 -8
  60. data/lib/overload/{r.rb → raise_variants.rb} +23 -4
  61. data/lib/overload/string.rb +22 -6
  62. data/misc/demo/app/cells/demo_cell.rb +12 -0
  63. data/misc/demo/app/controllers/application_controller.rb +7 -0
  64. data/misc/demo/app/controllers/main/root_controller.rb +9 -0
  65. data/misc/demo/app/routes.rb +5 -0
  66. data/misc/demo/config/application.rb +14 -0
  67. data/misc/demo/config/assets.rb +6 -0
  68. data/misc/demo/config/environment.rb +7 -0
  69. data/misc/puma_auto_tune.rb +43 -0
  70. data/misc/unicorn.rb +37 -0
  71. data/{lib/lux → plugins}/api/api.rb +46 -29
  72. data/plugins/api/lib/attr.rb +31 -0
  73. data/{lib/lux → plugins}/api/lib/dsl.rb +3 -6
  74. data/{lib/lux → plugins}/api/lib/error.rb +0 -0
  75. data/{lib/lux → plugins}/api/lib/model_api.rb +51 -12
  76. data/{lib/lux → plugins}/api/lib/response.rb +31 -17
  77. data/{bin/cli/am → plugins/db/auto_migrate/auto_migrate.rb} +18 -35
  78. data/plugins/db/helpers/array_search.rb +27 -0
  79. data/plugins/db/helpers/before_save_filters.rb +32 -0
  80. data/plugins/db/helpers/composite_primary_keys.rb +36 -0
  81. data/plugins/db/helpers/core.rb +94 -0
  82. data/plugins/db/helpers/dataset_methods.rb +138 -0
  83. data/plugins/db/helpers/enums_plugin.rb +52 -0
  84. data/plugins/db/helpers/find_precache.rb +31 -0
  85. data/plugins/db/helpers/link_objects.rb +84 -0
  86. data/plugins/db/helpers/schema_checks.rb +83 -0
  87. data/plugins/db/helpers/typero_adapter.rb +71 -0
  88. data/plugins/db/logger/config.rb +22 -0
  89. data/plugins/db/logger/lux_response_adapter.rb +10 -0
  90. data/plugins/db/paginate/helper.rb +32 -0
  91. data/plugins/db/paginate/sequel_adapter.rb +23 -0
  92. data/plugins/exceptions/simple_exception.rb +64 -0
  93. data/plugins/favicon/favicon.rb +10 -0
  94. data/plugins/html/html_form.rb +118 -0
  95. data/plugins/html/html_input.rb +98 -0
  96. data/plugins/html/html_menu.rb +79 -0
  97. data/plugins/html/input_types.rb +346 -0
  98. data/plugins/js_widgets/js_widgets.rb +15 -0
  99. data/plugins/oauth/lib/facebook.rb +35 -0
  100. data/plugins/oauth/lib/github.rb +38 -0
  101. data/plugins/oauth/lib/google.rb +41 -0
  102. data/plugins/oauth/lib/linkedin.rb +41 -0
  103. data/plugins/oauth/lib/stackexchange.rb +41 -0
  104. data/plugins/oauth/lib/twitter.rb +38 -0
  105. data/plugins/oauth/oauth.rb +42 -0
  106. data/{lib/common → plugins/policy}/policy.rb +6 -7
  107. data/tasks/loader.rb +49 -0
  108. metadata +151 -49
  109. data/bin/cli/assets +0 -41
  110. data/bin/cli/console +0 -51
  111. data/bin/cli/dev +0 -1
  112. data/bin/cli/eval +0 -24
  113. data/bin/cli/exceptions +0 -62
  114. data/bin/cli/generate +0 -86
  115. data/bin/cli/get +0 -5
  116. data/bin/cli/nginx +0 -34
  117. data/bin/cli/production +0 -1
  118. data/bin/cli/render +0 -18
  119. data/bin/cli/routes +0 -14
  120. data/bin/cli/server +0 -4
  121. data/bin/cli/stat +0 -1
  122. data/bin/cli/systemd +0 -36
  123. data/bin/txt/nginx.conf +0 -46
  124. data/bin/txt/siege-and-puma.txt +0 -3
  125. data/lib/common/base32.rb +0 -47
  126. data/lib/common/dynamic_class.rb +0 -28
  127. data/lib/common/folder_model.rb +0 -50
  128. data/lib/common/generic_model.rb +0 -62
  129. data/lib/lux/application/lib/plugs.rb +0 -10
  130. data/lib/lux/application/lib/route_test.rb +0 -64
  131. data/lib/lux/cache/lib/memcached.rb +0 -3
  132. data/lib/lux/cell/cell.rb +0 -261
  133. data/lib/lux/current/lib/static_file.rb +0 -103
  134. data/lib/lux/helper/helpers/application_helper.rb +0 -3
  135. data/lib/lux/helper/helpers/html_helper.rb +0 -3
  136. data/lib/overload/auto_loader.rb +0 -27
  137. data/lib/overload/module.rb +0 -10
  138. 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}">&larr;</a>]
13
+ else
14
+ ret.push %[<span>&larr;</span>]
15
+ end
16
+
17
+ ret.push %[<i>#{list.paginate_page == 1 ? '&bull;' : 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}">&rarr;</a>]
23
+ else
24
+ ret.push %[<span>&rarr;</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