merb 0.0.6 → 0.0.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/README +22 -4
- data/Rakefile +15 -3
- data/TODO +2 -3
- data/bin/merb +61 -36
- data/examples/sample_app/dist/app/controllers/files.rb +31 -0
- data/examples/sample_app/dist/app/controllers/posts.rb +26 -2
- data/examples/sample_app/dist/app/controllers/test.rb +7 -1
- data/examples/sample_app/dist/app/views/files/progress.jerb +3 -0
- data/examples/sample_app/dist/app/views/files/start.herb +62 -0
- data/examples/sample_app/dist/app/views/files/upload.herb +6 -0
- data/examples/sample_app/dist/app/views/layout/{application.rhtml → application.herb} +2 -3
- data/examples/sample_app/dist/app/views/layout/{foo.rhtml → foo.herb} +0 -0
- data/examples/sample_app/dist/app/views/posts/{_comments.rhtml → _comments.herb} +0 -0
- data/examples/sample_app/dist/app/views/posts/comment.jerb +1 -0
- data/examples/sample_app/dist/app/views/posts/{list.rhtml → list.herb} +0 -0
- data/examples/sample_app/dist/app/views/posts/{new.rhtml → new.herb} +0 -0
- data/examples/sample_app/dist/app/views/posts/{show.rhtml → show.herb} +0 -0
- data/examples/sample_app/dist/app/views/posts/xml_test.xerb +3 -0
- data/examples/sample_app/dist/app/views/test/{foo.rhtml → foo.herb} +0 -0
- data/examples/sample_app/dist/app/views/test/{hello.rhtml → hello.herb} +0 -0
- data/examples/sample_app/dist/app/views/test/json.jerb +1 -0
- data/examples/sample_app/dist/conf/merb.yml +11 -0
- data/examples/sample_app/dist/conf/merb_init.rb +1 -1
- data/examples/sample_app/dist/conf/mup.conf +11 -0
- data/examples/sample_app/dist/public/javascripts/mup.js +113 -0
- data/examples/sample_app/script/merb_stop +7 -3
- data/examples/sample_app/script/startdrb +8 -0
- data/lib/merb.rb +37 -2
- data/lib/merb/merb_class_extensions.rb +21 -22
- data/lib/merb/merb_controller.rb +101 -33
- data/lib/merb/merb_handler.rb +26 -25
- data/lib/merb/merb_router.rb +1 -1
- data/lib/merb/merb_utils.rb +35 -37
- data/lib/merb/mixins/basic_authentication_mixin.rb +39 -0
- data/lib/merb/mixins/controller_mixin.rb +119 -115
- data/lib/merb/mixins/javascript_mixin.rb +63 -0
- data/lib/merb/mixins/render_mixin.rb +85 -69
- data/lib/merb/mixins/responder_mixin.rb +38 -0
- data/lib/merb/session/merb_drb_server.rb +107 -0
- data/lib/merb/session/merb_drb_session.rb +71 -0
- data/lib/merb/session/merb_session.rb +1 -0
- data/lib/merb/vendor/paginator/README.txt +84 -0
- data/lib/merb/vendor/paginator/paginator.rb +121 -0
- data/lib/mutex_hotfix.rb +34 -0
- metadata +41 -63
- data/doc/rdoc/classes/ControllerMixin.html +0 -676
- data/doc/rdoc/classes/Hash.html +0 -148
- data/doc/rdoc/classes/Merb.html +0 -140
- data/doc/rdoc/classes/Merb/Controller.html +0 -338
- data/doc/rdoc/classes/Merb/RouteMatcher.html +0 -388
- data/doc/rdoc/classes/Merb/Server.html +0 -148
- data/doc/rdoc/classes/Merb/Session.html +0 -201
- data/doc/rdoc/classes/Merb/SessionMixin.html +0 -199
- data/doc/rdoc/classes/MerbControllerError.html +0 -111
- data/doc/rdoc/classes/MerbHandler.html +0 -430
- data/doc/rdoc/classes/MerbHash.html +0 -469
- data/doc/rdoc/classes/MerbHash/Mutex.html +0 -198
- data/doc/rdoc/classes/Noroutefound.html +0 -153
- data/doc/rdoc/classes/Object.html +0 -149
- data/doc/rdoc/classes/RenderMixin.html +0 -362
- data/doc/rdoc/classes/String.html +0 -212
- data/doc/rdoc/classes/Symbol.html +0 -179
- data/doc/rdoc/created.rid +0 -1
- data/doc/rdoc/files/LICENSE.html +0 -129
- data/doc/rdoc/files/README.html +0 -417
- data/doc/rdoc/files/TODO.html +0 -151
- data/doc/rdoc/files/lib/merb/merb_class_extensions_rb.html +0 -101
- data/doc/rdoc/files/lib/merb/merb_controller_rb.html +0 -101
- data/doc/rdoc/files/lib/merb/merb_handler_rb.html +0 -101
- data/doc/rdoc/files/lib/merb/merb_router_rb.html +0 -101
- data/doc/rdoc/files/lib/merb/merb_utils_rb.html +0 -108
- data/doc/rdoc/files/lib/merb/mixins/controller_mixin_rb.html +0 -101
- data/doc/rdoc/files/lib/merb/mixins/render_mixin_rb.html +0 -101
- data/doc/rdoc/files/lib/merb/session/merb_session_rb.html +0 -101
- data/doc/rdoc/files/lib/merb_rb.html +0 -140
- data/doc/rdoc/files/lib/merb_tasks_rb.html +0 -101
- data/doc/rdoc/fr_class_index.html +0 -43
- data/doc/rdoc/fr_file_index.html +0 -40
- data/doc/rdoc/fr_method_index.html +0 -104
- data/doc/rdoc/index.html +0 -24
- data/doc/rdoc/rdoc-style.css +0 -208
- data/examples/sample_app/dist/app/controllers/upload.rb +0 -29
- data/examples/sample_app/dist/app/views/posts/comment.merbjs +0 -1
- data/examples/sample_app/dist/app/views/upload/start.rhtml +0 -15
- data/examples/sample_app/dist/app/views/upload/upload.rhtml +0 -4
- data/examples/sample_app/dist/public/files/README +0 -35
- data/examples/sample_app/dist/public/files/setup.rb +0 -1346
- data/examples/sample_app/log/merb.log +0 -778
data/lib/merb/merb_handler.rb
CHANGED
|
@@ -25,6 +25,8 @@ class MerbHandler < Mongrel::HttpHandler
|
|
|
25
25
|
# and your controller can go on processing other requests.
|
|
26
26
|
def process(request, response)
|
|
27
27
|
|
|
28
|
+
start = Time.now
|
|
29
|
+
|
|
28
30
|
if response.socket.closed?
|
|
29
31
|
return
|
|
30
32
|
end
|
|
@@ -41,11 +43,10 @@ class MerbHandler < Mongrel::HttpHandler
|
|
|
41
43
|
if get_or_head and @files.can_serve(path_info)
|
|
42
44
|
# File exists as-is so serve it up
|
|
43
45
|
MERB_LOGGER.info("Serving static file: #{path_info}")
|
|
44
|
-
|
|
45
46
|
@files.process(request,response)
|
|
46
47
|
elsif get_or_head and @files.can_serve(page_cached)
|
|
47
48
|
# Possible cached page, serve it up
|
|
48
|
-
MERB_LOGGER.info("Serving static file: #{
|
|
49
|
+
MERB_LOGGER.info("Serving static file: #{page_cached}")
|
|
49
50
|
request.params[Mongrel::Const::PATH_INFO] = page_cached
|
|
50
51
|
@files.process(request,response)
|
|
51
52
|
else
|
|
@@ -53,14 +54,22 @@ class MerbHandler < Mongrel::HttpHandler
|
|
|
53
54
|
# This handles parsing the query string and post/file upload
|
|
54
55
|
# params and is outside of the synchronize call so that
|
|
55
56
|
# multiple file uploads can be done at once.
|
|
57
|
+
controller = nil
|
|
56
58
|
controller, action = handle(request)
|
|
57
|
-
MERB_LOGGER.info("Routing to controller: #{controller.class} action: #{action}")
|
|
58
|
-
|
|
59
|
-
#
|
|
60
|
-
#
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
59
|
+
MERB_LOGGER.info("Routing to controller: #{controller.class} action: #{action}\nParsing HTTP Input took: #{Time.now - start} seconds")
|
|
60
|
+
|
|
61
|
+
# special case allows the progress action of a Files controller
|
|
62
|
+
# to be handled without locking since no db access is required.
|
|
63
|
+
# but we do need to synchronize whenever you use ActiveRecord
|
|
64
|
+
# in your controllers.
|
|
65
|
+
if (action == 'progress') && (Files === controller)
|
|
66
|
+
puts 'skip mutex'
|
|
67
|
+
controller.dispatch(action)
|
|
68
|
+
else
|
|
69
|
+
@guard.synchronize {
|
|
70
|
+
controller.dispatch(action)
|
|
71
|
+
}
|
|
72
|
+
end
|
|
64
73
|
rescue Exception => e
|
|
65
74
|
response.start(500) do |head,out|
|
|
66
75
|
head["Content-Type"] = "text/html"
|
|
@@ -87,20 +96,18 @@ class MerbHandler < Mongrel::HttpHandler
|
|
|
87
96
|
end
|
|
88
97
|
end
|
|
89
98
|
|
|
90
|
-
controller = nil
|
|
91
|
-
|
|
92
99
|
if sendfile
|
|
93
|
-
MERB_LOGGER.info("X-SENDFILE: #{sendfile}")
|
|
100
|
+
MERB_LOGGER.info("X-SENDFILE: #{sendfile}\nComplete Request took: #{Time.now - start} seconds")
|
|
94
101
|
# send X-SENDFILE header to mongrel
|
|
95
102
|
response.send_status(File.size(sendfile))
|
|
96
103
|
response.send_header
|
|
97
104
|
response.send_file(sendfile)
|
|
98
105
|
else
|
|
99
|
-
MERB_LOGGER.info("Response status: #{response.status}\n\n")
|
|
106
|
+
MERB_LOGGER.info("Response status: #{response.status}\nComplete Request took: #{Time.now - start} seconds\n\n")
|
|
100
107
|
# render response from successful controller
|
|
101
|
-
response.send_status((
|
|
108
|
+
response.send_status((controller.body||='').length)
|
|
102
109
|
response.send_header
|
|
103
|
-
response.write(
|
|
110
|
+
response.write(controller.body)
|
|
104
111
|
end
|
|
105
112
|
end
|
|
106
113
|
end
|
|
@@ -114,14 +121,8 @@ class MerbHandler < Mongrel::HttpHandler
|
|
|
114
121
|
path = request.params[Mongrel::Const::PATH_INFO].sub(/\/+/, '/')
|
|
115
122
|
path = path[0..-2] if (path[-1] == ?/)
|
|
116
123
|
route = Merb::RouteMatcher.new.route_request(path)
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
[ instantiate_controller(route[:controller], request.body, request.params, route),
|
|
120
|
-
route[:action] ]
|
|
121
|
-
else
|
|
122
|
-
MERB_LOGGER.info("No Matching Route!")
|
|
123
|
-
["<html><body>Error: no route matches!</body></html>", nil]
|
|
124
|
-
end
|
|
124
|
+
[ instantiate_controller(route[:controller], request.body, request.params, route),
|
|
125
|
+
route[:action] ]
|
|
125
126
|
end
|
|
126
127
|
|
|
127
128
|
# take a controller class name string and reload or require
|
|
@@ -143,8 +144,8 @@ class MerbHandler < Mongrel::HttpHandler
|
|
|
143
144
|
|
|
144
145
|
# format exception message for browser display
|
|
145
146
|
def html_exception(e)
|
|
146
|
-
"<html><h2>Merb Error!</h2><p>#{ e.message } - (#{ e.class })\n" <<
|
|
147
|
-
"#{(e.backtrace or []).join('<br />')}</p></html>"
|
|
147
|
+
"<html><body><h2>Merb Error!</h2><p>#{ e.message } - (#{ e.class })\n" <<
|
|
148
|
+
"#{(e.backtrace or []).join('<br />')}</p></body></html>"
|
|
148
149
|
end
|
|
149
150
|
|
|
150
151
|
def exception(e)
|
data/lib/merb/merb_router.rb
CHANGED
|
@@ -59,7 +59,7 @@ module Merb
|
|
|
59
59
|
|
|
60
60
|
# compile each individual route into a when /.../
|
|
61
61
|
# component of the case statement. Takes /:sections
|
|
62
|
-
#
|
|
62
|
+
# of the route def that start with : and turns them
|
|
63
63
|
# into placeholders for whatever urls match against
|
|
64
64
|
# the route in question. Special case for the default
|
|
65
65
|
# /:controller/:action/:id route.
|
data/lib/merb/merb_utils.rb
CHANGED
|
@@ -4,7 +4,12 @@ class String
|
|
|
4
4
|
# :allow_reloading is set to true in the config
|
|
5
5
|
# file or command line options.
|
|
6
6
|
def import
|
|
7
|
-
Merb::Server.config[:allow_reloading]
|
|
7
|
+
if Merb::Server.config[:allow_reloading]
|
|
8
|
+
Object.send(:remove_const, self.camel_case.intern) rescue nil
|
|
9
|
+
load(self.snake_case + '.rb')
|
|
10
|
+
else
|
|
11
|
+
require(self.snake_case)
|
|
12
|
+
end
|
|
8
13
|
end
|
|
9
14
|
|
|
10
15
|
# "FooBar".snake_case #=> "foo_bar"
|
|
@@ -20,14 +25,40 @@ class String
|
|
|
20
25
|
words.map!{|w| w.downcase.sub(%r/^./){|c| c.upcase}}
|
|
21
26
|
words.join
|
|
22
27
|
end
|
|
28
|
+
|
|
29
|
+
# Concatenates a path
|
|
30
|
+
def /(o)
|
|
31
|
+
File.join(self, o.to_s)
|
|
32
|
+
end
|
|
23
33
|
|
|
24
34
|
end
|
|
25
35
|
|
|
36
|
+
|
|
37
|
+
module Enumerable
|
|
38
|
+
def injecting(s)
|
|
39
|
+
inject(s) do |k, i|
|
|
40
|
+
yield(k, i); k
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
class Numeric
|
|
46
|
+
def to_currency( pre_symbol='$', thousands=',', decimal='.',
|
|
47
|
+
post_symbol=nil )
|
|
48
|
+
"#{pre_symbol}#{
|
|
49
|
+
( "%.2f" % self ).gsub(
|
|
50
|
+
/(\d)(?=(?:\d{3})+(?:$|\.))/,
|
|
51
|
+
"\\1#{thousands}"
|
|
52
|
+
)
|
|
53
|
+
}#{post_symbol}"
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
26
57
|
class Symbol
|
|
27
58
|
|
|
28
59
|
# faster Symbol#to_s to speed up routing.
|
|
29
60
|
def to_s
|
|
30
|
-
@str_rep
|
|
61
|
+
@str_rep ||= id2name.freeze
|
|
31
62
|
end
|
|
32
63
|
|
|
33
64
|
# ["foo", "bar"].map &:reverse #=> ['oof', 'rab']
|
|
@@ -107,8 +138,9 @@ class MerbHash < Hash
|
|
|
107
138
|
super(convert_key(key))
|
|
108
139
|
end
|
|
109
140
|
|
|
141
|
+
# allow merbhash.key to work the same as merbhash[key]
|
|
110
142
|
def method_missing(m,*a)
|
|
111
|
-
m.to_s
|
|
143
|
+
m.to_s =~ /=$/?self[$`]=a[0]:a==[]?self[m]:raise(NoMethodError,"#{m}")
|
|
112
144
|
end
|
|
113
145
|
|
|
114
146
|
protected
|
|
@@ -120,37 +152,3 @@ class MerbHash < Hash
|
|
|
120
152
|
end
|
|
121
153
|
end
|
|
122
154
|
|
|
123
|
-
require 'thread'
|
|
124
|
-
|
|
125
|
-
# monkey patch Mutex so it does not leak memory.
|
|
126
|
-
class Mutex
|
|
127
|
-
|
|
128
|
-
def lock
|
|
129
|
-
while (Thread.critical = true; @locked)
|
|
130
|
-
@waiting.unshift Thread.current
|
|
131
|
-
Thread.stop
|
|
132
|
-
end
|
|
133
|
-
@locked = true
|
|
134
|
-
Thread.critical = false
|
|
135
|
-
self
|
|
136
|
-
end
|
|
137
|
-
|
|
138
|
-
def unlock
|
|
139
|
-
return unless @locked
|
|
140
|
-
Thread.critical = true
|
|
141
|
-
@locked = false
|
|
142
|
-
begin
|
|
143
|
-
t = @waiting.pop
|
|
144
|
-
t.wakeup if t
|
|
145
|
-
rescue ThreadError
|
|
146
|
-
retry
|
|
147
|
-
end
|
|
148
|
-
Thread.critical = false
|
|
149
|
-
begin
|
|
150
|
-
t.run if t
|
|
151
|
-
rescue ThreadError
|
|
152
|
-
end
|
|
153
|
-
self
|
|
154
|
-
end
|
|
155
|
-
|
|
156
|
-
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
module Merb
|
|
2
|
+
|
|
3
|
+
module Authentication
|
|
4
|
+
require 'base64'
|
|
5
|
+
|
|
6
|
+
def credentials
|
|
7
|
+
if d = %w{REDIRECT_X_HTTP_AUTHORIZATION
|
|
8
|
+
X-HTTP_AUTHORIZATION HTTP_AUTHORIZATION}.
|
|
9
|
+
inject([]) { |d,h| @env.has_key?(h) ? @env[h].to_s.split : d }
|
|
10
|
+
return Base64.decode64(d[1]).split(':')[0..1] if d[0] == 'Basic'
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def authenticated?
|
|
15
|
+
username, password = *credentials
|
|
16
|
+
username == Merb::Server.config[:basic_auth][:username] and password == Merb::Server.config[:basic_auth][:password]
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def authenticate
|
|
20
|
+
if !authenticated?
|
|
21
|
+
throw :halt
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def self.included(base)
|
|
26
|
+
base.class_eval do
|
|
27
|
+
def filters_halted
|
|
28
|
+
@status = 401
|
|
29
|
+
@headers['Content-type'] = 'text/plain'
|
|
30
|
+
@headers['Status'] = 'Unauthorized'
|
|
31
|
+
@headers['WWW-Authenticate'] = "Basic realm=\"#{Merb::Server.config[:basic_auth][:domain]}\""
|
|
32
|
+
return 'Unauthorized'
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
end
|
|
@@ -1,129 +1,133 @@
|
|
|
1
|
-
module
|
|
2
|
-
|
|
3
|
-
# redirect to another url It can be like /foo/bar
|
|
4
|
-
# for redirecting within your same app. Or it can
|
|
5
|
-
# be a fully qualified url to another site.
|
|
6
|
-
def redirect(url)
|
|
7
|
-
MERB_LOGGER.info("Redirecting to: #{url}")
|
|
8
|
-
@status = 302
|
|
9
|
-
@headers.merge!({'Location'=> url})
|
|
10
|
-
return ''
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
# pass in a path to a file and this will set the
|
|
14
|
-
# right headers and let mongrel do its thang and
|
|
15
|
-
# serve the static file directly.
|
|
16
|
-
def send_file(file)
|
|
17
|
-
headers['X-SENDFILE'] = file
|
|
18
|
-
return
|
|
19
|
-
end
|
|
1
|
+
module Merb
|
|
2
|
+
module ControllerMixin
|
|
20
3
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
# parses a query string or the payload of a POST
|
|
30
|
-
# request into the params hash. So for example:
|
|
31
|
-
# /foo?bar=nik&post[title]=heya&post[body]=whatever
|
|
32
|
-
# parses into:
|
|
33
|
-
# {:bar => 'nik', :post => {:title => 'heya', :body => 'whatever}}
|
|
34
|
-
def query_parse(qs, d = '&;')
|
|
35
|
-
m = proc {|_,o,n|o.u(n,&m)rescue([*o]<<n)}
|
|
36
|
-
(qs||'').split(/[#{d}] */n).inject(MerbHash[]) { |h,p|
|
|
37
|
-
k, v=unescape(p).split('=',2)
|
|
38
|
-
h.u(k.split(/[\]\[]+/).reverse.
|
|
39
|
-
inject(v) { |x,i| MerbHash[i,x] },&m)
|
|
40
|
-
}
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
# does url escaping
|
|
44
|
-
def escape(s)
|
|
45
|
-
Mongrel::HttpRequest.escape(s)
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
# does url unescaping
|
|
49
|
-
def unescape(s)
|
|
50
|
-
Mongrel::HttpRequest.unescape(s)
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
# escape text for javascript.
|
|
54
|
-
def escape_js(javascript)
|
|
55
|
-
(javascript || '').gsub('\\','\0\0').gsub(/\r\n|\n|\r/, "\\n").gsub(/["']/) { |m| "\\#{m}" }
|
|
56
|
-
end
|
|
57
|
-
alias js :escape_js
|
|
58
|
-
|
|
59
|
-
def xml_http_request?
|
|
60
|
-
not /XMLHttpRequest/i.match(@headers['HTTP_X_REQUESTED_WITH']).nil?
|
|
61
|
-
end
|
|
62
|
-
alias xhr? :xml_http_request?
|
|
63
|
-
alias ajax? :xml_http_request?
|
|
64
|
-
|
|
65
|
-
def remote_ip
|
|
66
|
-
return @headers['HTTP_CLIENT_IP'] if @headers.include?('HTTP_CLIENT_IP')
|
|
67
|
-
|
|
68
|
-
if @headers.include?('HTTP_X_FORWARDED_FOR') then
|
|
69
|
-
remote_ips = @headers['HTTP_X_FORWARDED_FOR'].split(',').reject do |ip|
|
|
70
|
-
ip =~ /^unknown$|^(127|10|172\.16|192\.168)\./i
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
return remote_ips.first.strip unless remote_ips.empty?
|
|
4
|
+
# redirect to another url It can be like /foo/bar
|
|
5
|
+
# for redirecting within your same app. Or it can
|
|
6
|
+
# be a fully qualified url to another site.
|
|
7
|
+
def redirect(url)
|
|
8
|
+
MERB_LOGGER.info("Redirecting to: #{url}")
|
|
9
|
+
@status = 302
|
|
10
|
+
headers.merge!({'Location'=> url})
|
|
11
|
+
return ''
|
|
74
12
|
end
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
13
|
+
|
|
14
|
+
# pass in a path to a file and this will set the
|
|
15
|
+
# right headers and let mongrel do its thang and
|
|
16
|
+
# serve the static file directly.
|
|
17
|
+
def send_file(file)
|
|
18
|
+
headers['X-SENDFILE'] = file
|
|
19
|
+
return
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# This uses nginx X-Accel-Redirect header to send
|
|
23
|
+
# a file directly from nginx. See the nginx wiki:
|
|
24
|
+
# http://wiki.codemongers.com/NginxXSendfile
|
|
25
|
+
def nginx_send_file(file)
|
|
26
|
+
headers['X-Accel-Redirect'] = file
|
|
27
|
+
return
|
|
28
|
+
end
|
|
82
29
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
30
|
+
# parses a query string or the payload of a POST
|
|
31
|
+
# request into the params hash. So for example:
|
|
32
|
+
# /foo?bar=nik&post[title]=heya&post[body]=whatever
|
|
33
|
+
# parses into:
|
|
34
|
+
# {:bar => 'nik', :post => {:title => 'heya', :body => 'whatever}}
|
|
35
|
+
def query_parse(qs, d = '&;')
|
|
36
|
+
m = proc {|_,o,n|o.u(n,&m)rescue([*o]<<n)}
|
|
37
|
+
(qs||'').split(/[#{d}] */n).inject(MerbHash[]) { |h,p|
|
|
38
|
+
k, v=unescape(p).split('=',2)
|
|
39
|
+
h.u(k.split(/[\]\[]+/).reverse.
|
|
40
|
+
inject(v) { |x,i| MerbHash[i,x] },&m)
|
|
41
|
+
}
|
|
42
|
+
end
|
|
86
43
|
|
|
87
|
-
|
|
44
|
+
def make_token
|
|
45
|
+
require 'digest/md5'
|
|
46
|
+
Digest::MD5.hexdigest("#{inspect}#{Time.now}#{rand}")
|
|
47
|
+
end
|
|
88
48
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
49
|
+
# does url escaping
|
|
50
|
+
def escape(s)
|
|
51
|
+
Mongrel::HttpRequest.escape(s)
|
|
52
|
+
end
|
|
92
53
|
|
|
93
|
-
|
|
54
|
+
# does url unescaping
|
|
55
|
+
def unescape(s)
|
|
56
|
+
Mongrel::HttpRequest.unescape(s)
|
|
57
|
+
end
|
|
94
58
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
59
|
+
# returns true if the request is an ajax request.
|
|
60
|
+
def xml_http_request?
|
|
61
|
+
not /XMLHttpRequest/i.match(@env['HTTP_X_REQUESTED_WITH']).nil?
|
|
62
|
+
end
|
|
63
|
+
alias xhr? :xml_http_request?
|
|
64
|
+
alias ajax? :xml_http_request?
|
|
98
65
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
66
|
+
# returns the remote IP address if it can find it.
|
|
67
|
+
def remote_ip
|
|
68
|
+
return @env['HTTP_CLIENT_IP'] if @env.include?('HTTP_CLIENT_IP')
|
|
102
69
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
def host
|
|
108
|
-
@headers['HTTP_X_FORWARDED_HOST'] || @headers['HTTP_HOST']
|
|
109
|
-
end
|
|
70
|
+
if @env.include?(Mongrel::Const::HTTP_X_FORWARDED_FOR) then
|
|
71
|
+
remote_ips = @env[Mongrel::Const::HTTP_X_FORWARDED_FOR].split(',').reject do |ip|
|
|
72
|
+
ip =~ /^unknown$|^(127|10|172\.16|192\.168)\./i
|
|
73
|
+
end
|
|
110
74
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
parts[0..-(tld_length+2)]
|
|
114
|
-
end
|
|
75
|
+
return remote_ips.first.strip unless remote_ips.empty?
|
|
76
|
+
end
|
|
115
77
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
78
|
+
return @env[Mongrel::Const::REMOTE_ADDR]
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# returns either 'https://' or 'http://' depending on
|
|
82
|
+
# the HTTPS header
|
|
83
|
+
def protocol
|
|
84
|
+
@env['HTTPS'] == 'on' ? 'https://' : 'http://'
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# returns true if the request is an SSL request
|
|
88
|
+
def ssl?
|
|
89
|
+
@env['HTTPS'] == 'on'
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# The request uri.
|
|
93
|
+
def uri
|
|
94
|
+
@env['REQUEST_URI']
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# The path is the uri without the query string.
|
|
98
|
+
def path
|
|
99
|
+
uri ? uri.split('?').first : ''
|
|
100
|
+
end
|
|
119
101
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
102
|
+
def path_info
|
|
103
|
+
@env['PATH_INFO']
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def port
|
|
107
|
+
@env['SERVER_PORT'].to_i
|
|
108
|
+
end
|
|
123
109
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
110
|
+
def host
|
|
111
|
+
@env['HTTP_X_FORWARDED_HOST'] || @env['HTTP_HOST']
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def subdomains(tld_length = 1)
|
|
115
|
+
parts = host.split('.')
|
|
116
|
+
parts[0..-(tld_length+2)]
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def domain(tld_length = 1)
|
|
120
|
+
host.split('.').last(1 + tld_length).join('.')
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def method
|
|
124
|
+
@method ||= @env['REQUEST_METHOD']
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
[:get, :post, :put, :delete, :head].each do |m|
|
|
128
|
+
eval %{
|
|
129
|
+
def #{m}?; method == :#{m}; end
|
|
130
|
+
}
|
|
131
|
+
end
|
|
128
132
|
end
|
|
129
|
-
end
|
|
133
|
+
end
|