From 53d3bafc8bf3f0cb90a02751aa93b0c30b26ab78 Mon Sep 17 00:00:00 2001 From: Evan Petrie Date: Thu, 14 Apr 2011 12:08:18 -0700 Subject: ruby 1.9.2 and other ruby implementations may not return the same hash value for the same string each time. This can result in your static assets being served from different asset hosts, which makes browser caching less effective. Use md5 or some other digest method instead. --- actionpack/lib/action_view/helpers/asset_tag_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb index f6b2d4f3f4..c383e4990c 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb @@ -57,7 +57,7 @@ module ActionView # +asset_host+ to a proc like this: # # ActionController::Base.asset_host = Proc.new { |source| - # "http://assets#{source.hash % 2 + 1}.example.com" + # "http://assets#{Digest::MD5.hexdigest(source).to_i(16) % 2 + 1}.example.com" # } # image_tag("rails.png") # # => Rails -- cgit v1.2.3 From 2adeaa9c90b7559387b55e7a24a7eb82671c88cc Mon Sep 17 00:00:00 2001 From: Joshua Ballanco Date: Thu, 14 Apr 2011 23:25:18 -0400 Subject: Fix for stripping tags from frozen strings. This returns behavior under Ruby 1.9 to match Ruby 1.8. --- actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb b/actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb index 09dd08898c..91a97c02ff 100644 --- a/actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +++ b/actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb @@ -33,7 +33,7 @@ module HTML result = super # strip any comments, and if they have a newline at the end (ie. line with # only a comment) strip that too - result.gsub!(/[\n]?/m, "") if result + result = result.gsub(/[\n]?/m, "") if (result && result =~ /[\n]?/m) # Recurse - handle all dirty nested tags result == text ? result : sanitize(result, options) end -- cgit v1.2.3 From 3ca6d0e8fe72f6840049f7c2461243cf362b9896 Mon Sep 17 00:00:00 2001 From: Matt Duncan Date: Fri, 15 Apr 2011 19:56:48 -0400 Subject: Including actual usage in example --- actionpack/lib/action_view/helpers/date_helper.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb index 6cd1565031..72ee31a246 100644 --- a/actionpack/lib/action_view/helpers/date_helper.rb +++ b/actionpack/lib/action_view/helpers/date_helper.rb @@ -115,7 +115,9 @@ module ActionView # time_ago_in_words(Time.now - 15.hours) # => 15 hours # time_ago_in_words(Time.now) # => less than a minute # - # from_time = Time.now - 3.days - 14.minutes - 25.seconds # => 3 days + # from_time = Time.now - 3.days - 14.minutes - 25.seconds + # time_ago_in_words(from_time) # => 3 days + # def time_ago_in_words(from_time, include_seconds = false) distance_of_time_in_words(from_time, Time.now, include_seconds) end -- cgit v1.2.3 From 8ac365f47626fd4d9569cf9c378c3a8f4f60eb58 Mon Sep 17 00:00:00 2001 From: Matt Duncan Date: Fri, 15 Apr 2011 20:03:52 -0400 Subject: Making example result match actual result --- actionpack/lib/action_view/helpers/date_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb index 72ee31a246..4c65ebc1f3 100644 --- a/actionpack/lib/action_view/helpers/date_helper.rb +++ b/actionpack/lib/action_view/helpers/date_helper.rb @@ -112,7 +112,7 @@ module ActionView # # ==== Examples # time_ago_in_words(3.minutes.from_now) # => 3 minutes - # time_ago_in_words(Time.now - 15.hours) # => 15 hours + # time_ago_in_words(Time.now - 15.hours) # => about 15 hours # time_ago_in_words(Time.now) # => less than a minute # # from_time = Time.now - 3.days - 14.minutes - 25.seconds -- cgit v1.2.3 From 6ddd4a3d954cd81f96c747f515406be50041431a Mon Sep 17 00:00:00 2001 From: Matt Duncan Date: Fri, 15 Apr 2011 20:08:11 -0400 Subject: Days are never approximated using 'about' --- actionpack/lib/action_view/helpers/date_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb index 4c65ebc1f3..3c0c7c319c 100644 --- a/actionpack/lib/action_view/helpers/date_helper.rb +++ b/actionpack/lib/action_view/helpers/date_helper.rb @@ -51,7 +51,7 @@ module ActionView # distance_of_time_in_words(from_time, from_time + 15.seconds) # => less than a minute # distance_of_time_in_words(from_time, from_time + 15.seconds, true) # => less than 20 seconds # distance_of_time_in_words(from_time, 3.years.from_now) # => about 3 years - # distance_of_time_in_words(from_time, from_time + 60.hours) # => about 3 days + # distance_of_time_in_words(from_time, from_time + 60.hours) # => 3 days # distance_of_time_in_words(from_time, from_time + 45.seconds, true) # => less than a minute # distance_of_time_in_words(from_time, from_time - 45.seconds, true) # => less than a minute # distance_of_time_in_words(from_time, 76.seconds.from_now) # => 1 minute -- cgit v1.2.3 From 7f98b544e3b29c7ffdb8cb4a2ad1da5d1a8c611c Mon Sep 17 00:00:00 2001 From: Matt Duncan Date: Fri, 15 Apr 2011 20:18:30 -0400 Subject: Negative format example should use a negative number --- actionpack/lib/action_view/helpers/number_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_view/helpers/number_helper.rb b/actionpack/lib/action_view/helpers/number_helper.rb index 05a9c5b4f1..68ebedd328 100644 --- a/actionpack/lib/action_view/helpers/number_helper.rb +++ b/actionpack/lib/action_view/helpers/number_helper.rb @@ -100,7 +100,7 @@ module ActionView # number_to_currency(1234567890.506, :precision => 3) # => $1,234,567,890.506 # number_to_currency(1234567890.506, :locale => :fr) # => 1 234 567 890,506 € # - # number_to_currency(1234567890.50, :negative_format => "(%u%n)") + # number_to_currency(-1234567890.50, :negative_format => "(%u%n)") # # => ($1,234,567,890.51) # number_to_currency(1234567890.50, :unit => "£", :separator => ",", :delimiter => "") # # => £1234567890,50 -- cgit v1.2.3 From 004042c0d94f0294e691de46094a665acd852653 Mon Sep 17 00:00:00 2001 From: Matt Duncan Date: Fri, 15 Apr 2011 20:27:05 -0400 Subject: Fixing missing colon on symbol in example --- actionpack/lib/action_view/helpers/number_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_view/helpers/number_helper.rb b/actionpack/lib/action_view/helpers/number_helper.rb index 68ebedd328..a150019e5c 100644 --- a/actionpack/lib/action_view/helpers/number_helper.rb +++ b/actionpack/lib/action_view/helpers/number_helper.rb @@ -238,7 +238,7 @@ module ActionView # number_with_precision(111.2345, :precision => 1, :significant => true) # => 100 # number_with_precision(13, :precision => 5, :significant => true) # => 13.000 # number_with_precision(111.234, :locale => :fr) # => 111,234 - # number_with_precision(13, :precision => 5, :significant => true, strip_insignificant_zeros => true) + # number_with_precision(13, :precision => 5, :significant => true, :strip_insignificant_zeros => true) # # => 13 # number_with_precision(389.32314, :precision => 4, :significant => true) # => 389.3 # number_with_precision(1111.2345, :precision => 2, :separator => ',', :delimiter => '.') -- cgit v1.2.3 From e8afe4e1ea9ea4c59368bb84efde785876f25061 Mon Sep 17 00:00:00 2001 From: Matt Duncan Date: Fri, 15 Apr 2011 20:30:51 -0400 Subject: Making spacing consistent --- actionpack/lib/action_view/helpers/number_helper.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_view/helpers/number_helper.rb b/actionpack/lib/action_view/helpers/number_helper.rb index a150019e5c..b545031fcc 100644 --- a/actionpack/lib/action_view/helpers/number_helper.rb +++ b/actionpack/lib/action_view/helpers/number_helper.rb @@ -318,7 +318,7 @@ module ActionView # Non-significant zeros after the fractional separator are stripped out by default (set # :strip_insignificant_zeros to +false+ to change that): # number_to_human_size(1234567890123, :precision => 5) # => "1.1229 TB" - # number_to_human_size(524288000, :precision=>5) # => "500 MB" + # number_to_human_size(524288000, :precision => 5) # => "500 MB" def number_to_human_size(number, options = {}) options.symbolize_keys! @@ -407,7 +407,7 @@ module ActionView # Unsignificant zeros after the decimal separator are stripped out by default (set # :strip_insignificant_zeros to +false+ to change that): # number_to_human(12345012345, :significant_digits => 6) # => "12.345 Billion" - # number_to_human(500000000, :precision=>5) # => "500 Million" + # number_to_human(500000000, :precision => 5) # => "500 Million" # # ==== Custom Unit Quantifiers # -- cgit v1.2.3 From f274394afbedd2097d5855f8c2d7d384b46977c3 Mon Sep 17 00:00:00 2001 From: Matt Duncan Date: Fri, 15 Apr 2011 21:34:49 -0400 Subject: Fixing more spacing inconsistencies --- actionpack/lib/action_view/helpers/date_helper.rb | 2 +- actionpack/lib/action_view/helpers/url_helper.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb index 3c0c7c319c..6dbd2e3e43 100644 --- a/actionpack/lib/action_view/helpers/date_helper.rb +++ b/actionpack/lib/action_view/helpers/date_helper.rb @@ -466,7 +466,7 @@ module ActionView # # # Generates a select field for hours with a custom prompt. Use :prompt => true for a # # generic prompt. - # select_hour(13, :prompt =>'Choose hour') + # select_hour(13, :prompt => 'Choose hour') # def select_hour(datetime, options = {}, html_options = {}) DateTimeSelector.new(datetime, options, html_options).select_hour diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb index de75488e72..051d3eb049 100644 --- a/actionpack/lib/action_view/helpers/url_helper.rb +++ b/actionpack/lib/action_view/helpers/url_helper.rb @@ -555,10 +555,10 @@ module ActionView # current_page?(:controller => 'shop', :action => 'checkout') # # => true # - # current_page?(:controller => 'shop', :action => 'checkout', :order => 'desc', :page=>'1') + # current_page?(:controller => 'shop', :action => 'checkout', :order => 'desc', :page => '1') # # => true # - # current_page?(:controller => 'shop', :action => 'checkout', :order => 'desc', :page=>'2') + # current_page?(:controller => 'shop', :action => 'checkout', :order => 'desc', :page => '2') # # => false # # current_page?(:controller => 'shop', :action => 'checkout', :order => 'desc') -- cgit v1.2.3 From b6bfcc9115df00620e155a84f80d46342c1fe7ee Mon Sep 17 00:00:00 2001 From: Matt Duncan Date: Fri, 15 Apr 2011 21:50:29 -0400 Subject: Fix syntax error in example --- actionpack/lib/action_view/helpers/text_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb index bdda1df437..d1c505d2b5 100644 --- a/actionpack/lib/action_view/helpers/text_helper.rb +++ b/actionpack/lib/action_view/helpers/text_helper.rb @@ -19,7 +19,7 @@ module ActionView # simple_format('Example') # # => "

Example

" # - # simple_format('Example') + # simple_format('Example') # # => "

Example

