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
@@ -1,91 +1,193 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # 400: for bad parameter request or similar
4
- BadRequestError ||= Class.new(StandardError)
3
+ # https://en.wikipedia.org/wiki/List_of_HTTP_status_codes
4
+
5
+ # default error handler for lux
6
+ # e = Lux::Error.new 404
7
+ # e.code => 404
8
+ # e.message => 'Not Found'
9
+ #
10
+ # e = Lux::Error.not_found('foo')
11
+ # e.code => 404
12
+ # e.message => foo
13
+
14
+ class Lux::Error < StandardError
15
+ class AutoRaise < Lux::Error
16
+ end
5
17
 
6
- # 401: for unauthorized access
7
- UnauthorizedError ||= Class.new(StandardError)
18
+ # https://httpstatuses.com/
19
+ CODE_LIST ||= {
20
+ # 1×× Informational
21
+ 100 => { name: 'Continue' },
22
+ 101 => { name: 'Switching Protocols' },
23
+ 102 => { name: 'Processing' },
24
+
25
+ # 2×× Success
26
+ 200 => { name: 'OK' },
27
+ 201 => { name: 'Created' },
28
+ 202 => { name: 'Accepted' },
29
+ 203 => { name: 'Non-authoritative Information' },
30
+ 204 => { name: 'No Content' },
31
+ 205 => { name: 'Reset Content' },
32
+ 206 => { name: 'Partial Content' },
33
+ 207 => { name: 'Multi-Status' },
34
+ 208 => { name: 'Already Reported' },
35
+ 226 => { name: 'IM Used' },
36
+
37
+ # 3×× Redirection
38
+ 300 => { name: 'Multiple Choices' },
39
+ 301 => { name: 'Moved Permanently' },
40
+ 302 => { name: 'Found' },
41
+ 303 => { name: 'See Other' },
42
+ 304 => { name: 'Not Modified' },
43
+ 305 => { name: 'Use Proxy' },
44
+ 307 => { name: 'Temporary Redirect' },
45
+ 308 => { name: 'Permanent Redirect' },
46
+
47
+ # 4×× Client Error
48
+ 400 => { name: 'Bad Request', code: :bad_request },
49
+ 401 => { name: 'Unauthorized', code: :unauthorized },
50
+ 402 => { name: 'Payment Required', code: :payment_required },
51
+ 403 => { name: 'Forbidden', code: :forbidden },
52
+ 404 => { name: 'Document Not Found', code: :not_found },
53
+ 405 => { name: 'Method Not Allowed', code: :method_not_allowed },
54
+ 406 => { name: 'Not Acceptable', code: :not_acceptable },
55
+ 407 => { name: 'Proxy Authentication Required' },
56
+ 408 => { name: 'Request Timeout' },
57
+ 409 => { name: 'Conflict' },
58
+ 410 => { name: 'Gone' },
59
+ 411 => { name: 'Length Required' },
60
+ 412 => { name: 'Precondition Failed' },
61
+ 413 => { name: 'Payload Too Large' },
62
+ 414 => { name: 'Request-URI Too Long' },
63
+ 415 => { name: 'Unsupported Media Type' },
64
+ 416 => { name: 'Requested Range Not Satisfiable' },
65
+ 417 => { name: 'Expectation Failed' },
66
+ 418 => { name: 'I\'m a teapot' },
67
+ 421 => { name: 'Misdirected Request' },
68
+ 422 => { name: 'Unprocessable Entity' },
69
+ 423 => { name: 'Locked' },
70
+ 424 => { name: 'Failed Dependency' },
71
+ 426 => { name: 'Upgrade Required' },
72
+ 428 => { name: 'Precondition Required' },
73
+ 429 => { name: 'Too Many Requests' },
74
+ 431 => { name: 'Request Header Fields Too Large' },
75
+ 444 => { name: 'Connection Closed Without Response' },
76
+ 451 => { name: 'Unavailable For Legal Reasons' },
77
+ 499 => { name: 'Client Closed Request' },
78
+
79
+ # 5×× Server Error
80
+ 500 => { name: 'Internal Server Error', code: :internal_server_error },
81
+ 501 => { name: 'Not Implemented', code: :not_implemented },
82
+ 502 => { name: 'Bad Gateway' },
83
+ 503 => { name: 'Service Unavailable' },
84
+ 504 => { name: 'Gateway Timeout' },
85
+ 505 => { name: 'HTTP Version Not Supported' },
86
+ 506 => { name: 'Variant Also Negotiates' },
87
+ 507 => { name: 'Insufficient Storage' },
88
+ 508 => { name: 'Loop Detected' },
89
+ 510 => { name: 'Not Extended' },
90
+ 511 => { name: 'Network Authentication Required' },
91
+ 599 => { name: 'Network Connect Timeout Error' },
92
+ }
93
+
94
+ # e = Lux::Error.not_found('foo')
95
+ CODE_LIST.each do |status, data|
96
+ if data[:code]
97
+ define_singleton_method(data[:code]) do |message=nil|
98
+ error = new status, message
99
+ raise error if Lux::Error::AutoRaise === error
100
+ error
101
+ end
102
+ end
103
+ end
8
104
 
