lux-fw 0.1.17 → 0.1.35

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 (73) hide show
  1. checksums.yaml +4 -4
  2. data/.version +1 -1
  3. data/bin/cli/am +38 -29
  4. data/bin/cli/assets +8 -4
  5. data/bin/cli/exceptions +6 -6
  6. data/bin/cli/generate +0 -0
  7. data/bin/cli/get +0 -0
  8. data/bin/cli/nginx +11 -5
  9. data/bin/cli/routes +0 -0
  10. data/bin/cli/systemd +36 -0
  11. data/bin/forever +0 -0
  12. data/bin/job_que +0 -0
  13. data/bin/lux +1 -0
  14. data/lib/common/base32.rb +0 -0
  15. data/lib/common/class_attributes.rb +13 -4
  16. data/lib/common/crypt.rb +6 -10
  17. data/lib/common/dynamic_class.rb +23 -0
  18. data/lib/common/generic_model.rb +0 -0
  19. data/lib/common/hash_with_indifferent_access.rb +352 -0
  20. data/lib/common/method_attr.rb +69 -0
  21. data/lib/lux/api/api.rb +26 -27
  22. data/lib/lux/api/lib/application_api.rb +26 -7
  23. data/lib/lux/api/lib/doc_builder.rb +18 -17
  24. data/lib/lux/api/lib/dsl.rb +23 -41
  25. data/lib/lux/api/lib/error.rb +3 -0
  26. data/lib/lux/api/lib/model_api.rb +22 -20
  27. data/lib/lux/api/lib/rescue.rb +5 -15
  28. data/lib/lux/api/lib/response.rb +46 -0
  29. data/lib/lux/cache/cache.rb +13 -6
  30. data/lib/lux/cell/cell.rb +3 -14
  31. data/lib/lux/config/config.rb +4 -3
  32. data/lib/lux/controller/controller.rb +3 -3
  33. data/lib/lux/controller/lib/nav.rb +6 -2
  34. data/lib/lux/error/error.rb +15 -14
  35. data/lib/lux/helper/helper.rb +5 -5
  36. data/lib/lux/helper/lib/html_tag.rb +67 -0
  37. data/lib/lux/html/lib/input_types.rb +26 -16
  38. data/lib/lux/lib/lux.rb +51 -0
  39. data/lib/lux/lux.rb +5 -52
  40. data/lib/lux/page/lib/response.rb +178 -0
  41. data/lib/lux/page/page.rb +72 -51
  42. data/lib/lux/rescue_from/rescue_from.rb +8 -6
  43. data/lib/lux/template/template.rb +1 -0
  44. data/lib/lux-fw.rb +2 -0
  45. data/lib/overload/array.rb +4 -0
  46. data/lib/overload/date.rb +2 -0
  47. data/lib/overload/hash.rb +19 -10
  48. data/lib/overload/it.rb +29 -0
  49. data/lib/overload/object.rb +3 -19
  50. data/lib/overload/r.rb +53 -0
  51. data/lib/overload/string.rb +5 -6
  52. data/lib/overload/string_inflections.rb +4 -3
  53. data/lib/plugins/assets/assets_plug.rb +9 -4
  54. data/lib/plugins/assets/helper_module_adapter.rb +4 -2
  55. data/lib/plugins/db_helpers/link_plugin.rb +2 -2
  56. data/lib/plugins/db_logger/init.rb +1 -1
  57. data/lib/vendor/mini_assets/lib/asset/css.rb +19 -0
  58. data/lib/vendor/mini_assets/lib/asset/js.rb +17 -0
  59. data/lib/vendor/mini_assets/lib/asset.rb +71 -0
  60. data/lib/vendor/mini_assets/lib/base/javascript.rb +13 -0
  61. data/lib/vendor/mini_assets/lib/base/stylesheet.rb +5 -0
  62. data/lib/vendor/mini_assets/lib/base.rb +69 -0
  63. data/lib/vendor/mini_assets/lib/manifest.rb +18 -0
  64. data/lib/vendor/mini_assets/lib/opts.rb +16 -0
  65. data/lib/vendor/mini_assets/mini_assets.rb +74 -0
  66. metadata +23 -10
  67. data/lib/common/class_method_params.rb +0 -94
  68. data/lib/overload/hash_wia.rb +0 -282
  69. data/lib/overload/inflections.rb +0 -199
  70. data/lib/vendor/mini_assets/mini_asset/base.rb +0 -167
  71. data/lib/vendor/mini_assets/mini_asset/css.rb +0 -38
  72. data/lib/vendor/mini_assets/mini_asset/js.rb +0 -38
  73. data/lib/vendor/mini_assets/mini_asset.rb +0 -31
