diff options
Diffstat (limited to 'actionpack/lib')
17 files changed, 185 insertions, 143 deletions
diff --git a/actionpack/lib/action_controller/metal/data_streaming.rb b/actionpack/lib/action_controller/metal/data_streaming.rb index 3140327d87..379ff97048 100644 --- a/actionpack/lib/action_controller/metal/data_streaming.rb +++ b/actionpack/lib/action_controller/metal/data_streaming.rb @@ -8,10 +8,8 @@ module ActionController #:nodoc: include ActionController::Rendering - DEFAULT_SEND_FILE_OPTIONS = { - :type => 'application/octet-stream'.freeze, - :disposition => 'attachment'.freeze, - }.freeze + DEFAULT_SEND_FILE_TYPE = 'application/octet-stream'.freeze #:nodoc: + DEFAULT_SEND_FILE_DISPOSITION = 'attachment'.freeze #:nodoc: protected # Sends the file. This uses a server-appropriate method (such as X-Sendfile) @@ -127,7 +125,7 @@ module ActionController #:nodoc: # # See +send_file+ for more information on HTTP Content-* headers and caching. def send_data(data, options = {}) #:doc: - send_file_headers! options.dup + send_file_headers! options render options.slice(:status, :content_type).merge(:text => data) end @@ -135,15 +133,8 @@ module ActionController #:nodoc: def send_file_headers!(options) type_provided = options.has_key?(:type) - 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] + content_type = options.fetch(:type, DEFAULT_SEND_FILE_TYPE) + raise ArgumentError, ":type option required" if content_type.nil? if content_type.is_a?(Symbol) extension = Mime[content_type] @@ -157,10 +148,13 @@ module ActionController #:nodoc: self.content_type = content_type end - headers.merge!( - 'Content-Disposition' => disposition, - 'Content-Transfer-Encoding' => 'binary' - ) + disposition = options.fetch(:disposition, DEFAULT_SEND_FILE_DISPOSITION) + unless disposition.nil? + disposition += %(; filename="#{options[:filename]}") if options[:filename] + headers['Content-Disposition'] = disposition + end + + headers['Content-Transfer-Encoding'] = 'binary' response.sending_file = true diff --git a/actionpack/lib/action_controller/metal/redirecting.rb b/actionpack/lib/action_controller/metal/redirecting.rb index 35ded8a56c..5e7bd44562 100644 --- a/actionpack/lib/action_controller/metal/redirecting.rb +++ b/actionpack/lib/action_controller/metal/redirecting.rb @@ -45,6 +45,16 @@ module ActionController # integer, or a symbol representing the downcased, underscored and symbolized description. # Note that the status code must be a 3xx HTTP code, or redirection will not occur. # + # If you are using XHR requests other than GET or POST and redirecting after the + # request then some browsers will follow the redirect using the original request + # method. This may lead to undesirable behavior such as a double DELETE. To work + # around this you can return a <tt>303 See Other</tt> status code which will be + # followed using a GET request. + # + # Examples: + # redirect_to posts_url, :status => :see_other + # redirect_to :action => 'index', :status => 303 + # # It is also possible to assign a flash message as part of the redirection. There are two special accessors for the commonly used flash names # +alert+ and +notice+ as well as a general purpose +flash+ bucket. # diff --git a/actionpack/lib/action_controller/metal/renderers.rb b/actionpack/lib/action_controller/metal/renderers.rb index 6e9ce450ac..4a0c1c7dd7 100644 --- a/actionpack/lib/action_controller/metal/renderers.rb +++ b/actionpack/lib/action_controller/metal/renderers.rb @@ -91,9 +91,14 @@ module ActionController add :json do |json, options| json = json.to_json(options) unless json.kind_of?(String) - json = "#{options[:callback]}(#{json})" unless options[:callback].blank? - self.content_type ||= Mime::JSON - json + + if options[:callback].present? + self.content_type ||= Mime::JS + "#{options[:callback]}(#{json})" + else + self.content_type ||= Mime::JSON + json + end end add :js do |js, options| diff --git a/actionpack/lib/action_controller/record_identifier.rb b/actionpack/lib/action_controller/record_identifier.rb index 5c40889f6b..e7af3f5b8d 100644 --- a/actionpack/lib/action_controller/record_identifier.rb +++ b/actionpack/lib/action_controller/record_identifier.rb @@ -75,12 +75,7 @@ module ActionController def record_key_for_dom_id(record) record = record.to_model if record.respond_to?(:to_model) key = record.to_key - key ? sanitize_dom_id(key.join('_')) : key - end - - # Replaces characters that are invalid in HTML DOM ids with valid ones. - def sanitize_dom_id(candidate_id) - candidate_id # TODO implement conversion to valid DOM id values + key ? key.join('_') : key end end end diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb index 66a4808e36..21997c4d79 100644 --- a/actionpack/lib/action_controller/test_case.rb +++ b/actionpack/lib/action_controller/test_case.rb @@ -147,17 +147,23 @@ module ActionController extra_keys = routes.extra_keys(parameters) non_path_parameters = get? ? query_parameters : request_parameters parameters.each do |key, value| - if value.is_a? Fixnum - value = value.to_s - elsif value.is_a? Array - value = Result.new(value.map { |v| v.is_a?(String) ? v.dup : v }) - elsif value.is_a? String + if value.is_a?(Array) && (value.frozen? || value.any?(&:frozen?)) + value = value.map{ |v| v.duplicable? ? v.dup : v } + elsif value.is_a?(Hash) && (value.frozen? || value.any?{ |k,v| v.frozen? }) + value = Hash[value.map{ |k,v| [k, v.duplicable? ? v.dup : v] }] + elsif value.frozen? && value.duplicable? value = value.dup end if extra_keys.include?(key.to_sym) non_path_parameters[key] = value else + if value.is_a?(Array) + value = Result.new(value.map(&:to_param)) + else + value = value.to_param + end + path_parameters[key.to_s] = value end end @@ -453,7 +459,7 @@ module ActionController # Ensure that numbers and symbols passed as params are converted to # proper params, as is the case when engaging rack. - parameters = paramify_values(parameters) + parameters = paramify_values(parameters) if html_format?(parameters) @request.recycle! @response.recycle! @@ -466,14 +472,13 @@ module ActionController parameters ||= {} controller_class_name = @controller.class.anonymous? ? - "anonymous_controller" : + "anonymous" : @controller.class.name.underscore.sub(/_controller$/, '') @request.assign_parameters(@routes, controller_class_name, action.to_s, parameters) - @request.session = ActionController::TestSession.new(session) if session + @request.session.update(session) if session @request.session["flash"] = @request.flash.update(flash || {}) - @request.session["flash"].sweep @controller.request = @request build_request_uri(action, parameters) @@ -546,6 +551,12 @@ module ActionController @request.env["QUERY_STRING"] = query_string || "" end end + + def html_format?(parameters) + return true unless parameters.is_a?(Hash) + format = Mime[parameters[:format]] + format.nil? || format.html? + end end # When the request.remote_addr remains the default for testing, which is 0.0.0.0, the exception is simply raised inline diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb index 25d842c572..2c0c31de9d 100644 --- a/actionpack/lib/action_dispatch/middleware/cookies.rb +++ b/actionpack/lib/action_dispatch/middleware/cookies.rb @@ -82,7 +82,7 @@ module ActionDispatch TOKEN_KEY = "action_dispatch.secret_token".freeze # Raised when storing more than 4K of session data. - class CookieOverflow < StandardError; end + CookieOverflow = Class.new StandardError class CookieJar #:nodoc: include Enumerable @@ -117,7 +117,6 @@ module ActionDispatch @delete_cookies = {} @host = host @secure = secure - @closed = false @cookies = {} end @@ -154,7 +153,7 @@ module ActionDispatch end elsif options[:domain].is_a? Array # if host matches one of the supplied domains without a dot in front of it - options[:domain] = options[:domain].find {|domain| @host.include? domain[/^\.?(.*)$/, 1] } + options[:domain] = options[:domain].find {|domain| @host.include? domain.sub(/^\./, '') } end end @@ -169,12 +168,14 @@ module ActionDispatch options = { :value => value } end - @cookies[key.to_s] = value - handle_options(options) - @set_cookies[key.to_s] = options - @delete_cookies.delete(key.to_s) + if @cookies[key.to_s] != value or options[:expires] + @cookies[key.to_s] = value + @set_cookies[key.to_s] = options + @delete_cookies.delete(key.to_s) + end + value end @@ -182,8 +183,9 @@ module ActionDispatch # and setting its expiration date into the past. Like <tt>[]=</tt>, you can pass in # an options hash to delete cookies with extra data such as a <tt>:path</tt>. def delete(key, options = {}) - options.symbolize_keys! + return unless @cookies.has_key? key.to_s + options.symbolize_keys! handle_options(options) value = @cookies.delete(key.to_s) @@ -339,7 +341,6 @@ module ActionDispatch end def call(env) - cookie_jar = nil status, headers, body = @app.call(env) if cookie_jar = env['action_dispatch.cookies'] diff --git a/actionpack/lib/action_dispatch/middleware/flash.rb b/actionpack/lib/action_dispatch/middleware/flash.rb index c92c91df65..fd0ce62a3b 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 <tt>flash["notice"] = "hello"</tt> # to put a new one. def flash - @env[Flash::KEY] ||= (session["flash"] || Flash::FlashHash.new) + @env[Flash::KEY] ||= (session["flash"] || Flash::FlashHash.new).tap(&:sweep) end end @@ -79,7 +79,6 @@ module ActionDispatch def initialize #:nodoc: @discard = Set.new - @closed = false @flashes = {} @now = nil end @@ -217,10 +216,6 @@ module ActionDispatch end def call(env) - if (session = env['rack.session']) && (flash = session['flash']) - flash.sweep - end - @app.call(env) ensure session = env['rack.session'] || {} @@ -237,7 +232,8 @@ module ActionDispatch env[KEY] = new_hash end - if session.key?('flash') && session['flash'].empty? + if (!session.respond_to?(:loaded?) || session.loaded?) && # (reset_session uses {}, which doesn't implement #loaded?) + session.key?('flash') && session['flash'].empty? session.delete('flash') end end diff --git a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb index 6a8e690d18..45a00b5056 100644 --- a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb +++ b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb @@ -74,6 +74,13 @@ module ActionDispatch class AbstractStore < Rack::Session::Abstract::ID include Compatibility include StaleSessionCheck + + private + + def set_cookie(env, session_id, cookie) + request = ActionDispatch::Request.new(env) + request.cookie_jar[key] = cookie + end end end end diff --git a/actionpack/lib/action_dispatch/routing/redirection.rb b/actionpack/lib/action_dispatch/routing/redirection.rb index ae01781013..444a79c50d 100644 --- a/actionpack/lib/action_dispatch/routing/redirection.rb +++ b/actionpack/lib/action_dispatch/routing/redirection.rb @@ -1,4 +1,6 @@ require 'action_dispatch/http/request' +require 'active_support/core_ext/uri' +require 'rack/utils' module ActionDispatch module Routing @@ -46,8 +48,17 @@ module ActionDispatch :params => request.query_parameters }.merge options + if !params.empty? && url_options[:path].match(/%\{\w*\}/) + url_options[:path] = (url_options[:path] % escape_path(params)) + end + ActionDispatch::Http::URL.url_for url_options end + + private + def escape_path(params) + Hash[params.map{ |k,v| [k, URI.parser.escape(v)] }] + end end module Redirection @@ -96,13 +107,18 @@ module ActionDispatch path = args.shift block = lambda { |params, request| - (params.empty? || !path.match(/%\{\w*\}/)) ? path : (path % params) + (params.empty? || !path.match(/%\{\w*\}/)) ? path : (path % escape(params)) } if String === path block = path if path.respond_to? :call raise ArgumentError, "redirection argument not supported" unless block Redirect.new status, block end + + private + def escape(params) + Hash[params.map{ |k,v| [k, Rack::Utils.escape(v)] }] + end 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 dd4e9ae4cc..35f91cec18 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,5 +1,6 @@ require 'thread' require 'active_support/core_ext/file' +require 'active_support/core_ext/module/attribute_accessors' module ActionView module Helpers diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb index 81f856feda..6bd8e62e0d 100644 --- a/actionpack/lib/action_view/helpers/date_helper.rb +++ b/actionpack/lib/action_view/helpers/date_helper.rb @@ -19,7 +19,7 @@ module ActionView # of \date[month]. module DateHelper # Reports the approximate distance in time between two Time, Date or DateTime objects or integers as seconds. - # Set <tt>include_seconds</tt> to true if you want more detailed approximations when distance < 1 min, 29 secs. + # Pass <tt>:include_seconds => true</tt> if you want more detailed approximations when distance < 1 min, 29 secs. # Distances are reported based on the following table: # # 0 <-> 29 secs # => less than a minute @@ -29,14 +29,15 @@ module ActionView # 89 mins, 30 secs <-> 23 hrs, 59 mins, 29 secs # => about [2..24] hours # 23 hrs, 59 mins, 30 secs <-> 41 hrs, 59 mins, 29 secs # => 1 day # 41 hrs, 59 mins, 30 secs <-> 29 days, 23 hrs, 59 mins, 29 secs # => [2..29] days - # 29 days, 23 hrs, 59 mins, 30 secs <-> 59 days, 23 hrs, 59 mins, 29 secs # => about 1 month + # 29 days, 23 hrs, 59 mins, 30 secs <-> 44 days, 23 hrs, 59 mins, 29 secs # => about 1 month + # 44 days, 23 hrs, 59 mins, 30 secs <-> 59 days, 23 hrs, 59 mins, 29 secs # => about 2 months # 59 days, 23 hrs, 59 mins, 30 secs <-> 1 yr minus 1 sec # => [2..12] months # 1 yr <-> 1 yr, 3 months # => about 1 year # 1 yr, 3 months <-> 1 yr, 9 months # => over 1 year # 1 yr, 9 months <-> 2 yr minus 1 sec # => almost 2 years # 2 yrs <-> max time or date # => (same rules as 1 yr) # - # With <tt>include_seconds</tt> = true and the difference < 1 minute 29 seconds: + # With <tt>:include_seconds => true</tt> and the difference < 1 minute 29 seconds: # 0-4 secs # => less than 5 seconds # 5-9 secs # => less than 10 seconds # 10-19 secs # => less than 20 seconds @@ -46,28 +47,33 @@ module ActionView # # ==== Examples # from_time = Time.now - # distance_of_time_in_words(from_time, from_time + 50.minutes) # => about 1 hour - # distance_of_time_in_words(from_time, 50.minutes.from_now) # => about 1 hour - # 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) # => 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 - # distance_of_time_in_words(from_time, from_time + 1.year + 3.days) # => about 1 year - # distance_of_time_in_words(from_time, from_time + 3.years + 6.months) # => over 3 years + # distance_of_time_in_words(from_time, from_time + 50.minutes) # => about 1 hour + # distance_of_time_in_words(from_time, 50.minutes.from_now) # => about 1 hour + # 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, :include_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) # => 3 days + # distance_of_time_in_words(from_time, from_time + 45.seconds, :include_seconds => true) # => less than a minute + # distance_of_time_in_words(from_time, from_time - 45.seconds, :include_seconds => true) # => less than a minute + # distance_of_time_in_words(from_time, 76.seconds.from_now) # => 1 minute + # distance_of_time_in_words(from_time, from_time + 1.year + 3.days) # => about 1 year + # distance_of_time_in_words(from_time, from_time + 3.years + 6.months) # => over 3 years # distance_of_time_in_words(from_time, from_time + 4.years + 9.days + 30.minutes + 5.seconds) # => about 4 years # # to_time = Time.now + 6.years + 19.days - # distance_of_time_in_words(from_time, to_time, true) # => about 6 years - # distance_of_time_in_words(to_time, from_time, true) # => about 6 years - # distance_of_time_in_words(Time.now, Time.now) # => less than a minute - # - # distance_of_time_in_words(70) # => 1 minute - # distance_of_time_in_words(60*60) # => about 1 hour - # - def distance_of_time_in_words(from_time, to_time = 0, include_seconds = false, options = {}) + # distance_of_time_in_words(from_time, to_time, :include_seconds => true) # => about 6 years + # distance_of_time_in_words(to_time, from_time, :include_seconds => true) # => about 6 years + # distance_of_time_in_words(Time.now, Time.now) # => less than a minute + # + def distance_of_time_in_words(from_time, to_time = 0, include_seconds_or_options = {}, options = {}) + unless include_seconds_or_options.is_a?(Hash) + ActiveSupport::Deprecation.warn "distance_of_time_in_words and time_ago_in_words now accept :include_seconds " + + "as a part of options hash, not a boolean argument", caller + options[:include_seconds] ||= !!include_seconds_or_options + else + options = include_seconds_or_options + end + from_time = from_time.to_time if from_time.respond_to?(:to_time) to_time = to_time.to_time if to_time.respond_to?(:to_time) from_time, to_time = to_time, from_time if from_time > to_time @@ -79,7 +85,7 @@ module ActionView when 0..1 return distance_in_minutes == 0 ? locale.t(:less_than_x_minutes, :count => 1) : - locale.t(:x_minutes, :count => distance_in_minutes) unless include_seconds + locale.t(:x_minutes, :count => distance_in_minutes) unless options[:include_seconds] case distance_in_seconds when 0..4 then locale.t :less_than_x_seconds, :count => 5 @@ -90,13 +96,18 @@ module ActionView else locale.t :x_minutes, :count => 1 end - when 2..44 then locale.t :x_minutes, :count => distance_in_minutes - when 45..89 then locale.t :about_x_hours, :count => 1 - when 90..1439 then locale.t :about_x_hours, :count => (distance_in_minutes.to_f / 60.0).round - when 1440..2519 then locale.t :x_days, :count => 1 - when 2520..43199 then locale.t :x_days, :count => (distance_in_minutes.to_f / 1440.0).round - when 43200..86399 then locale.t :about_x_months, :count => 1 - when 86400..525599 then locale.t :x_months, :count => (distance_in_minutes.to_f / 43200.0).round + when 2...45 then locale.t :x_minutes, :count => distance_in_minutes + when 45...90 then locale.t :about_x_hours, :count => 1 + # 90 mins up to 24 hours + when 90...1440 then locale.t :about_x_hours, :count => (distance_in_minutes.to_f / 60.0).round + # 24 hours up to 42 hours + when 1440...2520 then locale.t :x_days, :count => 1 + # 42 hours up to 30 days + when 2520...43200 then locale.t :x_days, :count => (distance_in_minutes.to_f / 1440.0).round + # 30 days up to 60 days + when 43200...86400 then locale.t :about_x_months, :count => (distance_in_minutes.to_f / 43200.0).round + # 60 days up to 365 days + when 86400...525600 then locale.t :x_months, :count => (distance_in_minutes.to_f / 43200.0).round else if from_time.acts_like?(:time) && to_time.acts_like?(:time) fyear = from_time.year @@ -130,15 +141,16 @@ module ActionView # Like <tt>distance_of_time_in_words</tt>, but where <tt>to_time</tt> is fixed to <tt>Time.now</tt>. # # ==== Examples - # time_ago_in_words(3.minutes.from_now) # => 3 minutes - # time_ago_in_words(Time.now - 15.hours) # => about 15 hours - # time_ago_in_words(Time.now) # => less than a minute + # time_ago_in_words(3.minutes.from_now) # => 3 minutes + # time_ago_in_words(Time.now - 15.hours) # => about 15 hours + # time_ago_in_words(Time.now) # => less than a minute + # time_ago_in_words(Time.now, :include_seconds => true) # => less than 5 seconds # # 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) + def time_ago_in_words(from_time, include_seconds_or_options = {}) + distance_of_time_in_words(from_time, Time.now, include_seconds_or_options) end alias_method :distance_of_time_in_words_to_now, :time_ago_in_words @@ -1005,6 +1017,8 @@ module ActionView # Returns the separator for a given datetime component. def separator(type) + return "" if @options[:use_hidden] + case type when :year, :month, :day @options[:"discard_#{type}"] ? "" : @options[:date_separator] diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index 8e7224937d..67f2abe509 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -10,6 +10,7 @@ require 'active_support/core_ext/object/blank' require 'active_support/core_ext/string/output_safety' require 'active_support/core_ext/array/extract_options' require 'active_support/deprecation' +require 'active_support/core_ext/string/inflections' module ActionView # = Action View Form Helpers @@ -1039,9 +1040,14 @@ module ActionView object_name = ActiveModel::Naming.param_key(object) end - builder = options[:builder] || ActionView::Base.default_form_builder + builder = options[:builder] || default_form_builder builder.new(object_name, object, self, options) end + + def default_form_builder + builder = ActionView::Base.default_form_builder + builder.respond_to?(:constantize) ? builder.constantize : builder + end end class FormBuilder diff --git a/actionpack/lib/action_view/helpers/javascript_helper.rb b/actionpack/lib/action_view/helpers/javascript_helper.rb index 5a869d6303..cc20518b93 100644 --- a/actionpack/lib/action_view/helpers/javascript_helper.rb +++ b/actionpack/lib/action_view/helpers/javascript_helper.rb @@ -15,7 +15,6 @@ module ActionView JS_ESCAPE_MAP["\342\200\250".force_encoding('UTF-8').encode!] = '
' JS_ESCAPE_MAP["\342\200\251".force_encoding('UTF-8').encode!] = '
' - # Escapes carriage returns and single and double quotes for JavaScript segments. # @@ -68,40 +67,6 @@ module ActionView def javascript_cdata_section(content) #:nodoc: "\n//#{cdata_section("\n#{content}\n//")}\n".html_safe end - - # Returns a button whose +onclick+ handler triggers the passed JavaScript. - # - # The helper receives a name, JavaScript code, and an optional hash of HTML options. The - # name is used as button label and the JavaScript code goes into its +onclick+ attribute. - # If +html_options+ has an <tt>:onclick</tt>, that one is put before +function+. - # - # button_to_function "Greeting", "alert('Hello world!')", :class => "ok" - # # => <input class="ok" onclick="alert('Hello world!');" type="button" value="Greeting" /> - # - def button_to_function(name, function=nil, html_options={}) - onclick = "#{"#{html_options[:onclick]}; " if html_options[:onclick]}#{function};" - - tag(:input, html_options.merge(:type => 'button', :value => name, :onclick => onclick)) - end - - # Returns a link whose +onclick+ handler triggers the passed JavaScript. - # - # The helper receives a name, JavaScript code, and an optional hash of HTML options. The - # name is used as the link text and the JavaScript code goes into the +onclick+ attribute. - # If +html_options+ has an <tt>:onclick</tt>, that one is put before +function+. Once all - # the JavaScript is set, the helper appends "; return false;". - # - # The +href+ attribute of the tag is set to "#" unless +html_options+ has one. - # - # link_to_function "Greeting", "alert('Hello world!')", :class => "nav_link" - # # => <a class="nav_link" href="#" onclick="alert('Hello world!'); return false;">Greeting</a> - # - def link_to_function(name, function, html_options={}) - onclick = "#{"#{html_options[:onclick]}; " if html_options[:onclick]}#{function}; return false;" - href = html_options[:href] || '#' - - content_tag(:a, name, html_options.merge(:href => href, :onclick => onclick)) - end end end end diff --git a/actionpack/lib/action_view/helpers/tag_helper.rb b/actionpack/lib/action_view/helpers/tag_helper.rb index 23777721f6..f7afa48256 100644 --- a/actionpack/lib/action_view/helpers/tag_helper.rb +++ b/actionpack/lib/action_view/helpers/tag_helper.rb @@ -109,8 +109,12 @@ module ActionView # # cdata_section(File.read("hello_world.txt")) # # => <![CDATA[<hello from a text file]]> + # + # cdata_section("hello]]>world") + # # => <![CDATA[hello]]]]><![CDATA[>world]]> def cdata_section(content) - "<![CDATA[#{content}]]>".html_safe + splitted = content.gsub(']]>', ']]]]><![CDATA[>') + "<![CDATA[#{splitted}]]>".html_safe end # Returns an escaped version of +html+ without affecting existing escaped entities. diff --git a/actionpack/lib/action_view/helpers/tags/check_box.rb b/actionpack/lib/action_view/helpers/tags/check_box.rb index 1a4aebb936..9d17a1dde3 100644 --- a/actionpack/lib/action_view/helpers/tags/check_box.rb +++ b/actionpack/lib/action_view/helpers/tags/check_box.rb @@ -41,17 +41,15 @@ module ActionView def checked?(value) case value when TrueClass, FalseClass - value + value == !!@checked_value when NilClass false - when Integer - value != 0 when String value == @checked_value when Array value.include?(@checked_value) else - value.to_i != 0 + value.to_i == @checked_value.to_i end end diff --git a/actionpack/lib/action_view/helpers/translation_helper.rb b/actionpack/lib/action_view/helpers/translation_helper.rb index cc74eff53a..fd06bfa2a8 100644 --- a/actionpack/lib/action_view/helpers/translation_helper.rb +++ b/actionpack/lib/action_view/helpers/translation_helper.rb @@ -45,6 +45,7 @@ module ActionView # you know what kind of output to expect when you call translate in a template. def translate(key, options = {}) options.merge!(:rescue_format => :html) unless options.key?(:rescue_format) + options[:default] = wrap_translate_defaults(options[:default]) if options[:default] if html_safe_translation_key?(key) html_safe_options = options.dup options.except(*I18n::RESERVED_KEYS).each do |name, value| @@ -83,6 +84,21 @@ module ActionView def html_safe_translation_key?(key) key.to_s =~ /(\b|_|\.)html$/ end + + def wrap_translate_defaults(defaults) + new_defaults = [] + defaults = Array(defaults) + while key = defaults.shift + if key.is_a?(Symbol) + new_defaults << lambda { |_, options| translate key, options.merge(:default => defaults) } + break + else + new_defautls << key + end + end + + new_defaults + end end end end diff --git a/actionpack/lib/action_view/renderer/partial_renderer.rb b/actionpack/lib/action_view/renderer/partial_renderer.rb index 34ea06c9cf..c5d5540510 100644 --- a/actionpack/lib/action_view/renderer/partial_renderer.rb +++ b/actionpack/lib/action_view/renderer/partial_renderer.rb @@ -158,8 +158,8 @@ module ActionView # Name: <%= user.name %> # </div> # - # If a collection is given, the layout will be rendered once for each item in the collection. Just think - # these two snippets have the same output: + # If a collection is given, the layout will be rendered once for each item in + # the collection. Just think these two snippets have the same output: # # <%# app/views/users/_user.html.erb %> # Name: <%= user.name %> @@ -184,7 +184,7 @@ module ActionView # <%= render :partial => "user", :layout => "li_layout", :collection => users %> # </ul> # - # Given two users whose names are Alice and Bob, these snippets return: + # Given two users whose names are Alice and Bob, these snippets return: # # <ul> # <li> @@ -195,6 +195,10 @@ module ActionView # </li> # </ul> # + # The current object being rendered, as well as the object_counter, will be + # available as local variables inside the layout template under the same names + # as available in the partial. + # # You can also apply a layout to a block within any template: # # <%# app/views/users/_chief.html.erb &> @@ -282,14 +286,7 @@ module ActionView spacer = find_template(@options[:spacer_template]).render(@view, @locals) end - if layout = @options[:layout] - layout = find_template(layout) - end - result = @template ? collection_with_template : collection_without_template - - result.map!{|content| layout.render(@view, @locals) { content } } if layout - result.join(spacer).html_safe end @@ -298,7 +295,7 @@ module ActionView object, as = @object, @variable if !block && (layout = @options[:layout]) - layout = find_template(layout) + layout = find_template(layout, @locals.keys + [@variable]) end object ||= locals[as] @@ -384,17 +381,23 @@ module ActionView segments, locals, template = [], @locals, @template as, counter = @variable, @variable_counter + if layout = @options[:layout] + layout = find_template(layout, @locals.keys + [@variable, @variable_counter]) + end + locals[counter] = -1 @collection.each do |object| locals[counter] += 1 locals[as] = object - segments << template.render(@view, locals) + + content = template.render(@view, locals) + content = layout.render(@view, locals) { content } if layout + segments << content end - + segments end - def collection_without_template segments, locals, collection_data = [], @locals, @collection_data |