merb 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README +105 -95
- data/Rakefile +4 -5
- data/examples/skeleton.tar +0 -0
- data/examples/skeleton/dist/conf/router.rb +5 -2
- data/examples/skeleton/dist/schema/migrations/001_add_sessions_table.rb +0 -1
- data/lib/merb.rb +10 -3
- data/lib/merb/core_ext.rb +3 -1
- data/lib/merb/core_ext/merb_class.rb +85 -51
- data/lib/merb/core_ext/merb_inflections.rb +112 -0
- data/lib/merb/core_ext/merb_inflector.rb +275 -0
- data/lib/merb/core_ext/merb_object.rb +0 -55
- data/lib/merb/core_ext/merb_string.rb +1 -13
- data/lib/merb/merb_controller.rb +26 -20
- data/lib/merb/merb_dispatcher.rb +10 -3
- data/lib/merb/merb_exceptions.rb +6 -1
- data/lib/merb/merb_handler.rb +1 -1
- data/lib/merb/merb_mailer.rb +3 -2
- data/lib/merb/merb_request.rb +56 -0
- data/lib/merb/merb_router.rb +24 -38
- data/lib/merb/mixins/controller_mixin.rb +34 -5
- data/lib/merb/mixins/render_mixin.rb +7 -7
- data/lib/merb/mixins/responder_mixin.rb +17 -10
- data/lib/merb/mixins/view_context_mixin.rb +13 -2
- data/lib/merb/session/merb_ar_session.rb +4 -4
- data/lib/merb/session/merb_memory_session.rb +4 -4
- data/lib/merb/template/erubis.rb +1 -1
- data/lib/merb/template/haml.rb +20 -14
- data/lib/tasks/merb.rake +11 -2
- metadata +4 -2
data/lib/merb/merb_controller.rb
CHANGED
@@ -12,10 +12,18 @@ module Merb
|
|
12
12
|
# to your controller via params. It also parses the ?query=string and
|
13
13
|
# puts that into params as well.
|
14
14
|
class Controller
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
15
|
+
|
16
|
+
class_inheritable_accessor :_layout,
|
17
|
+
:_session_id_key,
|
18
|
+
:_template_extensions,
|
19
|
+
:_template_root,
|
20
|
+
:_layout_root
|
21
|
+
self._layout = :application
|
22
|
+
self._session_id_key = :_session_id
|
23
|
+
self._template_extensions = { }
|
24
|
+
self._template_root = File.expand_path(MERB_ROOT / "dist/app/views")
|
25
|
+
self._layout_root = File.expand_path(MERB_ROOT / "dist/app/views/layout")
|
26
|
+
|
19
27
|
include Merb::ControllerMixin
|
20
28
|
include Merb::RenderMixin
|
21
29
|
include Merb::ResponderMixin
|
@@ -23,22 +31,22 @@ module Merb
|
|
23
31
|
attr_accessor :status, :body, :request
|
24
32
|
|
25
33
|
MULTIPART_REGEXP = /\Amultipart\/form-data.*boundary=\"?([^\";,]+)/n.freeze
|
26
|
-
|
34
|
+
|
27
35
|
# parses the http request into params, headers and cookies
|
28
36
|
# that you can use in your controller classes. Also handles
|
29
37
|
# file uploads by writing a tempfile and passing a reference
|
30
38
|
# in params.
|
31
39
|
def initialize(request, env, args, response)
|
32
40
|
@env = MerbHash[env.to_hash]
|
33
|
-
@status, @method, @response, @headers = 200, (env[
|
34
|
-
|
35
|
-
cookies = query_parse(@env[
|
36
|
-
querystring = query_parse(@env[
|
41
|
+
@status, @method, @response, @headers = 200, (env[Mongrel::Const::REQUEST_METHOD]||Mongrel::Const::GET).downcase.to_sym, response,
|
42
|
+
Mongrel::Const::CONTENT_TYPE_TEXT_HTML_HASH
|
43
|
+
cookies = query_parse(@env[Mongrel::Const::HTTP_COOKIE], ';,')
|
44
|
+
querystring = query_parse(@env[Mongrel::Const::QUERY_STRING])
|
37
45
|
|
38
|
-
if MULTIPART_REGEXP =~ @env[
|
46
|
+
if MULTIPART_REGEXP =~ @env[Mongrel::Const::UPCASE_CONTENT_TYPE] && @method == :post
|
39
47
|
querystring.update(parse_multipart(request, $1))
|
40
48
|
elsif @method == :post
|
41
|
-
if [
|
49
|
+
if [Mongrel::Const::APPLICATION_JSON, Mongrel::Const::TEXT_JSON].include?(@env[Mongrel::Const::UPCASE_CONTENT_TYPE])
|
42
50
|
MERB_LOGGER.info("JSON Request")
|
43
51
|
json = JSON.parse(request.read || "") || {}
|
44
52
|
json = MerbHash.new(json) if json.is_a? Hash
|
@@ -49,7 +57,7 @@ module Merb
|
|
49
57
|
end
|
50
58
|
|
51
59
|
@cookies, @params = cookies, querystring.update(args)
|
52
|
-
@cookies[
|
60
|
+
@cookies[_session_id_key] = @params[_session_id_key] if @params.key?(_session_id_key)
|
53
61
|
|
54
62
|
allow = [:post, :put, :delete]
|
55
63
|
allow << :get if MERB_ENV == 'development'
|
@@ -81,7 +89,7 @@ module Merb
|
|
81
89
|
end
|
82
90
|
call_filters(after_filters)
|
83
91
|
finalize_session if respond_to?:finalize_session
|
84
|
-
MERB_LOGGER.info("Time spent in #{action} action: #{Time.now - start} seconds")
|
92
|
+
MERB_LOGGER.info("Time spent in #{self.class}##{action} action: #{Time.now - start} seconds")
|
85
93
|
end
|
86
94
|
|
87
95
|
# override this method on your controller classes to specialize
|
@@ -126,15 +134,13 @@ module Merb
|
|
126
134
|
@response
|
127
135
|
end
|
128
136
|
|
129
|
-
|
130
|
-
trait :template_root => File.expand_path(MERB_ROOT / "dist/app/views")
|
131
|
-
trait :layout_root => File.expand_path(MERB_ROOT / "dist/app/views/layout")
|
137
|
+
|
132
138
|
# lookup the trait[:template_extensions] for the extname of the filename
|
133
139
|
# you pass. Answers with the engine that matches the extension, Template::Erubis
|
134
140
|
# is used if none matches.
|
135
141
|
def engine_for(file)
|
136
142
|
extension = File.extname(file)[1..-1]
|
137
|
-
|
143
|
+
_template_extensions[extension]
|
138
144
|
rescue
|
139
145
|
::Merb::Template::Erubis
|
140
146
|
end
|
@@ -143,7 +149,7 @@ module Merb
|
|
143
149
|
# a list of extensions that will be looked up on #render of an action.
|
144
150
|
def self.register_engine(engine, *extensions)
|
145
151
|
[extensions].flatten.uniq.each do |ext|
|
146
|
-
|
152
|
+
_template_extensions[ext] = engine
|
147
153
|
end
|
148
154
|
end
|
149
155
|
|
@@ -154,8 +160,8 @@ module Merb
|
|
154
160
|
# from its superclasses. Since @@class variables are almost
|
155
161
|
# global vars within an inheritance tree, we use
|
156
162
|
# @class_instance_variables instead
|
157
|
-
|
158
|
-
|
163
|
+
class_inheritable_accessor :before_filters
|
164
|
+
class_inheritable_accessor :after_filters
|
159
165
|
|
160
166
|
# calls a filter chain according to rules.
|
161
167
|
def call_filters(filter_set)
|
data/lib/merb/merb_dispatcher.rb
CHANGED
@@ -5,6 +5,10 @@ module Merb
|
|
5
5
|
|
6
6
|
attr_accessor :path_prefix
|
7
7
|
|
8
|
+
def use_mutex=(val)
|
9
|
+
@@use_mutex = val
|
10
|
+
end
|
11
|
+
|
8
12
|
@@mutex = Mutex.new
|
9
13
|
@@use_mutex = ::Merb::Server.use_mutex
|
10
14
|
# This is where we grab the incoming request PATH_INFO
|
@@ -45,7 +49,7 @@ module Merb
|
|
45
49
|
def route_path(path)
|
46
50
|
path = path.sub(/\/+/, '/').sub(/\?.*$/, '')
|
47
51
|
path = path[0..-2] if (path[-1] == ?/) && path.size > 1
|
48
|
-
Merb::RouteMatcher.
|
52
|
+
Merb::RouteMatcher.route_request(path)
|
49
53
|
end
|
50
54
|
|
51
55
|
# take a controller class name string and reload or require
|
@@ -57,14 +61,17 @@ module Merb
|
|
57
61
|
raise "Bad controller! #{controller_name.snake_case}"
|
58
62
|
end unless $TESTING
|
59
63
|
begin
|
60
|
-
|
64
|
+
unless MERB_ENV == 'production'
|
65
|
+
Object.send(:remove_const, controller_name.camel_case.intern) rescue nil
|
66
|
+
load(controller_name.snake_case + '.rb')
|
67
|
+
end
|
61
68
|
return Object.const_get( controller_name.camel_case ).new(req, env, params, res)
|
62
69
|
rescue RuntimeError
|
63
70
|
warn "Error getting instance of '#{controller_name.camel_case}': #{$!}"
|
64
71
|
raise $!
|
65
72
|
end
|
66
73
|
end
|
67
|
-
|
74
|
+
|
68
75
|
end # end class << self
|
69
76
|
|
70
77
|
end
|
data/lib/merb/merb_exceptions.rb
CHANGED
@@ -43,7 +43,12 @@ module Merb
|
|
43
43
|
|
44
44
|
backtrace.map! do |line|
|
45
45
|
file, lineno, meth = line.scan(/(.*?):(\d+)(?::in `(.*?)')?/).first
|
46
|
-
|
46
|
+
|
47
|
+
# When a backtrace entry doesn't have a filepath as the first field
|
48
|
+
# (e.g., "(haml):12" or "(eval):41:in `_haml_render'"), it'll cause
|
49
|
+
# a second exception to be raised, masking the first. This traps it.
|
50
|
+
lines = (__caller_lines__(file, lineno, 5) rescue [])
|
51
|
+
|
47
52
|
[ lines, lines.object_id.abs, file, lineno, meth ]
|
48
53
|
end
|
49
54
|
|
data/lib/merb/merb_handler.rb
CHANGED
@@ -62,7 +62,7 @@ class MerbHandler < Mongrel::HttpHandler
|
|
62
62
|
@files.process(request,response)
|
63
63
|
else
|
64
64
|
begin
|
65
|
-
#
|
65
|
+
# Let Merb:Dispatcher find the route and call the filter chain and action
|
66
66
|
controller = nil
|
67
67
|
controller, action = Merb::Dispatcher.handle(request, response)
|
68
68
|
|
data/lib/merb/merb_mailer.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
|
-
|
1
|
+
|
2
2
|
|
3
3
|
begin
|
4
4
|
require 'mailfactory'
|
5
|
+
require 'net/smtp'
|
5
6
|
rescue LoadError
|
6
7
|
puts "You need to install the mailfactory gem to use Merb::Mailer"
|
7
8
|
MERB_LOGGER.warn "You need to install the mailfactory gem to use Merb::Mailer"
|
@@ -10,7 +11,7 @@ end
|
|
10
11
|
module Merb
|
11
12
|
class Mailer
|
12
13
|
|
13
|
-
|
14
|
+
class_inheritable_accessor :config
|
14
15
|
|
15
16
|
def sendmail
|
16
17
|
sendmail = IO.popen("sendmail #{@mail.to}", 'w+')
|
data/lib/merb/merb_request.rb
CHANGED
@@ -50,6 +50,62 @@ module Merb
|
|
50
50
|
@env['REQUEST_URI']
|
51
51
|
end
|
52
52
|
|
53
|
+
def user_agent
|
54
|
+
@env['HTTP_USER_AGENT']
|
55
|
+
end
|
56
|
+
|
57
|
+
def server_name
|
58
|
+
@env['SERVER_NAME']
|
59
|
+
end
|
60
|
+
|
61
|
+
def accept_encoding
|
62
|
+
@env['HTTP_ACCEPT_ENCODING']
|
63
|
+
end
|
64
|
+
|
65
|
+
def script_name
|
66
|
+
@env['SCRIPT_NAME']
|
67
|
+
end
|
68
|
+
|
69
|
+
def cache_control
|
70
|
+
@env['HTTP_CACHE_CONTROL']
|
71
|
+
end
|
72
|
+
|
73
|
+
def accept_language
|
74
|
+
@env['HTTP_ACCEPT_LANGUAGE']
|
75
|
+
end
|
76
|
+
|
77
|
+
def host
|
78
|
+
@env['HTTP_HOST']
|
79
|
+
end
|
80
|
+
|
81
|
+
def server_software
|
82
|
+
@env['SERVER_SOFTWARE']
|
83
|
+
end
|
84
|
+
|
85
|
+
def keep_alive
|
86
|
+
@env['HTTP_KEEP_ALIVE']
|
87
|
+
end
|
88
|
+
|
89
|
+
def accept_charset
|
90
|
+
@env['HTTP_ACCEPT_CHARSET']
|
91
|
+
end
|
92
|
+
|
93
|
+
def version
|
94
|
+
@env['HTTP_VERSION']
|
95
|
+
end
|
96
|
+
|
97
|
+
def gateway
|
98
|
+
@env['GATEWAY_INTERFACE']
|
99
|
+
end
|
100
|
+
|
101
|
+
def accept
|
102
|
+
@env['HTTP_ACCEPT']
|
103
|
+
end
|
104
|
+
|
105
|
+
def connection
|
106
|
+
@env['HTTP_CONNECTION']
|
107
|
+
end
|
108
|
+
|
53
109
|
def query_string
|
54
110
|
@env['QUERY_STRING']
|
55
111
|
end
|
data/lib/merb/merb_router.rb
CHANGED
@@ -1,9 +1,5 @@
|
|
1
1
|
module Merb
|
2
|
-
|
3
|
-
require 'active_support'
|
4
|
-
rescue
|
5
|
-
MERB_LOGGER.warn "You must have ActiveSupport installed to use merb restful routing\nNormal routing works fine without"
|
6
|
-
end
|
2
|
+
|
7
3
|
# Merb::RouteMatcher is the request routing mapper for the merb framework.
|
8
4
|
# You can define placeholder parts of the url with the :symbol notation.
|
9
5
|
# so r.add '/foo/:bar/baz/:id', :class => 'Bar', :method => 'foo'
|
@@ -28,17 +24,7 @@ module Merb
|
|
28
24
|
yield self
|
29
25
|
compile_router
|
30
26
|
end
|
31
|
-
|
32
|
-
# init @sections for route segment recognition
|
33
|
-
def initialize
|
34
|
-
@sections = Hash.new
|
35
|
-
end
|
36
|
-
|
37
|
-
# all defined routes in their raw form.
|
38
|
-
def routes
|
39
|
-
@@routes
|
40
|
-
end
|
41
|
-
|
27
|
+
|
42
28
|
# the final compiled lambda that gets used
|
43
29
|
# as the body of the route_request method.
|
44
30
|
def self.compiled_statement
|
@@ -63,28 +49,20 @@ module Merb
|
|
63
49
|
# against each of the compiled routes in turn.
|
64
50
|
# first route that matches wins.
|
65
51
|
def self.compile_router
|
66
|
-
router_lambda = @@routes.inject("lambda{|path| \n case path\n") { |m,r|
|
52
|
+
router_lambda = @@routes.inject("lambda{|path| \n sections={}\n case path\n") { |m,r|
|
67
53
|
m << compile(r)
|
68
54
|
} <<" else\n return {:controller=>'Noroutefound', :action=>'noroute'}\n end\n}"
|
69
55
|
@@compiled_statement = router_lambda
|
70
|
-
|
56
|
+
meta_def(:route_request, &eval(router_lambda))
|
71
57
|
end
|
72
58
|
|
73
59
|
# compile each individual route into a when /.../
|
74
60
|
# component of the case statement. Takes /:sections
|
75
61
|
# of the route def that start with : and turns them
|
76
62
|
# into placeholders for whatever urls match against
|
77
|
-
# the route in question.
|
78
|
-
# /:controller/:action/:id route.
|
63
|
+
# the route in question.
|
79
64
|
def self.compile(route)
|
80
65
|
raise ArgumentError unless String === route[0]
|
81
|
-
if route[0] == '/:controller/:action/:id'
|
82
|
-
return ' when /\A\/([^\/;.,?]+)(?:\/?\Z|\/([^\/;.,?]+)\/?)(?:\/?\Z|\/([^\/;.,?]+)\/?)\Z/
|
83
|
-
@sections[:controller] = $1
|
84
|
-
@sections[:action] = $2 || \'index\'
|
85
|
-
@sections[:id] = $3 if $3
|
86
|
-
return @sections'<<"\n"
|
87
|
-
end
|
88
66
|
code, count = '', 0
|
89
67
|
while route[0] =~ @@section_regexp
|
90
68
|
route[0] = route[0].dup
|
@@ -96,13 +74,13 @@ module Merb
|
|
96
74
|
else
|
97
75
|
route[0].sub!(@@section_regexp, "([^\/,?]+)")
|
98
76
|
end
|
99
|
-
code << "
|
77
|
+
code << " sections[:#{name}] = $#{count}\n"
|
100
78
|
end
|
101
79
|
@@compiled_regexen << Regexp.new(route[0])
|
102
80
|
index = @@compiled_regexen.size - 1
|
103
81
|
condition = " when @@compiled_regexen[#{index}] "
|
104
82
|
statement = "#{condition}\n#{code}"
|
105
|
-
statement << " return #{route[1].inspect}.update(
|
83
|
+
statement << " return #{route[1].inspect}.update(sections)\n"
|
106
84
|
statement
|
107
85
|
end
|
108
86
|
|
@@ -145,7 +123,7 @@ module Merb
|
|
145
123
|
if block_given?
|
146
124
|
procs = []
|
147
125
|
yield Resource.new(res, procs, opts)
|
148
|
-
procs.reverse.each
|
126
|
+
procs.reverse.each &:call
|
149
127
|
else
|
150
128
|
generate_resources_routes(res,opts)
|
151
129
|
end
|
@@ -157,7 +135,7 @@ module Merb
|
|
157
135
|
if block_given?
|
158
136
|
procs = []
|
159
137
|
yield Resource.new(res, procs, opts)
|
160
|
-
procs.reverse.each
|
138
|
+
procs.reverse.each &:call
|
161
139
|
else
|
162
140
|
generate_singleton_routes(res,opts)
|
163
141
|
end
|
@@ -165,19 +143,19 @@ module Merb
|
|
165
143
|
|
166
144
|
def self.generate_resources_routes(res,opt)
|
167
145
|
with_options :controller => res.to_s, :rest => true do |r|
|
168
|
-
r.add "#{opt[:prefix]}/#{res}/:id[
|
169
|
-
r.add "#{opt[:prefix]}/#{res}/new[
|
146
|
+
r.add "#{opt[:prefix]}/#{res}/:id[;/]+edit", :allowed => {:get => 'edit'}
|
147
|
+
r.add "#{opt[:prefix]}/#{res}/new[;/]+:action", :allowed => {:get => 'new', :post => 'new', :put => 'new', :delete => 'new'}
|
170
148
|
r.add "#{opt[:prefix]}/#{res}/new" , :allowed => {:get => 'new'}
|
171
149
|
if mem = opt[:member]
|
172
150
|
mem.keys.sort_by{|x| "#{x}"}.each {|action|
|
173
|
-
allowed = mem[action].
|
174
|
-
r.add "#{opt[:prefix]}/#{res}/:id[
|
151
|
+
allowed = mem[action].injecting({}) {|h, verb| h[verb] = "#{action}"}
|
152
|
+
r.add "#{opt[:prefix]}/#{res}/:id[;/]+#{action}", :allowed => allowed
|
175
153
|
}
|
176
154
|
end
|
177
155
|
if coll = opt[:collection]
|
178
156
|
coll.keys.sort_by{|x| "#{x}"}.each {|action|
|
179
|
-
allowed = coll[action].
|
180
|
-
r.add "#{opt[:prefix]}/#{res}[
|
157
|
+
allowed = coll[action].injecting({}) {|h, verb| h[verb] = "#{action}"}
|
158
|
+
r.add "#{opt[:prefix]}/#{res}[;/]+#{action}", :allowed => allowed
|
181
159
|
}
|
182
160
|
end
|
183
161
|
r.add "#{opt[:prefix]}/#{res}/:id\\.:format", :allowed => {:get => 'show', :put => 'update', :delete => 'destroy'}
|
@@ -189,13 +167,21 @@ module Merb
|
|
189
167
|
|
190
168
|
def self.generate_singleton_routes(res,opt)
|
191
169
|
with_options :controller => res.to_s, :rest => true do |r|
|
192
|
-
r.add "#{opt[:prefix]}/#{res}[
|
170
|
+
r.add "#{opt[:prefix]}/#{res}[;/]+edit", :allowed => {:get => 'edit'}
|
193
171
|
r.add "#{opt[:prefix]}/#{res}\\.:format", :allowed => {:get => 'show'}
|
194
172
|
r.add "#{opt[:prefix]}/#{res}/new" , :allowed => {:get => 'new'}
|
195
173
|
r.add "#{opt[:prefix]}/#{res}/?", :allowed => {:get => 'show', :post => 'create', :put => 'update', :delete => 'destroy'}
|
196
174
|
end
|
197
175
|
end
|
198
176
|
|
177
|
+
def self.default_routes
|
178
|
+
add "/:controller/:action/:id\\.:format"
|
179
|
+
add "/:controller/:action/:id"
|
180
|
+
add "/:controller/:action\\.:format"
|
181
|
+
add "/:controller/:action"
|
182
|
+
add "/:controller\\.:format", :action => 'index'
|
183
|
+
add "/:controller", :action => 'index'
|
184
|
+
end
|
199
185
|
end
|
200
186
|
|
201
187
|
end
|
@@ -11,12 +11,13 @@ module Merb
|
|
11
11
|
buf = ""
|
12
12
|
content_length = @env['CONTENT_LENGTH'].to_i
|
13
13
|
input = request
|
14
|
+
input.binmode if defined? input.binmode
|
14
15
|
boundary_size = boundary.size + EOL.size
|
15
16
|
bufsize = 16384
|
16
17
|
content_length -= boundary_size
|
17
18
|
status = input.read(boundary_size)
|
18
19
|
raise EOFError, "bad content body" unless status == boundary + EOL
|
19
|
-
rx = /(?:#{EOL})?#{Regexp.quote(boundary)}(#{EOL}|--)/
|
20
|
+
rx = /(?:#{EOL})?#{Regexp.quote(boundary,'n')}(#{EOL}|--)/
|
20
21
|
|
21
22
|
loop {
|
22
23
|
head = nil
|
@@ -32,7 +33,7 @@ module Merb
|
|
32
33
|
content_type = head[CONTENT_TYPE_REGEX, 1]
|
33
34
|
name = head[NAME_REGEX, 1]
|
34
35
|
|
35
|
-
if filename
|
36
|
+
if filename && !filename.empty?
|
36
37
|
body = Tempfile.new(:Merb)
|
37
38
|
body.binmode if defined? body.binmode
|
38
39
|
end
|
@@ -58,9 +59,14 @@ module Merb
|
|
58
59
|
content_length = -1 if $1 == "--"
|
59
60
|
end
|
60
61
|
|
61
|
-
if filename
|
62
|
+
if filename && !filename.empty?
|
62
63
|
body.rewind
|
63
|
-
data = {
|
64
|
+
data = {
|
65
|
+
:filename => File.basename(filename),
|
66
|
+
:content_type => content_type,
|
67
|
+
:tempfile => body,
|
68
|
+
:size => File.size(body)
|
69
|
+
}
|
64
70
|
else
|
65
71
|
data = body
|
66
72
|
end
|
@@ -90,7 +96,7 @@ module Merb
|
|
90
96
|
# request into the params hash. So for example:
|
91
97
|
# /foo?bar=nik&post[title]=heya&post[body]=whatever
|
92
98
|
# parses into:
|
93
|
-
# {:bar => 'nik', :post => {:title => 'heya', :body => 'whatever}}
|
99
|
+
# {:bar => 'nik', :post => {:title => 'heya', :body => 'whatever'}}
|
94
100
|
def query_parse(qs, d = '&;')
|
95
101
|
m = proc {|_,o,n|o.u(n,&m)rescue([*o]<<n)}
|
96
102
|
(qs||'').split(/[#{d}] */n).inject(MerbHash[]) { |h,p|
|
@@ -161,6 +167,29 @@ module Merb
|
|
161
167
|
return
|
162
168
|
end
|
163
169
|
|
170
|
+
# stream_file( { :filename => file_name,
|
171
|
+
# :type => content_type,
|
172
|
+
# :content_length => content_length }) do
|
173
|
+
# @response.send_status(opts[:content_length])
|
174
|
+
# @response.send_header
|
175
|
+
# AWS::S3::S3Object.stream(user.folder_name + "-" + user_file.unique_id, bucket_name) do |chunk|
|
176
|
+
# @response.write chunk
|
177
|
+
# end
|
178
|
+
# end
|
179
|
+
def stream_file(opts={}, &stream)
|
180
|
+
opts.update(Merb::Const::DEFAULT_SEND_FILE_OPTIONS.merge(opts))
|
181
|
+
disposition = opts[:disposition].dup || 'attachment'
|
182
|
+
disposition << %(; filename="#{opts[:filename]}")
|
183
|
+
@response.headers.update(
|
184
|
+
'Content-Type' => opts[:type].strip, # fixes a problem with extra '\r' with some browsers
|
185
|
+
'Content-Disposition' => disposition,
|
186
|
+
'Content-Transfer-Encoding' => 'binary',
|
187
|
+
'CONTENT-LENGTH' => opts[:content_length]
|
188
|
+
)
|
189
|
+
stream
|
190
|
+
end
|
191
|
+
|
192
|
+
|
164
193
|
# This uses nginx X-Accel-Redirect header to send
|
165
194
|
# a file directly from nginx. See the nginx wiki:
|
166
195
|
# http://wiki.codemongers.com/NginxXSendfile
|