@@ -1,5 +1,4 @@
1
1
  class Lux::Api
2
-
3
2
  # name 'Show user data'
4
3
  # param :email, type: :email, req: false
5
4
  # param :pass
@@ -8,56 +7,41 @@ class Lux::Api
8
7
  # @user.slice(:id, :name, :avatar, :email)
9
8
  # end
10
9
 
11
- class << self
12
- @@actions ||= {}
13
-
14
- def method_added(m)
15
- ClassMethodParams.add_method self, m
16
- end
17
-
18
- # helper for standard definition of parametars
19
- # param :o_id
20
- # param :o_id, Integer
21
- # param :o_id, Integer, req: false
22
- # param :o_id, req: false
23
- def param(field, type=String, opts={})
24
- opts = type.is_a?(Hash) ? type : opts.merge(type: type)
25
- opts[:name] = field
26
- opts[:req] = opts[:req].class.name == 'FalseClass' ? false : true
27
- opts[:type] ||= String
28
-
29
- ClassMethodParams.add_param self, :param, opts
30
- end
10
+ # helper for standard definition of parametars
11
+ # param :o_id
12
+ # param :o_id, Integer
13
+ # param :o_id, Integer, req: false
14
+ # param :o_id, req: false
15
+ method_attr :param do |field, type=String, opts={}|
16
+ opts = type.is_a?(Hash) ? type : opts.merge(type: type)
17
+ opts[:name] = field
18
+ opts[:req] = true if opts[:req].nil?
19
+ opts[:type] ||= String
20
+ opts
21
+ end
31
22
 
32
- # helper for standard definition of name
33
- def name(name)
34
- ClassMethodParams.add_param self, :name, name
35
- end
23
+ # helper for standard definition of name
24
+ method_attr :name
36
25
 
37
- # helper for standard definition of description
38
- def description(data)
39
- ClassMethodParams.add_param self, :description, name
40
- end
41
- end
26
+ # helper for standard definition of description
27
+ method_attr :data
42
28
 
43
29
  ###
44
30
 
45
31
  def check_params_and_mock_instance_variables action
46
- opts = ClassMethodParams.params self.class, action
47
-
48
- if opts && opts[:param]
49
- local = opts[:param].inject({}) { |h, el| o=el.dup; h[o.delete(:name)] = o; h }
32
+ @method_attr = self.class.method_attr[action] || {}
50
33
 
51
- rules = Typero.new local
34
+ # params check if framework default
35
+ if @method_attr[:param]
36
+ local = @method_attr[:param].inject({}) { |h, el| o=el.dup; h[o.delete(:name)] = o; h }
37
+ rules = Typero.new local
52
38
  errors = rules.validate(@params)
53
39
 
54
40
  if errors.keys.length > 0
55
41
  raise ArgumentError.new(errors.values.to_sentence) unless Lux.page
56
42
 
57
- Lux.page.status(400)
58
- @response[:error] = errors.values.to_sentence
59
- @response[:errors] = errors
60
- return false
43
+ Lux.page.status 400
44
+ return response.error errors.values.to_sentence
61
45
  end
62
46
 
63
47
  # define local prefixed @_ variables
@@ -66,8 +50,6 @@ class Lux::Api
66
50
  eval "@_#{key.downcase.gsub(/[^\w]/,'_')} = value" if key.length < 15 && value.present? && key =~ /^[\w]+$/