" # # If you want to escape all content, you should invoke the +h+ method before -- cgit v1.2.3 From 1229904602b8c3b354a144ce33468be408cf9cec Mon Sep 17 00:00:00 2001 From: Matt Duncan Date: Fri, 15 Apr 2011 21:56:01 -0400 Subject: Removing incorrect example results --- actionpack/lib/action_view/helpers/text_helper.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb index d1c505d2b5..06e2b027da 100644 --- a/actionpack/lib/action_view/helpers/text_helper.rb +++ b/actionpack/lib/action_view/helpers/text_helper.rb @@ -295,11 +295,11 @@ module ActionView # +link+ as its optional second parameter and the +html_options+ hash # as its optional third parameter: # post_body = "Welcome to my new blog at http://www.myblog.com/. Please e-mail me at me@email.com." - # auto_link(post_body, :urls) # => Once upon\na time + # auto_link(post_body, :urls) # # => "Welcome to my new blog at http://www.myblog.com. # Please e-mail me at me@email.com." # - # auto_link(post_body, :all, :target => "_blank") # => Once upon\na time + # auto_link(post_body, :all, :target => "_blank") # # => "Welcome to my new blog at http://www.myblog.com. # Please e-mail me at me@email.com." def auto_link(text, *args, &block)#link = :all, html = {}, &block) -- cgit v1.2.3 From 50b2eb8cbd6cb923026b8fbb98482e06cdbd21ce Mon Sep 17 00:00:00 2001 From: Matt Duncan Date: Fri, 15 Apr 2011 22:09:23 -0400 Subject: Fixing another example result --- actionpack/lib/action_view/helpers/sanitize_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_view/helpers/sanitize_helper.rb b/actionpack/lib/action_view/helpers/sanitize_helper.rb index 0fee34f8a4..841be0a567 100644 --- a/actionpack/lib/action_view/helpers/sanitize_helper.rb +++ b/actionpack/lib/action_view/helpers/sanitize_helper.rb @@ -94,7 +94,7 @@ module ActionView # # => Please e-mail me at me@email.com. # # strip_links('Blog: Visit.') - # # => Blog: Visit + # # => Blog: Visit. def strip_links(html) self.class.link_sanitizer.sanitize(html) end -- cgit v1.2.3 From 3dc4d543f177e57cb08ac8fd16a5fff6f635822f Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 15 Apr 2011 14:34:14 -0700 Subject: make our constructor signature match the superclass --- actionpack/lib/action_controller/metal.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/metal.rb b/actionpack/lib/action_controller/metal.rb index 585bd5e5ab..0133b2ecbc 100644 --- a/actionpack/lib/action_controller/metal.rb +++ b/actionpack/lib/action_controller/metal.rb @@ -131,7 +131,7 @@ module ActionController attr_internal :headers, :response, :request delegate :session, :to => "@_request" - def initialize(*) + def initialize @_headers = {"Content-Type" => "text/html"} @_status = 200 @_request = nil -- cgit v1.2.3 From 4f044528c0373b0a581a12b1311a7a544ded6c58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Mon, 18 Apr 2011 08:12:51 +0200 Subject: Slightly reorganize rendering stack. --- actionpack/lib/abstract_controller/rendering.rb | 31 +++++++++++++++++----- .../lib/action_controller/metal/renderers.rb | 6 ++--- 2 files changed, 28 insertions(+), 9 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/abstract_controller/rendering.rb b/actionpack/lib/abstract_controller/rendering.rb index 66f6d0eebb..d0dd730b06 100644 --- a/actionpack/lib/abstract_controller/rendering.rb +++ b/actionpack/lib/abstract_controller/rendering.rb @@ -105,16 +105,22 @@ module AbstractController # Normalize arguments, options and then delegates render_to_body and # sticks the result in self.response_body. def render(*args, &block) - self.response_body = render_to_string(*args, &block) + options = _normalize_render(*args, &block) + self.response_body = render_to_body(options) end # Raw rendering of a template to a string. Just convert the results of - # render_to_body into a String. + # render_response into a String. # :api: plugin def render_to_string(*args, &block) - options = _normalize_args(*args, &block) - _normalize_options(options) - render_to_body(options).tap { self.response_body = nil } + options = _normalize_render(*args, &block) + if self.response_body = render_to_body(options) + string = "" + response_body.each { |r| string << r } + string + end + ensure + self.response_body = nil end # Raw rendering of a template to a Rack-compatible body. @@ -151,8 +157,17 @@ module AbstractController hash end - # Normalize options by converting render "foo" to render :action => "foo" and + # Normalize args and options. + # :api: private + def _normalize_render(*args, &block) + options = _normalize_args(*args, &block) + _normalize_options(options) + options + end + + # Normalize args by converting render "foo" to render :action => "foo" and # render "foo/bar" to render :file => "foo/bar". + # :api: plugin def _normalize_args(action=nil, options={}) case action when NilClass @@ -169,6 +184,8 @@ module AbstractController options end + # Normalize options. + # :api: plugin def _normalize_options(options) if options[:partial] == true options[:partial] = action_name @@ -182,6 +199,8 @@ module AbstractController options end + # Process extra options. + # :api: plugin def _process_options(options) end end diff --git a/actionpack/lib/action_controller/metal/renderers.rb b/actionpack/lib/action_controller/metal/renderers.rb index dfda6618e7..0ad9dbeda9 100644 --- a/actionpack/lib/action_controller/metal/renderers.rb +++ b/actionpack/lib/action_controller/metal/renderers.rb @@ -95,17 +95,17 @@ module ActionController json = json.to_json(options) unless json.kind_of?(String) json = "#{options[:callback]}(#{json})" unless options[:callback].blank? self.content_type ||= Mime::JSON - self.response_body = json + json end add :js do |js, options| self.content_type ||= Mime::JS - self.response_body = js.respond_to?(:to_js) ? js.to_js(options) : js + js.respond_to?(:to_js) ? js.to_js(options) : js end add :xml do |xml, options| self.content_type ||= Mime::XML - self.response_body = xml.respond_to?(:to_xml) ? xml.to_xml(options) : xml + xml.respond_to?(:to_xml) ? xml.to_xml(options) : xml end end end -- cgit v1.2.3 From 944b4d57960569d5c9a08783044677721a6de4df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Mon, 18 Apr 2011 08:13:28 +0200 Subject: Add missing dependency. --- actionpack/lib/action_controller/metal/mime_responds.rb | 2 ++ 1 file changed, 2 insertions(+) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/metal/mime_responds.rb b/actionpack/lib/action_controller/metal/mime_responds.rb index 16d48e4677..f10287afb4 100644 --- a/actionpack/lib/action_controller/metal/mime_responds.rb +++ b/actionpack/lib/action_controller/metal/mime_responds.rb @@ -6,6 +6,8 @@ module ActionController #:nodoc: module MimeResponds extend ActiveSupport::Concern + include ActionController::ImplicitRender + included do class_attribute :responder, :mimes_for_respond_to self.responder = ActionController::Responder -- cgit v1.2.3 From 7a152ab0127877eea6f2cef8ff6d1975a3fc16d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Mon, 18 Apr 2011 08:17:47 +0200 Subject: Rename it to DataStreaming. --- actionpack/lib/action_controller.rb | 2 +- actionpack/lib/action_controller/base.rb | 2 +- .../lib/action_controller/metal/data_streaming.rb | 145 +++++++++++++++++++++ .../lib/action_controller/metal/streaming.rb | 145 --------------------- 4 files changed, 147 insertions(+), 147 deletions(-) create mode 100644 actionpack/lib/action_controller/metal/data_streaming.rb delete mode 100644 actionpack/lib/action_controller/metal/streaming.rb (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index 62cc18b253..26e6ac770f 100644 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -13,6 +13,7 @@ module ActionController autoload :Compatibility autoload :ConditionalGet autoload :Cookies + autoload :DataStreaming autoload :Flash autoload :ForceSSL autoload :Head @@ -30,7 +31,6 @@ module ActionController autoload :Rescue autoload :Responder autoload :SessionManagement - autoload :Streaming autoload :Testing autoload :UrlFor end diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index 5f9e082cd3..53e0a4b9d1 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -199,7 +199,7 @@ module ActionController Flash, RequestForgeryProtection, ForceSSL, - Streaming, + DataStreaming, RecordIdentifier, HttpAuthentication::Basic::ControllerMethods, HttpAuthentication::Digest::ControllerMethods, diff --git a/actionpack/lib/action_controller/metal/data_streaming.rb b/actionpack/lib/action_controller/metal/data_streaming.rb new file mode 100644 index 0000000000..997bc6e958 --- /dev/null +++ b/actionpack/lib/action_controller/metal/data_streaming.rb @@ -0,0 +1,145 @@ +require 'active_support/core_ext/file/path' + +module ActionController #:nodoc: + # Methods for sending arbitrary data and for streaming files to the browser, + # instead of rendering. + module DataStreaming + extend ActiveSupport::Concern + + include ActionController::Rendering + + DEFAULT_SEND_FILE_OPTIONS = { + :type => 'application/octet-stream'.freeze, + :disposition => 'attachment'.freeze, + }.freeze + + protected + # Sends the file. This uses a server-appropriate method (such as X-Sendfile) + # via the Rack::Sendfile middleware. The header to use is set via + # config.action_dispatch.x_sendfile_header, and defaults to "X-Sendfile". + # Your server can also configure this for you by setting the X-Sendfile-Type header. + # + # Be careful to sanitize the path parameter if it is coming from a web + # page. send_file(params[:path]) allows a malicious user to + # download any file on your server. + # + # Options: + # * :filename - suggests a filename for the browser to use. + # Defaults to File.basename(path). + # * :type - specifies an HTTP content type. Defaults to 'application/octet-stream'. You can specify + # either a string or a symbol for a registered type register with Mime::Type.register, for example :json + # * :disposition - specifies whether the file will be shown inline or downloaded. + # Valid values are 'inline' and 'attachment' (default). + # * :status - specifies the status code to send with the response. Defaults to '200 OK'. + # * :url_based_filename - set to +true+ if you want the browser guess the filename from + # the URL, which is necessary for i18n filenames on certain browsers + # (setting :filename overrides this option). + # + # The default Content-Type and Content-Disposition headers are + # set to download arbitrary binary files in as many browsers as + # possible. IE versions 4, 5, 5.5, and 6 are all known to have + # a variety of quirks (especially when downloading over SSL). + # + # Simple download: + # + # send_file '/path/to.zip' + # + # Show a JPEG in the browser: + # + # send_file '/path/to.jpeg', :type => 'image/jpeg', :disposition => 'inline' + # + # Show a 404 page in the browser: + # + # send_file '/path/to/404.html', :type => 'text/html; charset=utf-8', :status => 404 + # + # Read about the other Content-* HTTP headers if you'd like to + # provide the user with more information (such as Content-Description) in + # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.11. + # + # Also be aware that the document may be cached by proxies and browsers. + # The Pragma and Cache-Control headers declare how the file may be cached + # by intermediaries. They default to require clients to validate with + # the server before releasing cached responses. See + # http://www.mnot.net/cache_docs/ for an overview of web caching and + # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9 + # for the Cache-Control header spec. + def send_file(path, options = {}) #:doc: + raise MissingFile, "Cannot read file #{path}" unless File.file?(path) and File.readable?(path) + + options[:filename] ||= File.basename(path) unless options[:url_based_filename] + send_file_headers! options + + self.status = options[:status] || 200 + self.content_type = options[:content_type] if options.key?(:content_type) + self.response_body = File.open(path, "rb") + end + + # Sends the given binary data to the browser. This method is similar to + # render :text => data, but also allows you to specify whether + # the browser should display the response as a file attachment (i.e. in a + # download dialog) or as inline data. You may also set the content type, + # the apparent file name, and other things. + # + # Options: + # * :filename - suggests a filename for the browser to use. + # * :type - specifies an HTTP content type. Defaults to 'application/octet-stream'. You can specify + # either a string or a symbol for a registered type register with Mime::Type.register, for example :json + # * :disposition - specifies whether the file will be shown inline or downloaded. + # Valid values are 'inline' and 'attachment' (default). + # * :status - specifies the status code to send with the response. Defaults to '200 OK'. + # + # Generic data download: + # + # send_data buffer + # + # Download a dynamically-generated tarball: + # + # send_data generate_tgz('dir'), :filename => 'dir.tgz' + # + # Display an image Active Record in the browser: + # + # send_data image.data, :type => image.content_type, :disposition => 'inline' + # + # See +send_file+ for more information on HTTP Content-* headers and caching. + def send_data(data, options = {}) #:doc: + send_file_headers! options.dup + render options.slice(:status, :content_type).merge(:text => data) + end + + private + def send_file_headers!(options) + options.update(DEFAULT_SEND_FILE_OPTIONS.merge(options)) + [:type, :disposition].each do |arg| + raise ArgumentError, ":#{arg} option required" if options[arg].nil? + end + + disposition = options[:disposition] + disposition += %(; filename="#{options[:filename]}") if options[:filename] + + content_type = options[:type] + + if content_type.is_a?(Symbol) + extension = Mime[content_type] + raise ArgumentError, "Unknown MIME type #{options[:type]}" unless extension + self.content_type = extension + else + self.content_type = content_type + end + + headers.merge!( + 'Content-Disposition' => disposition, + 'Content-Transfer-Encoding' => 'binary' + ) + + response.sending_file = true + + # Fix a problem with IE 6.0 on opening downloaded files: + # If Cache-Control: no-cache is set (which Rails does by default), + # IE removes the file it just downloaded from its cache immediately + # after it displays the "open/save" dialog, which means that if you + # hit "open" the file isn't there anymore when the application that + # is called for handling the download is run, so let's workaround that + response.cache_control[:public] ||= false + end + end +end diff --git a/actionpack/lib/action_controller/metal/streaming.rb b/actionpack/lib/action_controller/metal/streaming.rb deleted file mode 100644 index 312dc8eb3e..0000000000 --- a/actionpack/lib/action_controller/metal/streaming.rb +++ /dev/null @@ -1,145 +0,0 @@ -require 'active_support/core_ext/file/path' - -module ActionController #:nodoc: - # Methods for sending arbitrary data and for streaming files to the browser, - # instead of rendering. - module Streaming - extend ActiveSupport::Concern - - include ActionController::Rendering - - DEFAULT_SEND_FILE_OPTIONS = { - :type => 'application/octet-stream'.freeze, - :disposition => 'attachment'.freeze, - }.freeze - - protected - # Sends the file. This uses a server-appropriate method (such as X-Sendfile) - # via the Rack::Sendfile middleware. The header to use is set via - # config.action_dispatch.x_sendfile_header, and defaults to "X-Sendfile". - # Your server can also configure this for you by setting the X-Sendfile-Type header. - # - # Be careful to sanitize the path parameter if it is coming from a web - # page. send_file(params[:path]) allows a malicious user to - # download any file on your server. - # - # Options: - # * :filename - suggests a filename for the browser to use. - # Defaults to File.basename(path). - # * :type - specifies an HTTP content type. Defaults to 'application/octet-stream'. You can specify - # either a string or a symbol for a registered type register with Mime::Type.register, for example :json - # * :disposition - specifies whether the file will be shown inline or downloaded. - # Valid values are 'inline' and 'attachment' (default). - # * :status - specifies the status code to send with the response. Defaults to '200 OK'. - # * :url_based_filename - set to +true+ if you want the browser guess the filename from - # the URL, which is necessary for i18n filenames on certain browsers - # (setting :filename overrides this option). - # - # The default Content-Type and Content-Disposition headers are - # set to download arbitrary binary files in as many browsers as - # possible. IE versions 4, 5, 5.5, and 6 are all known to have - # a variety of quirks (especially when downloading over SSL). - # - # Simple download: - # - # send_file '/path/to.zip' - # - # Show a JPEG in the browser: - # - # send_file '/path/to.jpeg', :type => 'image/jpeg', :disposition => 'inline' - # - # Show a 404 page in the browser: - # - # send_file '/path/to/404.html', :type => 'text/html; charset=utf-8', :status => 404 - # - # Read about the other Content-* HTTP headers if you'd like to - # provide the user with more information (such as Content-Description) in - # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.11. - # - # Also be aware that the document may be cached by proxies and browsers. - # The Pragma and Cache-Control headers declare how the file may be cached - # by intermediaries. They default to require clients to validate with - # the server before releasing cached responses. See - # http://www.mnot.net/cache_docs/ for an overview of web caching and - # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9 - # for the Cache-Control header spec. - def send_file(path, options = {}) #:doc: - raise MissingFile, "Cannot read file #{path}" unless File.file?(path) and File.readable?(path) - - options[:filename] ||= File.basename(path) unless options[:url_based_filename] - send_file_headers! options - - self.status = options[:status] || 200 - self.content_type = options[:content_type] if options.key?(:content_type) - self.response_body = File.open(path, "rb") - end - - # Sends the given binary data to the browser. This method is similar to - # render :text => data, but also allows you to specify whether - # the browser should display the response as a file attachment (i.e. in a - # download dialog) or as inline data. You may also set the content type, - # the apparent file name, and other things. - # - # Options: - # * :filename - suggests a filename for the browser to use. - # * :type - specifies an HTTP content type. Defaults to 'application/octet-stream'. You can specify - # either a string or a symbol for a registered type register with Mime::Type.register, for example :json - # * :disposition - specifies whether the file will be shown inline or downloaded. - # Valid values are 'inline' and 'attachment' (default). - # * :status - specifies the status code to send with the response. Defaults to '200 OK'. - # - # Generic data download: - # - # send_data buffer - # - # Download a dynamically-generated tarball: - # - # send_data generate_tgz('dir'), :filename => 'dir.tgz' - # - # Display an image Active Record in the browser: - # - # send_data image.data, :type => image.content_type, :disposition => 'inline' - # - # See +send_file+ for more information on HTTP Content-* headers and caching. - def send_data(data, options = {}) #:doc: - send_file_headers! options.dup - render options.slice(:status, :content_type).merge(:text => data) - end - - private - def send_file_headers!(options) - options.update(DEFAULT_SEND_FILE_OPTIONS.merge(options)) - [:type, :disposition].each do |arg| - raise ArgumentError, ":#{arg} option required" if options[arg].nil? - end - - disposition = options[:disposition] - disposition += %(; filename="#{options[:filename]}") if options[:filename] - - content_type = options[:type] - - if content_type.is_a?(Symbol) - extension = Mime[content_type] - raise ArgumentError, "Unknown MIME type #{options[:type]}" unless extension - self.content_type = extension - else - self.content_type = content_type - end - - headers.merge!( - 'Content-Disposition' => disposition, - 'Content-Transfer-Encoding' => 'binary' - ) - - response.sending_file = true - - # Fix a problem with IE 6.0 on opening downloaded files: - # If Cache-Control: no-cache is set (which Rails does by default), - # IE removes the file it just downloaded from its cache immediately - # after it displays the "open/save" dialog, which means that if you - # hit "open" the file isn't there anymore when the application that - # is called for handling the download is run, so let's workaround that - response.cache_control[:public] ||= false - end - end -end -- cgit v1.2.3 From 389d15ef139e50696b274f2d61dd309ba2632877 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Mon, 18 Apr 2011 08:52:29 +0200 Subject: Body... wanna *stream* my body? Body... such a thrill my body! Added stream as class level method to make it explicit when to stream. Render also accepts :stream as option. --- actionpack/lib/action_controller.rb | 1 + actionpack/lib/action_controller/base.rb | 1 + .../lib/action_controller/metal/streaming.rb | 57 ++++++++++++++++++++++ actionpack/lib/action_view/context.rb | 1 + 4 files changed, 60 insertions(+) create mode 100644 actionpack/lib/action_controller/metal/streaming.rb (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index 26e6ac770f..aab2b9dc25 100644 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -31,6 +31,7 @@ module ActionController autoload :Rescue autoload :Responder autoload :SessionManagement + autoload :Streaming autoload :Testing autoload :UrlFor end diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index 53e0a4b9d1..ca0dccf575 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -199,6 +199,7 @@ module ActionController Flash, RequestForgeryProtection, ForceSSL, + Streaming, DataStreaming, RecordIdentifier, HttpAuthentication::Basic::ControllerMethods, diff --git a/actionpack/lib/action_controller/metal/streaming.rb b/actionpack/lib/action_controller/metal/streaming.rb new file mode 100644 index 0000000000..adb3e94134 --- /dev/null +++ b/actionpack/lib/action_controller/metal/streaming.rb @@ -0,0 +1,57 @@ +require 'active_support/core_ext/file/path' +require 'rack/chunked' + +module ActionController #:nodoc: + # Methods for sending streaming templates back to the client. + module Streaming + extend ActiveSupport::Concern + + include AbstractController::Rendering + attr_internal :stream + + module ClassMethods + # Render streaming templates. It accepts :only, :except, :if and :unless as options + # to specify when to stream, as in ActionController filters. + def stream(options={}) + if defined?(Fiber) + before_filter :_stream_filter, options + else + raise "You cannot use streaming if Fiber is not available." + end + end + end + + protected + + # Mark following render calls as streaming. + def _stream_filter #:nodoc: + self.stream = true + end + + # Consider the stream option when normalazing options. + def _normalize_options(options) #:nodoc: + super + options[:stream] = self.stream unless options.key?(:stream) + end + + # Set proper cache control and transfer encoding when streaming + def _process_options(options) #:nodoc: + super + if options[:stream] + headers["Cache-Control"] ||= "no-cache" + headers["Transfer-Encoding"] = "chunked" + headers.delete("Content-Length") + end + end + + # Call render_to_body if we are streaming instead of usual +render+. + def _render_template(options) #:nodoc: + if options.delete(:stream) + Rack::Chunked::Body.new view_context.render_body(options) + else + super + end + end + end +end + \ No newline at end of file diff --git a/actionpack/lib/action_view/context.rb b/actionpack/lib/action_view/context.rb index 39d88333e8..a2a64de206 100644 --- a/actionpack/lib/action_view/context.rb +++ b/actionpack/lib/action_view/context.rb @@ -2,6 +2,7 @@ module ActionView module CompiledTemplates #:nodoc: # holds compiled template code end + # = Action View Context # # Action View contexts are supplied to Action Controller to render template. -- cgit v1.2.3 From 6380f1a9f45e68f38480c0805cac62eb6708f72e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Tue, 19 Apr 2011 10:34:17 +0200 Subject: Be sure to not store the closed flash in the session. --- actionpack/lib/action_dispatch/middleware/flash.rb | 41 +++++++++++++--------- 1 file changed, 25 insertions(+), 16 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_dispatch/middleware/flash.rb b/actionpack/lib/action_dispatch/middleware/flash.rb index 027ff7f8ac..414405cc9e 100644 --- a/actionpack/lib/action_dispatch/middleware/flash.rb +++ b/actionpack/lib/action_dispatch/middleware/flash.rb @@ -4,7 +4,7 @@ module ActionDispatch # read a notice you put there or flash["notice"] = "hello" # to put a new one. def flash - @env['action_dispatch.request.flash_hash'] ||= (session["flash"] || Flash::FlashHash.new) + @env[Flash::KEY] ||= (session["flash"] || Flash::FlashHash.new) end end @@ -40,18 +40,14 @@ module ActionDispatch # # See docs on the FlashHash class for more details about the flash. class Flash + KEY = 'action_dispatch.request.flash_hash'.freeze + class FlashNow #:nodoc: def initialize(flash) @flash = flash - @closed = false end - attr_reader :closed - alias :closed? :closed - def close!; @closed = true end - def []=(k, v) - raise ClosedError, :flash if closed? @flash[k] = v @flash.discard(k) v @@ -70,6 +66,10 @@ module ActionDispatch def notice=(message) self[:notice] = message end + + def close!(new_flash) + @flash = new_flash + end end class FlashHash @@ -81,10 +81,6 @@ module ActionDispatch @flashes = {} end - attr_reader :closed - alias :closed? :closed - def close!; @closed = true end - def []=(k, v) #:nodoc: raise ClosedError, :flash if closed? keep(k) @@ -152,6 +148,14 @@ module ActionDispatch @now ||= FlashNow.new(self) end + attr_reader :closed + alias :closed? :closed + + def close! + @closed = true + @now.close!(self) if @now + end + # Keeps either the entire current flash or a specific flash entry available for the next action: # # flash.keep # keeps the entire flash @@ -231,13 +235,18 @@ module ActionDispatch @app.call(env) ensure session = env['rack.session'] || {} - flash_hash = env['action_dispatch.request.flash_hash'] + flash_hash = env[KEY] if flash_hash - if !flash_hash.empty? || session.key?('flash') - session["flash"] = flash_hash - end - flash_hash.close! + if !flash_hash.empty? || session.key?('flash') + session["flash"] = flash_hash + new_hash = flash_hash.dup + else + new_hash = flash_hash + end + + env[KEY] = new_hash + new_hash.close! end if session.key?('flash') && session['flash'].empty? -- cgit v1.2.3 From a66c91723565d37969de4cb46baa50fb8865b02a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Tue, 19 Apr 2011 11:54:12 +0200 Subject: Do not inherit from Rack::Response, remove a shit-ton of unused code. --- actionpack/lib/abstract_controller/rendering.rb | 8 +- .../lib/action_controller/metal/rendering.rb | 11 +++ actionpack/lib/action_dispatch/http/response.rb | 101 ++++++++++----------- actionpack/lib/action_dispatch/middleware/flash.rb | 1 + 4 files changed, 61 insertions(+), 60 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/abstract_controller/rendering.rb b/actionpack/lib/abstract_controller/rendering.rb index d0dd730b06..306bd41e2d 100644 --- a/actionpack/lib/abstract_controller/rendering.rb +++ b/actionpack/lib/abstract_controller/rendering.rb @@ -114,13 +114,7 @@ module AbstractController # :api: plugin def render_to_string(*args, &block) options = _normalize_render(*args, &block) - if self.response_body = render_to_body(options) - string = "" - response_body.each { |r| string << r } - string - end - ensure - self.response_body = nil + render_to_body(options) end # Raw rendering of a template to a Rack-compatible body. diff --git a/actionpack/lib/action_controller/metal/rendering.rb b/actionpack/lib/action_controller/metal/rendering.rb index 32d52c84c4..70fd79bb8b 100644 --- a/actionpack/lib/action_controller/metal/rendering.rb +++ b/actionpack/lib/action_controller/metal/rendering.rb @@ -18,6 +18,17 @@ module ActionController response_body end + # Overwrite render_to_string because body can now be set to a rack body. + def render_to_string(*) + if self.response_body = super + string = "" + response_body.each { |r| string << r } + string + end + ensure + self.response_body = nil + end + private # Normalize arguments by catching blocks and setting them on :update. diff --git a/actionpack/lib/action_dispatch/http/response.rb b/actionpack/lib/action_dispatch/http/response.rb index 8e03a7879f..78ecf177be 100644 --- a/actionpack/lib/action_dispatch/http/response.rb +++ b/actionpack/lib/action_dispatch/http/response.rb @@ -32,24 +32,35 @@ module ActionDispatch # :nodoc: # puts @response.body # end # end - class Response < Rack::Response - attr_accessor :request, :blank + class Response + attr_accessor :request, :header, :status + attr_writer :sending_file - attr_writer :header, :sending_file alias_method :headers=, :header= + alias_method :headers, :header + + delegate :[], :[]=, :to => :@header + delegate :each, :to => :@body + + # Sets the HTTP response's content MIME type. For example, in the controller + # you could write this: + # + # response.content_type = "text/plain" + # + # If a character set has been defined for this response (see charset=) then + # the character set information will also be included in the content type + # information. + attr_accessor :charset, :content_type + + CONTENT_TYPE = "Content-Type" + + cattr_accessor(:default_charset) { "utf-8" } module Setup def initialize(status = 200, header = {}, body = []) - @writer = lambda { |x| @body << x } - @block = nil - @length = 0 - - @header = header - self.body, self.status = body, status + self.body, self.header, self.status = body, header, status - @cookie = [] @sending_file = false - @blank = false if content_type = self["Content-Type"] @@ -62,6 +73,7 @@ module ActionDispatch # :nodoc: end end + include Rack::Response::Helpers include Setup include ActionDispatch::Http::Cache::Response @@ -106,13 +118,21 @@ module ActionDispatch # :nodoc: def body=(body) @blank = true if body == EMPTY - @body = body.respond_to?(:to_str) ? [body] : body + @body = body.respond_to?(:each) ? body : [body] end def body_parts @body end + def set_cookie(key, value) + ::Rack::Utils.set_cookie_header!(header, key, value) + end + + def delete_cookie(key, value={}) + ::Rack::Utils.delete_cookie_header!(header, key, value) + end + def location headers['Location'] end @@ -122,46 +142,21 @@ module ActionDispatch # :nodoc: headers['Location'] = url end - # Sets the HTTP response's content MIME type. For example, in the controller - # you could write this: - # - # response.content_type = "text/plain" - # - # If a character set has been defined for this response (see charset=) then - # the character set information will also be included in the content type - # information. - attr_accessor :charset, :content_type - - CONTENT_TYPE = "Content-Type" - - cattr_accessor(:default_charset) { "utf-8" } - def to_a assign_default_content_type_and_charset! handle_conditional_get! - self["Set-Cookie"] = self["Set-Cookie"].join("\n") if self["Set-Cookie"].respond_to?(:join) - super - end - alias prepare! to_a + @header["Set-Cookie"] = @header["Set-Cookie"].join("\n") if @header["Set-Cookie"].respond_to?(:join) - def each(&callback) - if @body.respond_to?(:call) - @writer = lambda { |x| callback.call(x) } - @body.call(self, self) + if [204, 304].include?(@status) + @header.delete "Content-Type" + [@status, @header, []] else - @body.each { |part| callback.call(part.to_s) } + [@status, @header, self] end - - @writer = callback - @block.call(self) if @block - end - - def write(str) - str = str.to_s - @writer.call str - str end + alias prepare! to_a + alias to_ary to_a # For implicit splat on 1.9.2 # Returns the response cookies, converted to a Hash of (name => value) pairs # @@ -180,18 +175,18 @@ module ActionDispatch # :nodoc: cookies end - private - def assign_default_content_type_and_charset! - return if headers[CONTENT_TYPE].present? + private - @content_type ||= Mime::HTML - @charset ||= self.class.default_charset + def assign_default_content_type_and_charset! + return if headers[CONTENT_TYPE].present? - type = @content_type.to_s.dup - type << "; charset=#{@charset}" unless @sending_file + @content_type ||= Mime::HTML + @charset ||= self.class.default_charset - headers[CONTENT_TYPE] = type - end + type = @content_type.to_s.dup + type << "; charset=#{@charset}" unless @sending_file + headers[CONTENT_TYPE] = type + end end end diff --git a/actionpack/lib/action_dispatch/middleware/flash.rb b/actionpack/lib/action_dispatch/middleware/flash.rb index 414405cc9e..735c72d34a 100644 --- a/actionpack/lib/action_dispatch/middleware/flash.rb +++ b/actionpack/lib/action_dispatch/middleware/flash.rb @@ -79,6 +79,7 @@ module ActionDispatch @used = Set.new @closed = false @flashes = {} + @now = nil end def []=(k, v) #:nodoc: -- cgit v1.2.3 From b398520c1406824efd12df6bb57996aa9781f876 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Tue, 19 Apr 2011 12:25:01 +0200 Subject: Output a redirect to the 500 page if something happens when streaming. Currently, we output: "> --- actionpack/lib/action_view/base.rb | 6 ++++++ .../lib/action_view/renderer/streaming_template_renderer.rb | 11 ++++++----- 2 files changed, 12 insertions(+), 5 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb index 9e8a3c51a3..87501d5b88 100644 --- a/actionpack/lib/action_view/base.rb +++ b/actionpack/lib/action_view/base.rb @@ -137,6 +137,12 @@ module ActionView #:nodoc: cattr_accessor :field_error_proc @@field_error_proc = Proc.new{ |html_tag, instance| "
#{html_tag}
".html_safe } + # How to complete the streaming when an exception occurs. + # This is our best guess: first try to close the attribute, then the tag. + # Currently this is private API and may be changed at *any* time. + cattr_accessor :streaming_completion_on_exception + @@streaming_completion_on_exception = %(">) + class_attribute :helpers class_attribute :_routes diff --git a/actionpack/lib/action_view/renderer/streaming_template_renderer.rb b/actionpack/lib/action_view/renderer/streaming_template_renderer.rb index 52f0e9f5bd..03aab444f8 100644 --- a/actionpack/lib/action_view/renderer/streaming_template_renderer.rb +++ b/actionpack/lib/action_view/renderer/streaming_template_renderer.rb @@ -46,11 +46,8 @@ module ActionView # # == TODO # - # * Add streaming support in the controllers with no-cache settings - # * What should happen when an error happens? # * Support streaming from child templates, partials and so on. - # * Support on sprockets async JS load? - # + # * Integrate exceptions with exceptron class StreamingTemplateRenderer < TemplateRenderer #:nodoc: # A valid Rack::Body (i.e. it responds to each). # It is initialized with a block that, when called, starts @@ -61,7 +58,11 @@ module ActionView end def each(&block) - @start.call(block) + begin + @start.call(block) + rescue + block.call ActionView::Base.streaming_completion_on_exception + end self end end -- cgit v1.2.3 From 069e9b004f91c4ace1373ab5203bb00ab41bd1f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Tue, 19 Apr 2011 15:04:28 +0200 Subject: Do not stream on HTTP/1.0. --- actionpack/lib/action_controller/metal/streaming.rb | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/metal/streaming.rb b/actionpack/lib/action_controller/metal/streaming.rb index adb3e94134..b9bd49f670 100644 --- a/actionpack/lib/action_controller/metal/streaming.rb +++ b/actionpack/lib/action_controller/metal/streaming.rb @@ -38,9 +38,13 @@ module ActionController #:nodoc: def _process_options(options) #:nodoc: super if options[:stream] - headers["Cache-Control"] ||= "no-cache" - headers["Transfer-Encoding"] = "chunked" - headers.delete("Content-Length") + if env["HTTP_VERSION"] == "HTTP/1.0" + options.delete(:stream) + else + headers["Cache-Control"] ||= "no-cache" + headers["Transfer-Encoding"] = "chunked" + headers.delete("Content-Length") + end end end -- cgit v1.2.3 From a3a5c7eba39c64413abd0fb4766282c9f071d248 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Tue, 19 Apr 2011 18:07:14 +0200 Subject: All assets, including images, audio, and video, now uses the asset pipeline when its on --- actionpack/lib/action_view/helpers/asset_tag_helper.rb | 18 +++++++++++++++--- actionpack/lib/action_view/helpers/sprockets_helper.rb | 15 ++++++++++----- 2 files changed, 25 insertions(+), 8 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb index f6b2d4f3f4..10bdede1b4 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb @@ -274,7 +274,11 @@ module ActionView # The alias +path_to_image+ is provided to avoid that. Rails uses the alias internally, and # plugin authors are encouraged to do so. def image_path(source) - asset_paths.compute_public_path(source, 'images') + if config.use_sprockets + sprockets_asset_path(source) + else + asset_paths.compute_public_path(source, 'images') + end end alias_method :path_to_image, :image_path # aliased to avoid conflicts with an image_path named route @@ -289,7 +293,11 @@ module ActionView # video_path("/trailers/hd.avi") # => /trailers/hd.avi # video_path("http://www.railsapplication.com/vid/hd.avi") # => http://www.railsapplication.com/vid/hd.avi def video_path(source) - asset_paths.compute_public_path(source, 'videos') + if config.use_sprockets + sprockets_asset_path(source) + else + asset_paths.compute_public_path(source, 'videos') + end end alias_method :path_to_video, :video_path # aliased to avoid conflicts with a video_path named route @@ -304,7 +312,11 @@ module ActionView # audio_path("/sounds/horse.wav") # => /sounds/horse.wav # audio_path("http://www.railsapplication.com/sounds/horse.wav") # => http://www.railsapplication.com/sounds/horse.wav def audio_path(source) - asset_paths.compute_public_path(source, 'audios') + if config.use_sprockets + sprockets_asset_path(source) + else + asset_paths.compute_public_path(source, 'audios') + end end alias_method :path_to_audio, :audio_path # aliased to avoid conflicts with an audio_path named route diff --git a/actionpack/lib/action_view/helpers/sprockets_helper.rb b/actionpack/lib/action_view/helpers/sprockets_helper.rb index 408a2030ab..fee13be886 100644 --- a/actionpack/lib/action_view/helpers/sprockets_helper.rb +++ b/actionpack/lib/action_view/helpers/sprockets_helper.rb @@ -3,8 +3,12 @@ require 'uri' module ActionView module Helpers module SprocketsHelper + def sprockets_asset_path(source, default_ext = nil) + compute_sprockets_path(source, 'assets', default_ext) + end + def sprockets_javascript_path(source) - compute_sprockets_path source, 'assets', 'js' + sprockets_asset_path(source, 'js') end def sprockets_javascript_include_tag(source, options = {}) @@ -17,9 +21,9 @@ module ActionView end def sprockets_stylesheet_path(source) - compute_sprockets_path source, 'assets', 'css' + sprockets_asset_path(source, 'css') end - + def sprockets_stylesheet_link_tag(source, options = {}) options = { 'rel' => "stylesheet", @@ -31,13 +35,14 @@ module ActionView tag 'link', options end + private - def compute_sprockets_path(source, dir, default_ext) + def compute_sprockets_path(source, dir, default_ext = nil) source = source.to_s return source if URI.parse(source).host - # Add /javscripts to relative paths + # Add /assets to relative paths if source[0] != ?/ source = "/#{dir}/#{source}" end -- cgit v1.2.3 From d35c91225e8eea967358328ba618e6608222a615 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Tue, 19 Apr 2011 18:29:18 +0200 Subject: Cleanup compute_sprockets_path -- when you are reaching for code comments, the method could be simpler --- .../lib/action_view/helpers/sprockets_helper.rb | 51 +++++++++++++--------- 1 file changed, 30 insertions(+), 21 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_view/helpers/sprockets_helper.rb b/actionpack/lib/action_view/helpers/sprockets_helper.rb index fee13be886..d2a31c02d4 100644 --- a/actionpack/lib/action_view/helpers/sprockets_helper.rb +++ b/actionpack/lib/action_view/helpers/sprockets_helper.rb @@ -11,6 +11,11 @@ module ActionView sprockets_asset_path(source, 'js') end + def sprockets_stylesheet_path(source) + sprockets_asset_path(source, 'css') + end + + def sprockets_javascript_include_tag(source, options = {}) options = { 'type' => "text/javascript", @@ -20,10 +25,6 @@ module ActionView content_tag 'script', "", options end - def sprockets_stylesheet_path(source) - sprockets_asset_path(source, 'css') - end - def sprockets_stylesheet_link_tag(source, options = {}) options = { 'rel' => "stylesheet", @@ -40,30 +41,38 @@ module ActionView def compute_sprockets_path(source, dir, default_ext = nil) source = source.to_s - return source if URI.parse(source).host - - # Add /assets to relative paths - if source[0] != ?/ - source = "/#{dir}/#{source}" + unless source_is_a_url?(source) + add_asset_directory(source, dir) + add_default_extension(source, default_ext) + add_fingerprint(source, dir) + add_asset_host(source) end - # Add default extension if there isn't one - if default_ext && File.extname(source).empty? - source = "#{source}.#{default_ext}" - end - - # Fingerprint url - if source =~ /^\/#{dir}\/(.+)/ - source = assets.path($1, config.perform_caching, dir) - end + source + end + + def add_asset_directory(source, dir) + source.replace("/#{dir}/#{source}") if source[0] != ?/ + end + + def add_default_extension(source, default_ext) + source.replace("#{source}.#{default_ext}") if default_ext && File.extname(source).empty? + end + + def add_fingerprint(source, dir) + source.replace(assets.path($1, config.perform_caching, dir)) if source =~ /^\/#{dir}\/(.+)/ + end + def add_asset_host(source) host = compute_asset_host(source) if controller.respond_to?(:request) && host && URI.parse(host).host - source = "#{controller.request.protocol}#{host}#{source}" + source.replace("#{controller.request.protocol}#{host}#{source}") end - - source + end + + def source_is_a_url?(source) + URI.parse(source).host.present? end def compute_asset_host(source) -- cgit v1.2.3 From 626bcc9bf415ca9872bbdaceac30a4df9aca86bb Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Tue, 19 Apr 2011 19:05:07 +0200 Subject: Switch to asset_path and make it available in the Sprockets::Context (now you can do asset_path("logo.png") in a stylesheet.css.erb file and get fingerprinting) --- .../lib/action_view/helpers/asset_tag_helper.rb | 6 ++-- .../asset_tag_helpers/javascript_tag_helpers.rb | 2 +- .../asset_tag_helpers/stylesheet_tag_helpers.rb | 2 +- .../lib/action_view/helpers/sprockets_helper.rb | 32 +++++++++++++--------- 4 files changed, 24 insertions(+), 18 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb index 10bdede1b4..e859b3ae49 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb @@ -275,7 +275,7 @@ module ActionView # plugin authors are encouraged to do so. def image_path(source) if config.use_sprockets - sprockets_asset_path(source) + asset_path(source) else asset_paths.compute_public_path(source, 'images') end @@ -294,7 +294,7 @@ module ActionView # video_path("http://www.railsapplication.com/vid/hd.avi") # => http://www.railsapplication.com/vid/hd.avi def video_path(source) if config.use_sprockets - sprockets_asset_path(source) + asset_path(source) else asset_paths.compute_public_path(source, 'videos') end @@ -313,7 +313,7 @@ module ActionView # audio_path("http://www.railsapplication.com/sounds/horse.wav") # => http://www.railsapplication.com/sounds/horse.wav def audio_path(source) if config.use_sprockets - sprockets_asset_path(source) + asset_path(source) else asset_paths.compute_public_path(source, 'audios') end diff --git a/actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb b/actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb index ce5a7dc2e5..a0f6fb5692 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb @@ -87,7 +87,7 @@ module ActionView # javascript_path "http://www.railsapplication.com/js/xmlhr.js" # => http://www.railsapplication.com/js/xmlhr.js def javascript_path(source) if config.use_sprockets - sprockets_javascript_path(source) + asset_path(source, 'js') else asset_paths.compute_public_path(source, 'javascripts', 'js') end diff --git a/actionpack/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb b/actionpack/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb index a994afb65e..309762ee05 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb @@ -64,7 +64,7 @@ module ActionView # stylesheet_path "http://www.railsapplication.com/css/style.css" # => http://www.railsapplication.com/css/style.css def stylesheet_path(source) if config.use_sprockets - sprockets_stylesheet_path(source) + asset_path(source, 'css') else asset_paths.compute_public_path(source, 'stylesheets', 'css') end diff --git a/actionpack/lib/action_view/helpers/sprockets_helper.rb b/actionpack/lib/action_view/helpers/sprockets_helper.rb index d2a31c02d4..bf47af6e10 100644 --- a/actionpack/lib/action_view/helpers/sprockets_helper.rb +++ b/actionpack/lib/action_view/helpers/sprockets_helper.rb @@ -3,23 +3,14 @@ require 'uri' module ActionView module Helpers module SprocketsHelper - def sprockets_asset_path(source, default_ext = nil) + def asset_path(source, default_ext = nil) compute_sprockets_path(source, 'assets', default_ext) end - def sprockets_javascript_path(source) - sprockets_asset_path(source, 'js') - end - - def sprockets_stylesheet_path(source) - sprockets_asset_path(source, 'css') - end - - def sprockets_javascript_include_tag(source, options = {}) options = { 'type' => "text/javascript", - 'src' => sprockets_javascript_path(source) + 'src' => asset_path(source, 'js') }.merge(options.stringify_keys) content_tag 'script', "", options @@ -30,7 +21,7 @@ module ActionView 'rel' => "stylesheet", 'type' => "text/css", 'media' => "screen", - 'href' => sprockets_stylesheet_path(source) + 'href' => asset_path(source, 'css') }.merge(options.stringify_keys) tag 'link', options @@ -60,10 +51,15 @@ module ActionView end def add_fingerprint(source, dir) - source.replace(assets.path($1, config.perform_caching, dir)) if source =~ /^\/#{dir}\/(.+)/ + if source =~ /^\/#{dir}\/(.+)/ + source.replace(assets.path($1, performing_caching?, dir)) + end end def add_asset_host(source) + # When included in Sprockets::Context, there's no controller + return unless respond_to?(:controller) + host = compute_asset_host(source) if controller.respond_to?(:request) && host && URI.parse(host).host @@ -94,6 +90,16 @@ module ActionView def assets Rails.application.assets end + + def performing_caching? + # When included in Sprockets::Context, we need to ask the top-level config as the controller is not available + respond_to?(:config) ? config.perform_caching : Rails.application.config.action_controller.perform_caching + end end end end + +# FIXME: Temp hack for extending Sprockets::Context so +class Sprockets::Context + include ActionView::Helpers::SprocketsHelper +end if defined?(Sprockets) \ No newline at end of file -- cgit v1.2.3 From 914218ef302542f3f58ef7f8f46c0ff0b540ac82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Tue, 19 Apr 2011 21:14:55 +0200 Subject: Let's use inheritance here, shall we? --- .../helpers/asset_tag_helpers/asset_include_tag.rb | 5 +- .../helpers/asset_tag_helpers/asset_paths.rb | 13 ++-- .../asset_tag_helpers/javascript_tag_helpers.rb | 3 - .../asset_tag_helpers/stylesheet_tag_helpers.rb | 3 - .../lib/action_view/helpers/sprockets_helper.rb | 77 +++++++--------------- 5 files changed, 35 insertions(+), 66 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb b/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb index 52eb43a1cd..e4662a2919 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb @@ -8,9 +8,11 @@ module ActionView module AssetTagHelper class AssetIncludeTag - attr_reader :config, :asset_paths + include TagHelper + attr_reader :config, :asset_paths class_attribute :expansions + def self.inherited(base) base.expansions = { } end @@ -56,7 +58,6 @@ module ActionView end end - private def path_to_asset(source, include_host = true) diff --git a/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb b/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb index 1e00fd996b..955634bb19 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb @@ -26,15 +26,18 @@ module ActionView # roots. Rewrite the asset path for cache-busting asset ids. Include # asset host, if configured, with the correct request protocol. def compute_public_path(source, dir, ext = nil, include_host = true) + source = source.to_s return source if is_uri?(source) source = rewrite_extension(source, dir, ext) if ext source = "/#{dir}/#{source}" unless source[0] == ?/ - source = rewrite_asset_path(source, config.asset_path) + source = rewrite_asset_path(source, dir) - has_request = controller.respond_to?(:request) - source = rewrite_relative_url_root(source, controller.config.relative_url_root) if has_request && include_host - source = rewrite_host_and_protocol(source, has_request) if include_host + if controller && include_host + has_request = controller.respond_to?(:request) + source = rewrite_relative_url_root(source, controller.config.relative_url_root) if has_request + source = rewrite_host_and_protocol(source, has_request) + end source end @@ -70,6 +73,8 @@ module ActionView # Break out the asset path rewrite in case plugins wish to put the asset id # someplace other than the query string. def rewrite_asset_path(source, path = nil) + path = config.asset_path + if path && path.respond_to?(:call) return path.call(source) elsif path && path.is_a?(String) diff --git a/actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb b/actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb index a0f6fb5692..07ff49659a 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb @@ -1,6 +1,5 @@ require 'active_support/concern' require 'active_support/core_ext/file' -require 'action_view/helpers/tag_helper' require 'action_view/helpers/asset_tag_helpers/asset_include_tag' module ActionView @@ -8,8 +7,6 @@ module ActionView module AssetTagHelper class JavascriptIncludeTag < AssetIncludeTag - include TagHelper - def asset_name 'javascript' end diff --git a/actionpack/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb b/actionpack/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb index 309762ee05..c3dcd410bb 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb @@ -1,6 +1,5 @@ require 'active_support/concern' require 'active_support/core_ext/file' -require 'action_view/helpers/tag_helper' require 'action_view/helpers/asset_tag_helpers/asset_include_tag' module ActionView @@ -8,8 +7,6 @@ module ActionView module AssetTagHelper class StylesheetIncludeTag < AssetIncludeTag - include TagHelper - def asset_name 'stylesheet' end diff --git a/actionpack/lib/action_view/helpers/sprockets_helper.rb b/actionpack/lib/action_view/helpers/sprockets_helper.rb index bf47af6e10..e2e844c74d 100644 --- a/actionpack/lib/action_view/helpers/sprockets_helper.rb +++ b/actionpack/lib/action_view/helpers/sprockets_helper.rb @@ -1,10 +1,11 @@ require 'uri' +require 'action_view/helpers/asset_tag_helpers/asset_paths' module ActionView module Helpers module SprocketsHelper def asset_path(source, default_ext = nil) - compute_sprockets_path(source, 'assets', default_ext) + sprockets_asset_paths.compute_public_path(source, 'assets', default_ext, true) end def sprockets_javascript_include_tag(source, options = {}) @@ -27,74 +28,42 @@ module ActionView tag 'link', options end - private - def compute_sprockets_path(source, dir, default_ext = nil) - source = source.to_s - - unless source_is_a_url?(source) - add_asset_directory(source, dir) - add_default_extension(source, default_ext) - add_fingerprint(source, dir) - add_asset_host(source) - end - source - end - - def add_asset_directory(source, dir) - source.replace("/#{dir}/#{source}") if source[0] != ?/ - end - - def add_default_extension(source, default_ext) - source.replace("#{source}.#{default_ext}") if default_ext && File.extname(source).empty? - end - - def add_fingerprint(source, dir) - if source =~ /^\/#{dir}\/(.+)/ - source.replace(assets.path($1, performing_caching?, dir)) - end + def sprockets_asset_paths + @sprockets_asset_paths ||= begin + config = self.config if respond_to?(:config) + controller = self.controller if respond_to?(:controller) + SprocketsHelper::AssetPaths.new(config, controller) end + end - def add_asset_host(source) - # When included in Sprockets::Context, there's no controller - return unless respond_to?(:controller) - - host = compute_asset_host(source) - - if controller.respond_to?(:request) && host && URI.parse(host).host - source.replace("#{controller.request.protocol}#{host}#{source}") + class AssetPaths < ActionView::Helpers::AssetTagHelper::AssetPaths + def rewrite_asset_path(source, dir) + if source =~ /^\/#{dir}\/(.+)/ + assets.path($1, performing_caching?, dir) + else + source end end - - def source_is_a_url?(source) - URI.parse(source).host.present? - end - def compute_asset_host(source) - if host = config.asset_host - if host.is_a?(Proc) || host.respond_to?(:call) - case host.is_a?(Proc) ? host.arity : host.method(:call).arity - when 2 - request = controller.respond_to?(:request) && controller.request - host.call(source, request) - else - host.call(source) - end - else - (host =~ /%d/) ? host % (source.hash % 4) : host - end + def rewrite_extension(source, dir, ext) + if ext && File.extname(source).empty? + "#{source}.#{ext}" + else + source end end def assets Rails.application.assets end - + + # When included in Sprockets::Context, we need to ask the top-level config as the controller is not available def performing_caching? - # When included in Sprockets::Context, we need to ask the top-level config as the controller is not available - respond_to?(:config) ? config.perform_caching : Rails.application.config.action_controller.perform_caching + @config ? @config.perform_caching : Rails.application.config.action_controller.perform_caching end + end end end end -- cgit v1.2.3 From a19c260038a9b5b688a2e8d883b604983ac59eae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Tue, 19 Apr 2011 21:49:28 +0200 Subject: Include modules to the context in the railtie. --- actionpack/lib/action_view/helpers/sprockets_helper.rb | 7 +------ actionpack/lib/sprockets/railtie.rb | 6 ++++++ 2 files changed, 7 insertions(+), 6 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_view/helpers/sprockets_helper.rb b/actionpack/lib/action_view/helpers/sprockets_helper.rb index e2e844c74d..947c827f3c 100644 --- a/actionpack/lib/action_view/helpers/sprockets_helper.rb +++ b/actionpack/lib/action_view/helpers/sprockets_helper.rb @@ -66,9 +66,4 @@ module ActionView end end end -end - -# FIXME: Temp hack for extending Sprockets::Context so -class Sprockets::Context - include ActionView::Helpers::SprocketsHelper -end if defined?(Sprockets) \ No newline at end of file +end \ No newline at end of file diff --git a/actionpack/lib/sprockets/railtie.rb b/actionpack/lib/sprockets/railtie.rb index fe3c8c9783..2f7f95c44d 100644 --- a/actionpack/lib/sprockets/railtie.rb +++ b/actionpack/lib/sprockets/railtie.rb @@ -32,6 +32,12 @@ module Sprockets next unless assets.enabled app.assets = asset_environment(app) + + # FIXME: Temp hack for extending Sprockets::Context so + ActiveSupport.on_load(:action_view) do + ::Sprockets::Context.send :include, ::ActionView::Helpers::SprocketsHelper + end + app.routes.append do mount app.assets => assets.prefix end -- cgit v1.2.3 From 22fcef90b185199563719fc511346bf4c2f5bbff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Tue, 19 Apr 2011 22:01:25 +0200 Subject: Actually add an abstract class, so it is easier to get rid of old asset paths in the future. --- actionpack/lib/action_view/helpers/asset_paths.rb | 80 ++++++++++ .../lib/action_view/helpers/asset_tag_helper.rb | 2 +- .../helpers/asset_tag_helpers/asset_paths.rb | 161 +++++++-------------- .../lib/action_view/helpers/sprockets_helper.rb | 4 +- 4 files changed, 136 insertions(+), 111 deletions(-) create mode 100644 actionpack/lib/action_view/helpers/asset_paths.rb (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_view/helpers/asset_paths.rb b/actionpack/lib/action_view/helpers/asset_paths.rb new file mode 100644 index 0000000000..55a4c442fd --- /dev/null +++ b/actionpack/lib/action_view/helpers/asset_paths.rb @@ -0,0 +1,80 @@ +require 'active_support/core_ext/file' +require 'action_view/helpers/asset_paths' + +module ActionView + module Helpers + + class AssetPaths #:nodoc: + attr_reader :config, :controller + + def initialize(config, controller) + @config = config + @controller = controller + end + + # Add the extension +ext+ if not present. Return full URLs otherwise untouched. + # Prefix with /dir/ if lacking a leading +/+. Account for relative URL + # roots. Rewrite the asset path for cache-busting asset ids. Include + # asset host, if configured, with the correct request protocol. + def compute_public_path(source, dir, ext = nil, include_host = true) + source = source.to_s + return source if is_uri?(source) + + source = rewrite_extension(source, dir, ext) if ext + source = "/#{dir}/#{source}" unless source[0] == ?/ + source = rewrite_asset_path(source, dir) + + if controller && include_host + has_request = controller.respond_to?(:request) + source = rewrite_host_and_protocol(source, has_request) + end + + source + end + + def is_uri?(path) + path =~ %r{^[-a-z]+://|^cid:} + end + + private + + def rewrite_extension(source, dir, ext) + raise NotImplementedError + end + + def rewrite_asset_path(source, path = nil) + raise NotImplementedError + end + + def rewrite_host_and_protocol(source, has_request) + host = compute_asset_host(source) + if has_request && host && !is_uri?(host) + host = "#{controller.request.protocol}#{host}" + end + "#{host}#{source}" + end + + # Pick an asset host for this source. Returns +nil+ if no host is set, + # the host if no wildcard is set, the host interpolated with the + # numbers 0-3 if it contains %d (the number is the source hash mod 4), + # or the value returned from invoking the proc if it's a proc or the value from + # invoking call if it's an object responding to call. + def compute_asset_host(source) + if host = config.asset_host + if host.is_a?(Proc) || host.respond_to?(:call) + case host.is_a?(Proc) ? host.arity : host.method(:call).arity + when 2 + request = controller.respond_to?(:request) && controller.request + host.call(source, request) + else + host.call(source) + end + else + (host =~ /%d/) ? host % (source.hash % 4) : host + end + end + end + end + + end +end \ No newline at end of file diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb index e859b3ae49..a7ae6ad0bf 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb @@ -446,7 +446,7 @@ module ActionView private def asset_paths - @asset_paths ||= AssetPaths.new(config, controller) + @asset_paths ||= AssetTagHelper::AssetPaths.new(config, controller) end end end diff --git a/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb b/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb index 955634bb19..38860431b4 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb @@ -1,10 +1,11 @@ require 'active_support/core_ext/file' +require 'action_view/helpers/asset_paths' module ActionView module Helpers module AssetTagHelper - class AssetPaths + class AssetPaths < ActionView::Helpers::AssetPaths #:nodoc: # You can enable or disable the asset tag ids cache. # With the cache enabled, the asset tag helper methods will make fewer # expensive file system calls (the default implementation checks the file @@ -14,34 +15,6 @@ module ActionView # ActionView::Helpers::AssetTagHelper::AssetPaths.cache_asset_ids = false mattr_accessor :cache_asset_ids - attr_reader :config, :controller - - def initialize(config, controller) - @config = config - @controller = controller - end - - # Add the extension +ext+ if not present. Return full URLs otherwise untouched. - # Prefix with /dir/ if lacking a leading +/+. Account for relative URL - # roots. Rewrite the asset path for cache-busting asset ids. Include - # asset host, if configured, with the correct request protocol. - def compute_public_path(source, dir, ext = nil, include_host = true) - source = source.to_s - return source if is_uri?(source) - - source = rewrite_extension(source, dir, ext) if ext - source = "/#{dir}/#{source}" unless source[0] == ?/ - source = rewrite_asset_path(source, dir) - - if controller && include_host - has_request = controller.respond_to?(:request) - source = rewrite_relative_url_root(source, controller.config.relative_url_root) if has_request - source = rewrite_host_and_protocol(source, has_request) - end - - source - end - # Add or change an asset id in the asset id cache. This can be used # for SASS on Heroku. # :api: public @@ -51,103 +24,75 @@ module ActionView end end - def is_uri?(path) - path =~ %r{^[-a-z]+://|^cid:} - end - - private - - def rewrite_extension(source, dir, ext) - source_ext = File.extname(source) + private - source_with_ext = if source_ext.empty? - "#{source}.#{ext}" - elsif ext != source_ext[1..-1] - with_ext = "#{source}.#{ext}" - with_ext if File.exist?(File.join(config.assets_dir, dir, with_ext)) - end + def rewrite_extension(source, dir, ext) + source_ext = File.extname(source) - source_with_ext || source + source_with_ext = if source_ext.empty? + "#{source}.#{ext}" + elsif ext != source_ext[1..-1] + with_ext = "#{source}.#{ext}" + with_ext if File.exist?(File.join(config.assets_dir, dir, with_ext)) end - # Break out the asset path rewrite in case plugins wish to put the asset id - # someplace other than the query string. - def rewrite_asset_path(source, path = nil) - path = config.asset_path + source_with_ext || source + end - if path && path.respond_to?(:call) - return path.call(source) - elsif path && path.is_a?(String) - return path % [source] - end + # Break out the asset path rewrite in case plugins wish to put the asset id + # someplace other than the query string. + def rewrite_asset_path(source, path = nil) + path = config.asset_path - asset_id = rails_asset_id(source) - if asset_id.empty? - source - else - "#{source}?#{asset_id}" - end + if path && path.respond_to?(:call) + return path.call(source) + elsif path && path.is_a?(String) + return path % [source] end - mattr_accessor :asset_ids_cache - self.asset_ids_cache = {} + asset_id = rails_asset_id(source) + if asset_id.empty? + source + else + "#{source}?#{asset_id}" + end + end + + mattr_accessor :asset_ids_cache + self.asset_ids_cache = {} - mattr_accessor :asset_ids_cache_guard - self.asset_ids_cache_guard = Mutex.new + mattr_accessor :asset_ids_cache_guard + self.asset_ids_cache_guard = Mutex.new - # Use the RAILS_ASSET_ID environment variable or the source's - # modification time as its cache-busting asset id. - def rails_asset_id(source) - if asset_id = ENV["RAILS_ASSET_ID"] + # Use the RAILS_ASSET_ID environment variable or the source's + # modification time as its cache-busting asset id. + def rails_asset_id(source) + if asset_id = ENV["RAILS_ASSET_ID"] + asset_id + else + if self.cache_asset_ids && (asset_id = self.asset_ids_cache[source]) asset_id else - if self.cache_asset_ids && (asset_id = self.asset_ids_cache[source]) - asset_id - else - path = File.join(config.assets_dir, source) - asset_id = File.exist?(path) ? File.mtime(path).to_i.to_s : '' + path = File.join(config.assets_dir, source) + asset_id = File.exist?(path) ? File.mtime(path).to_i.to_s : '' - if self.cache_asset_ids - add_to_asset_ids_cache(source, asset_id) - end - - asset_id + if self.cache_asset_ids + add_to_asset_ids_cache(source, asset_id) end - end - end - - def rewrite_relative_url_root(source, relative_url_root) - relative_url_root && !source.starts_with?("#{relative_url_root}/") ? "#{relative_url_root}#{source}" : source - end - def rewrite_host_and_protocol(source, has_request) - host = compute_asset_host(source) - if has_request && host && !is_uri?(host) - host = "#{controller.request.protocol}#{host}" + asset_id end - "#{host}#{source}" end + end - # Pick an asset host for this source. Returns +nil+ if no host is set, - # the host if no wildcard is set, the host interpolated with the - # numbers 0-3 if it contains %d (the number is the source hash mod 4), - # or the value returned from invoking the proc if it's a proc or the value from - # invoking call if it's an object responding to call. - def compute_asset_host(source) - if host = config.asset_host - if host.is_a?(Proc) || host.respond_to?(:call) - case host.is_a?(Proc) ? host.arity : host.method(:call).arity - when 2 - request = controller.respond_to?(:request) && controller.request - host.call(source, request) - else - host.call(source) - end - else - (host =~ /%d/) ? host % (source.hash % 4) : host - end - end - end + def rewrite_relative_url_root(source, relative_url_root) + relative_url_root && !source.starts_with?("#{relative_url_root}/") ? "#{relative_url_root}#{source}" : source + end + + def rewrite_host_and_protocol(source, has_request) + source = rewrite_relative_url_root(source, controller.config.relative_url_root) if has_request + super(source, has_request) + end end end diff --git a/actionpack/lib/action_view/helpers/sprockets_helper.rb b/actionpack/lib/action_view/helpers/sprockets_helper.rb index 947c827f3c..b43b91178c 100644 --- a/actionpack/lib/action_view/helpers/sprockets_helper.rb +++ b/actionpack/lib/action_view/helpers/sprockets_helper.rb @@ -1,5 +1,5 @@ require 'uri' -require 'action_view/helpers/asset_tag_helpers/asset_paths' +require 'action_view/helpers/asset_paths' module ActionView module Helpers @@ -38,7 +38,7 @@ module ActionView end end - class AssetPaths < ActionView::Helpers::AssetTagHelper::AssetPaths + class AssetPaths < ActionView::Helpers::AssetPaths #:nodoc: def rewrite_asset_path(source, dir) if source =~ /^\/#{dir}\/(.+)/ assets.path($1, performing_caching?, dir) -- cgit v1.2.3 From 89ed9fbd1917e431e489dc856042d996d0f088c5 Mon Sep 17 00:00:00 2001 From: Florent Piteau Date: Wed, 20 Apr 2011 02:10:29 +0800 Subject: Don't reuse a closed flash when using now --- actionpack/lib/action_dispatch/middleware/flash.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_dispatch/middleware/flash.rb b/actionpack/lib/action_dispatch/middleware/flash.rb index 735c72d34a..c7f7d4d4f0 100644 --- a/actionpack/lib/action_dispatch/middleware/flash.rb +++ b/actionpack/lib/action_dispatch/middleware/flash.rb @@ -70,6 +70,10 @@ module ActionDispatch def close!(new_flash) @flash = new_flash end + + def closed? + @flash.closed? + end end class FlashHash @@ -146,7 +150,7 @@ module ActionDispatch # # Entries set via now are accessed the same way as standard entries: flash['my-key']. def now - @now ||= FlashNow.new(self) + @now = (!@now || @now.closed?) ? FlashNow.new(self) : @now end attr_reader :closed -- cgit v1.2.3 From 2f549b8bbd733ad0563d977e83a9b2a2b6b8e07c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Tue, 19 Apr 2011 22:38:51 +0200 Subject: Use initialize_copy! to proper initialize now on clone. --- actionpack/lib/action_dispatch/middleware/flash.rb | 33 ++++++++++++---------- 1 file changed, 18 insertions(+), 15 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_dispatch/middleware/flash.rb b/actionpack/lib/action_dispatch/middleware/flash.rb index c7f7d4d4f0..2adbce031b 100644 --- a/actionpack/lib/action_dispatch/middleware/flash.rb +++ b/actionpack/lib/action_dispatch/middleware/flash.rb @@ -43,6 +43,8 @@ module ActionDispatch KEY = 'action_dispatch.request.flash_hash'.freeze class FlashNow #:nodoc: + attr_accessor :flash + def initialize(flash) @flash = flash end @@ -66,14 +68,6 @@ module ActionDispatch def notice=(message) self[:notice] = message end - - def close!(new_flash) - @flash = new_flash - end - - def closed? - @flash.closed? - end end class FlashHash @@ -86,6 +80,14 @@ module ActionDispatch @now = nil end + def initialize_copy(other) + if other.now_is_loaded? + @now = other.now.dup + @now.flash = self + end + super + end + def []=(k, v) #:nodoc: raise ClosedError, :flash if closed? keep(k) @@ -150,16 +152,12 @@ module ActionDispatch # # Entries set via now are accessed the same way as standard entries: flash['my-key']. def now - @now = (!@now || @now.closed?) ? FlashNow.new(self) : @now + @now ||= FlashNow.new(self) end attr_reader :closed alias :closed? :closed - - def close! - @closed = true - @now.close!(self) if @now - end + def close!; @closed = true; end # Keeps either the entire current flash or a specific flash entry available for the next action: # @@ -214,7 +212,12 @@ module ActionDispatch self[:notice] = message end - private + protected + + def now_is_loaded? + !!@now + end + # Used internally by the keep and discard methods # use() # marks the entire flash as used # use('msg') # marks the "msg" entry as used -- cgit v1.2.3 From d31af44012a6ba3ac5dbec45417ae9bfb5454d8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Wed, 20 Apr 2011 21:54:19 +0200 Subject: Fix tests on 1.8 by explicitly checking for strings (which also improves performance). --- actionpack/lib/action_dispatch/http/response.rb | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_dispatch/http/response.rb b/actionpack/lib/action_dispatch/http/response.rb index 78ecf177be..1f4f3ac0da 100644 --- a/actionpack/lib/action_dispatch/http/response.rb +++ b/actionpack/lib/action_dispatch/http/response.rb @@ -118,7 +118,15 @@ module ActionDispatch # :nodoc: def body=(body) @blank = true if body == EMPTY - @body = body.respond_to?(:each) ? body : [body] + + # Explicitly check for strings. This is *wrong* theoretically + # but if we don't check this, the performance on string bodies + # is bad on Ruby 1.8 (because strings responds to each then). + @body = if body.respond_to?(:to_str) || !body.respond_to?(:each) + [body] + else + body + end end def body_parts -- cgit v1.2.3 From 783007a8ad7b4b61a1a671d1737a8a6e0369ceb9 Mon Sep 17 00:00:00 2001 From: Joost Baaij Date: Thu, 21 Apr 2011 16:32:02 +0200 Subject: Replace example hostname with "example.com". The hostname used in these comments actually exists, which is undesirable. See also RFC 2606. --- actionpack/lib/action_view/helpers/asset_tag_helper.rb | 6 +++--- .../asset_tag_helpers/javascript_tag_helpers.rb | 18 +++++++++--------- .../asset_tag_helpers/stylesheet_tag_helpers.rb | 14 +++++++------- actionpack/lib/action_view/helpers/url_helper.rb | 2 +- 4 files changed, 20 insertions(+), 20 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb index a7ae6ad0bf..f7cb1f5b58 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb @@ -268,7 +268,7 @@ module ActionView # image_path("edit.png") # => "/images/edit.png" # image_path("icons/edit.png") # => "/images/icons/edit.png" # image_path("/icons/edit.png") # => "/icons/edit.png" - # image_path("http://www.railsapplication.com/img/edit.png") # => "http://www.railsapplication.com/img/edit.png" + # image_path("http://www.example.com/img/edit.png") # => "http://www.example.com/img/edit.png" # # If you have images as application resources this method may conflict with their named routes. # The alias +path_to_image+ is provided to avoid that. Rails uses the alias internally, and @@ -291,7 +291,7 @@ module ActionView # video_path("hd.avi") # => /videos/hd.avi # video_path("trailers/hd.avi") # => /videos/trailers/hd.avi # video_path("/trailers/hd.avi") # => /trailers/hd.avi - # video_path("http://www.railsapplication.com/vid/hd.avi") # => http://www.railsapplication.com/vid/hd.avi + # video_path("http://www.example.com/vid/hd.avi") # => http://www.example.com/vid/hd.avi def video_path(source) if config.use_sprockets asset_path(source) @@ -310,7 +310,7 @@ module ActionView # audio_path("horse.wav") # => /audios/horse.wav # audio_path("sounds/horse.wav") # => /audios/sounds/horse.wav # audio_path("/sounds/horse.wav") # => /sounds/horse.wav - # audio_path("http://www.railsapplication.com/sounds/horse.wav") # => http://www.railsapplication.com/sounds/horse.wav + # audio_path("http://www.example.com/sounds/horse.wav") # => http://www.example.com/sounds/horse.wav def audio_path(source) if config.use_sprockets asset_path(source) diff --git a/actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb b/actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb index 07ff49659a..3d815b5e1f 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb @@ -77,11 +77,11 @@ module ActionView # Used internally by javascript_include_tag to build the script path. # # ==== Examples - # javascript_path "xmlhr" # => /javascripts/xmlhr.js - # javascript_path "dir/xmlhr.js" # => /javascripts/dir/xmlhr.js - # javascript_path "/dir/xmlhr" # => /dir/xmlhr.js - # javascript_path "http://www.railsapplication.com/js/xmlhr" # => http://www.railsapplication.com/js/xmlhr - # javascript_path "http://www.railsapplication.com/js/xmlhr.js" # => http://www.railsapplication.com/js/xmlhr.js + # javascript_path "xmlhr" # => /javascripts/xmlhr.js + # javascript_path "dir/xmlhr.js" # => /javascripts/dir/xmlhr.js + # javascript_path "/dir/xmlhr" # => /dir/xmlhr.js + # javascript_path "http://www.example.com/js/xmlhr" # => http://www.example.com/js/xmlhr + # javascript_path "http://www.example.com/js/xmlhr.js" # => http://www.example.com/js/xmlhr.js def javascript_path(source) if config.use_sprockets asset_path(source, 'js') @@ -123,11 +123,11 @@ module ActionView # # => # # # - # javascript_include_tag "http://www.railsapplication.com/xmlhr" - # # => + # javascript_include_tag "http://www.example.com/xmlhr" + # # => # - # javascript_include_tag "http://www.railsapplication.com/xmlhr.js" - # # => + # javascript_include_tag "http://www.example.com/xmlhr.js" + # # => # # javascript_include_tag :defaults # # => diff --git a/actionpack/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb b/actionpack/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb index c3dcd410bb..a95eb221be 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb @@ -54,11 +54,11 @@ module ActionView # Used internally by +stylesheet_link_tag+ to build the stylesheet path. # # ==== Examples - # stylesheet_path "style" # => /stylesheets/style.css - # stylesheet_path "dir/style.css" # => /stylesheets/dir/style.css - # stylesheet_path "/dir/style.css" # => /dir/style.css - # stylesheet_path "http://www.railsapplication.com/css/style" # => http://www.railsapplication.com/css/style - # stylesheet_path "http://www.railsapplication.com/css/style.css" # => http://www.railsapplication.com/css/style.css + # stylesheet_path "style" # => /stylesheets/style.css + # stylesheet_path "dir/style.css" # => /stylesheets/dir/style.css + # stylesheet_path "/dir/style.css" # => /dir/style.css + # stylesheet_path "http://www.example.com/css/style" # => http://www.example.com/css/style + # stylesheet_path "http://www.example.com/css/style.css" # => http://www.example.com/css/style.css def stylesheet_path(source) if config.use_sprockets asset_path(source, 'css') @@ -79,8 +79,8 @@ module ActionView # stylesheet_link_tag "style.css" # => # # - # stylesheet_link_tag "http://www.railsapplication.com/style.css" # => - # + # stylesheet_link_tag "http://www.example.com/style.css" # => + # # # stylesheet_link_tag "style", :media => "all" # => # diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb index 051d3eb049..ffa9a5bb0b 100644 --- a/actionpack/lib/action_view/helpers/url_helper.rb +++ b/actionpack/lib/action_view/helpers/url_helper.rb @@ -68,7 +68,7 @@ module ActionView # # => /books/find # # <%= url_for(:action => 'login', :controller => 'members', :only_path => false, :protocol => 'https') %> - # # => https://www.railsapplication.com/members/login/ + # # => https://www.example.com/members/login/ # # <%= url_for(:action => 'play', :anchor => 'player') %> # # => /messages/play/#player -- cgit v1.2.3 From 84f1b83df84d36b75cc7b6d1c84ffd84c6c07260 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Thu, 21 Apr 2011 11:03:56 -0500 Subject: Only include SprocketsHelper into assets context --- actionpack/lib/sprockets/railtie.rb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/sprockets/railtie.rb b/actionpack/lib/sprockets/railtie.rb index 2f7f95c44d..ccec65ba95 100644 --- a/actionpack/lib/sprockets/railtie.rb +++ b/actionpack/lib/sprockets/railtie.rb @@ -33,9 +33,10 @@ module Sprockets app.assets = asset_environment(app) - # FIXME: Temp hack for extending Sprockets::Context so ActiveSupport.on_load(:action_view) do - ::Sprockets::Context.send :include, ::ActionView::Helpers::SprocketsHelper + app.assets.context.instance_eval do + include ::ActionView::Helpers::SprocketsHelper + end end app.routes.append do @@ -59,4 +60,4 @@ module Sprockets env end end -end \ No newline at end of file +end -- cgit v1.2.3 From 6822f39f67729884d7911b42542c965434a22250 Mon Sep 17 00:00:00 2001 From: Semyon Perepelitsa Date: Fri, 22 Apr 2011 23:12:14 +0800 Subject: Remove HTML escaping from Record Tag Helper docs. --- actionpack/lib/action_view/helpers/record_tag_helper.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_view/helpers/record_tag_helper.rb b/actionpack/lib/action_view/helpers/record_tag_helper.rb index 4d300a1469..142a25f118 100644 --- a/actionpack/lib/action_view/helpers/record_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/record_tag_helper.rb @@ -10,7 +10,7 @@ module ActionView # relate to the specified Active Record object. Usage example: # # <%= div_for(@person, :class => "foo") do %> - # <%=h @person.name %> + # <%= @person.name %> # <% end %> # # produces: @@ -25,8 +25,8 @@ module ActionView # that relate to the specified Active Record object. For example: # # <%= content_tag_for(:tr, @person) do %> - # <%=h @person.first_name %> - # <%=h @person.last_name %> + # <%= @person.first_name %> + # <%= @person.last_name %> # <% end %> # # would produce the following HTML (assuming @person is an instance of -- cgit v1.2.3 From dab96a267eeccd7380ad99fa19cefdfd3cd5ad2b Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Fri, 22 Apr 2011 10:49:55 -0500 Subject: Add shorthand for js and css compressors --- actionpack/lib/sprockets/railtie.rb | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) (limited to 'actionpack/lib') diff --git a/actionpack/lib/sprockets/railtie.rb b/actionpack/lib/sprockets/railtie.rb index ccec65ba95..9c10decd60 100644 --- a/actionpack/lib/sprockets/railtie.rb +++ b/actionpack/lib/sprockets/railtie.rb @@ -57,7 +57,44 @@ module Sprockets env.static_root = File.join(app.root.join("public"), assets.prefix) env.paths.concat assets.paths env.logger = Rails.logger + env.js_compressor = expand_js_compressor(assets.js_compressor) + env.css_compressor = expand_css_compressor(assets.css_compressor) env end + + def expand_js_compressor(sym) + case sym + when :closure + require 'closure-compiler' + Closure::Compiler.new + when :uglifier + require 'uglifier' + Uglifier.new + when :yui + require 'yui/compressor' + YUI::JavaScriptCompressor.new + else + sym + end + end + + def expand_css_compressor(sym) + case sym + when :scss + require 'sass' + compressor = Object.new + def compressor.compress(source) + Sass::Engine.new(source, + :syntax => :scss, :style => :compressed + ).render + end + compressor + when :yui + require 'yui/compressor' + YUI::JavaScriptCompressor.new(:munge => true) + else + sym + end + end end end -- cgit v1.2.3 From 89f315bfb287f5428e78195e7c93c85c0892ab64 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Fri, 22 Apr 2011 19:37:52 +0200 Subject: We cant use assert_block because its buggy in MiniTest and wont actually show you the failure message you provide -- instead you just always get a "Expected block to return true" --- actionpack/lib/action_dispatch/testing/assertions/response.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_dispatch/testing/assertions/response.rb b/actionpack/lib/action_dispatch/testing/assertions/response.rb index 8a04cfa886..e209978fb7 100644 --- a/actionpack/lib/action_dispatch/testing/assertions/response.rb +++ b/actionpack/lib/action_dispatch/testing/assertions/response.rb @@ -42,7 +42,7 @@ module ActionDispatch elsif type.is_a?(Symbol) && @response.response_code == Rack::Utils::SYMBOL_TO_STATUS_CODE[type] assert_block("") { true } # to count the assertion else - assert_block(build_message(message, "Expected response to be a , but was ", type, @response.response_code)) { false } + assert(false, build_message(message, "Expected response to be a , but was ", type, @response.response_code)) end end -- cgit v1.2.3 From a8870d140ececc6327533d08b112cece03c41499 Mon Sep 17 00:00:00 2001 From: Schneems Date: Sat, 23 Apr 2011 02:23:12 +0800 Subject: show http method in routing error message --- actionpack/lib/action_dispatch/middleware/show_exceptions.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb index dbe3206808..b1adf3d2d1 100644 --- a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb +++ b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb @@ -50,7 +50,7 @@ module ActionDispatch # Only this middleware cares about RoutingError. So, let's just raise # it here. if headers['X-Cascade'] == 'pass' - raise ActionController::RoutingError, "No route matches #{env['PATH_INFO'].inspect}" + raise ActionController::RoutingError, "No route matches [#{env['REQUEST_METHOD']}] #{env['PATH_INFO'].inspect}" end rescue Exception => exception raise exception if env['action_dispatch.show_exceptions'] == false -- cgit v1.2.3 From 3e1f4dbb4db852a3278a883b3f599664e5705de3 Mon Sep 17 00:00:00 2001 From: misfo Date: Sat, 23 Apr 2011 10:15:38 -0500 Subject: document HTML::Selector's :has(string) pseudo class --- actionpack/lib/action_controller/vendor/html-scanner/html/selector.rb | 2 ++ 1 file changed, 2 insertions(+) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/vendor/html-scanner/html/selector.rb b/actionpack/lib/action_controller/vendor/html-scanner/html/selector.rb index 0fe2e6d1a6..1eadfc0390 100644 --- a/actionpack/lib/action_controller/vendor/html-scanner/html/selector.rb +++ b/actionpack/lib/action_controller/vendor/html-scanner/html/selector.rb @@ -128,6 +128,8 @@ module HTML # (no parent element). # * :empty -- Match the element only if it has no child elements, # and no text content. + # * :content(string) -- Match the element only if it has string + # as its text content (ignoring leading and trailing whitespace). # * :only-child -- Match the element if it is the only child (element) # of its parent element. # * :only-of-type -- Match the element if it is the only child (element) -- cgit v1.2.3 From 820c0feda33495cfe68add8c3f25adc515ab3e04 Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki Date: Mon, 25 Apr 2011 14:56:58 +0200 Subject: Explicitly define main_app proxy --- actionpack/lib/action_dispatch/routing/route_set.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index b28f6c2297..1d09091dc7 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -275,8 +275,7 @@ module ActionDispatch module MountedHelpers end - def mounted_helpers(name = :main_app) - define_mounted_helper(name) if name + def mounted_helpers MountedHelpers end -- cgit v1.2.3 From d4bea35f1ae458246a9e3bb1c914c5d05b8e9cdf Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki Date: Mon, 25 Apr 2011 15:02:41 +0200 Subject: Make ActionController::Base.modules_for_helpers and ActionController::Base.all_helpers_from_path public methods --- actionpack/lib/abstract_controller/helpers.rb | 22 +++++----- actionpack/lib/action_controller/metal/helpers.rb | 52 +++++++++++------------ 2 files changed, 37 insertions(+), 37 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/abstract_controller/helpers.rb b/actionpack/lib/abstract_controller/helpers.rb index 20f8601a8e..0ff1c0491a 100644 --- a/actionpack/lib/abstract_controller/helpers.rb +++ b/actionpack/lib/abstract_controller/helpers.rb @@ -112,17 +112,6 @@ module AbstractController default_helper_module! unless anonymous? end - private - # Makes all the (instance) methods in the helper module available to templates - # rendered through this controller. - # - # ==== Parameters - # * module - The module to include into the current helper module - # for the class - def add_template_helper(mod) - _helpers.module_eval { include mod } - end - # Returns a list of modules, normalized from the acceptable kinds of # helpers with the following behavior: # @@ -155,6 +144,17 @@ module AbstractController end end + private + # Makes all the (instance) methods in the helper module available to templates + # rendered through this controller. + # + # ==== Parameters + # * module - The module to include into the current helper module + # for the class + def add_template_helper(mod) + _helpers.module_eval { include mod } + end + def default_helper_module! module_name = name.sub(/Controller$/, '') module_path = module_name.underscore diff --git a/actionpack/lib/action_controller/metal/helpers.rb b/actionpack/lib/action_controller/metal/helpers.rb index 91a88ab68a..75757db564 100644 --- a/actionpack/lib/action_controller/metal/helpers.rb +++ b/actionpack/lib/action_controller/metal/helpers.rb @@ -76,35 +76,35 @@ module ActionController @helper_proxy ||= ActionView::Base.new.extend(_helpers) end - private - # Overwrite modules_for_helpers to accept :all as argument, which loads - # all helpers in helpers_path. - # - # ==== Parameters - # * args - A list of helpers - # - # ==== Returns - # * array - A normalized list of modules for the list of helpers provided. - def modules_for_helpers(args) - args += all_application_helpers if args.delete(:all) - super(args) - end + # Overwrite modules_for_helpers to accept :all as argument, which loads + # all helpers in helpers_path. + # + # ==== Parameters + # * args - A list of helpers + # + # ==== Returns + # * array - A normalized list of modules for the list of helpers provided. + def modules_for_helpers(args) + args += all_application_helpers if args.delete(:all) + super(args) + end - # Extract helper names from files in app/helpers/**/*_helper.rb - def all_application_helpers - all_helpers_from_path(helpers_path) + def all_helpers_from_path(path) + helpers = [] + Array.wrap(path).each do |_path| + extract = /^#{Regexp.quote(_path.to_s)}\/?(.*)_helper.rb$/ + helpers += Dir["#{_path}/**/*_helper.rb"].map { |file| file.sub(extract, '\1') } end + helpers.sort! + helpers.uniq! + helpers + end - def all_helpers_from_path(path) - helpers = [] - Array.wrap(path).each do |_path| - extract = /^#{Regexp.quote(_path.to_s)}\/?(.*)_helper.rb$/ - helpers += Dir["#{_path}/**/*_helper.rb"].map { |file| file.sub(extract, '\1') } - end - helpers.sort! - helpers.uniq! - helpers - end + private + # Extract helper names from files in app/helpers/**/*_helper.rb + def all_application_helpers + all_helpers_from_path(helpers_path) + end end end end -- cgit v1.2.3 From 057412ce38ead06307a887dca333837a99f84f22 Mon Sep 17 00:00:00 2001 From: Chad Krsek Date: Mon, 25 Apr 2011 21:57:28 -0700 Subject: asset helpers should understand scheme-relative URLs --- actionpack/lib/action_view/helpers/asset_paths.rb | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_view/helpers/asset_paths.rb b/actionpack/lib/action_view/helpers/asset_paths.rb index 55a4c442fd..0429e60cd5 100644 --- a/actionpack/lib/action_view/helpers/asset_paths.rb +++ b/actionpack/lib/action_view/helpers/asset_paths.rb @@ -12,13 +12,13 @@ module ActionView @controller = controller end - # Add the extension +ext+ if not present. Return full URLs otherwise untouched. + # Add the extension +ext+ if not present. Return full or scheme-relative URLs otherwise untouched. # Prefix with /dir/ if lacking a leading +/+. Account for relative URL # roots. Rewrite the asset path for cache-busting asset ids. Include # asset host, if configured, with the correct request protocol. def compute_public_path(source, dir, ext = nil, include_host = true) source = source.to_s - return source if is_uri?(source) + return source if is_uri?(source) || is_scheme_relative_uri?(source) source = rewrite_extension(source, dir, ext) if ext source = "/#{dir}/#{source}" unless source[0] == ?/ @@ -36,6 +36,13 @@ module ActionView path =~ %r{^[-a-z]+://|^cid:} end + # A URI relative to a base URI's scheme? + # See http://labs.apache.org/webarch/uri/rfc/rfc3986.html#relative-normal + # "//g" => "http://g" + def is_scheme_relative_uri?(path) + path =~ %r{^//} + end + private def rewrite_extension(source, dir, ext) -- cgit v1.2.3 From 7469041c59724163a9108b037f0965469282b70c Mon Sep 17 00:00:00 2001 From: Chad Krsek Date: Mon, 25 Apr 2011 21:57:49 -0700 Subject: Revert "asset helpers should understand scheme-relative URLs" This reverts commit 057412ce38ead06307a887dca333837a99f84f22. --- actionpack/lib/action_view/helpers/asset_paths.rb | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_view/helpers/asset_paths.rb b/actionpack/lib/action_view/helpers/asset_paths.rb index 0429e60cd5..55a4c442fd 100644 --- a/actionpack/lib/action_view/helpers/asset_paths.rb +++ b/actionpack/lib/action_view/helpers/asset_paths.rb @@ -12,13 +12,13 @@ module ActionView @controller = controller end - # Add the extension +ext+ if not present. Return full or scheme-relative URLs otherwise untouched. + # Add the extension +ext+ if not present. Return full URLs otherwise untouched. # Prefix with /dir/ if lacking a leading +/+. Account for relative URL # roots. Rewrite the asset path for cache-busting asset ids. Include # asset host, if configured, with the correct request protocol. def compute_public_path(source, dir, ext = nil, include_host = true) source = source.to_s - return source if is_uri?(source) || is_scheme_relative_uri?(source) + return source if is_uri?(source) source = rewrite_extension(source, dir, ext) if ext source = "/#{dir}/#{source}" unless source[0] == ?/ @@ -36,13 +36,6 @@ module ActionView path =~ %r{^[-a-z]+://|^cid:} end - # A URI relative to a base URI's scheme? - # See http://labs.apache.org/webarch/uri/rfc/rfc3986.html#relative-normal - # "//g" => "http://g" - def is_scheme_relative_uri?(path) - path =~ %r{^//} - end - private def rewrite_extension(source, dir, ext) -- cgit v1.2.3 From a5b0f7064c0b74f4e007d8536afb8976d10fd49d Mon Sep 17 00:00:00 2001 From: Sven Fuchs Date: Sun, 24 Apr 2011 17:37:57 +0200 Subject: bump the i18n gem to 0.6.0beta1 and update docs for the i18n exception handler [#6739 state:committed] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- actionpack/lib/action_view/helpers/translation_helper.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_view/helpers/translation_helper.rb b/actionpack/lib/action_view/helpers/translation_helper.rb index 59e6ce878f..26ebae6546 100644 --- a/actionpack/lib/action_view/helpers/translation_helper.rb +++ b/actionpack/lib/action_view/helpers/translation_helper.rb @@ -5,7 +5,7 @@ module I18n class ExceptionHandler include Module.new { def call(exception, locale, key, options) - exception.is_a?(MissingTranslationData) ? super.html_safe : super + exception.is_a?(MissingTranslation) ? super.html_safe : super end } end @@ -17,8 +17,8 @@ module ActionView module TranslationHelper # Delegates to I18n#translate but also performs three additional functions. # - # First, it'll pass the :rescue_format => :html option to I18n so that any caught - # MissingTranslationData exceptions will be turned into inline spans that + # First, it'll pass the :rescue_format => :html option to I18n so that any + # thrown MissingTranslation messages will be turned into inline spans that # # * have a "translation-missing" class set, # * contain the missing key as a title attribute and -- cgit v1.2.3 From b49a1192d3d7f2dffff742fc154c8b3d2533b621 Mon Sep 17 00:00:00 2001 From: Chad Krsek Date: Mon, 25 Apr 2011 21:57:28 -0700 Subject: asset helpers should understand scheme-relative URLs --- actionpack/lib/action_view/helpers/asset_paths.rb | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_view/helpers/asset_paths.rb b/actionpack/lib/action_view/helpers/asset_paths.rb index 55a4c442fd..0429e60cd5 100644 --- a/actionpack/lib/action_view/helpers/asset_paths.rb +++ b/actionpack/lib/action_view/helpers/asset_paths.rb @@ -12,13 +12,13 @@ module ActionView @controller = controller end - # Add the extension +ext+ if not present. Return full URLs otherwise untouched. + # Add the extension +ext+ if not present. Return full or scheme-relative URLs otherwise untouched. # Prefix with /dir/ if lacking a leading +/+. Account for relative URL # roots. Rewrite the asset path for cache-busting asset ids. Include # asset host, if configured, with the correct request protocol. def compute_public_path(source, dir, ext = nil, include_host = true) source = source.to_s - return source if is_uri?(source) + return source if is_uri?(source) || is_scheme_relative_uri?(source) source = rewrite_extension(source, dir, ext) if ext source = "/#{dir}/#{source}" unless source[0] == ?/ @@ -36,6 +36,13 @@ module ActionView path =~ %r{^[-a-z]+://|^cid:} end + # A URI relative to a base URI's scheme? + # See http://labs.apache.org/webarch/uri/rfc/rfc3986.html#relative-normal + # "//g" => "http://g" + def is_scheme_relative_uri?(path) + path =~ %r{^//} + end + private def rewrite_extension(source, dir, ext) -- cgit v1.2.3 From c15108ce063f6396a6e93a54e973b858d0280d41 Mon Sep 17 00:00:00 2001 From: Chad Krsek Date: Tue, 26 Apr 2011 11:50:08 -0700 Subject: moving check fo scheme-relative URI into is_uri? --- actionpack/lib/action_view/helpers/asset_paths.rb | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_view/helpers/asset_paths.rb b/actionpack/lib/action_view/helpers/asset_paths.rb index 0429e60cd5..cb6737b94e 100644 --- a/actionpack/lib/action_view/helpers/asset_paths.rb +++ b/actionpack/lib/action_view/helpers/asset_paths.rb @@ -18,7 +18,7 @@ module ActionView # asset host, if configured, with the correct request protocol. def compute_public_path(source, dir, ext = nil, include_host = true) source = source.to_s - return source if is_uri?(source) || is_scheme_relative_uri?(source) + return source if is_uri?(source) source = rewrite_extension(source, dir, ext) if ext source = "/#{dir}/#{source}" unless source[0] == ?/ @@ -33,14 +33,7 @@ module ActionView end def is_uri?(path) - path =~ %r{^[-a-z]+://|^cid:} - end - - # A URI relative to a base URI's scheme? - # See http://labs.apache.org/webarch/uri/rfc/rfc3986.html#relative-normal - # "//g" => "http://g" - def is_scheme_relative_uri?(path) - path =~ %r{^//} + path =~ %r{^[-a-z]+://|^cid:|^//} end private -- cgit v1.2.3 From 9f6cafd5fd43b551f30b28d276713791c5098b3c Mon Sep 17 00:00:00 2001 From: misfo Date: Tue, 8 Feb 2011 12:08:35 +0800 Subject: prevent errors when passing a frozen string as a param to ActionController::TestCase#process since ActionDispatch::Http::Parameters#encode_params will force encoding on all params strings (when using an encoding aware Ruby), dup all strings passed into process. This prevents modification of params passed in and, more importantly, doesn't barf when a frozen string is passed thanks and high fives to kinsteronline --- actionpack/lib/action_controller/test_case.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb index bc4f8bb9ce..0085f542aa 100644 --- a/actionpack/lib/action_controller/test_case.rb +++ b/actionpack/lib/action_controller/test_case.rb @@ -147,7 +147,9 @@ module ActionController if value.is_a? Fixnum value = value.to_s elsif value.is_a? Array - value = Result.new(value) + value = Result.new(value.map { |v| v.is_a?(String) ? v.dup : v }) + elsif value.is_a? String + value = value.dup end if extra_keys.include?(key.to_sym) -- cgit v1.2.3 From 07bbaaa3b14d7048a3e43851aed199b8b515a820 Mon Sep 17 00:00:00 2001 From: Diego Carrion Date: Wed, 27 Apr 2011 20:29:03 -0300 Subject: added a :prefix option to number_to_human_size --- actionpack/lib/action_view/helpers/number_helper.rb | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_view/helpers/number_helper.rb b/actionpack/lib/action_view/helpers/number_helper.rb index b545031fcc..63d13a0f0b 100644 --- a/actionpack/lib/action_view/helpers/number_helper.rb +++ b/actionpack/lib/action_view/helpers/number_helper.rb @@ -304,6 +304,7 @@ module ActionView # * :separator - Sets the separator between the fractional and integer digits (defaults to "."). # * :delimiter - Sets the thousands delimiter (defaults to ""). # * :strip_insignificant_zeros - If +true+ removes insignificant zeros after the decimal separator (defaults to +true+) + # * :prefix - If +:si+ formats the number using the SI prefix (defaults to :binary) # ==== Examples # number_to_human_size(123) # => 123 Bytes # number_to_human_size(1234) # => 1.21 KB @@ -341,15 +342,17 @@ module ActionView options[:strip_insignificant_zeros] = true if not options.key?(:strip_insignificant_zeros) storage_units_format = I18n.translate(:'number.human.storage_units.format', :locale => options[:locale], :raise => true) + + base = options[:prefix] == :si ? 1000 : 1024 - if number.to_i < 1024 + if number.to_i < base unit = I18n.translate(:'number.human.storage_units.units.byte', :locale => options[:locale], :count => number.to_i, :raise => true) storage_units_format.gsub(/%n/, number.to_i.to_s).gsub(/%u/, unit).html_safe else max_exp = STORAGE_UNITS.size - 1 - exponent = (Math.log(number) / Math.log(1024)).to_i # Convert to base 1024 + exponent = (Math.log(number) / Math.log(base)).to_i # Convert to base exponent = max_exp if exponent > max_exp # we need this to avoid overflow for the highest unit - number /= 1024 ** exponent + number /= base ** exponent unit_key = STORAGE_UNITS[exponent] unit = I18n.translate(:"number.human.storage_units.units.#{unit_key}", :locale => options[:locale], :count => number, :raise => true) -- cgit v1.2.3 From a07cee62c7d23ec587ef961beb35ce3ac63df340 Mon Sep 17 00:00:00 2001 From: Jason Garber Date: Wed, 27 Apr 2011 21:00:27 -0600 Subject: Allow a label with the value option to have value-specific translations. --- actionpack/lib/action_view/helpers/form_helper.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index 440acafa88..efe30441b1 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -947,7 +947,8 @@ module ActionView label_tag(name_and_id["id"], options, &block) else content = if text.blank? - I18n.t("helpers.label.#{object_name}.#{method_name}", :default => "").presence + method_and_value = tag_value.present? ? "#{method_name}.#{tag_value}" : method_name + I18n.t("helpers.label.#{object_name}.#{method_and_value}", :default => "").presence else text.to_s end -- cgit v1.2.3 From a869382a9fa241f72a7a73d4a9738531c4c37ba5 Mon Sep 17 00:00:00 2001 From: Aditya Sanghi Date: Fri, 29 Apr 2011 01:49:45 +0530 Subject: Allow AM/PM in datetime selectors --- actionpack/lib/action_view/helpers/date_helper.rb | 35 +++++++++++++++++++++++ 1 file changed, 35 insertions(+) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb index 9277359d5c..7e8ad54d5f 100644 --- a/actionpack/lib/action_view/helpers/date_helper.rb +++ b/actionpack/lib/action_view/helpers/date_helper.rb @@ -468,6 +468,9 @@ module ActionView # # generic prompt. # select_hour(13, :prompt => 'Choose hour') # + # # Generate a select field for hours in the AM/PM format + # select_hour(my_time, :ampm => true) + # def select_hour(datetime, options = {}, html_options = {}) DateTimeSelector.new(datetime, options, html_options).select_hour end @@ -600,6 +603,18 @@ module ActionView POSITION = { :year => 1, :month => 2, :day => 3, :hour => 4, :minute => 5, :second => 6 }.freeze + ampm = ["AM","PM"].map do |s| + ["12 #{s}"] + (1..11).map{|x| "#{sprintf('%02d',x)} #{s}"} + end.flatten + + AMPM_TRANSLATION = Hash[ + [[0, "12 AM"], [1, "01 AM"], [2, "02 AM"], [3, "03 AM"], + [4, "04 AM"], [5, "05 AM"], [6, "06 AM"], [7, "07 AM"], + [8, "08 AM"], [9, "09 AM"], [10, "10 AM"], [11, "11 AM"], + [12, "12 PM"], [13, "01 PM"], [14, "02 PM"], [15, "03 PM"], + [16, "04 PM"], [17, "05 PM"], [18, "06 PM"], [19, "07 PM"], + [20, "08 PM"], [21, "09 PM"], [22, "10 PM"], [23, "11 PM"]] + ].freeze def initialize(datetime, options = {}, html_options = {}) @options = options.dup @@ -691,6 +706,8 @@ module ActionView def select_hour if @options[:use_hidden] || @options[:discard_hour] build_hidden(:hour, hour) + elsif @options[:ampm] + build_select(:hour, build_ampm_options(hour, :end => 23)) else build_options_and_select(:hour, hour, :end => 23) end @@ -801,6 +818,24 @@ module ActionView build_select(type, build_options(selected, options)) end + def build_ampm_options(selected, options = {}) + start = options.delete(:start) || 0 + stop = options.delete(:end) || 23 + step = options.delete(:step) || 1 + options.reverse_merge!({:leading_zeros => true}) + leading_zeros = options.delete(:leading_zeros) + + select_options = [] + start.step(stop, step) do |i| + text = AMPM_TRANSLATION[i] + value = leading_zeros ? sprintf("%02d", i) : i + tag_options = { :value => value } + tag_options[:selected] = "selected" if selected == i + select_options << content_tag(:option, text, tag_options) + end + (select_options.join("\n") + "\n").html_safe + end + # Build select option html from date value and options # build_options(15, :start => 1, :end => 31) # => " -- cgit v1.2.3 From 610e4d9f24b8fe0c372104481a733b48d2270c3f Mon Sep 17 00:00:00 2001 From: Aditya Sanghi Date: Fri, 29 Apr 2011 02:03:56 +0530 Subject: add more documentation; remove unused assignment --- actionpack/lib/action_view/helpers/date_helper.rb | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb index 7e8ad54d5f..9e1be05e6c 100644 --- a/actionpack/lib/action_view/helpers/date_helper.rb +++ b/actionpack/lib/action_view/helpers/date_helper.rb @@ -207,7 +207,8 @@ module ActionView # Returns a set of select tags (one for hour, minute and optionally second) pre-selected for accessing a # specified time-based attribute (identified by +method+) on an object assigned to the template (identified by - # +object+). You can include the seconds with :include_seconds. + # +object+). You can include the seconds with :include_seconds. You can get hours in the AM/PM format + # with :ampm option. # # This method will also generate 3 input hidden tags, for the actual year, month and day unless the option # :ignore_date is set to +true+. @@ -230,6 +231,9 @@ module ActionView # time_select("post", "written_on", :prompt => {:hour => true}) # generic prompt for hours # time_select("post", "written_on", :prompt => true) # generic prompts for all # + # # You can set :ampm option to true which will show the hours as: 12 PM, 01 AM .. 11 PM. + # time_select 'game', 'game_time', {:ampm => true} + # # The selects are prepared for multi-parameter assignment to an Active Record object. # # Note: If the day is not included as an option but the month is, the day will be set to the 1st to ensure that @@ -257,6 +261,9 @@ module ActionView # # be stored in the trip variable in the departing attribute. # datetime_select("trip", "departing", :default => 3.days.from_now) # + # # Generate a datetime select with hours in the AM/PM format + # datetime_select("post", "written_on", :ampm => true) + # # # Generates a datetime select that discards the type that, when POSTed, will be stored in the post variable # # as the written_on attribute. # datetime_select("post", "written_on", :discard_type => true) @@ -306,6 +313,9 @@ module ActionView # # my_date_time (four days after today) # select_datetime(my_date_time, :discard_type => true) # + # # Generate a datetime field with hours in the AM/PM format + # select_datetime(my_date_time, :ampm => true) + # # # Generates a datetime select that defaults to the datetime in my_date_time (four days after today) # # prefixed with 'payday' rather than 'date' # select_datetime(my_date_time, :prefix => 'payday') @@ -387,6 +397,9 @@ module ActionView # # separated by ':' and includes an input for seconds # select_time(my_time, :time_separator => ':', :include_seconds => true) # + # # Generate a time select field with hours in the AM/PM format + # select_time(my_time, :ampm => true) + # # # Generates a time select with a custom prompt. Use :prompt=>true for generic prompts. # select_time(my_time, :prompt => {:day => 'Choose day', :month => 'Choose month', :year => 'Choose year'}) # select_time(my_time, :prompt => {:hour => true}) # generic prompt for hours @@ -603,9 +616,6 @@ module ActionView POSITION = { :year => 1, :month => 2, :day => 3, :hour => 4, :minute => 5, :second => 6 }.freeze - ampm = ["AM","PM"].map do |s| - ["12 #{s}"] + (1..11).map{|x| "#{sprintf('%02d',x)} #{s}"} - end.flatten AMPM_TRANSLATION = Hash[ [[0, "12 AM"], [1, "01 AM"], [2, "02 AM"], [3, "03 AM"], -- cgit v1.2.3 From 8bce6e761d52afd68bb06ca9ff8222f072e06cc8 Mon Sep 17 00:00:00 2001 From: Aditya Sanghi Date: Fri, 29 Apr 2011 10:21:16 +0530 Subject: DRY this baby up --- actionpack/lib/action_view/helpers/date_helper.rb | 27 ++++------------------- 1 file changed, 4 insertions(+), 23 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb index 9e1be05e6c..080cf0a866 100644 --- a/actionpack/lib/action_view/helpers/date_helper.rb +++ b/actionpack/lib/action_view/helpers/date_helper.rb @@ -716,10 +716,8 @@ module ActionView def select_hour if @options[:use_hidden] || @options[:discard_hour] build_hidden(:hour, hour) - elsif @options[:ampm] - build_select(:hour, build_ampm_options(hour, :end => 23)) else - build_options_and_select(:hour, hour, :end => 23) + build_options_and_select(:hour, hour, :end => 23, :ampm => @options[:ampm]) end end @@ -828,24 +826,6 @@ module ActionView build_select(type, build_options(selected, options)) end - def build_ampm_options(selected, options = {}) - start = options.delete(:start) || 0 - stop = options.delete(:end) || 23 - step = options.delete(:step) || 1 - options.reverse_merge!({:leading_zeros => true}) - leading_zeros = options.delete(:leading_zeros) - - select_options = [] - start.step(stop, step) do |i| - text = AMPM_TRANSLATION[i] - value = leading_zeros ? sprintf("%02d", i) : i - tag_options = { :value => value } - tag_options[:selected] = "selected" if selected == i - select_options << content_tag(:option, text, tag_options) - end - (select_options.join("\n") + "\n").html_safe - end - # Build select option html from date value and options # build_options(15, :start => 1, :end => 31) # => " @@ -855,7 +835,7 @@ module ActionView start = options.delete(:start) || 0 stop = options.delete(:end) || 59 step = options.delete(:step) || 1 - options.reverse_merge!({:leading_zeros => true}) + options.reverse_merge!({:leading_zeros => true, :ampm => false}) leading_zeros = options.delete(:leading_zeros) select_options = [] @@ -863,7 +843,8 @@ module ActionView value = leading_zeros ? sprintf("%02d", i) : i tag_options = { :value => value } tag_options[:selected] = "selected" if selected == i - select_options << content_tag(:option, value, tag_options) + text = options[:ampm] ? AMPM_TRANSLATION[i] : value + select_options << content_tag(:option, text, tag_options) end (select_options.join("\n") + "\n").html_safe end -- cgit v1.2.3 From b2aacc346c8e975cd9ab8c1b0a6b90adbb98debc Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Sat, 30 Apr 2011 03:52:06 +0200 Subject: Prototype and Scriptaculous are no longer vendored, but provided by prototype-rails from now on, also the -j option of the application generator is removed --- .../helpers/asset_tag_helpers/javascript_tag_helpers.rb | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb b/actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb index 3d815b5e1f..e1ee0d0e1a 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb @@ -99,10 +99,9 @@ module ActionView # # When passing paths, the ".js" extension is optional. # - # To include the default JavaScript expansion pass :defaults as source. - # By default, :defaults loads jQuery. If the application was generated - # with "-j prototype" the libraries Prototype and Scriptaculous are loaded instead. - # In any case, the defaults can be overridden in config/application.rb: + # If the application is not using the asset pipeline, to include the default JavaScript + # expansion pass :defaults as source. By default, :defaults loads jQuery, + # and that can be overridden in config/application.rb: # # config.action_view.javascript_expansions[:defaults] = %w(foo.js bar.js) # -- cgit v1.2.3 From d08f65118cc328de5493a68db33a155487f5fceb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sun, 1 May 2011 10:33:30 +0200 Subject: Start abstracting the renderer. --- actionpack/lib/action_view.rb | 2 +- actionpack/lib/action_view/base.rb | 5 +- actionpack/lib/action_view/context.rb | 6 +- actionpack/lib/action_view/helpers/form_helper.rb | 5 + actionpack/lib/action_view/partials.rb | 226 --------------------- .../lib/action_view/renderer/abstract_renderer.rb | 4 +- .../lib/action_view/renderer/partial_renderer.rb | 217 +++++++++++++++++++- actionpack/lib/action_view/renderer/renderer.rb | 70 +++++++ actionpack/lib/action_view/rendering.rb | 46 +---- 9 files changed, 301 insertions(+), 280 deletions(-) delete mode 100644 actionpack/lib/action_view/partials.rb create mode 100644 actionpack/lib/action_view/renderer/renderer.rb (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_view.rb b/actionpack/lib/action_view.rb index 4547aceb28..69c50a056c 100644 --- a/actionpack/lib/action_view.rb +++ b/actionpack/lib/action_view.rb @@ -34,13 +34,13 @@ module ActionView autoload :Context autoload :Helpers autoload :LookupContext - autoload :Partials autoload :PathSet autoload :Rendering autoload :Template autoload :TestCase autoload_under "renderer" do + autoload :Renderer autoload :AbstractRenderer autoload :PartialRenderer autoload :TemplateRenderer diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb index 87501d5b88..c1dbbe1613 100644 --- a/actionpack/lib/action_view/base.rb +++ b/actionpack/lib/action_view/base.rb @@ -131,7 +131,7 @@ module ActionView #:nodoc: # # More builder documentation can be found at http://builder.rubyforge.org. class Base - include Helpers, Rendering, Partials, ::ERB::Util, Context + include Helpers, Rendering, ::ERB::Util, Context # Specify the proc used to decorate input tags that refer to attributes with errors. cattr_accessor :field_error_proc @@ -162,6 +162,7 @@ module ActionView #:nodoc: attr_accessor :_template, :_view_flow attr_internal :request, :controller, :config, :assigns, :lookup_context + # TODO Consider removing those setters once we have the renderer in place. delegate :formats, :formats=, :locale, :locale=, :view_paths, :view_paths=, :to => :lookup_context delegate :request_forgery_protection_token, :params, :session, :cookies, :response, :headers, @@ -199,6 +200,8 @@ module ActionView #:nodoc: @_lookup_context = lookup_context.is_a?(ActionView::LookupContext) ? lookup_context : ActionView::LookupContext.new(lookup_context) @_lookup_context.formats = formats if formats + + @_renderer = ActionView::Renderer.new(@_lookup_context, self) end def controller_path diff --git a/actionpack/lib/action_view/context.rb b/actionpack/lib/action_view/context.rb index a2a64de206..710cdc613d 100644 --- a/actionpack/lib/action_view/context.rb +++ b/actionpack/lib/action_view/context.rb @@ -31,10 +31,6 @@ module ActionView # template and provides the output buffer. module Context include CompiledTemplates - attr_accessor :output_buffer - - def convert_to_model(object) - object.respond_to?(:to_model) ? object.to_model : object - end + attr_accessor :output_buffer, :view_renderer, :view_flow end end \ No newline at end of file diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index efe30441b1..68db17c254 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -102,6 +102,11 @@ module ActionView include FormTagHelper include UrlHelper + # Converts the given object to an ActiveModel compliant one. + def convert_to_model(object) + object.respond_to?(:to_model) ? object.to_model : object + end + # Creates a form and a scope around a specific model object that is used # as a base for questioning about values for the fields. # diff --git a/actionpack/lib/action_view/partials.rb b/actionpack/lib/action_view/partials.rb deleted file mode 100644 index c181689e62..0000000000 --- a/actionpack/lib/action_view/partials.rb +++ /dev/null @@ -1,226 +0,0 @@ -require 'active_support/core_ext/object/blank' - -module ActionView - # = Action View Partials - # - # There's also a convenience method for rendering sub templates within the current controller that depends on a - # single object (we call this kind of sub templates for partials). It relies on the fact that partials should - # follow the naming convention of being prefixed with an underscore -- as to separate them from regular - # templates that could be rendered on their own. - # - # In a template for Advertiser#account: - # - # <%= render :partial => "account" %> - # - # This would render "advertiser/_account.html.erb" and pass the instance variable @account in as a local variable - # +account+ to the template for display. - # - # In another template for Advertiser#buy, we could have: - # - # <%= render :partial => "account", :locals => { :account => @buyer } %> - # - # <% @advertisements.each do |ad| %> - # <%= render :partial => "ad", :locals => { :ad => ad } %> - # <% end %> - # - # This would first render "advertiser/_account.html.erb" with @buyer passed in as the local variable +account+, then - # render "advertiser/_ad.html.erb" and pass the local variable +ad+ to the template for display. - # - # == The :as and :object options - # - # By default ActionView::Partials::PartialRenderer has its object in a local variable with the same - # name as the template. So, given - # - # <%= render :partial => "contract" %> - # - # within contract we'll get @contract in the local variable +contract+, as if we had written - # - # <%= render :partial => "contract", :locals => { :contract => @contract } %> - # - # With the :as option we can specify a different name for said local variable. For example, if we - # wanted it to be +agreement+ instead of +contract+ we'd do: - # - # <%= render :partial => "contract", :as => 'agreement' %> - # - # The :object option can be used to directly specify which object is rendered into the partial; - # useful when the template's object is elsewhere, in a different ivar or in a local variable for instance. - # - # Revisiting a previous example we could have written this code: - # - # <%= render :partial => "account", :object => @buyer %> - # - # <% @advertisements.each do |ad| %> - # <%= render :partial => "ad", :object => ad %> - # <% end %> - # - # The :object and :as options can be used together. - # - # == Rendering a collection of partials - # - # The example of partial use describes a familiar pattern where a template needs to iterate over an array and - # render a sub template for each of the elements. This pattern has been implemented as a single method that - # accepts an array and renders a partial by the same name as the elements contained within. So the three-lined - # example in "Using partials" can be rewritten with a single line: - # - # <%= render :partial => "ad", :collection => @advertisements %> - # - # This will render "advertiser/_ad.html.erb" and pass the local variable +ad+ to the template for display. An - # iteration counter will automatically be made available to the template with a name of the form - # +partial_name_counter+. In the case of the example above, the template would be fed +ad_counter+. - # - # The :as option may be used when rendering partials. - # - # You can specify a partial to be rendered between elements via the :spacer_template option. - # The following example will render advertiser/_ad_divider.html.erb between each ad partial: - # - # <%= render :partial => "ad", :collection => @advertisements, :spacer_template => "ad_divider" %> - # - # If the given :collection is nil or empty, render will return nil. This will allow you - # to specify a text which will displayed instead by using this form: - # - # <%= render(:partial => "ad", :collection => @advertisements) || "There's no ad to be displayed" %> - # - # NOTE: Due to backwards compatibility concerns, the collection can't be one of hashes. Normally you'd also - # just keep domain objects, like Active Records, in there. - # - # == Rendering shared partials - # - # Two controllers can share a set of partials and render them like this: - # - # <%= render :partial => "advertisement/ad", :locals => { :ad => @advertisement } %> - # - # This will render the partial "advertisement/_ad.html.erb" regardless of which controller this is being called from. - # - # == Rendering objects with the RecordIdentifier - # - # Instead of explicitly naming the location of a partial, you can also let the RecordIdentifier do the work if - # you're following its conventions for RecordIdentifier#partial_path. Examples: - # - # # @account is an Account instance, so it uses the RecordIdentifier to replace - # # <%= render :partial => "accounts/account", :locals => { :account => @account} %> - # <%= render :partial => @account %> - # - # # @posts is an array of Post instances, so it uses the RecordIdentifier to replace - # # <%= render :partial => "posts/post", :collection => @posts %> - # <%= render :partial => @posts %> - # - # == Rendering the default case - # - # If you're not going to be using any of the options like collections or layouts, you can also use the short-hand - # defaults of render to render partials. Examples: - # - # # Instead of <%= render :partial => "account" %> - # <%= render "account" %> - # - # # Instead of <%= render :partial => "account", :locals => { :account => @buyer } %> - # <%= render "account", :account => @buyer %> - # - # # @account is an Account instance, so it uses the RecordIdentifier to replace - # # <%= render :partial => "accounts/account", :locals => { :account => @account } %> - # <%= render(@account) %> - # - # # @posts is an array of Post instances, so it uses the RecordIdentifier to replace - # # <%= render :partial => "posts/post", :collection => @posts %> - # <%= render(@posts) %> - # - # == Rendering partials with layouts - # - # Partials can have their own layouts applied to them. These layouts are different than the ones that are - # specified globally for the entire action, but they work in a similar fashion. Imagine a list with two types - # of users: - # - # <%# app/views/users/index.html.erb &> - # Here's the administrator: - # <%= render :partial => "user", :layout => "administrator", :locals => { :user => administrator } %> - # - # Here's the editor: - # <%= render :partial => "user", :layout => "editor", :locals => { :user => editor } %> - # - # <%# app/views/users/_user.html.erb &> - # Name: <%= user.name %> - # - # <%# app/views/users/_administrator.html.erb &> - #
- # Budget: $<%= user.budget %> - # <%= yield %> - #
- # - # <%# app/views/users/_editor.html.erb &> - #
- # Deadline: <%= user.deadline %> - # <%= yield %> - #
- # - # ...this will return: - # - # Here's the administrator: - #
- # Budget: $<%= user.budget %> - # Name: <%= user.name %> - #
- # - # Here's the editor: - #
- # Deadline: <%= user.deadline %> - # Name: <%= user.name %> - #
- # - # You can also apply a layout to a block within any template: - # - # <%# app/views/users/_chief.html.erb &> - # <%= render(:layout => "administrator", :locals => { :user => chief }) do %> - # Title: <%= chief.title %> - # <% end %> - # - # ...this will return: - # - #
- # Budget: $<%= user.budget %> - # Title: <%= chief.name %> - #
- # - # As you can see, the :locals hash is shared between both the partial and its layout. - # - # If you pass arguments to "yield" then this will be passed to the block. One way to use this is to pass - # an array to layout and treat it as an enumerable. - # - # <%# app/views/users/_user.html.erb &> - #
- # Budget: $<%= user.budget %> - # <%= yield user %> - #
- # - # <%# app/views/users/index.html.erb &> - # <%= render :layout => @users do |user| %> - # Title: <%= user.title %> - # <% end %> - # - # This will render the layout for each user and yield to the block, passing the user, each time. - # - # You can also yield multiple times in one layout and use block arguments to differentiate the sections. - # - # <%# app/views/users/_user.html.erb &> - #
- # <%= yield user, :header %> - # Budget: $<%= user.budget %> - # <%= yield user, :footer %> - #
- # - # <%# app/views/users/index.html.erb &> - # <%= render :layout => @users do |user, section| %> - # <%- case section when :header -%> - # Title: <%= user.title %> - # <%- when :footer -%> - # Deadline: <%= user.deadline %> - # <%- end -%> - # <% end %> - module Partials - def _render_partial(options, &block) #:nodoc: - _partial_renderer.setup(options, block).render - end - - def _partial_renderer #:nodoc: - @_partial_renderer ||= PartialRenderer.new(self) - end - end -end diff --git a/actionpack/lib/action_view/renderer/abstract_renderer.rb b/actionpack/lib/action_view/renderer/abstract_renderer.rb index 4a52b3172e..37bc0ae244 100644 --- a/actionpack/lib/action_view/renderer/abstract_renderer.rb +++ b/actionpack/lib/action_view/renderer/abstract_renderer.rb @@ -3,9 +3,9 @@ module ActionView delegate :find_template, :template_exists?, :with_fallbacks, :update_details, :with_layout_format, :formats, :freeze_formats, :to => :@lookup_context - def initialize(view) + def initialize(view, lookup_context) @view = view - @lookup_context = view.lookup_context + @lookup_context = lookup_context end def render diff --git a/actionpack/lib/action_view/renderer/partial_renderer.rb b/actionpack/lib/action_view/renderer/partial_renderer.rb index 10cd37d56f..83efc95f39 100644 --- a/actionpack/lib/action_view/renderer/partial_renderer.rb +++ b/actionpack/lib/action_view/renderer/partial_renderer.rb @@ -1,8 +1,223 @@ +require 'active_support/core_ext/object/blank' + module ActionView + # = Action View Partials + # + # There's also a convenience method for rendering sub templates within the current controller that depends on a + # single object (we call this kind of sub templates for partials). It relies on the fact that partials should + # follow the naming convention of being prefixed with an underscore -- as to separate them from regular + # templates that could be rendered on their own. + # + # In a template for Advertiser#account: + # + # <%= render :partial => "account" %> + # + # This would render "advertiser/_account.html.erb" and pass the instance variable @account in as a local variable + # +account+ to the template for display. + # + # In another template for Advertiser#buy, we could have: + # + # <%= render :partial => "account", :locals => { :account => @buyer } %> + # + # <% @advertisements.each do |ad| %> + # <%= render :partial => "ad", :locals => { :ad => ad } %> + # <% end %> + # + # This would first render "advertiser/_account.html.erb" with @buyer passed in as the local variable +account+, then + # render "advertiser/_ad.html.erb" and pass the local variable +ad+ to the template for display. + # + # == The :as and :object options + # + # By default ActionView::Partials::PartialRenderer has its object in a local variable with the same + # name as the template. So, given + # + # <%= render :partial => "contract" %> + # + # within contract we'll get @contract in the local variable +contract+, as if we had written + # + # <%= render :partial => "contract", :locals => { :contract => @contract } %> + # + # With the :as option we can specify a different name for said local variable. For example, if we + # wanted it to be +agreement+ instead of +contract+ we'd do: + # + # <%= render :partial => "contract", :as => 'agreement' %> + # + # The :object option can be used to directly specify which object is rendered into the partial; + # useful when the template's object is elsewhere, in a different ivar or in a local variable for instance. + # + # Revisiting a previous example we could have written this code: + # + # <%= render :partial => "account", :object => @buyer %> + # + # <% @advertisements.each do |ad| %> + # <%= render :partial => "ad", :object => ad %> + # <% end %> + # + # The :object and :as options can be used together. + # + # == Rendering a collection of partials + # + # The example of partial use describes a familiar pattern where a template needs to iterate over an array and + # render a sub template for each of the elements. This pattern has been implemented as a single method that + # accepts an array and renders a partial by the same name as the elements contained within. So the three-lined + # example in "Using partials" can be rewritten with a single line: + # + # <%= render :partial => "ad", :collection => @advertisements %> + # + # This will render "advertiser/_ad.html.erb" and pass the local variable +ad+ to the template for display. An + # iteration counter will automatically be made available to the template with a name of the form + # +partial_name_counter+. In the case of the example above, the template would be fed +ad_counter+. + # + # The :as option may be used when rendering partials. + # + # You can specify a partial to be rendered between elements via the :spacer_template option. + # The following example will render advertiser/_ad_divider.html.erb between each ad partial: + # + # <%= render :partial => "ad", :collection => @advertisements, :spacer_template => "ad_divider" %> + # + # If the given :collection is nil or empty, render will return nil. This will allow you + # to specify a text which will displayed instead by using this form: + # + # <%= render(:partial => "ad", :collection => @advertisements) || "There's no ad to be displayed" %> + # + # NOTE: Due to backwards compatibility concerns, the collection can't be one of hashes. Normally you'd also + # just keep domain objects, like Active Records, in there. + # + # == Rendering shared partials + # + # Two controllers can share a set of partials and render them like this: + # + # <%= render :partial => "advertisement/ad", :locals => { :ad => @advertisement } %> + # + # This will render the partial "advertisement/_ad.html.erb" regardless of which controller this is being called from. + # + # == Rendering objects with the RecordIdentifier + # + # Instead of explicitly naming the location of a partial, you can also let the RecordIdentifier do the work if + # you're following its conventions for RecordIdentifier#partial_path. Examples: + # + # # @account is an Account instance, so it uses the RecordIdentifier to replace + # # <%= render :partial => "accounts/account", :locals => { :account => @account} %> + # <%= render :partial => @account %> + # + # # @posts is an array of Post instances, so it uses the RecordIdentifier to replace + # # <%= render :partial => "posts/post", :collection => @posts %> + # <%= render :partial => @posts %> + # + # == Rendering the default case + # + # If you're not going to be using any of the options like collections or layouts, you can also use the short-hand + # defaults of render to render partials. Examples: + # + # # Instead of <%= render :partial => "account" %> + # <%= render "account" %> + # + # # Instead of <%= render :partial => "account", :locals => { :account => @buyer } %> + # <%= render "account", :account => @buyer %> + # + # # @account is an Account instance, so it uses the RecordIdentifier to replace + # # <%= render :partial => "accounts/account", :locals => { :account => @account } %> + # <%= render(@account) %> + # + # # @posts is an array of Post instances, so it uses the RecordIdentifier to replace + # # <%= render :partial => "posts/post", :collection => @posts %> + # <%= render(@posts) %> + # + # == Rendering partials with layouts + # + # Partials can have their own layouts applied to them. These layouts are different than the ones that are + # specified globally for the entire action, but they work in a similar fashion. Imagine a list with two types + # of users: + # + # <%# app/views/users/index.html.erb &> + # Here's the administrator: + # <%= render :partial => "user", :layout => "administrator", :locals => { :user => administrator } %> + # + # Here's the editor: + # <%= render :partial => "user", :layout => "editor", :locals => { :user => editor } %> + # + # <%# app/views/users/_user.html.erb &> + # Name: <%= user.name %> + # + # <%# app/views/users/_administrator.html.erb &> + #
+ # Budget: $<%= user.budget %> + # <%= yield %> + #
+ # + # <%# app/views/users/_editor.html.erb &> + #
+ # Deadline: <%= user.deadline %> + # <%= yield %> + #
+ # + # ...this will return: + # + # Here's the administrator: + #
+ # Budget: $<%= user.budget %> + # Name: <%= user.name %> + #
+ # + # Here's the editor: + #
+ # Deadline: <%= user.deadline %> + # Name: <%= user.name %> + #
+ # + # You can also apply a layout to a block within any template: + # + # <%# app/views/users/_chief.html.erb &> + # <%= render(:layout => "administrator", :locals => { :user => chief }) do %> + # Title: <%= chief.title %> + # <% end %> + # + # ...this will return: + # + #
+ # Budget: $<%= user.budget %> + # Title: <%= chief.name %> + #
+ # + # As you can see, the :locals hash is shared between both the partial and its layout. + # + # If you pass arguments to "yield" then this will be passed to the block. One way to use this is to pass + # an array to layout and treat it as an enumerable. + # + # <%# app/views/users/_user.html.erb &> + #
+ # Budget: $<%= user.budget %> + # <%= yield user %> + #
+ # + # <%# app/views/users/index.html.erb &> + # <%= render :layout => @users do |user| %> + # Title: <%= user.title %> + # <% end %> + # + # This will render the layout for each user and yield to the block, passing the user, each time. + # + # You can also yield multiple times in one layout and use block arguments to differentiate the sections. + # + # <%# app/views/users/_user.html.erb &> + #
+ # <%= yield user, :header %> + # Budget: $<%= user.budget %> + # <%= yield user, :footer %> + #
+ # + # <%# app/views/users/index.html.erb &> + # <%= render :layout => @users do |user, section| %> + # <%- case section when :header -%> + # Title: <%= user.title %> + # <%- when :footer -%> + # Deadline: <%= user.deadline %> + # <%- end -%> + # <% end %> class PartialRenderer < AbstractRenderer #:nodoc: PARTIAL_NAMES = Hash.new {|h,k| h[k] = {} } - def initialize(view) + def initialize(view, *) super @partial_names = PARTIAL_NAMES[@view.controller.class.name] end diff --git a/actionpack/lib/action_view/renderer/renderer.rb b/actionpack/lib/action_view/renderer/renderer.rb new file mode 100644 index 0000000000..f0ee103d80 --- /dev/null +++ b/actionpack/lib/action_view/renderer/renderer.rb @@ -0,0 +1,70 @@ +module ActionView + # This is the main entry point for rendering. It basically delegates + # to other objects like TemplateRenderer and PartialRenderer which + # actually renders the template. + class Renderer + attr_accessor :lookup_context + + # TODO: render_context should not be an initialization parameter + def initialize(lookup_context, render_context) + @render_context = render_context + @lookup_context = lookup_context + @view_flow = OutputFlow.new + end + + # Returns the result of a render that's dictated by the options hash. The primary options are: + # + # * :partial - See ActionView::Partials. + # * :file - Renders an explicit template file (this used to be the old default), add :locals to pass in those. + # * :inline - Renders an inline template similar to how it's done in the controller. + # * :text - Renders the text passed in out. + # + # If no options hash is passed or :update specified, the default is to render a partial and use the second parameter + # as the locals hash. + def render(options = {}, locals = {}, &block) + case options + when Hash + if block_given? + _render_partial(options.merge(:partial => options[:layout]), &block) + elsif options.key?(:partial) + _render_partial(options) + else + _render_template(options) + end + else + _render_partial(:partial => options, :locals => locals) + end + end + + # Render but returns a valid Rack body. If fibers are defined, we return + # a streaming body that renders the template piece by piece. + # + # Note that partials are not supported to be rendered with streaming, + # so in such cases, we just wrap them in an array. + def render_body(options) + if options.key?(:partial) + [_render_partial(options)] + else + StreamingTemplateRenderer.new(@render_context, @lookup_context).render(options) + end + end + + private + + def _render_template(options) #:nodoc: + _template_renderer.render(options) + end + + def _template_renderer #:nodoc: + @_template_renderer ||= TemplateRenderer.new(@render_context, @lookup_context) + end + + def _render_partial(options, &block) #:nodoc: + _partial_renderer.setup(options, block).render + end + + def _partial_renderer #:nodoc: + @_partial_renderer ||= PartialRenderer.new(@render_context, @lookup_context) + end + end +end \ No newline at end of file diff --git a/actionpack/lib/action_view/rendering.rb b/actionpack/lib/action_view/rendering.rb index 2bce2fb045..017a27976c 100644 --- a/actionpack/lib/action_view/rendering.rb +++ b/actionpack/lib/action_view/rendering.rb @@ -3,42 +3,8 @@ require 'active_support/core_ext/object/try' module ActionView # = Action View Rendering module Rendering - # Returns the result of a render that's dictated by the options hash. The primary options are: - # - # * :partial - See ActionView::Partials. - # * :file - Renders an explicit template file (this used to be the old default), add :locals to pass in those. - # * :inline - Renders an inline template similar to how it's done in the controller. - # * :text - Renders the text passed in out. - # - # If no options hash is passed or :update specified, the default is to render a partial and use the second parameter - # as the locals hash. - def render(options = {}, locals = {}, &block) - case options - when Hash - if block_given? - _render_partial(options.merge(:partial => options[:layout]), &block) - elsif options.key?(:partial) - _render_partial(options) - else - _render_template(options) - end - else - _render_partial(:partial => options, :locals => locals) - end - end - - # Render but returns a valid Rack body. If fibers are defined, we return - # a streaming body that renders the template piece by piece. - # - # Note that partials are not supported to be rendered with streaming, - # so in such cases, we just wrap them in an array. - def render_body(options) - if options.key?(:partial) - [_render_partial(options)] - else - StreamingTemplateRenderer.new(self).render(options) - end - end + # This is temporary until we remove the renderer dependency from AV. + delegate :render, :render_body, :to => :@_renderer # Returns the contents that are yielded to a layout, given a name or a block. # @@ -102,13 +68,5 @@ module ActionView _layout_for(*args) end end - - def _render_template(options) #:nodoc: - _template_renderer.render(options) - end - - def _template_renderer #:nodoc: - @_template_renderer ||= TemplateRenderer.new(self) - end end end -- cgit v1.2.3 From bebaccdf4a3a17f2ead349cca891032e245655ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sun, 1 May 2011 11:14:38 +0200 Subject: Remove dependency from _template. --- actionpack/lib/action_view/base.rb | 3 ++- actionpack/lib/action_view/helpers/form_helper.rb | 2 +- actionpack/lib/action_view/helpers/translation_helper.rb | 4 ++-- actionpack/lib/action_view/template.rb | 14 +++----------- actionpack/lib/action_view/test_case.rb | 16 +++++++++++++--- 5 files changed, 21 insertions(+), 18 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb index c1dbbe1613..0866e808e6 100644 --- a/actionpack/lib/action_view/base.rb +++ b/actionpack/lib/action_view/base.rb @@ -159,7 +159,7 @@ module ActionView #:nodoc: end end - attr_accessor :_template, :_view_flow + attr_accessor :_view_flow attr_internal :request, :controller, :config, :assigns, :lookup_context # TODO Consider removing those setters once we have the renderer in place. @@ -191,6 +191,7 @@ module ActionView #:nodoc: @_virtual_path = nil @_view_flow = OutputFlow.new @output_buffer = nil + @virtual_path = nil if @_controller = controller @_request = controller.request if controller.respond_to?(:request) diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index 68db17c254..b6bb90a3ce 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -1172,7 +1172,7 @@ module ActionView class FormBuilder # The methods which wrap a form helper call. class_attribute :field_helpers - self.field_helpers = (FormHelper.instance_method_names - ['form_for']) + self.field_helpers = FormHelper.instance_method_names - %w(form_for convert_to_model) attr_accessor :object_name, :object, :options diff --git a/actionpack/lib/action_view/helpers/translation_helper.rb b/actionpack/lib/action_view/helpers/translation_helper.rb index 26ebae6546..ec9bdd5320 100644 --- a/actionpack/lib/action_view/helpers/translation_helper.rb +++ b/actionpack/lib/action_view/helpers/translation_helper.rb @@ -63,8 +63,8 @@ module ActionView private def scope_key_by_partial(key) if key.to_s.first == "." - if (path = @_template && @_template.virtual_path) - path.gsub(%r{/_?}, ".") + key.to_s + if @virtual_path + @virtual_path.gsub(%r{/_?}, ".") + key.to_s else raise "Cannot use t(#{key.inspect}) shortcut because path is not available" end diff --git a/actionpack/lib/action_view/template.rb b/actionpack/lib/action_view/template.rb index 6dfc4f68ae..98ecd15aa0 100644 --- a/actionpack/lib/action_view/template.rb +++ b/actionpack/lib/action_view/template.rb @@ -139,15 +139,12 @@ module ActionView # we use a bang in this instrumentation because you don't want to # consume this in production. This is only slow if it's being listened to. def render(view, locals, buffer=nil, &block) - old_template, view._template = view._template, self ActiveSupport::Notifications.instrument("!render_template.action_view", :virtual_path => @virtual_path) do compile!(view) view.send(method_name, locals, buffer, &block) end rescue Exception => e handle_render_error(view, e) - ensure - view._template = old_template end def mime_type @@ -174,12 +171,7 @@ module ActionView end def inspect - @inspect ||= - if defined?(Rails.root) - identifier.sub("#{Rails.root}/", '') - else - identifier - end + @inspect ||= defined?(Rails.root) ? identifier.sub("#{Rails.root}/", '') : identifier end protected @@ -264,9 +256,9 @@ module ActionView # encoding of the code source = <<-end_src def #{method_name}(local_assigns, output_buffer) - _old_output_buffer = @output_buffer;#{locals_code};#{code} + _old_virtual_path, @virtual_path = @virtual_path, #{@virtual_path.inspect};_old_output_buffer = @output_buffer;#{locals_code};#{code} ensure - @output_buffer = _old_output_buffer + @virtual_path, @output_buffer = _old_virtual_path, _old_output_buffer end end_src diff --git a/actionpack/lib/action_view/test_case.rb b/actionpack/lib/action_view/test_case.rb index 5c74bf843a..14e032790d 100644 --- a/actionpack/lib/action_view/test_case.rb +++ b/actionpack/lib/action_view/test_case.rb @@ -147,9 +147,19 @@ module ActionView module Locals attr_accessor :locals - def _render_partial(options) - locals[options[:partial]] = options[:locals] - super(options) + def render(options = {}, local_assigns = {}) + case options + when Hash + if block_given? + locals[options[:layout]] = options[:locals] + elsif options.key?(:partial) + locals[options[:partial]] = options[:locals] + end + else + locals[options] = local_assigns + end + + super end end -- cgit v1.2.3 From 2f683fd870d0e4c5aff38510ef03c7e5144a1ea4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sun, 1 May 2011 11:53:09 +0200 Subject: Remove more dependencies from the view. --- actionpack/lib/action_view/base.rb | 6 +----- actionpack/lib/action_view/renderer/partial_renderer.rb | 14 ++++++++++---- actionpack/lib/action_view/renderer/renderer.rb | 1 + actionpack/lib/action_view/rendering.rb | 17 ++++++----------- 4 files changed, 18 insertions(+), 20 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb index 0866e808e6..37cd4d9ddc 100644 --- a/actionpack/lib/action_view/base.rb +++ b/actionpack/lib/action_view/base.rb @@ -202,17 +202,13 @@ module ActionView #:nodoc: lookup_context : ActionView::LookupContext.new(lookup_context) @_lookup_context.formats = formats if formats - @_renderer = ActionView::Renderer.new(@_lookup_context, self) + @view_renderer = ActionView::Renderer.new(@_lookup_context, self) end def controller_path @controller_path ||= controller && controller.controller_path end - def controller_prefixes - @controller_prefixes ||= controller && controller._prefixes - end - ActiveSupport.run_load_hooks(:action_view, self) end end diff --git a/actionpack/lib/action_view/renderer/partial_renderer.rb b/actionpack/lib/action_view/renderer/partial_renderer.rb index 83efc95f39..0eeead2e5d 100644 --- a/actionpack/lib/action_view/renderer/partial_renderer.rb +++ b/actionpack/lib/action_view/renderer/partial_renderer.rb @@ -217,9 +217,11 @@ module ActionView class PartialRenderer < AbstractRenderer #:nodoc: PARTIAL_NAMES = Hash.new {|h,k| h[k] = {} } + # TODO Controller should not come from the view def initialize(view, *) super - @partial_names = PARTIAL_NAMES[@view.controller.class.name] + @controller = @view.controller + @partial_names = PARTIAL_NAMES[@controller.class.name] end def setup(options, block) @@ -292,7 +294,7 @@ module ActionView locals[as] = object content = @template.render(view, locals) do |*name| - view._block_layout_for(*name, &block) + view._layout_for(*name, &block) end content = layout.render(view, locals){ content } if layout @@ -301,6 +303,10 @@ module ActionView private + def controller_prefixes + @controller_prefixes ||= @controller && @controller._prefixes + end + def collection if @options.key?(:collection) collection = @options[:collection] @@ -324,7 +330,7 @@ module ActionView end def find_template(path=@path, locals=@locals.keys) - prefixes = path.include?(?/) ? [] : @view.controller_prefixes + prefixes = path.include?(?/) ? [] : controller_prefixes @lookup_context.find_template(path, prefixes, true, locals) end @@ -365,7 +371,7 @@ module ActionView object = object.to_model if object.respond_to?(:to_model) object.class.model_name.partial_path.dup.tap do |partial| - path = @view.controller_prefixes.first + path = controller_prefixes.first partial.insert(0, "#{File.dirname(path)}/") if partial.include?(?/) && path.include?(?/) end end diff --git a/actionpack/lib/action_view/renderer/renderer.rb b/actionpack/lib/action_view/renderer/renderer.rb index f0ee103d80..7b95c2dd92 100644 --- a/actionpack/lib/action_view/renderer/renderer.rb +++ b/actionpack/lib/action_view/renderer/renderer.rb @@ -6,6 +6,7 @@ module ActionView attr_accessor :lookup_context # TODO: render_context should not be an initialization parameter + # TODO: controller should be received on initialization def initialize(lookup_context, render_context) @render_context = render_context @lookup_context = lookup_context diff --git a/actionpack/lib/action_view/rendering.rb b/actionpack/lib/action_view/rendering.rb index 017a27976c..9850965456 100644 --- a/actionpack/lib/action_view/rendering.rb +++ b/actionpack/lib/action_view/rendering.rb @@ -4,7 +4,7 @@ module ActionView # = Action View Rendering module Rendering # This is temporary until we remove the renderer dependency from AV. - delegate :render, :render_body, :to => :@_renderer + delegate :render, :render_body, :to => :@view_renderer # Returns the contents that are yielded to a layout, given a name or a block. # @@ -52,20 +52,15 @@ module ActionView # Hello David # # - def _layout_for(*args) - name = args.first - name = :layout unless name.is_a?(Symbol) - @_view_flow.get(name).html_safe - end - - # Handle layout for calls from partials that supports blocks. - def _block_layout_for(*args, &block) + def _layout_for(*args, &block) name = args.first - if !name.is_a?(Symbol) && block + if name.is_a?(Symbol) + @_view_flow.get(name).html_safe + elsif block capture(*args, &block) else - _layout_for(*args) + @_view_flow.get(:layout).html_safe end end end -- cgit v1.2.3 From 33cc001f9158463389a9c9c321de0dbdccb1df8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sun, 1 May 2011 12:16:31 +0200 Subject: More cleanup and moving responsibilities around. --- actionpack/lib/action_view.rb | 1 - actionpack/lib/action_view/base.rb | 19 +++-- actionpack/lib/action_view/context.rb | 84 ++++++++++++++++------ actionpack/lib/action_view/flows.rb | 2 +- .../lib/action_view/helpers/capture_helper.rb | 6 +- .../renderer/streaming_template_renderer.rb | 4 +- .../lib/action_view/renderer/template_renderer.rb | 2 +- actionpack/lib/action_view/rendering.rb | 67 ----------------- 8 files changed, 78 insertions(+), 107 deletions(-) delete mode 100644 actionpack/lib/action_view/rendering.rb (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_view.rb b/actionpack/lib/action_view.rb index 69c50a056c..92b6f7c770 100644 --- a/actionpack/lib/action_view.rb +++ b/actionpack/lib/action_view.rb @@ -35,7 +35,6 @@ module ActionView autoload :Helpers autoload :LookupContext autoload :PathSet - autoload :Rendering autoload :Template autoload :TestCase diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb index 37cd4d9ddc..cde1c36bd9 100644 --- a/actionpack/lib/action_view/base.rb +++ b/actionpack/lib/action_view/base.rb @@ -131,7 +131,7 @@ module ActionView #:nodoc: # # More builder documentation can be found at http://builder.rubyforge.org. class Base - include Helpers, Rendering, ::ERB::Util, Context + include Helpers, ::ERB::Util, Context # Specify the proc used to decorate input tags that refer to attributes with errors. cattr_accessor :field_error_proc @@ -159,10 +159,10 @@ module ActionView #:nodoc: end end - attr_accessor :_view_flow - attr_internal :request, :controller, :config, :assigns, :lookup_context + attr_accessor :view_renderer + attr_internal :request, :controller, :config, :assigns - # TODO Consider removing those setters once we have the renderer in place. + delegate :lookup_context, :render, :render_body, :to => :view_renderer delegate :formats, :formats=, :locale, :locale=, :view_paths, :view_paths=, :to => :lookup_context delegate :request_forgery_protection_token, :params, :session, :cookies, :response, :headers, @@ -187,22 +187,21 @@ module ActionView #:nodoc: assign(assigns_for_first_render) self.helpers = Module.new unless self.class.helpers - @_config = {} - @_virtual_path = nil - @_view_flow = OutputFlow.new + @view_flow = OutputFlow.new @output_buffer = nil @virtual_path = nil + @_config = {} if @_controller = controller @_request = controller.request if controller.respond_to?(:request) @_config = controller.config.inheritable_copy if controller.respond_to?(:config) end - @_lookup_context = lookup_context.is_a?(ActionView::LookupContext) ? + _lookup_context = lookup_context.is_a?(ActionView::LookupContext) ? lookup_context : ActionView::LookupContext.new(lookup_context) - @_lookup_context.formats = formats if formats + _lookup_context.formats = formats if formats - @view_renderer = ActionView::Renderer.new(@_lookup_context, self) + @view_renderer = ActionView::Renderer.new(_lookup_context, self) end def controller_path diff --git a/actionpack/lib/action_view/context.rb b/actionpack/lib/action_view/context.rb index 710cdc613d..c4bebd7d95 100644 --- a/actionpack/lib/action_view/context.rb +++ b/actionpack/lib/action_view/context.rb @@ -8,29 +8,69 @@ module ActionView # Action View contexts are supplied to Action Controller to render template. # The default Action View context is ActionView::Base. # - # In order to work with ActionController, a Context must implement: - # - # Context#render_partial[options] - # - responsible for setting options[:_template] - # - Returns String with the rendered partial - # options:: see _render_partial in ActionView::Base - # Context#render_template[template, layout, options, partial] - # - Returns String with the rendered template - # template:: The template to render - # layout:: The layout to render around the template - # options:: See _render_template_with_layout in ActionView::Base - # partial:: Whether or not the template to render is a partial - # - # An Action View context can also mix in Action View's helpers. In order to - # mix in helpers, a context must implement: - # - # Context#controller - # - Returns an instance of AbstractController - # - # In any case, a context must mix in ActionView::Context, which stores compiled - # template and provides the output buffer. + # In order to work with ActionController, a Context must just include this module. module Context include CompiledTemplates - attr_accessor :output_buffer, :view_renderer, :view_flow + attr_accessor :output_buffer, :view_flow + + # Returns the contents that are yielded to a layout, given a name or a block. + # + # You can think of a layout as a method that is called with a block. If the user calls + # yield :some_name, the block, by default, returns content_for(:some_name). + # If the user calls simply +yield+, the default block returns content_for(:layout). + # + # The user can override this default by passing a block to the layout: + # + # # The template + # <%= render :layout => "my_layout" do %> + # Content + # <% end %> + # + # # The layout + # + # <%= yield %> + # + # + # In this case, instead of the default block, which would return content_for(:layout), + # this method returns the block that was passed in to render :layout, and the response + # would be + # + # + # Content + # + # + # Finally, the block can take block arguments, which can be passed in by +yield+: + # + # # The template + # <%= render :layout => "my_layout" do |customer| %> + # Hello <%= customer.name %> + # <% end %> + # + # # The layout + # + # <%= yield Struct.new(:name).new("David") %> + # + # + # In this case, the layout would receive the block passed into render :layout, + # and the struct specified would be passed into the block as an argument. The result + # would be + # + # + # Hello David + # + # + def _layout_for(*args, &block) + name = args.first + + if name.is_a?(Symbol) + view_flow.get(name).html_safe + elsif block + # TODO Import capture into AV::Context or + # leave it as implicit dependency? + capture(*args, &block) + else + view_flow.get(:layout).html_safe + end + end end end \ No newline at end of file diff --git a/actionpack/lib/action_view/flows.rb b/actionpack/lib/action_view/flows.rb index 386a06511f..a8f740713f 100644 --- a/actionpack/lib/action_view/flows.rb +++ b/actionpack/lib/action_view/flows.rb @@ -34,7 +34,7 @@ module ActionView @view = view @parent = nil @child = view.output_buffer - @content = view._view_flow.content + @content = view.view_flow.content @fiber = fiber @root = Fiber.current.object_id end diff --git a/actionpack/lib/action_view/helpers/capture_helper.rb b/actionpack/lib/action_view/helpers/capture_helper.rb index 0139714240..ead7feb091 100644 --- a/actionpack/lib/action_view/helpers/capture_helper.rb +++ b/actionpack/lib/action_view/helpers/capture_helper.rb @@ -135,7 +135,7 @@ module ActionView # for elements that will be fragment cached. def content_for(name, content = nil, &block) content = capture(&block) if block_given? - result = @_view_flow.append(name, content) if content + result = @view_flow.append(name, content) if content result unless content end @@ -146,7 +146,7 @@ module ActionView # the layout to stop looking for more contents. def provide(name, content = nil, &block) content = capture(&block) if block_given? - result = @_view_flow.append!(name, content) if content + result = @view_flow.append!(name, content) if content result unless content end @@ -169,7 +169,7 @@ module ActionView # # def content_for?(name) - @_view_flow.get(name).present? + @view_flow.get(name).present? end # Use an alternate output buffer for the duration of the block. diff --git a/actionpack/lib/action_view/renderer/streaming_template_renderer.rb b/actionpack/lib/action_view/renderer/streaming_template_renderer.rb index 03aab444f8..d60b42a284 100644 --- a/actionpack/lib/action_view/renderer/streaming_template_renderer.rb +++ b/actionpack/lib/action_view/renderer/streaming_template_renderer.rb @@ -106,7 +106,7 @@ module ActionView # Set the view flow to support streaming. It will be aware # when to stop rendering the layout because it needs to search # something in the template and vice-versa. - view._view_flow = StreamingFlow.new(view, fiber) + view.view_flow = StreamingFlow.new(view, fiber) # Yo! Start the fiber! fiber.resume @@ -118,7 +118,7 @@ module ActionView content = template.render(view, locals, &yielder) # Once rendering the template is done, sets its content in the :layout key. - view._view_flow.set(:layout, content) + view.view_flow.set(:layout, content) # In case the layout continues yielding, we need to resume # the fiber until all yields are handled. diff --git a/actionpack/lib/action_view/renderer/template_renderer.rb b/actionpack/lib/action_view/renderer/template_renderer.rb index 6b5ead463f..471428ac9e 100644 --- a/actionpack/lib/action_view/renderer/template_renderer.rb +++ b/actionpack/lib/action_view/renderer/template_renderer.rb @@ -46,7 +46,7 @@ module ActionView if layout view = @view - view._view_flow.set(:layout, content) + view.view_flow.set(:layout, content) layout.render(view, locals){ |*name| view._layout_for(*name) } else content diff --git a/actionpack/lib/action_view/rendering.rb b/actionpack/lib/action_view/rendering.rb deleted file mode 100644 index 9850965456..0000000000 --- a/actionpack/lib/action_view/rendering.rb +++ /dev/null @@ -1,67 +0,0 @@ -require 'active_support/core_ext/object/try' - -module ActionView - # = Action View Rendering - module Rendering - # This is temporary until we remove the renderer dependency from AV. - delegate :render, :render_body, :to => :@view_renderer - - # Returns the contents that are yielded to a layout, given a name or a block. - # - # You can think of a layout as a method that is called with a block. If the user calls - # yield :some_name, the block, by default, returns content_for(:some_name). - # If the user calls simply +yield+, the default block returns content_for(:layout). - # - # The user can override this default by passing a block to the layout: - # - # # The template - # <%= render :layout => "my_layout" do %> - # Content - # <% end %> - # - # # The layout - # - # <%= yield %> - # - # - # In this case, instead of the default block, which would return content_for(:layout), - # this method returns the block that was passed in to render :layout, and the response - # would be - # - # - # Content - # - # - # Finally, the block can take block arguments, which can be passed in by +yield+: - # - # # The template - # <%= render :layout => "my_layout" do |customer| %> - # Hello <%= customer.name %> - # <% end %> - # - # # The layout - # - # <%= yield Struct.new(:name).new("David") %> - # - # - # In this case, the layout would receive the block passed into render :layout, - # and the struct specified would be passed into the block as an argument. The result - # would be - # - # - # Hello David - # - # - def _layout_for(*args, &block) - name = args.first - - if name.is_a?(Symbol) - @_view_flow.get(name).html_safe - elsif block - capture(*args, &block) - else - @_view_flow.get(:layout).html_safe - end - end - end -end -- cgit v1.2.3 From 367bdc53611fe1da9cedda3220a83d3f39409cff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sun, 1 May 2011 12:37:57 +0200 Subject: Remove view dependency from AV::Renderer. --- actionpack/lib/action_view.rb | 1 + actionpack/lib/action_view/base.rb | 23 +++++--- .../lib/action_view/renderer/abstract_renderer.rb | 4 +- .../lib/action_view/renderer/partial_renderer.rb | 69 +++++++++++----------- actionpack/lib/action_view/renderer/renderer.rb | 46 ++++++--------- .../lib/action_view/renderer/template_renderer.rb | 4 +- actionpack/lib/action_view/rendering.rb | 27 +++++++++ 7 files changed, 100 insertions(+), 74 deletions(-) create mode 100644 actionpack/lib/action_view/rendering.rb (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_view.rb b/actionpack/lib/action_view.rb index 92b6f7c770..69c50a056c 100644 --- a/actionpack/lib/action_view.rb +++ b/actionpack/lib/action_view.rb @@ -35,6 +35,7 @@ module ActionView autoload :Helpers autoload :LookupContext autoload :PathSet + autoload :Rendering autoload :Template autoload :TestCase diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb index cde1c36bd9..1890259ca0 100644 --- a/actionpack/lib/action_view/base.rb +++ b/actionpack/lib/action_view/base.rb @@ -131,7 +131,7 @@ module ActionView #:nodoc: # # More builder documentation can be found at http://builder.rubyforge.org. class Base - include Helpers, ::ERB::Util, Context + include Helpers, Rendering, ::ERB::Util, Context # Specify the proc used to decorate input tags that refer to attributes with errors. cattr_accessor :field_error_proc @@ -162,7 +162,7 @@ module ActionView #:nodoc: attr_accessor :view_renderer attr_internal :request, :controller, :config, :assigns - delegate :lookup_context, :render, :render_body, :to => :view_renderer + delegate :lookup_context, :to => :view_renderer delegate :formats, :formats=, :locale, :locale=, :view_paths, :view_paths=, :to => :lookup_context delegate :request_forgery_protection_token, :params, :session, :cookies, :response, :headers, @@ -183,10 +183,11 @@ module ActionView #:nodoc: @_assigns = new_assigns.each { |key, value| instance_variable_set("@#{key}", value) } end - def initialize(lookup_context = nil, assigns_for_first_render = {}, controller = nil, formats = nil) #:nodoc: + def initialize(context = nil, assigns_for_first_render = {}, controller = nil, formats = nil) #:nodoc: assign(assigns_for_first_render) self.helpers = Module.new unless self.class.helpers + # Context vars initialization @view_flow = OutputFlow.new @output_buffer = nil @virtual_path = nil @@ -197,13 +198,19 @@ module ActionView #:nodoc: @_config = controller.config.inheritable_copy if controller.respond_to?(:config) end - _lookup_context = lookup_context.is_a?(ActionView::LookupContext) ? - lookup_context : ActionView::LookupContext.new(lookup_context) - _lookup_context.formats = formats if formats - - @view_renderer = ActionView::Renderer.new(_lookup_context, self) + # Handle all these for backwards compatibility. + # TODO Provide a new API for AV::Base and deprecate this one. + if context.is_a?(ActionView::Renderer) + @view_renderer = context + elsif + lookup_context = context.is_a?(ActionView::LookupContext) ? + context : ActionView::LookupContext.new(context) + lookup_context.formats = formats if formats + @view_renderer = ActionView::Renderer.new(lookup_context, controller) + end end + # TODO Is this needed anywhere? Maybe deprecate it? def controller_path @controller_path ||= controller && controller.controller_path end diff --git a/actionpack/lib/action_view/renderer/abstract_renderer.rb b/actionpack/lib/action_view/renderer/abstract_renderer.rb index 37bc0ae244..d389105a7a 100644 --- a/actionpack/lib/action_view/renderer/abstract_renderer.rb +++ b/actionpack/lib/action_view/renderer/abstract_renderer.rb @@ -3,9 +3,9 @@ module ActionView delegate :find_template, :template_exists?, :with_fallbacks, :update_details, :with_layout_format, :formats, :freeze_formats, :to => :@lookup_context - def initialize(view, lookup_context) - @view = view + def initialize(lookup_context, controller) @lookup_context = lookup_context + @controller = controller end def render diff --git a/actionpack/lib/action_view/renderer/partial_renderer.rb b/actionpack/lib/action_view/renderer/partial_renderer.rb index 0eeead2e5d..70327b16c4 100644 --- a/actionpack/lib/action_view/renderer/partial_renderer.rb +++ b/actionpack/lib/action_view/renderer/partial_renderer.rb @@ -217,45 +217,14 @@ module ActionView class PartialRenderer < AbstractRenderer #:nodoc: PARTIAL_NAMES = Hash.new {|h,k| h[k] = {} } - # TODO Controller should not come from the view - def initialize(view, *) + def initialize(*) super - @controller = @view.controller @partial_names = PARTIAL_NAMES[@controller.class.name] end - def setup(options, block) - partial = options[:partial] - - @options = options - @locals = options[:locals] || {} - @block = block - - if String === partial - @object = options[:object] - @path = partial - @collection = collection - else - @object = partial - - if @collection = collection_from_object || collection - paths = @collection_data = @collection.map { |o| partial_path(o) } - @path = paths.uniq.size == 1 ? paths.first : nil - else - @path = partial_path - end - end + def render(context, options, block) + setup(context, options, block) - if @path - @variable, @variable_counter = retrieve_variable(@path) - else - paths.map! { |path| retrieve_variable(path).unshift(path) } - end - - self - end - - def render wrap_formats(@path) do identifier = ((@template = find_partial) ? @template.identifier : @path) @@ -303,6 +272,38 @@ module ActionView private + def setup(context, options, block) + @view = context + partial = options[:partial] + + @options = options + @locals = options[:locals] || {} + @block = block + + if String === partial + @object = options[:object] + @path = partial + @collection = collection + else + @object = partial + + if @collection = collection_from_object || collection + paths = @collection_data = @collection.map { |o| partial_path(o) } + @path = paths.uniq.size == 1 ? paths.first : nil + else + @path = partial_path + end + end + + if @path + @variable, @variable_counter = retrieve_variable(@path) + else + paths.map! { |path| retrieve_variable(path).unshift(path) } + end + + self + end + def controller_prefixes @controller_prefixes ||= @controller && @controller._prefixes end diff --git a/actionpack/lib/action_view/renderer/renderer.rb b/actionpack/lib/action_view/renderer/renderer.rb index 7b95c2dd92..582ed2f9f8 100644 --- a/actionpack/lib/action_view/renderer/renderer.rb +++ b/actionpack/lib/action_view/renderer/renderer.rb @@ -3,37 +3,25 @@ module ActionView # to other objects like TemplateRenderer and PartialRenderer which # actually renders the template. class Renderer - attr_accessor :lookup_context + attr_accessor :lookup_context, :controller - # TODO: render_context should not be an initialization parameter - # TODO: controller should be received on initialization - def initialize(lookup_context, render_context) - @render_context = render_context + def initialize(lookup_context, controller) @lookup_context = lookup_context - @view_flow = OutputFlow.new + @controller = controller end - # Returns the result of a render that's dictated by the options hash. The primary options are: - # - # * :partial - See ActionView::Partials. - # * :file - Renders an explicit template file (this used to be the old default), add :locals to pass in those. - # * :inline - Renders an inline template similar to how it's done in the controller. - # * :text - Renders the text passed in out. - # - # If no options hash is passed or :update specified, the default is to render a partial and use the second parameter - # as the locals hash. - def render(options = {}, locals = {}, &block) + def render(context, options = {}, locals = {}, &block) case options when Hash if block_given? - _render_partial(options.merge(:partial => options[:layout]), &block) + _render_partial(context, options.merge(:partial => options[:layout]), &block) elsif options.key?(:partial) - _render_partial(options) + _render_partial(context, options) else - _render_template(options) + _render_template(context, options) end else - _render_partial(:partial => options, :locals => locals) + _render_partial(context, :partial => options, :locals => locals) end end @@ -42,30 +30,30 @@ module ActionView # # Note that partials are not supported to be rendered with streaming, # so in such cases, we just wrap them in an array. - def render_body(options) + def render_body(context, options) if options.key?(:partial) - [_render_partial(options)] + [_render_partial(context, options)] else - StreamingTemplateRenderer.new(@render_context, @lookup_context).render(options) + StreamingTemplateRenderer.new(@lookup_context, @controller).render(context, options) end end private - def _render_template(options) #:nodoc: - _template_renderer.render(options) + def _render_template(context, options) #:nodoc: + _template_renderer.render(context, options) end def _template_renderer #:nodoc: - @_template_renderer ||= TemplateRenderer.new(@render_context, @lookup_context) + @_template_renderer ||= TemplateRenderer.new(@lookup_context, @controller) end - def _render_partial(options, &block) #:nodoc: - _partial_renderer.setup(options, block).render + def _render_partial(context, options, &block) #:nodoc: + _partial_renderer.render(context, options, block) end def _partial_renderer #:nodoc: - @_partial_renderer ||= PartialRenderer.new(@render_context, @lookup_context) + @_partial_renderer ||= PartialRenderer.new(@lookup_context, @controller) end end end \ No newline at end of file diff --git a/actionpack/lib/action_view/renderer/template_renderer.rb b/actionpack/lib/action_view/renderer/template_renderer.rb index 471428ac9e..6c55a865a1 100644 --- a/actionpack/lib/action_view/renderer/template_renderer.rb +++ b/actionpack/lib/action_view/renderer/template_renderer.rb @@ -3,7 +3,9 @@ require 'active_support/core_ext/array/wrap' module ActionView class TemplateRenderer < AbstractRenderer #:nodoc: - def render(options) + def render(context, options) + @view = context + wrap_formats(options[:template] || options[:file]) do template = determine_template(options) freeze_formats(template.formats, true) diff --git a/actionpack/lib/action_view/rendering.rb b/actionpack/lib/action_view/rendering.rb new file mode 100644 index 0000000000..2f420dc992 --- /dev/null +++ b/actionpack/lib/action_view/rendering.rb @@ -0,0 +1,27 @@ +module ActionView + # = Action View Rendering + # + # Implements methods that allow rendering from a view context. + # In order to use this module, all you need is to implement + # view_renderer that returns an ActionView::Renderer object. + module Rendering + # Returns the result of a render that's dictated by the options hash. The primary options are: + # + # * :partial - See ActionView::Partials. + # * :file - Renders an explicit template file (this used to be the old default), add :locals to pass in those. + # * :inline - Renders an inline template similar to how it's done in the controller. + # * :text - Renders the text passed in out. + # + # If no options hash is passed or :update specified, the default is to render a partial and use the second parameter + # as the locals hash. + # def render(options = {}, locals = {}, &block) + def render(*args, &block) + view_renderer.render(self, *args, &block) + end + + # TODO: This is temporary, but the previous render is sticking. + def render_body(*args, &block) + view_renderer.render_body(self, *args, &block) + end + end +end \ No newline at end of file -- cgit v1.2.3 From b73576138529b1344a38f4e4b16c642f3510d514 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sun, 1 May 2011 12:56:04 +0200 Subject: Introduce view renderer. --- actionpack/lib/abstract_controller/rendering.rb | 13 +++++++-- .../lib/action_controller/metal/streaming.rb | 2 +- actionpack/lib/action_view/context.rb | 2 ++ actionpack/lib/action_view/renderer/renderer.rb | 33 +++++++--------------- actionpack/lib/action_view/rendering.rb | 20 ++++++++----- 5 files changed, 37 insertions(+), 33 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/abstract_controller/rendering.rb b/actionpack/lib/abstract_controller/rendering.rb index 306bd41e2d..db6ff41f55 100644 --- a/actionpack/lib/abstract_controller/rendering.rb +++ b/actionpack/lib/abstract_controller/rendering.rb @@ -99,7 +99,12 @@ module AbstractController # # Override this method in a module to change the default behavior. def view_context - view_context_class.new(lookup_context, view_assigns, self) + view_context_class.new(view_renderer, view_assigns, self) + end + + # Returns an object that is able to render templates. + def view_renderer + @view_renderer ||= ActionView::Renderer.new(lookup_context, self) end # Normalize arguments, options and then delegates render_to_body and @@ -127,7 +132,11 @@ module AbstractController # Find and renders a template based on the options given. # :api: private def _render_template(options) #:nodoc: - view_context.render(options) + if options.key?(:partial) + view_renderer.render_partial(view_context, options) + else + view_renderer.render_template(view_context, options) + end end # The prefixes used in render "foo" shortcuts. diff --git a/actionpack/lib/action_controller/metal/streaming.rb b/actionpack/lib/action_controller/metal/streaming.rb index b9bd49f670..5b8111f30d 100644 --- a/actionpack/lib/action_controller/metal/streaming.rb +++ b/actionpack/lib/action_controller/metal/streaming.rb @@ -51,7 +51,7 @@ module ActionController #:nodoc: # Call render_to_body if we are streaming instead of usual +render+. def _render_template(options) #:nodoc: if options.delete(:stream) - Rack::Chunked::Body.new view_context.render_body(options) + Rack::Chunked::Body.new view_renderer.render_body(view_context, options) else super end diff --git a/actionpack/lib/action_view/context.rb b/actionpack/lib/action_view/context.rb index c4bebd7d95..01be294284 100644 --- a/actionpack/lib/action_view/context.rb +++ b/actionpack/lib/action_view/context.rb @@ -9,6 +9,8 @@ module ActionView # The default Action View context is ActionView::Base. # # In order to work with ActionController, a Context must just include this module. + # The initialization of the variables used by the context (@output_buffer, @view_flow, + # and @virtual_path) is responsibility of the object that includes this module. module Context include CompiledTemplates attr_accessor :output_buffer, :view_flow diff --git a/actionpack/lib/action_view/renderer/renderer.rb b/actionpack/lib/action_view/renderer/renderer.rb index 582ed2f9f8..3c0126f6bb 100644 --- a/actionpack/lib/action_view/renderer/renderer.rb +++ b/actionpack/lib/action_view/renderer/renderer.rb @@ -10,21 +10,6 @@ module ActionView @controller = controller end - def render(context, options = {}, locals = {}, &block) - case options - when Hash - if block_given? - _render_partial(context, options.merge(:partial => options[:layout]), &block) - elsif options.key?(:partial) - _render_partial(context, options) - else - _render_template(context, options) - end - else - _render_partial(context, :partial => options, :locals => locals) - end - end - # Render but returns a valid Rack body. If fibers are defined, we return # a streaming body that renders the template piece by piece. # @@ -32,24 +17,26 @@ module ActionView # so in such cases, we just wrap them in an array. def render_body(context, options) if options.key?(:partial) - [_render_partial(context, options)] + [render_partial(context, options)] else StreamingTemplateRenderer.new(@lookup_context, @controller).render(context, options) end end - private - - def _render_template(context, options) #:nodoc: + # Direct accessor to template rendering. + def render_template(context, options) #:nodoc: _template_renderer.render(context, options) end - def _template_renderer #:nodoc: - @_template_renderer ||= TemplateRenderer.new(@lookup_context, @controller) + # Direct access to partial rendering. + def render_partial(context, options, &block) #:nodoc: + _partial_renderer.render(context, options, block) end - def _render_partial(context, options, &block) #:nodoc: - _partial_renderer.render(context, options, block) + private + + def _template_renderer #:nodoc: + @_template_renderer ||= TemplateRenderer.new(@lookup_context, @controller) end def _partial_renderer #:nodoc: diff --git a/actionpack/lib/action_view/rendering.rb b/actionpack/lib/action_view/rendering.rb index 2f420dc992..25ec450e6e 100644 --- a/actionpack/lib/action_view/rendering.rb +++ b/actionpack/lib/action_view/rendering.rb @@ -15,13 +15,19 @@ module ActionView # If no options hash is passed or :update specified, the default is to render a partial and use the second parameter # as the locals hash. # def render(options = {}, locals = {}, &block) - def render(*args, &block) - view_renderer.render(self, *args, &block) - end - - # TODO: This is temporary, but the previous render is sticking. - def render_body(*args, &block) - view_renderer.render_body(self, *args, &block) + def render(options = {}, locals = {}, &block) + case options + when Hash + if block_given? + view_renderer.render_partial(self, options.merge(:partial => options[:layout]), &block) + elsif options.key?(:partial) + view_renderer.render_partial(self, options) + else + view_renderer.render_template(self, options) + end + else + view_renderer.render_partial(self, :partial => options, :locals => locals) + end end end end \ No newline at end of file -- cgit v1.2.3 From 46611a995d91abf7bb2a64c62af13b6449c75b07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sun, 1 May 2011 13:39:15 +0200 Subject: log errors when an exception happens when streaming. --- actionpack/lib/action_view/context.rb | 2 ++ .../action_view/renderer/streaming_template_renderer.rb | 17 ++++++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_view/context.rb b/actionpack/lib/action_view/context.rb index 01be294284..d501a6eaf9 100644 --- a/actionpack/lib/action_view/context.rb +++ b/actionpack/lib/action_view/context.rb @@ -15,6 +15,8 @@ module ActionView include CompiledTemplates attr_accessor :output_buffer, :view_flow + # TODO Provide an easy method that initializes all variables? + # Returns the contents that are yielded to a layout, given a name or a block. # # You can think of a layout as a method that is called with a block. If the user calls diff --git a/actionpack/lib/action_view/renderer/streaming_template_renderer.rb b/actionpack/lib/action_view/renderer/streaming_template_renderer.rb index d60b42a284..d987bc122a 100644 --- a/actionpack/lib/action_view/renderer/streaming_template_renderer.rb +++ b/actionpack/lib/action_view/renderer/streaming_template_renderer.rb @@ -60,11 +60,26 @@ module ActionView def each(&block) begin @start.call(block) - rescue + rescue Exception => exception + log_error(exception) block.call ActionView::Base.streaming_completion_on_exception end self end + + private + + # This is the same logging logic as in ShowExceptions middleware. + # TODO Once "exceptron" is in, refactor this piece to simply re-use exceptron. + def log_error(exception) #:nodoc: + logger = ActionController::Base.logger + return unless logger + + message = "\n#{exception.class} (#{exception.message}):\n" + message << exception.annoted_source_code.to_s if exception.respond_to?(:annoted_source_code) + message << " " << exception.backtrace.join("\n ") + logger.fatal("#{message}\n\n") + end end # For streaming, instead of rendering a given a template, we return a Body -- cgit v1.2.3 From 8dbee3aba6920edcae20abf3af7f803e528f9ee0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sun, 1 May 2011 14:50:42 +0200 Subject: Streaming docs. --- .../lib/action_controller/metal/streaming.rb | 202 ++++++++++++++++++++- actionpack/lib/action_view/base.rb | 1 - .../renderer/streaming_template_renderer.rb | 41 +---- 3 files changed, 202 insertions(+), 42 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/metal/streaming.rb b/actionpack/lib/action_controller/metal/streaming.rb index 5b8111f30d..0dd847f967 100644 --- a/actionpack/lib/action_controller/metal/streaming.rb +++ b/actionpack/lib/action_controller/metal/streaming.rb @@ -2,7 +2,207 @@ require 'active_support/core_ext/file/path' require 'rack/chunked' module ActionController #:nodoc: - # Methods for sending streaming templates back to the client. + # Allow views to be streamed back to the client as they are rendered. + # + # The default way Rails renders views is by first rendering the template + # and then the layout. The first chunk of response is sent to the client + # just after the whole template is rendered, all queries are made and the + # layout is processed. + # + # Streaming inverts the rendering flow by rendering the layout first and + # streaming each part of the layout as they are processed. This allows the + # header of the html (which is usually in the layout) to be streamed back + # to client very quickly, allowing javascripts and stylesheets to be loaded + # earlier than usual. + # + # This approach was introduced in Rails 3.1 and is still improving. Several + # Rack middlewares may not work and you need to be careful when streaming. + # Those points are going to be addressed soon. + # + # In order to use streaming, you will need to use a Ruby version that + # supports Fibers (Fibers are supported since version 1.9.2 of the main + # Ruby implementation). + # + # == Examples + # + # Streaming can be added to a controller easily, all you need to do is + # call stream at the controller class: + # + # class PostsController + # stream + # end + # + # The +stream+ method accepts the same options as +before_filter+ and friends: + # + # class PostsController + # stream :only => :index + # end + # + # You can also selectively turn on streaming for specific actions: + # + # class PostsController + # def index + # @post = Post.scoped + # render :stream => true + # end + # end + # + # == When to use streaming + # + # Streaming may be considering an overkill for common actions like + # new or edit. The real benefit of streaming is on expensive actions + # that, for example, does a lot of queries on the database. + # + # In such actions, you want to delay queries execution as much as you can. + # For example, imagine the following dashboard action: + # + # def dashboard + # @posts = Post.all + # @pages = Page.all + # @articles = Article.all + # end + # + # Most of the queries here are happening in the controller. In order to benefit + # most of streaming, you would want to rewrite it as: + # + # def dashboard + # # Allow lazily execution of the query + # @posts = Post.scoped + # @pages = Page.scoped + # @articles = Article.scoped + # render :stream => true + # end + # + # == Communication between layout and template + # + # When streaming, the layout is rendered first than the template. + # This means that, if your application currently rely on variables set + # in the template to be used in the layout, they won't work once you + # move to streaming. The proper way to communicate between layout and + # template, regardless if you use streaming or not, is by using + # +content_for+, +provide+ and +yield+. + # + # Take a simple example where the layout expects the template to tell + # which title to use: + # + # + # <%= yield :title %> + # <%= yield %> + # + # + # You would use +content_for+ in your template to specify the title: + # + # <%= content_for :title, "Main" %> + # Hello + # + # And the final result would be: + # + # + # Main + # Hello + # + # + # However, if +content_for+ is called several times, the final result + # would have all calls concatenated. For instance, if we have the following + # template: + # + # <%= content_for :title, "Main" %> + # Hello + # <%= content_for :title, " page" %> + # + # The final result would be: + # + # + # Main page + # Hello + # + # + # This means that, if you have yield :title in your layout + # and you want to use streaming, you would have to render the whole template + # (and eventually trigger all queries) before streaming the title and all + # assets, which kills the purpose of streaming. For this reason Rails 3.1 + # introduces a helper called +provide+ that does the same as +content_for+ + # but tells the layout to stop searching for other entries and continue rendering. + # + # For instance, the template below, using +provide+: + # + # <%= provide :title, "Main" %> + # Hello + # <%= content_for :title, " page" %> + # + # Has as final result: + # + # + # Main + # Hello + # + # + # That said, when streaming, you need to properly check your templates + # and chose when to use +provide+ and +content_for+. + # + # == Headers, cookies, session and flash + # + # When streaming, the HTTP headers are sent to the client right before + # it renders the first line. This means that, modifying headers, cookies, + # session or flash after the template start rendering will not propagate + # to the client. + # + # If you try to modify cookies, session or flash, a ClosedError will be + # raised, showing those objects are closed for modification. + # + # == Middlewares + # + # Middlewares that need to manipulate the body won't work with streaming. + # You should disable those middlewares whenever streaming in development + # or production. For instance, Rack::Bug won't work when streaming as it + # needs to inject contents in the HTML body. + # + # Also Rack::Cache won't work with streaming as it does not support + # streaming bodies yet. So, whenever streaming, Cache-Control is automatically + # set to "no-cache". + # + # == Errors + # + # When it comes to streaming, exceptions get a bit more complicated. This + # happens because part of the template was already rendered and streamed to + # the client, making it impossible to render a whole exception page. + # + # Currently, when an exception happens in development or production, Rails + # will automatically stream to the client: + # + # "> + # + # The first two characters (">) are required in case the exception happens + # while rendering attributes for a given tag. You can check the real cause + # for the exception in your logger. + # + # == Web server support + # + # Not all web servers support streaming out-of-the-box. You need to check + # the instructions for each of them. + # + # ==== Unicorn + # + # Unicorn supports streaming but it needs to be configured. For this, you + # need to create a config file as follow: + # + # # unicorn.config.rb + # listen 3000, :tcp_nopush => false + # + # And use it on initialization: + # + # unicorn_rails --config-file unicorn.config.rb + # + # You may also want to configure other parameters like :tcp_nodelay. Please + # check its documentation for more information: http://unicorn.bogomips.org/Unicorn/Configurator.html#method-i-listen + # + # If you are using unicorn with nginx, you may need to tweak nginx. + # Streaming should work out of the box on Rainbows. + # + # ==== Passenger + # + # To be described. + # module Streaming extend ActiveSupport::Concern diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb index 1890259ca0..baa2c5729f 100644 --- a/actionpack/lib/action_view/base.rb +++ b/actionpack/lib/action_view/base.rb @@ -139,7 +139,6 @@ module ActionView #:nodoc: # How to complete the streaming when an exception occurs. # This is our best guess: first try to close the attribute, then the tag. - # Currently this is private API and may be changed at *any* time. cattr_accessor :streaming_completion_on_exception @@streaming_completion_on_exception = %(">) diff --git a/actionpack/lib/action_view/renderer/streaming_template_renderer.rb b/actionpack/lib/action_view/renderer/streaming_template_renderer.rb index d987bc122a..1ccf5a8ddb 100644 --- a/actionpack/lib/action_view/renderer/streaming_template_renderer.rb +++ b/actionpack/lib/action_view/renderer/streaming_template_renderer.rb @@ -4,50 +4,11 @@ require 'fiber' if defined?(Fiber) module ActionView - # Consider the following layout: - # - # <%= yield :header %> - # 2 - # <%= yield %> - # 5 - # <%= yield :footer %> - # - # And template: - # - # <%= provide :header, "1" %> - # 3 - # 4 - # <%= provide :footer, "6" %> - # - # It will stream: - # - # "1\n", "2\n", "3\n4\n", "5\n", "6\n" - # - # Notice that once you <%= yield %>, it will render the whole template - # before streaming again. In the future, we can also support streaming - # from the template and not only the layout. - # - # Also, notice we use +provide+ instead of +content_for+, as +provide+ - # gives the control back to the layout as soon as it is called. - # With +content_for+, it would render all the template to find all - # +content_for+ calls. For instance, consider this layout: - # - # <%= yield :header %> - # - # With this template: - # - # <%= content_for :header, "1" %> - # <%= provide :header, "2" %> - # <%= provide :header, "3" %> - # - # It will return "12\n" because +content_for+ continues rendering the - # template but it is returns back to the layout as soon as it sees the - # first +provide+. - # # == TODO # # * Support streaming from child templates, partials and so on. # * Integrate exceptions with exceptron + # * Rack::Cache needs to support streaming bodies class StreamingTemplateRenderer < TemplateRenderer #:nodoc: # A valid Rack::Body (i.e. it responds to each). # It is initialized with a block that, when called, starts -- cgit v1.2.3 From 13df194c002f362556560303da9c73a24ebb189e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sun, 1 May 2011 19:39:57 +0200 Subject: Tidy up pending TODOs after discussion with Mr. Gatoz (@wycats). --- actionpack/lib/action_view.rb | 1 - actionpack/lib/action_view/base.rb | 9 +-- actionpack/lib/action_view/context.rb | 76 ++++-------------- actionpack/lib/action_view/helpers.rb | 2 + .../lib/action_view/helpers/rendering_helper.rb | 92 ++++++++++++++++++++++ actionpack/lib/action_view/rendering.rb | 33 -------- 6 files changed, 113 insertions(+), 100 deletions(-) create mode 100644 actionpack/lib/action_view/helpers/rendering_helper.rb delete mode 100644 actionpack/lib/action_view/rendering.rb (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_view.rb b/actionpack/lib/action_view.rb index 69c50a056c..92b6f7c770 100644 --- a/actionpack/lib/action_view.rb +++ b/actionpack/lib/action_view.rb @@ -35,7 +35,6 @@ module ActionView autoload :Helpers autoload :LookupContext autoload :PathSet - autoload :Rendering autoload :Template autoload :TestCase diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb index baa2c5729f..ca0a89c8a5 100644 --- a/actionpack/lib/action_view/base.rb +++ b/actionpack/lib/action_view/base.rb @@ -131,7 +131,7 @@ module ActionView #:nodoc: # # More builder documentation can be found at http://builder.rubyforge.org. class Base - include Helpers, Rendering, ::ERB::Util, Context + include Helpers, ::ERB::Util, Context # Specify the proc used to decorate input tags that refer to attributes with errors. cattr_accessor :field_error_proc @@ -186,11 +186,6 @@ module ActionView #:nodoc: assign(assigns_for_first_render) self.helpers = Module.new unless self.class.helpers - # Context vars initialization - @view_flow = OutputFlow.new - @output_buffer = nil - @virtual_path = nil - @_config = {} if @_controller = controller @_request = controller.request if controller.respond_to?(:request) @@ -207,6 +202,8 @@ module ActionView #:nodoc: lookup_context.formats = formats if formats @view_renderer = ActionView::Renderer.new(lookup_context, controller) end + + _prepare_context end # TODO Is this needed anywhere? Maybe deprecate it? diff --git a/actionpack/lib/action_view/context.rb b/actionpack/lib/action_view/context.rb index d501a6eaf9..083856b2ca 100644 --- a/actionpack/lib/action_view/context.rb +++ b/actionpack/lib/action_view/context.rb @@ -10,71 +10,27 @@ module ActionView # # In order to work with ActionController, a Context must just include this module. # The initialization of the variables used by the context (@output_buffer, @view_flow, - # and @virtual_path) is responsibility of the object that includes this module. + # and @virtual_path) is responsibility of the object that includes this module + # (although you can call _prepare_context defined below). module Context include CompiledTemplates attr_accessor :output_buffer, :view_flow - # TODO Provide an easy method that initializes all variables? - - # Returns the contents that are yielded to a layout, given a name or a block. - # - # You can think of a layout as a method that is called with a block. If the user calls - # yield :some_name, the block, by default, returns content_for(:some_name). - # If the user calls simply +yield+, the default block returns content_for(:layout). - # - # The user can override this default by passing a block to the layout: - # - # # The template - # <%= render :layout => "my_layout" do %> - # Content - # <% end %> - # - # # The layout - # - # <%= yield %> - # - # - # In this case, instead of the default block, which would return content_for(:layout), - # this method returns the block that was passed in to render :layout, and the response - # would be - # - # - # Content - # - # - # Finally, the block can take block arguments, which can be passed in by +yield+: - # - # # The template - # <%= render :layout => "my_layout" do |customer| %> - # Hello <%= customer.name %> - # <% end %> - # - # # The layout - # - # <%= yield Struct.new(:name).new("David") %> - # - # - # In this case, the layout would receive the block passed into render :layout, - # and the struct specified would be passed into the block as an argument. The result - # would be - # - # - # Hello David - # - # - def _layout_for(*args, &block) - name = args.first + # Prepares the context by setting the appropriate instance variables. + # :api: plugin + def _prepare_context + @view_flow = OutputFlow.new + @output_buffer = nil + @virtual_path = nil + end - if name.is_a?(Symbol) - view_flow.get(name).html_safe - elsif block - # TODO Import capture into AV::Context or - # leave it as implicit dependency? - capture(*args, &block) - else - view_flow.get(:layout).html_safe - end + # Encapsulates the interaction with the view flow so it + # returns the correct buffer on yield. This is usually + # overwriten by helpers to add more behavior. + # :api: plugin + def _layout_for(name=nil) + name ||= :layout + view_flow.get(name).html_safe end end end \ No newline at end of file diff --git a/actionpack/lib/action_view/helpers.rb b/actionpack/lib/action_view/helpers.rb index 205116f610..3ef9826d30 100644 --- a/actionpack/lib/action_view/helpers.rb +++ b/actionpack/lib/action_view/helpers.rb @@ -19,6 +19,7 @@ module ActionView #:nodoc: autoload :NumberHelper autoload :OutputSafetyHelper autoload :RecordTagHelper + autoload :RenderingHelper autoload :SanitizeHelper autoload :SprocketsHelper autoload :TagHelper @@ -48,6 +49,7 @@ module ActionView #:nodoc: include NumberHelper include OutputSafetyHelper include RecordTagHelper + include RenderingHelper include SanitizeHelper include SprocketsHelper include TagHelper diff --git a/actionpack/lib/action_view/helpers/rendering_helper.rb b/actionpack/lib/action_view/helpers/rendering_helper.rb new file mode 100644 index 0000000000..c91e03b7e8 --- /dev/null +++ b/actionpack/lib/action_view/helpers/rendering_helper.rb @@ -0,0 +1,92 @@ +module ActionView + module Helpers + # = Action View Rendering + # + # Implements methods that allow rendering from a view context. + # In order to use this module, all you need is to implement + # view_renderer that returns an ActionView::Renderer object. + module RenderingHelper + # Returns the result of a render that's dictated by the options hash. The primary options are: + # + # * :partial - See ActionView::Partials. + # * :file - Renders an explicit template file (this used to be the old default), add :locals to pass in those. + # * :inline - Renders an inline template similar to how it's done in the controller. + # * :text - Renders the text passed in out. + # + # If no options hash is passed or :update specified, the default is to render a partial and use the second parameter + # as the locals hash. + def render(options = {}, locals = {}, &block) + case options + when Hash + if block_given? + view_renderer.render_partial(self, options.merge(:partial => options[:layout]), &block) + elsif options.key?(:partial) + view_renderer.render_partial(self, options) + else + view_renderer.render_template(self, options) + end + else + view_renderer.render_partial(self, :partial => options, :locals => locals) + end + end + + # Overwrites _layout_for in the context object so it supports the case a block is + # passed to a partial. Returns the contents that are yielded to a layout, given a + # name or a block. + # + # You can think of a layout as a method that is called with a block. If the user calls + # yield :some_name, the block, by default, returns content_for(:some_name). + # If the user calls simply +yield+, the default block returns content_for(:layout). + # + # The user can override this default by passing a block to the layout: + # + # # The template + # <%= render :layout => "my_layout" do %> + # Content + # <% end %> + # + # # The layout + # + # <%= yield %> + # + # + # In this case, instead of the default block, which would return content_for(:layout), + # this method returns the block that was passed in to render :layout, and the response + # would be + # + # + # Content + # + # + # Finally, the block can take block arguments, which can be passed in by +yield+: + # + # # The template + # <%= render :layout => "my_layout" do |customer| %> + # Hello <%= customer.name %> + # <% end %> + # + # # The layout + # + # <%= yield Struct.new(:name).new("David") %> + # + # + # In this case, the layout would receive the block passed into render :layout, + # and the struct specified would be passed into the block as an argument. The result + # would be + # + # + # Hello David + # + # + def _layout_for(*args, &block) + name = args.first + + if block && !name.is_a?(Symbol) + capture(*args, &block) + else + super + end + end + end + end +end \ No newline at end of file diff --git a/actionpack/lib/action_view/rendering.rb b/actionpack/lib/action_view/rendering.rb deleted file mode 100644 index 25ec450e6e..0000000000 --- a/actionpack/lib/action_view/rendering.rb +++ /dev/null @@ -1,33 +0,0 @@ -module ActionView - # = Action View Rendering - # - # Implements methods that allow rendering from a view context. - # In order to use this module, all you need is to implement - # view_renderer that returns an ActionView::Renderer object. - module Rendering - # Returns the result of a render that's dictated by the options hash. The primary options are: - # - # * :partial - See ActionView::Partials. - # * :file - Renders an explicit template file (this used to be the old default), add :locals to pass in those. - # * :inline - Renders an inline template similar to how it's done in the controller. - # * :text - Renders the text passed in out. - # - # If no options hash is passed or :update specified, the default is to render a partial and use the second parameter - # as the locals hash. - # def render(options = {}, locals = {}, &block) - def render(options = {}, locals = {}, &block) - case options - when Hash - if block_given? - view_renderer.render_partial(self, options.merge(:partial => options[:layout]), &block) - elsif options.key?(:partial) - view_renderer.render_partial(self, options) - else - view_renderer.render_template(self, options) - end - else - view_renderer.render_partial(self, :partial => options, :locals => locals) - end - end - end -end \ No newline at end of file -- cgit v1.2.3 From 2db538c8a86d0a15d37d830179bce2452a1531b7 Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Mon, 2 May 2011 01:26:53 +0200 Subject: made a copy-edit pass on the streaming RDoc --- .../lib/action_controller/metal/streaming.rb | 68 +++++++++++----------- 1 file changed, 35 insertions(+), 33 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/metal/streaming.rb b/actionpack/lib/action_controller/metal/streaming.rb index 0dd847f967..1d27c3aa51 100644 --- a/actionpack/lib/action_controller/metal/streaming.rb +++ b/actionpack/lib/action_controller/metal/streaming.rb @@ -2,17 +2,16 @@ require 'active_support/core_ext/file/path' require 'rack/chunked' module ActionController #:nodoc: - # Allow views to be streamed back to the client as they are rendered. + # Allows views to be streamed back to the client as they are rendered. # # The default way Rails renders views is by first rendering the template - # and then the layout. The first chunk of response is sent to the client - # just after the whole template is rendered, all queries are made and the - # layout is processed. + # and then the layout. The response is sent to the client after the whole + # template is rendered, all queries are made, and the layout is processed. # # Streaming inverts the rendering flow by rendering the layout first and # streaming each part of the layout as they are processed. This allows the - # header of the html (which is usually in the layout) to be streamed back - # to client very quickly, allowing javascripts and stylesheets to be loaded + # header of the HTML (which is usually in the layout) to be streamed back + # to client very quickly, allowing JavaScripts and stylesheets to be loaded # earlier than usual. # # This approach was introduced in Rails 3.1 and is still improving. Several @@ -20,13 +19,13 @@ module ActionController #:nodoc: # Those points are going to be addressed soon. # # In order to use streaming, you will need to use a Ruby version that - # supports Fibers (Fibers are supported since version 1.9.2 of the main + # supports fibers (fibers are supported since version 1.9.2 of the main # Ruby implementation). # # == Examples # # Streaming can be added to a controller easily, all you need to do is - # call stream at the controller class: + # call +stream+ in the controller class: # # class PostsController # stream @@ -42,19 +41,19 @@ module ActionController #:nodoc: # # class PostsController # def index - # @post = Post.scoped + # @posts = Post.scoped # render :stream => true # end # end # # == When to use streaming # - # Streaming may be considering an overkill for common actions like - # new or edit. The real benefit of streaming is on expensive actions - # that, for example, does a lot of queries on the database. + # Streaming may be considered to be overkill for lightweight actions like + # +new+ or +edit+. The real benefit of streaming is on expensive actions + # that, for example, do a lot of queries on the database. # # In such actions, you want to delay queries execution as much as you can. - # For example, imagine the following dashboard action: + # For example, imagine the following +dashboard+ action: # # def dashboard # @posts = Post.all @@ -63,10 +62,10 @@ module ActionController #:nodoc: # end # # Most of the queries here are happening in the controller. In order to benefit - # most of streaming, you would want to rewrite it as: + # from streaming you would want to rewrite it as: # # def dashboard - # # Allow lazily execution of the query + # # Allow lazy execution of the queries # @posts = Post.scoped # @pages = Page.scoped # @articles = Article.scoped @@ -75,12 +74,15 @@ module ActionController #:nodoc: # # == Communication between layout and template # - # When streaming, the layout is rendered first than the template. - # This means that, if your application currently rely on variables set - # in the template to be used in the layout, they won't work once you - # move to streaming. The proper way to communicate between layout and - # template, regardless if you use streaming or not, is by using - # +content_for+, +provide+ and +yield+. + # When streaming, rendering happens top-down instead of inside-out. + # Rails starts with the layout, and the template is rendered later, + # when its +yield+ is reached. + # + # This means that, if your application currently relies on instance + # variables set in the template to be used in the layout, they won't + # work once you move to streaming. The proper way to communicate + # between layout and template, regardless of whether you use streaming + # or not, is by using +content_for+, +provide+ and +yield+. # # Take a simple example where the layout expects the template to tell # which title to use: @@ -121,16 +123,16 @@ module ActionController #:nodoc: # and you want to use streaming, you would have to render the whole template # (and eventually trigger all queries) before streaming the title and all # assets, which kills the purpose of streaming. For this reason Rails 3.1 - # introduces a helper called +provide+ that does the same as +content_for+ + # introduces a new helper called +provide+ that does the same as +content_for+ # but tells the layout to stop searching for other entries and continue rendering. # - # For instance, the template below, using +provide+: + # For instance, the template above using +provide+ would be: # # <%= provide :title, "Main" %> # Hello # <%= content_for :title, " page" %> # - # Has as final result: + # Giving: # # # Main @@ -138,26 +140,26 @@ module ActionController #:nodoc: # # # That said, when streaming, you need to properly check your templates - # and chose when to use +provide+ and +content_for+. + # and choose when to use +provide+ and +content_for+. # # == Headers, cookies, session and flash # # When streaming, the HTTP headers are sent to the client right before # it renders the first line. This means that, modifying headers, cookies, - # session or flash after the template start rendering will not propagate + # session or flash after the template starts rendering will not propagate # to the client. # - # If you try to modify cookies, session or flash, a ClosedError will be - # raised, showing those objects are closed for modification. + # If you try to modify cookies, session or flash, an +ActionDispatch::ClosedError+ + # will be raised, showing those objects are closed for modification. # # == Middlewares # # Middlewares that need to manipulate the body won't work with streaming. # You should disable those middlewares whenever streaming in development - # or production. For instance, Rack::Bug won't work when streaming as it + # or production. For instance, +Rack::Bug+ won't work when streaming as it # needs to inject contents in the HTML body. # - # Also Rack::Cache won't work with streaming as it does not support + # Also +Rack::Cache+ won't work with streaming as it does not support # streaming bodies yet. So, whenever streaming, Cache-Control is automatically # set to "no-cache". # @@ -193,10 +195,10 @@ module ActionController #:nodoc: # # unicorn_rails --config-file unicorn.config.rb # - # You may also want to configure other parameters like :tcp_nodelay. Please - # check its documentation for more information: http://unicorn.bogomips.org/Unicorn/Configurator.html#method-i-listen + # You may also want to configure other parameters like :tcp_nodelay. + # Please check its documentation for more information: http://unicorn.bogomips.org/Unicorn/Configurator.html#method-i-listen # - # If you are using unicorn with nginx, you may need to tweak nginx. + # If you are using Unicorn with Nginx, you may need to tweak Nginx. # Streaming should work out of the box on Rainbows. # # ==== Passenger -- cgit v1.2.3 From 8c9e4d520291871e5319bc0e0a890527d8aea099 Mon Sep 17 00:00:00 2001 From: Prem Sichanugrist Date: Thu, 28 Apr 2011 15:56:11 +0700 Subject: Add `ActionController::ParamsWrapper` to wrap parameters into a nested hash This will allow us to do a rootless JSON/XML request to server. --- actionpack/lib/action_controller.rb | 1 + actionpack/lib/action_controller/base.rb | 1 + .../lib/action_controller/metal/params_wrapper.rb | 197 +++++++++++++++++++++ 3 files changed, 199 insertions(+) create mode 100644 actionpack/lib/action_controller/metal/params_wrapper.rb (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index aab2b9dc25..eba5e9377b 100644 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -23,6 +23,7 @@ module ActionController autoload :ImplicitRender autoload :Instrumentation autoload :MimeResponds + autoload :ParamsWrapper autoload :RackDelegation autoload :Redirecting autoload :Renderers diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index ca0dccf575..373df7fb55 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -194,6 +194,7 @@ module ActionController Caching, MimeResponds, ImplicitRender, + ParamsWrapper, Cookies, Flash, diff --git a/actionpack/lib/action_controller/metal/params_wrapper.rb b/actionpack/lib/action_controller/metal/params_wrapper.rb new file mode 100644 index 0000000000..29ff546139 --- /dev/null +++ b/actionpack/lib/action_controller/metal/params_wrapper.rb @@ -0,0 +1,197 @@ +require 'active_support/core_ext/class/attribute' +require 'action_dispatch/http/mime_types' + +module ActionController + # Wraps parameters hash into nested hash. This will allow client to submit + # POST request without having to specify a root element in it. + # + # By default, this functionality won't be enabled by default. You can enable + # it globally by setting +ActionController::Base.wrap_parameters+: + # + # ActionController::Base.wrap_parameters = [:json] + # + # You could also turn it on per controller by setting the format array to + # non-empty array: + # + # class UsersController < ApplicationController + # wrap_parameters :format => [:json, :xml] + # end + # + # If you enable +ParamsWrapper+ for +:json+ format. Instead of having to + # send JSON parameters like this: + # + # {"user": {"name": "Konata"}} + # + # You can now just send a parameters like this: + # + # {"name": "Konata"} + # + # And it will be wrapped into a nested hash with the key name matching + # controller's name. For example, if you're posting to +UsersController+, + # your new +params+ hash will look like this: + # + # {"name" => "Konata", "user" => {"name" => "Konata"}} + # + # You can also specify the key in which the parameters should be wrapped to, + # and also the list of attributes it should wrap by using either +:only+ or + # +:except+ options like this: + # + # class UsersController < ApplicationController + # wrap_parameters :person, :only => [:username, :password] + # end + # + # If you're going to pass the parameters to an +ActiveModel+ object (such as + # +User.new(params[:user])+), you might consider passing the model class to + # the method instead. The +ParamsWrapper+ will actually try to determine the + # list of attribute names from the model and only wrap those attributes: + # + # class UsersController < ApplicationController + # wrap_parameters Person + # end + # + # You still could pass +:only+ and +:except+ to set the list of attributes + # you want to wrap. + # + # By default, if you don't specify the key in which the parameters would be + # wrapped to, +ParamsWrapper+ will actually try to determine if there's + # a model related to it or not. This controller, for example: + # + # class Admin::UsersController < ApplicationController + # end + # + # will try to check if +Admin::User+ or +User+ model exists, and use it to + # determine the wrapper key respectively. If both of the model doesn't exists, + # it will then fallback to use +user+ as the key. + module ParamsWrapper + extend ActiveSupport::Concern + + EXCLUDE_PARAMETERS = %w(authenticity_token _method utf8) + + included do + class_attribute :_wrapper_options + self._wrapper_options = {:format => []} + end + + module ClassMethods + # Sets the name of the wrapper key, or the model which +ParamsWrapper+ + # would use to determine the attribute names from. + # + # ==== Examples + # wrap_parameters :format => :xml + # # enables the parmeter wrappes for XML format + # + # wrap_parameters :person + # # wraps parameters into +params[:person]+ hash + # + # wrap_parameters Person + # # wraps parameters by determine the wrapper key from Person class + # (+person+, in this case) and the list of attribute names + # + # wrap_parameters :only => [:username, :title] + # # wraps only +:username+ and +:title+ attributes from parameters. + # + # wrap_parameters false + # # disable parameters wrapping for this controller altogether. + # + # ==== Options + # * :format - The list of formats in which the parameters wrapper + # will be enabled. + # * :only - The list of attribute names which parmeters wrapper + # will wrap into a nested hash. + # * :only - The list of attribute names which parmeters wrapper + # will exclude from a nested hash. + def wrap_parameters(name_or_model_or_options, options = {}) + if !name_or_model_or_options.is_a? Hash + if name_or_model_or_options != false + options = options.merge(:name_or_model => name_or_model_or_options) + else + options = opions.merge(:format => []) + end + else + options = name_or_model_or_options + end + + options[:name_or_model] ||= _default_wrap_model + self._wrapper_options = self._wrapper_options.merge(options) + end + + # Sets the default wrapper key or model which will be used to determine + # wrapper key and attribute names. Will be called automatically when the + # module is inherited. + def inherited(klass) + if klass._wrapper_options[:format].present? + klass._wrapper_options = klass._wrapper_options.merge(:name_or_model => klass._default_wrap_model) + end + super + end + + # Determine the wrapper model from the controller's name. By convention, + # this could be done by trying to find the defined model that has the + # same singularize name as the controller. For example, +UsersController+ + # will try to find if the +User+ model exists. + def _default_wrap_model + model_name = self.name.sub(/Controller$/, '').singularize + + begin + model_klass = model_name.constantize + rescue NameError => e + unscoped_model_name = model_name.split("::", 2).last + break if unscoped_model_name == model_name + model_name = unscoped_model_name + end until model_klass + + model_klass + end + end + + # Performs parameters wrapping upon the request. Will be called automatically + # by the metal call stack. + def process_action(*args) + if _wrapper_enabled? + wrapped_hash = { _wrapper_key => request.request_parameters.slice(*_wrapped_keys) } + wrapped_filtered_hash = { _wrapper_key => request.filtered_parameters.slice(*_wrapped_keys) } + + # This will make the wrapped hash accessible from controller and view + request.parameters.merge! wrapped_hash + request.request_parameters.merge! wrapped_hash + + # This will make the wrapped hash displayed in the log file + request.filtered_parameters.merge! wrapped_filtered_hash + end + super + end + + private + # Returns the wrapper key which will use to stored wrapped parameters. + def _wrapper_key + @_wrapper_key ||= if _wrapper_options[:name_or_model] + _wrapper_options[:name_or_model].to_s.demodulize.underscore + else + self.class.controller_name.singularize + end + end + + # Returns the list of parameters which will be selected for wrapped. + def _wrapped_keys + @_wrapped_keys ||= if _wrapper_options[:only] + Array(_wrapper_options[:only]).collect(&:to_s) + elsif _wrapper_options[:except] + request.request_parameters.keys - Array(_wrapper_options[:except]).collect(&:to_s) - EXCLUDE_PARAMETERS + elsif _wrapper_options[:name_or_model].respond_to?(:column_names) + _wrapper_options[:name_or_model].column_names + else + request.request_parameters.keys - EXCLUDE_PARAMETERS + end + end + + # Returns the list of enabled formats. + def _wrapper_formats + Array(_wrapper_options[:format]) + end + + # Checks if we should perform parameters wrapping. + def _wrapper_enabled? + _wrapper_formats.any?{ |format| format == request.content_mime_type.try(:ref) } && request.request_parameters[_wrapper_key].nil? + end + end +end -- cgit v1.2.3 From 73c94ed97ab6639d06dade1738aa5b9f49294340 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Mon, 2 May 2011 23:33:58 +0200 Subject: Add ignore_accept_header config to AD::Request. --- .../lib/action_dispatch/http/mime_negotiation.rb | 27 ++++++++++++++++++---- 1 file changed, 22 insertions(+), 5 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_dispatch/http/mime_negotiation.rb b/actionpack/lib/action_dispatch/http/mime_negotiation.rb index 68ba1a81b5..980c658ab7 100644 --- a/actionpack/lib/action_dispatch/http/mime_negotiation.rb +++ b/actionpack/lib/action_dispatch/http/mime_negotiation.rb @@ -1,6 +1,13 @@ module ActionDispatch module Http module MimeNegotiation + extend ActiveSupport::Concern + + included do + mattr_accessor :ignore_accept_header + self.ignore_accept_header = false + end + # The MIME type of the HTTP request, such as Mime::XML. # # For backward compatibility, the post \format is extracted from the @@ -42,16 +49,14 @@ module ActionDispatch formats.first end - BROWSER_LIKE_ACCEPTS = /,\s*\*\/\*|\*\/\*\s*,/ - def formats - accept = @env['HTTP_ACCEPT'] - @env["action_dispatch.request.formats"] ||= if parameters[:format] Array(Mime[parameters[:format]]) - elsif xhr? || (accept && accept !~ BROWSER_LIKE_ACCEPTS) + elsif use_accept_header && valid_accept_header accepts + elsif xhr? + [Mime::JS] else [Mime::HTML] end @@ -87,6 +92,18 @@ module ActionDispatch order.include?(Mime::ALL) ? formats.first : nil end + + protected + + BROWSER_LIKE_ACCEPTS = /,\s*\*\/\*|\*\/\*\s*,/ + + def valid_accept_header + xhr? || (accept && accept !~ BROWSER_LIKE_ACCEPTS) + end + + def use_accept_header + !self.class.ignore_accept_header + end end end end -- cgit v1.2.3 From 83e35b9c08b97db7605542e69a8fa8d23c7df211 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Mon, 2 May 2011 23:38:39 +0200 Subject: Allow ignore_accept_header through configuration option. --- actionpack/lib/action_dispatch/railtie.rb | 2 ++ 1 file changed, 2 insertions(+) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_dispatch/railtie.rb b/actionpack/lib/action_dispatch/railtie.rb index 0a3bd5fe40..f51cc3711b 100644 --- a/actionpack/lib/action_dispatch/railtie.rb +++ b/actionpack/lib/action_dispatch/railtie.rb @@ -9,10 +9,12 @@ module ActionDispatch config.action_dispatch.show_exceptions = true config.action_dispatch.best_standards_support = true config.action_dispatch.tld_length = 1 + config.action_dispatch.ignore_accept_header = false config.action_dispatch.rack_cache = {:metastore => "rails:/", :entitystore => "rails:/", :verbose => true} initializer "action_dispatch.configure" do |app| ActionDispatch::Http::URL.tld_length = app.config.action_dispatch.tld_length + ActionDispatch::Request.ignore_accept_header = app.config.action_dispatch.ignore_accept_header end end end -- cgit v1.2.3 From b29a905f949dbed5052c55184bd5e0838517ec8d Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Mon, 2 May 2011 17:04:21 -0500 Subject: Flunk makes a lot more sense, doesnt it (hat tip @tenderlove) --- actionpack/lib/action_dispatch/testing/assertions/response.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_dispatch/testing/assertions/response.rb b/actionpack/lib/action_dispatch/testing/assertions/response.rb index e209978fb7..3335742d47 100644 --- a/actionpack/lib/action_dispatch/testing/assertions/response.rb +++ b/actionpack/lib/action_dispatch/testing/assertions/response.rb @@ -42,7 +42,7 @@ module ActionDispatch elsif type.is_a?(Symbol) && @response.response_code == Rack::Utils::SYMBOL_TO_STATUS_CODE[type] assert_block("") { true } # to count the assertion else - assert(false, build_message(message, "Expected response to be a , but was ", type, @response.response_code)) + flunk(build_message(message, "Expected response to be a , but was ", type, @response.response_code)) end end -- cgit v1.2.3 From c894fff60a9146754fc9f7c4ddf992634c2bedd3 Mon Sep 17 00:00:00 2001 From: Sebastian Martinez Date: Mon, 2 May 2011 19:07:48 -0300 Subject: Fix ParamsWrapper docs errors --- actionpack/lib/action_controller/metal/params_wrapper.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/metal/params_wrapper.rb b/actionpack/lib/action_controller/metal/params_wrapper.rb index 29ff546139..7827ed598e 100644 --- a/actionpack/lib/action_controller/metal/params_wrapper.rb +++ b/actionpack/lib/action_controller/metal/params_wrapper.rb @@ -5,7 +5,7 @@ module ActionController # Wraps parameters hash into nested hash. This will allow client to submit # POST request without having to specify a root element in it. # - # By default, this functionality won't be enabled by default. You can enable + # By default this functionality won't be enabled. You can enable # it globally by setting +ActionController::Base.wrap_parameters+: # # ActionController::Base.wrap_parameters = [:json] @@ -78,7 +78,7 @@ module ActionController # # ==== Examples # wrap_parameters :format => :xml - # # enables the parmeter wrappes for XML format + # # enables the parmeter wrapper for XML format # # wrap_parameters :person # # wraps parameters into +params[:person]+ hash -- cgit v1.2.3 From 81cfbf4146d3c5a58054b64112b8ce196f2fc061 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Mon, 2 May 2011 15:42:38 -0700 Subject: removing auto_link and moving it to the rails_autolink gem. :bomb: --- actionpack/lib/action_view/helpers/text_helper.rb | 125 ---------------------- 1 file changed, 125 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb index 06e2b027da..ca09c77b5c 100644 --- a/actionpack/lib/action_view/helpers/text_helper.rb +++ b/actionpack/lib/action_view/helpers/text_helper.rb @@ -265,60 +265,6 @@ module ActionView text.html_safe.safe_concat("

