merb 0.4.1 → 0.4.2
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 +138 -56
- data/Rakefile +23 -8
- data/app_generators/merb/templates/Rakefile +13 -0
- data/app_generators/merb/templates/app/helpers/global_helper.rb +1 -1
- data/app_generators/merb/templates/app/views/exceptions/internal_server_error.html.erb +12 -3
- data/app_generators/merb/templates/config/merb.yml +14 -1
- data/app_generators/merb/templates/spec/spec_helper.rb +6 -0
- data/app_generators/merb/templates/test/test_helper.rb +1 -0
- data/lib/merb.rb +27 -7
- data/lib/merb/abstract_controller.rb +76 -36
- data/lib/merb/caching/store/memcache.rb +20 -0
- data/lib/merb/constants.rb +2 -4
- data/lib/merb/controller.rb +44 -2
- data/lib/merb/core_ext/get_args.rb +23 -4
- data/lib/merb/core_ext/hash.rb +16 -11
- data/lib/merb/core_ext/inflections.rb +1 -1
- data/lib/merb/core_ext/kernel.rb +106 -26
- data/lib/merb/core_ext/numeric.rb +1 -1
- data/lib/merb/core_ext/string.rb +10 -13
- data/lib/merb/dispatcher.rb +2 -2
- data/lib/merb/exceptions.rb +3 -1
- data/lib/merb/logger.rb +15 -6
- data/lib/merb/mail_controller.rb +18 -2
- data/lib/merb/mailer.rb +1 -1
- data/lib/merb/mixins/controller.rb +64 -228
- data/lib/merb/mixins/erubis_capture.rb +1 -1
- data/lib/merb/mixins/general_controller.rb +258 -0
- data/lib/merb/mixins/render.rb +45 -24
- data/lib/merb/mixins/responder.rb +89 -18
- data/lib/merb/mixins/view_context.rb +32 -5
- data/lib/merb/mixins/web_controller.rb +8 -1
- data/lib/merb/mongrel_handler.rb +27 -17
- data/lib/merb/part_controller.rb +10 -0
- data/lib/merb/request.rb +34 -14
- data/lib/merb/router.rb +77 -45
- data/lib/merb/server.rb +116 -72
- data/lib/merb/session/cookie_store.rb +14 -22
- data/lib/merb/session/mem_cache_session.rb +2 -2
- data/lib/merb/session/memory_session.rb +12 -1
- data/lib/merb/template/erubis.rb +31 -0
- data/lib/merb/template/haml.rb +4 -14
- data/lib/merb/template/xml_builder.rb +1 -1
- data/lib/merb/test/helper.rb +90 -18
- data/lib/merb/test/rspec.rb +145 -74
- data/lib/merb/version.rb +11 -0
- data/lib/merb/view_context.rb +3 -6
- data/lib/patch +69 -0
- data/lib/tasks/merb.rake +1 -1
- data/spec/fixtures/config/environments/environment_config_test.yml +1 -0
- data/spec/fixtures/controllers/render_spec_controllers.rb +63 -4
- data/spec/fixtures/views/examples/template_throw_content_without_block.html.erb +3 -0
- data/spec/fixtures/views/partials/_erubis.html.erb +1 -1
- data/spec/merb/abstract_controller_spec.rb +1 -0
- data/spec/merb/controller_filters_spec.rb +68 -3
- data/spec/merb/controller_spec.rb +35 -68
- data/spec/merb/cookie_store_spec.rb +7 -20
- data/spec/merb/core_ext_spec.rb +35 -1
- data/spec/merb/dispatch_spec.rb +8 -2
- data/spec/merb/generator_spec.rb +12 -4
- data/spec/merb/mail_controller_spec.rb +33 -0
- data/spec/merb/part_controller_spec.rb +33 -1
- data/spec/merb/render_spec.rb +74 -0
- data/spec/merb/request_spec.rb +43 -0
- data/spec/merb/responder_spec.rb +1 -0
- data/spec/merb/router_spec.rb +118 -13
- data/spec/merb/server_spec.rb +19 -0
- data/spec/merb/view_context_spec.rb +31 -3
- data/spec/spec_helper.rb +8 -0
- data/spec/spec_helpers/url_shared_behaviour.rb +112 -0
- metadata +124 -87
@@ -9,7 +9,7 @@ class Numeric
|
|
9
9
|
# after the number:
|
10
10
|
# 1.5.to_currency nil, ",", ".", "$USD" #=> "1.50$USD"
|
11
11
|
# 67_000.5.to_currency nil, ".", ",", "DM" #=> "67.000,50DM"
|
12
|
-
def to_currency(pre_symbol='$', thousands=',', decimal='.', post_symbol=nil)
|
12
|
+
def to_currency(pre_symbol='$', thousands=',', decimal='.', post_symbol=nil)
|
13
13
|
"#{pre_symbol}#{("%.2f" % self ).gsub(".", decimal).gsub(/(\d)(?=(?:\d{3})+(?:$|[\\#{decimal}]))/,"\\1#{thousands}")}#{post_symbol}"
|
14
14
|
end
|
15
15
|
|
data/lib/merb/core_ext/string.rb
CHANGED
@@ -2,7 +2,10 @@ require 'strscan'
|
|
2
2
|
|
3
3
|
class String
|
4
4
|
class InvalidPathConversion < Exception; end
|
5
|
-
|
5
|
+
|
6
|
+
# Escapes any characters in the string that would have special meaning in a
|
7
|
+
# regular expression.
|
8
|
+
# $ "\*?{}.".escape_regexp #=> "\\*\\?\\{\\}\\."
|
6
9
|
def escape_regexp
|
7
10
|
Regexp.escape self
|
8
11
|
end
|
@@ -17,6 +20,8 @@ class String
|
|
17
20
|
split('_').map{|e| e.capitalize}.join
|
18
21
|
end
|
19
22
|
|
23
|
+
# "merb/core_ext/string" #=> "Merb::CoreExt::String"
|
24
|
+
#
|
20
25
|
# About 50% faster than string.split('/').map{ |s| s.camel_case }.join('::')
|
21
26
|
def to_const_string
|
22
27
|
new_string = ""
|
@@ -33,24 +38,16 @@ class String
|
|
33
38
|
end
|
34
39
|
|
35
40
|
# Concatenates a path
|
41
|
+
# $ "merb"/"core_ext" #=> "merb/core_ext"
|
36
42
|
def /(o)
|
37
43
|
File.join(self, o.to_s)
|
38
44
|
end
|
39
45
|
|
40
46
|
# Takes lines of text, removes any indentation, and
|
41
47
|
# adds +indentation+ number of spaces to each line
|
48
|
+
# $ " Hello\n Bob\nHow are you?".indent(3)
|
49
|
+
# #=> " Hello\n Bob\n How are you?"
|
42
50
|
def indent(indentation)
|
43
|
-
|
44
|
-
initial_indentation = lines.first.scan(/^(\s+)/).flatten.first
|
45
|
-
lines.map do |line|
|
46
|
-
if initial_indentation.nil?
|
47
|
-
" " * indentation + line
|
48
|
-
elsif line.index(initial_indentation) == 0
|
49
|
-
" " * indentation + line[initial_indentation.size..-1]
|
50
|
-
else
|
51
|
-
" " * indentation + line
|
52
|
-
end
|
53
|
-
end.join
|
51
|
+
match(/\s*/) && gsub(/^\s{0,#{$&.length}}/, " " * indentation)
|
54
52
|
end
|
55
|
-
|
56
53
|
end
|
data/lib/merb/dispatcher.rb
CHANGED
@@ -25,9 +25,9 @@ module Merb
|
|
25
25
|
MERB_LOGGER.info("Cookies: #{request.cookies.inspect}")
|
26
26
|
# user friendly error messages
|
27
27
|
if request.route_params.empty?
|
28
|
-
raise ControllerExceptions::
|
28
|
+
raise ControllerExceptions::BadRequest, "No routes match the request"
|
29
29
|
elsif request.controller_name.nil?
|
30
|
-
raise ControllerExceptions::
|
30
|
+
raise ControllerExceptions::BadRequest, "Route matched, but route did not specify a controller"
|
31
31
|
end
|
32
32
|
MERB_LOGGER.debug("Routed to: #{request.route_params.inspect}")
|
33
33
|
# set controller class and the action to call
|
data/lib/merb/exceptions.rb
CHANGED
@@ -136,6 +136,7 @@ module Merb
|
|
136
136
|
class TemporaryRedirect < Merb::ControllerExceptions::Redirection; STATUS = 307; end
|
137
137
|
class ClientError < Merb::ControllerExceptions::Base; end
|
138
138
|
class BadRequest < Merb::ControllerExceptions::ClientError; STATUS = 400; end
|
139
|
+
class MultiPartParseError < Merb::ControllerExceptions::BadRequest; end
|
139
140
|
class Unauthorized < Merb::ControllerExceptions::ClientError; STATUS = 401; end
|
140
141
|
class PaymentRequired < Merb::ControllerExceptions::ClientError; STATUS = 402; end
|
141
142
|
class Forbidden < Merb::ControllerExceptions::ClientError; STATUS = 403; end
|
@@ -162,7 +163,8 @@ module Merb
|
|
162
163
|
class ServiceUnavailable < Merb::ControllerExceptions::ServerError; STATUS = 503; end
|
163
164
|
class GatewayTimeout < Merb::ControllerExceptions::ServerError; STATUS = 504; end
|
164
165
|
class HTTPVersionNotSupported < Merb::ControllerExceptions::ServerError; STATUS = 505; end
|
165
|
-
class InternalServerError < Merb::ControllerExceptions::ServerError;
|
166
|
+
class InternalServerError < Merb::ControllerExceptions::ServerError;
|
167
|
+
STATUS = 500
|
166
168
|
DEFAULT_TEMPLATE = ::Merb::Dispatcher::DEFAULT_ERROR_TEMPLATE
|
167
169
|
|
168
170
|
def initialize(exception = nil)
|
data/lib/merb/logger.rb
CHANGED
@@ -17,6 +17,7 @@ module Merb
|
|
17
17
|
def initialize(log, level = DEBUG)
|
18
18
|
@level = level
|
19
19
|
@buffer = []
|
20
|
+
@aio = false
|
20
21
|
if log.respond_to?(:write)
|
21
22
|
@log = log
|
22
23
|
elsif File.exist?(log)
|
@@ -27,16 +28,24 @@ module Merb
|
|
27
28
|
@log.sync = true
|
28
29
|
@log.write("# Logfile created on %s\n" % [Time.now.to_s])
|
29
30
|
end
|
31
|
+
if !MERB_ENV.match(/development|test/) &&
|
32
|
+
!RUBY_PLATFORM.match(/java|mswin/) &&
|
33
|
+
!(@log == STDOUT) &&
|
34
|
+
@log.respond_to?(:write_nonblock)
|
35
|
+
@aio = true
|
36
|
+
end
|
30
37
|
end
|
31
|
-
|
38
|
+
|
32
39
|
def flush
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
40
|
+
unless @buffer.size == 0
|
41
|
+
if @aio
|
42
|
+
@log.write_nonblock(@buffer.slice!(0..-1).to_s)
|
43
|
+
else
|
44
|
+
@log.write(@buffer.slice!(0..-1).to_s)
|
45
|
+
end
|
37
46
|
end
|
38
47
|
end
|
39
|
-
|
48
|
+
|
40
49
|
def close
|
41
50
|
flush
|
42
51
|
@log.close if @log.respond_to?(:close)
|
data/lib/merb/mail_controller.rb
CHANGED
@@ -245,9 +245,25 @@ module Merb
|
|
245
245
|
|
246
246
|
# A convenience method that creates a blank copy of the MailController and runs
|
247
247
|
# dispatch_and_deliver on it.
|
248
|
-
def self.dispatch_and_deliver(method, mail_params)
|
249
|
-
new(
|
248
|
+
def self.dispatch_and_deliver(method, mail_params, send_params = {})
|
249
|
+
new(send_params).dispatch_and_deliver method, mail_params
|
250
250
|
end
|
251
251
|
|
252
|
+
protected
|
253
|
+
def route
|
254
|
+
@base_controller.route if @base_controller
|
255
|
+
end
|
256
|
+
|
257
|
+
private
|
258
|
+
# This method is here to overwrite the one in the general_controller mixin
|
259
|
+
# The method ensures that when a url is generated with a hash, it contains a controller
|
260
|
+
def get_controller_for_url_generation(opts)
|
261
|
+
puts @base_controller.params[:controller] if @base_controller
|
262
|
+
controller = opts[:controller] || ( @base_controller.params[:controller] if @base_controller)
|
263
|
+
raise "No Controller Specified for url()" unless controller
|
264
|
+
controller
|
265
|
+
end
|
266
|
+
|
267
|
+
|
252
268
|
end
|
253
269
|
end
|
data/lib/merb/mailer.rb
CHANGED
@@ -53,7 +53,7 @@ module Merb
|
|
53
53
|
def net_smtp
|
54
54
|
Net::SMTP.start(config[:host], config[:port].to_i, config[:domain],
|
55
55
|
config[:user], config[:pass], config[:auth]) { |smtp|
|
56
|
-
smtp.send_message(@mail.to_s, @mail.from.first, @mail.to)
|
56
|
+
smtp.send_message(@mail.to_s, @mail.from.first, @mail.to.to_s.split(/[,;]/))
|
57
57
|
}
|
58
58
|
end
|
59
59
|
|
@@ -1,201 +1,32 @@
|
|
1
1
|
module Merb
|
2
|
+
# Module that is mixed in to all implemented controllers.
|
2
3
|
module ControllerMixin
|
3
4
|
|
4
|
-
#
|
5
|
-
#
|
6
|
-
# hash fills in the dynamic parts of the route.
|
5
|
+
# Renders the block given as a parameter using chunked
|
6
|
+
# encoding.
|
7
7
|
#
|
8
|
-
#
|
9
|
-
# the route, then knowing the controller & action won't be enough
|
10
|
-
# to reverse-generate the route. However, if you use the default
|
11
|
-
# /controller/action/id?query route, +default_route+ can generate
|
12
|
-
# it for you.
|
8
|
+
# ==== Examples
|
13
9
|
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
#
|
28
|
-
#
|
29
|
-
# [:comment, "/blogposts/:blogpost_id/comments/:id"]
|
30
|
-
# [:edit_comment, "/blogposts/:blogpost_id/comments/:id/edit"]
|
31
|
-
# [:new_comment, "/blogposts/:blogpost_id/comments/new"]
|
32
|
-
# [:custom_new_comment, "/blogposts/:blogpost_id/comments/new/:action"]
|
33
|
-
#
|
34
|
-
# Examples:
|
35
|
-
#
|
36
|
-
# @post = Post.find(1)
|
37
|
-
# @comment = @post.comments.find(1)
|
38
|
-
#
|
39
|
-
# url(:blogposts) # => /blogposts
|
40
|
-
# url(:new_post) # => /blogposts/new
|
41
|
-
# url(:blogpost, @post) # => /blogposts/1
|
42
|
-
# url(:edit_blogpost, @post) # => /blogposts/1/edit
|
43
|
-
# url(:custom_new_blogpost, :action => 'alternate') # => /blogposts/new/alternate
|
44
|
-
#
|
45
|
-
# url(:comments, :blogpost => @post) # => /blogposts/1/comments
|
46
|
-
# url(:new_comment, :blogpost => @post) # => /blogposts/1/comments/new
|
47
|
-
# url(:comment, @comment) # => /blogposts/1/comments/1
|
48
|
-
# url(:edit_comment, @comment) # => /blogposts/1/comments/1/edit
|
49
|
-
# url(:custom_new_comment, :blogpost => @post)
|
50
|
-
#
|
51
|
-
# url(:page => 2) # => /posts/show/1?page=2
|
52
|
-
# url(:new_post, :page => 3) # => /posts/new?page=3
|
53
|
-
# url('/go/here', :page => 3) # => /go/here?page=3
|
54
|
-
#
|
55
|
-
# url(:controller => "welcome") # => /welcome
|
56
|
-
# url(:controller => "welcome", :action => "greet")
|
57
|
-
# # => /welcome/greet
|
58
|
-
#
|
59
|
-
def url(route_name = nil, new_params = {})
|
60
|
-
if route_name.is_a?(Hash)
|
61
|
-
new_params = route_name
|
62
|
-
route_name = nil
|
63
|
-
end
|
64
|
-
|
65
|
-
url = if new_params.respond_to?(:keys) && route_name.nil? &&
|
66
|
-
!(new_params.keys & [:controller, :action, :id]).empty?
|
67
|
-
url_from_default_route(new_params)
|
68
|
-
elsif route_name.nil? && !route.regexp?
|
69
|
-
url_from_route(route, new_params)
|
70
|
-
elsif route_name.nil?
|
71
|
-
request.path + (new_params.empty? ? "" : "?" + params_to_query_string(new_params))
|
72
|
-
elsif route_name.is_a?(Symbol)
|
73
|
-
url_from_route(route_name, new_params)
|
74
|
-
elsif route_name.is_a?(String)
|
75
|
-
route_name + (new_params.empty? ? "" : "?" + params_to_query_string(new_params))
|
76
|
-
else
|
77
|
-
raise "URL not generated: #{route_name.inspect}, #{new_params.inspect}"
|
78
|
-
end
|
79
|
-
url = MerbHandler.path_prefix + url if MerbHandler.path_prefix
|
80
|
-
url
|
81
|
-
end
|
82
|
-
|
83
|
-
def url_from_route(symbol, new_params = {})
|
84
|
-
if new_params.respond_to?(:new_record?) && new_params.new_record?
|
85
|
-
symbol = "#{symbol}".singularize.to_sym
|
86
|
-
new_params = {}
|
87
|
-
end
|
88
|
-
route = symbol.is_a?(Symbol) ? Merb::Router.named_routes[symbol] : symbol
|
89
|
-
raise "URL could not be constructed. Route symbol not found: #{symbol.inspect}" unless route
|
90
|
-
path = route.generate(new_params, params)
|
91
|
-
keys = route.symbol_segments
|
92
|
-
if new_params.is_a? Hash
|
93
|
-
if ext = format_extension(new_params)
|
94
|
-
new_params.delete(:format)
|
95
|
-
path += "." + ext
|
96
|
-
end
|
97
|
-
extras = new_params.reject{ |k, v| keys.include?(k) }
|
98
|
-
path += "?" + params_to_query_string(extras) unless extras.empty?
|
99
|
-
end
|
100
|
-
path
|
101
|
-
end
|
102
|
-
|
103
|
-
# this is pretty ugly, but it works. TODO: make this cleaner
|
104
|
-
def url_from_default_route(new_params)
|
105
|
-
query_params = new_params.reject do |k,v|
|
106
|
-
[:controller, :action, :id, :format].include?(k)
|
107
|
-
end
|
108
|
-
controller = new_params[:controller] || params[:controller]
|
109
|
-
controller = params[:controller] if controller == :current
|
110
|
-
url = "/#{controller}"
|
111
|
-
if new_params[:action] || new_params[:id] ||
|
112
|
-
new_params[:format] || !query_params.empty?
|
113
|
-
action = new_params[:action] || params[:action]
|
114
|
-
url += "/#{action}"
|
115
|
-
end
|
116
|
-
if new_params[:id]
|
117
|
-
url += "/#{new_params[:id]}"
|
118
|
-
end
|
119
|
-
if format = new_params[:format]
|
120
|
-
format = params[:format] if format == :current
|
121
|
-
url += ".#{format}"
|
122
|
-
end
|
123
|
-
unless query_params.empty?
|
124
|
-
url += "?" + params_to_query_string(query_params)
|
125
|
-
end
|
126
|
-
url
|
127
|
-
end
|
128
|
-
|
129
|
-
protected
|
130
|
-
|
131
|
-
# Creates query string from params, supporting nested arrays and hashes.
|
132
|
-
# ==== Example
|
133
|
-
# params_to_query_string(:user => {:filter => {:name => "quux*"}, :order => ["name"]})
|
134
|
-
# # => user[filter][name]=quux%2A&user[order][]=name
|
135
|
-
def params_to_query_string(value, prefix = nil)
|
136
|
-
case value
|
137
|
-
when Array
|
138
|
-
value.map { |v|
|
139
|
-
params_to_query_string(v, "#{prefix}[]")
|
140
|
-
} * "&"
|
141
|
-
when Hash
|
142
|
-
value.map { |k, v|
|
143
|
-
params_to_query_string(v, prefix ? "#{prefix}[#{escape(k)}]" : escape(k))
|
144
|
-
} * "&"
|
145
|
-
else
|
146
|
-
"#{prefix}=#{escape(value)}"
|
147
|
-
end
|
148
|
-
end
|
149
|
-
|
150
|
-
# +format_extension+ dictates when named route urls generated by #url
|
151
|
-
# will have a file-extension. It will return false or the format
|
152
|
-
# extension to append.
|
153
|
-
#
|
154
|
-
# url(:post, :id => post, :format => 'xml')
|
155
|
-
#
|
156
|
-
# generates:
|
157
|
-
#
|
158
|
-
# /posts/34.xml
|
159
|
-
#
|
160
|
-
# by default NON-HTML urls will be given an extension. it is posible
|
161
|
-
# to override this behaviour by setting +:use_format_in_urls+ in your
|
162
|
-
# server config (merb.yml) to either true/false
|
163
|
-
#
|
164
|
-
# +true+ would result in ALL urls (even html) being given extensions
|
165
|
-
# this is often desirable when you have many formats and dont
|
166
|
-
# wish to treat .html any differently from
|
167
|
-
# +false+ would result in NO urls being given extensions, and format
|
168
|
-
# gets treated just like any other param. leave it unset for
|
169
|
-
# the default behavior
|
170
|
-
#
|
171
|
-
def format_extension(new_params={})
|
172
|
-
use_format = Merb::Server.config[:use_format_in_urls]
|
173
|
-
if use_format.nil?
|
174
|
-
prms = params.merge(new_params)
|
175
|
-
use_format = prms[:format] != 'html' && prms[:format]
|
176
|
-
end
|
177
|
-
use_format
|
178
|
-
end
|
179
|
-
|
180
|
-
# render using chunked encoding
|
181
|
-
# def stream
|
182
|
-
# prefix = '<p>'
|
183
|
-
# suffix = "</p>\r\n"
|
184
|
-
# render_chunked do
|
185
|
-
# IO.popen("cat /tmp/test.log") do |io|
|
186
|
-
# done = false
|
187
|
-
# until done
|
188
|
-
# sleep 0.3
|
189
|
-
# line = io.gets.chomp
|
190
|
-
# if line == 'EOF'
|
191
|
-
# done = true
|
192
|
-
# else
|
193
|
-
# send_chunk(prefix + line + suffix)
|
10
|
+
# def stream
|
11
|
+
# prefix = '<p>'
|
12
|
+
# suffix = "</p>\r\n"
|
13
|
+
# render_chunked do
|
14
|
+
# IO.popen("cat /tmp/test.log") do |io|
|
15
|
+
# done = false
|
16
|
+
# until done
|
17
|
+
# sleep 0.3
|
18
|
+
# line = io.gets.chomp
|
19
|
+
#
|
20
|
+
# if line == 'EOF'
|
21
|
+
# done = true
|
22
|
+
# else
|
23
|
+
# send_chunk(prefix + line + suffix)
|
24
|
+
# end
|
194
25
|
# end
|
195
26
|
# end
|
196
27
|
# end
|
197
28
|
# end
|
198
|
-
#
|
29
|
+
#
|
199
30
|
def render_chunked(&blk)
|
200
31
|
headers['Transfer-Encoding'] = 'chunked'
|
201
32
|
Proc.new {
|
@@ -206,6 +37,9 @@ module Merb
|
|
206
37
|
}
|
207
38
|
end
|
208
39
|
|
40
|
+
# Returns a +Proc+ that Mongrel can call later, allowing
|
41
|
+
# Merb to release the thread lock and render another request.
|
42
|
+
#
|
209
43
|
def render_deferred(&blk)
|
210
44
|
Proc.new {
|
211
45
|
result = blk.call
|
@@ -215,25 +49,36 @@ module Merb
|
|
215
49
|
}
|
216
50
|
end
|
217
51
|
|
218
|
-
#
|
52
|
+
# Writes a chunk from render_chunked to the response that
|
53
|
+
# is sent back to the client.
|
219
54
|
def send_chunk(data)
|
220
55
|
response.write('%x' % data.size + "\r\n")
|
221
56
|
response.write(data + "\r\n")
|
222
57
|
end
|
223
58
|
|
224
|
-
#
|
225
|
-
#
|
226
|
-
#
|
59
|
+
# Redirects to a URL. The +url+ parameter can be either
|
60
|
+
# a relative URL (e.g., +/posts/34+) or a fully-qualified URL
|
61
|
+
# (e.g., +http://www.merbivore.com/+).
|
62
|
+
#
|
63
|
+
# ==== Parameters
|
64
|
+
#
|
65
|
+
# +url+ - URL to redirect to; it can be either a relative or
|
66
|
+
# fully-qualified URL.
|
67
|
+
#
|
227
68
|
def redirect(url)
|
228
69
|
MERB_LOGGER.info("Redirecting to: #{url}")
|
229
70
|
set_status(302)
|
230
|
-
headers
|
231
|
-
|
71
|
+
headers['Location'] = url
|
72
|
+
"<html><body>You are being <a href=\"#{url}\">redirected</a>.</body></html>"
|
232
73
|
end
|
233
74
|
|
234
|
-
#
|
235
|
-
# right headers
|
236
|
-
#
|
75
|
+
# Sends a file over HTTP. When given a path to a file, it will set the
|
76
|
+
# right headers so that the static file is served directly.
|
77
|
+
#
|
78
|
+
# ==== Parameters
|
79
|
+
#
|
80
|
+
# +file+ - Path to file to send to the client.
|
81
|
+
#
|
237
82
|
def send_file(file, opts={})
|
238
83
|
opts.update(Merb::Const::DEFAULT_SEND_FILE_OPTIONS.merge(opts))
|
239
84
|
disposition = opts[:disposition].dup || 'attachment'
|
@@ -247,6 +92,10 @@ module Merb
|
|
247
92
|
return
|
248
93
|
end
|
249
94
|
|
95
|
+
# Streams a file over HTTP.
|
96
|
+
#
|
97
|
+
# ==== Example
|
98
|
+
#
|
250
99
|
# stream_file( { :filename => file_name,
|
251
100
|
# :type => content_type,
|
252
101
|
# :content_length => content_length }) do
|
@@ -270,15 +119,24 @@ module Merb
|
|
270
119
|
end
|
271
120
|
|
272
121
|
|
273
|
-
#
|
274
|
-
# a file directly from nginx.
|
122
|
+
# Uses the nginx specific +X-Accel-Redirect+ header to send
|
123
|
+
# a file directly from nginx. For more information, see the nginx wiki:
|
275
124
|
# http://wiki.codemongers.com/NginxXSendfile
|
125
|
+
#
|
126
|
+
# ==== Parameters
|
127
|
+
#
|
128
|
+
# +file+ - Path to file to send to the client.
|
129
|
+
#
|
276
130
|
def nginx_send_file(file)
|
277
131
|
headers['X-Accel-Redirect'] = File.expand_path(file)
|
278
132
|
return
|
279
133
|
end
|
280
134
|
|
281
|
-
# Sets a cookie to be included in the response.
|
135
|
+
# Sets a cookie to be included in the response. This method is used
|
136
|
+
# primarily internally in Merb.
|
137
|
+
#
|
138
|
+
# If you need to set a cookie, then use the +cookies+ hash.
|
139
|
+
#
|
282
140
|
def set_cookie(name, value, expires)
|
283
141
|
(headers['Set-Cookie'] ||='') << (Merb::Const::SET_COOKIE % [
|
284
142
|
name.to_s,
|
@@ -288,35 +146,13 @@ module Merb
|
|
288
146
|
])
|
289
147
|
end
|
290
148
|
|
291
|
-
# Marks a cookie as deleted
|
292
|
-
# the past.
|
149
|
+
# Marks a cookie as deleted and gives it an expires stamp in
|
150
|
+
# the past. This method is used primarily internally in Merb.
|
151
|
+
#
|
152
|
+
# Use the +cookies+ hash to manipulate cookies instead.
|
153
|
+
#
|
293
154
|
def delete_cookie(name)
|
294
155
|
set_cookie(name, nil, Merb::Const::COOKIE_EXPIRED_TIME)
|
295
156
|
end
|
296
|
-
|
297
|
-
# creates a random token like:
|
298
|
-
# "b9a82e011694cc13a4249731b9e83cea"
|
299
|
-
def make_token
|
300
|
-
require 'digest/md5'
|
301
|
-
Digest::MD5.hexdigest("#{inspect}#{Time.now}#{rand}")
|
302
|
-
end
|
303
|
-
|
304
|
-
|
305
|
-
def escape_xml(obj)
|
306
|
-
obj.to_s.gsub(/[&<>"']/) { |s| Merb::Const::ESCAPE_TABLE[s] }
|
307
|
-
end
|
308
|
-
alias h escape_xml
|
309
|
-
alias html_escape escape_xml
|
310
|
-
|
311
|
-
# does url escaping
|
312
|
-
def escape(s)
|
313
|
-
Mongrel::HttpRequest.escape(s)
|
314
|
-
end
|
315
|
-
|
316
|
-
# does url unescaping
|
317
|
-
def unescape(s)
|
318
|
-
Mongrel::HttpRequest.unescape(s)
|
319
|
-
end
|
320
|
-
|
321
157
|
end
|
322
158
|
end
|