67
51
  end
68
52
  end
69
-
70
- true
71
53
  end
72
54
 
73
55
  end
@@ -0,0 +1,3 @@
1
+ class Lux::Api::Error < StandardError
2
+
3
+ end
@@ -56,12 +56,12 @@ class ModelApi < ApplicationApi
56
56
  return if report_errros_if_any @object
57
57
 
58
58
  if @object.id
59
- @message = "#{@class_name.capitalize} created"
59
+ message '%s created' % @class_name.capitalize
60
60
  else
61
61
  error 'object not created, error unknown'
62
62
  end
63
63
 
64
- @response[:path] = (@object.path rescue nil)
64
+ response.meta :path, @object.path if @object.respond_to?(:path)
65
65
 
66
66
  @object.attributes
67
67
  end
@@ -76,8 +76,8 @@ class ModelApi < ApplicationApi
76
76
  can? :update, @object
77
77
  @object.save if @object.valid?
78
78
  report_errros_if_any @object
79
- @message = "#{@class_name} updated"
80
- @response[:path] = (@object.path rescue nil)
79
+ response.message '%s updated' % @class_name
80
+ response.meta :path, @object.path if @object.respond_to?(:path)
81
81
  @object.attributes
82
82
  end
83
83
 
@@ -88,15 +88,19 @@ class ModelApi < ApplicationApi
88
88
 
89
89
  if @object.respond_to?(:is_active)
90
90
  @object.update is_active: false
91
- @message = 'Object deleted (exists in trashcan)'
91
+
92
+ message 'Object deleted (exists in trashcan)'
92
93
  elsif @object.respond_to?(:active)
93
94
  @object.update active: false
94
- @message = 'Object deleted (exists in trashcan)'
95
+
96
+ message 'Object deleted (exists in trashcan)'
95
97
  else
96
98
  @object.destroy
97
- @message = "#{@object.class.name} deleted"
99
+ message '%s deleted' % @object.class.name
98
100
  end
101
+
99
102
  report_errros_if_any @object
103
+
100
104
  @object.attributes
101
105
  end
102
106
 
@@ -112,26 +116,24 @@ class ModelApi < ApplicationApi
112
116
  error "No is_active, can't undelete"
113
117
  end
114
118
 
115
- @message = 'Object raised from the dead.'
119
+ response.message = 'Object raised from the dead.'
116
120
  end
117
121
 
118
- def report_errros_if_any(obj)
119
- @response[:object] = obj.class.to_s.tableize.singularize
120
- @response[:objects] = obj.class.to_s.tableize
121
-
122
+ def report_errros_if_any obj
122
123
  if obj.errors.count > 0
123
- @response[:error] = ''
124
- @response[:errors] = {}
125
- for k,v in obj.errors
124
+ for k, v in obj.errors
126
125
  desc = v.join(', ')
127
- desc = "#{k} #{desc}" if desc.starts_with?('is ')
128
- @response[:errors][k] = desc
129
- @response[:error] += ', ' unless @response[:error].blank?
130
- @response[:error] += desc
126
+
127
+ response.error k, desc
128
+ response.error desc
129
+ # desc = "#{k} #{desc}" if desc.starts_with?('is ')
131
130
  end
132
- Lux.page.status(400)
131
+
132
+ Lux.page.status 400
133
+
133
134
  return true
134
135
  end
136
+
135
137
  false
136
138
  end
137
139
 
@@ -1,18 +1,8 @@
1
- Lux::Api.class_eval do
1
+ Lux::Api.rescue_from(:default) do |msg|
2
+ response.error msg
2
3
 
3
- rescue_from(:default) do |msg|
4
- Lux.page.status $!.class
5
-
6
- data = { error:msg }
7
-
8
- if Lux.dev? && $!.class != StandardError
9
- data[:backtrace] = $!.backtrace.reject{ |el| el.index('/gems/') }.map{ |el| el.sub(Lux.root.to_s, '') }
10
- ap data[:backtrace]
11
- end
12
-
13
- ApplicationApi.new.decorate_response!(data)
14
-
15
- Lux.page.body data
4
+ if Lux.dev? && $!.class != Lux::Api::Error
5
+ data[:backtrace] = $!.backtrace.reject{ |el| el.index('/gems/') }.map{ |el| el.sub(Lux.root.to_s, '') }
6
+ ap data[:backtrace]
16
7
  end