") end - # Turns all URLs and e-mail addresses into clickable links. The :link option - # will limit what should be linked. You can add HTML attributes to the links using - # :html. Possible values for :link are :all (default), - # :email_addresses, and :urls. If a block is given, each URL and - # e-mail address is yielded and the result is used as the link text. - # - # ==== Examples - # auto_link("Go to http://www.rubyonrails.org and say hello to david@loudthinking.com") - # # => "Go to http://www.rubyonrails.org and - # # say hello to david@loudthinking.com" - # - # auto_link("Visit http://www.loudthinking.com/ or e-mail david@loudthinking.com", :link => :urls) - # # => "Visit http://www.loudthinking.com/ - # # or e-mail david@loudthinking.com" - # - # auto_link("Visit http://www.loudthinking.com/ or e-mail david@loudthinking.com", :link => :email_addresses) - # # => "Visit http://www.loudthinking.com/ or e-mail david@loudthinking.com" - # - # post_body = "Welcome to my new blog at http://www.myblog.com/. Please e-mail me at me@email.com." - # auto_link(post_body, :html => { :target => '_blank' }) do |text| - # truncate(text, :length => 15) - # end - # # => "Welcome to my new blog at http://www.m.... - # Please e-mail me at me@email.com." - # - # - # You can still use auto_link with the old API that accepts the - # +link+ as its optional second parameter and the +html_options+ hash - # as its optional third parameter: - # post_body = "Welcome to my new blog at http://www.myblog.com/. Please e-mail me at me@email.com." - # auto_link(post_body, :urls) - # # => "Welcome to my new blog at http://www.myblog.com. - # Please e-mail me at me@email.com." - # - # auto_link(post_body, :all, :target => "_blank") - # # => "Welcome to my new blog at http://www.myblog.com. - # Please e-mail me at me@email.com." - def auto_link(text, *args, &block)#link = :all, html = {}, &block) - return '' if text.blank? - - options = args.size == 2 ? {} : args.extract_options! # this is necessary because the old auto_link API has a Hash as its last parameter - unless args.empty? - options[:link] = args[0] || :all - options[:html] = args[1] || {} - end - options.reverse_merge!(:link => :all, :html => {}) - - case options[:link].to_sym - when :all then auto_link_email_addresses(auto_link_urls(text, options[:html], options, &block), options[:html], &block) - when :email_addresses then auto_link_email_addresses(text, options[:html], &block) - when :urls then auto_link_urls(text, options[:html], options, &block) - end - end - # Creates a Cycle object whose _to_s_ method cycles through elements of an # array every time it is called. This can be used for example, to alternate # classes for table rows. You can use named cycles to allow nesting in loops. @@ -464,77 +410,6 @@ module ActionView @_cycles = Hash.new unless defined?(@_cycles) @_cycles[name] = cycle_object end - - AUTO_LINK_RE = %r{ - (?: ([0-9A-Za-z+.:-]+:)// | www\. ) - [^\s<]+ - }x - - # regexps for determining context, used high-volume - AUTO_LINK_CRE = [/<[^>]+$/, /^[^>]*>/, //i, /<\/a>/i] - - AUTO_EMAIL_RE = /[\w.!#\$%+-]+@[\w-]+(?:\.[\w-]+)+/ - - BRACKETS = { ']' => '[', ')' => '(', '}' => '{' } - - # Turns all urls into clickable links. If a block is given, each url - # is yielded and the result is used as the link text. - def auto_link_urls(text, html_options = {}, options = {}) - link_attributes = html_options.stringify_keys - text.gsub(AUTO_LINK_RE) do - scheme, href = $1, $& - punctuation = [] - - if auto_linked?($`, $') - # do not change string; URL is already linked - href - else - # don't include trailing punctuation character as part of the URL - while href.sub!(/[^\w\/-]$/, '') - punctuation.push $& - if opening = BRACKETS[punctuation.last] and href.scan(opening).size > href.scan(punctuation.last).size - href << punctuation.pop - break - end - end - - link_text = block_given?? yield(href) : href - href = 'http://' + href unless scheme - - unless options[:sanitize] == false - link_text = sanitize(link_text) - href = sanitize(href) - end - content_tag(:a, link_text, link_attributes.merge('href' => href), !!options[:sanitize]) + punctuation.reverse.join('') - end - end - end - - # Turns all email addresses into clickable links. If a block is given, - # each email is yielded and the result is used as the link text. - def auto_link_email_addresses(text, html_options = {}, options = {}) - text.gsub(AUTO_EMAIL_RE) do - text = $& - - if auto_linked?($`, $') - text.html_safe - else - display_text = (block_given?) ? yield(text) : text - - unless options[:sanitize] == false - text = sanitize(text) - display_text = sanitize(display_text) unless text == display_text - end - mail_to text, display_text, html_options - end - end - end - - # Detects already linked context or position in the middle of a tag - def auto_linked?(left, right) - (left =~ AUTO_LINK_CRE[0] and right =~ AUTO_LINK_CRE[1]) or - (left.rindex(AUTO_LINK_CRE[2]) and $' !~ AUTO_LINK_CRE[3]) - end end end end -- cgit v1.2.3 From 3cca86641e91400e3317ce2d03b483edf1db3ec2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Mon, 2 May 2011 23:53:53 +0200 Subject: Update CHANGELOG. --- actionpack/lib/action_controller/metal/streaming.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/metal/streaming.rb b/actionpack/lib/action_controller/metal/streaming.rb index 1d27c3aa51..3892a12407 100644 --- a/actionpack/lib/action_controller/metal/streaming.rb +++ b/actionpack/lib/action_controller/metal/streaming.rb @@ -160,7 +160,7 @@ module ActionController #:nodoc: # needs to inject contents in the HTML body. # # Also +Rack::Cache+ won't work with streaming as it does not support - # streaming bodies yet. So, whenever streaming, Cache-Control is automatically + # streaming bodies yet. Whenever streaming Cache-Control is automatically # set to "no-cache". # # == Errors -- cgit v1.2.3 From 4bddc06e83acecce662b4282159c5eb0096c4783 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Tue, 3 May 2011 00:37:40 +0200 Subject: Move most processing to load time for performance and improve test suite. --- .../lib/action_controller/metal/params_wrapper.rb | 74 ++++++++++++++-------- 1 file changed, 49 insertions(+), 25 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/metal/params_wrapper.rb b/actionpack/lib/action_controller/metal/params_wrapper.rb index 7827ed598e..3262e24f67 100644 --- a/actionpack/lib/action_controller/metal/params_wrapper.rb +++ b/actionpack/lib/action_controller/metal/params_wrapper.rb @@ -1,4 +1,6 @@ require 'active_support/core_ext/class/attribute' +require 'active_support/core_ext/hash/slice' +require 'active_support/core_ext/array/wrap' require 'action_dispatch/http/mime_types' module ActionController @@ -96,23 +98,25 @@ module ActionController # ==== Options # * :format - The list of formats in which the parameters wrapper # will be enabled. - # * :only - The list of attribute names which parmeters wrapper + # * :only - The list of attribute names which parameters wrapper # will wrap into a nested hash. - # * :only - The list of attribute names which parmeters wrapper + # * :except - The list of attribute names which parameters wrapper # will exclude from a nested hash. def wrap_parameters(name_or_model_or_options, options = {}) - if !name_or_model_or_options.is_a? Hash - if name_or_model_or_options != false - options = options.merge(:name_or_model => name_or_model_or_options) - else - options = opions.merge(:format => []) - end - else + model = nil + + case name_or_model_or_options + when Hash options = name_or_model_or_options + when false + options = options.merge(:format => []) + when Symbol, String + options = options.merge(:name => name_or_model_or_options) + else + model = name_or_model_or_options end - options[:name_or_model] ||= _default_wrap_model - self._wrapper_options = self._wrapper_options.merge(options) + _set_wrapper_defaults(_wrapper_options.slice(:format).merge(options), model) end # Sets the default wrapper key or model which will be used to determine @@ -120,11 +124,13 @@ module ActionController # module is inherited. def inherited(klass) if klass._wrapper_options[:format].present? - klass._wrapper_options = klass._wrapper_options.merge(:name_or_model => klass._default_wrap_model) + klass._set_wrapper_defaults(klass._wrapper_options) end super end + protected + # Determine the wrapper model from the controller's name. By convention, # this could be done by trying to find the defined model that has the # same singularize name as the controller. For example, +UsersController+ @@ -142,6 +148,29 @@ module ActionController model_klass end + + def _set_wrapper_defaults(options, model=nil) + options = options.dup + + unless options[:only] || options[:except] + model ||= _default_wrap_model + if model.respond_to?(:column_names) + options[:only] = model.column_names + end + end + + unless options[:name] + model ||= _default_wrap_model + options[:name] = model ? model.to_s.demodulize.underscore : + controller_name.singularize + end + + options[:only] = Array.wrap(options[:only]).collect(&:to_s) if options[:only] + options[:except] = Array.wrap(options[:except]).collect(&:to_s) if options[:except] + options[:format] = Array.wrap(options[:format]) + + self._wrapper_options = options + end end # Performs parameters wrapping upon the request. Will be called automatically @@ -164,21 +193,15 @@ module ActionController private # Returns the wrapper key which will use to stored wrapped parameters. def _wrapper_key - @_wrapper_key ||= if _wrapper_options[:name_or_model] - _wrapper_options[:name_or_model].to_s.demodulize.underscore - else - self.class.controller_name.singularize - end + _wrapper_options[:name] end # Returns the list of parameters which will be selected for wrapped. def _wrapped_keys - @_wrapped_keys ||= if _wrapper_options[:only] - Array(_wrapper_options[:only]).collect(&:to_s) - elsif _wrapper_options[:except] - request.request_parameters.keys - Array(_wrapper_options[:except]).collect(&:to_s) - EXCLUDE_PARAMETERS - elsif _wrapper_options[:name_or_model].respond_to?(:column_names) - _wrapper_options[:name_or_model].column_names + @_wrapped_keys ||= if only = _wrapper_options[:only] + only + elsif except = _wrapper_options[:except] + request.request_parameters.keys - except - EXCLUDE_PARAMETERS else request.request_parameters.keys - EXCLUDE_PARAMETERS end @@ -186,12 +209,13 @@ module ActionController # Returns the list of enabled formats. def _wrapper_formats - Array(_wrapper_options[:format]) + _wrapper_options[:format] end # Checks if we should perform parameters wrapping. def _wrapper_enabled? - _wrapper_formats.any?{ |format| format == request.content_mime_type.try(:ref) } && request.request_parameters[_wrapper_key].nil? + ref = request.content_mime_type.try(:ref) + _wrapper_formats.any? { |format| format == ref } && !request.request_parameters[_wrapper_key] end end end -- cgit v1.2.3 From 1afb56f4818381098c6ed0babc4a5899e324e2e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Tue, 3 May 2011 00:47:11 +0200 Subject: Instrumentation should have callbacks. --- actionpack/lib/action_controller/base.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index 373df7fb55..ccf1632a14 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -207,14 +207,14 @@ module ActionController HttpAuthentication::Digest::ControllerMethods, HttpAuthentication::Token::ControllerMethods, - # Add instrumentations hooks at the bottom, to ensure they instrument - # all the methods properly. - Instrumentation, - # Before callbacks should also be executed the earliest as possible, so # also include them at the bottom. AbstractController::Callbacks, + # Add instrumentations hooks at the bottom, to ensure they instrument + # all the methods properly. + Instrumentation, + # The same with rescue, append it at the end to wrap as much as possible. Rescue ] -- cgit v1.2.3 From a55f2de0c5baae589b1730df1e4068f0cd1474ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Tue, 3 May 2011 01:03:21 +0200 Subject: Improve performance for filtered parameters and add tests. --- actionpack/lib/action_controller/base.rb | 5 ++++- actionpack/lib/action_controller/metal/instrumentation.rb | 2 +- actionpack/lib/action_controller/metal/params_wrapper.rb | 5 ++--- actionpack/lib/action_dispatch/http/filter_parameters.rb | 5 +++++ 4 files changed, 12 insertions(+), 5 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index ccf1632a14..c03c77cb4a 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -194,7 +194,6 @@ module ActionController Caching, MimeResponds, ImplicitRender, - ParamsWrapper, Cookies, Flash, @@ -215,6 +214,10 @@ module ActionController # all the methods properly. Instrumentation, + # Params wrapper should come before instrumentation so they are + # properly showed in logs + ParamsWrapper, + # The same with rescue, append it at the end to wrap as much as possible. Rescue ] diff --git a/actionpack/lib/action_controller/metal/instrumentation.rb b/actionpack/lib/action_controller/metal/instrumentation.rb index dc3ea939e6..4e54c2ad88 100644 --- a/actionpack/lib/action_controller/metal/instrumentation.rb +++ b/actionpack/lib/action_controller/metal/instrumentation.rb @@ -14,7 +14,7 @@ module ActionController attr_internal :view_runtime - def process_action(action, *args) + def process_action(*args) raw_payload = { :controller => self.class.name, :action => self.action_name, diff --git a/actionpack/lib/action_controller/metal/params_wrapper.rb b/actionpack/lib/action_controller/metal/params_wrapper.rb index 3262e24f67..d128f6d03c 100644 --- a/actionpack/lib/action_controller/metal/params_wrapper.rb +++ b/actionpack/lib/action_controller/metal/params_wrapper.rb @@ -178,14 +178,13 @@ module ActionController def process_action(*args) if _wrapper_enabled? wrapped_hash = { _wrapper_key => request.request_parameters.slice(*_wrapped_keys) } - wrapped_filtered_hash = { _wrapper_key => request.filtered_parameters.slice(*_wrapped_keys) } # This will make the wrapped hash accessible from controller and view request.parameters.merge! wrapped_hash request.request_parameters.merge! wrapped_hash # This will make the wrapped hash displayed in the log file - request.filtered_parameters.merge! wrapped_filtered_hash + request.clear_filtered_parameters end super end @@ -215,7 +214,7 @@ module ActionController # Checks if we should perform parameters wrapping. def _wrapper_enabled? ref = request.content_mime_type.try(:ref) - _wrapper_formats.any? { |format| format == ref } && !request.request_parameters[_wrapper_key] + _wrapper_formats.include?(ref) && !request.request_parameters[_wrapper_key] end end end diff --git a/actionpack/lib/action_dispatch/http/filter_parameters.rb b/actionpack/lib/action_dispatch/http/filter_parameters.rb index 8dd1af7f3d..9c5b6a6b88 100644 --- a/actionpack/lib/action_dispatch/http/filter_parameters.rb +++ b/actionpack/lib/action_dispatch/http/filter_parameters.rb @@ -33,6 +33,11 @@ module ActionDispatch @filtered_parameters ||= parameter_filter.filter(parameters) end + # Clear any filtered parameters forcing them to be filtered again. + def clear_filtered_parameters + @filtered_parameters = nil + end + # Return a hash of request.env with all sensitive data replaced. def filtered_env @filtered_env ||= env_filter.filter(@env) -- cgit v1.2.3 From 35d0d82ae3edf8fe959624999c858a63b2b4ed52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Tue, 3 May 2011 01:36:58 +0200 Subject: More performance optimizations. --- .../lib/action_controller/metal/params_wrapper.rb | 30 ++++++++++++---------- .../lib/action_dispatch/http/filter_parameters.rb | 5 ---- 2 files changed, 17 insertions(+), 18 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/metal/params_wrapper.rb b/actionpack/lib/action_controller/metal/params_wrapper.rb index d128f6d03c..21bbe17dc3 100644 --- a/actionpack/lib/action_controller/metal/params_wrapper.rb +++ b/actionpack/lib/action_controller/metal/params_wrapper.rb @@ -1,5 +1,6 @@ require 'active_support/core_ext/class/attribute' require 'active_support/core_ext/hash/slice' +require 'active_support/core_ext/hash/except' require 'active_support/core_ext/array/wrap' require 'action_dispatch/http/mime_types' @@ -177,40 +178,43 @@ module ActionController # by the metal call stack. def process_action(*args) if _wrapper_enabled? - wrapped_hash = { _wrapper_key => request.request_parameters.slice(*_wrapped_keys) } + wrapped_hash = _wrap_parameters request.request_parameters + wrapped_filtered_hash = _wrap_parameters request.filtered_parameters # This will make the wrapped hash accessible from controller and view request.parameters.merge! wrapped_hash request.request_parameters.merge! wrapped_hash # This will make the wrapped hash displayed in the log file - request.clear_filtered_parameters + request.filtered_parameters.merge! wrapped_filtered_hash end super end private + # Returns the wrapper key which will use to stored wrapped parameters. def _wrapper_key _wrapper_options[:name] end - # Returns the list of parameters which will be selected for wrapped. - def _wrapped_keys - @_wrapped_keys ||= if only = _wrapper_options[:only] - only - elsif except = _wrapper_options[:except] - request.request_parameters.keys - except - EXCLUDE_PARAMETERS - else - request.request_parameters.keys - EXCLUDE_PARAMETERS - end - end - # Returns the list of enabled formats. def _wrapper_formats _wrapper_options[:format] end + # Returns the list of parameters which will be selected for wrapped. + def _wrap_parameters(parameters) + value = if only = _wrapper_options[:only] + parameters.slice(*only) + else + except = _wrapper_options[:except] || [] + parameters.except(*(except + EXCLUDE_PARAMETERS)) + end + + { _wrapper_key => value } + end + # Checks if we should perform parameters wrapping. def _wrapper_enabled? ref = request.content_mime_type.try(:ref) diff --git a/actionpack/lib/action_dispatch/http/filter_parameters.rb b/actionpack/lib/action_dispatch/http/filter_parameters.rb index 9c5b6a6b88..8dd1af7f3d 100644 --- a/actionpack/lib/action_dispatch/http/filter_parameters.rb +++ b/actionpack/lib/action_dispatch/http/filter_parameters.rb @@ -33,11 +33,6 @@ module ActionDispatch @filtered_parameters ||= parameter_filter.filter(parameters) end - # Clear any filtered parameters forcing them to be filtered again. - def clear_filtered_parameters - @filtered_parameters = nil - end - # Return a hash of request.env with all sensitive data replaced. def filtered_env @filtered_env ||= env_filter.filter(@env) -- cgit v1.2.3 From e1c16850168fbadc5ae8a0688e23170021a84955 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Tue, 3 May 2011 12:32:14 +0200 Subject: Static middleware accepts cache control. --- actionpack/lib/action_dispatch/middleware/static.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_dispatch/middleware/static.rb b/actionpack/lib/action_dispatch/middleware/static.rb index 348f7b86b8..360c1209bb 100644 --- a/actionpack/lib/action_dispatch/middleware/static.rb +++ b/actionpack/lib/action_dispatch/middleware/static.rb @@ -2,10 +2,10 @@ require 'rack/utils' module ActionDispatch class FileHandler - def initialize(root) + def initialize(root, cache_control) @root = root.chomp('/') @compiled_root = /^#{Regexp.escape(root)}/ - @file_server = ::Rack::File.new(@root) + @file_server = ::Rack::File.new(@root, cache_control) end def match?(path) @@ -37,9 +37,9 @@ module ActionDispatch class Static FILE_METHODS = %w(GET HEAD).freeze - def initialize(app, path) + def initialize(app, path, cache_control=nil) @app = app - @file_handler = FileHandler.new(path) + @file_handler = FileHandler.new(path, cache_control) end def call(env) -- cgit v1.2.3 From f7c711baee9ccd25fb976d45cb9fca033878fd26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Tue, 3 May 2011 12:42:42 +0200 Subject: No need for a regexp here. --- actionpack/lib/action_view/helpers/asset_paths.rb | 1 - actionpack/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb | 3 ++- actionpack/lib/action_view/helpers/sprockets_helper.rb | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_view/helpers/asset_paths.rb b/actionpack/lib/action_view/helpers/asset_paths.rb index cb6737b94e..958f0e0a10 100644 --- a/actionpack/lib/action_view/helpers/asset_paths.rb +++ b/actionpack/lib/action_view/helpers/asset_paths.rb @@ -21,7 +21,6 @@ module ActionView return source if is_uri?(source) source = rewrite_extension(source, dir, ext) if ext - source = "/#{dir}/#{source}" unless source[0] == ?/ source = rewrite_asset_path(source, dir) if controller && include_host diff --git a/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb b/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb index 38860431b4..cd0f8c8878 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb @@ -41,7 +41,8 @@ module ActionView # Break out the asset path rewrite in case plugins wish to put the asset id # someplace other than the query string. - def rewrite_asset_path(source, path = nil) + def rewrite_asset_path(source, dir) + source = "/#{dir}/#{source}" unless source[0] == ?/ path = config.asset_path if path && path.respond_to?(:call) diff --git a/actionpack/lib/action_view/helpers/sprockets_helper.rb b/actionpack/lib/action_view/helpers/sprockets_helper.rb index b43b91178c..ab98da9624 100644 --- a/actionpack/lib/action_view/helpers/sprockets_helper.rb +++ b/actionpack/lib/action_view/helpers/sprockets_helper.rb @@ -40,10 +40,10 @@ module ActionView class AssetPaths < ActionView::Helpers::AssetPaths #:nodoc: def rewrite_asset_path(source, dir) - if source =~ /^\/#{dir}\/(.+)/ - assets.path($1, performing_caching?, dir) - else + if source[0] == ?/ source + else + assets.path(source, performing_caching?, dir) end end -- cgit v1.2.3 From c7f7a45676f929195d6b12824acd7f200610e081 Mon Sep 17 00:00:00 2001 From: Matias Korhonen Date: Tue, 3 May 2011 13:44:31 +0300 Subject: Rescues template HTML5 doctype and the utf8 charset meta tag, and better font choices for Mac users. --- .../lib/action_dispatch/middleware/templates/rescues/layout.erb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/layout.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/layout.erb index 6c32fb17b8..6e71fd7ddc 100644 --- a/actionpack/lib/action_dispatch/middleware/templates/rescues/layout.erb +++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/layout.erb @@ -1,11 +1,13 @@ - + + + Action Controller: Exception caught