lib/nyara/controller.rb in nyara-0.0.1.pre.6 vs lib/nyara/controller.rb in nyara-0.0.1.pre.8
- old
+ new
@@ -91,17 +91,17 @@
# ---
# todo http method: trace ?
# +++
# Set default layout
- def layout l
+ def set_default_layout l
@default_layout = l
end
attr_reader :default_layout
# Set controller name, so you can use a shorter name to reference the controller in path helper
- def set_name n
+ def set_controller_name n
@controller_name = n
end
attr_reader :controller_name
def compile_route_entries scope # :nodoc:
@@ -202,10 +202,11 @@
# similar to send_header, but without content-type
Ext.request_send_data r, HTTP_STATUS_FIRST_LINES[r.status]
data = header.serialize
data.concat r.response_header_extra_lines
+ data << Session.encode_set_cookie(r.session, r.ssl?)
data << "\r\n"
Ext.request_send_data r, data.join
Fiber.yield :term_close
end
@@ -336,12 +337,12 @@
data << "\r\n"
Ext.request_send_data r, data.join
# forbid further modification
header.freeze
- session.freeze
- flash.next.freeze
+ r.session.freeze
+ r.flash.next.freeze
end
# Send raw data, that is, not wrapped in chunked encoding<br>
# NOTE: often you should call send_header before doing this.
def send_data data
@@ -357,37 +358,94 @@
send_header unless request.response_header.frozen?
Ext.request_send_chunk request, data.to_s
end
alias send_string send_chunk
- # Send file
- def send_file file
- if behind_proxy? # todo
- header['X-Sendfile'] = file # todo escape name?
- # todo content type and disposition
- header['Content-Type'] = determine_ct_by_file_name
+ # Set aproppriate headers and send the file<br>
+ # :call-seq:
+ #
+ # send_file '/home/www/no-virus-inside.exe', disposition: 'attachment'
+ #
+ # options are:
+ #
+ # [disposition] 'inline' by default, if set to 'attachment', the file is presented as a download item in browser.
+ # [x_send_file] if not false/nil, it is considered to be behind a web server.
+ # Then the app sends file with only header configures,
+ # which proxies the actual action to the web server,
+ # which can take the advantage of system calls and reduce transfered data,
+ # thus faster.
+ # [filename] name for the downloaded file, will use basename of +file+ if not set.
+ # [content_type] defaults to the MIME type matching +file+ or +filename+.
+ #
+ # To configure for lighttpd and apache2 mod_xsendfile (https://tn123.org/mod_xsendfile/):
+ #
+ # configure do
+ # set :x_send_file, 'X-Sendfile'
+ # end
+ #
+ # To configure for nginx (http://wiki.nginx.org/XSendfile):
+ #
+ # configure do
+ # set :x_send_file, 'X-Accel-Redirect'
+ # end
+ #
+ # To disable x_send_file while configured:
+ #
+ # send_file '/some/file', x_send_file: false
+ #
+ # To enable x_send_file while not configured:
+ #
+ # send_file '/some/file', x_send_file: 'X-Sendfile'
+ #
+ def send_file file, disposition: 'inline', x_send_file: Config['x_send_file'], filename: nil, content_type: nil
+ header = request.response_header
+
+ unless header['Content-Type']
+ unless content_type
+ extname = File.extname(file)
+ extname = File.extname(filename) if extname.blank? and filename
+ content_type = MIME_TYPES[extname] || 'application/octet-stream'
+ end
+ header['Content-Type'] = content_type
+ end
+
+ disposition = disposition.to_s
+ if disposition != 'inline'
+ if disposition != 'attachment'
+ raise ArgumentError, "disposition should be inline or attachment, but got #{disposition.inspect}"
+ end
+ end
+
+ filename ||= File.basename file
+ header['Content-Disposition'] = "#{disposition}; filename=#{Ext.escape filename, true}"
+
+ header['Transfer-Encoding'] = '' # delete it
+
+ if x_send_file
+ header[x_send_file] = file # todo escape name?
send_header unless request.response_header.frozen?
else
+ # todo nonblock read file?
data = File.binread file
- header['Content-Type'] = determine_ct_by_file_name
+ header['Content-Length'] = data.bytesize
send_header unless request.response_header.frozen?
send_data data
end
- Fiber.yield :term_close # is it right? content type changed
+ Fiber.yield :term_close
end
# Resume action after +seconds+
def sleep seconds
seconds = seconds.to_f
raise ArgumentError, 'bad sleep seconds' if seconds < 0
# NOTE request_wake requires request as param, so this method can not be generalized to Fiber.sleep
- Ext.request_sleep self # place sleep actions before wake
+ Ext.request_sleep request # place sleep actions before wake
Thread.new do
- sleep seconds
- Ext.request_wakeup self
+ Kernel.sleep seconds
+ Ext.request_wakeup request
end
Fiber.yield :sleep # see event.c for the handler
end
# One shot render, and terminate the action.
@@ -397,9 +455,12 @@
# # render a template, engine determined by extension
# render 'user/index', locals: {}
#
# # with template source, set content type to +text/html+ if not given
# render erb: "<%= 1 + 1 %>"
+ #
+ # # layout can be string or array
+ # render 'index', ['inner_layout', 'outer_layout']
#
# For steam rendering, see #stream
def render view_path=nil, layout: self.class.default_layout, locals: nil, **opts
view = View.new self, view_path, layout, locals, opts
unless request.response_header.frozen?