9
- # 403: for unalloed access
10
- ForbidenError ||= Class.new(StandardError)
105
+ class << self
106
+ # template to show full error page
107
+ def render text, status=500
108
+ Lux.current.response.status status
109
+ Lux.current.response.body Lux.config.server_error_template.call(text)
110
+ throw :done
111
+ end
11
112
 
12
- # 404: for not found pages
13
- NotFoundError ||= Class.new(StandardError)
113
+ # render error inline or break in production
114
+ def inline name, error=nil
115
+ error ||= $!
14
116
 
15
- # 503: for too many requests at the same time
16
- RateLimitError ||= Class.new(StandardError)
117
+ unless Lux.config(:dump_errors)
118
+ key = log error
119
+ render "Lux inline error: %s\n\nkey: %s" % [error.message, key]
120
+ end
17
121
 
18
- module Lux::Error
19
- extend self
122
+ name ||= 'Undefined name'
123
+ msg = error.message.to_s.gsub('","',%[",\n "]).gsub('<','&lt;')
20
124
 
21
- def try name=nil
22
- begin
23
- yield
24
- rescue Exception => e
25
- Lux.current.response.status 500
125
+ dmp = split_backtrace error
26
126
 
27
- key = log e
127
+ dmp[0] = dmp[0].map { |_| _ = _.split(':', 3); '<b>%s</b> - %s - %s' % _ }
28
128
 
29
- if Lux.config(:show_server_errors)
30
- inline name
31
- else
32
- name ||= 'Server error occured'
33
- name += "\n\nkey: %s" % key
129
+ log error
34
130
 
35
- Lux.error(name)
36
- end
37
- end
38
- end
131
+ <<~TEXT
132
+ <pre style="color:red; background:#eee; padding:10px; font-family:'Lucida Console'; line-height:15pt; font-size:11pt;">
133
+ <b style="font-size:110%;">#{name}</b>
39
134
 
