aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--actionpack/CHANGELOG.md7
-rw-r--r--actionpack/lib/action_view/helpers/text_helper.rb34
-rw-r--r--actionpack/test/template/text_helper_test.rb67
-rw-r--r--activesupport/lib/active_support/core_ext/string/filters.rb2
-rw-r--r--activesupport/test/core_ext/string_ext_test.rb4
-rw-r--r--guides/source/initialization.textile96
-rw-r--r--railties/lib/rails/application.rb2
-rw-r--r--railties/test/railties/engine_test.rb4
8 files changed, 125 insertions, 91 deletions
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md
index 920858d8c0..ba66b525bc 100644
--- a/actionpack/CHANGELOG.md
+++ b/actionpack/CHANGELOG.md
@@ -1,5 +1,12 @@
## Rails 4.0.0 (unreleased) ##
+* `truncate` now always returns an escaped HTMl-safe string. The option `:escape` can be used as
+ false to not escape the result.
+
+ *Li Ellis Gallardo + Rafael Mendonça França*
+
+* `truncate` now accepts a block to show extra content when the text is truncated. *Li Ellis Gallardo*
+
* Add `week_field`, `week_field_tag`, `month_field`, `month_field_tag`, `datetime_local_field`,
`datetime_local_field_tag`, `datetime_field` and `datetime_field_tag` helpers. *Carlos Galdino*
diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb
index 8cd7cf0052..0cc0d069ea 100644
--- a/actionpack/lib/action_view/helpers/text_helper.rb
+++ b/actionpack/lib/action_view/helpers/text_helper.rb
@@ -62,9 +62,11 @@ module ActionView
#
# Pass a <tt>:separator</tt> to truncate +text+ at a natural break.
#
- # The result is not marked as HTML-safe, so will be subject to the default escaping when
- # used in views, unless wrapped by <tt>raw()</tt>. Care should be taken if +text+ contains HTML tags
- # or entities, because truncation may produce invalid HTML (such as unbalanced or incomplete tags).
+ # Pass a block if you want to show extra content when the text is truncated.
+ #
+ # The result is marked as HTML-safe, but it is escaped by default, unless <tt>:escape</tt> is
+ # +false+. Care should be taken if +text+ contains HTML tags or entities, because truncation
+ # may produce invalid HTML (such as unbalanced or incomplete tags).
#
# truncate("Once upon a time in a world far far away")
# # => "Once upon a time in a world..."
@@ -80,8 +82,18 @@ 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 = {})
- text.truncate(options.fetch(:length, 30), options) if text
+ #
+ # truncate("Once upon a time in a world far far away") { link_to "Continue", "#" }
+ # # => "Once upon a time in a wo...<a href="#">Continue</a>"
+ def truncate(text, options = {}, &block)
+ if text
+ length = options.fetch(:length, 30)
+
+ content = text.truncate(length, options)
+ content = options[:escape] == false ? content.html_safe : ERB::Util.html_escape(content)
+ content << capture(&block) if block_given? && text.length > length
+ content
+ end
end
# Highlights one or more +phrases+ everywhere in +text+ by inserting it into
@@ -102,7 +114,7 @@ module ActionView
# # => You searched for: <a href="search?q=rails">rails</a>
def highlight(text, phrases, options = {})
highlighter = options.fetch(:highlighter, '<mark>\1</mark>')
-
+
text = sanitize(text) if options.fetch(:sanitize, true)
if text.blank? || phrases.blank?
text
@@ -165,12 +177,12 @@ module ActionView
# pluralize(0, 'person')
# # => 0 people
def pluralize(count, singular, plural = nil)
- word = if (count == 1 || count =~ /^1(\.0+)?$/)
- singular
+ word = if (count == 1 || count =~ /^1(\.0+)?$/)
+ singular
else
plural || singular.pluralize
end
-
+
"#{count || 0} #{word}"
end
@@ -215,7 +227,7 @@ module ActionView
#
# 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>"
#
@@ -231,7 +243,7 @@ module ActionView
# # => "<p><span>I'm allowed!</span> It's true.</p>"
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)
diff --git a/actionpack/test/template/text_helper_test.rb b/actionpack/test/template/text_helper_test.rb
index f58e474759..a3ab091c6c 100644
--- a/actionpack/test/template/text_helper_test.rb
+++ b/actionpack/test/template/text_helper_test.rb
@@ -60,14 +60,14 @@ class TextHelperTest < ActionView::TestCase
simple_format(text)
assert_equal text_clone, text
end
-
+
def test_simple_format_does_not_modify_the_html_options_hash
options = { :class => "foobar"}
passed_options = options.dup
simple_format("some text", passed_options)
assert_equal options, passed_options
end
-
+
def test_simple_format_does_not_modify_the_options_hash
options = { :wrapper_tag => :div, :sanitize => false }
passed_options = options.dup
@@ -75,19 +75,11 @@ class TextHelperTest < ActionView::TestCase
assert_equal options, passed_options
end
- def test_truncate_should_not_be_html_safe
- assert !truncate("Hello World!", :length => 12).html_safe?
- end
-
def test_truncate
assert_equal "Hello World!", truncate("Hello World!", :length => 12)
assert_equal "Hello Wor...", truncate("Hello World!!", :length => 12)
end
- def test_truncate_should_not_escape_input
- assert_equal "Hello <sc...", truncate("Hello <script>code!</script>World!!", :length => 12)
- end
-
def test_truncate_should_use_default_length_of_30
str = "This is a string that will go longer then the default truncate length of 30"
assert_equal str[0...27] + "...", truncate(str)
@@ -106,7 +98,7 @@ class TextHelperTest < ActionView::TestCase
assert_equal "\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 ...".force_encoding('UTF-8'),
truncate("\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 \354\225\204\353\235\274\353\246\254\354\230\244".force_encoding('UTF-8'), :length => 10)
end
-
+
def test_truncate_does_not_modify_the_options_hash
options = { :length => 10 }
passed_options = options.dup
@@ -114,6 +106,53 @@ class TextHelperTest < ActionView::TestCase
assert_equal options, passed_options
end
+ def test_truncate_with_link_options
+ assert_equal "Here's a long test and I...<a href=\"#\">Continue</a>",
+ truncate("Here's a long test and I need a continue to read link", :length => 27) { link_to 'Continue', '#' }
+ end
+
+ def test_truncate_should_be_html_safe
+ assert truncate("Hello World!", :length => 12).html_safe?
+ end
+
+ def test_truncate_should_escape_the_input
+ assert_equal "Hello &lt;sc...", truncate("Hello <script>code!</script>World!!", :length => 12)
+ end
+
+ def test_truncate_should_not_escape_the_input_with_escape_false
+ assert_equal "Hello <sc...", truncate("Hello <script>code!</script>World!!", :length => 12, :escape => false)
+ end
+
+ def test_truncate_with_escape_false_should_be_html_safe
+ truncated = truncate("Hello <script>code!</script>World!!", :length => 12, :escape => false)
+ assert truncated.html_safe?
+ end
+
+ def test_truncate_with_block_should_be_html_safe
+ truncated = truncate("Here's a long test and I need a continue to read link", :length => 27) { link_to 'Continue', '#' }
+ assert truncated.html_safe?
+ end
+
+ def test_truncate_with_block_should_escape_the_input
+ assert_equal "&lt;script&gt;code!&lt;/script&gt;He...<a href=\"#\">Continue</a>",
+ truncate("<script>code!</script>Here's a long test and I need a continue to read link", :length => 27) { link_to 'Continue', '#' }
+ end
+
+ def test_truncate_with_block_should_not_escape_the_input_with_escape_false
+ assert_equal "<script>code!</script>He...<a href=\"#\">Continue</a>",
+ truncate("<script>code!</script>Here's a long test and I need a continue to read link", :length => 27, :escape => false) { link_to 'Continue', '#' }
+ end
+
+ def test_truncate_with_block_with_escape_false_should_be_html_safe
+ truncated = truncate("<script>code!</script>Here's a long test and I need a continue to read link", :length => 27, :escape => false) { link_to 'Continue', '#' }
+ assert truncated.html_safe?
+ end
+
+ def test_truncate_with_block_should_escape_the_block
+ assert_equal "Here's a long test and I...&lt;script&gt;alert('foo');&lt;/script&gt;",
+ truncate("Here's a long test and I need a continue to read link", :length => 27) { "<script>alert('foo');</script>" }
+ end
+
def test_highlight_should_be_html_safe
assert highlight("This is a beautiful morning", "beautiful").html_safe?
end
@@ -203,7 +242,7 @@ class TextHelperTest < ActionView::TestCase
highlight("<div>abc div</div>", "div", :highlighter => '<b>\1</b>')
)
end
-
+
def test_highlight_does_not_modify_the_options_hash
options = { :highlighter => '<b>\1</b>', :sanitize => false }
passed_options = options.dup
@@ -256,7 +295,7 @@ class TextHelperTest < ActionView::TestCase
def test_excerpt_with_utf8
assert_equal("...\357\254\203ciency could not be...".force_encoding('UTF-8'), excerpt("That's why e\357\254\203ciency could not be helped".force_encoding('UTF-8'), 'could', :radius => 8))
end
-
+
def test_excerpt_does_not_modify_the_options_hash
options = { :omission => "[...]",:radius => 5 }
passed_options = options.dup
@@ -271,7 +310,7 @@ class TextHelperTest < ActionView::TestCase
def test_word_wrap_with_extra_newlines
assert_equal("my very very\nvery long\nstring\n\nwith another\nline", word_wrap("my very very very long string\n\nwith another line", :line_width => 15))
end
-
+
def test_word_wrap_does_not_modify_the_options_hash
options = { :line_width => 15 }
passed_options = options.dup
diff --git a/activesupport/lib/active_support/core_ext/string/filters.rb b/activesupport/lib/active_support/core_ext/string/filters.rb
index 2478f42290..70f2dcb562 100644
--- a/activesupport/lib/active_support/core_ext/string/filters.rb
+++ b/activesupport/lib/active_support/core_ext/string/filters.rb
@@ -33,7 +33,7 @@ class String
# # => "Once upon a time in a..."
#
# The last characters will be replaced with the <tt>:omission</tt> string (defaults to "...")
- # for a total length not exceeding <tt>:length</tt>:
+ # for a total length not exceeding <tt>length</tt>:
#
# 'And they found that many people were sleeping better.'.truncate(25, :omission => '... (continued)')
# # => "And they f... (continued)"
diff --git a/activesupport/test/core_ext/string_ext_test.rb b/activesupport/test/core_ext/string_ext_test.rb
index 8437ef1347..e5b774425e 100644
--- a/activesupport/test/core_ext/string_ext_test.rb
+++ b/activesupport/test/core_ext/string_ext_test.rb
@@ -290,6 +290,10 @@ class StringInflectionsTest < ActiveSupport::TestCase
"\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 \354\225\204\353\235\274\353\246\254\354\230\244".force_encoding('UTF-8').truncate(10)
end
+ def test_truncate_should_not_be_html_safe
+ assert !"Hello World!".truncate(12).html_safe?
+ end
+
def test_constantize
run_constantize_tests_on do |string|
string.constantize
diff --git a/guides/source/initialization.textile b/guides/source/initialization.textile
index 913ff24290..48d4373afe 100644
--- a/guides/source/initialization.textile
+++ b/guides/source/initialization.textile
@@ -57,7 +57,7 @@ else
end
</ruby>
-The +rbconfig+ file from the Ruby standard library provides us with the +RbConfig+ class which contains detailed information about the Ruby environment, including how Ruby was compiled. We can see thisin use in +railties/lib/rails/script_rails_loader+.
+The +rbconfig+ file from the Ruby standard library provides us with the +RbConfig+ class which contains detailed information about the Ruby environment, including how Ruby was compiled. We can see this in use in +railties/lib/rails/script_rails_loader+.
<ruby>
require 'pathname'
@@ -157,11 +157,11 @@ The gems that a Rails 4 application depends on are as follows:
TODO: change these when the Rails 4 release is near.
* abstract (1.0.0)
-* actionmailer (3.1.0.beta)
-* actionpack (3.1.0.beta)
-* activemodel (3.1.0.beta)
-* activerecord (3.1.0.beta)
-* activesupport (3.1.0.beta)
+* actionmailer (4.0.0.beta)
+* actionpack (4.0.0.beta)
+* activemodel (4.0.0.beta)
+* activerecord (4.0.0.beta)
+* activesupport (4.0.0.beta)
* arel (2.0.7)
* builder (3.0.0)
* bundler (1.0.6)
@@ -174,8 +174,8 @@ TODO: change these when the Rails 4 release is near.
* rack-cache (0.5.3)
* rack-mount (0.6.13)
* rack-test (0.5.6)
-* rails (3.1.0.beta)
-* railties (3.1.0.beta)
+* rails (4.0.0.beta)
+* railties (4.0.0.beta)
* rake (0.8.7)
* sqlite3-ruby (1.3.2)
* thor (0.14.6)
@@ -191,6 +191,7 @@ ARGV << '--help' if ARGV.empty?
aliases = {
"g" => "generate",
+ "d" => "destroy",
"c" => "console",
"s" => "server",
"db" => "dbconsole",
@@ -579,28 +580,6 @@ this time to the +Array+ and +Hash+ classes. This file defines an
+extract_options!+ method which Rails uses to extract options from
parameters.
-<ruby>
-class Array
- # Extracts options from a set of arguments. Removes and returns the
- # last
- # element in the array if it's a hash, otherwise returns a blank hash.
- #
- # def options(*args)
- # args.extract_options!
- # end
- #
- # options(1, 2) # => {}
- # options(1, 2, :a => :b) # => {:a=>:b}
- def extract_options!
- if last.is_a?(Hash) && last.extractable_options?
- pop
- else
- {}
- end
- end
-end
-</ruby>
-
h4. +railties/lib/rails/application.rb+
The next file required by +railties/lib/rails.rb+ is +application.rb+.
@@ -612,8 +591,7 @@ Before the +Rails::Application+ class is
defined however, +rails/engine+ is also loaded, which is responsible for
handling the behavior and definitions of Rails engines.
-TIP: You can read more about engines in the "Getting Started with Engines":engines.html
-guide.
+TIP: You can read more about engines in the "Getting Started with Engines":engines.html guide.
Among other things, Rails Engine is also responsible for loading the
Railtie class.
@@ -678,7 +656,7 @@ h4. +activesupport/lib/active_support/deprecation/proxy_wrappers.rb+
+proxy_wrappers.rb+ defines deprecation wrappers for methods, instance variables and constants. Previously, this was used for the +RAILS_ENV+ and +RAILS_ROOT+ constants for 3.0 but since then these constants have been removed. The deprecation message that would be raised from these would be something like:
<plain>
- BadConstant is deprecated! Use GoodConstant instead.
+BadConstant is deprecated! Use GoodConstant instead.
</plain>
h4. +active_support/ordered_options+
@@ -689,7 +667,7 @@ The next file required is +active_support/core_ext/hash/deep_dup+ which is cover
h4. +active_support/core_ext/object+
-This file is responsible for requiring many more core extensions:
+This file is responsible for requiring many more Active Support core extensions:
<ruby>
require 'active_support/core_ext/object/acts_like'
@@ -947,7 +925,7 @@ The +initializers_chain+ method referenced in the +initializers_for+ method is d
<ruby>
def initializers_chain
initializers = Collection.new
- ancestors.reverse_each do | klass |
+ ancestors.reverse_each do |klass|
next unless klass.respond_to?(:initializers)
initializers = initializers + klass.initializers
end
@@ -1010,46 +988,35 @@ This file defines the +ActiveSupport::Railtie+ constant which like the +I18n::Ra
Then this Railtie sets up three more initializers:
-* +active_support.initialize_whiny_nils+
* +active_support.deprecation_behavior+
* +active_support.initialize_time_zone+
+* +active_support.set_configs+
We will cover what each of these initializers do when they run.
Once the +active_support/railtie+ file has finished loading the next file required from +railties/lib/rails.rb+ is the +action_dispatch/railtie+.
-h4. +activesupport/lib/action_dispatch/railtie.rb+
+h4. +actionpack/lib/action_dispatch/railtie.rb+
This file defines the +ActionDispatch::Railtie+ class, but not before requiring +action_dispatch+.
-h4. +activesupport/lib/action_dispatch.rb+
-
-This file attempts to locate the +active_support+ and +active_model+ libraries by looking a couple of directories back from the current file and then adds the +active_support+ and +active_model+ +lib+ directories to the load path, but only if they aren't already, which they are.
-
-<ruby>
-activesupport_path = File.expand_path('../../../activesupport/lib', __FILE__)
-$:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.include?(activesupport_path)
-
-activemodel_path = File.expand_path('../../../activemodel/lib', __FILE__)
-$:.unshift(activemodel_path) if File.directory?(activemodel_path) && !$:.include?(activemodel_path)
-</ruby>
-
-In effect, these lines only define the +activesupport_path+ and +activemodel_path+ variables and nothing more.
+h4. +actionpack/lib/action_dispatch.rb+
-The next two requires in this file are already done, so they are not run:
+This file starts off with the following requires:
<ruby>
require 'active_support'
require 'active_support/dependencies/autoload'
+require 'active_support/core_ext/module/attribute_accessors'
</ruby>
-The following require is to +action_pack+ (+activesupport/lib/action_pack.rb+) which has a 22-line copyright notice at the top of it and ends in a simple require to +action_pack/version+. This file, like other +version.rb+ files before it, defines the +ActionPack::VERSION+ constant:
+The following require is to +action_pack+ (+actionpack/lib/action_pack.rb+) which contains a simple require to +action_pack/version+. This file, like other +version.rb+ files before it, defines the +ActionPack::VERSION+ constant:
<ruby>
module ActionPack
module VERSION #:nodoc:
- MAJOR = 3
- MINOR = 1
+ MAJOR = 4
+ MINOR = 0
TINY = 0
PRE = "beta"
@@ -1067,8 +1034,8 @@ This file makes a require to +active_model/version+ which defines the version fo
<ruby>
module ActiveModel
module VERSION #:nodoc:
- MAJOR = 3
- MINOR = 1
+ MAJOR = 4
+ MINOR = 0
TINY = 0
PRE = "beta"
@@ -1105,7 +1072,7 @@ Once it has finished loading, the +I18n.load_path+ method is used to add the +ac
The loading of this file finishes the loading of +active_model+ and so we go back to +action_dispatch+.
-h4. Back to +activesupport/lib/action_dispatch.rb+
+h4. Back to +actionpack/lib/action_dispatch.rb+
The remainder of this file requires the +rack+ file from the Rack gem which defines the +Rack+ module. After +rack+, there's autoloads defined for the +Rack+, +ActionDispatch+, +ActionDispatch::Http+, +ActionDispatch::Session+. A new method called +autoload_under+ is used here, and this simply prefixes the files where the modules are autoloaded from with the path specified. For example here:
@@ -1119,7 +1086,7 @@ The +Assertions+ module is in the +action_dispatch/testing+ folder rather than s
Finally, this file defines a top-level autoload, the +Mime+ constant.
-h4. Back to +activesupport/lib/action_dispatch/railtie.rb+
+h4. Back to +actionpack/lib/action_dispatch/railtie.rb+
After +action_dispatch+ is required in this file, the +ActionDispatch::Railtie+ class is defined and is yet another class that inherits from +Rails::Railtie+. This class defines some initial configuration option defaults for +config.action_dispatch+ before setting up a single initializer called +action_dispatch.configure+.
@@ -1141,22 +1108,21 @@ h4. +activerecord/lib/active_record.rb+
This file begins by detecting if the +lib+ directories of +active_support+ and +active_model+ are not in the load path and if they aren't then adds them. As we saw back in +action_dispatch.rb+, these directories are already there.
-The first three requires have already been done by other files and so aren't loaded here, but the 4th require, the one to +arel+ will require the file provided by the Arel gem, which defines the +Arel+ module.
+The first couple of requires have already been done by other files and so aren't loaded here, but the next one to +arel+ will require the file provided by the Arel gem, which defines the +Arel+ module.
<ruby>
require 'active_support'
-require 'active_support/i18n'
require 'active_model'
require 'arel'
</ruby>
-The 5th require in this file is one to +active_record/version+ which defines the +ActiveRecord::VERSION+ constant:
+The file required next is +active_record/version+ which defines the +ActiveRecord::VERSION+ constant:
<ruby>
module ActiveRecord
module VERSION #:nodoc:
- MAJOR = 3
- MINOR = 1
+ MAJOR = 4
+ MINOR = 0
TINY = 0
PRE = "beta"
@@ -1180,7 +1146,9 @@ This will set the engine for +Arel::Table+ to be +ActiveRecord::Base+.
The file then finishes with this line:
<ruby>
-I18n.load_path << File.dirname(__FILE__) + '/active_record/locale/en.yml'
+ActiveSupport.on_load(:i18n) do
+ I18n.load_path << File.dirname(__FILE__) + '/active_record/locale/en.yml'
+end
</ruby>
This will add the translations from +activerecord/lib/active_record/locale/en.yml+ to the load path for +I18n+, with this file being parsed when all the translations are loaded.
diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb
index d44465e547..660e864d2a 100644
--- a/railties/lib/rails/application.rb
+++ b/railties/lib/rails/application.rb
@@ -185,7 +185,7 @@ module Rails
end
all = (railties.all - order)
- all.push(self) unless all.include?(self)
+ all.push(self) unless (all + order).include?(self)
order.push(:all) unless order.include?(:all)
index = order.index(:all)
diff --git a/railties/test/railties/engine_test.rb b/railties/test/railties/engine_test.rb
index 7a047ef93a..4437e2c8af 100644
--- a/railties/test/railties/engine_test.rb
+++ b/railties/test/railties/engine_test.rb
@@ -1098,6 +1098,10 @@ YAML
get("/assets/bar.js")
assert_equal "// App's bar js\n;", last_response.body.strip
+
+ # ensure that railties are not added twice
+ railties = Rails.application.ordered_railties.map(&:class)
+ assert_equal railties, railties.uniq
end
test "railties_order adds :all with lowest priority if not given" do