lux-fw 0.1.17 → 0.1.35

Sign up to get free protection for your applications and to get access to all the features.
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