17
-
18
8
  end
@@ -0,0 +1,46 @@
1
+ class Lux::Api::Response
2
+ attr_accessor :data
3
+ attr_accessor :message
4
+
5
+ def meta key, value
6
+ @meta ||= {}
7
+ @meta[key.to_s] = value
8
+ end
9
+
10
+ def error key, data=nil
11
+ if data
12
+ @error_hash ||= {}
13
+ @error_hash[key.to_s] = data
14
+ data
15
+ else
16
+ @errors ||= []
17
+ @errors.push key unless @errors.include?(key)
18
+ key
19
+ end
20
+ end
21
+
22
+ def message what
23
+ @message = what
24
+ end
25
+
26
+ def errors?
27
+ (@error_hash || @errors) ? true : false
28
+ end
29
+
30
+ def render
31
+ output = {}
32
+
33
+ output[:data] = @data if @data.present?
34
+ output[:meta] = @meta if @meta.present?
35
+ output[:message] = @message if @message.present?
36
+
37
+ if errors?
38
+ output[:error] ||= {}
39
+ output[:error][:messages] = @errors if @errors
40
+ output[:error][:hash] = @error_hash if @error_hash
41
+ end
42
+
43
+ output
44
+ end
45
+ alias :to_hash :render
46
+ end
@@ -35,14 +35,21 @@ module Lux::Cache
35
35
  @@server.delete(key)
36
36
  end
37
37
 
38
- def fetch key, ttl=nil, log=true
39
- ttl = ttl.to_i if ttl
40
- @@server.delete key if Lux.page && Lux.page.no_cache?
38
+ def fetch key, opts={}
39
+ opts = { ttl: opts } unless opts.is_a?(Hash)
40
+ opts = opts.to_opts!(:ttl, :log, :force)
41
+
42
+ opts.ttl = opts.ttl.to_i if opts.ttl
43
+ opts.log ||= true if opts.log.nil?
44
+ opts.force ||= true if opts.force.nil?
45
+
46
+ @@server.delete key if Lux.page && opts.force && Lux.page.no_cache?
47
+
48
+ Lux.log " Cache.fetch.get #{key} (ttl: #{opts.ttl.or(:nil)})".green if opts.log
41
49
 
42
- Lux.log " Cache.fetch.get #{key} (ttl: #{ttl.or(:nil)})" if log
43
- data = @@server.fetch key, ttl do
50
+ data = @@server.fetch key, opts.ttl do
44
51
  data = yield
45
- Lux.log " Cache.fetch.SET #{key} len:#{data.to_s.length}" if log
52
+ Lux.log " Cache.fetch.SET #{key} len:#{data.to_s.length}" if opts.log
46
53
  data
47
54
  end
48
55
 
data/lib/lux/cell/cell.rb CHANGED
@@ -61,6 +61,8 @@ class Lux::Cell
61
61
 
62
62
  ### INSTANCE METHODS
63
63
 
64
+ attr_reader :cell_action
65
+
64
66
  def initialize
65
67
  # before and after should be exected only once
66
68
  @executed_filters = {}
@@ -91,7 +93,7 @@ class Lux::Cell
91
93
 
92
94
  method_name = method_name.to_s.gsub('-', '_').gsub(/[^\w]/, '')
93
95
 
94
- Lux.log " #{self.class.to_s}(:#{method_name})".blue
96
+ Lux.log " #{self.class.to_s}(:#{method_name})".light_blue
95
97
  Lux.page.files_in_use.push "app/cells/#{self.class.to_s.underscore}.rb"
96
98
 
97
99
  @cell_action = method_name
@@ -137,19 +139,6 @@ class Lux::Cell
137
139
 
