lux-fw 0.1.17

Sign up to get free protection for your applications and to get access to all the features.
Files changed (109) hide show
  1. checksums.yaml +7 -0
  2. data/.version +1 -0
  3. data/bin/cli/am +250 -0
  4. data/bin/cli/assets +37 -0
  5. data/bin/cli/console +50 -0
  6. data/bin/cli/dev +1 -0
  7. data/bin/cli/eval +15 -0
  8. data/bin/cli/exceptions +62 -0
  9. data/bin/cli/generate +82 -0
  10. data/bin/cli/get +5 -0
  11. data/bin/cli/nginx +28 -0
  12. data/bin/cli/production +1 -0
  13. data/bin/cli/routes +12 -0
  14. data/bin/cli/server +1 -0
  15. data/bin/cli/stat +1 -0
  16. data/bin/forever +65 -0
  17. data/bin/job_que +39 -0
  18. data/bin/lux +87 -0
  19. data/bin/txt/nginx.conf +29 -0
  20. data/bin/txt/siege-and-puma.txt +3 -0
  21. data/lib/common/base32.rb +47 -0
  22. data/lib/common/before_and_after.rb +71 -0
  23. data/lib/common/class_attributes.rb +66 -0
  24. data/lib/common/class_method_params.rb +94 -0
  25. data/lib/common/crypt.rb +66 -0
  26. data/lib/common/folder_model.rb +50 -0
  27. data/lib/common/generic_model.rb +62 -0
  28. data/lib/common/policy.rb +54 -0
  29. data/lib/common/string_base.rb +49 -0
  30. data/lib/common/url.rb +171 -0
  31. data/lib/lux/api/api.rb +150 -0
  32. data/lib/lux/api/lib/application_api.rb +19 -0
  33. data/lib/lux/api/lib/doc_builder.rb +18 -0
  34. data/lib/lux/api/lib/dsl.rb +73 -0
  35. data/lib/lux/api/lib/model_api.rb +145 -0
  36. data/lib/lux/api/lib/rescue.rb +18 -0
  37. data/lib/lux/cache/cache.rb +71 -0
  38. data/lib/lux/cache/lib/memcached.rb +3 -0
  39. data/lib/lux/cache/lib/null.rb +23 -0
  40. data/lib/lux/cache/lib/ram.rb +38 -0
  41. data/lib/lux/cell/cell.rb +260 -0
  42. data/lib/lux/config/config.rb +88 -0
  43. data/lib/lux/controller/controller.rb +185 -0
  44. data/lib/lux/controller/lib/nav.rb +77 -0
  45. data/lib/lux/controller/lib/plugs.rb +10 -0
  46. data/lib/lux/delayed_job/delayed_job.rb +44 -0
  47. data/lib/lux/delayed_job/lib/memory.rb +14 -0
  48. data/lib/lux/delayed_job/lib/nsq.rb +3 -0
  49. data/lib/lux/delayed_job/lib/postgre.rb +6 -0
  50. data/lib/lux/delayed_job/lib/redis.rb +19 -0
  51. data/lib/lux/error/error.rb +75 -0
  52. data/lib/lux/helper/helper.rb +109 -0
  53. data/lib/lux/html/html.rb +3 -0
  54. data/lib/lux/html/lib/form.rb +81 -0
  55. data/lib/lux/html/lib/input.rb +71 -0
  56. data/lib/lux/html/lib/input_types.rb +277 -0
  57. data/lib/lux/lux.rb +164 -0
  58. data/lib/lux/mailer/mailer.rb +73 -0
  59. data/lib/lux/page/lib/encrypt_params.rb +44 -0
  60. data/lib/lux/page/lib/flash.rb +49 -0
  61. data/lib/lux/page/lib/static_file.rb +97 -0
  62. data/lib/lux/page/page.rb +271 -0
  63. data/lib/lux/rescue_from/rescue_from.rb +61 -0
  64. data/lib/lux/template/template.rb +95 -0
  65. data/lib/lux-fw.rb +48 -0
  66. data/lib/overload/array.rb +52 -0
  67. data/lib/overload/blank.rb +62 -0
  68. data/lib/overload/date.rb +58 -0
  69. data/lib/overload/file.rb +14 -0
  70. data/lib/overload/hash.rb +86 -0
  71. data/lib/overload/hash_wia.rb +282 -0
  72. data/lib/overload/inflections.rb +199 -0
  73. data/lib/overload/integer.rb +19 -0
  74. data/lib/overload/module.rb +10 -0
  75. data/lib/overload/nil.rb +8 -0
  76. data/lib/overload/object.rb +77 -0
  77. data/lib/overload/string.rb +89 -0
  78. data/lib/overload/string_inflections.rb +7 -0
  79. data/lib/overload/struct.rb +5 -0
  80. data/lib/plugins/assets/assets_plug.rb +26 -0
  81. data/lib/plugins/assets/helper_module_adapter.rb +49 -0
  82. data/lib/plugins/assets/init.rb +4 -0
  83. data/lib/plugins/db_helpers/array_and_hstore.rb +64 -0
  84. data/lib/plugins/db_helpers/arrays_and_tags.rb +23 -0
  85. data/lib/plugins/db_helpers/before_save.rb +44 -0
  86. data/lib/plugins/db_helpers/cached_find_by.rb +45 -0
  87. data/lib/plugins/db_helpers/class_and_instance.rb +120 -0
  88. data/lib/plugins/db_helpers/dataset_plugin.rb +101 -0
  89. data/lib/plugins/db_helpers/filter_wrappers.rb +21 -0
  90. data/lib/plugins/db_helpers/link_plugin.rb +95 -0
  91. data/lib/plugins/db_helpers/localize_plugin.rb +57 -0
  92. data/lib/plugins/db_helpers/primary_keys.rb +36 -0
  93. data/lib/plugins/db_helpers/typero_attributes.rb +69 -0
  94. data/lib/plugins/db_logger/init.rb +18 -0
  95. data/lib/plugins/db_logger/lux_response_adapter.rb +9 -0
  96. data/lib/plugins/paginate/helper.rb +32 -0
  97. data/lib/plugins/paginate/sequel_adapter.rb +18 -0
  98. data/lib/vendor/mini_assets/mini_asset/base.rb +167 -0
  99. data/lib/vendor/mini_assets/mini_asset/css.rb +38 -0
  100. data/lib/vendor/mini_assets/mini_asset/js.rb +38 -0
  101. data/lib/vendor/mini_assets/mini_asset.rb +31 -0
  102. data/lib/vendor/oauth/lib/facebook.rb +35 -0
  103. data/lib/vendor/oauth/lib/github.rb +37 -0
  104. data/lib/vendor/oauth/lib/google.rb +41 -0
  105. data/lib/vendor/oauth/lib/linkedin.rb +41 -0
  106. data/lib/vendor/oauth/lib/stackexchange.rb +37 -0
  107. data/lib/vendor/oauth/lib/twitter.rb +41 -0
  108. data/lib/vendor/oauth/oauth.rb +46 -0
  109. metadata +334 -0
