aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib/action_view
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack/lib/action_view')
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helper.rb4
-rw-r--r--actionpack/lib/action_view/helpers/form_helper.rb17
-rw-r--r--actionpack/lib/action_view/helpers/form_options_helper.rb27
-rw-r--r--actionpack/lib/action_view/helpers/form_tag_helper.rb11
-rw-r--r--actionpack/lib/action_view/helpers/number_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/tag_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/tags.rb1
-rw-r--r--actionpack/lib/action_view/helpers/tags/time_field.rb14
-rw-r--r--actionpack/lib/action_view/helpers/text_helper.rb39
-rw-r--r--actionpack/lib/action_view/template.rb32
10 files changed, 106 insertions, 43 deletions
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
index a7a4ce21ff..02c1250c76 100644
--- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
@@ -274,7 +274,7 @@ 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')
+ source.present? ? asset_paths.compute_public_path(source, 'images') : ""
end
alias_method :path_to_image, :image_path # aliased to avoid conflicts with an image_path named route
@@ -377,7 +377,7 @@ module ActionView
src = options[:src] = path_to_image(source)
- unless src =~ /^(?:cid|data):/
+ unless src =~ /^(?:cid|data):/ || src.blank?
options[:alt] = options.fetch(:alt){ image_alt(src) }
end
diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb
index cc1f133196..6510610034 100644
--- a/actionpack/lib/action_view/helpers/form_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_helper.rb
@@ -990,6 +990,23 @@ module ActionView
Tags::DateField.new(object_name, method, self, options).render
end
+ # Returns a text_field of type "time".
+ #
+ # The default value is generated by trying to call +strftime+ with "%T.%L"
+ # on the objects's value. It is still possible to override that
+ # by passing the "value" option.
+ #
+ # === Options
+ # * Accepts same options as time_field_tag
+ #
+ # === Example
+ # time_field("task", "started_at")
+ # # => <input id="task_started_at" name="task[started_at]" type="time" />
+ #
+ def time_field(object_name, method, options = {})
+ Tags::TimeField.new(object_name, method, self, options).render
+ end
+
# Returns a text_field of type "url".
#
# url_field("user", "homepage")
diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb
index 52eb1aa447..eef426703d 100644
--- a/actionpack/lib/action_view/helpers/form_options_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_options_helper.rb
@@ -477,8 +477,8 @@ module ActionView
#
# Sample usage (Hash):
# grouped_options = {
- # 'North America' => [['United States','US'], 'Canada'],
- # 'Europe' => ['Denmark','Germany','France']
+ # 'North America' => [['United States','US'], 'Canada'],
+ # 'Europe' => ['Denmark','Germany','France']
# }
# grouped_options_for_select(grouped_options)
#
@@ -495,10 +495,10 @@ module ActionView
#
# Sample usage (divider):
# grouped_options = [
- # [['United States','US'], 'Canada'],
- # ['Denmark','Germany','France']
+ # [['United States','US'], 'Canada'],
+ # ['Denmark','Germany','France']
# ]
- # grouped_options_for_select(grouped_options, divider: '---------')
+ # grouped_options_for_select(grouped_options, nil, divider: '---------')
#
# Possible output:
# <optgroup label="---------">
@@ -513,15 +513,14 @@ module ActionView
#
# <b>Note:</b> Only the <tt><optgroup></tt> and <tt><option></tt> tags are returned, so you still have to
# wrap the output in an appropriate <tt><select></tt> tag.
- def grouped_options_for_select(*args)
- grouped_options = args.shift
- options = args.extract_options!
- selected_key = args.shift
- if prompt = args.shift
- ActiveSupport::Deprecation.warn 'Passing the prompt to grouped_options_for_select as an argument is deprecated. Please pass it in an options hash.'
- else
- prompt = options[:prompt]
+ def grouped_options_for_select(grouped_options, selected_key = nil, options = {})
+ if options.is_a?(Hash)
+ prompt = options[:prompt]
divider = options[:divider]
+ else
+ prompt = options
+ options = {}
+ ActiveSupport::Deprecation.warn "Passing the prompt to grouped_options_for_select as an argument is deprecated. Please use an options hash like `{ prompt: #{prompt.inspect} }`."
end
body = "".html_safe
@@ -534,7 +533,7 @@ module ActionView
grouped_options.each do |container|
if divider
- label, container = divider, container
+ label = divider
else
label, container = container
end
diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb
index 9e5c66f4a9..e65b4e3e95 100644
--- a/actionpack/lib/action_view/helpers/form_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb
@@ -549,6 +549,17 @@ module ActionView
text_field_tag(name, value, options.stringify_keys.update("type" => "date"))
end
+ # Creates a text field of type "time".
+ #
+ # === Options
+ # * <tt>:min</tt> - The minimum acceptable value.
+ # * <tt>:max</tt> - The maximum acceptable value.
+ # * <tt>:step</tt> - The acceptable value granularity.
+ # * Otherwise accepts the same options as text_field_tag.
+ def time_field_tag(name, value = nil, options = {})
+ text_field_tag(name, value, options.stringify_keys.update("type" => "time"))
+ end
+
# Creates a text field of type "url".
#
# ==== Options
diff --git a/actionpack/lib/action_view/helpers/number_helper.rb b/actionpack/lib/action_view/helpers/number_helper.rb
index 62455b97f9..dfc26acfad 100644
--- a/actionpack/lib/action_view/helpers/number_helper.rb
+++ b/actionpack/lib/action_view/helpers/number_helper.rb
@@ -254,7 +254,7 @@ module ActionView
parts = number.to_s.to_str.split('.')
parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{options[:delimiter]}")
- parts.join(options[:separator]).html_safe
+ safe_join(parts, options[:separator])
end
# Formats a +number+ with the specified level of
diff --git a/actionpack/lib/action_view/helpers/tag_helper.rb b/actionpack/lib/action_view/helpers/tag_helper.rb
index 498be596ad..9572f1c192 100644
--- a/actionpack/lib/action_view/helpers/tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/tag_helper.rb
@@ -41,7 +41,7 @@ module ActionView
# thus accessed as <tt>dataset.userId</tt>.
#
# Values are encoded to JSON, with the exception of strings and symbols.
- # This may come in handy when using jQuery's HTML5-aware <tt>.data()<tt>
+ # This may come in handy when using jQuery's HTML5-aware <tt>.data()</tt>
# from 1.4.3.
#
# ==== Examples
diff --git a/actionpack/lib/action_view/helpers/tags.rb b/actionpack/lib/action_view/helpers/tags.rb
index 3cf762877f..5cd77c8ec3 100644
--- a/actionpack/lib/action_view/helpers/tags.rb
+++ b/actionpack/lib/action_view/helpers/tags.rb
@@ -25,6 +25,7 @@ module ActionView
autoload :TelField
autoload :TextArea
autoload :TextField
+ autoload :TimeField
autoload :TimeSelect
autoload :TimeZoneSelect
autoload :UrlField
diff --git a/actionpack/lib/action_view/helpers/tags/time_field.rb b/actionpack/lib/action_view/helpers/tags/time_field.rb
new file mode 100644
index 0000000000..271dc00c54
--- /dev/null
+++ b/actionpack/lib/action_view/helpers/tags/time_field.rb
@@ -0,0 +1,14 @@
+module ActionView
+ module Helpers
+ module Tags
+ class TimeField < TextField #:nodoc:
+ def render
+ options = @options.stringify_keys
+ options["value"] = @options.fetch("value") { value(object).try(:strftime, "%T.%L") }
+ @options = options
+ super
+ end
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb
index da8c0d1de6..8cd7cf0052 100644
--- a/actionpack/lib/action_view/helpers/text_helper.rb
+++ b/actionpack/lib/action_view/helpers/text_helper.rb
@@ -81,8 +81,7 @@ module ActionView
# truncate("<p>Once upon a time in a world far far away</p>")
# # => "<p>Once upon a time in a wo..."
def truncate(text, options = {})
- options.reverse_merge!(:length => 30)
- text.truncate(options.delete(:length), options) if text
+ text.truncate(options.fetch(:length, 30), options) if text
end
# Highlights one or more +phrases+ everywhere in +text+ by inserting it into
@@ -102,14 +101,14 @@ module ActionView
# highlight('You searched for: rails', 'rails', :highlighter => '<a href="search?q=\1">\1</a>')
# # => You searched for: <a href="search?q=rails">rails</a>
def highlight(text, phrases, options = {})
- options[:highlighter] ||= '<mark>\1</mark>'
-
- text = sanitize(text) unless options[:sanitize] == false
+ highlighter = options.fetch(:highlighter, '<mark>\1</mark>')
+
+ text = sanitize(text) if options.fetch(:sanitize, true)
if text.blank? || phrases.blank?
text
else
match = Array(phrases).map { |p| Regexp.escape(p) }.join('|')
- text.gsub(/(#{match})(?![^<]*?>)/i, options[:highlighter])
+ text.gsub(/(#{match})(?![^<]*?>)/i, highlighter)
end.html_safe
end
@@ -135,8 +134,8 @@ module ActionView
# # => <chop> is also an example
def excerpt(text, phrase, options = {})
return unless text && phrase
- radius = options[:radius] || 100
- omission = options[:omission] || "..."
+ radius = options.fetch(:radius, 100)
+ omission = options.fetch(:omission, "...")
phrase = Regexp.escape(phrase)
return unless found_pos = text =~ /(#{phrase})/i
@@ -152,7 +151,7 @@ module ActionView
# Attempts to pluralize the +singular+ word unless +count+ is 1. If
# +plural+ is supplied, it will use that when count is > 1, otherwise
- # it will use the Inflector to determine the plural form
+ # it will use the Inflector to determine the plural form.
#
# pluralize(1, 'person')
# # => 1 person
@@ -166,7 +165,13 @@ module ActionView
# pluralize(0, 'person')
# # => 0 people
def pluralize(count, singular, plural = nil)
- "#{count || 0} " + ((count == 1 || count =~ /^1(\.0+)?$/) ? singular : (plural || singular.pluralize))
+ word = if (count == 1 || count =~ /^1(\.0+)?$/)
+ singular
+ else
+ plural || singular.pluralize
+ end
+
+ "#{count || 0} #{word}"
end
# Wraps the +text+ into lines no longer than +line_width+ width. This method
@@ -185,7 +190,7 @@ module ActionView
# word_wrap('Once upon a time', :line_width => 1)
# # => Once\nupon\na\ntime
def word_wrap(text, options = {})
- line_width = options[:line_width] || 80
+ line_width = options.fetch(:line_width, 80)
text.split("\n").collect do |line|
line.length > line_width ? line.gsub(/(.{1,#{line_width}})(\s+|$)/, "\\1\n").strip : line
@@ -203,13 +208,16 @@ module ActionView
#
# ==== Options
# * <tt>:sanitize</tt> - If +false+, does not sanitize +text+.
- # * <tt>:wrapper_tag</tt> - String representing the tag wrapper, defaults to <tt>"p"</tt>
+ # * <tt>:wrapper_tag</tt> - String representing the wrapper tag, defaults to <tt>"p"</tt>
#
# ==== Examples
# my_text = "Here is some basic text...\n...with a line break."
#
# simple_format(my_text)
# # => "<p>Here is some basic text...\n<br />...with a line break.</p>"
+ #
+ # simple_format(my_text, {}, :wrapper_tag => "div")
+ # # => "<div>Here is some basic text...\n<br />...with a line break.</div>"
#
# more_text = "We want to put a paragraph...\n\n...right there."
#
@@ -221,9 +229,10 @@ module ActionView
#
# simple_format("<span>I'm allowed!</span> It's true.", {}, :sanitize => false)
# # => "<p><span>I'm allowed!</span> It's true.</p>"
- def simple_format(text, html_options={}, options={})
- text = sanitize(text) unless options[:sanitize] == false
+ def simple_format(text, html_options = {}, options = {})
wrapper_tag = options.fetch(:wrapper_tag, :p)
+
+ text = sanitize(text) if options.fetch(:sanitize, true)
paragraphs = split_paragraphs(text)
if paragraphs.empty?
@@ -274,7 +283,7 @@ module ActionView
# <% end %>
def cycle(first_value, *values)
options = values.extract_options!
- name = options.fetch(:name, "default")
+ name = options.fetch(:name, 'default')
values.unshift(first_value)
diff --git a/actionpack/lib/action_view/template.rb b/actionpack/lib/action_view/template.rb
index edb3d427d5..cd79468502 100644
--- a/actionpack/lib/action_view/template.rb
+++ b/actionpack/lib/action_view/template.rb
@@ -1,6 +1,7 @@
require 'active_support/core_ext/object/blank'
require 'active_support/core_ext/object/try'
require 'active_support/core_ext/kernel/singleton_class'
+require 'thread'
module ActionView
# = Action View Template
@@ -122,6 +123,7 @@ module ActionView
@virtual_path = details[:virtual_path]
@updated_at = details[:updated_at] || Time.now
@formats = Array(format).map { |f| f.is_a?(Mime::Type) ? f.ref : f }
+ @compile_mutex = Mutex.new
end
# Returns if the underlying handler supports streaming. If so,
@@ -223,18 +225,28 @@ module ActionView
def compile!(view) #:nodoc:
return if @compiled
- if view.is_a?(ActionView::CompiledTemplates)
- mod = ActionView::CompiledTemplates
- else
- mod = view.singleton_class
- end
+ # Templates can be used concurrently in threaded environments
+ # so compilation and any instance variable modification must
+ # be synchronized
+ @compile_mutex.synchronize do
+ # Any thread holding this lock will be compiling the template needed
+ # by the threads waiting. So re-check the @compiled flag to avoid
+ # re-compilation
+ return if @compiled
+
+ if view.is_a?(ActionView::CompiledTemplates)
+ mod = ActionView::CompiledTemplates
+ else
+ mod = view.singleton_class
+ end
- compile(view, mod)
+ compile(view, mod)
- # Just discard the source if we have a virtual path. This
- # means we can get the template back.
- @source = nil if @virtual_path
- @compiled = true
+ # Just discard the source if we have a virtual path. This
+ # means we can get the template back.
+ @source = nil if @virtual_path
+ @compiled = true
+ end
end
# Among other things, this method is responsible for properly setting