138
140
  opts[:template] = name if name
139
141
 
140
- # etag and cache string are same things, one is client based, other one is server based
141
- if opts[:cache]
142
- opts[:cache] = Lux.cache.generate_key(opts[:cache] )
143
- opts[:etag] ||= opts[:cache]
144
- end
145
-
146
- return if opts[:etag] && Lux.page.etag(opts[:etag])
147
-
148
- if opts[:cache]
149
- data = Lux.cache.get(opts[:cache])
150
- return Lux.page.body(data) if data
151
- end
152
-
153
142
  render_resolve_body(opts)
154
143
  Lux.cache.set(opts[:cache], Lux.page.body) if opts[:cache]
155
144
  end
@@ -74,11 +74,12 @@ module Lux::Config
74
74
  speed = ((Time.now - load_start)*1000).round.to_s.sub(/(\d)(\d{3})$/,'\1s \2')+'ms'
75
75
  ram = `ps -o rss -p #{$$}`.chomp.split("\n").last.to_i / 1000
76
76
  opts = []
77
- opts.push Lux.verbose? ? 'verbose'.yellow : 'non-verbose'.green
78
- opts.push Lux.config(:auto_code_reload) ? 'code-reload'.yellow : 'no-code-reload'.green
77
+ opts.push Lux.verbose? ? 'verbose'.yellow : 'no-verbose'.green
78
+ opts.push Lux.config(:auto_code_reload) ? 'auto-code-reload'.yellow : 'no-code-reload'.green
79
79
 
80
- "* #{'Lux'.blue} loaded #{Lux.env('RACK_ENV').green} (#{opts.join(', ')}) mode in #{speed.to_s.white}, uses #{ram.to_s.white} MB RAM with total of #{Gem.loaded_specs.keys.length.to_s.white} gems in spec"
80
+ "* #{'Lux'.white} loaded #{Lux.env('RACK_ENV').green} (#{opts.join(', ')}) mode in #{speed.to_s.white}, uses #{ram.to_s.white} MB RAM with total of #{Gem.loaded_specs.keys.length.to_s.white} gems in spec"
81
81
  end
82
+
82
83
  end
83
84
 
84
85
  class Object
@@ -66,7 +66,7 @@ class Lux::Controller
66
66
 
67
67
  def rescued_main
68
68
  if respond_to?(:main)
69
- @@rescue_from_ivar.call { main }
69
+ @@rescue_from_ivar.call(self) { main }
70
70
  else
71
71
  Lux.error %[No instance method "main" in Lux::Controller defiend]
72
72
  end
@@ -81,7 +81,7 @@ class Lux::Controller
81
81
  when nil
82
82
  '/'.ljust(20).green
83
83
  else
84
- @route.to_s.ljust(20).blue
84
+ @route.to_s.ljust(20).light_blue
85
85
  end
86
86
 
87
87
  route_target = case @route_target
@@ -169,7 +169,7 @@ class Lux::Controller
169
169
 
170
170
  object, action = object if object.is_a? Array
171
171
 
172
- Lux.log ' %s %s # %s' % [object.to_s.blue, nav.path, @route_object]
172
+ Lux.log ' %s %s # %s' % [object.to_s.light_blue, nav.path, @route_object]
173
173
 
174
174
  if action
175
175
  object.action action
@@ -7,7 +7,7 @@ class Lux::Controller::Nav
7
7
  # acepts path as a string
8
8
  def initialize request
9
9
  @path = request.path.split('/').slice(1, 100) || []
10
- @root = @path.shift if @path.first
10
+ shift_to_root if @path.first
11
11
 
12
12
  @subdomain = request.host.split('.')
13
13
  @domain = @subdomain.pop(2).join('.')
@@ -22,7 +22,7 @@ class Lux::Controller::Nav
22
22
 
23
23
  def shift_to_root
24
24
  @root.tap do
25
- @root = @path.shift
25
+ @root = @path.shift.to_s.gsub('-', '_')
26
26
  build_full
27
27
  end
28
28
  end
