kiss 1.1 → 1.7
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.
- data/LICENSE +1 -1
- data/Rakefile +2 -1
- data/VERSION +1 -1
- data/bin/kiss +151 -34
- data/data/scaffold.tgz +0 -0
- data/lib/kiss.rb +389 -742
- data/lib/kiss/accessors/controller.rb +47 -0
- data/lib/kiss/accessors/request.rb +106 -0
- data/lib/kiss/accessors/template.rb +23 -0
- data/lib/kiss/action.rb +502 -132
- data/lib/kiss/bench.rb +14 -5
- data/lib/kiss/debug.rb +14 -6
- data/lib/kiss/exception_report.rb +22 -299
- data/lib/kiss/ext/core.rb +700 -0
- data/lib/kiss/ext/rack.rb +33 -0
- data/lib/kiss/ext/sequel_database.rb +47 -0
- data/lib/kiss/ext/sequel_mysql_dataset.rb +23 -0
- data/lib/kiss/form.rb +404 -179
- data/lib/kiss/form/field.rb +183 -307
- data/lib/kiss/form/field_types.rb +239 -0
- data/lib/kiss/format.rb +88 -70
- data/lib/kiss/html/exception_report.css +222 -0
- data/lib/kiss/html/exception_report.html +210 -0
- data/lib/kiss/iterator.rb +14 -12
- data/lib/kiss/login.rb +8 -8
- data/lib/kiss/mailer.rb +68 -66
- data/lib/kiss/model.rb +323 -36
- data/lib/kiss/rack/bench.rb +16 -8
- data/lib/kiss/rack/email_errors.rb +25 -15
- data/lib/kiss/rack/errors_ok.rb +2 -2
- data/lib/kiss/rack/facebook.rb +6 -6
- data/lib/kiss/rack/file_not_found.rb +10 -8
- data/lib/kiss/rack/log_exceptions.rb +3 -3
- data/lib/kiss/rack/recorder.rb +2 -2
- data/lib/kiss/rack/show_debug.rb +2 -2
- data/lib/kiss/rack/show_exceptions.rb +2 -2
- data/lib/kiss/request.rb +435 -0
- data/lib/kiss/sequel_session.rb +15 -14
- data/lib/kiss/static_file.rb +20 -13
- data/lib/kiss/template.rb +327 -0
- metadata +60 -25
- data/lib/kiss/controller_accessors.rb +0 -81
- data/lib/kiss/hacks.rb +0 -188
- data/lib/kiss/sequel_mysql.rb +0 -25
- data/lib/kiss/template_methods.rb +0 -167
data/lib/kiss/rack/bench.rb
CHANGED
@@ -7,15 +7,15 @@ module Rack
|
|
7
7
|
# Rack::Bench shows total request duration for any request.
|
8
8
|
class Bench
|
9
9
|
def initialize(app)
|
10
|
-
@
|
10
|
+
@_app = app
|
11
11
|
end
|
12
12
|
|
13
13
|
def call(env)
|
14
14
|
start_time = Time.now
|
15
|
-
code, headers, body = @
|
15
|
+
code, headers, body = @_app.call(env)
|
16
16
|
end_time = Time.now
|
17
17
|
|
18
|
-
|
18
|
+
html = <<-EOT
|
19
19
|
<style>
|
20
20
|
.kiss_bench {
|
21
21
|
text-align: left;
|
@@ -35,17 +35,25 @@ module Rack
|
|
35
35
|
color: #930;
|
36
36
|
text-decoration: underline;
|
37
37
|
}
|
38
|
+
.kiss_bench small {
|
39
|
+
font-family: arial, sans-serif;
|
40
|
+
float: right;
|
41
|
+
margin-left: 8px;
|
42
|
+
color: #a60;
|
43
|
+
text-align: right;
|
44
|
+
white-space: nowrap;
|
45
|
+
}
|
38
46
|
</style>
|
39
47
|
<div class="kiss_bench">
|
40
|
-
<
|
41
|
-
<
|
48
|
+
<small>kiss bench</small>
|
49
|
+
<tt><b>TOTAL request duration: #{sprintf("%0.3f", end_time.to_f - start_time.to_f)} s</b></tt>
|
42
50
|
</div>
|
43
51
|
EOT
|
44
52
|
|
45
|
-
body
|
46
|
-
headers['Content-Length'] =
|
53
|
+
body = body.prepend_html(html, 'body')
|
54
|
+
headers['Content-Length'] = body.content_length.to_s
|
47
55
|
|
48
|
-
[ code, headers,
|
56
|
+
[ code, headers, body ]
|
49
57
|
end
|
50
58
|
end
|
51
59
|
end
|
@@ -1,17 +1,27 @@
|
|
1
|
+
# DEPRECATED
|
2
|
+
# This module is now deprecated as slated to be removed in Kiss 1.6.
|
3
|
+
|
1
4
|
module Rack
|
2
5
|
# Rack::EmailErrors sends error responses (code 5xx) to email addresses as
|
3
6
|
# specified in the Rack::Builder config.
|
4
7
|
class EmailErrors
|
5
|
-
def initialize(app,
|
6
|
-
@
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
8
|
+
def initialize(app, *args)
|
9
|
+
@_app = app
|
10
|
+
|
11
|
+
if (options = args.first).is_a?(Hash)
|
12
|
+
@_agent = options[:agent]
|
13
|
+
@_subject = options[:subject]
|
14
|
+
@_from = options[:from]
|
15
|
+
@_to = options[:to]
|
16
|
+
else
|
17
|
+
@_agent, @_subject, @_from, *@_to = *args
|
18
|
+
end
|
19
|
+
|
20
|
+
@_agent = '/usr/sbin/sendmail -t' if @_agent == :sendmail
|
11
21
|
end
|
12
22
|
|
13
23
|
def call(env)
|
14
|
-
code, headers, body = @
|
24
|
+
code, headers, body = @_app.call(env)
|
15
25
|
|
16
26
|
if code >= 500 && code < 600
|
17
27
|
begin # rescue any errors in message composition and sending
|
@@ -20,9 +30,9 @@ module Rack
|
|
20
30
|
|
21
31
|
message = <<-EOT
|
22
32
|
Content-type: text/html
|
23
|
-
From: #{@
|
24
|
-
To: #{@
|
25
|
-
Subject: #{@
|
33
|
+
From: #{@_from}
|
34
|
+
To: #{@_to.join(', ')}
|
35
|
+
Subject: #{@_subject} - #{error_type}#{ error_message ? ": #{error_message}" : ''}
|
26
36
|
|
27
37
|
EOT
|
28
38
|
|
@@ -30,15 +40,15 @@ EOT
|
|
30
40
|
message += part
|
31
41
|
end
|
32
42
|
|
33
|
-
if @
|
34
|
-
IO.popen(@
|
43
|
+
if @_agent.is_a?(String)
|
44
|
+
IO.popen(@_agent, "w") do |pipe|
|
35
45
|
pipe.puts(message)
|
36
46
|
end
|
37
47
|
else
|
38
48
|
require 'net/smtp' unless defined?(Net::SMTP)
|
39
|
-
smtp = @
|
49
|
+
smtp = @_agent.is_a?(Net::SMTP) ? @_agent : Net::SMTP.new('localhost')
|
40
50
|
smtp.start do |smtp|
|
41
|
-
smtp.send_message(message, @
|
51
|
+
smtp.send_message(message, @_from, *@_to)
|
42
52
|
end
|
43
53
|
end
|
44
54
|
rescue
|
@@ -55,7 +65,7 @@ EOT
|
|
55
65
|
</body>
|
56
66
|
</html>
|
57
67
|
EOT
|
58
|
-
headers['Content-Length'] = body.
|
68
|
+
headers['Content-Length'] = body.content_length.to_s
|
59
69
|
end
|
60
70
|
|
61
71
|
[ code, headers, body ]
|
data/lib/kiss/rack/errors_ok.rb
CHANGED
@@ -3,11 +3,11 @@ module Rack
|
|
3
3
|
# and HTML entities that are invalid as FBML responses.
|
4
4
|
class ErrorsOK
|
5
5
|
def initialize(app)
|
6
|
-
@
|
6
|
+
@_app = app
|
7
7
|
end
|
8
8
|
|
9
9
|
def call(env)
|
10
|
-
code, headers, body = @
|
10
|
+
code, headers, body = @_app.call(env)
|
11
11
|
|
12
12
|
if code >= 500 && code < 600
|
13
13
|
code = 200
|
data/lib/kiss/rack/facebook.rb
CHANGED
@@ -3,11 +3,11 @@ module Rack
|
|
3
3
|
# and HTML entities that are invalid as FBML responses.
|
4
4
|
class Facebook
|
5
5
|
def initialize(app)
|
6
|
-
@
|
6
|
+
@_app = app
|
7
7
|
end
|
8
8
|
|
9
9
|
def call(env)
|
10
|
-
code, headers, body = @
|
10
|
+
code, headers, body = @_app.call(env)
|
11
11
|
|
12
12
|
if code >= 500 && code < 600
|
13
13
|
code = 200
|
@@ -17,11 +17,11 @@ module Rack
|
|
17
17
|
body.each {|p| contents += p }
|
18
18
|
|
19
19
|
contents.gsub!(/txmt:\/\//, 'http://textmate.local/')
|
20
|
-
contents.gsub!('<body>','<div class="body">')
|
21
|
-
contents.gsub!('</body>','</div>')
|
22
|
-
contents.gsub!('<wbr/>','')
|
20
|
+
contents.gsub!('<body>', '<div class="body">')
|
21
|
+
contents.gsub!('</body>', '</div>')
|
22
|
+
contents.gsub!('<wbr/>', '')
|
23
23
|
|
24
|
-
headers['Content-Length'] = contents.
|
24
|
+
headers['Content-Length'] = contents.content_length.to_s
|
25
25
|
|
26
26
|
[ code, headers, contents ]
|
27
27
|
end
|
@@ -1,11 +1,13 @@
|
|
1
1
|
module Rack
|
2
|
-
# Rack::FileNotFound rescues
|
2
|
+
# Rack::FileNotFound rescues file-not-found exceptions
|
3
3
|
# (raised when action template files are not found) and returns an
|
4
4
|
# HTTP 404 error response.
|
5
5
|
class FileNotFound
|
6
|
-
def initialize(app,
|
7
|
-
@
|
8
|
-
|
6
|
+
def initialize(app, options = {})
|
7
|
+
@_app = app
|
8
|
+
|
9
|
+
path = options[:path]
|
10
|
+
@_body = path ? (
|
9
11
|
::File.file?(path) ?
|
10
12
|
::File.read(path) :
|
11
13
|
template('could not find specified FileNotFound error document')
|
@@ -13,12 +15,12 @@ module Rack
|
|
13
15
|
end
|
14
16
|
|
15
17
|
def call(env)
|
16
|
-
code, headers, body = @
|
17
|
-
rescue Kiss::
|
18
|
+
code, headers, body = @_app.call(env)
|
19
|
+
rescue Kiss::FileNotFoundError => e
|
18
20
|
[ 404, {
|
19
21
|
"Content-Type" => "text/html",
|
20
|
-
"Content-Length" => @
|
21
|
-
}, @
|
22
|
+
"Content-Length" => @_body.content_length.to_s
|
23
|
+
}, @_body ]
|
22
24
|
end
|
23
25
|
|
24
26
|
def template(error = nil)
|
@@ -3,12 +3,12 @@ module Rack
|
|
3
3
|
# Functionality moved to Kiss#initialize (lib/kiss.rb).
|
4
4
|
|
5
5
|
class LogExceptions
|
6
|
-
def initialize(app,path)
|
7
|
-
@
|
6
|
+
def initialize(app, path)
|
7
|
+
@_app = app
|
8
8
|
end
|
9
9
|
|
10
10
|
def call(env)
|
11
|
-
@
|
11
|
+
@_app.call(env)
|
12
12
|
end
|
13
13
|
end
|
14
14
|
end
|
data/lib/kiss/rack/recorder.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module Rack
|
2
2
|
class Recorder
|
3
3
|
def initialize(app)
|
4
|
-
@
|
4
|
+
@_app = app
|
5
5
|
|
6
6
|
@@filepath ||= begin
|
7
7
|
puts "Yo. Starting app..."
|
@@ -11,7 +11,7 @@ module Rack
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def call(env)
|
14
|
-
code, headers, body = @
|
14
|
+
code, headers, body = @_app.call(env)
|
15
15
|
|
16
16
|
puts "OK, that was fun."
|
17
17
|
puts "Would save some request and response data here."
|
data/lib/kiss/rack/show_debug.rb
CHANGED
data/lib/kiss/request.rb
ADDED
@@ -0,0 +1,435 @@
|
|
1
|
+
class Kiss
|
2
|
+
class Request
|
3
|
+
|
4
|
+
_attr_accessor :controller
|
5
|
+
|
6
|
+
# Data pertaining to the current request.
|
7
|
+
_attr_reader :env, :protocol, :host, :request, :response,
|
8
|
+
:exception_cache, :exception_email_sent, :path
|
9
|
+
|
10
|
+
# Processes and responds to a request.
|
11
|
+
# Returns array of response code, headers, and body.
|
12
|
+
def initialize(env, controller, passed_config = {})
|
13
|
+
@_controller = controller
|
14
|
+
@_config = passed_config
|
15
|
+
|
16
|
+
@_files_cached_this_request = {}
|
17
|
+
end
|
18
|
+
|
19
|
+
def call(env)
|
20
|
+
@_env = env
|
21
|
+
|
22
|
+
if @_controller.rack_file && (
|
23
|
+
(env["PATH_INFO"] == '/favicon.ico') ||
|
24
|
+
(env["PATH_INFO"].sub!(/\A#{@_controller.asset_uri}/, ''))
|
25
|
+
)
|
26
|
+
return @_controller.rack_file.call(env)
|
27
|
+
elsif env["PATH_INFO"] =~ /favicon\.\w{3}\Z/
|
28
|
+
return [404, {'Content-type' => 'text/html'}, 'File not found']
|
29
|
+
end
|
30
|
+
|
31
|
+
@_request = Rack::Request.new(env)
|
32
|
+
|
33
|
+
@_protocol, @_app_host = (@_request.server.split(/\:\/\//, 2) rescue ['', ''])
|
34
|
+
@_app_host = @_config[:app_host] if @_config[:app_host]
|
35
|
+
@_app_uri = @_config[:app_uri] || @_request.script_name || ''
|
36
|
+
|
37
|
+
@_host ||= @_request.host rescue ''
|
38
|
+
|
39
|
+
# unfreeze path
|
40
|
+
@_path = "#{@_request.path_info}" || '/'
|
41
|
+
|
42
|
+
# remove extra path noise +[..][R]+GET...
|
43
|
+
@_path.sub!(/\++(\[.*?\]\+*)*\[R\].*/, '')
|
44
|
+
|
45
|
+
@_params = @_request.params
|
46
|
+
@_query_string = @_request.query_string
|
47
|
+
|
48
|
+
# catch and report exceptions in this block
|
49
|
+
status_code, headers, body = handle_request(@_path, @_params)
|
50
|
+
|
51
|
+
if body.respond_to?(:prepend_html)
|
52
|
+
unless @_debug_messages.empty?
|
53
|
+
extend Kiss::Debug
|
54
|
+
body = prepend_debug(body)
|
55
|
+
|
56
|
+
headers['Content-Type'] = 'text/html'
|
57
|
+
headers['Content-Length'] = body.content_length.to_s
|
58
|
+
end
|
59
|
+
|
60
|
+
unless @_benchmarks.empty?
|
61
|
+
stop_benchmark
|
62
|
+
extend Kiss::Bench
|
63
|
+
body = prepend_benchmarks(body)
|
64
|
+
headers['Content-Type'] = 'text/html'
|
65
|
+
headers['Content-Length'] = body.content_length.to_s
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
return_database if @_db
|
70
|
+
|
71
|
+
[status_code, headers, body]
|
72
|
+
end
|
73
|
+
|
74
|
+
def path_with_query_string
|
75
|
+
@_path + (@_query_string.empty? ? '' : '?' + @_query_string)
|
76
|
+
end
|
77
|
+
|
78
|
+
def handle_request(path, params = {})
|
79
|
+
begin
|
80
|
+
@_exception_cache = {}
|
81
|
+
@_debug_messages = []
|
82
|
+
@_benchmarks = []
|
83
|
+
@_response = Rack::Response.new
|
84
|
+
|
85
|
+
catch :kiss_request_done do
|
86
|
+
action = invoke_action(path, params)
|
87
|
+
extension = action.extension
|
88
|
+
options = action.output_options
|
89
|
+
|
90
|
+
if content_type = options[:content_type] || (extension ? Kiss.mime_type(extension) : nil)
|
91
|
+
@_response['Content-Type'] = "#{content_type}; #{options[:document_encoding] || 'utf-8'}"
|
92
|
+
end
|
93
|
+
|
94
|
+
send_response(action.output, options)
|
95
|
+
end
|
96
|
+
|
97
|
+
finalize_session if @_session
|
98
|
+
@_response.finish
|
99
|
+
rescue StandardError, LoadError, SyntaxError => e
|
100
|
+
handle_exception(e)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def lookup_exception_handler(e)
|
105
|
+
klass = e.class
|
106
|
+
while klass
|
107
|
+
break if controller.exception_handlers[klass]
|
108
|
+
klass = klass.superclass
|
109
|
+
end
|
110
|
+
|
111
|
+
klass && controller.exception_handlers[klass]
|
112
|
+
end
|
113
|
+
|
114
|
+
def load_exception_handler(exception_handler); end
|
115
|
+
|
116
|
+
def handle_exception(e)
|
117
|
+
@_exception_messages = []
|
118
|
+
|
119
|
+
report = Kiss::ExceptionReport.generate(e, @_env, @_exception_cache, @_db ? @_db.last_query : nil)
|
120
|
+
exception_message = e.message.sub(/\n.*/m, '')
|
121
|
+
|
122
|
+
if @_controller.exception_log_file
|
123
|
+
@_controller.exception_log_file.print(report + "\n--- End of exception report --- \n\n")
|
124
|
+
end
|
125
|
+
|
126
|
+
status_code = 500
|
127
|
+
body = report
|
128
|
+
|
129
|
+
result = [status_code, {
|
130
|
+
"Content-Type" => "text/html",
|
131
|
+
"Content-Length" => body.content_length.to_s,
|
132
|
+
"X-Kiss-Error-Type" => e.class.name,
|
133
|
+
"X-Kiss-Error-Message" => exception_message
|
134
|
+
}, body]
|
135
|
+
|
136
|
+
send_email = true
|
137
|
+
|
138
|
+
unless @_loading_exception_handler
|
139
|
+
@_loading_exception_handler = true
|
140
|
+
begin
|
141
|
+
exception_handler = lookup_exception_handler(e)
|
142
|
+
if exception_handler
|
143
|
+
send_email = exception_handler[:send_email]
|
144
|
+
new_result = load_exception_handler(exception_handler)
|
145
|
+
if exception_handler[:action]
|
146
|
+
result = handle_request(exception_handler[:action])
|
147
|
+
result[0] = exception_handler[:status_code] || 500
|
148
|
+
end
|
149
|
+
end
|
150
|
+
rescue StandardError, LoadError, SyntaxError => e
|
151
|
+
result = handle_exception(e)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
if send_email && @_config.email_errors
|
156
|
+
email_message = <<-EOT
|
157
|
+
Content-type: text/html
|
158
|
+
From: #{@_config.email_errors.from}
|
159
|
+
To: #{@_config.email_errors.to.is_a?(String) ? @_config.email_errors.to : @_config.email_errors.to.join(', ')}
|
160
|
+
Subject: #{@_config.email_errors.app} - #{e.class.name}#{ exception_message ? ": #{exception_message}" : ''}
|
161
|
+
|
162
|
+
#{report}
|
163
|
+
EOT
|
164
|
+
Kiss::Mailer.send( @_config.email_errors.merge(:message => email_message) )
|
165
|
+
@_exception_email_sent = true
|
166
|
+
end
|
167
|
+
|
168
|
+
result
|
169
|
+
end
|
170
|
+
|
171
|
+
|
172
|
+
##### ACTION METHODS #####
|
173
|
+
|
174
|
+
def invoke_action(path, params = {}, render_options = {})
|
175
|
+
action = get_action(path, params)
|
176
|
+
|
177
|
+
catch :kiss_action_done do
|
178
|
+
action.authenticate if action.class.authentication_required
|
179
|
+
|
180
|
+
action.before_call
|
181
|
+
action.call
|
182
|
+
action.render(render_options)
|
183
|
+
end
|
184
|
+
action.after_call
|
185
|
+
action
|
186
|
+
end
|
187
|
+
|
188
|
+
# Parses request URI to determine action path and arguments, then
|
189
|
+
# instantiates action class to create action handler.
|
190
|
+
def get_action(path, params)
|
191
|
+
@@action_class ||= Kiss::Action.get_root_class(@_controller, @_controller.action_dir)
|
192
|
+
|
193
|
+
# return action handler (instance of action class)
|
194
|
+
@@action_class.get_subclass_from_path(path, self, params)
|
195
|
+
end
|
196
|
+
|
197
|
+
|
198
|
+
##### FILE METHODS #####
|
199
|
+
|
200
|
+
# If file has already been cached in handling the current request, retrieve from cache
|
201
|
+
# and do not check filesystem for updates. Else cache file via controller's file_cache.
|
202
|
+
def file_cache(path, *args, &block)
|
203
|
+
return @_controller.file_cache[path] if @_files_cached_this_request[path]
|
204
|
+
@_files_cached_this_request[path] = true
|
205
|
+
|
206
|
+
@_controller.file_cache(path, *args, &block)
|
207
|
+
end
|
208
|
+
|
209
|
+
|
210
|
+
##### DATABASE METHODS #####
|
211
|
+
|
212
|
+
# Acquires and returns a database connection object from the connection pool.
|
213
|
+
#
|
214
|
+
# Tip: `db' is a shorthand alias for `database'.
|
215
|
+
def database
|
216
|
+
@_db ||= begin
|
217
|
+
db = @_controller.database
|
218
|
+
check_evolution_number(db)
|
219
|
+
db.kiss_request = self
|
220
|
+
db
|
221
|
+
end
|
222
|
+
end
|
223
|
+
alias_method :db, :database
|
224
|
+
|
225
|
+
def return_database
|
226
|
+
@_db.kiss_request = nil
|
227
|
+
@_controller.return_database(@_db)
|
228
|
+
end
|
229
|
+
|
230
|
+
# Kiss Model cache, used to invoke and store Kiss database models.
|
231
|
+
#
|
232
|
+
# Example:
|
233
|
+
# models[:users] == database model for `users' table
|
234
|
+
#
|
235
|
+
# Tip: `dbm' (stands for `database models') is a shorthand alias for `models'.
|
236
|
+
def models
|
237
|
+
# make sure we have a database connection
|
238
|
+
# create new model cache unless exists already
|
239
|
+
db.kiss_model_cache
|
240
|
+
end
|
241
|
+
alias_method :dbm, :models
|
242
|
+
|
243
|
+
# Check whether there exists a file in evolution_dir whose number is greater than app's
|
244
|
+
# current evolution number. If so, raise an error to indicate need to apply new evolutions.
|
245
|
+
def check_evolution_number(db)
|
246
|
+
db_version = db.evolution_number
|
247
|
+
if @_controller.directory_exists?(@_controller.evolution_dir) && @_controller.evolution_file(db_version+1)
|
248
|
+
raise <<-EOT
|
249
|
+
database evolution number #{db_version} < last evolution file number #{@_controller.last_evolution_file_number}
|
250
|
+
apply evolutions or set database evolution number
|
251
|
+
EOT
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
|
256
|
+
##### SESSION METHODS #####
|
257
|
+
|
258
|
+
# Retrieves or generates session data object, based on session ID from cookie value.
|
259
|
+
def session
|
260
|
+
@_session ||= begin
|
261
|
+
@_controller.session_class ? begin
|
262
|
+
@_controller.session_setup ||= begin
|
263
|
+
# setup session storage
|
264
|
+
@_controller.session_class.setup_storage(self)
|
265
|
+
true
|
266
|
+
end
|
267
|
+
|
268
|
+
session = @_controller.session_class.persist(self, @_request.cookies[@_controller.cookie_name])
|
269
|
+
@_session_fingerprint = Marshal.dump(session.data).hash
|
270
|
+
|
271
|
+
cookie_vars = {
|
272
|
+
:value => session.values[:session_id],
|
273
|
+
:path => @_config[:cookie_path] || @_app_uri,
|
274
|
+
:domain => @_config[:cookie_domain] || @_request.host
|
275
|
+
}
|
276
|
+
cookie_vars[:expires] = Time.now + @_config[:cookie_lifespan] if @_config[:cookie_lifespan]
|
277
|
+
|
278
|
+
# set_cookie here or at render time
|
279
|
+
@_response.set_cookie @_controller.cookie_name, cookie_vars
|
280
|
+
|
281
|
+
session
|
282
|
+
end : {}
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
# Saves session to session store, if session data has changed since load.
|
287
|
+
def finalize_session
|
288
|
+
@_session.save if @_session_fingerprint != Marshal.dump(@_session.data).hash
|
289
|
+
end
|
290
|
+
|
291
|
+
# Returns a Kiss::Login object containing data from session.login.
|
292
|
+
def login
|
293
|
+
@_login ||= Kiss::Login.new(session)
|
294
|
+
end
|
295
|
+
|
296
|
+
|
297
|
+
##### OUTPUT METHODS #####
|
298
|
+
|
299
|
+
# Outputs a Kiss::StaticFile object as response to Rack.
|
300
|
+
# Used to return static files efficiently.
|
301
|
+
def send_file(path, options = {})
|
302
|
+
@_response = Kiss::StaticFile.new(path, options)
|
303
|
+
|
304
|
+
throw :kiss_request_done
|
305
|
+
end
|
306
|
+
|
307
|
+
# Prepares Rack::Response object to return application response to Rack.
|
308
|
+
def send_response(output = '', options = {})
|
309
|
+
@_response['Content-Length'] = output.content_length.to_s
|
310
|
+
@_response['Content-Type'] = options[:content_type] if options[:content_type]
|
311
|
+
if options[:filename]
|
312
|
+
@_response['Content-Disposition'] = "#{options[:disposition] || 'inline'}; filename=#{options[:filename]}"
|
313
|
+
elsif options[:disposition]
|
314
|
+
@_response['Content-Disposition'] = options[:disposition]
|
315
|
+
end
|
316
|
+
@_response.body = output
|
317
|
+
|
318
|
+
throw :kiss_request_done
|
319
|
+
end
|
320
|
+
|
321
|
+
# Sends HTTP 302 response to redirect client browser agent to specified URL.
|
322
|
+
def redirect_url(url)
|
323
|
+
@_response.status = 302
|
324
|
+
@_response['Location'] = url
|
325
|
+
@_response.body = ''
|
326
|
+
|
327
|
+
throw :kiss_request_done
|
328
|
+
end
|
329
|
+
|
330
|
+
# Redirects to specified action path, which may also include arguments.
|
331
|
+
def redirect_action(action, options = {})
|
332
|
+
redirect_url( app_url(options) + action + (options[:params] ?
|
333
|
+
'?' + options[:params].keys.map do |k|
|
334
|
+
"#{k.to_s.url_escape}=#{options[:params][k].to_s.url_escape}"
|
335
|
+
end.join('&') : '')
|
336
|
+
)
|
337
|
+
end
|
338
|
+
|
339
|
+
|
340
|
+
##### DEBUG/BENCH OUTPUT #####
|
341
|
+
|
342
|
+
# Adds debug message to inspect object. Debug messages will be shown at top of
|
343
|
+
# application response body.
|
344
|
+
def debug(object, context = Kernel.caller[0])
|
345
|
+
@_debug_messages.push( [object.inspect.gsub(/ /, ' '), context] )
|
346
|
+
object
|
347
|
+
end
|
348
|
+
alias_method :trace, :debug
|
349
|
+
|
350
|
+
# Starts a new benchmark timer, with optional label. Benchmark results will be shown
|
351
|
+
# at top of application response body.
|
352
|
+
def start_benchmark(label = nil, context = Kernel.caller[0])
|
353
|
+
stop_benchmark(context)
|
354
|
+
@_benchmarks.push(
|
355
|
+
:label => label,
|
356
|
+
:start_time => Time.now,
|
357
|
+
:start_context => context
|
358
|
+
)
|
359
|
+
end
|
360
|
+
alias_method :bench, :start_benchmark
|
361
|
+
|
362
|
+
# Stops last benchmark timer, if still running.
|
363
|
+
def stop_benchmark(end_context = nil)
|
364
|
+
if @_benchmarks[-1] && !@_benchmarks[-1][:end_time]
|
365
|
+
@_benchmarks[-1][:end_time] = Time.now
|
366
|
+
@_benchmarks[-1][:end_context] = end_context
|
367
|
+
end
|
368
|
+
end
|
369
|
+
alias_method :bench_stop, :stop_benchmark
|
370
|
+
|
371
|
+
##### OTHER METHODS #####
|
372
|
+
|
373
|
+
# Returns URL/URI of app root (corresponding to top level of action_dir).
|
374
|
+
def app_url(options = {})
|
375
|
+
@_controller.app_url({
|
376
|
+
:protocol => @_protocol,
|
377
|
+
:host => @_app_host,
|
378
|
+
:uri => @_app_uri
|
379
|
+
}.merge(options))
|
380
|
+
end
|
381
|
+
|
382
|
+
# Returns URL/URI of app's static assets (asset_host or public_uri).
|
383
|
+
def pub_url(options = {})
|
384
|
+
if options.empty?
|
385
|
+
@_pub ||= (@_controller.asset_host ? @_protocol + '://' + @_controller.asset_host : '') +
|
386
|
+
(options[:uri] || @_controller.asset_uri || '')
|
387
|
+
else
|
388
|
+
(options[:protocol] || @_protocol) + '://' + (options[:host] || @_controller.asset_host) +
|
389
|
+
(options[:uri] || @_controller.asset_uri || '')
|
390
|
+
end
|
391
|
+
end
|
392
|
+
alias_method :asset_url, :pub_url
|
393
|
+
|
394
|
+
def query_string_with_params(params = {})
|
395
|
+
params = @_request.GET.merge(params)
|
396
|
+
params.keys.map do |key|
|
397
|
+
"#{key.to_s.url_escape}=#{params[key].to_s.url_escape}"
|
398
|
+
end.join('&')
|
399
|
+
end
|
400
|
+
|
401
|
+
def url_with_params(params = {})
|
402
|
+
"#{app_url}#{@_path}?#{query_string_with_params(params)}"
|
403
|
+
end
|
404
|
+
|
405
|
+
# Adds data to be displayed in "Cache" section of Kiss exception reports.
|
406
|
+
def exception_cache(data = nil)
|
407
|
+
@_exception_cache.merge!(data) if data
|
408
|
+
@_exception_cache
|
409
|
+
end
|
410
|
+
alias_method :set_exception_cache, :exception_cache
|
411
|
+
|
412
|
+
# Returns new Kiss::Mailer object using specified options.
|
413
|
+
def new_email(options = {})
|
414
|
+
controller.new_email({
|
415
|
+
:request => self
|
416
|
+
}.merge(options))
|
417
|
+
end
|
418
|
+
|
419
|
+
def send_email(options = {})
|
420
|
+
new_email(options).send
|
421
|
+
end
|
422
|
+
|
423
|
+
def cookies
|
424
|
+
request.cookies
|
425
|
+
end
|
426
|
+
|
427
|
+
def set_cookie(*args)
|
428
|
+
response.set_cookie(*args)
|
429
|
+
end
|
430
|
+
|
431
|
+
def path_info
|
432
|
+
@_request.env["PATH_INFO"]
|
433
|
+
end
|
434
|
+
end
|
435
|
+
end
|