data/lib/lux/lux.rb ADDED
@@ -0,0 +1,164 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lux
4
+ extend self
5
+
6
+ ENV_PROD = ENV['RACK_ENV'] == 'production'
7
+ ENV_DEV = ENV['RACK_ENV'] == 'development'
8
+ ENV_TEST = ENV['RACK_ENV'] == 'test'
9
+ LUX_LIVE = ENV['LUX_LIVE'] == 'true'
10
+ LUX_VERBOSE = ENV['LUX_VERBOSE'] == 'true'
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 ||= {}
16
+
17
+ 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(:live?) { LUX_LIVE } # is on a live web server
24
+ define_method(:verbose?) { LUX_VERBOSE } # show verbose output
25
+ define_method(:cli?) { $0 == 'pry' || $0.index('/run.rb') || ENV['RACK_ENV'] == 'test' }
26
+
27
+ def env(key=nil)
28
+ return ENV['RACK_ENV'] unless key
29
+ die "ENV['#{key}'] not found" if ENV[key].nil?
30
+ ENV[key]
31
+ end
32
+
33
+ def config(key=nil)
34
+ return CONFIG unless key
35
+ die 'Lux.config.%s not found' % key if CONFIG[key].nil?
36
+ CONFIG[key].kind_of?(Proc) ? CONFIG[key].call() : CONFIG[key]
37
+ end
38
+
39
+ def thread
40
+ Thread.current[:lux]
41
+ end
42
+
43
+ def page
44
+ Thread.current[:lux][:page]
45
+ end
46
+
47
+ def page=(what)
48
+ Thread.current[:lux][:page] = what
49
+ end
50
+
51
+ def cache
52
+ Lux::Cache
53
+ end
54
+
55
+ def root
56
+ @@lux_app_root ||= Pathname.new(Dir.pwd).freeze
57
+ end
58
+
59
+ def fw_root
60
+ @@lux_fw_root ||= Pathname.new(File.expand_path('../../', File.dirname(__FILE__))).freeze
61
+ end
62
+
63
+ def error data
64
+ Lux::Error.show(data)
65
+ end
66
+
67
+ def log what
68
+ return unless Lux.config(:log_to_stdout)
69
+ puts what
70
+ end
71
+
72
+ def uid
73
+ Thread.current[:uid_cnt] ||= 0
74
+ "uid-#{Thread.current[:uid_cnt]+=1}"
75
+ end
76
+
77
+ def on name, ref=nil, &proc
78
+ EVENTS[name] ||= []
79
+ if block_given?
80
+ puts "* event: #{name} defined".white
81
+ EVENTS[name].push(proc)
82
+ else
83
+ for func in EVENTS[name]
84
+ ref.instance_eval(&func)
85
+ end
86
+ end
87
+ end
88
+
89
+ # if block given, simple new thread bg job
90
+ # if string given, eval it in bg
91
+ # if object given, instance it and run it
92
+ def delay *args
93
+ if block_given?
94
+ BACKGROUND_THREADS.push Thread.new { yield }
95
+ elsif args[0]
96
+ # Lux.delay(mail_object, :deliver)
97
+ Lux::DelayedJob.push(*args)
98
+ else
99
+ Lux::DelayedJob
100
+ end
101
+ end
102
+
103
+ def speed loops=1
104
+ render_start = Time.now
105
+ loops.times { yield }
106
+ num = ((Time.now-render_start)*1000).to_i.to_s
107
+ num = num.sub(/(\d)(\d{3})$/,'\1s \2')
108
+ num = "#{num}ms"
109
+ loops == 1 ? num : "Done #{loops.to_s.sub(/(\d)(\d{3})$/,'\1s \2')} loops in #{num}"
110
+ end
111
+
112
+ def consumed_memory
113
+ `ps -o rss -p #{$$}`.chomp.split("\n").last.to_i / 1000
114
+ end
115
+
116
+ end
117
+
118
+ # handy :)
119
+ # renders full pages and exposes page object (req, res) in yiled
120
+ # for easy and powerful testing
121
+ # Hash :qs, Hash :post, String :method, Hash :cookies, Hash :session
122
+ # https://github.com/rack/rack/blob/master/test/spec_request.rb
123
+ def Lux(path, in_opts={}, &block)
124
+ allowed_opts = [:qs, :post, :method, :session, :cookies]
125
+ in_opts.keys.each { |k| die "#{k} is not allowed as opts param. allowed are #{allowed_opts}" unless allowed_opts.index(k) }
126
+
127
+ opts = {}
128
+
129
+ if in_opts[:post]
130
+ opts[:query_string] = in_opts[:post]
131
+ opts[:request_method] = :post
132
+ else
133
+ opts[:query_string] = in_opts[:qs] || {}
134
+ opts[:request_method] ||= in_opts[:method] || :get
135
+ end
136
+ opts[:request_method] = opts[:request_method].to_s.upcase
137
+ opts[:query_string] = opts[:query_string].to_query if opts[:query_string].class.to_s == 'Hash'
138
+
139
+ if path[0,4] == 'http'
140
+ parsed = URI.parse(path)
141
+ opts[:server_name] = parsed.host
142
+ opts[:server_port] = parsed.port
143
+ path = '/'+path.split('/', 4).last
144
+ end
145
+
146
+ env = Rack::MockRequest.env_for(path)
147
+ env[:input] = opts[:post] if opts[:post]
148
+ for k,v in opts
149
+ env[k.to_s.upcase] = v
150
+ end
151
+
152
+ page = Lux::Page.prepare(env)
153
+ page.session = in_opts[:session] if in_opts[:session]
154
+
155
+ return page.instance_exec &block if block_given?
156
+
157
+ response = page.render
158
+ body = response[2].join('')
159
+ body = JSON.parse body if response[1]['content-type'].index('/json')
160
+
161
+ { status: response[0], headers: response[1], body: body }.h
162
+ end
163
+
164
+ require_relative 'config/config'
@@ -0,0 +1,73 @@
1
+ # sugessted usage
2
+ # Mailer.deliver(:confirm_email, 'rejotl@gmailcom')
3
+ # Mailer.render(:confirm_email, 'rejotl@gmailcom')
4
+
5
+ # natively works like
6
+ # Mailer.prepare(:confirm_email, 'rejotl@gmailcom').deliver
7
+ # Mailer.prepare(:confirm_email, 'rejotl@gmailcom').body
8
+
9
+ # Rails mode via method missing is suported
10
+ # Mailer.confirm_email('rejotl@gmailcom').deliver
11
+ # Mailer.confirm_email('rejotl@gmailcom').body
12
+
13
+ class Lux::Mailer
14
+
15
+ BeforeAndAfter.define self, :before, :after
16
+
17
+ attr_reader :subject, :to, :from
18
+
19
+ # Mailer.prepare(:confirm_email, 'rejotl@gmailcom')
20
+ def self.prepare(template, *args)
21
+ obj = new
22
+ obj.instance_variable_set :@_template, template
23
+
24
+ BeforeAndAfter.execute(obj, :before)
25
+ obj.send(template, *args)
26
+ BeforeAndAfter.execute(obj, :after)
27
+
28
+ obj
29
+ end
30
+
31
+ def self.render(method_name, *args)
32
+ send(method_name, *args).body
33
+ end
34
+
35
+ def deliver
36
+ raise "From in mailer not defined" unless @from
37
+ raise "To in mailer not defined" unless @to
38
+ raise "Subject in mailer not defined" unless @subject
39
+
40
+ require 'mail'
41
+
42
+ Mail.defaults { delivery_method Lux.config(:mail).delivery, Lux.config(:mail).opts }
43
+
44
+ m = Mail.new
45
+ m[:from] = @from
46
+ m[:to] = @to
47
+ m[:subject] = @subject
48
+ m[:body] = body
49
+ m[:content_type] = 'text/html; charset=UTF-8'
50
+ m.deliver!
51
+ end
52
+
53
+ def deliver_later
54
+ Lux.delay(self, :deliver)
55
+ end
56
+
57
+ def self.deliver
58
+ send(method_name, *args).deliver
59
+ end
60
+
61
+ def body
62
+ Lux::Template.render_with_layout("mailer/#{@_template}", instance_variables_hash)
63
+ end
64
+
65
+ def subject
66
+ @subject
67
+ end
68
+
69
+ def self.method_missing(method_sym, *args)
70
+ prepare(method_sym, *args)
71
+ end
72
+
73
+ end
@@ -0,0 +1,44 @@
1
+ # used for encrypting and decrypting data in forms
2
+
3
+ module Lux::Page::EncryptParams
4
+ extend self
5
+
6
+ @cnt = 0
7
+
8
+ # encrypt_param('dux', 'foo')
9
+ # <OpenStruct name="_data_1", value="eyJ0eXAiOiJKV1QiLCJhbGciOi..."
10
+ def encrypt name, value
11
+ base = name.include?('[') ? name.split(/[\[\]]/).first(2).join('::') : name
12
+ base += '#%s' % value
13
+
14
+ OpenStruct.new(name: "_data_#{@cnt+=1}", value: Crypt.encrypt(base))
15
+ end
16
+
17
+ def hidden_input name, value
18
+ data = encrypt name, value
19
+
20
+ %[<input type="hidden" name="#{data.name}" value="#{data.value}" />]
21
+ end
22
+
23
+ # decrypts params starting with _data_
24
+ def decrypt hash
25
+ for key in hash.keys
26
+ next unless key.starts_with?('_data_')
27
+ data = Crypt.decrypt(hash.delete(key))
28
+ data, value = data.split('#', 2)
29
+ data = data.split('::')
30
+
31
+ if data[1]
32
+ hash[data[0]] ||= {}
33
+ hash[data[0]][data[1]] = value
34
+ else
35
+ hash[data[0]] = value
36
+ end
37
+ end
38
+
39
+ hash
40
+ rescue
41
+ Lux.log ' Lux::Page::EncryptParams decrypt error'.red
42
+ {}
43
+ end
44
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Lux::Page::Flash
4
+
5
+ # flash.info 'messsage ...'
6
+ # flash.info = 'messsage ...'
7
+ def self.add_type name
8
+ define_method(name) { |message| add name, message }
9
+ eval "alias #{name}= #{name}"
10
+ end
11
+
12
+ add_type :info
13
+ add_type :error
14
+ add_type :warning
15
+
16
+ ###
17
+
18
+ def initialize h=nil
19
+ @msg = h || {}
20
+ end
21
+
22
+ def clear
23
+ to_h.tap { @msg = {} }
24
+ end
25
+
26
+ def to_h
27
+ @msg
28
+ end
29
+
30
+ # clears white space, replaces quotes
31
+ def clear_for_js
32
+ {}.tap do |msg|
33
+ clear.each do |k, v|
34
+ msg[k] = v.join(', ').gsub(/\s+/, ' ')
35
+ end
36
+ end
37
+ end
38
+
39
+ private
40
+
41
+ def add name, message
42
+ @msg[name] ||= []
43
+
44
+ return if @msg[name].last == message
45
+ return if @msg[name].length > 4
46
+
47
+ @msg[name].push message.to_s
48
+ end
49
+ end
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Lux::Page::StaticFile
4
+ DIRS = Dir.entries('./public').select{|d| d[0,1]!='.' && File.directory?("./public/#{d}") } rescue []
5
+
6
+ MIMME_TYPES = {
7
+ txt: 'text/plain',
8
+ html: 'text/html',
9
+ gif: 'image/gif',
10
+ jpg: 'image/jpeg',
11
+ jpeg: 'image/jpeg',
12
+ png: 'image/png',
13
+ ico: 'image/png', # image/x-icon
14
+ css: 'text/css',
15
+ map: 'application/json',
16
+ js: 'application/javascript',
17
+ gz: 'application/x-gzip',
18
+ zip: 'application/x-gzip',
19
+ svg: 'image/svg+xml',
20
+ mp3: 'application/mp3',
21
+ woff: 'application/x-font-woff',
22
+ woff2: 'application/x-font-woff',
23
+ ttf: 'application/font-ttf',
24
+ eot: 'application/vnd.ms-fontobject',
25
+ otf: 'application/font-otf',
26
+ }
27
+
28
+ class << self
29
+ def deliver(file)
30
+ new(file).read
31
+ end
32
+ end
33
+
34
+ ###
35
+
36
+ [:body, :status].each { |f| eval "def #{f}(what=nil); Lux.page.#{f}(what); end" }
37
+ [:headers].each { |f| eval "def #{f}; Lux.page.#{f}; end" }
38
+
39
+ def initialize(file)
40
+ @file = file
41
+ end
42
+
43
+ def is_static_file?
44
+ return false unless @file.index('.')
45
+
46
+ path = @file.split('/')
47
+ path.pop
48
+
49
+ file = path.shift
50
+ ext = @file.split('.').last
51
+
52
+ return false if ext.to_s.length == 0
53
+ return false unless MIMME_TYPES[ext.to_sym]
54
+ return true if path.first.blank? # if /favico.ico is not present return true
55
+ return DIRS.index(path.first) ? true : false
56
+ true
57
+ end
58
+
59
+ def read(data=nil)
60
+ file = File.exist?(@file) ? @file : Lux.root.join("public#{@file}")
61
+
62
+ unless File.exists?(file)
63
+ if @file == '/favicon.ico'
64
+ file = Lux.fw_root.join('public/lux.png')
65
+ else
66
+ raise NotFoundError, %[Static file "#{@file.split(Lux.root.to_s+'/public')[1]}" not found]
67
+ end
68
+ end
69
+
70
+ ext = file.to_s.split('.').last
71
+ mimme = MIMME_TYPES[ext.to_sym]
72
+ unless mimme
73
+ body('Mimme type not supported')
74
+ status(404)
75
+ return
76
+ end
77
+
78
+ Lux.page.content_type = mimme
79
+
80
+ file_mtime = File.mtime(file).utc.to_s
81
+ key = Crypt.md5(file+file_mtime.to_s)
82
+ headers['cache-control'] = 'max-age=31536000, no-transform, public'
83
+ headers['etag'] = key
84
+ headers['last-modified'] = file_mtime
85
+
86
+ # IF etags match, returnfrom cache
87
+ if Lux.page.request.env['HTTP_IF_NONE_MATCH'] == key
88
+ status(304)
89
+ body('not-modified')
90
+ return
91
+ end
92
+
93
+ data ||= File.read(file)
94
+ body(data)
95
+ true
96
+ end
97
+ end
@@ -0,0 +1,271 @@
1
+ # frozen_string_literal: true
2
+
3
+ # we need this for command line
4
+ Thread.current[:lux] ||= {}
5
+ Thread.current[:lux][:cache] ||= {}
6
+
7
+ class Lux::Page
8
+ attr_accessor :headers, :cookies, :session, :files_in_use
9
+ attr_reader :params, :request, :nav
10
+
11
+ BeforeAndAfter.define self, :before, :after
12
+
13
+ class << self
14
+ # expects Rack env object but can acceept url
15
+ # we need this for testing
16
+ def prepare env
17
+ env = Rack::MockRequest.env_for(env) if env.class == String
18
+ request = Rack::Request.new env
19
+ page = Lux::Page.new request
20
+
21
+ # reset page cache
22
+ Thread.current[:lux] = { cache:{}, page:page }
23
+
24
+ page
25
+ end
26
+
27
+ # default rack env call
28
+ def call env=nil
29
+ prepare(env).render
30
+ rescue
31
+ Lux::Error.log $!
32
+
33
+ raise $! if Lux.config(:show_server_errors)
34
+
35
+ [500, {}, ['Lux server error']]
36
+ end
37
+ end
38
+
39
+ ###
40
+
41
+ def initialize(request)
42
+ @render_start = Time.now
43
+ @files_in_use = []
44
+ @request = request
45
+ @headers = {}
46
+ @cookies = {}
47
+ @session = {}
48
+
49
+ for cookie in request.env['HTTP_COOKIE'].to_s.split(/;\s*/).map{ |el| el.split('=',2) }
50
+ @cookies[cookie[0]] = cookie[1]
51
+ end
52
+
53
+ @session = @cookies['__luxs'] ? (JSON.parse Crypt.decrypt @cookies['__luxs'] rescue {}) : {}
54
+
55
+ # check for session
56
+ if Lux.dev? && request.env['HTTP_REFERER'] && request.env['HTTP_REFERER'].index(request.host) && @session.keys.length == 0
57
+ puts "ERROR: There is no session set!".red
58
+ end
59
+
60
+ @session = HashWithIndifferentAccess.new(@session)
61
+ @session['_junk'] = Crypt.uid
62
+
63
+ @params = request.params.h_wia
64
+ Lux::Page::EncryptParams.decrypt @params
65
+
66
+ @nav = Lux::Controller::Nav.new request
67
+ end
68
+
69
+ def status num=nil
70
+ Lux.log caller if num == 500
71
+ return @status if @status
72
+ if num
73
+ if num.is_numeric?
74
+ @status ||= num.to_i
75
+ else
76
+ @status = case num.to_s
77
+ when 'StandardError'; 400
78
+ when 'BadRequestError'; 400
79
+ when 'UnauthorizedError'; 401
80
+ when 'ForbidenError'; 403
81
+ when 'NotFoundError'; 404
82
+ when 'RateLimitError'; 503
83
+ else; 500
84
+ end
85
+ end
86
+ end
87
+
88
+ @status
89
+ end
90
+
91
+ def body what=nil
92
+ @body ||= what
93
+
94
+ if @body && block_given?
95
+ @body = yield @body
96
+ Lux.error 'Lux.page.body is not a string (bad page.body filter)' unless @body.is_a?(String)
97
+ end
98
+
99
+ @body
100
+ end
101
+
102
+ def body= what
103
+ body(what)
104
+ end
105
+
106
+ def body! what
107
+ @body = what
108
+ end
109
+
110
+ def flash message=nil
111
+ @flash ||= Flash.new(@session[:lux_flash])
112
+
113
+ message ? @flash.error(message) : @flash
114
+ end
115
+
116
+ def base_domain
117
+ host = Lux.page.request.host.split('.')
118
+ host_country = host.pop
119
+ host_name = host.pop
120
+ host_name ? "#{host_name}.#{host_country}" : host_country
121
+ end
122
+
123
+ def var
124
+ Thread.current[:lux][:var] ||= Hashie::Mash.new
125
+ end
126
+
127
+ def host
128
+ "#{request.env['rack.url_scheme']}://#{request.host}:#{request.port}".sub(':80','') rescue 'http://locahost:3000'
129
+ end
130
+
131
+ def no_cache? force=nil
132
+ return false if !force && Lux.prod?
133
+ Lux.page.request.env['HTTP_CACHE_CONTROL'] == 'no-cache' ? true : false
134
+ rescue
135
+ false
136
+ end
137
+
138
+ def redirect(where=nil, opts={})
139
+ return @headers['location'] unless where
140
+
141
+ @status = opts.delete(:status) || 302
142
+ opts.map { |k,v| flash.send(k, v) }
143
+
144
+ @headers['location'] = where.index('//') ? where : "#{Lux.page.host}#{where}"
145
+ @body = %[redirecting to #{@headers['location']}\n\n#{opts.values.join("\n")}]
146
+ end
147
+
148
+ def permanent_redirect(where)
149
+ redirect(where, status:301)
150
+ end
151
+
152
+ def content_type(type=nil)
153
+ return @content_type unless type
154
+
155
+ # can be set only once
156
+ return @content_type if @content_type
157
+
158
+ type = 'application/json' if type == :json
159
+ type = 'text/plain' if type == :text
160
+ type = 'text/html' if type == :html
161
+
162
+ @content_type = type
163
+ end
164
+
165
+ def content_type=(type)
166
+ content_type(type)
167
+ end
168
+
169
+ def etag(*args)
170
+ @headers['etag'] ||= %[W/"#{Lux.cache.generate_key(args)}"]
171
+
172
+ if Lux.page.request.env['HTTP_IF_NONE_MATCH'] == @headers['etag']
173
+ if Lux.config(:perform_caching)
174
+ Lux.page.status(304)
175
+ Lux.page.body 'not-modified'
176
+ return true
177
+ else
178
+ print ' 304>' if Lux.page.status != 304 && Lux.verbose?
179
+ end
180
+ end
181
+ false
182
+ end
183
+
184
+ def once(id, data=nil)
185
+ @once_hash ||= {}
186
+ return if @once_hash[id]
187
+ @once_hash[id] = true
188
+ data || yield
189
+ end
190
+
191
+ def process_body
192
+ # if @body is not set, this is error now
193
+ unless @body
194
+ @status = 500
195
+ @body = 'Page BODY not defined. Maybe you did not call cell action but method instad.'
196
+ end
197
+
198
+ # respond as JSON if we recive hash
199
+ if @body.kind_of?(Hash)
200
+ @body = Lux.dev? ? JSON.pretty_generate(@body) : JSON.generate(@body)
201
+ if Lux.page.request.params[:callback]
202
+ @body = "#{@request.params[:callback]}(#{ret})"
203
+ @content_type ||= 'text/javascript'
204
+ else
205
+ @content_type ||= 'application/json'
206
+ end
207
+ @body += "\n"
208
+ else
209
+ # if somebody sets @content_type, respect that
210
+ @body = @body.to_s unless @body.kind_of?(String)
211
+ @content_type ||= 'text/plain' if @body[0,1] != '<'
212
+ @content_type ||= 'text/html'
213
+ end
214
+
215
+ # show out of process errors if enables and if is html page
216
+ # becasue for eaxmple we dont want to show error page instead of image
217
+ # we want user to see it
218
+ if Lux.config(:show_server_errors) && @content_type == 'text/html' && File.exist?(Lux::Error::OUT_OF_PROCESS_ERROR_PATH)
219
+ data = File.read(Lux::Error::OUT_OF_PROCESS_ERROR_PATH)
220
+ File.unlink(Lux::Error::OUT_OF_PROCESS_ERROR_PATH)
221
+ @body = Lux::Error::render data
222
+ end
223
+
224
+ if request.host =~ %r{^[\d\.]+$} # if request is IP
225
+ domain = request.host
226
+ else
227
+ domain = base_domain
228
+ domain = domain.index('.') ? ".#{domain}" : domain
229
+ end
230
+
231
+ @session[:lux_flash] = flash.to_h
232
+
233
+ # skip adding of cookies and time to strong etag parameters
234
+ if !@headers['etag'] || @headers['etag'].index('/')
235
+ @headers['set-cookie'] = "__luxs=#{Crypt.encrypt(@session.to_json)}; Expires=#{(Time.now+1.month).utc}; Path=/; Domain=#{domain};"
236
+ @headers['x-lux-speed'] = "#{((Time.now-@render_start)*1000).round(1)}ms"
237
+ end
238
+
239
+ @headers['content-type'] ||= "#{@content_type}; charset=utf-8"
240
+
241
+ # if "no-store" is present then HTTP_IF_NONE_MATCH is not sent from browser
242
+ @headers['cache-control'] ||= 'must-revalidate, %s, max-age=0' % Lux.page.var.user ? :private : :public
243
+
244
+ Lux.page.etag(@body) if Lux.page.request.request_method == 'GET'
245
+ @status ||= 200
246
+ Lux.log " #{@status}, #{@headers['x-lux-speed']}"
247
+
248
+ if ENV['LUX_PRINT_ROUTES']
249
+ print '* Finished route print '
250
+ puts @status == 404 ? 'without a match'.red : 'with a match'.green
251
+ exit
252
+ end
253
+
254
+ [@status, @headers, [@body]]
255
+ end
256
+
257
+ def render
258
+ Lux.log "\n#{Lux.page.request.request_method.white} #{Lux.page.request.path.white}"
259
+
260
+ Lux::Config.live_require_check! if Lux.config(:auto_code_reload)
261
+
262
+ BeforeAndAfter.execute(self, :before)
263
+ Lux::Controller.new.rescued_main
264
+ BeforeAndAfter.execute(self, :after)
265
+
266
+ process_body
267
+ end
268
+
269
+ end
270
+
271
+