@@ -48,6 +48,10 @@ class Lux::Controller::Nav
48
48
  @path.first
49
49
  end
50
50
 
51
+ def first= data
52
+ @path[0] = data
53
+ end
54
+
51
55
  def second
52
56
  @path[1]
53
57
  end
@@ -16,14 +16,13 @@ NotFoundError ||= Class.new(StandardError)
16
16
  RateLimitError ||= Class.new(StandardError)
17
17
 
18
18
  module Lux::Error
19
- OUT_OF_PROCESS_ERROR_PATH = Lux.root.join('tmp/request-error.txt')
20
-
21
19
  extend self
22
20
 
23
21
  def try(name)
24
22
  begin
25
23
  yield
26
24
  rescue
25
+ Lux.page.status 500
27
26
  log($!)
28
27
  inline('%s (%s)' % [name, $!.class])
29
28
  end
@@ -41,9 +40,20 @@ module Lux::Error
41
40
 
42
41
  def inline(name=nil, o=nil)
43
42
  o ||= $!
44
- trace = o.backtrace.select{ |el| el.index(Lux.root.to_s) }.map{ |el| el.split(Lux.root.to_s, 2)[1] }.map{ |el| "- #{el}" }.join("\n")
45
- msg = $!.to_s.gsub('","',%[",\n "]).gsub('<','&lt;')
46
- %[<pre style="color:red; background:#eee; padding:10px; font-family:'Lucida Console'; line-height:14pt; font-size:10pt;"><b style="font-size:110%;">#{name || 'Undefined name'}</b>\n\n<b>#{msg}</b>\n\n#{trace}</pre>]
43
+
44
+ dmp = [[], []]
45
+
46
+ o.backtrace.each do |line|
47
+ line = line.sub(Lux.root.to_s, '.')
48
+ dmp[line.include?('/app/') ? 0 : 1].push line
49
+ end
50
+
51
+ dmp[0] = dmp[0].map { |_| _ = _.split(':', 3); '<b>%s</b> - %s - %s' % _ }
52
+
53
+ name ||= 'Undefined name'
54
+ msg = $!.to_s.gsub('","',%[",\n "]).gsub('<','&lt;')
55
+
56
+ %[<pre style="color:red; background:#eee; padding:10px; font-family:'Lucida Console'; line-height:15pt; font-size:11pt;"><b style="font-size:110%;">#{name}</b>\n\n<b>#{msg}</b>\n\n#{dmp[0].join("\n")}\n\n#{dmp[1].join("\n")}</pre>]
47
57
  end
48
58
 
49
59
  def log(exception)
@@ -63,13 +73,4 @@ module Lux::Error
63
73
 
64
74
  File.write("#{folder}/#{key}.txt", data)
65
75
  end
66
-
67
-
68
- # if some other process as asset complation raises error
69
- # use this method to show errors
70
- def log_out_of_process_error(data)
71
- return unless Lux.config(:show_server_errors)
72
- File.write OUT_OF_PROCESS_ERROR_PATH, data
73
- end
74
-
75
76
  end
@@ -99,11 +99,11 @@ class Lux::Helper
99
99
  end
100
100
 
101
101
  # tag :div, { 'class'=>'iform' } do
102
- def tag name, opts={}, data=nil
103
- data = block_given? ? yield(opts) : data
104
- data = data.join('') if data.is_a?(Array)
105
- opts.tag name, data
102
+ def tag name=nil, opts={}, data=nil
103
+ return Lux::Helper::HtmlTag unless name
104
+
105
+ data = yield(opts) if block_given?
106
+ Lux::Helper::HtmlTag.tag name, opts, data
106
107
  end
107
108
 
108
109
  end
109
-
@@ -0,0 +1,67 @@
1
+ # tag.ul do |n|
2
+ # 1.upto(3) do |num|
3
+ # n.li do |n|
4
+ # n.i '.arrow'
5
+ # n.span num
6
+ # end
7
+ # end
8
+ # end
9
+
10
+ class Lux::Helper::HtmlTag
11
+ class << self
12
+ # tag.div -> tag.tag :div
13
+ def method_missing tag_name, *args, &block
14
+ tag tag_name, args[0], args[1], &block
15
+ end
16
+
17
+ # tag :div, { 'class'=>'iform' } do
18
+ def tag name=nil, opts={}, data=nil
19
+ # covert tag.a '.foo.bar' to class names
20
+ if opts.class == String && opts[0,1] == '.'
21
+ opts = { class: opts.sub('.', '').gsub('.', ' ') }
22
+ end
23
+
24
+ # fix data and opts unless opts is Hash
25
+ data, opts = opts, {} unless opts.class == Hash
26
+
27
+ if block_given?
28
+ inline = new
29
+ data = yield(inline, opts)
30
+
31
+ # if data is pushed to passed node, use that data
32
+ data = inline.data if inline.data.first
33
+ end
34
+
35
+ data = data.join('') if data.is_a?(Array)
36
+
37
+ build opts, name, data
38
+ end
39
+
40
+ # build html node
41
+ def build attrs, node=nil, text=nil
42
+ opts = ''
43
+ attrs.each do |k,v|
44
+ opts += ' '+k.to_s.gsub(/_/,'-')+'="'+v.to_s.gsub(/"/,'&quot;')+'"' if v.present?
45
+ end
46
+
47
+ return opts unless node
48
+
49
+ text = yield opts if block_given?
50
+ text ||= '' unless ['input', 'img', 'meta', 'link', 'hr', 'br'].include?(node.to_s)
51
+ text ? %{<#{node}#{opts}>#{text}</#{node}>} : %{<#{node}#{opts} />}
52
+ end
53
+ end
54
+
55
+ ###
56
+
57
+ attr_reader :data
58
+
59
+ def initialize
60
+ @data = []
61
+ end
62
+
63
+ # forward to class
64
+ def method_missing tag_name, *args, &block
65
+ @data.push self.class.tag(tag_name, args[0], args[1], &block)
66
+ end
67
+ end
@@ -62,12 +62,15 @@ class Lux::Html::Input
62
62
  def as_select
63
63
  body = []
64
64
  collection = @opts.delete(:collection)
65
+
65
66
  if nullval = @opts.delete(:null)
66
67
  body.push %[<option value="">#{nullval}</option>] if nullval
67
68
  end
69
+
68
70
  for el in prepare_collection(collection)
69
71
  body.push(%[<option value="#{el[0]}"#{@opts[:value].to_s == el[0].to_s ? ' selected=""' : nil}>#{el[1]}</option>])
70
72
  end
73
+
71
74
  body = body.join("\n")
72
75
  @opts.tag(:select, body)
73
76
  end
@@ -94,25 +97,32 @@ class Lux::Html::Input
94
97
  end
95
98
 
96
99
  def as_date
97
- @opts[:type] = 'text'
98
- @opts[:style] = 'width: 120px; display: inline;'
99
- @opts[:value] = @opts[:value].to_s.split(' +').first.to_s.sub(/:\d{2}$/,'')
100
- @opts[:hint] ||= 'YEAR - MONTH - DAY'
101
- ret = @opts.tag(:input)
102
- ret += ' &bull; %s' % Time.ago(Time.parse(@opts[:value])) if @opts[:value].present?
103
- ret += ' &bull; <small>%s</small>' % @opts[:hint]
104
- ret + %[<script>new Pikaday({ field: document.getElementById('#{@opts [:id]}'), format: "YYYY-MM-DD" }); </script>]
100
+ @opts[:type] = :date
101
+ @opts[:style] = 'width: 160px; display: inline;'
102
+
103
+ value = @opts[:value] || nil
104
+ desc = value ? '&mdash;' + Time.ago(value) : ''
105
+
106
+ [@opts.tag(:input), desc].join(' ')
105
107
  end
106
108
 
107
109
  def as_datetime
108
- @opts[:type] = 'text'
109
- @opts[:style] ||= 'width: 170px;'
110
- @opts[:value] = @opts[:value].to_s.split(' +').first.sub(/:\d{2}$/,'') if @opts[:value].present?
111
- ret = @opts.tag(:input)
112
- hint = []
113
- hint.push Time.ago(Time.parse(@opts[:value]))+'. ' if @opts[:value].present?
114
- hint.push %[<span class="btn btn-xs" onclick="$('##{@opts[:id]}').val('#{(Time.now-1.minute).to_s.split(' +').first.sub(/:\d{2}$/,'')}')">Now!</span>]
115
- ret + {class:'hint'}.tag(:p, hint.join(''))
110
+ value = @opts[:value]
111
+ id = @opts[:id]
112
+
113
+ value_day = value ? value.strftime('%Y-%m-%d') : ''
114
+ value_time = value ? value.strftime('%H:%M') : ''
115
+ value_all = value ? value.strftime('%H:%M') : ''
116
+
117
+ base = { class: 'form-control', onchange: "datetime_set('#{id}');", style: 'width: 160px; display: inline;' }
118
+
119
+ input_day = base.merge({ type: :date, id: '%s_day' % id, value: value_day }).tag :input
120
+ input_time = base.merge({ style: 'width: 110px; display: inline;', type: :time, id: '%s_time' % id, value: value_time }).tag :input
121
+ input_all = base.merge({ style: 'width: 150px; display: inline;', type: :text, id: id, name: @opts[:name], onfocus: 'blur();' }).tag :input
122
+ script = %[<script>window.datetime_set = function(id) { $('#'+id).val($('#'+id+'_day').val()+' '+$('#'+id+'_time').val()); }; datetime_set('#{id}');</script>]
123
+ desc = value ? '&mdash;' + Time.ago(value) : ''
124
+
125
+ [input_day, input_time, input_all, script, desc].join(' ')
116
126
  end
117
127
 
118
128
  def as_datebuttons
@@ -0,0 +1,51 @@
1
+ # handy :)
2
+ # renders full pages and exposes page object (req, res) in yiled
3
+ # for easy and powerful testing
4
+ # Hash :qs, Hash :post, String :method, Hash :cookies, Hash :session
5
+ # https://github.com/rack/rack/blob/master/test/spec_request.rb
6
+ def Lux(path, in_opts={}, &block)
7
+ allowed_opts = [:qs, :post, :method, :session, :cookies]
8
+ in_opts.keys.each { |k| die "#{k} is not allowed as opts param. allowed are #{allowed_opts}" unless allowed_opts.index(k) }
9
+
10
+ opts = {}
11
+
12
+ if in_opts[:post]
13
+ opts[:query_string] = in_opts[:post]
14
+ opts[:request_method] = :post
15
+ else
16
+ opts[:query_string] = in_opts[:qs] || {}
17
+ opts[:request_method] ||= in_opts[:method] || :get
18
+ end
19
+ opts[:request_method] = opts[:request_method].to_s.upcase
20
+ opts[:query_string] = opts[:query_string].to_query if opts[:query_string].class.to_s == 'Hash'
21
+
22
+ if path[0,4] == 'http'
23
+ parsed = URI.parse(path)
24
+ opts[:server_name] = parsed.host
25
+ opts[:server_port] = parsed.port
26
+ path = '/'+path.split('/', 4).last
27
+ end
28
+
29
+ env = Rack::MockRequest.env_for(path)
30
+ env[:input] = opts[:post] if opts[:post]
31
+ for k,v in opts
32
+ env[k.to_s.upcase] = v
33
+ end
34
+
35
+ page = nil
36
+ time_ms = Lux.speed { page = Lux::Page.prepare(env) }
37
+ page.session = in_opts[:session] if in_opts[:session]
38
+
39
+ return page.instance_exec &block if block_given?
40
+
41
+ response = page.render
42
+ body = response[2].join('')
43
+ body = JSON.parse body if response[1]['content-type'].index('/json')
44
+
45
+ {
46
+ time: time_ms,
47
+ status: response[0],
48
+ headers: response[1],
49
+ body: body
50
+ }.h
51
+ end