40
- def render data
41
- %[<html><head><title>Server error (#{Lux.current.response.status})</title></head><body style="background:#fdd;"><pre style="color:red; padding:10px; font-size:14pt;">#{data.gsub('<','&lt;')}</pre></body></html>]
42
- end
135
+ <b>#{error}: #{msg}</b>
43
136
 
44
- def show desc=nil
45
- Lux.current.response.status 500
46
- data = "Lux #{Lux.current.response.status} error\n\n#{desc}"
47
- Lux.current.response.body! render(data)
48
- end
137
+ #{dmp[0].join("\n")}
49
138
 
50
- def inline name=nil, o=nil
51
- o ||= $!
139
+ #{dmp[1].join("\n")}
140
+ </pre>
141
+ TEXT
142
+ end
52
143
 
53
- dmp = [[], []]
144
+ def report code, msg=nil
145
+ e = Integer === code ? Lux::Error.new(code) : Lux::Error.send(code)
146
+ e.message = msg if msg
147
+ raise e
148
+ end
54
149
 
55
- o.backtrace.each do |line|
56
- line = line.sub(Lux.root.to_s, '.')
57
- dmp[line.include?('/app/') ? 0 : 1].push line
150
+ def log error
151
+ Lux.config.error_logger.call error
58
152
  end
59
153
 
60
- dmp[0] = dmp[0].map { |_| _ = _.split(':', 3); '<b>%s</b> - %s - %s' % _ }
154
+ def split_backtrace error
155
+ # split app log rest of the log
156
+ dmp = [[], []]
61
157
 
62
- name ||= 'Undefined name'
63
- msg = $!.to_s.gsub('","',%[",\n "]).gsub('<','&lt;')
158
+ root = Lux.root.to_s
64
159
 
65
- %[<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>]
160
+ error.backtrace.each do |line|
161
+ line = line.sub(root, '.')
162
+ dmp[line[0,1] == '.' ? 0 : 1].push line
163
+ end
164
+
165
+ dmp
166
+ end
66
167
  end
67
168
 
68
- def log exception
69
- return if Lux.env == 'test'
70
- return if exception.class == LocalRaiseError
71
- return unless Lux.current
169
+ ###
72
170
 
73
- # .backtrace.reject{ |el| el.index('/gems/') }
74
- history = exception.backtrace
75
- .map{ |el| el.sub(Lux.root.to_s, '') }
76
- .join("\n")
171
+ attr_accessor :message
77
172
 
78
- data = '%s in %s (user: %s)' % [exception.class, Lux.current.request.url, (Lux.current.var.user.email rescue 'guest')]
79
- data = [data, exception.message, history].join("\n\n")
80
- key = Digest::SHA1.hexdigest exception.backtrace.first.split(' ').first
173
+ def initialize code_num, message=nil
174
+ self.code = code_num
175
+ @message = message || CODE_LIST[code_num][:name]
176
+ end
81
177
 
82
- folder = Lux.root.join('log/exceptions').to_s
83
- Dir.mkdir(folder) unless Dir.exists?(folder)
84
- folder += "/#{exception.class.to_s.tableize.gsub('/','-')}"
85
- Dir.mkdir(folder) unless Dir.exists?(folder)
178
+ def code
179
+ # 400 is a default
180
+ @code || 400
181
+ end
86
182
 
87
- File.write("#{folder}/#{key}.txt", data)
183
+ def code= num
184
+ @code = num.to_i
88
185
 
89
- key
186
+ raise 'Status code %s not found' % @code unless CODE_LIST[@code]
187
+ end
188
+
189
+ def render
190
+ self.class.render message, code
90
191
  end
91
192
  end
193
+
@@ -0,0 +1,27 @@
1
+ # EventBus.on('test') { |arg| puts 'jedan: %s' % arg }
2
+ # EventBus.on('test') { |arg| puts 'dva: %s' % arg }
3
+ # EventBus.on('test') { |arg| raise 'abc' }
4
+ # EventBus.call 'test', 'xxx'
5
+
6
+ module Lux::EventBus
7
+ extend self
8
+
9
+ EVENTS = {}
10
+
11
+ def on name, key=nil, &proc
12
+ key ||= caller[0].split(':in ').first.gsub(/[^\w]/,'')
13
+
14
+ EVENTS[name] ||= {}
15
+ EVENTS[name][key] ||= proc
16
+ end
17
+
18
+ def call name, opts=nil
19
+ for func in EVENTS[name].values
20
+ begin
21
+ func.call opts
22
+ rescue => error
23
+ Lux.config.on_event_bus_error.call error, name
24
+ end
25
+ end
26
+ end
27
+ end
data/lib/lux/lux.rb CHANGED
@@ -1,50 +1,68 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../common/class_callbacks.rb'
3
+ require_relative '../common/class_callbacks'
4
+ require_relative 'cache/cache'
4
5
 
5
- module Lux
6
+ module ::Lux
6
7
  extend self
7
8
 
8
- ENV_PROD = ENV['RACK_ENV'] == 'production'
9
- ENV_DEV = ENV['RACK_ENV'] == 'development'
10
- ENV_TEST = ENV['RACK_ENV'] == 'test'
11
- LUX_CLI = $0 == 'pry' || $0.index('/run.rb') || ENV['RACK_ENV'] == 'test'
12
-
13
- VERSION = File.read File.expand_path('../../../.version', __FILE__).chomp
14
- CONFIG ||= Hashie::Mash.new
15
- EVENTS ||= {}
9
+ ENV_PROD = ENV['RACK_ENV'] == 'production' unless defined?(ENV_PROD)
10
+ CACHE_SERVER ||= Lux::Cache.new
11
+ VERSION ||= File.read File.expand_path('../../../.version', __FILE__).chomp
12
+ CONFIG ||= Hashie::Mash.new
13
+ APP_ROOT ||= Pathname.new(Dir.pwd).freeze
14
+ FW_ROOT ||= Pathname.new(File.expand_path('../../', File.dirname(__FILE__))).freeze
15
+ EVENTS ||= {}
16
+ MCACHE ||= {}
16
17
 
17
18
  BACKGROUND_THREADS ||= []
18
- Kernel.at_exit { BACKGROUND_THREADS.each { |t| t.join } }
19
-
20
- define_method(:prod?) { ENV_PROD }
21
- define_method(:dev?) { ENV_DEV }
22
- define_method(:test?) { ENV_TEST }
23
- define_method(:cli?) { !!($0 =~ %r[/bin/lux$]) }
24
- define_method(:thread) { Thread.current[:lux] }
25
- define_method(:cache) { Lux::Cache }
19
+ # Kernel.at_exit { BACKGROUND_THREADS.each { |t| t.join } }
20
+
21
+ define_method(:cli?) { !@rackup_start }
22
+ define_method(:test?) { ENV['RACK_ENV'] == 'test' }
23
+ define_method(:prod?) { ENV_PROD }
24
+ define_method(:production?) { ENV_PROD }
25
+ define_method(:dev?) { !ENV_PROD }
26
+ define_method(:development?) { !ENV_PROD }
27
+ define_method(:cache) { CACHE_SERVER }
28
+ define_method(:secrets) { @secrets ||= Lux::Config::Secrets.new.load }
29
+ define_method(:root) { APP_ROOT }
30
+ define_method(:fw_root) { FW_ROOT }
31
+ define_method(:event) { Lux::EventBus }
32
+ define_method(:require_all) { |folder| Lux::Config.require_all folder }
26
33
 
27
34
  # main rack response
28
35
  def call env=nil
29
36
  state = Lux::Current.new env
30
37
  app = Lux::Application.new state
31
38
  app.render
32
- rescue => e
33
- raise e if Lux.config(:show_server_errors)
34
-
35
- [500, {}, ['Lux server error']]
39
+ rescue => error
40
+ if Lux.config(:dump_errors)
41
+ raise error
42
+ else
43
+ log error.backtrace
44
+ [500, {}, ['Server error: %s' % error.message]]
45
+ end
36
46
  end
37
47
 
38
48
  def env key=nil
39
- return ENV['RACK_ENV'] unless key
40
- die "ENV['#{key}'] not found" if ENV[key].nil?
41
- ENV[key]
49
+ if key
50
+ value = ENV[key]
51
+ die "ENV['#{key}'] not found" if value.nil?
52
+ value
53
+ else
54
+ ENV['RACK_ENV']
55
+ end
42
56
  end
43
57
 
44
58
  def config key=nil
45
- return CONFIG unless key
46
- die 'Lux.config.%s not found' % key if CONFIG[key].nil?
47
- CONFIG[key].kind_of?(Proc) ? CONFIG[key].call() : CONFIG[key]
59
+ if key
60
+ value = CONFIG[key]
61
+ die 'Lux.config.%s not found' % key if value.nil?
62
+ value.kind_of?(Proc) ? value.call() : value
63
+ else
64
+ CONFIG
65
+ end
48
66
  end
49
67
 
50
68
  def current
@@ -59,43 +77,39 @@ module Lux
59
77
  block ? Lux::Application.class_eval(&block) : Lux::Application
60
78
  end
61
79
 
62
- def root
63
- @@lux_app_root ||= Pathname.new(Dir.pwd).freeze
64
- end
65
-
66
- def fw_root
67
- @@lux_fw_root ||= Pathname.new(File.expand_path('../../', File.dirname(__FILE__))).freeze
80
+ def error data=nil
81
+ data ? Lux::Error.render(data) : Lux::Error
68
82
  end
69
83
 
70
- def error data
71
- current.response.status 500
72
- Lux::Error.show(data)
73
- end
74
-
75
- def log what
84
+ # simple log to stdout
85
+ def log what=nil
76
86
  return unless Lux.config(:log_to_stdout)
77
- puts what
87
+ puts what || yield
78
88
  end
79
89
 
80
- def on name, ref=nil, &proc
81
- EVENTS[name] ||= []
82
-
83
- if block_given?
84
- puts "* event: #{name} defined".white
85
- EVENTS[name].push(proc)
86
- else
87
- for func in EVENTS[name]
88
- ref.instance_eval(&func)
89
- end
90
- end
90
+ # simple interface to plugins
91
+ # Lux.plugin :foo
92
+ # Lux.plugin
93
+ def plugin *args
94
+ args.first ? Lux::Config::Plugin.load(*args) : Lux::Config::Plugin
91
95
  end
92
96
 
93
97
  # if block given, simple new thread bg job
94
98
  # if string given, eval it in bg
95
99
  # if object given, instance it and run it
96
- def delay *args
100
+ def delay *args, &block
97
101
  if block_given?
98
- BACKGROUND_THREADS.push Thread.new { yield }
102
+ puts 'add'
103
+
104
+ t = Thread.new do
105
+ begin
106
+ block.call
107
+ rescue => e
108
+ Lux.logger(:delay_errors).error [e.message, e.backtrace]
109
+ end
110
+ end
111
+
112
+ BACKGROUND_THREADS.push t
99
113
  elsif args[0]
100
114
  # Lux.delay(mail_object, :deliver)
101
115
  Lux::DelayedJob.push(*args)
@@ -104,23 +118,46 @@ module Lux
104
118
  end
105
119
  end
106
120
 
107
- def speed loops=1
121
+ # load rake tasks + including ones in plugins
122
+ def load_tasks
123
+ require_relative '../../tasks/loader.rb'
124
+ end
125
+
126
+ # in memory cache, used on app init, no need for Mutex
127
+ def ram_cache key
128
+ MCACHE[key] = nil if Lux.config(:compile_assets)
129
+ MCACHE[key] ||= yield
130
+ end
131
+
132
+ # initialize the Lux application
133
+ def start
134
+ Lux.config.lux_config_loaded = true
135
+
136
+ Config.start!
137
+ end
138
+
139
+ # must be called when serving web pages from rackup
140
+ def serve rack_handler
141
+ @rackup_start = true
142
+ Object.class_callback :web_boot, Lux::Application, rack_handler
143
+ rack_handler.run self
144
+ end
145
+
146
+ # simple block to calc block execution speed
147
+ def speed
108
148
  render_start = Time.monotonic
109
- loops.times { yield }
149
+ yield
110
150
  num = (Time.monotonic - render_start) * 1000
111
- num = "#{num.round(1)} ms"
112
- loops == 1 ? num : "Done #{loops.to_s.sub(/(\d)(\d{3})$/,'\1s \2')} loops in #{num}"
151
+ '%s ms' % num.round(1)
113
152
  end
114
153
 
115
- # load specific plugin
116
- def plugin name
117
- dir = '%s/plugins/%s' % [Lux.fw_root, name]
118
- base = '%s/base.rb' % dir
154
+ # Lux.logger(:foo).warn 'bar'
155
+ def logger name=nil
156
+ name ||= ENV.fetch('RACK_ENV').downcase
119
157
 
120
- if File.exist?(base)
121
- load base
122
- else
123
- Lux::Config.require_all('%s/*' % dir)
158
+ MCACHE['lux-logger-%s' % name] ||=
159
+ Logger.new('./log/%s.log' % name).tap do |it|
160
+ it.formatter = Lux.config.logger_formater
124
161
  end
125
162
  end
126
163
  end
@@ -1,31 +1,32 @@
1
1
  # sugessted usage
2
- # Mailer.deliver(:confirm_email, 'rejotl@gmailcom')
3
- # Mailer.render(:confirm_email, 'rejotl@gmailcom')
2
+ # Mailer.deliver(:email_login, 'foo@bar.baz')
3
+ # Mailer.render(:email_login, 'foo@bar.baz')
4
4
 
5
5
  # natively works like
6
- # Mailer.prepare(:confirm_email, 'rejotl@gmailcom').deliver
7
- # Mailer.prepare(:confirm_email, 'rejotl@gmailcom').body
6
+ # Mailer.prepare(:email_login, 'foo@bar.baz').deliver
7
+ # Mailer.prepare(:email_login, 'foo@bar.baz').body
8
8
 
9
9
  # Rails mode via method missing is suported
10
- # Mailer.confirm_email('rejotl@gmailcom').deliver
11
- # Mailer.confirm_email('rejotl@gmailcom').body
10
+ # Mailer.email_login('foo@bar.baz').deliver
11
+ # Mailer.email_login('foo@bar.baz').body
12
12
 
13
13
  class Lux::Mailer
14
- ClassCallbacks.define self, :before, :after
14
+ class_callback :before
15
+ class_callback :after
16
+
15
17
  class_attribute :helper
18
+ class_attribute :layout, 'mailer'
16
19
 
17
20
  attr_reader :mail
18
21
 
19
22
  class << self
20
- # Mailer.prepare(:confirm_email, 'rejotl@gmailcom')
23
+ # Mailer.prepare(:email_login, 'foo@bar.baz')
21
24
  def prepare template, *args
22
25
  obj = new
23
26
  obj.instance_variable_set :@_template, template
24
-
25
- ClassCallbacks.execute obj, :before
27
+ Object.class_callback :before, obj
26
28
  obj.send template, *args
27
- ClassCallbacks.execute obj, :after
28
-
29
+ Object.class_callback :after, obj
29
30
  obj
30
31
  end
31
32
 
@@ -45,7 +46,7 @@ class Lux::Mailer
45
46
  ###
46
47
 
47
48
  def initialize
48
- @mail = DynamicClass.new subject: nil, body: nil, to: nil, cc: nil, from: nil
49
+ @mail = FreeStruct.new subject: nil, body: nil, to: nil, cc: nil, from: nil
49
50
  end
50
51
 
51
52
  def deliver
@@ -53,30 +54,27 @@ class Lux::Mailer
53
54
  raise "To in mailer not defined" unless @mail.to
54
55
  raise "Subject in mailer not defined" unless @mail.subject
55
56
 
56
- require 'mail'
57
-
58
- Mail.defaults { delivery_method Lux.config(:mail).delivery, Lux.config(:mail).opts }
59
-
60
57
  m = Mail.new
61
58
  m[:from] = @mail.from
62
59
  m[:to] = @mail.to
63
60
  m[:subject] = @mail.subject
64
- m[:body] = @mail.body || body
61
+ m[:body] = body
65
62
  m[:content_type] = 'text/html; charset=UTF-8'
66
63
 
67
- Thread.new { m.deliver! }
68
- end
64
+ Lux.delay { m.deliver! }
69
65
 
70
- def deliver_later
71
- Lux.delay self, :deliver
66
+ instance_exec m, &Lux.config.on_mail
72
67
  end
73
68
 
74
69
  def body
75
- return @mail.body if @mail.body
70
+ data = @mail.body
76
71
 
77
- helper = Lux::Helper.new self, self.class.helper
72
+ unless data
73
+ helper = Lux::View::Helper.new self, self.class.helper
74
+ data = Lux::View.render_with_layout "layouts/#{self.class.layout}", "mailer/#{@_template}", helper
75
+ end
78
76
 
79
- Lux::Template.render_with_layout "mailer/#{@_template}", helper
77
+ data.gsub(%r{\shref=(['"])/}) { %[ href=#{$1}#{Lux.config.host}/] }
80
78
  end
81
79
 
82
80
  def subject
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Lux::Response::File
4
+ MIMME_TYPES = {
5
+ txt: 'text/plain',
6
+ html: 'text/html',
7
+ gif: 'image/gif',
8
+ jpg: 'image/jpeg',
9
+ jpeg: 'image/jpeg',
10
+ png: 'image/png',
11
+ ico: 'image/png', # image/x-icon
12
+ css: 'text/css',
13
+ map: 'application/json',
14
+ js: 'application/javascript',
15
+ gz: 'application/x-gzip',
16
+ zip: 'application/x-gzip',
17
+ svg: 'image/svg+xml',
18
+ mp3: 'application/mp3',
19
+ woff: 'application/x-font-woff',
20
+ woff2: 'application/x-font-woff',
21
+ ttf: 'application/font-ttf',
22
+ eot: 'application/vnd.ms-fontobject',
23
+ otf: 'application/font-otf',
24
+ doc: 'application/msword'
25
+ }
26
+
27
+ ###
28
+ # all parametars are optional
29
+ # :name - file name
30
+ # :cache - client cache in seconds
31
+ # :content_type - string type
32
+ # :inline - sets disposition to inline if true
33
+ # :disposition - inline or attachment
34
+ # :content - raw file data
35
+ def initialize file, in_opts={}
36
+ opts = in_opts.to_opts :name, :cache, :content_type, :inline, :disposition, :content
37
+ opts.disposition ||= opts.inline.class == TrueClass ? 'inline' : 'attachment'
38
+ opts.cache = true if opts.cache.nil?
39
+
40
+ file = 'public/%s' % file unless file[0, 1] == '/'
41
+
42
+ @ext = file.include?('.') ? file.split('.').last.to_sym : nil
43
+ @file = file
44
+ @opts = opts
45
+ end
46
+
47
+ define_method(:request) { Lux.current.request }
48
+ define_method(:response) { Lux.current.response }
49
+
50
+ def is_static_file?
51
+ return false unless @ext
52
+ File.exist?(@file)
53
+ end
54
+
55
+ def send
56
+ file = File.exist?(@file) ? @file : Lux.root.join("public#{@file}").to_s
57
+
58
+ raise Lux::Error.not_found('Static file not found') unless File.exists?(file)
59
+
60
+ response.content_type(@opts.content_type || MIMME_TYPES[@ext || '_'] || 'application/octet-stream')
61
+
62
+ file_mtime = File.mtime(file).utc.to_s
63
+ key = Crypt.sha1(file + (@opts.content || file_mtime.to_s))
64
+
65
+ if @opts.disposition == 'attachment'
66
+ @opts.name ||= @file.split('/').last
67
+ response.headers['content-disposition'] = 'attachment; filename=%s' % @opts.name
68
+ end
69
+
70
+ response.headers['cache-control'] = 'max-age=%d, public' % (@opts.cache ? 31536000 : 0)
71
+ response.headers['etag'] = '"%s"' % key
72
+ response.headers['last-modified'] = file_mtime
73
+
74
+ # IF etags match, returnfrom cache
75
+ if request.env['HTTP_IF_NONE_MATCH'] == key
76
+ response.body('not-modified', 304)
77
+ else
78
+ response.body @opts.content || File.read(file)
79
+ end
80
+ end
81
+ end