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?