aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Heinemeier Hansson <david@loudthinking.com>2010-01-03 22:27:28 -0800
committerDavid Heinemeier Hansson <david@loudthinking.com>2010-01-03 22:27:28 -0800
commit5e94d3e3ea06e577d97a32eb4b38b61c4ce8ff8f (patch)
treeb89d06302223be67c595e65bffce54357d41a15a
parentd7d917335e242f48fae31bc99e6d4ab381244913 (diff)
parenteca11bfdb5d3cf375faf8a24e2dc0cc5abd20f92 (diff)
downloadrails-5e94d3e3ea06e577d97a32eb4b38b61c4ce8ff8f.tar.gz
rails-5e94d3e3ea06e577d97a32eb4b38b61c4ce8ff8f.tar.bz2
rails-5e94d3e3ea06e577d97a32eb4b38b61c4ce8ff8f.zip
Merge
-rw-r--r--actionmailer/CHANGELOG9
-rw-r--r--actionmailer/actionmailer.gemspec2
-rw-r--r--actionmailer/lib/action_mailer/base.rb2
-rwxr-xr-xactionmailer/lib/action_mailer/vendor/text-format-0.6.3/text/format.rb2933
-rw-r--r--actionmailer/test/mail_layout_test.rb18
-rw-r--r--actionmailer/test/mail_service_test.rb88
-rw-r--r--actionmailer/test/quoting_test.rb2
-rw-r--r--actionmailer/test/test_helper_test.rb4
-rw-r--r--actionmailer/test/tmail_compat_test.rb4
-rw-r--r--actionpack/lib/action_controller/caching.rb4
-rw-r--r--actionpack/lib/action_controller/caching/fragments.rb14
-rw-r--r--actionpack/lib/action_controller/caching/pages.rb8
-rw-r--r--actionpack/lib/action_controller/metal/logger.rb7
-rw-r--r--actionpack/lib/action_dispatch/middleware/session/cookie_store.rb2
-rw-r--r--actionpack/lib/action_dispatch/middleware/show_exceptions.rb24
-rw-r--r--actionpack/lib/action_view/base.rb1
-rw-r--r--actionpack/lib/action_view/helpers/form_helper.rb42
-rw-r--r--actionpack/lib/action_view/render/partials.rb7
-rw-r--r--actionpack/lib/action_view/render/rendering.rb5
-rw-r--r--actionpack/test/abstract_unit.rb5
-rw-r--r--actionpack/test/activerecord/controller_runtime_test.rb7
-rw-r--r--actionpack/test/controller/caching_test.rb16
-rw-r--r--actionpack/test/controller/logging_test.rb3
-rw-r--r--actionpack/test/dispatch/show_exceptions_test.rb23
-rw-r--r--actionpack/test/lib/controller/fake_models.rb1
-rw-r--r--actionpack/test/template/form_helper_test.rb40
-rw-r--r--activemodel/lib/active_model/errors.rb11
-rw-r--r--activemodel/lib/active_model/lint.rb14
-rw-r--r--activemodel/lib/active_model/locale/en.yml3
-rw-r--r--activemodel/lib/active_model/validator.rb3
-rw-r--r--activemodel/test/cases/lint_test.rb2
-rw-r--r--activemodel/test/cases/validations/i18n_validation_test.rb22
-rw-r--r--activemodel/test/cases/validations/with_validation_test.rb47
-rw-r--r--activemodel/test/cases/validations_test.rb6
-rw-r--r--activerecord/CHANGELOG5
-rw-r--r--activerecord/lib/active_record.rb1
-rwxr-xr-xactiverecord/lib/active_record/associations.rb81
-rw-r--r--activerecord/lib/active_record/associations/association_collection.rb4
-rw-r--r--activerecord/lib/active_record/autosave_association.rb2
-rwxr-xr-xactiverecord/lib/active_record/base.rb34
-rwxr-xr-xactiverecord/lib/active_record/connection_adapters/abstract_adapter.rb2
-rw-r--r--activerecord/lib/active_record/locale/en.yml3
-rw-r--r--activerecord/lib/active_record/named_scope.rb2
-rw-r--r--activerecord/lib/active_record/railtie.rb2
-rw-r--r--activerecord/lib/active_record/relation.rb87
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb8
-rw-r--r--activerecord/lib/active_record/relation/spawn_methods.rb85
-rw-r--r--activerecord/test/cases/autosave_association_test.rb31
-rw-r--r--activerecord/test/cases/multiple_db_test.rb6
-rw-r--r--activerecord/test/cases/nested_attributes_test.rb2
-rw-r--r--activerecord/test/cases/relations_test.rb89
-rw-r--r--activesupport/lib/active_support/cache.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/integer/multiple.rb2
-rw-r--r--activesupport/test/core_ext/integer_ext_test.rb5
-rw-r--r--activesupport/test/isolation_test.rb6
m---------rack0
-rw-r--r--railties/lib/rails/application.rb15
-rw-r--r--railties/lib/rails/generators.rb150
-rw-r--r--railties/lib/rails/generators/base.rb62
-rw-r--r--railties/lib/rails/generators/rails/app/app_generator.rb14
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/application.rb2
-rw-r--r--railties/lib/rails/generators/test_case.rb239
-rw-r--r--railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb4
-rw-r--r--railties/lib/rails/vendor/thor-0.12.1/lib/thor/version.rb3
-rw-r--r--railties/lib/rails/vendor/thor-0.12.3/CHANGELOG.rdoc (renamed from railties/lib/rails/vendor/thor-0.12.1/CHANGELOG.rdoc)12
-rw-r--r--railties/lib/rails/vendor/thor-0.12.3/LICENSE (renamed from railties/lib/rails/vendor/thor-0.12.1/LICENSE)0
-rw-r--r--railties/lib/rails/vendor/thor-0.12.3/README.rdoc (renamed from railties/lib/rails/vendor/thor-0.12.1/README.rdoc)0
-rw-r--r--railties/lib/rails/vendor/thor-0.12.3/Thorfile (renamed from railties/lib/rails/vendor/thor-0.12.1/Thorfile)0
-rw-r--r--railties/lib/rails/vendor/thor-0.12.3/lib/thor.rb (renamed from railties/lib/rails/vendor/thor-0.12.1/lib/thor.rb)84
-rw-r--r--railties/lib/rails/vendor/thor-0.12.3/lib/thor/actions.rb (renamed from railties/lib/rails/vendor/thor-0.12.1/lib/thor/actions.rb)15
-rw-r--r--railties/lib/rails/vendor/thor-0.12.3/lib/thor/actions/create_file.rb (renamed from railties/lib/rails/vendor/thor-0.12.1/lib/thor/actions/create_file.rb)4
-rw-r--r--railties/lib/rails/vendor/thor-0.12.3/lib/thor/actions/directory.rb (renamed from railties/lib/rails/vendor/thor-0.12.1/lib/thor/actions/directory.rb)0
-rw-r--r--railties/lib/rails/vendor/thor-0.12.3/lib/thor/actions/empty_directory.rb (renamed from railties/lib/rails/vendor/thor-0.12.1/lib/thor/actions/empty_directory.rb)0
-rw-r--r--railties/lib/rails/vendor/thor-0.12.3/lib/thor/actions/file_manipulation.rb (renamed from railties/lib/rails/vendor/thor-0.12.1/lib/thor/actions/file_manipulation.rb)8
-rw-r--r--railties/lib/rails/vendor/thor-0.12.3/lib/thor/actions/inject_into_file.rb (renamed from railties/lib/rails/vendor/thor-0.12.1/lib/thor/actions/inject_into_file.rb)4
-rw-r--r--railties/lib/rails/vendor/thor-0.12.3/lib/thor/base.rb (renamed from railties/lib/rails/vendor/thor-0.12.1/lib/thor/base.rb)62
-rw-r--r--railties/lib/rails/vendor/thor-0.12.3/lib/thor/core_ext/file_binary_read.rb9
-rw-r--r--railties/lib/rails/vendor/thor-0.12.3/lib/thor/core_ext/hash_with_indifferent_access.rb (renamed from railties/lib/rails/vendor/thor-0.12.1/lib/thor/core_ext/hash_with_indifferent_access.rb)2
-rw-r--r--railties/lib/rails/vendor/thor-0.12.3/lib/thor/core_ext/ordered_hash.rb (renamed from railties/lib/rails/vendor/thor-0.12.1/lib/thor/core_ext/ordered_hash.rb)0
-rw-r--r--railties/lib/rails/vendor/thor-0.12.3/lib/thor/error.rb (renamed from railties/lib/rails/vendor/thor-0.12.1/lib/thor/error.rb)0
-rw-r--r--railties/lib/rails/vendor/thor-0.12.3/lib/thor/group.rb (renamed from railties/lib/rails/vendor/thor-0.12.1/lib/thor/group.rb)46
-rw-r--r--railties/lib/rails/vendor/thor-0.12.3/lib/thor/invocation.rb (renamed from railties/lib/rails/vendor/thor-0.12.1/lib/thor/invocation.rb)0
-rw-r--r--railties/lib/rails/vendor/thor-0.12.3/lib/thor/parser.rb (renamed from railties/lib/rails/vendor/thor-0.12.1/lib/thor/parser.rb)0
-rw-r--r--railties/lib/rails/vendor/thor-0.12.3/lib/thor/parser/argument.rb (renamed from railties/lib/rails/vendor/thor-0.12.1/lib/thor/parser/argument.rb)0
-rw-r--r--railties/lib/rails/vendor/thor-0.12.3/lib/thor/parser/arguments.rb (renamed from railties/lib/rails/vendor/thor-0.12.1/lib/thor/parser/arguments.rb)0
-rw-r--r--railties/lib/rails/vendor/thor-0.12.3/lib/thor/parser/option.rb (renamed from railties/lib/rails/vendor/thor-0.12.1/lib/thor/parser/option.rb)2
-rw-r--r--railties/lib/rails/vendor/thor-0.12.3/lib/thor/parser/options.rb (renamed from railties/lib/rails/vendor/thor-0.12.1/lib/thor/parser/options.rb)0
-rw-r--r--railties/lib/rails/vendor/thor-0.12.3/lib/thor/rake_compat.rb (renamed from railties/lib/rails/vendor/thor-0.12.1/lib/thor/rake_compat.rb)0
-rw-r--r--railties/lib/rails/vendor/thor-0.12.3/lib/thor/runner.rb (renamed from railties/lib/rails/vendor/thor-0.12.1/lib/thor/runner.rb)86
-rw-r--r--railties/lib/rails/vendor/thor-0.12.3/lib/thor/shell.rb (renamed from railties/lib/rails/vendor/thor-0.12.1/lib/thor/shell.rb)2
-rw-r--r--railties/lib/rails/vendor/thor-0.12.3/lib/thor/shell/basic.rb (renamed from railties/lib/rails/vendor/thor-0.12.1/lib/thor/shell/basic.rb)76
-rw-r--r--railties/lib/rails/vendor/thor-0.12.3/lib/thor/shell/color.rb (renamed from railties/lib/rails/vendor/thor-0.12.1/lib/thor/shell/color.rb)2
-rw-r--r--railties/lib/rails/vendor/thor-0.12.3/lib/thor/task.rb (renamed from railties/lib/rails/vendor/thor-0.12.1/lib/thor/task.rb)75
-rw-r--r--railties/lib/rails/vendor/thor-0.12.3/lib/thor/util.rb (renamed from railties/lib/rails/vendor/thor-0.12.1/lib/thor/util.rb)26
-rw-r--r--railties/lib/rails/vendor/thor-0.12.3/lib/thor/version.rb3
-rw-r--r--railties/test/application/configuration_test.rb5
-rw-r--r--railties/test/generators/actions_test.rb12
-rw-r--r--railties/test/generators/app_generator_test.rb39
-rw-r--r--railties/test/generators/controller_generator_test.rb15
-rw-r--r--railties/test/generators/generator_generator_test.rb9
-rw-r--r--railties/test/generators/generators_test_helper.rb92
-rw-r--r--railties/test/generators/helper_generator_test.rb9
-rw-r--r--railties/test/generators/integration_test_generator_test.rb9
-rw-r--r--railties/test/generators/mailer_generator_test.rb9
-rw-r--r--railties/test/generators/metal_generator_test.rb9
-rw-r--r--railties/test/generators/migration_generator_test.rb41
-rw-r--r--railties/test/generators/model_generator_test.rb15
-rw-r--r--railties/test/generators/named_base_test.rb8
-rw-r--r--railties/test/generators/observer_generator_test.rb9
-rw-r--r--railties/test/generators/performance_test_generator_test.rb9
-rw-r--r--railties/test/generators/plugin_generator_test.rb9
-rw-r--r--railties/test/generators/resource_generator_test.rb13
-rw-r--r--railties/test/generators/scaffold_controller_generator_test.rb27
-rw-r--r--railties/test/generators/scaffold_generator_test.rb28
-rw-r--r--railties/test/generators/session_migration_generator_test.rb9
-rw-r--r--railties/test/generators/stylesheets_generator_test.rb11
-rw-r--r--railties/test/generators_test.rb17
117 files changed, 2811 insertions, 2440 deletions
diff --git a/actionmailer/CHANGELOG b/actionmailer/CHANGELOG
index 6f4ba96844..785bf98c55 100644
--- a/actionmailer/CHANGELOG
+++ b/actionmailer/CHANGELOG
@@ -1,13 +1,14 @@
*Rails 3.0 (pending)*
-* ActionMailer::Base :default_implicit_parts_order now is in the sequence of the order you want, no reversing of ordering takes place. The default order now is text/plain, then text/enriched, then text/html and then any other part that is not one of these three.
+* The Mail::Message class has helped methods for all the field types that return 'common' defaults for the common use case, so to get the subject, mail.subject will give you a string, mail.date will give you a DateTime object, mail.from will give you an array of address specs (mikel@test.lindsaar.net) etc. If you want to access the field object itself, call mail[:field_name] which will return the field object you want, which you can then chain, like mail[:from].formatted
-* Mail does not have "quoted_body", "quoted_subject" etc. All of these are accessed via body.encoded, subject.encoded etc
+* Mail#content_type now returns the content_type field as a string. If you want the mime type of a mail, then you call Mail#mime_type (eg, text/plain), if you want the parameters of the content type field, you call Mail#content_type_parameters which gives you a hash, eg {'format' => 'flowed', 'charset' => 'utf-8'}
-* Every part of a Mail object returns an object, never a string. So Mail.body returns a Mail::Body class object, need to call #encoded or #decoded to get the string you want.
+* ActionMailer::Base :default_implicit_parts_order now is in the sequence of the order you want, no reversing of ordering takes place. The default order now is text/plain, then text/enriched, then text/html and then any other part that is not one of these three.
-* By default, a field will return the #decoded value when you send it :to_s and any object that is a container (like header, body etc) will return #encoded value when you send it :to_s
+* Mail does not have "quoted_body", "quoted_subject" etc. All of these are accessed via body.encoded, subject.encoded etc
+* Every object in a Mail object returns an object, never a string. So Mail.body returns a Mail::Body class object, need to call #encoded or #decoded to get the string you want.
* Mail::Message#set_content_type does not exist, it is simply Mail::Message#content_type
* Every mail message gets a unique message_id unless you specify one, had to change all the tests that check for equality with expected.encoded == actual.encoded to first replace their message_ids with control values
diff --git a/actionmailer/actionmailer.gemspec b/actionmailer/actionmailer.gemspec
index 201b56a739..8adea46d35 100644
--- a/actionmailer/actionmailer.gemspec
+++ b/actionmailer/actionmailer.gemspec
@@ -11,7 +11,7 @@ Gem::Specification.new do |s|
s.homepage = "http://www.rubyonrails.org"
s.add_dependency('actionpack', '= 3.0.pre')
- s.add_dependency('mail', '~> 1.4.3')
+ s.add_dependency('mail', '~> 1.5.0')
s.files = Dir['CHANGELOG', 'README', 'MIT-LICENSE', 'lib/**/*']
s.has_rdoc = true
diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb
index 0248e29cb7..c2ffa5a0e9 100644
--- a/actionmailer/lib/action_mailer/base.rb
+++ b/actionmailer/lib/action_mailer/base.rb
@@ -500,7 +500,7 @@ module ActionMailer #:nodoc:
logger.debug "\n#{mail.encoded}"
end
- ActiveSupport::Notifications.instrument(:deliver_mail, :mail => mail) do
+ ActiveSupport::Notifications.instrument("action_mailer.deliver", :mail => mail) do
begin
self.delivery_method.perform_delivery(mail) if perform_deliveries
rescue Exception => e # Net::SMTP errors or sendmail pipe errors
diff --git a/actionmailer/lib/action_mailer/vendor/text-format-0.6.3/text/format.rb b/actionmailer/lib/action_mailer/vendor/text-format-0.6.3/text/format.rb
index 2d20c7a6a1..81cc7906d8 100755
--- a/actionmailer/lib/action_mailer/vendor/text-format-0.6.3/text/format.rb
+++ b/actionmailer/lib/action_mailer/vendor/text-format-0.6.3/text/format.rb
@@ -1,1466 +1,1467 @@
-#--
-# Text::Format for Ruby
-# Version 0.63
-#
-# Copyright (c) 2002 - 2003 Austin Ziegler
-#
-# $Id: format.rb,v 1.1.1.1 2004/10/14 11:59:57 webster132 Exp $
-#
-# ==========================================================================
-# Revision History ::
-# YYYY.MM.DD Change ID Developer
-# Description
-# --------------------------------------------------------------------------
-# 2002.10.18 Austin Ziegler
-# Fixed a minor problem with tabs not being counted. Changed
-# abbreviations from Hash to Array to better suit Ruby's
-# capabilities. Fixed problems with the way that Array arguments
-# are handled in calls to the major object types, excepting in
-# Text::Format#expand and Text::Format#unexpand (these will
-# probably need to be fixed).
-# 2002.10.30 Austin Ziegler
-# Fixed the ordering of the <=> for binary tests. Fixed
-# Text::Format#expand and Text::Format#unexpand to handle array
-# arguments better.
-# 2003.01.24 Austin Ziegler
-# Fixed a problem with Text::Format::RIGHT_FILL handling where a
-# single word is larger than #columns. Removed Comparable
-# capabilities (<=> doesn't make sense; == does). Added Symbol
-# equivalents for the Hash initialization. Hash initialization has
-# been modified so that values are set as follows (Symbols are
-# highest priority; strings are middle; defaults are lowest):
-# @columns = arg[:columns] || arg['columns'] || @columns
-# Added #hard_margins, #split_rules, #hyphenator, and #split_words.
-# 2003.02.07 Austin Ziegler
-# Fixed the installer for proper case-sensitive handling.
-# 2003.03.28 Austin Ziegler
-# Added the ability for a hyphenator to receive the formatter
-# object. Fixed a bug for strings matching /\A\s*\Z/ failing
-# entirely. Fixed a test case failing under 1.6.8.
-# 2003.04.04 Austin Ziegler
-# Handle the case of hyphenators returning nil for first/rest.
-# 2003.09.17 Austin Ziegler
-# Fixed a problem where #paragraphs(" ") was raising
-# NoMethodError.
-#
-# ==========================================================================
-#++
-
-module Text #:nodoc:
- # Text::Format for Ruby is copyright 2002 - 2005 by Austin Ziegler. It
- # is available under Ruby's licence, the Perl Artistic licence, or the
- # GNU GPL version 2 (or at your option, any later version). As a
- # special exception, for use with official Rails (provided by the
- # rubyonrails.org development team) and any project created with
- # official Rails, the following alternative MIT-style licence may be
- # used:
- #
- # == Text::Format Licence for Rails and Rails Applications
- # Permission is hereby granted, free of charge, to any person
- # obtaining a copy of this software and associated documentation files
- # (the "Software"), to deal in the Software without restriction,
- # including without limitation the rights to use, copy, modify, merge,
- # publish, distribute, sublicense, and/or sell copies of the Software,
- # and to permit persons to whom the Software is furnished to do so,
- # subject to the following conditions:
- #
- # * The names of its contributors may not be used to endorse or
- # promote products derived from this software without specific prior
- # written permission.
- #
- # The above copyright notice and this permission notice shall be
- # included in all copies or substantial portions of the Software.
- #
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- # SOFTWARE.
- class Format
- VERSION = '0.63'
-
- # Local abbreviations. More can be added with Text::Format.abbreviations
- ABBREV = [ 'Mr', 'Mrs', 'Ms', 'Jr', 'Sr' ]
-
- # Formatting values
- LEFT_ALIGN = 0
- RIGHT_ALIGN = 1
- RIGHT_FILL = 2
- JUSTIFY = 3
-
- # Word split modes (only applies when #hard_margins is true).
- SPLIT_FIXED = 1
- SPLIT_CONTINUATION = 2
- SPLIT_HYPHENATION = 4
- SPLIT_CONTINUATION_FIXED = SPLIT_CONTINUATION | SPLIT_FIXED
- SPLIT_HYPHENATION_FIXED = SPLIT_HYPHENATION | SPLIT_FIXED
- SPLIT_HYPHENATION_CONTINUATION = SPLIT_HYPHENATION | SPLIT_CONTINUATION
- SPLIT_ALL = SPLIT_HYPHENATION | SPLIT_CONTINUATION | SPLIT_FIXED
-
- # Words forcibly split by Text::Format will be stored as split words.
- # This class represents a word forcibly split.
- class SplitWord
- # The word that was split.
- attr_reader :word
- # The first part of the word that was split.
- attr_reader :first
- # The remainder of the word that was split.
- attr_reader :rest
-
- def initialize(word, first, rest) #:nodoc:
- @word = word
- @first = first
- @rest = rest
- end
- end
-
- private
- LEQ_RE = /[.?!]['"]?$/
-
- def brk_re(i) #:nodoc:
- %r/((?:\S+\s+){#{i}})(.+)/
- end
-
- def posint(p) #:nodoc:
- p.to_i.abs
- end
-
- public
- # Compares two Text::Format objects. All settings of the objects are
- # compared *except* #hyphenator. Generated results (e.g., #split_words)
- # are not compared, either.
- def ==(o)
- (@text == o.text) &&
- (@columns == o.columns) &&
- (@left_margin == o.left_margin) &&
- (@right_margin == o.right_margin) &&
- (@hard_margins == o.hard_margins) &&
- (@split_rules == o.split_rules) &&
- (@first_indent == o.first_indent) &&
- (@body_indent == o.body_indent) &&
- (@tag_text == o.tag_text) &&
- (@tabstop == o.tabstop) &&
- (@format_style == o.format_style) &&
- (@extra_space == o.extra_space) &&
- (@tag_paragraph == o.tag_paragraph) &&
- (@nobreak == o.nobreak) &&
- (@abbreviations == o.abbreviations) &&
- (@nobreak_regex == o.nobreak_regex)
- end
-
- # The text to be manipulated. Note that value is optional, but if the
- # formatting functions are called without values, this text is what will
- # be formatted.
- #
- # *Default*:: <tt>[]</tt>
- # <b>Used in</b>:: All methods
- attr_accessor :text
-
- # The total width of the format area. The margins, indentation, and text
- # are formatted into this space.
- #
- # COLUMNS
- # <-------------------------------------------------------------->
- # <-----------><------><---------------------------><------------>
- # left margin indent text is formatted into here right margin
- #
- # *Default*:: <tt>72</tt>
- # <b>Used in</b>:: <tt>#format</tt>, <tt>#paragraphs</tt>,
- # <tt>#center</tt>
- attr_reader :columns
-
- # The total width of the format area. The margins, indentation, and text
- # are formatted into this space. The value provided is silently
- # converted to a positive integer.
- #
- # COLUMNS
- # <-------------------------------------------------------------->
- # <-----------><------><---------------------------><------------>
- # left margin indent text is formatted into here right margin
- #
- # *Default*:: <tt>72</tt>
- # <b>Used in</b>:: <tt>#format</tt>, <tt>#paragraphs</tt>,
- # <tt>#center</tt>
- def columns=(c)
- @columns = posint(c)
- end
-
- # The number of spaces used for the left margin.
- #
- # columns
- # <-------------------------------------------------------------->
- # <-----------><------><---------------------------><------------>
- # LEFT MARGIN indent text is formatted into here right margin
- #
- # *Default*:: <tt>0</tt>
- # <b>Used in</b>:: <tt>#format</tt>, <tt>#paragraphs</tt>,
- # <tt>#center</tt>
- attr_reader :left_margin
-
- # The number of spaces used for the left margin. The value provided is
- # silently converted to a positive integer value.
- #
- # columns
- # <-------------------------------------------------------------->
- # <-----------><------><---------------------------><------------>
- # LEFT MARGIN indent text is formatted into here right margin
- #
- # *Default*:: <tt>0</tt>
- # <b>Used in</b>:: <tt>#format</tt>, <tt>#paragraphs</tt>,
- # <tt>#center</tt>
- def left_margin=(left)
- @left_margin = posint(left)
- end
-
- # The number of spaces used for the right margin.
- #
- # columns
- # <-------------------------------------------------------------->
- # <-----------><------><---------------------------><------------>
- # left margin indent text is formatted into here RIGHT MARGIN
- #
- # *Default*:: <tt>0</tt>
- # <b>Used in</b>:: <tt>#format</tt>, <tt>#paragraphs</tt>,
- # <tt>#center</tt>
- attr_reader :right_margin
-
- # The number of spaces used for the right margin. The value provided is
- # silently converted to a positive integer value.
- #
- # columns
- # <-------------------------------------------------------------->
- # <-----------><------><---------------------------><------------>
- # left margin indent text is formatted into here RIGHT MARGIN
- #
- # *Default*:: <tt>0</tt>
- # <b>Used in</b>:: <tt>#format</tt>, <tt>#paragraphs</tt>,
- # <tt>#center</tt>
- def right_margin=(r)
- @right_margin = posint(r)
- end
-
- # The number of spaces to indent the first line of a paragraph.
- #
- # columns
- # <-------------------------------------------------------------->
- # <-----------><------><---------------------------><------------>
- # left margin INDENT text is formatted into here right margin
- #
- # *Default*:: <tt>4</tt>
- # <b>Used in</b>:: <tt>#format</tt>, <tt>#paragraphs</tt>
- attr_reader :first_indent
-
- # The number of spaces to indent the first line of a paragraph. The
- # value provided is silently converted to a positive integer value.
- #
- # columns
- # <-------------------------------------------------------------->
- # <-----------><------><---------------------------><------------>
- # left margin INDENT text is formatted into here right margin
- #
- # *Default*:: <tt>4</tt>
- # <b>Used in</b>:: <tt>#format</tt>, <tt>#paragraphs</tt>
- def first_indent=(f)
- @first_indent = posint(f)
- end
-
- # The number of spaces to indent all lines after the first line of a
- # paragraph.
- #
- # columns
- # <-------------------------------------------------------------->
- # <-----------><------><---------------------------><------------>
- # left margin INDENT text is formatted into here right margin
- #
- # *Default*:: <tt>0</tt>
- # <b>Used in</b>:: <tt>#format</tt>, <tt>#paragraphs</tt>
- attr_reader :body_indent
-
- # The number of spaces to indent all lines after the first line of
- # a paragraph. The value provided is silently converted to a
- # positive integer value.
- #
- # columns
- # <-------------------------------------------------------------->
- # <-----------><------><---------------------------><------------>
- # left margin INDENT text is formatted into here right margin
- #
- # *Default*:: <tt>0</tt>
- # <b>Used in</b>:: <tt>#format</tt>, <tt>#paragraphs</tt>
- def body_indent=(b)
- @body_indent = posint(b)
- end
-
- # Normally, words larger than the format area will be placed on a line
- # by themselves. Setting this to +true+ will force words larger than the
- # format area to be split into one or more "words" each at most the size
- # of the format area. The first line and the original word will be
- # placed into <tt>#split_words</tt>. Note that this will cause the
- # output to look *similar* to a #format_style of JUSTIFY. (Lines will be
- # filled as much as possible.)
- #
- # *Default*:: +false+
- # <b>Used in</b>:: <tt>#format</tt>, <tt>#paragraphs</tt>
- attr_accessor :hard_margins
-
- # An array of words split during formatting if #hard_margins is set to
- # +true+.
- # #split_words << Text::Format::SplitWord.new(word, first, rest)
- attr_reader :split_words
-
- # The object responsible for hyphenating. It must respond to
- # #hyphenate_to(word, size) or #hyphenate_to(word, size, formatter) and
- # return an array of the word split into two parts; if there is a
- # hyphenation mark to be applied, responsibility belongs to the
- # hyphenator object. The size is the MAXIMUM size permitted, including
- # any hyphenation marks. If the #hyphenate_to method has an arity of 3,
- # the formatter will be provided to the method. This allows the
- # hyphenator to make decisions about the hyphenation based on the
- # formatting rules.
- #
- # *Default*:: +nil+
- # <b>Used in</b>:: <tt>#format</tt>, <tt>#paragraphs</tt>
- attr_reader :hyphenator
-
- # The object responsible for hyphenating. It must respond to
- # #hyphenate_to(word, size) and return an array of the word hyphenated
- # into two parts. The size is the MAXIMUM size permitted, including any
- # hyphenation marks.
- #
- # *Default*:: +nil+
- # <b>Used in</b>:: <tt>#format</tt>, <tt>#paragraphs</tt>
- def hyphenator=(h)
- raise ArgumentError, "#{h.inspect} is not a valid hyphenator." unless h.respond_to?(:hyphenate_to)
- arity = h.method(:hyphenate_to).arity
- raise ArgumentError, "#{h.inspect} must have exactly two or three arguments." unless [2, 3].include?(arity)
- @hyphenator = h
- @hyphenator_arity = arity
- end
-
- # Specifies the split mode; used only when #hard_margins is set to
- # +true+. Allowable values are:
- # [+SPLIT_FIXED+] The word will be split at the number of
- # characters needed, with no marking at all.
- # repre
- # senta
- # ion
- # [+SPLIT_CONTINUATION+] The word will be split at the number of
- # characters needed, with a C-style continuation
- # character. If a word is the only item on a
- # line and it cannot be split into an
- # appropriate size, SPLIT_FIXED will be used.
- # repr\
- # esen\
- # tati\
- # on
- # [+SPLIT_HYPHENATION+] The word will be split according to the
- # hyphenator specified in #hyphenator. If there
- # is no #hyphenator specified, works like
- # SPLIT_CONTINUATION. The example is using
- # TeX::Hyphen. If a word is the only item on a
- # line and it cannot be split into an
- # appropriate size, SPLIT_CONTINUATION mode will
- # be used.
- # rep-
- # re-
- # sen-
- # ta-
- # tion
- #
- # *Default*:: <tt>Text::Format::SPLIT_FIXED</tt>
- # <b>Used in</b>:: <tt>#format</tt>, <tt>#paragraphs</tt>
- attr_reader :split_rules
-
- # Specifies the split mode; used only when #hard_margins is set to
- # +true+. Allowable values are:
- # [+SPLIT_FIXED+] The word will be split at the number of
- # characters needed, with no marking at all.
- # repre
- # senta
- # ion
- # [+SPLIT_CONTINUATION+] The word will be split at the number of
- # characters needed, with a C-style continuation
- # character.
- # repr\
- # esen\
- # tati\
- # on
- # [+SPLIT_HYPHENATION+] The word will be split according to the
- # hyphenator specified in #hyphenator. If there
- # is no #hyphenator specified, works like
- # SPLIT_CONTINUATION. The example is using
- # TeX::Hyphen as the #hyphenator.
- # rep-
- # re-
- # sen-
- # ta-
- # tion
- #
- # These values can be bitwise ORed together (e.g., <tt>SPLIT_FIXED |
- # SPLIT_CONTINUATION</tt>) to provide fallback split methods. In the
- # example given, an attempt will be made to split the word using the
- # rules of SPLIT_CONTINUATION; if there is not enough room, the word
- # will be split with the rules of SPLIT_FIXED. These combinations are
- # also available as the following values:
- # * +SPLIT_CONTINUATION_FIXED+
- # * +SPLIT_HYPHENATION_FIXED+
- # * +SPLIT_HYPHENATION_CONTINUATION+
- # * +SPLIT_ALL+
- #
- # *Default*:: <tt>Text::Format::SPLIT_FIXED</tt>
- # <b>Used in</b>:: <tt>#format</tt>, <tt>#paragraphs</tt>
- def split_rules=(s)
- raise ArgumentError, "Invalid value provided for split_rules." if ((s < SPLIT_FIXED) || (s > SPLIT_ALL))
- @split_rules = s
- end
-
- # Indicates whether sentence terminators should be followed by a single
- # space (+false+), or two spaces (+true+).
- #
- # *Default*:: +false+
- # <b>Used in</b>:: <tt>#format</tt>, <tt>#paragraphs</tt>
- attr_accessor :extra_space
-
- # Defines the current abbreviations as an array. This is only used if
- # extra_space is turned on.
- #
- # If one is abbreviating "President" as "Pres." (abbreviations =
- # ["Pres"]), then the results of formatting will be as illustrated in
- # the table below:
- #
- # extra_space | include? | !include?
- # true | Pres. Lincoln | Pres. Lincoln
- # false | Pres. Lincoln | Pres. Lincoln
- #
- # *Default*:: <tt>{}</tt>
- # <b>Used in</b>:: <tt>#format</tt>, <tt>#paragraphs</tt>
- attr_accessor :abbreviations
-
- # Indicates whether the formatting of paragraphs should be done with
- # tagged paragraphs. Useful only with <tt>#tag_text</tt>.
- #
- # *Default*:: +false+
- # <b>Used in</b>:: <tt>#format</tt>, <tt>#paragraphs</tt>
- attr_accessor :tag_paragraph
-
- # The array of text to be placed before each paragraph when
- # <tt>#tag_paragraph</tt> is +true+. When <tt>#format()</tt> is called,
- # only the first element of the array is used. When <tt>#paragraphs</tt>
- # is called, then each entry in the array will be used once, with
- # corresponding paragraphs. If the tag elements are exhausted before the
- # text is exhausted, then the remaining paragraphs will not be tagged.
- # Regardless of indentation settings, a blank line will be inserted
- # between all paragraphs when <tt>#tag_paragraph</tt> is +true+.
- #
- # *Default*:: <tt>[]</tt>
- # <b>Used in</b>:: <tt>#format</tt>, <tt>#paragraphs</tt>
- attr_accessor :tag_text
-
- # Indicates whether or not the non-breaking space feature should be
- # used.
- #
- # *Default*:: +false+
- # <b>Used in</b>:: <tt>#format</tt>, <tt>#paragraphs</tt>
- attr_accessor :nobreak
-
- # A hash which holds the regular expressions on which spaces should not
- # be broken. The hash is set up such that the key is the first word and
- # the value is the second word.
- #
- # For example, if +nobreak_regex+ contains the following hash:
- #
- # { '^Mrs?\.$' => '\S+$', '^\S+$' => '^(?:S|J)r\.$'}
- #
- # Then "Mr. Jones", "Mrs. Jones", and "Jones Jr." would not be broken.
- # If this simple matching algorithm indicates that there should not be a
- # break at the current end of line, then a backtrack is done until there
- # are two words on which line breaking is permitted. If two such words
- # are not found, then the end of the line will be broken *regardless*.
- # If there is a single word on the current line, then no backtrack is
- # done and the word is stuck on the end.
- #
- # *Default*:: <tt>{}</tt>
- # <b>Used in</b>:: <tt>#format</tt>, <tt>#paragraphs</tt>
- attr_accessor :nobreak_regex
-
- # Indicates the number of spaces that a single tab represents.
- #
- # *Default*:: <tt>8</tt>
- # <b>Used in</b>:: <tt>#expand</tt>, <tt>#unexpand</tt>,
- # <tt>#paragraphs</tt>
- attr_reader :tabstop
-
- # Indicates the number of spaces that a single tab represents.
- #
- # *Default*:: <tt>8</tt>
- # <b>Used in</b>:: <tt>#expand</tt>, <tt>#unexpand</tt>,
- # <tt>#paragraphs</tt>
- def tabstop=(t)
- @tabstop = posint(t)
- end
-
- # Specifies the format style. Allowable values are:
- # [+LEFT_ALIGN+] Left justified, ragged right.
- # |A paragraph that is|
- # |left aligned.|
- # [+RIGHT_ALIGN+] Right justified, ragged left.
- # |A paragraph that is|
- # | right aligned.|
- # [+RIGHT_FILL+] Left justified, right ragged, filled to width by
- # spaces. (Essentially the same as +LEFT_ALIGN+ except
- # that lines are padded on the right.)
- # |A paragraph that is|
- # |left aligned. |
- # [+JUSTIFY+] Fully justified, words filled to width by spaces,
- # except the last line.
- # |A paragraph that|
- # |is justified.|
- #
- # *Default*:: <tt>Text::Format::LEFT_ALIGN</tt>
- # <b>Used in</b>:: <tt>#format</tt>, <tt>#paragraphs</tt>
- attr_reader :format_style
-
- # Specifies the format style. Allowable values are:
- # [+LEFT_ALIGN+] Left justified, ragged right.
- # |A paragraph that is|
- # |left aligned.|
- # [+RIGHT_ALIGN+] Right justified, ragged left.
- # |A paragraph that is|
- # | right aligned.|
- # [+RIGHT_FILL+] Left justified, right ragged, filled to width by
- # spaces. (Essentially the same as +LEFT_ALIGN+ except
- # that lines are padded on the right.)
- # |A paragraph that is|
- # |left aligned. |
- # [+JUSTIFY+] Fully justified, words filled to width by spaces.
- # |A paragraph that|
- # |is justified.|
- #
- # *Default*:: <tt>Text::Format::LEFT_ALIGN</tt>
- # <b>Used in</b>:: <tt>#format</tt>, <tt>#paragraphs</tt>
- def format_style=(fs)
- raise ArgumentError, "Invalid value provided for format_style." if ((fs < LEFT_ALIGN) || (fs > JUSTIFY))
- @format_style = fs
- end
-
- # Indicates that the format style is left alignment.
- #
- # *Default*:: +true+
- # <b>Used in</b>:: <tt>#format</tt>, <tt>#paragraphs</tt>
- def left_align?
- return @format_style == LEFT_ALIGN
- end
-
- # Indicates that the format style is right alignment.
- #
- # *Default*:: +false+
- # <b>Used in</b>:: <tt>#format</tt>, <tt>#paragraphs</tt>
- def right_align?
- return @format_style == RIGHT_ALIGN
- end
-
- # Indicates that the format style is right fill.
- #
- # *Default*:: +false+
- # <b>Used in</b>:: <tt>#format</tt>, <tt>#paragraphs</tt>
- def right_fill?
- return @format_style == RIGHT_FILL
- end
-
- # Indicates that the format style is full justification.
- #
- # *Default*:: +false+
- # <b>Used in</b>:: <tt>#format</tt>, <tt>#paragraphs</tt>
- def justify?
- return @format_style == JUSTIFY
- end
-
- # The default implementation of #hyphenate_to implements
- # SPLIT_CONTINUATION.
- def hyphenate_to(word, size)
- [word[0 .. (size - 2)] + "\\", word[(size - 1) .. -1]]
- end
-
- private
- def __do_split_word(word, size) #:nodoc:
- [word[0 .. (size - 1)], word[size .. -1]]
- end
-
- def __format(to_wrap) #:nodoc:
- words = to_wrap.split(/\s+/).compact
- words.shift if words[0].nil? or words[0].empty?
- to_wrap = []
-
- abbrev = false
- width = @columns - @first_indent - @left_margin - @right_margin
- indent_str = ' ' * @first_indent
- first_line = true
- line = words.shift
- abbrev = __is_abbrev(line) unless line.nil? || line.empty?
-
- while w = words.shift
- if (w.size + line.size < (width - 1)) ||
- ((line !~ LEQ_RE || abbrev) && (w.size + line.size < width))
- line << " " if (line =~ LEQ_RE) && (not abbrev)
- line << " #{w}"
- else
- line, w = __do_break(line, w) if @nobreak
- line, w = __do_hyphenate(line, w, width) if @hard_margins
- if w.index(/\s+/)
- w, *w2 = w.split(/\s+/)
- words.unshift(w2)
- words.flatten!
- end
- to_wrap << __make_line(line, indent_str, width, w.nil?) unless line.nil?
- if first_line
- first_line = false
- width = @columns - @body_indent - @left_margin - @right_margin
- indent_str = ' ' * @body_indent
- end
- line = w
- end
-
- abbrev = __is_abbrev(w) unless w.nil?
- end
-
- loop do
- break if line.nil? or line.empty?
- line, w = __do_hyphenate(line, w, width) if @hard_margins
- to_wrap << __make_line(line, indent_str, width, w.nil?)
- line = w
- end
-
- if (@tag_paragraph && (to_wrap.size > 0)) then
- clr = %r{`(\w+)'}.match([caller(1)].flatten[0])[1]
- clr = "" if clr.nil?
-
- if ((not @tag_text[0].nil?) && (@tag_cur.size < 1) &&
- (clr != "__paragraphs")) then
- @tag_cur = @tag_text[0]
- end
-
- fchar = /(\S)/.match(to_wrap[0])[1]
- white = to_wrap[0].index(fchar)
- if ((white - @left_margin - 1) > @tag_cur.size) then
- white = @tag_cur.size + @left_margin
- to_wrap[0].gsub!(/^ {#{white}}/, "#{' ' * @left_margin}#{@tag_cur}")
- else
- to_wrap.unshift("#{' ' * @left_margin}#{@tag_cur}\n")
- end
- end
- to_wrap.join('')
- end
-
- # format lines in text into paragraphs with each element of @wrap a
- # paragraph; uses Text::Format.format for the formatting
- def __paragraphs(to_wrap) #:nodoc:
- if ((@first_indent == @body_indent) || @tag_paragraph) then
- p_end = "\n"
- else
- p_end = ''
- end
-
- cnt = 0
- ret = []
- to_wrap.each do |tw|
- @tag_cur = @tag_text[cnt] if @tag_paragraph
- @tag_cur = '' if @tag_cur.nil?
- line = __format(tw)
- ret << "#{line}#{p_end}" if (not line.nil?) && (line.size > 0)
- cnt += 1
- end
-
- ret[-1].chomp! unless ret.empty?
- ret.join('')
- end
-
- # center text using spaces on left side to pad it out empty lines
- # are preserved
- def __center(to_center) #:nodoc:
- tabs = 0
- width = @columns - @left_margin - @right_margin
- centered = []
- to_center.each do |tc|
- s = tc.strip
- tabs = s.count("\t")
- tabs = 0 if tabs.nil?
- ct = ((width - s.size - (tabs * @tabstop) + tabs) / 2)
- ct = (width - @left_margin - @right_margin) - ct
- centered << "#{s.rjust(ct)}\n"
- end
- centered.join('')
- end
-
- # expand tabs to spaces should be similar to Text::Tabs::expand
- def __expand(to_expand) #:nodoc:
- expanded = []
- to_expand.split("\n").each { |te| expanded << te.gsub(/\t/, ' ' * @tabstop) }
- expanded.join('')
- end
-
- def __unexpand(to_unexpand) #:nodoc:
- unexpanded = []
- to_unexpand.split("\n").each { |tu| unexpanded << tu.gsub(/ {#{@tabstop}}/, "\t") }
- unexpanded.join('')
- end
-
- def __is_abbrev(word) #:nodoc:
- # remove period if there is one.
- w = word.gsub(/\.$/, '') unless word.nil?
- return true if (!@extra_space || ABBREV.include?(w) || @abbreviations.include?(w))
- false
- end
-
- def __make_line(line, indent, width, last = false) #:nodoc:
- lmargin = " " * @left_margin
- fill = " " * (width - line.size) if right_fill? && (line.size <= width)
-
- if (justify? && ((not line.nil?) && (not line.empty?)) && line =~ /\S+\s+\S+/ && !last)
- spaces = width - line.size
- words = line.split(/(\s+)/)
- ws = spaces / (words.size / 2)
- spaces = spaces % (words.size / 2) if ws > 0
- words.reverse.each do |rw|
- next if (rw =~ /^\S/)
- rw.sub!(/^/, " " * ws)
- next unless (spaces > 0)
- rw.sub!(/^/, " ")
- spaces -= 1
- end
- line = words.join('')
- end
- line = "#{lmargin}#{indent}#{line}#{fill}\n" unless line.nil?
- if right_align? && (not line.nil?)
- line.sub(/^/, " " * (@columns - @right_margin - (line.size - 1)))
- else
- line
- end
- end
-
- def __do_hyphenate(line, next_line, width) #:nodoc:
- rline = line.dup rescue line
- rnext = next_line.dup rescue next_line
- loop do
- if rline.size == width
- break
- elsif rline.size > width
- words = rline.strip.split(/\s+/)
- word = words[-1].dup
- size = width - rline.size + word.size
- if (size <= 0)
- words[-1] = nil
- rline = words.join(' ').strip
- rnext = "#{word} #{rnext}".strip
- next
- end
-
- first = rest = nil
-
- if ((@split_rules & SPLIT_HYPHENATION) != 0)
- if @hyphenator_arity == 2
- first, rest = @hyphenator.hyphenate_to(word, size)
- else
- first, rest = @hyphenator.hyphenate_to(word, size, self)
- end
- end
-
- if ((@split_rules & SPLIT_CONTINUATION) != 0) and first.nil?
- first, rest = self.hyphenate_to(word, size)
- end
-
- if ((@split_rules & SPLIT_FIXED) != 0) and first.nil?
- first.nil? or @split_rules == SPLIT_FIXED
- first, rest = __do_split_word(word, size)
- end
-
- if first.nil?
- words[-1] = nil
- rest = word
- else
- words[-1] = first
- @split_words << SplitWord.new(word, first, rest)
- end
- rline = words.join(' ').strip
- rnext = "#{rest} #{rnext}".strip
- break
- else
- break if rnext.nil? or rnext.empty? or rline.nil? or rline.empty?
- words = rnext.split(/\s+/)
- word = words.shift
- size = width - rline.size - 1
-
- if (size <= 0)
- rnext = "#{word} #{words.join(' ')}".strip
- break
- end
-
- first = rest = nil
-
- if ((@split_rules & SPLIT_HYPHENATION) != 0)
- if @hyphenator_arity == 2
- first, rest = @hyphenator.hyphenate_to(word, size)
- else
- first, rest = @hyphenator.hyphenate_to(word, size, self)
- end
- end
-
- first, rest = self.hyphenate_to(word, size) if ((@split_rules & SPLIT_CONTINUATION) != 0) and first.nil?
-
- first, rest = __do_split_word(word, size) if ((@split_rules & SPLIT_FIXED) != 0) and first.nil?
-
- if (rline.size + (first ? first.size : 0)) < width
- @split_words << SplitWord.new(word, first, rest)
- rline = "#{rline} #{first}".strip
- rnext = "#{rest} #{words.join(' ')}".strip
- end
- break
- end
- end
- [rline, rnext]
- end
-
- def __do_break(line, next_line) #:nodoc:
- no_brk = false
- words = []
- words = line.split(/\s+/) unless line.nil?
- last_word = words[-1]
-
- @nobreak_regex.each { |k, v| no_brk = ((last_word =~ /#{k}/) and (next_line =~ /#{v}/)) }
-
- if no_brk && words.size > 1
- i = words.size
- while i > 0
- no_brk = false
- @nobreak_regex.each { |k, v| no_brk = ((words[i + 1] =~ /#{k}/) && (words[i] =~ /#{v}/)) }
- i -= 1
- break if not no_brk
- end
- if i > 0
- l = brk_re(i).match(line)
- line.sub!(brk_re(i), l[1])
- next_line = "#{l[2]} #{next_line}"
- line.sub!(/\s+$/, '')
- end
- end
- [line, next_line]
- end
-
- def __create(arg = nil, &block) #:nodoc:
- # Format::Text.new(text-to-wrap)
- @text = arg unless arg.nil?
- # Defaults
- @columns = 72
- @tabstop = 8
- @first_indent = 4
- @body_indent = 0
- @format_style = LEFT_ALIGN
- @left_margin = 0
- @right_margin = 0
- @extra_space = false
- @text = Array.new if @text.nil?
- @tag_paragraph = false
- @tag_text = Array.new
- @tag_cur = ""
- @abbreviations = Array.new
- @nobreak = false
- @nobreak_regex = Hash.new
- @split_words = Array.new
- @hard_margins = false
- @split_rules = SPLIT_FIXED
- @hyphenator = self
- @hyphenator_arity = self.method(:hyphenate_to).arity
-
- instance_eval(&block) unless block.nil?
- end
-
- public
- # Formats text into a nice paragraph format. The text is separated
- # into words and then reassembled a word at a time using the settings
- # of this Format object. If a word is larger than the number of
- # columns available for formatting, then that word will appear on the
- # line by itself.
- #
- # If +to_wrap+ is +nil+, then the value of <tt>#text</tt> will be
- # worked on.
- def format(to_wrap = nil)
- to_wrap = @text if to_wrap.nil?
- if to_wrap.class == Array
- __format(to_wrap[0])
- else
- __format(to_wrap)
- end
- end
-
- # Considers each element of text (provided or internal) as a paragraph.
- # If <tt>#first_indent</tt> is the same as <tt>#body_indent</tt>, then
- # paragraphs will be separated by a single empty line in the result;
- # otherwise, the paragraphs will follow immediately after each other.
- # Uses <tt>#format</tt> to do the heavy lifting.
- def paragraphs(to_wrap = nil)
- to_wrap = @text if to_wrap.nil?
- __paragraphs([to_wrap].flatten)
- end
-
- # Centers the text, preserving empty lines and tabs.
- def center(to_center = nil)
- to_center = @text if to_center.nil?
- __center([to_center].flatten)
- end
-
- # Replaces all tab characters in the text with <tt>#tabstop</tt> spaces.
- def expand(to_expand = nil)
- to_expand = @text if to_expand.nil?
- if to_expand.class == Array
- to_expand.collect { |te| __expand(te) }
- else
- __expand(to_expand)
- end
- end
-
- # Replaces all occurrences of <tt>#tabstop</tt> consecutive spaces
- # with a tab character.
- def unexpand(to_unexpand = nil)
- to_unexpand = @text if to_unexpand.nil?
- if to_unexpand.class == Array
- to_unexpand.collect { |te| v << __unexpand(te) }
- else
- __unexpand(to_unexpand)
- end
- end
-
- # This constructor takes advantage of a technique for Ruby object
- # construction introduced by Andy Hunt and Dave Thomas (see reference),
- # where optional values are set using commands in a block.
- #
- # Text::Format.new {
- # columns = 72
- # left_margin = 0
- # right_margin = 0
- # first_indent = 4
- # body_indent = 0
- # format_style = Text::Format::LEFT_ALIGN
- # extra_space = false
- # abbreviations = {}
- # tag_paragraph = false
- # tag_text = []
- # nobreak = false
- # nobreak_regex = {}
- # tabstop = 8
- # text = nil
- # }
- #
- # As shown above, +arg+ is optional. If +arg+ is specified and is a
- # +String+, then arg is used as the default value of <tt>#text</tt>.
- # Alternately, an existing Text::Format object can be used or a Hash can
- # be used. With all forms, a block can be specified.
- #
- # *Reference*:: "Object Construction and Blocks"
- # <http://www.pragmaticprogrammer.com/ruby/articles/insteval.html>
- #
- def initialize(arg = nil, &block)
- case arg
- when Text::Format
- __create(arg.text) do
- @columns = arg.columns
- @tabstop = arg.tabstop
- @first_indent = arg.first_indent
- @body_indent = arg.body_indent
- @format_style = arg.format_style
- @left_margin = arg.left_margin
- @right_margin = arg.right_margin
- @extra_space = arg.extra_space
- @tag_paragraph = arg.tag_paragraph
- @tag_text = arg.tag_text
- @abbreviations = arg.abbreviations
- @nobreak = arg.nobreak
- @nobreak_regex = arg.nobreak_regex
- @text = arg.text
- @hard_margins = arg.hard_margins
- @split_words = arg.split_words
- @split_rules = arg.split_rules
- @hyphenator = arg.hyphenator
- end
- instance_eval(&block) unless block.nil?
- when Hash
- __create do
- @columns = arg[:columns] || arg['columns'] || @columns
- @tabstop = arg[:tabstop] || arg['tabstop'] || @tabstop
- @first_indent = arg[:first_indent] || arg['first_indent'] || @first_indent
- @body_indent = arg[:body_indent] || arg['body_indent'] || @body_indent
- @format_style = arg[:format_style] || arg['format_style'] || @format_style
- @left_margin = arg[:left_margin] || arg['left_margin'] || @left_margin
- @right_margin = arg[:right_margin] || arg['right_margin'] || @right_margin
- @extra_space = arg[:extra_space] || arg['extra_space'] || @extra_space
- @text = arg[:text] || arg['text'] || @text
- @tag_paragraph = arg[:tag_paragraph] || arg['tag_paragraph'] || @tag_paragraph
- @tag_text = arg[:tag_text] || arg['tag_text'] || @tag_text
- @abbreviations = arg[:abbreviations] || arg['abbreviations'] || @abbreviations
- @nobreak = arg[:nobreak] || arg['nobreak'] || @nobreak
- @nobreak_regex = arg[:nobreak_regex] || arg['nobreak_regex'] || @nobreak_regex
- @hard_margins = arg[:hard_margins] || arg['hard_margins'] || @hard_margins
- @split_rules = arg[:split_rules] || arg['split_rules'] || @split_rules
- @hyphenator = arg[:hyphenator] || arg['hyphenator'] || @hyphenator
- end
- instance_eval(&block) unless block.nil?
- when String
- __create(arg, &block)
- when NilClass
- __create(&block)
- else
- raise TypeError
- end
- end
- end
-end
-
-if __FILE__ == $0
- require 'test/unit'
-
- class TestText__Format < Test::Unit::TestCase #:nodoc:
- attr_accessor :format_o
-
- GETTYSBURG = <<-'EOS'
- Four score and seven years ago our fathers brought forth on this
- continent a new nation, conceived in liberty and dedicated to the
- proposition that all men are created equal. Now we are engaged in
- a great civil war, testing whether that nation or any nation so
- conceived and so dedicated can long endure. We are met on a great
- battlefield of that war. We have come to dedicate a portion of
- that field as a final resting-place for those who here gave their
- lives that that nation might live. It is altogether fitting and
- proper that we should do this. But in a larger sense, we cannot
- dedicate, we cannot consecrate, we cannot hallow this ground.
- The brave men, living and dead who struggled here have consecrated
- it far above our poor power to add or detract. The world will
- little note nor long remember what we say here, but it can never
- forget what they did here. It is for us the living rather to be
- dedicated here to the unfinished work which they who fought here
- have thus far so nobly advanced. It is rather for us to be here
- dedicated to the great task remaining before us--that from these
- honored dead we take increased devotion to that cause for which
- they gave the last full measure of devotion--that we here highly
- resolve that these dead shall not have died in vain, that this
- nation under God shall have a new birth of freedom, and that
- government of the people, by the people, for the people shall
- not perish from the earth.
-
- -- Pres. Abraham Lincoln, 19 November 1863
- EOS
-
- FIVE_COL = "Four \nscore\nand s\neven \nyears\nago o\nur fa\nthers\nbroug\nht fo\nrth o\nn thi\ns con\ntinen\nt a n\new na\ntion,\nconce\nived \nin li\nberty\nand d\nedica\nted t\no the\npropo\nsitio\nn tha\nt all\nmen a\nre cr\neated\nequal\n. Now\nwe ar\ne eng\naged \nin a \ngreat\ncivil\nwar, \ntesti\nng wh\nether\nthat \nnatio\nn or \nany n\nation\nso co\nnceiv\ned an\nd so \ndedic\nated \ncan l\nong e\nndure\n. We \nare m\net on\na gre\nat ba\nttlef\nield \nof th\nat wa\nr. We\nhave \ncome \nto de\ndicat\ne a p\nortio\nn of \nthat \nfield\nas a \nfinal\nresti\nng-pl\nace f\nor th\nose w\nho he\nre ga\nve th\neir l\nives \nthat \nthat \nnatio\nn mig\nht li\nve. I\nt is \naltog\nether\nfitti\nng an\nd pro\nper t\nhat w\ne sho\nuld d\no thi\ns. Bu\nt in \na lar\nger s\nense,\nwe ca\nnnot \ndedic\nate, \nwe ca\nnnot \nconse\ncrate\n, we \ncanno\nt hal\nlow t\nhis g\nround\n. The\nbrave\nmen, \nlivin\ng and\ndead \nwho s\ntrugg\nled h\nere h\nave c\nonsec\nrated\nit fa\nr abo\nve ou\nr poo\nr pow\ner to\nadd o\nr det\nract.\nThe w\norld \nwill \nlittl\ne not\ne nor\nlong \nremem\nber w\nhat w\ne say\nhere,\nbut i\nt can\nnever\nforge\nt wha\nt the\ny did\nhere.\nIt is\nfor u\ns the\nlivin\ng rat\nher t\no be \ndedic\nated \nhere \nto th\ne unf\ninish\ned wo\nrk wh\nich t\nhey w\nho fo\nught \nhere \nhave \nthus \nfar s\no nob\nly ad\nvance\nd. It\nis ra\nther \nfor u\ns to \nbe he\nre de\ndicat\ned to\nthe g\nreat \ntask \nremai\nning \nbefor\ne us-\n-that\nfrom \nthese\nhonor\ned de\nad we\ntake \nincre\nased \ndevot\nion t\no tha\nt cau\nse fo\nr whi\nch th\ney ga\nve th\ne las\nt ful\nl mea\nsure \nof de\nvotio\nn--th\nat we\nhere \nhighl\ny res\nolve \nthat \nthese\ndead \nshall\nnot h\nave d\nied i\nn vai\nn, th\nat th\nis na\ntion \nunder\nGod s\nhall \nhave \na new\nbirth\nof fr\needom\n, and\nthat \ngover\nnment\nof th\ne peo\nple, \nby th\ne peo\nple, \nfor t\nhe pe\nople \nshall\nnot p\nerish\nfrom \nthe e\narth.\n-- Pr\nes. A\nbraha\nm Lin\ncoln,\n19 No\nvembe\nr 186\n3 \n"
-
- FIVE_CNT = "Four \nscore\nand \nseven\nyears\nago \nour \nfath\\\ners \nbrou\\\nght \nforth\non t\\\nhis \ncont\\\ninent\na new\nnati\\\non, \nconc\\\neived\nin l\\\niber\\\nty a\\\nnd d\\\nedic\\\nated \nto t\\\nhe p\\\nropo\\\nsiti\\\non t\\\nhat \nall \nmen \nare \ncrea\\\nted \nequa\\\nl. N\\\now we\nare \nenga\\\nged \nin a \ngreat\ncivil\nwar, \ntest\\\ning \nwhet\\\nher \nthat \nnati\\\non or\nany \nnati\\\non so\nconc\\\neived\nand \nso d\\\nedic\\\nated \ncan \nlong \nendu\\\nre. \nWe a\\\nre m\\\net on\na gr\\\neat \nbatt\\\nlefi\\\neld \nof t\\\nhat \nwar. \nWe h\\\nave \ncome \nto d\\\nedic\\\nate a\nport\\\nion \nof t\\\nhat \nfield\nas a \nfinal\nrest\\\ning-\\\nplace\nfor \nthose\nwho \nhere \ngave \ntheir\nlives\nthat \nthat \nnati\\\non m\\\night \nlive.\nIt is\nalto\\\ngeth\\\ner f\\\nitti\\\nng a\\\nnd p\\\nroper\nthat \nwe s\\\nhould\ndo t\\\nhis. \nBut \nin a \nlarg\\\ner s\\\nense,\nwe c\\\nannot\ndedi\\\ncate,\nwe c\\\nannot\ncons\\\necra\\\nte, \nwe c\\\nannot\nhall\\\now t\\\nhis \ngrou\\\nnd. \nThe \nbrave\nmen, \nlivi\\\nng a\\\nnd d\\\nead \nwho \nstru\\\nggled\nhere \nhave \ncons\\\necra\\\nted \nit f\\\nar a\\\nbove \nour \npoor \npower\nto a\\\ndd or\ndetr\\\nact. \nThe \nworld\nwill \nlitt\\\nle n\\\note \nnor \nlong \nreme\\\nmber \nwhat \nwe s\\\nay h\\\nere, \nbut \nit c\\\nan n\\\never \nforg\\\net w\\\nhat \nthey \ndid \nhere.\nIt is\nfor \nus t\\\nhe l\\\niving\nrath\\\ner to\nbe d\\\nedic\\\nated \nhere \nto t\\\nhe u\\\nnfin\\\nished\nwork \nwhich\nthey \nwho \nfoug\\\nht h\\\nere \nhave \nthus \nfar \nso n\\\nobly \nadva\\\nnced.\nIt is\nrath\\\ner f\\\nor us\nto be\nhere \ndedi\\\ncated\nto t\\\nhe g\\\nreat \ntask \nrema\\\nining\nbefo\\\nre u\\\ns--t\\\nhat \nfrom \nthese\nhono\\\nred \ndead \nwe t\\\nake \nincr\\\neased\ndevo\\\ntion \nto t\\\nhat \ncause\nfor \nwhich\nthey \ngave \nthe \nlast \nfull \nmeas\\\nure \nof d\\\nevot\\\nion-\\\n-that\nwe h\\\nere \nhigh\\\nly r\\\nesol\\\nve t\\\nhat \nthese\ndead \nshall\nnot \nhave \ndied \nin v\\\nain, \nthat \nthis \nnati\\\non u\\\nnder \nGod \nshall\nhave \na new\nbirth\nof f\\\nreed\\\nom, \nand \nthat \ngove\\\nrnme\\\nnt of\nthe \npeop\\\nle, \nby t\\\nhe p\\\neopl\\\ne, f\\\nor t\\\nhe p\\\neople\nshall\nnot \nperi\\\nsh f\\\nrom \nthe \neart\\\nh. --\nPres.\nAbra\\\nham \nLinc\\\noln, \n19 N\\\novem\\\nber \n1863 \n"
-
- # Tests both abbreviations and abbreviations=
- def test_abbreviations
- abbr = [" Pres. Abraham Lincoln\n", " Pres. Abraham Lincoln\n"]
- assert_nothing_raised { @format_o = Text::Format.new }
- assert_equal([], @format_o.abbreviations)
- assert_nothing_raised { @format_o.abbreviations = [ 'foo', 'bar' ] }
- assert_equal([ 'foo', 'bar' ], @format_o.abbreviations)
- assert_equal(abbr[0], @format_o.format(abbr[0]))
- assert_nothing_raised { @format_o.extra_space = true }
- assert_equal(abbr[1], @format_o.format(abbr[0]))
- assert_nothing_raised { @format_o.abbreviations = [ "Pres" ] }
- assert_equal([ "Pres" ], @format_o.abbreviations)
- assert_equal(abbr[0], @format_o.format(abbr[0]))
- assert_nothing_raised { @format_o.extra_space = false }
- assert_equal(abbr[0], @format_o.format(abbr[0]))
- end
-
- # Tests both body_indent and body_indent=
- def test_body_indent
- assert_nothing_raised { @format_o = Text::Format.new }
- assert_equal(0, @format_o.body_indent)
- assert_nothing_raised { @format_o.body_indent = 7 }
- assert_equal(7, @format_o.body_indent)
- assert_nothing_raised { @format_o.body_indent = -3 }
- assert_equal(3, @format_o.body_indent)
- assert_nothing_raised { @format_o.body_indent = "9" }
- assert_equal(9, @format_o.body_indent)
- assert_nothing_raised { @format_o.body_indent = "-2" }
- assert_equal(2, @format_o.body_indent)
- assert_match(/^ [^ ]/, @format_o.format(GETTYSBURG).split("\n")[1])
- end
-
- # Tests both columns and columns=
- def test_columns
- assert_nothing_raised { @format_o = Text::Format.new }
- assert_equal(72, @format_o.columns)
- assert_nothing_raised { @format_o.columns = 7 }
- assert_equal(7, @format_o.columns)
- assert_nothing_raised { @format_o.columns = -3 }
- assert_equal(3, @format_o.columns)
- assert_nothing_raised { @format_o.columns = "9" }
- assert_equal(9, @format_o.columns)
- assert_nothing_raised { @format_o.columns = "-2" }
- assert_equal(2, @format_o.columns)
- assert_nothing_raised { @format_o.columns = 40 }
- assert_equal(40, @format_o.columns)
- assert_match(/this continent$/,
- @format_o.format(GETTYSBURG).split("\n")[1])
- end
-
- # Tests both extra_space and extra_space=
- def test_extra_space
- assert_nothing_raised { @format_o = Text::Format.new }
- assert(!@format_o.extra_space)
- assert_nothing_raised { @format_o.extra_space = true }
- assert(@format_o.extra_space)
- # The behaviour of extra_space is tested in test_abbreviations. There
- # is no need to reproduce it here.
- end
-
- # Tests both first_indent and first_indent=
- def test_first_indent
- assert_nothing_raised { @format_o = Text::Format.new }
- assert_equal(4, @format_o.first_indent)
- assert_nothing_raised { @format_o.first_indent = 7 }
- assert_equal(7, @format_o.first_indent)
- assert_nothing_raised { @format_o.first_indent = -3 }
- assert_equal(3, @format_o.first_indent)
- assert_nothing_raised { @format_o.first_indent = "9" }
- assert_equal(9, @format_o.first_indent)
- assert_nothing_raised { @format_o.first_indent = "-2" }
- assert_equal(2, @format_o.first_indent)
- assert_match(/^ [^ ]/, @format_o.format(GETTYSBURG).split("\n")[0])
- end
-
- def test_format_style
- assert_nothing_raised { @format_o = Text::Format.new }
- assert_equal(Text::Format::LEFT_ALIGN, @format_o.format_style)
- assert_match(/^November 1863$/,
- @format_o.format(GETTYSBURG).split("\n")[-1])
- assert_nothing_raised {
- @format_o.format_style = Text::Format::RIGHT_ALIGN
- }
- assert_equal(Text::Format::RIGHT_ALIGN, @format_o.format_style)
- assert_match(/^ +November 1863$/,
- @format_o.format(GETTYSBURG).split("\n")[-1])
- assert_nothing_raised {
- @format_o.format_style = Text::Format::RIGHT_FILL
- }
- assert_equal(Text::Format::RIGHT_FILL, @format_o.format_style)
- assert_match(/^November 1863 +$/,
- @format_o.format(GETTYSBURG).split("\n")[-1])
- assert_nothing_raised { @format_o.format_style = Text::Format::JUSTIFY }
- assert_equal(Text::Format::JUSTIFY, @format_o.format_style)
- assert_match(/^of freedom, and that government of the people, by the people, for the$/,
- @format_o.format(GETTYSBURG).split("\n")[-3])
- assert_raise(ArgumentError) { @format_o.format_style = 33 }
- end
-
- def test_tag_paragraph
- assert_nothing_raised { @format_o = Text::Format.new }
- assert(!@format_o.tag_paragraph)
- assert_nothing_raised { @format_o.tag_paragraph = true }
- assert(@format_o.tag_paragraph)
- assert_not_equal(@format_o.paragraphs([GETTYSBURG, GETTYSBURG]),
- Text::Format.new.paragraphs([GETTYSBURG, GETTYSBURG]))
- end
-
- def test_tag_text
- assert_nothing_raised { @format_o = Text::Format.new }
- assert_equal([], @format_o.tag_text)
- assert_equal(@format_o.format(GETTYSBURG),
- Text::Format.new.format(GETTYSBURG))
- assert_nothing_raised {
- @format_o.tag_paragraph = true
- @format_o.tag_text = ["Gettysburg Address", "---"]
- }
- assert_not_equal(@format_o.format(GETTYSBURG),
- Text::Format.new.format(GETTYSBURG))
- assert_not_equal(@format_o.paragraphs([GETTYSBURG, GETTYSBURG]),
- Text::Format.new.paragraphs([GETTYSBURG, GETTYSBURG]))
- assert_not_equal(@format_o.paragraphs([GETTYSBURG, GETTYSBURG,
- GETTYSBURG]),
- Text::Format.new.paragraphs([GETTYSBURG, GETTYSBURG,
- GETTYSBURG]))
- end
-
- def test_justify?
- assert_nothing_raised { @format_o = Text::Format.new }
- assert(!@format_o.justify?)
- assert_nothing_raised {
- @format_o.format_style = Text::Format::RIGHT_ALIGN
- }
- assert(!@format_o.justify?)
- assert_nothing_raised {
- @format_o.format_style = Text::Format::RIGHT_FILL
- }
- assert(!@format_o.justify?)
- assert_nothing_raised {
- @format_o.format_style = Text::Format::JUSTIFY
- }
- assert(@format_o.justify?)
- # The format testing is done in test_format_style
- end
-
- def test_left_align?
- assert_nothing_raised { @format_o = Text::Format.new }
- assert(@format_o.left_align?)
- assert_nothing_raised {
- @format_o.format_style = Text::Format::RIGHT_ALIGN
- }
- assert(!@format_o.left_align?)
- assert_nothing_raised {
- @format_o.format_style = Text::Format::RIGHT_FILL
- }
- assert(!@format_o.left_align?)
- assert_nothing_raised { @format_o.format_style = Text::Format::JUSTIFY }
- assert(!@format_o.left_align?)
- # The format testing is done in test_format_style
- end
-
- def test_left_margin
- assert_nothing_raised { @format_o = Text::Format.new }
- assert_equal(0, @format_o.left_margin)
- assert_nothing_raised { @format_o.left_margin = -3 }
- assert_equal(3, @format_o.left_margin)
- assert_nothing_raised { @format_o.left_margin = "9" }
- assert_equal(9, @format_o.left_margin)
- assert_nothing_raised { @format_o.left_margin = "-2" }
- assert_equal(2, @format_o.left_margin)
- assert_nothing_raised { @format_o.left_margin = 7 }
- assert_equal(7, @format_o.left_margin)
- assert_nothing_raised {
- ft = @format_o.format(GETTYSBURG).split("\n")
- assert_match(/^ {11}Four score/, ft[0])
- assert_match(/^ {7}November/, ft[-1])
- }
- end
-
- def test_hard_margins
- assert_nothing_raised { @format_o = Text::Format.new }
- assert(!@format_o.hard_margins)
- assert_nothing_raised {
- @format_o.hard_margins = true
- @format_o.columns = 5
- @format_o.first_indent = 0
- @format_o.format_style = Text::Format::RIGHT_FILL
- }
- assert(@format_o.hard_margins)
- assert_equal(FIVE_COL, @format_o.format(GETTYSBURG))
- assert_nothing_raised {
- @format_o.split_rules |= Text::Format::SPLIT_CONTINUATION
- assert_equal(Text::Format::SPLIT_CONTINUATION_FIXED,
- @format_o.split_rules)
- }
- assert_equal(FIVE_CNT, @format_o.format(GETTYSBURG))
- end
-
- # Tests both nobreak and nobreak_regex, since one is only useful
- # with the other.
- def test_nobreak
- assert_nothing_raised { @format_o = Text::Format.new }
- assert(!@format_o.nobreak)
- assert(@format_o.nobreak_regex.empty?)
- assert_nothing_raised {
- @format_o.nobreak = true
- @format_o.nobreak_regex = { '^this$' => '^continent$' }
- @format_o.columns = 77
- }
- assert(@format_o.nobreak)
- assert_equal({ '^this$' => '^continent$' }, @format_o.nobreak_regex)
- assert_match(/^this continent/,
- @format_o.format(GETTYSBURG).split("\n")[1])
- end
-
- def test_right_align?
- assert_nothing_raised { @format_o = Text::Format.new }
- assert(!@format_o.right_align?)
- assert_nothing_raised {
- @format_o.format_style = Text::Format::RIGHT_ALIGN
- }
- assert(@format_o.right_align?)
- assert_nothing_raised {
- @format_o.format_style = Text::Format::RIGHT_FILL
- }
- assert(!@format_o.right_align?)
- assert_nothing_raised { @format_o.format_style = Text::Format::JUSTIFY }
- assert(!@format_o.right_align?)
- # The format testing is done in test_format_style
- end
-
- def test_right_fill?
- assert_nothing_raised { @format_o = Text::Format.new }
- assert(!@format_o.right_fill?)
- assert_nothing_raised {
- @format_o.format_style = Text::Format::RIGHT_ALIGN
- }
- assert(!@format_o.right_fill?)
- assert_nothing_raised {
- @format_o.format_style = Text::Format::RIGHT_FILL
- }
- assert(@format_o.right_fill?)
- assert_nothing_raised {
- @format_o.format_style = Text::Format::JUSTIFY
- }
- assert(!@format_o.right_fill?)
- # The format testing is done in test_format_style
- end
-
- def test_right_margin
- assert_nothing_raised { @format_o = Text::Format.new }
- assert_equal(0, @format_o.right_margin)
- assert_nothing_raised { @format_o.right_margin = -3 }
- assert_equal(3, @format_o.right_margin)
- assert_nothing_raised { @format_o.right_margin = "9" }
- assert_equal(9, @format_o.right_margin)
- assert_nothing_raised { @format_o.right_margin = "-2" }
- assert_equal(2, @format_o.right_margin)
- assert_nothing_raised { @format_o.right_margin = 7 }
- assert_equal(7, @format_o.right_margin)
- assert_nothing_raised {
- ft = @format_o.format(GETTYSBURG).split("\n")
- assert_match(/^ {4}Four score.*forth on$/, ft[0])
- assert_match(/^November/, ft[-1])
- }
- end
-
- def test_tabstop
- assert_nothing_raised { @format_o = Text::Format.new }
- assert_equal(8, @format_o.tabstop)
- assert_nothing_raised { @format_o.tabstop = 7 }
- assert_equal(7, @format_o.tabstop)
- assert_nothing_raised { @format_o.tabstop = -3 }
- assert_equal(3, @format_o.tabstop)
- assert_nothing_raised { @format_o.tabstop = "9" }
- assert_equal(9, @format_o.tabstop)
- assert_nothing_raised { @format_o.tabstop = "-2" }
- assert_equal(2, @format_o.tabstop)
- end
-
- def test_text
- assert_nothing_raised { @format_o = Text::Format.new }
- assert_equal([], @format_o.text)
- assert_nothing_raised { @format_o.text = "Test Text" }
- assert_equal("Test Text", @format_o.text)
- assert_nothing_raised { @format_o.text = ["Line 1", "Line 2"] }
- assert_equal(["Line 1", "Line 2"], @format_o.text)
- end
-
- def test_s_new
- # new(NilClass) { block }
- assert_nothing_raised do
- @format_o = Text::Format.new {
- self.text = "Test 1, 2, 3"
- }
- end
- assert_equal("Test 1, 2, 3", @format_o.text)
-
- # new(Hash Symbols)
- assert_nothing_raised { @format_o = Text::Format.new(:columns => 72) }
- assert_equal(72, @format_o.columns)
-
- # new(Hash String)
- assert_nothing_raised { @format_o = Text::Format.new('columns' => 72) }
- assert_equal(72, @format_o.columns)
-
- # new(Hash) { block }
- assert_nothing_raised do
- @format_o = Text::Format.new('columns' => 80) {
- self.text = "Test 4, 5, 6"
- }
- end
- assert_equal("Test 4, 5, 6", @format_o.text)
- assert_equal(80, @format_o.columns)
-
- # new(Text::Format)
- assert_nothing_raised do
- fo = Text::Format.new(@format_o)
- assert(fo == @format_o)
- end
-
- # new(Text::Format) { block }
- assert_nothing_raised do
- fo = Text::Format.new(@format_o) { self.columns = 79 }
- assert(fo != @format_o)
- end
-
- # new(String)
- assert_nothing_raised { @format_o = Text::Format.new("Test A, B, C") }
- assert_equal("Test A, B, C", @format_o.text)
-
- # new(String) { block }
- assert_nothing_raised do
- @format_o = Text::Format.new("Test X, Y, Z") { self.columns = -5 }
- end
- assert_equal("Test X, Y, Z", @format_o.text)
- assert_equal(5, @format_o.columns)
- end
-
- def test_center
- assert_nothing_raised { @format_o = Text::Format.new }
- assert_nothing_raised do
- ct = @format_o.center(GETTYSBURG.split("\n")).split("\n")
- assert_match(/^ Four score and seven years ago our fathers brought forth on this/, ct[0])
- assert_match(/^ not perish from the earth./, ct[-3])
- end
- end
-
- def test_expand
- assert_nothing_raised { @format_o = Text::Format.new }
- assert_equal(" ", @format_o.expand("\t "))
- assert_nothing_raised { @format_o.tabstop = 4 }
- assert_equal(" ", @format_o.expand("\t "))
- end
-
- def test_unexpand
- assert_nothing_raised { @format_o = Text::Format.new }
- assert_equal("\t ", @format_o.unexpand(" "))
- assert_nothing_raised { @format_o.tabstop = 4 }
- assert_equal("\t ", @format_o.unexpand(" "))
- end
-
- def test_space_only
- assert_equal("", Text::Format.new.format(" "))
- assert_equal("", Text::Format.new.format("\n"))
- assert_equal("", Text::Format.new.format(" "))
- assert_equal("", Text::Format.new.format(" \n"))
- assert_equal("", Text::Format.new.paragraphs("\n"))
- assert_equal("", Text::Format.new.paragraphs(" "))
- assert_equal("", Text::Format.new.paragraphs(" "))
- assert_equal("", Text::Format.new.paragraphs(" \n"))
- assert_equal("", Text::Format.new.paragraphs(["\n"]))
- assert_equal("", Text::Format.new.paragraphs([" "]))
- assert_equal("", Text::Format.new.paragraphs([" "]))
- assert_equal("", Text::Format.new.paragraphs([" \n"]))
- end
-
- def test_splendiferous
- h = nil
- test = "This is a splendiferous test"
- assert_nothing_raised { @format_o = Text::Format.new(:columns => 6, :left_margin => 0, :indent => 0, :first_indent => 0) }
- assert_match(/^splendiferous$/, @format_o.format(test))
- assert_nothing_raised { @format_o.hard_margins = true }
- assert_match(/^lendif$/, @format_o.format(test))
- assert_nothing_raised { h = Object.new }
- assert_nothing_raised do
- @format_o.split_rules = Text::Format::SPLIT_HYPHENATION
- class << h #:nodoc:
- def hyphenate_to(word, size)
- return ["", word] if size < 2
- [word[0 ... size], word[size .. -1]]
- end
- end
- @format_o.hyphenator = h
- end
- assert_match(/^iferou$/, @format_o.format(test))
- assert_nothing_raised { h = Object.new }
- assert_nothing_raised do
- class << h #:nodoc:
- def hyphenate_to(word, size, formatter)
- return ["", word] if word.size < formatter.columns
- [word[0 ... size], word[size .. -1]]
- end
- end
- @format_o.hyphenator = h
- end
- assert_match(/^ferous$/, @format_o.format(test))
- end
- end
-end
+#--
+# Text::Format for Ruby
+# Version 0.63
+#
+# Copyright (c) 2002 - 2003 Austin Ziegler
+#
+# $Id: format.rb,v 1.1.1.1 2004/10/14 11:59:57 webster132 Exp $
+#
+# ==========================================================================
+# Revision History ::
+# YYYY.MM.DD Change ID Developer
+# Description
+# --------------------------------------------------------------------------
+# 2002.10.18 Austin Ziegler
+# Fixed a minor problem with tabs not being counted. Changed
+# abbreviations from Hash to Array to better suit Ruby's
+# capabilities. Fixed problems with the way that Array arguments
+# are handled in calls to the major object types, excepting in
+# Text::Format#expand and Text::Format#unexpand (these will
+# probably need to be fixed).
+# 2002.10.30 Austin Ziegler
+# Fixed the ordering of the <=> for binary tests. Fixed
+# Text::Format#expand and Text::Format#unexpand to handle array
+# arguments better.
+# 2003.01.24 Austin Ziegler
+# Fixed a problem with Text::Format::RIGHT_FILL handling where a
+# single word is larger than #columns. Removed Comparable
+# capabilities (<=> doesn't make sense; == does). Added Symbol
+# equivalents for the Hash initialization. Hash initialization has
+# been modified so that values are set as follows (Symbols are
+# highest priority; strings are middle; defaults are lowest):
+# @columns = arg[:columns] || arg['columns'] || @columns
+# Added #hard_margins, #split_rules, #hyphenator, and #split_words.
+# 2003.02.07 Austin Ziegler
+# Fixed the installer for proper case-sensitive handling.
+# 2003.03.28 Austin Ziegler
+# Added the ability for a hyphenator to receive the formatter
+# object. Fixed a bug for strings matching /\A\s*\Z/ failing
+# entirely. Fixed a test case failing under 1.6.8.
+# 2003.04.04 Austin Ziegler
+# Handle the case of hyphenators returning nil for first/rest.
+# 2003.09.17 Austin Ziegler
+# Fixed a problem where #paragraphs(" ") was raising
+# NoMethodError.
+#
+# ==========================================================================
+#++
+
+module Text #:nodoc:
+ # Text::Format for Ruby is copyright 2002 - 2005 by Austin Ziegler. It
+ # is available under Ruby's licence, the Perl Artistic licence, or the
+ # GNU GPL version 2 (or at your option, any later version). As a
+ # special exception, for use with official Rails (provided by the
+ # rubyonrails.org development team) and any project created with
+ # official Rails, the following alternative MIT-style licence may be
+ # used:
+ #
+ # == Text::Format Licence for Rails and Rails Applications
+ # Permission is hereby granted, free of charge, to any person
+ # obtaining a copy of this software and associated documentation files
+ # (the "Software"), to deal in the Software without restriction,
+ # including without limitation the rights to use, copy, modify, merge,
+ # publish, distribute, sublicense, and/or sell copies of the Software,
+ # and to permit persons to whom the Software is furnished to do so,
+ # subject to the following conditions:
+ #
+ # * The names of its contributors may not be used to endorse or
+ # promote products derived from this software without specific prior
+ # written permission.
+ #
+ # The above copyright notice and this permission notice shall be
+ # included in all copies or substantial portions of the Software.
+ #
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ # SOFTWARE.
+ class Format
+ VERSION = '0.63'
+
+ # Local abbreviations. More can be added with Text::Format.abbreviations
+ ABBREV = [ 'Mr', 'Mrs', 'Ms', 'Jr', 'Sr' ]
+
+ # Formatting values
+ LEFT_ALIGN = 0
+ RIGHT_ALIGN = 1
+ RIGHT_FILL = 2
+ JUSTIFY = 3
+
+ # Word split modes (only applies when #hard_margins is true).
+ SPLIT_FIXED = 1
+ SPLIT_CONTINUATION = 2
+ SPLIT_HYPHENATION = 4
+ SPLIT_CONTINUATION_FIXED = SPLIT_CONTINUATION | SPLIT_FIXED
+ SPLIT_HYPHENATION_FIXED = SPLIT_HYPHENATION | SPLIT_FIXED
+ SPLIT_HYPHENATION_CONTINUATION = SPLIT_HYPHENATION | SPLIT_CONTINUATION
+ SPLIT_ALL = SPLIT_HYPHENATION | SPLIT_CONTINUATION | SPLIT_FIXED
+
+ # Words forcibly split by Text::Format will be stored as split words.
+ # This class represents a word forcibly split.
+ class SplitWord
+ # The word that was split.
+ attr_reader :word
+ # The first part of the word that was split.
+ attr_reader :first
+ # The remainder of the word that was split.
+ attr_reader :rest
+
+ def initialize(word, first, rest) #:nodoc:
+ @word = word
+ @first = first
+ @rest = rest
+ end
+ end
+
+ private
+ LEQ_RE = /[.?!]['"]?$/
+
+ def brk_re(i) #:nodoc:
+ %r/((?:\S+\s+){#{i}})(.+)/
+ end
+
+ def posint(p) #:nodoc:
+ p.to_i.abs
+ end
+
+ public
+ # Compares two Text::Format objects. All settings of the objects are
+ # compared *except* #hyphenator. Generated results (e.g., #split_words)
+ # are not compared, either.
+ def ==(o)
+ (@text == o.text) &&
+ (@columns == o.columns) &&
+ (@left_margin == o.left_margin) &&
+ (@right_margin == o.right_margin) &&
+ (@hard_margins == o.hard_margins) &&
+ (@split_rules == o.split_rules) &&
+ (@first_indent == o.first_indent) &&
+ (@body_indent == o.body_indent) &&
+ (@tag_text == o.tag_text) &&
+ (@tabstop == o.tabstop) &&
+ (@format_style == o.format_style) &&
+ (@extra_space == o.extra_space) &&
+ (@tag_paragraph == o.tag_paragraph) &&
+ (@nobreak == o.nobreak) &&
+ (@abbreviations == o.abbreviations) &&
+ (@nobreak_regex == o.nobreak_regex)
+ end
+
+ # The text to be manipulated. Note that value is optional, but if the
+ # formatting functions are called without values, this text is what will
+ # be formatted.
+ #
+ # *Default*:: <tt>[]</tt>
+ # <b>Used in</b>:: All methods
+ attr_accessor :text
+
+ # The total width of the format area. The margins, indentation, and text
+ # are formatted into this space.
+ #
+ # COLUMNS
+ # <-------------------------------------------------------------->
+ # <-----------><------><---------------------------><------------>
+ # left margin indent text is formatted into here right margin
+ #
+ # *Default*:: <tt>72</tt>
+ # <b>Used in</b>:: <tt>#format</tt>, <tt>#paragraphs</tt>,
+ # <tt>#center</tt>
+ attr_reader :columns
+
+ # The total width of the format area. The margins, indentation, and text
+ # are formatted into this space. The value provided is silently
+ # converted to a positive integer.
+ #
+ # COLUMNS
+ # <-------------------------------------------------------------->
+ # <-----------><------><---------------------------><------------>
+ # left margin indent text is formatted into here right margin
+ #
+ # *Default*:: <tt>72</tt>
+ # <b>Used in</b>:: <tt>#format</tt>, <tt>#paragraphs</tt>,
+ # <tt>#center</tt>
+ def columns=(c)
+ @columns = posint(c)
+ end
+
+ # The number of spaces used for the left margin.
+ #
+ # columns
+ # <-------------------------------------------------------------->
+ # <-----------><------><---------------------------><------------>
+ # LEFT MARGIN indent text is formatted into here right margin
+ #
+ # *Default*:: <tt>0</tt>
+ # <b>Used in</b>:: <tt>#format</tt>, <tt>#paragraphs</tt>,
+ # <tt>#center</tt>
+ attr_reader :left_margin
+
+ # The number of spaces used for the left margin. The value provided is
+ # silently converted to a positive integer value.
+ #
+ # columns
+ # <-------------------------------------------------------------->
+ # <-----------><------><---------------------------><------------>
+ # LEFT MARGIN indent text is formatted into here right margin
+ #
+ # *Default*:: <tt>0</tt>
+ # <b>Used in</b>:: <tt>#format</tt>, <tt>#paragraphs</tt>,
+ # <tt>#center</tt>
+ def left_margin=(left)
+ @left_margin = posint(left)
+ end
+
+ # The number of spaces used for the right margin.
+ #
+ # columns
+ # <-------------------------------------------------------------->
+ # <-----------><------><---------------------------><------------>
+ # left margin indent text is formatted into here RIGHT MARGIN
+ #
+ # *Default*:: <tt>0</tt>
+ # <b>Used in</b>:: <tt>#format</tt>, <tt>#paragraphs</tt>,
+ # <tt>#center</tt>
+ attr_reader :right_margin
+
+ # The number of spaces used for the right margin. The value provided is
+ # silently converted to a positive integer value.
+ #
+ # columns
+ # <-------------------------------------------------------------->
+ # <-----------><------><---------------------------><------------>
+ # left margin indent text is formatted into here RIGHT MARGIN
+ #
+ # *Default*:: <tt>0</tt>
+ # <b>Used in</b>:: <tt>#format</tt>, <tt>#paragraphs</tt>,
+ # <tt>#center</tt>
+ def right_margin=(r)
+ @right_margin = posint(r)
+ end
+
+ # The number of spaces to indent the first line of a paragraph.
+ #
+ # columns
+ # <-------------------------------------------------------------->
+ # <-----------><------><---------------------------><------------>
+ # left margin INDENT text is formatted into here right margin
+ #
+ # *Default*:: <tt>4</tt>
+ # <b>Used in</b>:: <tt>#format</tt>, <tt>#paragraphs</tt>
+ attr_reader :first_indent
+
+ # The number of spaces to indent the first line of a paragraph. The
+ # value provided is silently converted to a positive integer value.
+ #
+ # columns
+ # <-------------------------------------------------------------->
+ # <-----------><------><---------------------------><------------>
+ # left margin INDENT text is formatted into here right margin
+ #
+ # *Default*:: <tt>4</tt>
+ # <b>Used in</b>:: <tt>#format</tt>, <tt>#paragraphs</tt>
+ def first_indent=(f)
+ @first_indent = posint(f)
+ end
+
+ # The number of spaces to indent all lines after the first line of a
+ # paragraph.
+ #
+ # columns
+ # <-------------------------------------------------------------->
+ # <-----------><------><---------------------------><------------>
+ # left margin INDENT text is formatted into here right margin
+ #
+ # *Default*:: <tt>0</tt>
+ # <b>Used in</b>:: <tt>#format</tt>, <tt>#paragraphs</tt>
+ attr_reader :body_indent
+
+ # The number of spaces to indent all lines after the first line of
+ # a paragraph. The value provided is silently converted to a
+ # positive integer value.
+ #
+ # columns
+ # <-------------------------------------------------------------->
+ # <-----------><------><---------------------------><------------>
+ # left margin INDENT text is formatted into here right margin
+ #
+ # *Default*:: <tt>0</tt>
+ # <b>Used in</b>:: <tt>#format</tt>, <tt>#paragraphs</tt>
+ def body_indent=(b)
+ @body_indent = posint(b)
+ end
+
+ # Normally, words larger than the format area will be placed on a line
+ # by themselves. Setting this to +true+ will force words larger than the
+ # format area to be split into one or more "words" each at most the size
+ # of the format area. The first line and the original word will be
+ # placed into <tt>#split_words</tt>. Note that this will cause the
+ # output to look *similar* to a #format_style of JUSTIFY. (Lines will be
+ # filled as much as possible.)
+ #
+ # *Default*:: +false+
+ # <b>Used in</b>:: <tt>#format</tt>, <tt>#paragraphs</tt>
+ attr_accessor :hard_margins
+
+ # An array of words split during formatting if #hard_margins is set to
+ # +true+.
+ # #split_words << Text::Format::SplitWord.new(word, first, rest)
+ attr_reader :split_words
+
+ # The object responsible for hyphenating. It must respond to
+ # #hyphenate_to(word, size) or #hyphenate_to(word, size, formatter) and
+ # return an array of the word split into two parts; if there is a
+ # hyphenation mark to be applied, responsibility belongs to the
+ # hyphenator object. The size is the MAXIMUM size permitted, including
+ # any hyphenation marks. If the #hyphenate_to method has an arity of 3,
+ # the formatter will be provided to the method. This allows the
+ # hyphenator to make decisions about the hyphenation based on the
+ # formatting rules.
+ #
+ # *Default*:: +nil+
+ # <b>Used in</b>:: <tt>#format</tt>, <tt>#paragraphs</tt>
+ attr_reader :hyphenator
+
+ # The object responsible for hyphenating. It must respond to
+ # #hyphenate_to(word, size) and return an array of the word hyphenated
+ # into two parts. The size is the MAXIMUM size permitted, including any
+ # hyphenation marks.
+ #
+ # *Default*:: +nil+
+ # <b>Used in</b>:: <tt>#format</tt>, <tt>#paragraphs</tt>
+ def hyphenator=(h)
+ raise ArgumentError, "#{h.inspect} is not a valid hyphenator." unless h.respond_to?(:hyphenate_to)
+ arity = h.method(:hyphenate_to).arity
+ raise ArgumentError, "#{h.inspect} must have exactly two or three arguments." unless [2, 3].include?(arity)
+ @hyphenator = h
+ @hyphenator_arity = arity
+ end
+
+ # Specifies the split mode; used only when #hard_margins is set to
+ # +true+. Allowable values are:
+ # [+SPLIT_FIXED+] The word will be split at the number of
+ # characters needed, with no marking at all.
+ # repre
+ # senta
+ # ion
+ # [+SPLIT_CONTINUATION+] The word will be split at the number of
+ # characters needed, with a C-style continuation
+ # character. If a word is the only item on a
+ # line and it cannot be split into an
+ # appropriate size, SPLIT_FIXED will be used.
+ # repr\
+ # esen\
+ # tati\
+ # on
+ # [+SPLIT_HYPHENATION+] The word will be split according to the
+ # hyphenator specified in #hyphenator. If there
+ # is no #hyphenator specified, works like
+ # SPLIT_CONTINUATION. The example is using
+ # TeX::Hyphen. If a word is the only item on a
+ # line and it cannot be split into an
+ # appropriate size, SPLIT_CONTINUATION mode will
+ # be used.
+ # rep-
+ # re-
+ # sen-
+ # ta-
+ # tion
+ #
+ # *Default*:: <tt>Text::Format::SPLIT_FIXED</tt>
+ # <b>Used in</b>:: <tt>#format</tt>, <tt>#paragraphs</tt>
+ attr_reader :split_rules
+
+ # Specifies the split mode; used only when #hard_margins is set to
+ # +true+. Allowable values are:
+ # [+SPLIT_FIXED+] The word will be split at the number of
+ # characters needed, with no marking at all.
+ # repre
+ # senta
+ # ion
+ # [+SPLIT_CONTINUATION+] The word will be split at the number of
+ # characters needed, with a C-style continuation
+ # character.
+ # repr\
+ # esen\
+ # tati\
+ # on
+ # [+SPLIT_HYPHENATION+] The word will be split according to the
+ # hyphenator specified in #hyphenator. If there
+ # is no #hyphenator specified, works like
+ # SPLIT_CONTINUATION. The example is using
+ # TeX::Hyphen as the #hyphenator.
+ # rep-
+ # re-
+ # sen-
+ # ta-
+ # tion
+ #
+ # These values can be bitwise ORed together (e.g., <tt>SPLIT_FIXED |
+ # SPLIT_CONTINUATION</tt>) to provide fallback split methods. In the
+ # example given, an attempt will be made to split the word using the
+ # rules of SPLIT_CONTINUATION; if there is not enough room, the word
+ # will be split with the rules of SPLIT_FIXED. These combinations are
+ # also available as the following values:
+ # * +SPLIT_CONTINUATION_FIXED+
+ # * +SPLIT_HYPHENATION_FIXED+
+ # * +SPLIT_HYPHENATION_CONTINUATION+
+ # * +SPLIT_ALL+
+ #
+ # *Default*:: <tt>Text::Format::SPLIT_FIXED</tt>
+ # <b>Used in</b>:: <tt>#format</tt>, <tt>#paragraphs</tt>
+ def split_rules=(s)
+ raise ArgumentError, "Invalid value provided for split_rules." if ((s < SPLIT_FIXED) || (s > SPLIT_ALL))
+ @split_rules = s
+ end
+
+ # Indicates whether sentence terminators should be followed by a single
+ # space (+false+), or two spaces (+true+).
+ #
+ # *Default*:: +false+
+ # <b>Used in</b>:: <tt>#format</tt>, <tt>#paragraphs</tt>
+ attr_accessor :extra_space
+
+ # Defines the current abbreviations as an array. This is only used if
+ # extra_space is turned on.
+ #
+ # If one is abbreviating "President" as "Pres." (abbreviations =
+ # ["Pres"]), then the results of formatting will be as illustrated in
+ # the table below:
+ #
+ # extra_space | include? | !include?
+ # true | Pres. Lincoln | Pres. Lincoln
+ # false | Pres. Lincoln | Pres. Lincoln
+ #
+ # *Default*:: <tt>{}</tt>
+ # <b>Used in</b>:: <tt>#format</tt>, <tt>#paragraphs</tt>
+ attr_accessor :abbreviations
+
+ # Indicates whether the formatting of paragraphs should be done with
+ # tagged paragraphs. Useful only with <tt>#tag_text</tt>.
+ #
+ # *Default*:: +false+
+ # <b>Used in</b>:: <tt>#format</tt>, <tt>#paragraphs</tt>
+ attr_accessor :tag_paragraph
+
+ # The array of text to be placed before each paragraph when
+ # <tt>#tag_paragraph</tt> is +true+. When <tt>#format()</tt> is called,
+ # only the first element of the array is used. When <tt>#paragraphs</tt>
+ # is called, then each entry in the array will be used once, with
+ # corresponding paragraphs. If the tag elements are exhausted before the
+ # text is exhausted, then the remaining paragraphs will not be tagged.
+ # Regardless of indentation settings, a blank line will be inserted
+ # between all paragraphs when <tt>#tag_paragraph</tt> is +true+.
+ #
+ # *Default*:: <tt>[]</tt>
+ # <b>Used in</b>:: <tt>#format</tt>, <tt>#paragraphs</tt>
+ attr_accessor :tag_text
+
+ # Indicates whether or not the non-breaking space feature should be
+ # used.
+ #
+ # *Default*:: +false+
+ # <b>Used in</b>:: <tt>#format</tt>, <tt>#paragraphs</tt>
+ attr_accessor :nobreak
+
+ # A hash which holds the regular expressions on which spaces should not
+ # be broken. The hash is set up such that the key is the first word and
+ # the value is the second word.
+ #
+ # For example, if +nobreak_regex+ contains the following hash:
+ #
+ # { '^Mrs?\.$' => '\S+$', '^\S+$' => '^(?:S|J)r\.$'}
+ #
+ # Then "Mr. Jones", "Mrs. Jones", and "Jones Jr." would not be broken.
+ # If this simple matching algorithm indicates that there should not be a
+ # break at the current end of line, then a backtrack is done until there
+ # are two words on which line breaking is permitted. If two such words
+ # are not found, then the end of the line will be broken *regardless*.
+ # If there is a single word on the current line, then no backtrack is
+ # done and the word is stuck on the end.
+ #
+ # *Default*:: <tt>{}</tt>
+ # <b>Used in</b>:: <tt>#format</tt>, <tt>#paragraphs</tt>
+ attr_accessor :nobreak_regex
+
+ # Indicates the number of spaces that a single tab represents.
+ #
+ # *Default*:: <tt>8</tt>
+ # <b>Used in</b>:: <tt>#expand</tt>, <tt>#unexpand</tt>,
+ # <tt>#paragraphs</tt>
+ attr_reader :tabstop
+
+ # Indicates the number of spaces that a single tab represents.
+ #
+ # *Default*:: <tt>8</tt>
+ # <b>Used in</b>:: <tt>#expand</tt>, <tt>#unexpand</tt>,
+ # <tt>#paragraphs</tt>
+ def tabstop=(t)
+ @tabstop = posint(t)
+ end
+
+ # Specifies the format style. Allowable values are:
+ # [+LEFT_ALIGN+] Left justified, ragged right.
+ # |A paragraph that is|
+ # |left aligned.|
+ # [+RIGHT_ALIGN+] Right justified, ragged left.
+ # |A paragraph that is|
+ # | right aligned.|
+ # [+RIGHT_FILL+] Left justified, right ragged, filled to width by
+ # spaces. (Essentially the same as +LEFT_ALIGN+ except
+ # that lines are padded on the right.)
+ # |A paragraph that is|
+ # |left aligned. |
+ # [+JUSTIFY+] Fully justified, words filled to width by spaces,
+ # except the last line.
+ # |A paragraph that|
+ # |is justified.|
+ #
+ # *Default*:: <tt>Text::Format::LEFT_ALIGN</tt>
+ # <b>Used in</b>:: <tt>#format</tt>, <tt>#paragraphs</tt>
+ attr_reader :format_style
+
+ # Specifies the format style. Allowable values are:
+ # [+LEFT_ALIGN+] Left justified, ragged right.
+ # |A paragraph that is|
+ # |left aligned.|
+ # [+RIGHT_ALIGN+] Right justified, ragged left.
+ # |A paragraph that is|
+ # | right aligned.|
+ # [+RIGHT_FILL+] Left justified, right ragged, filled to width by
+ # spaces. (Essentially the same as +LEFT_ALIGN+ except
+ # that lines are padded on the right.)
+ # |A paragraph that is|
+ # |left aligned. |
+ # [+JUSTIFY+] Fully justified, words filled to width by spaces.
+ # |A paragraph that|
+ # |is justified.|
+ #
+ # *Default*:: <tt>Text::Format::LEFT_ALIGN</tt>
+ # <b>Used in</b>:: <tt>#format</tt>, <tt>#paragraphs</tt>
+ def format_style=(fs)
+ raise ArgumentError, "Invalid value provided for format_style." if ((fs < LEFT_ALIGN) || (fs > JUSTIFY))
+ @format_style = fs
+ end
+
+ # Indicates that the format style is left alignment.
+ #
+ # *Default*:: +true+
+ # <b>Used in</b>:: <tt>#format</tt>, <tt>#paragraphs</tt>
+ def left_align?
+ return @format_style == LEFT_ALIGN
+ end
+
+ # Indicates that the format style is right alignment.
+ #
+ # *Default*:: +false+
+ # <b>Used in</b>:: <tt>#format</tt>, <tt>#paragraphs</tt>
+ def right_align?
+ return @format_style == RIGHT_ALIGN
+ end
+
+ # Indicates that the format style is right fill.
+ #
+ # *Default*:: +false+
+ # <b>Used in</b>:: <tt>#format</tt>, <tt>#paragraphs</tt>
+ def right_fill?
+ return @format_style == RIGHT_FILL
+ end
+
+ # Indicates that the format style is full justification.
+ #
+ # *Default*:: +false+
+ # <b>Used in</b>:: <tt>#format</tt>, <tt>#paragraphs</tt>
+ def justify?
+ return @format_style == JUSTIFY
+ end
+
+ # The default implementation of #hyphenate_to implements
+ # SPLIT_CONTINUATION.
+ def hyphenate_to(word, size)
+ [word[0 .. (size - 2)] + "\\", word[(size - 1) .. -1]]
+ end
+
+ private
+ def __do_split_word(word, size) #:nodoc:
+ [word[0 .. (size - 1)], word[size .. -1]]
+ end
+
+ def __format(to_wrap) #:nodoc:
+ words = to_wrap.split(/\s+/).compact
+ words.shift if words[0].nil? or words[0].empty?
+ to_wrap = []
+
+ abbrev = false
+ width = @columns - @first_indent - @left_margin - @right_margin
+ indent_str = ' ' * @first_indent
+ first_line = true
+ line = words.shift
+ abbrev = __is_abbrev(line) unless line.nil? || line.empty?
+
+ while w = words.shift
+ if (w.size + line.size < (width - 1)) ||
+ ((line !~ LEQ_RE || abbrev) && (w.size + line.size < width))
+ line << " " if (line =~ LEQ_RE) && (not abbrev)
+ line << " #{w}"
+ else
+ line, w = __do_break(line, w) if @nobreak
+ line, w = __do_hyphenate(line, w, width) if @hard_margins
+ if w.index(/\s+/)
+ w, *w2 = w.split(/\s+/)
+ words.unshift(w2)
+ words.flatten!
+ end
+ to_wrap << __make_line(line, indent_str, width, w.nil?) unless line.nil?
+ if first_line
+ first_line = false
+ width = @columns - @body_indent - @left_margin - @right_margin
+ indent_str = ' ' * @body_indent
+ end
+ line = w
+ end
+
+ abbrev = __is_abbrev(w) unless w.nil?
+ end
+
+ loop do
+ break if line.nil? or line.empty?
+ line, w = __do_hyphenate(line, w, width) if @hard_margins
+ to_wrap << __make_line(line, indent_str, width, w.nil?)
+ line = w
+ end
+
+ if (@tag_paragraph && (to_wrap.size > 0)) then
+ clr = %r{`(\w+)'}.match([caller(1)].flatten[0])[1]
+ clr = "" if clr.nil?
+
+ if ((not @tag_text[0].nil?) && (@tag_cur.size < 1) &&
+ (clr != "__paragraphs")) then
+ @tag_cur = @tag_text[0]
+ end
+
+ fchar = /(\S)/.match(to_wrap[0])[1]
+ white = to_wrap[0].index(fchar)
+ if ((white - @left_margin - 1) > @tag_cur.size) then
+ white = @tag_cur.size + @left_margin
+ to_wrap[0].gsub!(/^ {#{white}}/, "#{' ' * @left_margin}#{@tag_cur}")
+ else
+ to_wrap.unshift("#{' ' * @left_margin}#{@tag_cur}\n")
+ end
+ end
+ to_wrap.join('')
+ end
+
+ # format lines in text into paragraphs with each element of @wrap a
+ # paragraph; uses Text::Format.format for the formatting
+ def __paragraphs(to_wrap) #:nodoc:
+ if ((@first_indent == @body_indent) || @tag_paragraph) then
+ p_end = "\n"
+ else
+ p_end = ''
+ end
+
+ cnt = 0
+ ret = []
+ to_wrap.each do |tw|
+ @tag_cur = @tag_text[cnt] if @tag_paragraph
+ @tag_cur = '' if @tag_cur.nil?
+ line = __format(tw)
+ ret << "#{line}#{p_end}" if (not line.nil?) && (line.size > 0)
+ cnt += 1
+ end
+
+ ret[-1].chomp! unless ret.empty?
+ ret.join('')
+ end
+
+ # center text using spaces on left side to pad it out empty lines
+ # are preserved
+ def __center(to_center) #:nodoc:
+ tabs = 0
+ width = @columns - @left_margin - @right_margin
+ centered = []
+ to_center.each do |tc|
+ s = tc.strip
+ tabs = s.count("\t")
+ tabs = 0 if tabs.nil?
+ ct = ((width - s.size - (tabs * @tabstop) + tabs) / 2)
+ ct = (width - @left_margin - @right_margin) - ct
+ centered << "#{s.rjust(ct)}\n"
+ end
+ centered.join('')
+ end
+
+ # expand tabs to spaces should be similar to Text::Tabs::expand
+ def __expand(to_expand) #:nodoc:
+ expanded = []
+ to_expand.split("\n").each { |te| expanded << te.gsub(/\t/, ' ' * @tabstop) }
+ expanded.join('')
+ end
+
+ def __unexpand(to_unexpand) #:nodoc:
+ unexpanded = []
+ to_unexpand.split("\n").each { |tu| unexpanded << tu.gsub(/ {#{@tabstop}}/, "\t") }
+ unexpanded.join('')
+ end
+
+ def __is_abbrev(word) #:nodoc:
+ # remove period if there is one.
+ w = word.gsub(/\.$/, '') unless word.nil?
+ return true if (!@extra_space || ABBREV.include?(w) || @abbreviations.include?(w))
+ false
+ end
+
+ def __make_line(line, indent, width, last = false) #:nodoc:
+ lmargin = " " * @left_margin
+ fill = " " * (width - line.size) if right_fill? && (line.size <= width)
+
+ if (justify? && ((not line.nil?) && (not line.empty?)) && line =~ /\S+\s+\S+/ && !last)
+ spaces = width - line.size
+ words = line.split(/(\s+)/)
+ ws = spaces / (words.size / 2)
+ spaces = spaces % (words.size / 2) if ws > 0
+ words.reverse.each do |rw|
+ next if (rw =~ /^\S/)
+ rw.sub!(/^/, " " * ws)
+ next unless (spaces > 0)
+ rw.sub!(/^/, " ")
+ spaces -= 1
+ end
+ line = words.join('')
+ end
+ line = "#{lmargin}#{indent}#{line}#{fill}\n" unless line.nil?
+ if right_align? && (not line.nil?)
+ line.sub(/^/, " " * (@columns - @right_margin - (line.size - 1)))
+ else
+ line
+ end
+ end
+
+ def __do_hyphenate(line, next_line, width) #:nodoc:
+ rline = line.dup rescue line
+ rnext = next_line.dup rescue next_line
+ loop do
+ if rline.size == width
+ break
+ elsif rline.size > width
+ words = rline.strip.split(/\s+/)
+ word = words[-1].dup
+ size = width - rline.size + word.size
+ if (size <= 0)
+ words[-1] = nil
+ rline = words.join(' ').strip
+ rnext = "#{word} #{rnext}".strip
+ next
+ end
+
+ first = rest = nil
+
+ if ((@split_rules & SPLIT_HYPHENATION) != 0)
+ if @hyphenator_arity == 2
+ first, rest = @hyphenator.hyphenate_to(word, size)
+ else
+ first, rest = @hyphenator.hyphenate_to(word, size, self)
+ end
+ end
+
+ if ((@split_rules & SPLIT_CONTINUATION) != 0) and first.nil?
+ first, rest = self.hyphenate_to(word, size)
+ end
+
+ if ((@split_rules & SPLIT_FIXED) != 0) and first.nil?
+ first.nil? or @split_rules == SPLIT_FIXED
+ first, rest = __do_split_word(word, size)
+ end
+
+ if first.nil?
+ words[-1] = nil
+ rest = word
+ else
+ words[-1] = first
+ @split_words << SplitWord.new(word, first, rest)
+ end
+ rline = words.join(' ').strip
+ rnext = "#{rest} #{rnext}".strip
+ break
+ else
+ break if rnext.nil? or rnext.empty? or rline.nil? or rline.empty?
+ words = rnext.split(/\s+/)
+ word = words.shift
+ size = width - rline.size - 1
+
+ if (size <= 0)
+ rnext = "#{word} #{words.join(' ')}".strip
+ break
+ end
+
+ first = rest = nil
+
+ if ((@split_rules & SPLIT_HYPHENATION) != 0)
+ if @hyphenator_arity == 2
+ first, rest = @hyphenator.hyphenate_to(word, size)
+ else
+ first, rest = @hyphenator.hyphenate_to(word, size, self)
+ end
+ end
+
+ first, rest = self.hyphenate_to(word, size) if ((@split_rules & SPLIT_CONTINUATION) != 0) and first.nil?
+
+ first, rest = __do_split_word(word, size) if ((@split_rules & SPLIT_FIXED) != 0) and first.nil?
+
+ if (rline.size + (first ? first.size : 0)) < width
+ @split_words << SplitWord.new(word, first, rest)
+ rline = "#{rline} #{first}".strip
+ rnext = "#{rest} #{words.join(' ')}".strip
+ end
+ break
+ end
+ end
+ [rline, rnext]
+ end
+
+ def __do_break(line, next_line) #:nodoc:
+ no_brk = false
+ words = []
+ words = line.split(/\s+/) unless line.nil?
+ last_word = words[-1]
+
+ @nobreak_regex.each { |k, v| no_brk = ((last_word =~ /#{k}/) and (next_line =~ /#{v}/)) }
+
+ if no_brk && words.size > 1
+ i = words.size
+ while i > 0
+ no_brk = false
+ @nobreak_regex.each { |k, v| no_brk = ((words[i + 1] =~ /#{k}/) && (words[i] =~ /#{v}/)) }
+ i -= 1
+ break if not no_brk
+ end
+ if i > 0
+ l = brk_re(i).match(line)
+ line.sub!(brk_re(i), l[1])
+ next_line = "#{l[2]} #{next_line}"
+ line.sub!(/\s+$/, '')
+ end
+ end
+ [line, next_line]
+ end
+
+ def __create(arg = nil, &block) #:nodoc:
+ # Format::Text.new(text-to-wrap)
+ @text = arg unless arg.nil?
+ # Defaults
+ @columns = 72
+ @tabstop = 8
+ @first_indent = 4
+ @body_indent = 0
+ @format_style = LEFT_ALIGN
+ @left_margin = 0
+ @right_margin = 0
+ @extra_space = false
+ @text = Array.new if @text.nil?
+ @tag_paragraph = false
+ @tag_text = Array.new
+ @tag_cur = ""
+ @abbreviations = Array.new
+ @nobreak = false
+ @nobreak_regex = Hash.new
+ @split_words = Array.new
+ @hard_margins = false
+ @split_rules = SPLIT_FIXED
+ @hyphenator = self
+ @hyphenator_arity = self.method(:hyphenate_to).arity
+
+ instance_eval(&block) unless block.nil?
+ end
+
+ public
+ # Formats text into a nice paragraph format. The text is separated
+ # into words and then reassembled a word at a time using the settings
+ # of this Format object. If a word is larger than the number of
+ # columns available for formatting, then that word will appear on the
+ # line by itself.
+ #
+ # If +to_wrap+ is +nil+, then the value of <tt>#text</tt> will be
+ # worked on.
+ def format(to_wrap = nil)
+ to_wrap = @text if to_wrap.nil?
+ if to_wrap.class == Array
+ __format(to_wrap[0])
+ else
+ __format(to_wrap)
+ end
+ end
+
+ # Considers each element of text (provided or internal) as a paragraph.
+ # If <tt>#first_indent</tt> is the same as <tt>#body_indent</tt>, then
+ # paragraphs will be separated by a single empty line in the result;
+ # otherwise, the paragraphs will follow immediately after each other.
+ # Uses <tt>#format</tt> to do the heavy lifting.
+ def paragraphs(to_wrap = nil)
+ to_wrap = @text if to_wrap.nil?
+ __paragraphs([to_wrap].flatten)
+ end
+
+ # Centers the text, preserving empty lines and tabs.
+ def center(to_center = nil)
+ to_center = @text if to_center.nil?
+ __center([to_center].flatten)
+ end
+
+ # Replaces all tab characters in the text with <tt>#tabstop</tt> spaces.
+ def expand(to_expand = nil)
+ to_expand = @text if to_expand.nil?
+ if to_expand.class == Array
+ to_expand.collect { |te| __expand(te) }
+ else
+ __expand(to_expand)
+ end
+ end
+
+ # Replaces all occurrences of <tt>#tabstop</tt> consecutive spaces
+ # with a tab character.
+ def unexpand(to_unexpand = nil)
+ to_unexpand = @text if to_unexpand.nil?
+ if to_unexpand.class == Array
+ to_unexpand.collect { |te| v << __unexpand(te) }
+ else
+ __unexpand(to_unexpand)
+ end
+ end
+
+ # This constructor takes advantage of a technique for Ruby object
+ # construction introduced by Andy Hunt and Dave Thomas (see reference),
+ # where optional values are set using commands in a block.
+ #
+ # Text::Format.new {
+ # columns = 72
+ # left_margin = 0
+ # right_margin = 0
+ # first_indent = 4
+ # body_indent = 0
+ # format_style = Text::Format::LEFT_ALIGN
+ # extra_space = false
+ # abbreviations = {}
+ # tag_paragraph = false
+ # tag_text = []
+ # nobreak = false
+ # nobreak_regex = {}
+ # tabstop = 8
+ # text = nil
+ # }
+ #
+ # As shown above, +arg+ is optional. If +arg+ is specified and is a
+ # +String+, then arg is used as the default value of <tt>#text</tt>.
+ # Alternately, an existing Text::Format object can be used or a Hash can
+ # be used. With all forms, a block can be specified.
+ #
+ # *Reference*:: "Object Construction and Blocks"
+ # <http://www.pragmaticprogrammer.com/ruby/articles/insteval.html>
+ #
+ def initialize(arg = nil, &block)
+ @text = nil
+ case arg
+ when Text::Format
+ __create(arg.text) do
+ @columns = arg.columns
+ @tabstop = arg.tabstop
+ @first_indent = arg.first_indent
+ @body_indent = arg.body_indent
+ @format_style = arg.format_style
+ @left_margin = arg.left_margin
+ @right_margin = arg.right_margin
+ @extra_space = arg.extra_space
+ @tag_paragraph = arg.tag_paragraph
+ @tag_text = arg.tag_text
+ @abbreviations = arg.abbreviations
+ @nobreak = arg.nobreak
+ @nobreak_regex = arg.nobreak_regex
+ @text = arg.text
+ @hard_margins = arg.hard_margins
+ @split_words = arg.split_words
+ @split_rules = arg.split_rules
+ @hyphenator = arg.hyphenator
+ end
+ instance_eval(&block) unless block.nil?
+ when Hash
+ __create do
+ @columns = arg[:columns] || arg['columns'] || @columns
+ @tabstop = arg[:tabstop] || arg['tabstop'] || @tabstop
+ @first_indent = arg[:first_indent] || arg['first_indent'] || @first_indent
+ @body_indent = arg[:body_indent] || arg['body_indent'] || @body_indent
+ @format_style = arg[:format_style] || arg['format_style'] || @format_style
+ @left_margin = arg[:left_margin] || arg['left_margin'] || @left_margin
+ @right_margin = arg[:right_margin] || arg['right_margin'] || @right_margin
+ @extra_space = arg[:extra_space] || arg['extra_space'] || @extra_space
+ @text = arg[:text] || arg['text'] || @text
+ @tag_paragraph = arg[:tag_paragraph] || arg['tag_paragraph'] || @tag_paragraph
+ @tag_text = arg[:tag_text] || arg['tag_text'] || @tag_text
+ @abbreviations = arg[:abbreviations] || arg['abbreviations'] || @abbreviations
+ @nobreak = arg[:nobreak] || arg['nobreak'] || @nobreak
+ @nobreak_regex = arg[:nobreak_regex] || arg['nobreak_regex'] || @nobreak_regex
+ @hard_margins = arg[:hard_margins] || arg['hard_margins'] || @hard_margins
+ @split_rules = arg[:split_rules] || arg['split_rules'] || @split_rules
+ @hyphenator = arg[:hyphenator] || arg['hyphenator'] || @hyphenator
+ end
+ instance_eval(&block) unless block.nil?
+ when String
+ __create(arg, &block)
+ when NilClass
+ __create(&block)
+ else
+ raise TypeError
+ end
+ end
+ end
+end
+
+if __FILE__ == $0
+ require 'test/unit'
+
+ class TestText__Format < Test::Unit::TestCase #:nodoc:
+ attr_accessor :format_o
+
+ GETTYSBURG = <<-'EOS'
+ Four score and seven years ago our fathers brought forth on this
+ continent a new nation, conceived in liberty and dedicated to the
+ proposition that all men are created equal. Now we are engaged in
+ a great civil war, testing whether that nation or any nation so
+ conceived and so dedicated can long endure. We are met on a great
+ battlefield of that war. We have come to dedicate a portion of
+ that field as a final resting-place for those who here gave their
+ lives that that nation might live. It is altogether fitting and
+ proper that we should do this. But in a larger sense, we cannot
+ dedicate, we cannot consecrate, we cannot hallow this ground.
+ The brave men, living and dead who struggled here have consecrated
+ it far above our poor power to add or detract. The world will
+ little note nor long remember what we say here, but it can never
+ forget what they did here. It is for us the living rather to be
+ dedicated here to the unfinished work which they who fought here
+ have thus far so nobly advanced. It is rather for us to be here
+ dedicated to the great task remaining before us--that from these
+ honored dead we take increased devotion to that cause for which
+ they gave the last full measure of devotion--that we here highly
+ resolve that these dead shall not have died in vain, that this
+ nation under God shall have a new birth of freedom, and that
+ government of the people, by the people, for the people shall
+ not perish from the earth.
+
+ -- Pres. Abraham Lincoln, 19 November 1863
+ EOS
+
+ FIVE_COL = "Four \nscore\nand s\neven \nyears\nago o\nur fa\nthers\nbroug\nht fo\nrth o\nn thi\ns con\ntinen\nt a n\new na\ntion,\nconce\nived \nin li\nberty\nand d\nedica\nted t\no the\npropo\nsitio\nn tha\nt all\nmen a\nre cr\neated\nequal\n. Now\nwe ar\ne eng\naged \nin a \ngreat\ncivil\nwar, \ntesti\nng wh\nether\nthat \nnatio\nn or \nany n\nation\nso co\nnceiv\ned an\nd so \ndedic\nated \ncan l\nong e\nndure\n. We \nare m\net on\na gre\nat ba\nttlef\nield \nof th\nat wa\nr. We\nhave \ncome \nto de\ndicat\ne a p\nortio\nn of \nthat \nfield\nas a \nfinal\nresti\nng-pl\nace f\nor th\nose w\nho he\nre ga\nve th\neir l\nives \nthat \nthat \nnatio\nn mig\nht li\nve. I\nt is \naltog\nether\nfitti\nng an\nd pro\nper t\nhat w\ne sho\nuld d\no thi\ns. Bu\nt in \na lar\nger s\nense,\nwe ca\nnnot \ndedic\nate, \nwe ca\nnnot \nconse\ncrate\n, we \ncanno\nt hal\nlow t\nhis g\nround\n. The\nbrave\nmen, \nlivin\ng and\ndead \nwho s\ntrugg\nled h\nere h\nave c\nonsec\nrated\nit fa\nr abo\nve ou\nr poo\nr pow\ner to\nadd o\nr det\nract.\nThe w\norld \nwill \nlittl\ne not\ne nor\nlong \nremem\nber w\nhat w\ne say\nhere,\nbut i\nt can\nnever\nforge\nt wha\nt the\ny did\nhere.\nIt is\nfor u\ns the\nlivin\ng rat\nher t\no be \ndedic\nated \nhere \nto th\ne unf\ninish\ned wo\nrk wh\nich t\nhey w\nho fo\nught \nhere \nhave \nthus \nfar s\no nob\nly ad\nvance\nd. It\nis ra\nther \nfor u\ns to \nbe he\nre de\ndicat\ned to\nthe g\nreat \ntask \nremai\nning \nbefor\ne us-\n-that\nfrom \nthese\nhonor\ned de\nad we\ntake \nincre\nased \ndevot\nion t\no tha\nt cau\nse fo\nr whi\nch th\ney ga\nve th\ne las\nt ful\nl mea\nsure \nof de\nvotio\nn--th\nat we\nhere \nhighl\ny res\nolve \nthat \nthese\ndead \nshall\nnot h\nave d\nied i\nn vai\nn, th\nat th\nis na\ntion \nunder\nGod s\nhall \nhave \na new\nbirth\nof fr\needom\n, and\nthat \ngover\nnment\nof th\ne peo\nple, \nby th\ne peo\nple, \nfor t\nhe pe\nople \nshall\nnot p\nerish\nfrom \nthe e\narth.\n-- Pr\nes. A\nbraha\nm Lin\ncoln,\n19 No\nvembe\nr 186\n3 \n"
+
+ FIVE_CNT = "Four \nscore\nand \nseven\nyears\nago \nour \nfath\\\ners \nbrou\\\nght \nforth\non t\\\nhis \ncont\\\ninent\na new\nnati\\\non, \nconc\\\neived\nin l\\\niber\\\nty a\\\nnd d\\\nedic\\\nated \nto t\\\nhe p\\\nropo\\\nsiti\\\non t\\\nhat \nall \nmen \nare \ncrea\\\nted \nequa\\\nl. N\\\now we\nare \nenga\\\nged \nin a \ngreat\ncivil\nwar, \ntest\\\ning \nwhet\\\nher \nthat \nnati\\\non or\nany \nnati\\\non so\nconc\\\neived\nand \nso d\\\nedic\\\nated \ncan \nlong \nendu\\\nre. \nWe a\\\nre m\\\net on\na gr\\\neat \nbatt\\\nlefi\\\neld \nof t\\\nhat \nwar. \nWe h\\\nave \ncome \nto d\\\nedic\\\nate a\nport\\\nion \nof t\\\nhat \nfield\nas a \nfinal\nrest\\\ning-\\\nplace\nfor \nthose\nwho \nhere \ngave \ntheir\nlives\nthat \nthat \nnati\\\non m\\\night \nlive.\nIt is\nalto\\\ngeth\\\ner f\\\nitti\\\nng a\\\nnd p\\\nroper\nthat \nwe s\\\nhould\ndo t\\\nhis. \nBut \nin a \nlarg\\\ner s\\\nense,\nwe c\\\nannot\ndedi\\\ncate,\nwe c\\\nannot\ncons\\\necra\\\nte, \nwe c\\\nannot\nhall\\\now t\\\nhis \ngrou\\\nnd. \nThe \nbrave\nmen, \nlivi\\\nng a\\\nnd d\\\nead \nwho \nstru\\\nggled\nhere \nhave \ncons\\\necra\\\nted \nit f\\\nar a\\\nbove \nour \npoor \npower\nto a\\\ndd or\ndetr\\\nact. \nThe \nworld\nwill \nlitt\\\nle n\\\note \nnor \nlong \nreme\\\nmber \nwhat \nwe s\\\nay h\\\nere, \nbut \nit c\\\nan n\\\never \nforg\\\net w\\\nhat \nthey \ndid \nhere.\nIt is\nfor \nus t\\\nhe l\\\niving\nrath\\\ner to\nbe d\\\nedic\\\nated \nhere \nto t\\\nhe u\\\nnfin\\\nished\nwork \nwhich\nthey \nwho \nfoug\\\nht h\\\nere \nhave \nthus \nfar \nso n\\\nobly \nadva\\\nnced.\nIt is\nrath\\\ner f\\\nor us\nto be\nhere \ndedi\\\ncated\nto t\\\nhe g\\\nreat \ntask \nrema\\\nining\nbefo\\\nre u\\\ns--t\\\nhat \nfrom \nthese\nhono\\\nred \ndead \nwe t\\\nake \nincr\\\neased\ndevo\\\ntion \nto t\\\nhat \ncause\nfor \nwhich\nthey \ngave \nthe \nlast \nfull \nmeas\\\nure \nof d\\\nevot\\\nion-\\\n-that\nwe h\\\nere \nhigh\\\nly r\\\nesol\\\nve t\\\nhat \nthese\ndead \nshall\nnot \nhave \ndied \nin v\\\nain, \nthat \nthis \nnati\\\non u\\\nnder \nGod \nshall\nhave \na new\nbirth\nof f\\\nreed\\\nom, \nand \nthat \ngove\\\nrnme\\\nnt of\nthe \npeop\\\nle, \nby t\\\nhe p\\\neopl\\\ne, f\\\nor t\\\nhe p\\\neople\nshall\nnot \nperi\\\nsh f\\\nrom \nthe \neart\\\nh. --\nPres.\nAbra\\\nham \nLinc\\\noln, \n19 N\\\novem\\\nber \n1863 \n"
+
+ # Tests both abbreviations and abbreviations=
+ def test_abbreviations
+ abbr = [" Pres. Abraham Lincoln\n", " Pres. Abraham Lincoln\n"]
+ assert_nothing_raised { @format_o = Text::Format.new }
+ assert_equal([], @format_o.abbreviations)
+ assert_nothing_raised { @format_o.abbreviations = [ 'foo', 'bar' ] }
+ assert_equal([ 'foo', 'bar' ], @format_o.abbreviations)
+ assert_equal(abbr[0], @format_o.format(abbr[0]))
+ assert_nothing_raised { @format_o.extra_space = true }
+ assert_equal(abbr[1], @format_o.format(abbr[0]))
+ assert_nothing_raised { @format_o.abbreviations = [ "Pres" ] }
+ assert_equal([ "Pres" ], @format_o.abbreviations)
+ assert_equal(abbr[0], @format_o.format(abbr[0]))
+ assert_nothing_raised { @format_o.extra_space = false }
+ assert_equal(abbr[0], @format_o.format(abbr[0]))
+ end
+
+ # Tests both body_indent and body_indent=
+ def test_body_indent
+ assert_nothing_raised { @format_o = Text::Format.new }
+ assert_equal(0, @format_o.body_indent)
+ assert_nothing_raised { @format_o.body_indent = 7 }
+ assert_equal(7, @format_o.body_indent)
+ assert_nothing_raised { @format_o.body_indent = -3 }
+ assert_equal(3, @format_o.body_indent)
+ assert_nothing_raised { @format_o.body_indent = "9" }
+ assert_equal(9, @format_o.body_indent)
+ assert_nothing_raised { @format_o.body_indent = "-2" }
+ assert_equal(2, @format_o.body_indent)
+ assert_match(/^ [^ ]/, @format_o.format(GETTYSBURG).split("\n")[1])
+ end
+
+ # Tests both columns and columns=
+ def test_columns
+ assert_nothing_raised { @format_o = Text::Format.new }
+ assert_equal(72, @format_o.columns)
+ assert_nothing_raised { @format_o.columns = 7 }
+ assert_equal(7, @format_o.columns)
+ assert_nothing_raised { @format_o.columns = -3 }
+ assert_equal(3, @format_o.columns)
+ assert_nothing_raised { @format_o.columns = "9" }
+ assert_equal(9, @format_o.columns)
+ assert_nothing_raised { @format_o.columns = "-2" }
+ assert_equal(2, @format_o.columns)
+ assert_nothing_raised { @format_o.columns = 40 }
+ assert_equal(40, @format_o.columns)
+ assert_match(/this continent$/,
+ @format_o.format(GETTYSBURG).split("\n")[1])
+ end
+
+ # Tests both extra_space and extra_space=
+ def test_extra_space
+ assert_nothing_raised { @format_o = Text::Format.new }
+ assert(!@format_o.extra_space)
+ assert_nothing_raised { @format_o.extra_space = true }
+ assert(@format_o.extra_space)
+ # The behaviour of extra_space is tested in test_abbreviations. There
+ # is no need to reproduce it here.
+ end
+
+ # Tests both first_indent and first_indent=
+ def test_first_indent
+ assert_nothing_raised { @format_o = Text::Format.new }
+ assert_equal(4, @format_o.first_indent)
+ assert_nothing_raised { @format_o.first_indent = 7 }
+ assert_equal(7, @format_o.first_indent)
+ assert_nothing_raised { @format_o.first_indent = -3 }
+ assert_equal(3, @format_o.first_indent)
+ assert_nothing_raised { @format_o.first_indent = "9" }
+ assert_equal(9, @format_o.first_indent)
+ assert_nothing_raised { @format_o.first_indent = "-2" }
+ assert_equal(2, @format_o.first_indent)
+ assert_match(/^ [^ ]/, @format_o.format(GETTYSBURG).split("\n")[0])
+ end
+
+ def test_format_style
+ assert_nothing_raised { @format_o = Text::Format.new }
+ assert_equal(Text::Format::LEFT_ALIGN, @format_o.format_style)
+ assert_match(/^November 1863$/,
+ @format_o.format(GETTYSBURG).split("\n")[-1])
+ assert_nothing_raised {
+ @format_o.format_style = Text::Format::RIGHT_ALIGN
+ }
+ assert_equal(Text::Format::RIGHT_ALIGN, @format_o.format_style)
+ assert_match(/^ +November 1863$/,
+ @format_o.format(GETTYSBURG).split("\n")[-1])
+ assert_nothing_raised {
+ @format_o.format_style = Text::Format::RIGHT_FILL
+ }
+ assert_equal(Text::Format::RIGHT_FILL, @format_o.format_style)
+ assert_match(/^November 1863 +$/,
+ @format_o.format(GETTYSBURG).split("\n")[-1])
+ assert_nothing_raised { @format_o.format_style = Text::Format::JUSTIFY }
+ assert_equal(Text::Format::JUSTIFY, @format_o.format_style)
+ assert_match(/^of freedom, and that government of the people, by the people, for the$/,
+ @format_o.format(GETTYSBURG).split("\n")[-3])
+ assert_raise(ArgumentError) { @format_o.format_style = 33 }
+ end
+
+ def test_tag_paragraph
+ assert_nothing_raised { @format_o = Text::Format.new }
+ assert(!@format_o.tag_paragraph)
+ assert_nothing_raised { @format_o.tag_paragraph = true }
+ assert(@format_o.tag_paragraph)
+ assert_not_equal(@format_o.paragraphs([GETTYSBURG, GETTYSBURG]),
+ Text::Format.new.paragraphs([GETTYSBURG, GETTYSBURG]))
+ end
+
+ def test_tag_text
+ assert_nothing_raised { @format_o = Text::Format.new }
+ assert_equal([], @format_o.tag_text)
+ assert_equal(@format_o.format(GETTYSBURG),
+ Text::Format.new.format(GETTYSBURG))
+ assert_nothing_raised {
+ @format_o.tag_paragraph = true
+ @format_o.tag_text = ["Gettysburg Address", "---"]
+ }
+ assert_not_equal(@format_o.format(GETTYSBURG),
+ Text::Format.new.format(GETTYSBURG))
+ assert_not_equal(@format_o.paragraphs([GETTYSBURG, GETTYSBURG]),
+ Text::Format.new.paragraphs([GETTYSBURG, GETTYSBURG]))
+ assert_not_equal(@format_o.paragraphs([GETTYSBURG, GETTYSBURG,
+ GETTYSBURG]),
+ Text::Format.new.paragraphs([GETTYSBURG, GETTYSBURG,
+ GETTYSBURG]))
+ end
+
+ def test_justify?
+ assert_nothing_raised { @format_o = Text::Format.new }
+ assert(!@format_o.justify?)
+ assert_nothing_raised {
+ @format_o.format_style = Text::Format::RIGHT_ALIGN
+ }
+ assert(!@format_o.justify?)
+ assert_nothing_raised {
+ @format_o.format_style = Text::Format::RIGHT_FILL
+ }
+ assert(!@format_o.justify?)
+ assert_nothing_raised {
+ @format_o.format_style = Text::Format::JUSTIFY
+ }
+ assert(@format_o.justify?)
+ # The format testing is done in test_format_style
+ end
+
+ def test_left_align?
+ assert_nothing_raised { @format_o = Text::Format.new }
+ assert(@format_o.left_align?)
+ assert_nothing_raised {
+ @format_o.format_style = Text::Format::RIGHT_ALIGN
+ }
+ assert(!@format_o.left_align?)
+ assert_nothing_raised {
+ @format_o.format_style = Text::Format::RIGHT_FILL
+ }
+ assert(!@format_o.left_align?)
+ assert_nothing_raised { @format_o.format_style = Text::Format::JUSTIFY }
+ assert(!@format_o.left_align?)
+ # The format testing is done in test_format_style
+ end
+
+ def test_left_margin
+ assert_nothing_raised { @format_o = Text::Format.new }
+ assert_equal(0, @format_o.left_margin)
+ assert_nothing_raised { @format_o.left_margin = -3 }
+ assert_equal(3, @format_o.left_margin)
+ assert_nothing_raised { @format_o.left_margin = "9" }
+ assert_equal(9, @format_o.left_margin)
+ assert_nothing_raised { @format_o.left_margin = "-2" }
+ assert_equal(2, @format_o.left_margin)
+ assert_nothing_raised { @format_o.left_margin = 7 }
+ assert_equal(7, @format_o.left_margin)
+ assert_nothing_raised {
+ ft = @format_o.format(GETTYSBURG).split("\n")
+ assert_match(/^ {11}Four score/, ft[0])
+ assert_match(/^ {7}November/, ft[-1])
+ }
+ end
+
+ def test_hard_margins
+ assert_nothing_raised { @format_o = Text::Format.new }
+ assert(!@format_o.hard_margins)
+ assert_nothing_raised {
+ @format_o.hard_margins = true
+ @format_o.columns = 5
+ @format_o.first_indent = 0
+ @format_o.format_style = Text::Format::RIGHT_FILL
+ }
+ assert(@format_o.hard_margins)
+ assert_equal(FIVE_COL, @format_o.format(GETTYSBURG))
+ assert_nothing_raised {
+ @format_o.split_rules |= Text::Format::SPLIT_CONTINUATION
+ assert_equal(Text::Format::SPLIT_CONTINUATION_FIXED,
+ @format_o.split_rules)
+ }
+ assert_equal(FIVE_CNT, @format_o.format(GETTYSBURG))
+ end
+
+ # Tests both nobreak and nobreak_regex, since one is only useful
+ # with the other.
+ def test_nobreak
+ assert_nothing_raised { @format_o = Text::Format.new }
+ assert(!@format_o.nobreak)
+ assert(@format_o.nobreak_regex.empty?)
+ assert_nothing_raised {
+ @format_o.nobreak = true
+ @format_o.nobreak_regex = { '^this$' => '^continent$' }
+ @format_o.columns = 77
+ }
+ assert(@format_o.nobreak)
+ assert_equal({ '^this$' => '^continent$' }, @format_o.nobreak_regex)
+ assert_match(/^this continent/,
+ @format_o.format(GETTYSBURG).split("\n")[1])
+ end
+
+ def test_right_align?
+ assert_nothing_raised { @format_o = Text::Format.new }
+ assert(!@format_o.right_align?)
+ assert_nothing_raised {
+ @format_o.format_style = Text::Format::RIGHT_ALIGN
+ }
+ assert(@format_o.right_align?)
+ assert_nothing_raised {
+ @format_o.format_style = Text::Format::RIGHT_FILL
+ }
+ assert(!@format_o.right_align?)
+ assert_nothing_raised { @format_o.format_style = Text::Format::JUSTIFY }
+ assert(!@format_o.right_align?)
+ # The format testing is done in test_format_style
+ end
+
+ def test_right_fill?
+ assert_nothing_raised { @format_o = Text::Format.new }
+ assert(!@format_o.right_fill?)
+ assert_nothing_raised {
+ @format_o.format_style = Text::Format::RIGHT_ALIGN
+ }
+ assert(!@format_o.right_fill?)
+ assert_nothing_raised {
+ @format_o.format_style = Text::Format::RIGHT_FILL
+ }
+ assert(@format_o.right_fill?)
+ assert_nothing_raised {
+ @format_o.format_style = Text::Format::JUSTIFY
+ }
+ assert(!@format_o.right_fill?)
+ # The format testing is done in test_format_style
+ end
+
+ def test_right_margin
+ assert_nothing_raised { @format_o = Text::Format.new }
+ assert_equal(0, @format_o.right_margin)
+ assert_nothing_raised { @format_o.right_margin = -3 }
+ assert_equal(3, @format_o.right_margin)
+ assert_nothing_raised { @format_o.right_margin = "9" }
+ assert_equal(9, @format_o.right_margin)
+ assert_nothing_raised { @format_o.right_margin = "-2" }
+ assert_equal(2, @format_o.right_margin)
+ assert_nothing_raised { @format_o.right_margin = 7 }
+ assert_equal(7, @format_o.right_margin)
+ assert_nothing_raised {
+ ft = @format_o.format(GETTYSBURG).split("\n")
+ assert_match(/^ {4}Four score.*forth on$/, ft[0])
+ assert_match(/^November/, ft[-1])
+ }
+ end
+
+ def test_tabstop
+ assert_nothing_raised { @format_o = Text::Format.new }
+ assert_equal(8, @format_o.tabstop)
+ assert_nothing_raised { @format_o.tabstop = 7 }
+ assert_equal(7, @format_o.tabstop)
+ assert_nothing_raised { @format_o.tabstop = -3 }
+ assert_equal(3, @format_o.tabstop)
+ assert_nothing_raised { @format_o.tabstop = "9" }
+ assert_equal(9, @format_o.tabstop)
+ assert_nothing_raised { @format_o.tabstop = "-2" }
+ assert_equal(2, @format_o.tabstop)
+ end
+
+ def test_text
+ assert_nothing_raised { @format_o = Text::Format.new }
+ assert_equal([], @format_o.text)
+ assert_nothing_raised { @format_o.text = "Test Text" }
+ assert_equal("Test Text", @format_o.text)
+ assert_nothing_raised { @format_o.text = ["Line 1", "Line 2"] }
+ assert_equal(["Line 1", "Line 2"], @format_o.text)
+ end
+
+ def test_s_new
+ # new(NilClass) { block }
+ assert_nothing_raised do
+ @format_o = Text::Format.new {
+ self.text = "Test 1, 2, 3"
+ }
+ end
+ assert_equal("Test 1, 2, 3", @format_o.text)
+
+ # new(Hash Symbols)
+ assert_nothing_raised { @format_o = Text::Format.new(:columns => 72) }
+ assert_equal(72, @format_o.columns)
+
+ # new(Hash String)
+ assert_nothing_raised { @format_o = Text::Format.new('columns' => 72) }
+ assert_equal(72, @format_o.columns)
+
+ # new(Hash) { block }
+ assert_nothing_raised do
+ @format_o = Text::Format.new('columns' => 80) {
+ self.text = "Test 4, 5, 6"
+ }
+ end
+ assert_equal("Test 4, 5, 6", @format_o.text)
+ assert_equal(80, @format_o.columns)
+
+ # new(Text::Format)
+ assert_nothing_raised do
+ fo = Text::Format.new(@format_o)
+ assert(fo == @format_o)
+ end
+
+ # new(Text::Format) { block }
+ assert_nothing_raised do
+ fo = Text::Format.new(@format_o) { self.columns = 79 }
+ assert(fo != @format_o)
+ end
+
+ # new(String)
+ assert_nothing_raised { @format_o = Text::Format.new("Test A, B, C") }
+ assert_equal("Test A, B, C", @format_o.text)
+
+ # new(String) { block }
+ assert_nothing_raised do
+ @format_o = Text::Format.new("Test X, Y, Z") { self.columns = -5 }
+ end
+ assert_equal("Test X, Y, Z", @format_o.text)
+ assert_equal(5, @format_o.columns)
+ end
+
+ def test_center
+ assert_nothing_raised { @format_o = Text::Format.new }
+ assert_nothing_raised do
+ ct = @format_o.center(GETTYSBURG.split("\n")).split("\n")
+ assert_match(/^ Four score and seven years ago our fathers brought forth on this/, ct[0])
+ assert_match(/^ not perish from the earth./, ct[-3])
+ end
+ end
+
+ def test_expand
+ assert_nothing_raised { @format_o = Text::Format.new }
+ assert_equal(" ", @format_o.expand("\t "))
+ assert_nothing_raised { @format_o.tabstop = 4 }
+ assert_equal(" ", @format_o.expand("\t "))
+ end
+
+ def test_unexpand
+ assert_nothing_raised { @format_o = Text::Format.new }
+ assert_equal("\t ", @format_o.unexpand(" "))
+ assert_nothing_raised { @format_o.tabstop = 4 }
+ assert_equal("\t ", @format_o.unexpand(" "))
+ end
+
+ def test_space_only
+ assert_equal("", Text::Format.new.format(" "))
+ assert_equal("", Text::Format.new.format("\n"))
+ assert_equal("", Text::Format.new.format(" "))
+ assert_equal("", Text::Format.new.format(" \n"))
+ assert_equal("", Text::Format.new.paragraphs("\n"))
+ assert_equal("", Text::Format.new.paragraphs(" "))
+ assert_equal("", Text::Format.new.paragraphs(" "))
+ assert_equal("", Text::Format.new.paragraphs(" \n"))
+ assert_equal("", Text::Format.new.paragraphs(["\n"]))
+ assert_equal("", Text::Format.new.paragraphs([" "]))
+ assert_equal("", Text::Format.new.paragraphs([" "]))
+ assert_equal("", Text::Format.new.paragraphs([" \n"]))
+ end
+
+ def test_splendiferous
+ h = nil
+ test = "This is a splendiferous test"
+ assert_nothing_raised { @format_o = Text::Format.new(:columns => 6, :left_margin => 0, :indent => 0, :first_indent => 0) }
+ assert_match(/^splendiferous$/, @format_o.format(test))
+ assert_nothing_raised { @format_o.hard_margins = true }
+ assert_match(/^lendif$/, @format_o.format(test))
+ assert_nothing_raised { h = Object.new }
+ assert_nothing_raised do
+ @format_o.split_rules = Text::Format::SPLIT_HYPHENATION
+ class << h #:nodoc:
+ def hyphenate_to(word, size)
+ return ["", word] if size < 2
+ [word[0 ... size], word[size .. -1]]
+ end
+ end
+ @format_o.hyphenator = h
+ end
+ assert_match(/^iferou$/, @format_o.format(test))
+ assert_nothing_raised { h = Object.new }
+ assert_nothing_raised do
+ class << h #:nodoc:
+ def hyphenate_to(word, size, formatter)
+ return ["", word] if word.size < formatter.columns
+ [word[0 ... size], word[size .. -1]]
+ end
+ end
+ @format_o.hyphenator = h
+ end
+ assert_match(/^ferous$/, @format_o.format(test))
+ end
+ end
+end
diff --git a/actionmailer/test/mail_layout_test.rb b/actionmailer/test/mail_layout_test.rb
index 84f13a6d3c..0877e7b2cb 100644
--- a/actionmailer/test/mail_layout_test.rb
+++ b/actionmailer/test/mail_layout_test.rb
@@ -72,12 +72,12 @@ class LayoutMailerTest < Test::Unit::TestCase
mail = AutoLayoutMailer.create_multipart(@recipient)
# CHANGED: content_type returns an object
# assert_equal "multipart/alternative", mail.content_type
- assert_equal "multipart/alternative", mail.content_type.string
+ assert_equal "multipart/alternative", mail.mime_type
assert_equal 2, mail.parts.size
# CHANGED: content_type returns an object
# assert_equal 'text/plain', mail.parts.first.content_type
- assert_equal 'text/plain', mail.parts.first.content_type.string
+ assert_equal 'text/plain', mail.parts.first.mime_type
# CHANGED: body returns an object
# assert_equal "text/plain layout - text/plain multipart", mail.parts.first.body
@@ -85,7 +85,7 @@ class LayoutMailerTest < Test::Unit::TestCase
# CHANGED: content_type returns an object
# assert_equal 'text/html', mail.parts.last.content_type
- assert_equal 'text/html', mail.parts.last.content_type.string
+ assert_equal 'text/html', mail.parts.last.mime_type
# CHANGED: body returns an object
# assert_equal "Hello from layout text/html multipart", mail.parts.last.body
@@ -96,19 +96,19 @@ class LayoutMailerTest < Test::Unit::TestCase
mail = AutoLayoutMailer.create_multipart(@recipient, "multipart/mixed")
# CHANGED: content_type returns an object
# assert_equal "multipart/mixed", mail.content_type
- assert_equal "multipart/mixed", mail.content_type.string
+ assert_equal "multipart/mixed", mail.mime_type
assert_equal 2, mail.parts.size
# CHANGED: content_type returns an object
# assert_equal 'text/plain', mail.parts.first.content_type
- assert_equal 'text/plain', mail.parts.first.content_type.string
+ assert_equal 'text/plain', mail.parts.first.mime_type
# CHANGED: body returns an object
# assert_equal "text/plain layout - text/plain multipart", mail.parts.first.body
assert_equal "text/plain layout - text/plain multipart", mail.parts.first.body.to_s
# CHANGED: content_type returns an object
# assert_equal 'text/html', mail.parts.last.content_type
- assert_equal 'text/html', mail.parts.last.content_type.string
+ assert_equal 'text/html', mail.parts.last.mime_type
# CHANGED: body returns an object
# assert_equal "Hello from layout text/html multipart", mail.parts.last.body
assert_equal "Hello from layout text/html multipart", mail.parts.last.body.to_s
@@ -116,13 +116,13 @@ class LayoutMailerTest < Test::Unit::TestCase
def test_should_fix_multipart_layout
mail = AutoLayoutMailer.create_multipart(@recipient, "text/plain")
- assert_equal "multipart/alternative", mail.content_type.string
+ assert_equal "multipart/alternative", mail.mime_type
assert_equal 2, mail.parts.size
- assert_equal 'text/plain', mail.parts.first.content_type.string
+ assert_equal 'text/plain', mail.parts.first.mime_type
assert_equal "text/plain layout - text/plain multipart", mail.parts.first.body.to_s
- assert_equal 'text/html', mail.parts.last.content_type.string
+ assert_equal 'text/html', mail.parts.last.mime_type
assert_equal "Hello from layout text/html multipart", mail.parts.last.body.to_s
end
diff --git a/actionmailer/test/mail_service_test.rb b/actionmailer/test/mail_service_test.rb
index 152900259d..f66b4a174b 100644
--- a/actionmailer/test/mail_service_test.rb
+++ b/actionmailer/test/mail_service_test.rb
@@ -362,13 +362,13 @@ class ActionMailerTest < Test::Unit::TestCase
assert_equal 2, created.parts.size
assert_equal 2, created.parts.first.parts.size
- assert_equal "multipart/mixed", created.content_type.string
- assert_equal "multipart/alternative", created.parts[0].content_type.string
+ assert_equal "multipart/mixed", created.mime_type
+ assert_equal "multipart/alternative", created.parts[0].mime_type
assert_equal "bar", created.parts[0].header['foo'].to_s
assert_nil created.parts[0].charset
- assert_equal "text/plain", created.parts[0].parts[0].content_type.string
- assert_equal "text/html", created.parts[0].parts[1].content_type.string
- assert_equal "application/octet-stream", created.parts[1].content_type.string
+ assert_equal "text/plain", created.parts[0].parts[0].mime_type
+ assert_equal "text/html", created.parts[0].parts[1].mime_type
+ assert_equal "application/octet-stream", created.parts[1].mime_type
end
@@ -380,11 +380,11 @@ class ActionMailerTest < Test::Unit::TestCase
assert_equal 1,created.parts.size
assert_equal 2,created.parts.first.parts.size
- assert_equal "multipart/mixed", created.content_type.string
- assert_equal "multipart/alternative", created.parts.first.content_type.string
- assert_equal "text/plain", created.parts.first.parts.first.content_type.string
+ assert_equal "multipart/mixed", created.mime_type
+ assert_equal "multipart/alternative", created.parts.first.mime_type
+ assert_equal "text/plain", created.parts.first.parts.first.mime_type
assert_equal "Nothing to see here.", created.parts.first.parts.first.body.to_s
- assert_equal "text/html", created.parts.first.parts.second.content_type.string
+ assert_equal "text/html", created.parts.first.parts.second.mime_type
assert_equal "<b>test</b> HTML<br/>", created.parts.first.parts.second.body.to_s
end
@@ -468,8 +468,8 @@ class ActionMailerTest < Test::Unit::TestCase
assert_nothing_raised { created = TestMailer.create_custom_templating_extension(@recipient) }
assert_not_nil created
assert_equal 2, created.parts.length
- assert_equal 'text/plain', created.parts[0].content_type.string
- assert_equal 'text/html', created.parts[1].content_type.string
+ assert_equal 'text/plain', created.parts[0].mime_type
+ assert_equal 'text/html', created.parts[1].mime_type
end
def test_cancelled_account
@@ -731,8 +731,8 @@ Content-Type: text/plain; charset=iso-8859-1
The body
EOF
mail = Mail.new(msg)
- assert_equal "testing testing \326\244", mail.subject.to_s
- assert_equal "Subject: =?utf-8?Q?testing_testing_=D6=A4?=\r\n", mail.subject.encoded
+ assert_equal "testing testing \326\244", mail.subject
+ assert_equal "Subject: =?utf-8?Q?testing_testing_=D6=A4?=\r\n", mail[:subject].encoded
end
def test_unquote_7bit_subject
@@ -744,8 +744,8 @@ Content-Type: text/plain; charset=iso-8859-1
The body
EOF
mail = Mail.new(msg)
- assert_equal "this == working?", mail.subject.to_s
- assert_equal "Subject: this == working?\r\n", mail.subject.encoded
+ assert_equal "this == working?", mail.subject
+ assert_equal "Subject: this == working?\r\n", mail[:subject].encoded
end
def test_unquote_7bit_body
@@ -868,7 +868,7 @@ EOF
mail = Mail.new(fixture)
attachment = mail.attachments.last
assert_equal "smime.p7s", attachment.original_filename
- assert_equal "application/pkcs7-signature", mail.parts.last.content_type.string
+ assert_equal "application/pkcs7-signature", mail.parts.last.mime_type
end
def test_decode_attachment_without_charset
@@ -913,7 +913,7 @@ EOF
def test_multipart_with_mime_version
mail = TestMailer.create_multipart_with_mime_version(@recipient)
- assert_equal "1.1", mail.mime_version.version
+ assert_equal "1.1", mail.mime_version
end
def test_multipart_with_utf8_subject
@@ -929,29 +929,29 @@ EOF
def test_explicitly_multipart_messages
mail = TestMailer.create_explicitly_multipart_example(@recipient)
assert_equal 3, mail.parts.length
- assert_equal 'multipart/mixed', mail.content_type.string
- assert_equal "text/plain", mail.parts[0].content_type.string
+ assert_equal 'multipart/mixed', mail.mime_type
+ assert_equal "text/plain", mail.parts[0].mime_type
- assert_equal "text/html", mail.parts[1].content_type.string
+ assert_equal "text/html", mail.parts[1].mime_type
assert_equal "iso-8859-1", mail.parts[1].charset
- assert_equal "image/jpeg", mail.parts[2].content_type.string
- assert_equal "attachment", mail.parts[2].content_disposition.disposition_type
- assert_equal "foo.jpg", mail.parts[2].content_disposition.filename
- assert_equal "foo.jpg", mail.parts[2].content_type.filename
+ assert_equal "image/jpeg", mail.parts[2].mime_type
+ assert_equal "attachment", mail.parts[2][:content_disposition].disposition_type
+ assert_equal "foo.jpg", mail.parts[2][:content_disposition].filename
+ assert_equal "foo.jpg", mail.parts[2][:content_type].filename
assert_nil mail.parts[2].charset
end
def test_explicitly_multipart_with_content_type
mail = TestMailer.create_explicitly_multipart_example(@recipient, "multipart/alternative")
assert_equal 3, mail.parts.length
- assert_equal "multipart/alternative", mail.content_type.string
+ assert_equal "multipart/alternative", mail.mime_type
end
def test_explicitly_multipart_with_invalid_content_type
mail = TestMailer.create_explicitly_multipart_example(@recipient, "text/xml")
assert_equal 3, mail.parts.length
- assert_equal 'multipart/mixed', mail.content_type.string
+ assert_equal 'multipart/mixed', mail.mime_type
end
def test_implicitly_multipart_messages
@@ -960,12 +960,12 @@ EOF
mail = TestMailer.create_implicitly_multipart_example(@recipient)
assert_equal 3, mail.parts.length
assert_equal "1.0", mail.mime_version.to_s
- assert_equal "multipart/alternative", mail.content_type.string
- assert_equal "text/plain", mail.parts[0].content_type.string
+ assert_equal "multipart/alternative", mail.mime_type
+ assert_equal "text/plain", mail.parts[0].mime_type
assert_equal "utf-8", mail.parts[0].charset
- assert_equal "text/html", mail.parts[1].content_type.string
+ assert_equal "text/html", mail.parts[1].mime_type
assert_equal "utf-8", mail.parts[1].charset
- assert_equal "application/x-yaml", mail.parts[2].content_type.string
+ assert_equal "application/x-yaml", mail.parts[2].mime_type
assert_equal "utf-8", mail.parts[2].charset
end
@@ -974,9 +974,9 @@ EOF
mail = TestMailer.create_implicitly_multipart_example(@recipient, nil, ["application/x-yaml", "text/plain"])
assert_equal 3, mail.parts.length
- assert_equal "application/x-yaml", mail.parts[0].content_type.string
- assert_equal "text/plain", mail.parts[1].content_type.string
- assert_equal "text/html", mail.parts[2].content_type.string
+ assert_equal "application/x-yaml", mail.parts[0].mime_type
+ assert_equal "text/plain", mail.parts[1].mime_type
+ assert_equal "text/html", mail.parts[2].mime_type
end
def test_implicitly_multipart_messages_with_charset
@@ -984,14 +984,14 @@ EOF
assert_equal "multipart/alternative", mail.header['content-type'].content_type
- assert_equal 'iso-8859-1', mail.parts[0].content_type.parameters[:charset]
- assert_equal 'iso-8859-1', mail.parts[1].content_type.parameters[:charset]
- assert_equal 'iso-8859-1', mail.parts[2].content_type.parameters[:charset]
+ assert_equal 'iso-8859-1', mail.parts[0].content_type_parameters[:charset]
+ assert_equal 'iso-8859-1', mail.parts[1].content_type_parameters[:charset]
+ assert_equal 'iso-8859-1', mail.parts[2].content_type_parameters[:charset]
end
def test_html_mail
mail = TestMailer.create_html_mail(@recipient)
- assert_equal "text/html", mail.content_type.string
+ assert_equal "text/html", mail.mime_type
end
def test_html_mail_with_underscores
@@ -1043,7 +1043,7 @@ EOF
assert_equal(4, mail.parts.first.parts.length)
assert_equal("This is the first part.", mail.parts.first.parts.first.body.to_s)
assert_equal("test.rb", mail.parts.first.parts.second.filename)
- assert_equal("flowed", mail.parts.first.parts.fourth.content_type.parameters[:format])
+ assert_equal("flowed", mail.parts.first.parts.fourth.content_type_parameters[:format])
assert_equal('smime.p7s', mail.parts.second.filename)
end
@@ -1081,9 +1081,9 @@ EOF
assert !mail.from_addrs.empty?
assert !mail.cc_addrs.empty?
assert !mail.bcc_addrs.empty?
- assert_match(/:/, mail.from_addrs.to_s)
- assert_match(/:/, mail.cc_addrs.to_s)
- assert_match(/:/, mail.bcc_addrs.to_s)
+ assert_match(/:/, mail[:from].decoded)
+ assert_match(/:/, mail[:cc].decoded)
+ assert_match(/:/, mail[:bcc].decoded)
end
def test_deliver_with_mail_object
@@ -1095,14 +1095,14 @@ EOF
def test_multipart_with_template_path_with_dots
mail = FunkyPathMailer.create_multipart_with_template_path_with_dots(@recipient)
assert_equal 2, mail.parts.length
- assert "text/plain", mail.parts[1].content_type.string
+ assert "text/plain", mail.parts[1].mime_type
assert "utf-8", mail.parts[1].charset
end
def test_custom_content_type_attributes
mail = TestMailer.create_custom_content_type_attributes
- assert_match %r{format="flowed"}, mail.content_type.encoded
- assert_match %r{charset="utf-8"}, mail.content_type.encoded
+ assert_match %r{format=flowed}, mail.content_type
+ assert_match %r{charset=utf-8}, mail.content_type
end
def test_return_path_with_create
diff --git a/actionmailer/test/quoting_test.rb b/actionmailer/test/quoting_test.rb
index b16d160805..7640f4b086 100644
--- a/actionmailer/test/quoting_test.rb
+++ b/actionmailer/test/quoting_test.rb
@@ -78,7 +78,7 @@ class QuotingTest < Test::Unit::TestCase
mail = Mail.new(IO.read("#{File.dirname(__FILE__)}/fixtures/raw_email_with_partially_quoted_subject"))
# CHANGED: subject returns an object now
# assert_equal "Re: Test: \"\346\274\242\345\255\227\" mid \"\346\274\242\345\255\227\" tail", mail.subject
- assert_equal "Re: Test: \"\346\274\242\345\255\227\" mid \"\346\274\242\345\255\227\" tail", mail.subject.decoded
+ assert_equal "Re: Test: \"\346\274\242\345\255\227\" mid \"\346\274\242\345\255\227\" tail", mail.subject
end
private
diff --git a/actionmailer/test/test_helper_test.rb b/actionmailer/test/test_helper_test.rb
index 86d22da9bf..1fed26f78f 100644
--- a/actionmailer/test/test_helper_test.rb
+++ b/actionmailer/test/test_helper_test.rb
@@ -19,8 +19,8 @@ class TestHelperMailerTest < ActionMailer::TestCase
def test_setup_creates_the_expected_mailer
assert @expected.is_a?(Mail::Message)
- assert_equal "1.0", @expected.mime_version.version
- assert_equal "text/plain", @expected.content_type.string
+ assert_equal "1.0", @expected.mime_version
+ assert_equal "text/plain", @expected.mime_type
end
def test_mailer_class_is_correctly_inferred
diff --git a/actionmailer/test/tmail_compat_test.rb b/actionmailer/test/tmail_compat_test.rb
index faa267e3bf..a1ca6a7243 100644
--- a/actionmailer/test/tmail_compat_test.rb
+++ b/actionmailer/test/tmail_compat_test.rb
@@ -8,7 +8,7 @@ class TmailCompatTest < Test::Unit::TestCase
assert_nothing_raised do
mail.set_content_type "text/plain"
end
- assert_equal mail.content_type.string, "text/plain"
+ assert_equal mail.mime_type, "text/plain"
end
def test_transfer_encoding_raises_deprecation_warning
@@ -17,7 +17,7 @@ class TmailCompatTest < Test::Unit::TestCase
assert_nothing_raised do
mail.transfer_encoding "base64"
end
- assert_equal mail.content_transfer_encoding.value, "base64"
+ assert_equal mail.content_transfer_encoding, "base64"
end
end
diff --git a/actionpack/lib/action_controller/caching.rb b/actionpack/lib/action_controller/caching.rb
index 69ed84da95..5c0d754ad6 100644
--- a/actionpack/lib/action_controller/caching.rb
+++ b/actionpack/lib/action_controller/caching.rb
@@ -62,9 +62,9 @@ module ActionController #:nodoc:
end
def log_event(name, before, after, instrumenter_id, payload)
- if name.to_s =~ /(read|write|cache|expire|exist)_(fragment|page)\??/
+ if name.to_s =~ /action_controller\.((read|write|expire|exist)_(fragment|page)\??)/
key_or_path = payload[:key] || payload[:path]
- human_name = name.to_s.humanize
+ human_name = $1.humanize
duration = (after - before) * 1000
logger.info("#{human_name} #{key_or_path.inspect} (%.1fms)" % duration)
else
diff --git a/actionpack/lib/action_controller/caching/fragments.rb b/actionpack/lib/action_controller/caching/fragments.rb
index f569d0dd8b..a0c5ed797e 100644
--- a/actionpack/lib/action_controller/caching/fragments.rb
+++ b/actionpack/lib/action_controller/caching/fragments.rb
@@ -53,7 +53,7 @@ module ActionController #:nodoc:
return content unless cache_configured?
key = fragment_cache_key(key)
- ActiveSupport::Notifications.instrument(:write_fragment, :key => key) do
+ instrument_fragment_cache :write_fragment, key do
cache_store.write(key, content, options)
end
content
@@ -64,7 +64,7 @@ module ActionController #:nodoc:
return unless cache_configured?
key = fragment_cache_key(key)
- ActiveSupport::Notifications.instrument(:read_fragment, :key => key) do
+ instrument_fragment_cache :read_fragment, key do
cache_store.read(key, options)
end
end
@@ -74,7 +74,7 @@ module ActionController #:nodoc:
return unless cache_configured?
key = fragment_cache_key(key)
- ActiveSupport::Notifications.instrument(:exist_fragment?, :key => key) do
+ instrument_fragment_cache :exist_fragment?, key do
cache_store.exist?(key, options)
end
end
@@ -101,16 +101,18 @@ module ActionController #:nodoc:
key = fragment_cache_key(key) unless key.is_a?(Regexp)
message = nil
- ActiveSupport::Notifications.instrument(:expire_fragment, :key => key) do
+ instrument_fragment_cache :expire_fragment, key do
if key.is_a?(Regexp)
- message = "Expired fragments matching: #{key.source}"
cache_store.delete_matched(key, options)
else
- message = "Expired fragment: #{key}"
cache_store.delete(key, options)
end
end
end
+
+ def instrument_fragment_cache(name, key)
+ ActiveSupport::Notifications.instrument("action_controller.#{name}", :key => key){ yield }
+ end
end
end
end
diff --git a/actionpack/lib/action_controller/caching/pages.rb b/actionpack/lib/action_controller/caching/pages.rb
index d46f528c7e..5797eeebd6 100644
--- a/actionpack/lib/action_controller/caching/pages.rb
+++ b/actionpack/lib/action_controller/caching/pages.rb
@@ -64,7 +64,7 @@ module ActionController #:nodoc:
return unless perform_caching
path = page_cache_path(path)
- ActiveSupport::Notifications.instrument(:expire_page, :path => path) do
+ instrument_page_cache :expire_page, path do
File.delete(path) if File.exist?(path)
end
end
@@ -75,7 +75,7 @@ module ActionController #:nodoc:
return unless perform_caching
path = page_cache_path(path)
- ActiveSupport::Notifications.instrument(:cache_page, :path => path) do
+ instrument_page_cache :write_page, path do
FileUtils.makedirs(File.dirname(path))
File.open(path, "wb+") { |f| f.write(content) }
end
@@ -107,6 +107,10 @@ module ActionController #:nodoc:
def page_cache_path(path)
page_cache_directory + page_cache_file(path)
end
+
+ def instrument_page_cache(name, path)
+ ActiveSupport::Notifications.instrument("action_controller.#{name}", :path => path){ yield }
+ end
end
# Expires the page that was cached with the +options+ as a key. Example:
diff --git a/actionpack/lib/action_controller/metal/logger.rb b/actionpack/lib/action_controller/metal/logger.rb
index 4f4370e5f0..bf5f3b774f 100644
--- a/actionpack/lib/action_controller/metal/logger.rb
+++ b/actionpack/lib/action_controller/metal/logger.rb
@@ -15,7 +15,8 @@ module ActionController
attr_internal :view_runtime
def process_action(action)
- ActiveSupport::Notifications.instrument(:process_action, :controller => self, :action => action) do
+ ActiveSupport::Notifications.instrument("action_controller.process_action",
+ :controller => self, :action => action) do
super
end
end
@@ -50,7 +51,7 @@ module ActionController
# This is the hook invoked by ActiveSupport::Notifications.subscribe.
# If you need to log any event, overwrite the method and do it here.
def log_event(name, before, after, instrumenter_id, payload) #:nodoc:
- if name == :process_action
+ if name == "action_controller.process_action"
duration = [(after - before) * 1000, 0.01].max
controller = payload[:controller]
request = controller.request
@@ -66,7 +67,7 @@ module ActionController
message << " [#{request.request_uri rescue "unknown"}]"
logger.info(message)
- elsif name == :render_template
+ elsif name == "action_view.render_template"
# TODO Make render_template logging work if you are using just ActionView
duration = (after - before) * 1000
message = "Rendered #{payload[:identifier]}"
diff --git a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb
index f27f22c7e7..04a101dbb2 100644
--- a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb
+++ b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb
@@ -179,7 +179,7 @@ module ActionDispatch
'cookie containing the session data. Use ' +
'config.action_controller.session = { :key => ' +
'"_myapp_session", :secret => "some secret phrase" } in ' +
- 'config/environment.rb'
+ 'config/application.rb'
end
end
diff --git a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
index 4ebc8a2ab9..af356707c6 100644
--- a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
+++ b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
@@ -1,7 +1,24 @@
require 'active_support/core_ext/exception'
+require 'active_support/notifications'
require 'action_dispatch/http/request'
module ActionDispatch
+ # This middleware rescues any exception returned by the application and renders
+ # nice exception pages if it's being rescued locally.
+ #
+ # Every time an exception is caught, a notification is published, becoming a good API
+ # to deal with exceptions. So, if you want send an e-mail through ActionMailer
+ # everytime this notification is published, you just need to do the following:
+ #
+ # ActiveSupport::Notifications.subscribe "action_dispatch.show_exception" do |name, start, end, instrumentation_id, payload|
+ # ExceptionNotifier.deliver_exception(start, payload)
+ # end
+ #
+ # The payload is a hash which has to pairs:
+ #
+ # * :env - Contains the rack env for the given request;
+ # * :exception - The exception raised;
+ #
class ShowExceptions
LOCALHOST = '127.0.0.1'.freeze
@@ -44,8 +61,11 @@ module ActionDispatch
def call(env)
@app.call(env)
rescue Exception => exception
- raise exception if env['action_dispatch.show_exceptions'] == false
- render_exception(env, exception)
+ ActiveSupport::Notifications.instrument 'action_dispatch.show_exception',
+ :env => env, :exception => exception do
+ raise exception if env['action_dispatch.show_exceptions'] == false
+ render_exception(env, exception)
+ end
end
private
diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb
index d69e5109fa..4970c768e8 100644
--- a/actionpack/lib/action_view/base.rb
+++ b/actionpack/lib/action_view/base.rb
@@ -274,6 +274,7 @@ module ActionView #:nodoc:
end
def initialize(view_paths = [], assigns_for_first_render = {}, controller = nil, formats = nil)#:nodoc:
+ @config = nil
@formats = formats
@assigns = assigns_for_first_render.each { |key, value| instance_variable_set("@#{key}", value) }
@controller = controller
diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb
index d0c66eda60..81c9c88820 100644
--- a/actionpack/lib/action_view/helpers/form_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_helper.rb
@@ -504,8 +504,9 @@ module ActionView
end
# Returns a label tag tailored for labelling an input field for a specified attribute (identified by +method+) on an object
- # assigned to the template (identified by +object+). The text of label will default to the attribute name unless you specify
- # it explicitly. Additional options on the label tag can be passed as a hash with +options+. These options will be tagged
+ # assigned to the template (identified by +object+). The text of label will default to the attribute name unless a translation
+ # is found in the current I18n locale (through views.labels.<modelname>.<attribute>) or you specify it explicitly.
+ # Additional options on the label tag can be passed as a hash with +options+. These options will be tagged
# onto the HTML as an HTML element attribute as in the example shown, except for the <tt>:value</tt> option, which is designed to
# target labels for radio_button tags (where the value is used in the ID of the input tag).
#
@@ -513,6 +514,29 @@ module ActionView
# label(:post, :title)
# # => <label for="post_title">Title</label>
#
+ # You can localize your labels based on model and attribute names.
+ # For example you can define the following in your locale (e.g. en.yml)
+ #
+ # views:
+ # labels:
+ # post:
+ # body: "Write your entire text here"
+ #
+ # Which then will result in
+ #
+ # label(:post, :body)
+ # # => <label for="post_body">Write your entire text here</label>
+ #
+ # Localization can also be based purely on the translation of the attribute-name like this:
+ #
+ # activemodel:
+ # attribute:
+ # post:
+ # cost: "Total cost"
+ #
+ # label(:post, :cost)
+ # # => <label for="post_cost">Total cost</label>
+ #
# label(:post, :title, "A short title")
# # => <label for="post_title">A short title</label>
#
@@ -751,7 +775,19 @@ module ActionView
add_default_name_and_id_for_value(tag_value, name_and_id)
options.delete("index")
options["for"] ||= name_and_id["id"]
- content = (text.blank? ? nil : text.to_s) || method_name.humanize
+
+ content = if text.blank?
+ I18n.t("views.labels.#{object_name}.#{method_name}", :default => "").presence
+ else
+ text.to_s
+ end
+
+ content ||= if object && object.class.respond_to?(:human_attribute_name)
+ object.class.human_attribute_name(method_name)
+ end
+
+ content ||= method_name.humanize
+
label_tag(name_and_id["id"], content, options)
end
diff --git a/actionpack/lib/action_view/render/partials.rb b/actionpack/lib/action_view/render/partials.rb
index 5158415c20..67a8ee4472 100644
--- a/actionpack/lib/action_view/render/partials.rb
+++ b/actionpack/lib/action_view/render/partials.rb
@@ -215,12 +215,13 @@ module ActionView
options = @options
if @collection
- ActiveSupport::Notifications.instrument(:render_collection, :path => @path,
- :count => @collection.size) do
+ ActiveSupport::Notifications.instrument("action_view.render_collection",
+ :path => @path, :count => @collection.size) do
render_collection
end
else
- content = ActiveSupport::Notifications.instrument(:render_partial, :path => @path) do
+ content = ActiveSupport::Notifications.instrument("action_view.render_partial",
+ :path => @path) do
render_partial
end
diff --git a/actionpack/lib/action_view/render/rendering.rb b/actionpack/lib/action_view/render/rendering.rb
index 48316cac53..2fdfad694d 100644
--- a/actionpack/lib/action_view/render/rendering.rb
+++ b/actionpack/lib/action_view/render/rendering.rb
@@ -93,7 +93,7 @@ module ActionView
def _render_template(template, layout = nil, options = {})
locals = options[:locals] || {}
- content = ActiveSupport::Notifications.instrument(:render_template,
+ content = ActiveSupport::Notifications.instrument("action_view.render_template",
:identifier => template.identifier, :layout => (layout ? layout.identifier : nil)) do
template.render(self, locals)
end
@@ -109,7 +109,8 @@ module ActionView
end
def _render_layout(layout, locals, &block)
- ActiveSupport::Notifications.instrument(:render_layout, :identifier => layout.identifier) do
+ ActiveSupport::Notifications.instrument("action_view.render_layout",
+ :identifier => layout.identifier) do
layout.render(self, locals){ |*name| _layout_for(*name, &block) }
end
end
diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb
index 8c65087898..c1aebefc77 100644
--- a/actionpack/test/abstract_unit.rb
+++ b/actionpack/test/abstract_unit.rb
@@ -96,7 +96,6 @@ class ActiveSupport::TestCase
end
class MockLogger
- attr_reader :logged
attr_accessor :level
def initialize
@@ -108,6 +107,10 @@ class MockLogger
@logged << args.first
@logged << blk.call if block_given?
end
+
+ def logged
+ @logged.compact.map { |l| l.to_s.strip }
+ end
end
class ActionController::IntegrationTest < ActiveSupport::TestCase
diff --git a/actionpack/test/activerecord/controller_runtime_test.rb b/actionpack/test/activerecord/controller_runtime_test.rb
index 0f534da14b..9525dd8307 100644
--- a/actionpack/test/activerecord/controller_runtime_test.rb
+++ b/actionpack/test/activerecord/controller_runtime_test.rb
@@ -23,9 +23,11 @@ class ARLoggingTest < ActionController::TestCase
end
def test_log_with_active_record
+ # Wait pending notifications to be published
+ wait
get :show
wait
- assert_match /ActiveRecord runtime/, logs[3]
+ assert_match /ActiveRecord runtime/, @controller.logger.logged[3]
end
private
@@ -33,7 +35,4 @@ class ARLoggingTest < ActionController::TestCase
@controller.logger = MockLogger.new
end
- def logs
- @logs ||= @controller.logger.logged.compact.map {|l| l.to_s.strip}
- end
end
diff --git a/actionpack/test/controller/caching_test.rb b/actionpack/test/controller/caching_test.rb
index 679eaf7b38..5a8dc0c358 100644
--- a/actionpack/test/controller/caching_test.rb
+++ b/actionpack/test/controller/caching_test.rb
@@ -630,17 +630,17 @@ class FragmentCachingTest < ActionController::TestCase
end
def test_fragment_for_logging
- fragment_computed = false
- events = []
- ActiveSupport::Notifications.subscribe { |*args| events << args }
+ # Wait pending notifications to be published
+ ActiveSupport::Notifications.notifier.wait
+ @controller.logger = MockLogger.new
- buffer = 'generated till now -> '
- @controller.fragment_for(buffer, 'expensive') { fragment_computed = true }
+ fragment_computed = false
+ @controller.fragment_for('buffer', 'expensive') { fragment_computed = true }
+ ActiveSupport::Notifications.notifier.wait
assert fragment_computed
- assert_equal 'generated till now -> ', buffer
- ActiveSupport::Notifications.notifier.wait
- assert_equal [:exist_fragment?, :write_fragment], events.map(&:first)
+ assert_match /Exist fragment\? "views\/expensive"/, @controller.logger.logged[0]
+ assert_match /Write fragment "views\/expensive"/, @controller.logger.logged[1]
end
end
diff --git a/actionpack/test/controller/logging_test.rb b/actionpack/test/controller/logging_test.rb
index 4206dffa7e..594cf17312 100644
--- a/actionpack/test/controller/logging_test.rb
+++ b/actionpack/test/controller/logging_test.rb
@@ -19,6 +19,7 @@ class LoggingTest < ActionController::TestCase
def setup
super
+ wait # Wait pending notifications to be published
set_logger
end
@@ -75,6 +76,6 @@ class LoggingTest < ActionController::TestCase
end
def logs
- @logs ||= @controller.logger.logged.compact.map {|l| l.to_s.strip}
+ @logs ||= @controller.logger.logged
end
end
diff --git a/actionpack/test/dispatch/show_exceptions_test.rb b/actionpack/test/dispatch/show_exceptions_test.rb
index 9f6a93756c..951fb4a22e 100644
--- a/actionpack/test/dispatch/show_exceptions_test.rb
+++ b/actionpack/test/dispatch/show_exceptions_test.rb
@@ -104,4 +104,27 @@ class ShowExceptionsTest < ActionController::IntegrationTest
assert_response 405
assert_match /ActionController::MethodNotAllowed/, body
end
+
+ test "publishes notifications" do
+ # Wait pending notifications to be published
+ ActiveSupport::Notifications.notifier.wait
+
+ @app, event = ProductionApp, nil
+ self.remote_addr = '127.0.0.1'
+
+ ActiveSupport::Notifications.subscribe('action_dispatch.show_exception') do |*args|
+ event = args
+ end
+
+ get "/"
+ assert_response 500
+ assert_match /puke/, body
+
+ ActiveSupport::Notifications.notifier.wait
+
+ assert_equal 'action_dispatch.show_exception', event.first
+ assert_kind_of Hash, event.last[:env]
+ assert_equal 'GET', event.last[:env]["REQUEST_METHOD"]
+ assert_kind_of RuntimeError, event.last[:exception]
+ end
end
diff --git a/actionpack/test/lib/controller/fake_models.rb b/actionpack/test/lib/controller/fake_models.rb
index 823de8bdc7..b0e5d7a94c 100644
--- a/actionpack/test/lib/controller/fake_models.rb
+++ b/actionpack/test/lib/controller/fake_models.rb
@@ -54,6 +54,7 @@ end
class Post < Struct.new(:title, :author_name, :body, :secret, :written_on, :cost)
extend ActiveModel::Naming
include ActiveModel::Conversion
+ extend ActiveModel::Translation
alias_method :secret?, :secret
diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb
index 44734abb18..b1e9fe99a2 100644
--- a/actionpack/test/template/form_helper_test.rb
+++ b/actionpack/test/template/form_helper_test.rb
@@ -6,6 +6,25 @@ class FormHelperTest < ActionView::TestCase
def setup
super
+
+ # Create "label" locale for testing I18n label helpers
+ I18n.backend.store_translations 'label', {
+ :activemodel => {
+ :attributes => {
+ :post => {
+ :cost => "Total cost"
+ }
+ }
+ },
+ :views => {
+ :labels => {
+ :post => {
+ :body => "Write entire text here"
+ }
+ }
+ }
+ }
+
@post = Post.new
@comment = Comment.new
def @post.errors()
@@ -51,6 +70,27 @@ class FormHelperTest < ActionView::TestCase
assert_dom_equal('<label for="post_secret">Secret?</label>', label(:post, :secret?))
end
+ def test_label_with_locales_strings
+ old_locale, I18n.locale = I18n.locale, :label
+ assert_dom_equal('<label for="post_body">Write entire text here</label>', label("post", "body"))
+ ensure
+ I18n.locale = old_locale
+ end
+
+ def test_label_with_human_attribute_name
+ old_locale, I18n.locale = I18n.locale, :label
+ assert_dom_equal('<label for="post_cost">Total cost</label>', label(:post, :cost))
+ ensure
+ I18n.locale = old_locale
+ end
+
+ def test_label_with_locales_symbols
+ old_locale, I18n.locale = I18n.locale, :label
+ assert_dom_equal('<label for="post_body">Write entire text here</label>', label(:post, :body))
+ ensure
+ I18n.locale = old_locale
+ end
+
def test_label_with_for_attribute_as_symbol
assert_dom_equal('<label for="my_for">Title</label>', label(:post, :title, nil, :for => "my_for"))
end
diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb
index e8bb62953d..abc084a74b 100644
--- a/activemodel/lib/active_model/errors.rb
+++ b/activemodel/lib/active_model/errors.rb
@@ -97,18 +97,19 @@ module ActiveModel
full_messages = []
each do |attribute, messages|
- messages = Array.wrap(messages)
+ messages = Array(messages)
next if messages.empty?
if attribute == :base
messages.each {|m| full_messages << m }
else
- attr_name = @base.class.human_attribute_name(attribute)
- options = { :default => ' ', :scope => @base.class.i18n_scope }
- prefix = attr_name + I18n.t(:"errors.format.separator", options)
+ attr_name = attribute.to_s.gsub('.', '_').humanize
+ attr_name = @base.class.human_attribute_name(attribute, :default => attr_name)
+ options = { :default => "{{attribute}} {{message}}", :attribute => attr_name,
+ :scope => @base.class.i18n_scope }
messages.each do |m|
- full_messages << "#{prefix}#{m}"
+ full_messages << I18n.t(:"errors.format", options.merge(:message => m))
end
end
end
diff --git a/activemodel/lib/active_model/lint.rb b/activemodel/lib/active_model/lint.rb
index 1c2347adbf..0be82aa180 100644
--- a/activemodel/lib/active_model/lint.rb
+++ b/activemodel/lib/active_model/lint.rb
@@ -41,6 +41,20 @@ module ActiveModel
assert_boolean model.destroyed?, "destroyed?"
end
+ # naming
+ # ------
+ #
+ # Model.model_name must returns a string with some convenience methods as
+ # :human and :partial_path. Check ActiveModel::Naming for more information.
+ #
+ def test_model_naming
+ assert model.class.respond_to?(:model_name), "The model should respond to model_name"
+ model_name = model.class.model_name
+ assert_kind_of String, model_name
+ assert_kind_of String, model_name.human
+ assert_kind_of String, model_name.partial_path
+ end
+
# errors
# ------
#
diff --git a/activemodel/lib/active_model/locale/en.yml b/activemodel/lib/active_model/locale/en.yml
index 0c2cf9ea33..1cdb897f13 100644
--- a/activemodel/lib/active_model/locale/en.yml
+++ b/activemodel/lib/active_model/locale/en.yml
@@ -1,6 +1,9 @@
en:
activemodel:
errors:
+ # model.errors.full_messages format.
+ format: "{{attribute}} {{message}}"
+
# The values :model, :attribute and :value are always available for interpolation
# The value :count is available when applicable. Can be used for pluralization.
messages:
diff --git a/activemodel/lib/active_model/validator.rb b/activemodel/lib/active_model/validator.rb
index 8c9f9c7fb3..01695cb73a 100644
--- a/activemodel/lib/active_model/validator.rb
+++ b/activemodel/lib/active_model/validator.rb
@@ -72,7 +72,8 @@ module ActiveModel #:nodoc:
attr_reader :attributes
def initialize(options)
- @attributes = options.delete(:attributes)
+ @attributes = Array(options.delete(:attributes))
+ raise ":attributes cannot be blank" if @attributes.empty?
super
check_validity!
end
diff --git a/activemodel/test/cases/lint_test.rb b/activemodel/test/cases/lint_test.rb
index da7d2112dc..63804004ee 100644
--- a/activemodel/test/cases/lint_test.rb
+++ b/activemodel/test/cases/lint_test.rb
@@ -4,6 +4,8 @@ class LintTest < ActiveModel::TestCase
include ActiveModel::Lint::Tests
class CompliantModel
+ extend ActiveModel::Naming
+
def to_model
self
end
diff --git a/activemodel/test/cases/validations/i18n_validation_test.rb b/activemodel/test/cases/validations/i18n_validation_test.rb
index 2717a09331..a7656fe219 100644
--- a/activemodel/test/cases/validations/i18n_validation_test.rb
+++ b/activemodel/test/cases/validations/i18n_validation_test.rb
@@ -21,20 +21,6 @@ class I18nValidationTest < ActiveModel::TestCase
I18n.backend = @old_backend
end
- def test_percent_s_interpolation_syntax_in_error_messages_was_deprecated
- assert_not_deprecated do
- default = "%s interpolation syntax was deprecated"
- assert_equal default, I18n.t(:does_not_exist, :default => default, :value => 'this')
- end
- end
-
- def test_percent_d_interpolation_syntax_in_error_messages_was_deprecated
- assert_not_deprecated do
- default = "%d interpolation syntaxes are deprecated"
- assert_equal default, I18n.t(:does_not_exist, :default => default, :count => 2)
- end
- end
-
def test_errors_add_on_empty_generates_message
@person.errors.expects(:generate_message).with(:title, :empty, {:default => nil})
@person.errors.add_on_empty :title
@@ -57,10 +43,16 @@ class I18nValidationTest < ActiveModel::TestCase
def test_errors_full_messages_translates_human_attribute_name_for_model_attributes
@person.errors.add('name', 'empty')
- I18n.expects(:translate).with(:"person.name", :default => ['Name'], :scope => [:activemodel, :attributes], :count => 1).returns('Name')
+ I18n.expects(:translate).with(:"person.name", :default => ['Name', 'Name'], :scope => [:activemodel, :attributes], :count => 1).returns('Name')
@person.errors.full_messages
end
+ def test_errors_full_messages_uses_format
+ I18n.backend.store_translations('en', :activemodel => {:errors => {:format => "Field {{attribute}} {{message}}"}})
+ @person.errors.add('name', 'empty')
+ assert_equal ["Field Name empty"], @person.errors.full_messages
+ end
+
# ActiveRecord::Validations
# validates_confirmation_of w/ mocha
def test_validates_confirmation_of_generates_message
diff --git a/activemodel/test/cases/validations/with_validation_test.rb b/activemodel/test/cases/validations/with_validation_test.rb
index 9e9b925df2..7540ccb580 100644
--- a/activemodel/test/cases/validations/with_validation_test.rb
+++ b/activemodel/test/cases/validations/with_validation_test.rb
@@ -39,6 +39,18 @@ class ValidatesWithTest < ActiveRecord::TestCase
end
end
+ class ValidatorPerEachAttribute < ActiveModel::EachValidator
+ def validate_each(record, attribute, value)
+ record.errors[attribute] << "Value is #{value}"
+ end
+ end
+
+ class ValidatorCheckValidity < ActiveModel::EachValidator
+ def check_validity!
+ raise "boom!"
+ end
+ end
+
test "vaidation with class that adds errors" do
Topic.validates_with(ValidatorThatAddsErrors)
topic = Topic.new
@@ -116,4 +128,39 @@ class ValidatesWithTest < ActiveRecord::TestCase
assert topic.errors[:base].include?(ERROR_MESSAGE)
end
+ test "validates_with each validator" do
+ Topic.validates_with(ValidatorPerEachAttribute, :attributes => [:title, :content])
+ topic = Topic.new :title => "Title", :content => "Content"
+ assert !topic.valid?
+ assert_equal ["Value is Title"], topic.errors[:title]
+ assert_equal ["Value is Content"], topic.errors[:content]
+ end
+
+ test "each validator checks validity" do
+ assert_raise RuntimeError do
+ Topic.validates_with(ValidatorCheckValidity, :attributes => [:title])
+ end
+ end
+
+ test "each validator expects attributes to be given" do
+ assert_raise RuntimeError do
+ Topic.validates_with(ValidatorPerEachAttribute)
+ end
+ end
+
+ test "each validator skip nil values if :allow_nil is set to true" do
+ Topic.validates_with(ValidatorPerEachAttribute, :attributes => [:title, :content], :allow_nil => true)
+ topic = Topic.new :content => ""
+ assert !topic.valid?
+ assert topic.errors[:title].empty?
+ assert_equal ["Value is "], topic.errors[:content]
+ end
+
+ test "each validator skip blank values if :allow_blank is set to true" do
+ Topic.validates_with(ValidatorPerEachAttribute, :attributes => [:title, :content], :allow_blank => true)
+ topic = Topic.new :content => ""
+ assert topic.valid?
+ assert topic.errors[:title].empty?
+ assert topic.errors[:content].empty?
+ end
end
diff --git a/activemodel/test/cases/validations_test.rb b/activemodel/test/cases/validations_test.rb
index 61910395b5..38a2a716a7 100644
--- a/activemodel/test/cases/validations_test.rb
+++ b/activemodel/test/cases/validations_test.rb
@@ -71,6 +71,12 @@ class ValidationsTest < ActiveModel::TestCase
assert_equal 2, r.errors.count
end
+ def test_errors_on_nested_attributes_expands_name
+ t = Topic.new
+ t.errors["replies.name"] << "can't be blank"
+ assert_equal ["Replies name can't be blank"], t.errors.full_messages
+ end
+
def test_errors_on_base
r = Reply.new
r.content = "Mismatch"
diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG
index fc6f61f121..38bcf0c787 100644
--- a/activerecord/CHANGELOG
+++ b/activerecord/CHANGELOG
@@ -2,6 +2,11 @@
* Changed ActiveRecord::Base.store_full_sti_class to be true by default reflecting the previously announced Rails 3 default [DHH]
+* Add Relation#except. [Pratik Naik]
+
+ one_red_item = Item.where(:colour => 'red').limit(1)
+ all_items = one_red_item.except(:where, :limit)
+
* Add Relation#delete_all. [Pratik Naik]
Item.where(:colour => 'red').delete_all
diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb
index cf439b0dc0..728dec8925 100644
--- a/activerecord/lib/active_record.rb
+++ b/activerecord/lib/active_record.rb
@@ -55,6 +55,7 @@ module ActiveRecord
autoload :FinderMethods
autoload :CalculationMethods
autoload :PredicateBuilder
+ autoload :SpawnMethods
end
autoload :Base
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index 80e6acf34c..b2ae447d5e 100755
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -1465,8 +1465,7 @@ module ActiveRecord
after_destroy(method_name)
end
- def find_with_associations(options = {}, join_dependency = nil)
- join_dependency ||= JoinDependency.new(self, merge_includes(scope(:find, :include), options[:include]), options[:joins])
+ def find_with_associations(options, join_dependency)
rows = select_all_rows(options, join_dependency)
join_dependency.instantiate(rows)
rescue ThrowResult
@@ -1770,84 +1769,6 @@ module ActiveRecord
relation.to_sql
end
- def tables_in_string(string)
- return [] if string.blank?
- string.scan(/([a-zA-Z_][\.\w]+).?\./).flatten
- end
-
- def tables_in_hash(hash)
- return [] if hash.blank?
- tables = hash.map do |key, value|
- if value.is_a?(Hash)
- key.to_s
- else
- tables_in_string(key) if key.is_a?(String)
- end
- end
- tables.flatten.compact
- end
-
- def conditions_tables(options)
- # look in both sets of conditions
- conditions = [scope(:find, :conditions), options[:conditions]].inject([]) do |all, cond|
- case cond
- when nil then all
- when Array then all << tables_in_string(cond.first)
- when Hash then all << tables_in_hash(cond)
- else all << tables_in_string(cond)
- end
- end
- conditions.flatten
- end
-
- def order_tables(options)
- order = [options[:order], scope(:find, :order) ].join(", ")
- return [] unless order && order.is_a?(String)
- tables_in_string(order)
- end
-
- def selects_tables(options)
- select = options[:select]
- return [] unless select && select.is_a?(String)
- tables_in_string(select)
- end
-
- def joined_tables(options)
- scope = scope(:find)
- joins = options[:joins]
- merged_joins = scope && scope[:joins] && joins ? merge_joins(scope[:joins], joins) : (joins || scope && scope[:joins])
- [table_name] + case merged_joins
- when Symbol, Hash, Array
- if array_of_strings?(merged_joins)
- tables_in_string(merged_joins.join(' '))
- else
- join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(self, merged_joins, nil)
- join_dependency.join_associations.collect {|join_association| [join_association.aliased_join_table_name, join_association.aliased_table_name]}.flatten.compact
- end
- else
- tables_in_string(merged_joins)
- end
- end
-
- # Checks if the conditions reference a table other than the current model table
- def include_eager_conditions?(options, tables = nil, joined_tables = nil)
- ((tables || conditions_tables(options)) - (joined_tables || joined_tables(options))).any?
- end
-
- # Checks if the query order references a table other than the current model's table.
- def include_eager_order?(options, tables = nil, joined_tables = nil)
- ((tables || order_tables(options)) - (joined_tables || joined_tables(options))).any?
- end
-
- def include_eager_select?(options, joined_tables = nil)
- (selects_tables(options) - (joined_tables || joined_tables(options))).any?
- end
-
- def references_eager_loaded_tables?(options)
- joined_tables = joined_tables(options)
- include_eager_order?(options, nil, joined_tables) || include_eager_conditions?(options, nil, joined_tables) || include_eager_select?(options, joined_tables)
- end
-
def using_limitable_reflections?(reflections)
reflections.reject { |r| [ :belongs_to, :has_one ].include?(r.macro) }.length.zero?
end
diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb
index 1ceb0dbf96..358db6df1d 100644
--- a/activerecord/lib/active_record/associations/association_collection.rb
+++ b/activerecord/lib/active_record/associations/association_collection.rb
@@ -21,7 +21,7 @@ module ActiveRecord
construct_sql
end
- delegate :group, :order, :limit, :joins, :where, :preload, :eager_load, :from, :lock, :readonly, :having, :to => :scoped
+ delegate :group, :order, :limit, :joins, :where, :preload, :eager_load, :includes, :from, :lock, :readonly, :having, :to => :scoped
def select(select = nil, &block)
if block_given?
@@ -58,7 +58,7 @@ module ActiveRecord
find_scope = construct_scope[:find].slice(:conditions, :order)
with_scope(:find => find_scope) do
- relation = @reflection.klass.send(:construct_finder_arel_with_includes, options)
+ relation = @reflection.klass.send(:construct_finder_arel, options)
case args.first
when :first, :last, :all
diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb
index 44c668b619..98ab64537e 100644
--- a/activerecord/lib/active_record/autosave_association.rb
+++ b/activerecord/lib/active_record/autosave_association.rb
@@ -267,7 +267,7 @@ module ActiveRecord
unless valid = association.valid?
if reflection.options[:autosave]
association.errors.each do |attribute, message|
- attribute = "#{reflection.name}_#{attribute}"
+ attribute = "#{reflection.name}.#{attribute}"
errors[attribute] << message if errors[attribute].empty?
end
else
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index 78b92be849..026bf55aaa 100755
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -645,7 +645,7 @@ module ActiveRecord #:nodoc:
options = args.extract_options!
set_readonly_option!(options)
- relation = construct_finder_arel_with_includes(options)
+ relation = construct_finder_arel(options)
case args.first
when :first, :last, :all
@@ -655,7 +655,7 @@ module ActiveRecord #:nodoc:
end
end
- delegate :select, :group, :order, :limit, :joins, :where, :preload, :eager_load, :from, :lock, :readonly, :having, :to => :scoped
+ delegate :select, :group, :order, :limit, :joins, :where, :preload, :eager_load, :includes, :from, :lock, :readonly, :having, :to => :scoped
# A convenience wrapper for <tt>find(:first, *args)</tt>. You can pass in all the
# same arguments to this method as you can to <tt>find(:first)</tt>.
@@ -1510,11 +1510,17 @@ module ActiveRecord #:nodoc:
end
def active_relation_table(table_name_alias = nil)
- Arel::Table.new(table_name, :as => table_name_alias)
+ Arel::Table.new(table_name, :as => table_name_alias, :engine => active_relation_engine)
end
def active_relation_engine
- @active_relation_engine ||= Arel::Sql::Engine.new(self)
+ @active_relation_engine ||= begin
+ if self == ActiveRecord::Base
+ Arel::Table.engine
+ else
+ connection_handler.connection_pools[name] ? Arel::Sql::Engine.new(self) : superclass.active_relation_engine
+ end
+ end
end
private
@@ -1579,7 +1585,8 @@ module ActiveRecord #:nodoc:
order(construct_order(options[:order], scope)).
limit(construct_limit(options[:limit], scope)).
offset(construct_offset(options[:offset], scope)).
- from(options[:from])
+ from(options[:from]).
+ includes( merge_includes(scope && scope[:include], options[:include]))
lock = (scope && scope[:lock]) || options[:lock]
relation = relation.lock if lock.present?
@@ -1589,21 +1596,6 @@ module ActiveRecord #:nodoc:
relation
end
- def construct_finder_arel_with_includes(options = {})
- relation = construct_finder_arel(options)
- include_associations = merge_includes(scope(:find, :include), options[:include])
-
- if include_associations.any?
- if references_eager_loaded_tables?(options)
- relation = relation.eager_load(include_associations)
- else
- relation = relation.preload(include_associations)
- end
- end
-
- relation
- end
-
def construct_join(joins, scope)
merged_joins = scope && scope[:joins] && joins ? merge_joins(scope[:joins], joins) : (joins || scope && scope[:joins])
case merged_joins
@@ -1722,7 +1714,7 @@ module ActiveRecord #:nodoc:
super unless all_attributes_exists?(attribute_names)
if match.finder?
options = arguments.extract_options!
- relation = options.any? ? construct_finder_arel_with_includes(options) : scoped
+ relation = options.any? ? construct_finder_arel(options) : scoped
relation.send :find_by_attributes, match, attribute_names, *arguments
elsif match.instantiator?
scoped.send :find_or_instantiator_by_attributes, match, attribute_names, *arguments, &block
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
index d09aa3c4d2..5eedf448a4 100755
--- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
@@ -201,7 +201,7 @@ module ActiveRecord
protected
def log(sql, name)
result = nil
- ActiveSupport::Notifications.instrument(:sql, :sql => sql, :name => name) do
+ ActiveSupport::Notifications.instrument("active_record.sql", :sql => sql, :name => name) do
@runtime += Benchmark.ms { result = yield }
end
result
diff --git a/activerecord/lib/active_record/locale/en.yml b/activerecord/lib/active_record/locale/en.yml
index 092f5f0023..e33d389f8c 100644
--- a/activerecord/lib/active_record/locale/en.yml
+++ b/activerecord/lib/active_record/locale/en.yml
@@ -1,6 +1,9 @@
en:
activerecord:
errors:
+ # model.errors.full_messages format.
+ format: "{{attribute}} {{message}}"
+
# The values :model, :attribute and :value are always available for interpolation
# The value :count is available when applicable. Can be used for pluralization.
messages:
diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb
index 5959265d5e..f63b249241 100644
--- a/activerecord/lib/active_record/named_scope.rb
+++ b/activerecord/lib/active_record/named_scope.rb
@@ -29,7 +29,7 @@ module ActiveRecord
unless scoped?(:find)
finder_needs_type_condition? ? active_relation.where(type_condition) : active_relation.spawn
else
- construct_finder_arel_with_includes
+ construct_finder_arel
end
end
end
diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb
index 657ee738c0..a35edace19 100644
--- a/activerecord/lib/active_record/railtie.rb
+++ b/activerecord/lib/active_record/railtie.rb
@@ -62,7 +62,7 @@ module ActiveRecord
initializer "active_record.notifications" do
require 'active_support/notifications'
- ActiveSupport::Notifications.subscribe("sql") do |name, before, after, instrumenter_id, payload|
+ ActiveSupport::Notifications.subscribe("active_record.sql") do |name, before, after, instrumenter_id, payload|
ActiveRecord::Base.connection.log_info(payload[:sql], payload[:name], (after - before) * 1000)
end
end
diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb
index 8756695d46..487b54f27d 100644
--- a/activerecord/lib/active_record/relation.rb
+++ b/activerecord/lib/active_record/relation.rb
@@ -1,36 +1,32 @@
module ActiveRecord
class Relation
- include QueryMethods, FinderMethods, CalculationMethods
+ include QueryMethods, FinderMethods, CalculationMethods, SpawnMethods
- delegate :to_sql, :to => :relation
delegate :length, :collect, :map, :each, :all?, :to => :to_a
- attr_reader :relation, :klass, :preload_associations, :eager_load_associations
- attr_writer :readonly, :preload_associations, :eager_load_associations, :table
+ attr_reader :relation, :klass
+ attr_writer :readonly, :table
+ attr_accessor :preload_associations, :eager_load_associations, :includes_associations, :create_with_attributes
def initialize(klass, relation)
@klass, @relation = klass, relation
@preload_associations = []
@eager_load_associations = []
+ @includes_associations = []
@loaded, @readonly = false
end
- def merge(r)
- raise ArgumentError, "Cannot merge a #{r.klass.name} relation with #{@klass.name} relation" if r.klass != @klass
+ def new(*args, &block)
+ with_create_scope { @klass.new(*args, &block) }
+ end
- joins(r.relation.joins(r.relation)).
- group(r.send(:group_clauses).join(', ')).
- order(r.send(:order_clauses).join(', ')).
- where(r.send(:where_clause)).
- limit(r.taken).
- offset(r.skipped).
- select(r.send(:select_clauses).join(', ')).
- eager_load(r.eager_load_associations).
- preload(r.preload_associations).
- from(r.send(:sources).present? ? r.send(:from_clauses) : nil)
+ def create(*args, &block)
+ with_create_scope { @klass.create(*args, &block) }
end
- alias :& :merge
+ def create!(*args, &block)
+ with_create_scope { @klass.create!(*args, &block) }
+ end
def respond_to?(method, include_private = false)
return true if @relation.respond_to?(method, include_private) || Array.method_defined?(method)
@@ -47,19 +43,21 @@ module ActiveRecord
def to_a
return @records if loaded?
- @records = if @eager_load_associations.any?
+ find_with_associations = @eager_load_associations.any? || references_eager_loaded_tables?
+
+ @records = if find_with_associations
begin
@klass.send(:find_with_associations, {
:select => @relation.send(:select_clauses).join(', '),
:joins => @relation.joins(relation),
:group => @relation.send(:group_clauses).join(', '),
- :order => @relation.send(:order_clauses).join(', '),
+ :order => order_clause,
:conditions => where_clause,
:limit => @relation.taken,
:offset => @relation.skipped,
:from => (@relation.send(:from_clauses) if @relation.send(:sources).present?)
},
- ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, @eager_load_associations, nil))
+ ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, @eager_load_associations + @includes_associations, nil))
rescue ThrowResult
[]
end
@@ -67,7 +65,10 @@ module ActiveRecord
@klass.find_by_sql(@relation.to_sql)
end
- @preload_associations.each {|associations| @klass.send(:preload_associations, @records, associations) }
+ preload = @preload_associations
+ preload += @includes_associations unless find_with_associations
+ preload.each {|associations| @klass.send(:preload_associations, @records, associations) }
+
@records.each { |record| record.readonly! } if @readonly
@loaded = true
@@ -123,28 +124,23 @@ module ActiveRecord
end
def reset
- @first = @last = nil
+ @first = @last = @to_sql = @order_clause = @scope_for_create = nil
@records = []
self
end
- def spawn(relation = @relation)
- relation = self.class.new(@klass, relation)
- relation.readonly = @readonly
- relation.preload_associations = @preload_associations
- relation.eager_load_associations = @eager_load_associations
- relation.table = table
- relation
- end
-
def table
- @table ||= Arel::Table.new(@klass.table_name, Arel::Sql::Engine.new(@klass))
+ @table ||= Arel::Table.new(@klass.table_name, :engine => @klass.active_relation_engine)
end
def primary_key
@primary_key ||= table[@klass.primary_key]
end
+ def to_sql
+ @to_sql ||= @relation.to_sql
+ end
+
protected
def method_missing(method, *args, &block)
@@ -166,9 +162,36 @@ module ActiveRecord
end
end
+ def with_create_scope
+ @klass.send(:with_scope, :create => scope_for_create) { yield }
+ end
+
+ def scope_for_create
+ @scope_for_create ||= begin
+ @create_with_attributes || wheres.inject({}) do |hash, where|
+ hash[where.operand1.name] = where.operand2.value if where.is_a?(Arel::Predicates::Equality)
+ hash
+ end
+ end
+ end
+
def where_clause(join_string = " AND ")
@relation.send(:where_clauses).join(join_string)
end
+ def order_clause
+ @order_clause ||= @relation.send(:order_clauses).join(', ')
+ end
+
+ def references_eager_loaded_tables?
+ joined_tables = (tables_in_string(@relation.joins(relation)) + [table.name, table.table_alias]).compact.uniq
+ (tables_in_string(to_sql) - joined_tables).any?
+ end
+
+ def tables_in_string(string)
+ return [] if string.blank?
+ string.scan(/([a-zA-Z_][\.\w]+).?\./).flatten.uniq
+ end
+
end
end
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index 4600aab574..5d7bf0b7bc 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -5,6 +5,10 @@ module ActiveRecord
spawn.tap {|r| r.preload_associations += Array.wrap(associations) }
end
+ def includes(*associations)
+ spawn.tap {|r| r.includes_associations += Array.wrap(associations) }
+ end
+
def eager_load(*associations)
spawn.tap {|r| r.eager_load_associations += Array.wrap(associations) }
end
@@ -13,6 +17,10 @@ module ActiveRecord
spawn.tap {|r| r.readonly = status }
end
+ def create_with(attributes = {})
+ spawn.tap {|r| r.create_with_attributes = attributes }
+ end
+
def select(selects)
if selects.present?
relation = spawn(@relation.project(selects))
diff --git a/activerecord/lib/active_record/relation/spawn_methods.rb b/activerecord/lib/active_record/relation/spawn_methods.rb
new file mode 100644
index 0000000000..4ecee8c634
--- /dev/null
+++ b/activerecord/lib/active_record/relation/spawn_methods.rb
@@ -0,0 +1,85 @@
+module ActiveRecord
+ module SpawnMethods
+ def spawn(relation = @relation)
+ relation = Relation.new(@klass, relation)
+ relation.readonly = @readonly
+ relation.preload_associations = @preload_associations
+ relation.eager_load_associations = @eager_load_associations
+ relation.includes_associations = @includes_associations
+ relation.create_with_attributes = @create_with_attributes
+ relation.table = table
+ relation
+ end
+
+ def merge(r)
+ raise ArgumentError, "Cannot merge a #{r.klass.name} relation with #{@klass.name} relation" if r.klass != @klass
+
+ merged_relation = spawn(table).eager_load(r.eager_load_associations).preload(r.preload_associations).includes(r.includes_associations)
+ merged_relation.readonly = r.readonly
+
+ [self.relation, r.relation].each do |arel|
+ merged_relation = merged_relation.
+ joins(arel.joins(arel)).
+ group(arel.groupings).
+ limit(arel.taken).
+ offset(arel.skipped).
+ select(arel.send(:select_clauses)).
+ from(arel.sources).
+ having(arel.havings).
+ lock(arel.locked)
+ end
+
+ relation_order = r.send(:order_clause)
+ merged_order = relation_order.present? ? relation_order : order_clause
+ merged_relation = merged_relation.order(merged_order)
+
+ merged_relation.create_with_attributes = @create_with_attributes
+
+ if @create_with_attributes && r.create_with_attributes
+ merged_relation.create_with_attributes = @create_with_attributes.merge(r.create_with_attributes)
+ else
+ merged_relation.create_with_attributes = r.create_with_attributes || @create_with_attributes
+ end
+
+ merged_wheres = @relation.wheres
+
+ r.wheres.each do |w|
+ if w.is_a?(Arel::Predicates::Equality)
+ merged_wheres = merged_wheres.reject {|p| p.is_a?(Arel::Predicates::Equality) && p.operand1.name == w.operand1.name }
+ end
+
+ merged_wheres << w
+ end
+
+ merged_relation.where(*merged_wheres)
+ end
+
+ alias :& :merge
+
+ def except(*skips)
+ result = Relation.new(@klass, table)
+ result.table = table
+
+ [:eager_load, :preload, :includes].each do |load_method|
+ result = result.send(load_method, send(:"#{load_method}_associations"))
+ end
+
+ result.readonly = self.readonly unless skips.include?(:readonly)
+ result.create_with_attributes = @create_with_attributes unless skips.include?(:create_with)
+
+ result = result.joins(@relation.joins(@relation)) unless skips.include?(:joins)
+ result = result.group(@relation.groupings) unless skips.include?(:group)
+ result = result.limit(@relation.taken) unless skips.include?(:limit)
+ result = result.offset(@relation.skipped) unless skips.include?(:offset)
+ result = result.select(@relation.send(:select_clauses)) unless skips.include?(:select)
+ result = result.from(@relation.sources) unless skips.include?(:from)
+ result = result.order(order_clause) unless skips.include?(:order)
+ result = result.where(*@relation.wheres) unless skips.include?(:where)
+ result = result.having(*@relation.havings) unless skips.include?(:having)
+ result = result.lock(@relation.locked) unless skips.include?(:lock)
+
+ result
+ end
+
+ end
+end
diff --git a/activerecord/test/cases/autosave_association_test.rb b/activerecord/test/cases/autosave_association_test.rb
index 803e5b25b1..cf763d730a 100644
--- a/activerecord/test/cases/autosave_association_test.rb
+++ b/activerecord/test/cases/autosave_association_test.rb
@@ -786,14 +786,14 @@ class TestAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCase
def test_should_automatically_validate_the_associated_model
@pirate.ship.name = ''
assert @pirate.invalid?
- assert @pirate.errors[:ship_name].any?
+ assert @pirate.errors[:"ship.name"].any?
end
def test_should_merge_errors_on_the_associated_models_onto_the_parent_even_if_it_is_not_valid
@pirate.ship.name = nil
@pirate.catchphrase = nil
assert @pirate.invalid?
- assert @pirate.errors[:ship_name].any?
+ assert @pirate.errors[:"ship.name"].any?
assert @pirate.errors[:catchphrase].any?
end
@@ -886,7 +886,7 @@ class TestAutosaveAssociationOnABelongsToAssociation < ActiveRecord::TestCase
def test_should_automatically_validate_the_associated_model
@ship.pirate.catchphrase = ''
assert @ship.invalid?
- assert @ship.errors[:pirate_catchphrase].any?
+ assert @ship.errors[:"pirate.catchphrase"].any?
end
def test_should_merge_errors_on_the_associated_model_onto_the_parent_even_if_it_is_not_valid
@@ -894,7 +894,7 @@ class TestAutosaveAssociationOnABelongsToAssociation < ActiveRecord::TestCase
@ship.pirate.catchphrase = nil
assert @ship.invalid?
assert @ship.errors[:name].any?
- assert @ship.errors[:pirate_catchphrase].any?
+ assert @ship.errors[:"pirate.catchphrase"].any?
end
def test_should_still_allow_to_bypass_validations_on_the_associated_model
@@ -961,7 +961,7 @@ module AutosaveAssociationOnACollectionAssociationTests
@pirate.send(@association_name).each { |child| child.name = '' }
assert !@pirate.valid?
- assert_equal ["can't be blank"], @pirate.errors["#{@association_name}_name"]
+ assert_equal ["can't be blank"], @pirate.errors["#{@association_name}.name"]
assert @pirate.errors[@association_name].empty?
end
@@ -969,16 +969,33 @@ module AutosaveAssociationOnACollectionAssociationTests
@pirate.send(@association_name).build(:name => '')
assert !@pirate.valid?
- assert_equal ["can't be blank"], @pirate.errors["#{@association_name}_name"]
+ assert_equal ["can't be blank"], @pirate.errors["#{@association_name}.name"]
assert @pirate.errors[@association_name].empty?
end
+ def test_should_default_invalid_error_from_i18n
+ I18n.backend.store_translations(:en, :activerecord => { :errors => { :models =>
+ { @association_name.to_s.singularize.to_sym => { :blank => "cannot be blank" } }
+ }})
+
+ @pirate.send(@association_name).build(:name => '')
+
+ assert !@pirate.valid?
+ assert_equal ["cannot be blank"], @pirate.errors["#{@association_name}.name"]
+ assert_equal ["#{@association_name.to_s.titleize} name cannot be blank"], @pirate.errors.full_messages
+ assert @pirate.errors[@association_name].empty?
+ ensure
+ I18n.backend.store_translations(:en, :activerecord => { :errors => { :models =>
+ { @association_name.to_s.singularize.to_sym => nil }
+ }})
+ end
+
def test_should_merge_errors_on_the_associated_models_onto_the_parent_even_if_it_is_not_valid
@pirate.send(@association_name).each { |child| child.name = '' }
@pirate.catchphrase = nil
assert !@pirate.valid?
- assert_equal ["can't be blank"], @pirate.errors["#{@association_name}_name"]
+ assert_equal ["can't be blank"], @pirate.errors["#{@association_name}.name"]
assert @pirate.errors[:catchphrase].any?
end
diff --git a/activerecord/test/cases/multiple_db_test.rb b/activerecord/test/cases/multiple_db_test.rb
index 7c3e0f2ca6..6155bfd50a 100644
--- a/activerecord/test/cases/multiple_db_test.rb
+++ b/activerecord/test/cases/multiple_db_test.rb
@@ -1,5 +1,6 @@
require "cases/helper"
require 'models/entrant'
+require 'models/bird'
# So we can test whether Course.connection survives a reload.
require_dependency 'models/course'
@@ -82,4 +83,9 @@ class MultipleDbTest < ActiveRecord::TestCase
assert_equal "Ruby Development", Course.find(1).name
assert_equal "Ruby Developer", Entrant.find(1).name
end
+
+ def test_arel_table_engines
+ assert_not_equal Entrant.active_relation_engine, Course.active_relation_engine
+ assert_equal Entrant.active_relation_engine, Bird.active_relation_engine
+ end
end
diff --git a/activerecord/test/cases/nested_attributes_test.rb b/activerecord/test/cases/nested_attributes_test.rb
index 60c5bad225..8891282915 100644
--- a/activerecord/test/cases/nested_attributes_test.rb
+++ b/activerecord/test/cases/nested_attributes_test.rb
@@ -592,7 +592,7 @@ module NestedAttributesOnACollectionAssociationTests
assert_no_difference ['Man.count', 'Interest.count'] do
man = Man.create(:name => 'John',
:interests_attributes => [{:topic=>'Cars'}, {:topic=>'Sports'}])
- assert !man.errors[:interests_man].empty?
+ assert !man.errors[:"interests.man"].empty?
end
end
# restore :inverse_of
diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb
index 6605c8bf34..195889f1df 100644
--- a/activerecord/test/cases/relations_test.rb
+++ b/activerecord/test/cases/relations_test.rb
@@ -10,6 +10,7 @@ require 'models/comment'
require 'models/entrant'
require 'models/developer'
require 'models/company'
+require 'models/bird'
class RelationTest < ActiveRecord::TestCase
fixtures :authors, :topics, :entrants, :developers, :companies, :developers_projects, :accounts, :categories, :categorizations, :posts, :comments,
@@ -177,7 +178,7 @@ class RelationTest < ActiveRecord::TestCase
end
end
- def test_find_with_included_associations
+ def test_find_with_preloaded_associations
assert_queries(2) do
posts = Post.preload(:comments)
assert posts.first.comments.first
@@ -205,6 +206,29 @@ class RelationTest < ActiveRecord::TestCase
end
end
+ def test_find_with_included_associations
+ assert_queries(2) do
+ posts = Post.includes(:comments)
+ assert posts.first.comments.first
+ end
+
+ assert_queries(2) do
+ posts = Post.scoped.includes(:comments)
+ assert posts.first.comments.first
+ end
+
+ assert_queries(2) do
+ posts = Post.includes(:author)
+ assert posts.first.author
+ end
+
+ assert_queries(3) do
+ posts = Post.includes(:author, :comments).to_a
+ assert posts.first.author
+ assert posts.first.comments.first
+ end
+ end
+
def test_default_scope_with_conditions_string
assert_equal Developer.find_all_by_name('David').map(&:id).sort, DeveloperCalledDavid.scoped.map(&:id).sort
assert_equal nil, DeveloperCalledDavid.create!.name
@@ -383,6 +407,11 @@ class RelationTest < ActiveRecord::TestCase
end
end
+ def test_relation_merging_with_locks
+ devs = Developer.lock.where("salary >= 80000").order("id DESC") & Developer.limit(2)
+ assert devs.locked.present?
+ end
+
def test_relation_merging_with_preload
[Post.scoped & Post.preload(:author), Post.preload(:author) & Post.scoped].each do |posts|
assert_queries(2) { assert posts.first.author }
@@ -477,4 +506,62 @@ class RelationTest < ActiveRecord::TestCase
assert posts.many?
assert ! posts.limit(1).many?
end
+
+ def test_build
+ posts = Post.scoped
+
+ post = posts.new
+ assert_kind_of Post, post
+ end
+
+ def test_scoped_build
+ posts = Post.where(:title => 'You told a lie')
+
+ post = posts.new
+ assert_kind_of Post, post
+ assert_equal 'You told a lie', post.title
+ end
+
+ def test_create
+ birds = Bird.scoped
+
+ sparrow = birds.create
+ assert_kind_of Bird, sparrow
+ assert sparrow.new_record?
+
+ hen = birds.where(:name => 'hen').create
+ assert ! hen.new_record?
+ assert_equal 'hen', hen.name
+ end
+
+ def test_create_bang
+ birds = Bird.scoped
+
+ assert_raises(ActiveRecord::RecordInvalid) { birds.create! }
+
+ hen = birds.where(:name => 'hen').create!
+ assert_kind_of Bird, hen
+ assert ! hen.new_record?
+ assert_equal 'hen', hen.name
+ end
+
+ def test_explicit_create_scope
+ hens = Bird.where(:name => 'hen')
+ assert_equal 'hen', hens.new.name
+
+ hens = hens.create_with(:name => 'cock')
+ assert_equal 'cock', hens.new.name
+ end
+
+ def test_except
+ relation = Post.where(:author_id => 1).order('id ASC').limit(1)
+ assert_equal [posts(:welcome)], relation.all
+
+ author_posts = relation.except(:order, :limit)
+ assert_equal Post.where(:author_id => 1).all, author_posts.all
+
+ all_posts = relation.except(:where, :order, :limit)
+ assert_equal Post.all, all_posts.all
+ end
+
end
diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb
index ad238c1d96..6360a4614e 100644
--- a/activesupport/lib/active_support/cache.rb
+++ b/activesupport/lib/active_support/cache.rb
@@ -247,13 +247,13 @@ module ActiveSupport
expires_in || 0
end
- def instrument(operation, key, options, &block)
+ def instrument(operation, key, options)
log(operation, key, options)
if self.class.instrument
payload = { :key => key }
payload.merge!(options) if options.is_a?(Hash)
- ActiveSupport::Notifications.instrument(:"cache_#{operation}", payload, &block)
+ ActiveSupport::Notifications.instrument("active_support.cache_#{operation}", payload){ yield }
else
yield
end
diff --git a/activesupport/lib/active_support/core_ext/integer/multiple.rb b/activesupport/lib/active_support/core_ext/integer/multiple.rb
index 40bea54c67..8dff217ddc 100644
--- a/activesupport/lib/active_support/core_ext/integer/multiple.rb
+++ b/activesupport/lib/active_support/core_ext/integer/multiple.rb
@@ -1,6 +1,6 @@
class Integer
# Check whether the integer is evenly divisible by the argument.
def multiple_of?(number)
- self % number == 0
+ number != 0 ? self % number == 0 : zero?
end
end
diff --git a/activesupport/test/core_ext/integer_ext_test.rb b/activesupport/test/core_ext/integer_ext_test.rb
index e1591089f5..fe8c7eb224 100644
--- a/activesupport/test/core_ext/integer_ext_test.rb
+++ b/activesupport/test/core_ext/integer_ext_test.rb
@@ -5,6 +5,11 @@ class IntegerExtTest < Test::Unit::TestCase
def test_multiple_of
[ -7, 0, 7, 14 ].each { |i| assert i.multiple_of?(7) }
[ -7, 7, 14 ].each { |i| assert ! i.multiple_of?(6) }
+
+ # test the 0 edge case
+ assert 0.multiple_of?(0)
+ assert !5.multiple_of?(0)
+
# test with a prime
assert !22953686867719691230002707821868552601124472329079.multiple_of?(2)
assert !22953686867719691230002707821868552601124472329079.multiple_of?(3)
diff --git a/activesupport/test/isolation_test.rb b/activesupport/test/isolation_test.rb
index 39c2166016..a7af5e96f6 100644
--- a/activesupport/test/isolation_test.rb
+++ b/activesupport/test/isolation_test.rb
@@ -1,10 +1,10 @@
require 'abstract_unit'
require 'rbconfig'
-# if defined?(MiniTest) || defined?(Test::Unit::TestResultFailureSupport)
-# $stderr.puts "Isolation tests can test test-unit 1 only"
+if defined?(MiniTest) || defined?(Test::Unit::TestResultFailureSupport)
+ $stderr.puts "Isolation tests can test test-unit 1 only"
-if ENV['CHILD']
+elsif ENV['CHILD']
class ChildIsolationTest < ActiveSupport::TestCase
include ActiveSupport::Testing::Isolation
diff --git a/rack b/rack
-Subproject c6805fb93da30e0056b38e0fa6015c3d1bca587
+Subproject 1ffa95c55394c862798727ac8b203ecedda8842
diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb
index 5e68c05d7c..3fb4b723ab 100644
--- a/railties/lib/rails/application.rb
+++ b/railties/lib/rails/application.rb
@@ -8,7 +8,7 @@ module Rails
class << self
attr_writer :config
alias configure class_eval
- delegate :initialize!, :load_tasks, :to => :instance
+ delegate :initialize!, :load_tasks, :root, :to => :instance
private :new
def instance
@@ -267,18 +267,5 @@ module Rails
ActiveSupport::Dependencies.unhook!
end
end
-
- # For each framework, search for instrument file with Notifications hooks.
- #
- initializer :load_notifications_hooks do
- frameworks = [ :active_record, :action_controller, :action_view,
- :action_mailer, :active_resource ]
- frameworks.each do |framework|
- begin
- require "#{framework}/notifications"
- rescue LoadError => e
- end
- end
- end
end
end
diff --git a/railties/lib/rails/generators.rb b/railties/lib/rails/generators.rb
index 2ba56bc3c5..3713a38b33 100644
--- a/railties/lib/rails/generators.rb
+++ b/railties/lib/rails/generators.rb
@@ -10,7 +10,7 @@ require 'active_support/core_ext/module/attribute_accessors'
require 'active_support/core_ext/string/inflections'
# TODO: Do not always push on vendored thor
-$LOAD_PATH.unshift("#{File.dirname(__FILE__)}/vendor/thor-0.12.1/lib")
+$LOAD_PATH.unshift("#{File.dirname(__FILE__)}/vendor/thor-0.12.3/lib")
require 'rails/generators/base'
require 'rails/generators/named_base'
@@ -117,11 +117,15 @@ module Rails
end
# Remove the color from output.
- #
def self.no_color!
Thor::Base.shell = Thor::Shell::Basic
end
+ # Track all generators subclasses.
+ def self.subclasses
+ @subclasses ||= []
+ end
+
# Generators load paths used on lookup. The lookup happens as:
#
# 1) lib generators
@@ -147,18 +151,10 @@ module Rails
end
load_paths # Cache load paths. Needed to avoid __FILE__ pointing to wrong paths.
- # Rails finds namespaces exactly as thor, with three conveniences:
- #
- # 1) If your generator name ends with generator, as WebratGenerator, it sets
- # its namespace to "webrat", so it can be invoked as "webrat" and not
- # "webrat_generator";
+ # Rails finds namespaces similar to thor, it only adds one rule:
#
- # 2) If your generator has a generators namespace, as Rails::Generators::WebratGenerator,
- # the namespace is set to "rails:generators:webrat", but Rails allows it
- # to be invoked simply as "rails:webrat". The "generators" is added
- # automatically when doing the lookup;
- #
- # 3) Rails looks in load paths and loads the generator just before it's going to be used.
+ # Generators names must end with "_generator.rb". This is required because Rails
+ # looks in load paths and loads the generator just before it's going to be used.
#
# ==== Examples
#
@@ -166,113 +162,81 @@ module Rails
#
# Will search for the following generators:
#
- # "rails:generators:webrat", "webrat:generators:integration", "webrat"
- #
- # On the other hand, if "rails:webrat" is given, it will search for:
+ # "rails:webrat", "webrat:integration", "webrat"
#
- # "rails:generators:webrat", "rails:webrat"
- #
- # Notice that the "generators" namespace is handled automatically by Rails,
- # so you don't need to type it when you want to invoke a generator in specific.
+ # Notice that "rails:generators:webrat" could be loaded as well, what
+ # Rails looks for is the first and last parts of the namespace.
#
def self.find_by_namespace(name, base=nil, context=nil) #:nodoc:
- name, attempts = name.to_s, [ ]
-
- case name.count(':')
- when 1
- base, name = name.split(':')
- return find_by_namespace(name, base)
- when 0
- attempts += generator_names(base, name) if base
- attempts += generator_names(name, context) if context
- end
-
- attempts << name
- attempts += generator_names(name, name) unless name.include?(?:)
- attempts.uniq!
-
- unloaded = attempts - namespaces
- lookup(unloaded)
+ # Mount regexps to lookup
+ regexps = []
+ regexps << /^#{base}:[\w:]*#{name}$/ if base
+ regexps << /^#{name}:[\w:]*#{context}$/ if context
+ regexps << /^[(#{name}):]+$/
+ regexps.uniq!
+
+ # Check if generator happens to be loaded
+ checked = subclasses.dup
+ klass = find_by_regexps(regexps, checked)
+ return klass if klass
+
+ # Try to require other generators by looking in load_paths
+ lookup(name, context)
+ unchecked = subclasses - checked
+ klass = find_by_regexps(regexps, unchecked)
+ return klass if klass
+
+ # Invoke fallbacks
+ invoke_fallbacks_for(name, base) || invoke_fallbacks_for(context, name)
+ end
- attempts.each do |namespace|
- klass = Thor::Util.find_by_namespace(namespace)
- return klass if klass
+ # Tries to find a generator which the namespace match the regexp.
+ def self.find_by_regexps(regexps, klasses)
+ klasses.find do |klass|
+ namespace = klass.namespace
+ regexps.find { |r| namespace =~ r }
end
-
- invoke_fallbacks_for(name, base) || invoke_fallbacks_for(context, name)
end
# Receives a namespace, arguments and the behavior to invoke the generator.
# It's used as the default entry point for generate, destroy and update
# commands.
- #
def self.invoke(namespace, args=ARGV, config={})
- if klass = find_by_namespace(namespace, "rails")
+ names = namespace.to_s.split(':')
+
+ if klass = find_by_namespace(names.pop, names.shift || "rails")
args << "--help" if klass.arguments.any? { |a| a.required? } && args.empty?
- klass.start args, config
+ klass.start(args, config)
else
puts "Could not find generator #{namespace}."
end
end
# Show help message with available generators.
- #
def self.help
- rails = Rails::Generators.builtin.map do |group, name|
- name if group == "rails"
- end
- rails.compact!
- rails.sort!
-
- puts "Please select a generator."
- puts "Builtin: #{rails.join(', ')}."
-
- # Load paths and remove builtin
- paths, others = load_paths.dup, []
- paths.pop
-
- paths.each do |path|
- tail = [ "*", "*", "*_generator.rb" ]
-
- until tail.empty?
- others += Dir[File.join(path, *tail)].collect do |file|
- name = file.split('/')[-tail.size, 2]
- name.last.sub!(/_generator\.rb$/, '')
- name.uniq!
- name.join(':')
- end
- tail.shift
- end
- end
+ builtin = Rails::Generators.builtin.each { |n| n.sub!(/^rails:/, '') }
+ builtin.sort!
+ lookup("*")
+ others = subclasses.map{ |k| k.namespace.gsub(':generators:', ':') }
+ others -= Rails::Generators.builtin
others.sort!
+
+ puts "Please select a generator."
+ puts "Builtin: #{builtin.join(', ')}."
puts "Others: #{others.join(', ')}." unless others.empty?
end
protected
- # Return all defined namespaces.
- #
- def self.namespaces #:nodoc:
- Thor::Base.subclasses.map { |klass| klass.namespace }
- end
-
- # Keep builtin generators in an Array[Array[group, name]].
- #
+ # Keep builtin generators in an Array.
def self.builtin #:nodoc:
Dir[File.dirname(__FILE__) + '/generators/*/*'].collect do |file|
- file.split('/')[-2, 2]
+ file.split('/')[-2, 2].join(':')
end
end
- # By default, Rails strips the generator namespace to make invocations
- # easier. This method generaters the both possibilities names.
- def self.generator_names(first, second) #:nodoc:
- [ "#{first}:generators:#{second}", "#{first}:#{second}" ]
- end
-
- # Try callbacks for the given base.
- #
+ # Try fallbacks for the given base.
def self.invoke_fallbacks_for(name, base) #:nodoc:
return nil unless base && fallbacks[base.to_sym]
invoked_fallbacks = []
@@ -290,10 +254,10 @@ module Rails
# Receives namespaces in an array and tries to find matching generators
# in the load path.
- #
- def self.lookup(attempts) #:nodoc:
- attempts = attempts.map { |a| "#{a.split(":").last}_generator" }.uniq
- attempts = "{#{attempts.join(',')}}.rb"
+ def self.lookup(*attempts) #:nodoc:
+ attempts.compact!
+ attempts.uniq!
+ attempts = "{#{attempts.join(',')}}_generator.rb"
self.load_paths.each do |path|
Dir[File.join(path, '**', attempts)].each do |file|
diff --git a/railties/lib/rails/generators/base.rb b/railties/lib/rails/generators/base.rb
index 226ae63963..5e8c2730fd 100644
--- a/railties/lib/rails/generators/base.rb
+++ b/railties/lib/rails/generators/base.rb
@@ -76,17 +76,18 @@ module Rails
#
# The controller generator will then try to invoke the following generators:
#
- # "rails:generators:test_unit", "test_unit:generators:controller", "test_unit"
+ # "rails:test_unit", "test_unit:controller", "test_unit"
#
- # In this case, the "test_unit:generators:controller" is available and is
- # invoked. This allows any test framework to hook into Rails as long as it
- # provides any of the hooks above.
+ # Notice that "rails:generators:test_unit" could be loaded as well, what
+ # Rails looks for is the first and last parts of the namespace. This is what
+ # allows any test framework to hook into Rails as long as it provides any
+ # of the hooks above.
#
# ==== Options
#
- # This lookup can be customized with two options: :base and :as. The first
- # is the root module value and in the example above defaults to "rails".
- # The later defaults to the generator name, without the "Generator" ending.
+ # The first and last part used to find the generator to be invoked are
+ # guessed based on class invokes hook_for, as noticed in the example above.
+ # This can be customized with two options: :base and :as.
#
# Let's suppose you are creating a generator that needs to invoke the
# controller generator from test unit. Your first attempt is:
@@ -97,7 +98,7 @@ module Rails
#
# The lookup in this case for test_unit as input is:
#
- # "test_unit:generators:awesome", "test_unit"
+ # "test_unit:awesome", "test_unit"
#
# Which is not the desired the lookup. You can change it by providing the
# :as option:
@@ -108,18 +109,18 @@ module Rails
#
# And now it will lookup at:
#
- # "test_unit:generators:awesome", "test_unit"
+ # "test_unit:controller", "test_unit"
#
# Similarly, if you want it to also lookup in the rails namespace, you just
# need to provide the :base value:
#
# class AwesomeGenerator < Rails::Generators::Base
- # hook_for :test_framework, :base => :rails, :as => :controller
+ # hook_for :test_framework, :in => :rails, :as => :controller
# end
#
# And the lookup is exactly the same as previously:
#
- # "rails:generators:test_unit", "test_unit:generators:controller", "test_unit"
+ # "rails:test_unit", "test_unit:controller", "test_unit"
#
# ==== Switches
#
@@ -151,11 +152,11 @@ module Rails
# ==== Custom invocations
#
# You can also supply a block to hook_for to customize how the hook is
- # going to be invoked. The block receives two parameters, an instance
+ # going to be invoked. The block receives two arguments, an instance
# of the current class and the klass to be invoked.
#
# For example, in the resource generator, the controller should be invoked
- # with a pluralized class name. By default, it is invoked with the same
+ # with a pluralized class name. But by default it is invoked with the same
# name as the resource generator, which is singular. To change this, we
# can give a block to customize how the controller can be invoked.
#
@@ -178,11 +179,11 @@ module Rails
end
unless class_options.key?(name)
- class_option name, defaults.merge!(options)
+ class_option(name, defaults.merge!(options))
end
hooks[name] = [ in_base, as_hook ]
- invoke_from_option name, options, &block
+ invoke_from_option(name, options, &block)
end
end
@@ -193,7 +194,7 @@ module Rails
# remove_hook_for :orm
#
def self.remove_hook_for(*names)
- remove_invocation *names
+ remove_invocation(*names)
names.each do |name|
hooks.delete(name)
@@ -219,12 +220,16 @@ module Rails
# and can point to wrong directions when inside an specified directory.
base.source_root
- if base.name && base.name !~ /Base$/ && base.base_name && base.generator_name && defined?(Rails.root) && Rails.root
- path = File.expand_path(File.join(Rails.root, 'lib', 'templates'))
- if base.name.include?('::')
- base.source_paths << File.join(path, base.base_name, base.generator_name)
- else
- base.source_paths << File.join(path, base.generator_name)
+ if base.name && base.name !~ /Base$/
+ Rails::Generators.subclasses << base
+
+ if defined?(Rails.root) && Rails.root
+ path = File.expand_path(File.join(Rails.root, 'lib', 'templates'))
+ if base.name.include?('::')
+ base.source_paths << File.join(path, base.base_name, base.generator_name)
+ else
+ base.source_paths << File.join(path, base.generator_name)
+ end
end
end
end
@@ -267,7 +272,7 @@ module Rails
# parameters.
#
def invoked?(args)
- args.last.is_a?(Hash) && args.last.key?(:invocations)
+ args.last.is_a?(Hash) && (args.last.key?(:invocations) || args.last.key?(:destination_root))
end
# Use Rails default banner.
@@ -290,12 +295,10 @@ module Rails
# Rails::Generators::MetalGenerator will return "metal" as generator name.
#
def self.generator_name
- if name
- @generator_name ||= begin
- if klass_name = name.to_s.split('::').last
- klass_name.sub!(/Generator$/, '')
- klass_name.underscore
- end
+ @generator_name ||= begin
+ if generator = name.to_s.split('::').last
+ generator.sub!(/Generator$/, '')
+ generator.underscore
end
end
end
@@ -339,6 +342,7 @@ module Rails
#
def self.prepare_for_invocation(name, value) #:nodoc:
if value && constants = self.hooks[name]
+ value = name if TrueClass === value
Rails::Generators.find_by_namespace(value, *constants)
else
super
diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb
index ee401b1fde..fc6a3cdee8 100644
--- a/railties/lib/rails/generators/rails/app/app_generator.rb
+++ b/railties/lib/rails/generators/rails/app/app_generator.rb
@@ -46,15 +46,16 @@ module Rails::Generators
def initialize(*args)
super
- if !options[:no_activerecord] && !DATABASES.include?(options[:database])
+ if !options[:skip_activerecord] && !DATABASES.include?(options[:database])
raise Error, "Invalid value for --database option. Supported for preconfiguration are: #{DATABASES.join(", ")}."
end
end
def create_root
self.destination_root = File.expand_path(app_path, destination_root)
- empty_directory '.'
+ valid_app_const?
+ empty_directory '.'
set_default_accessors!
FileUtils.cd(destination_root)
end
@@ -193,7 +194,14 @@ module Rails::Generators
end
def app_const
- @app_const ||= "#{app_name.classify}::Application"
+ @app_const ||= "#{app_name.gsub(/\W/, '_').squeeze('_').classify}::Application"
+ end
+
+ def valid_app_const?
+ case app_const
+ when /^\d/
+ raise Error, "Invalid application name #{app_name}. Please give a name which does not start with numbers."
+ end
end
def app_secret
diff --git a/railties/lib/rails/generators/rails/app/templates/config/application.rb b/railties/lib/rails/generators/rails/app/templates/config/application.rb
index b6c1cef8cd..dce5b55d86 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/application.rb
+++ b/railties/lib/rails/generators/rails/app/templates/config/application.rb
@@ -7,7 +7,7 @@ module <%= app_name.classify %>
# -- all .rb files in that directory are automatically loaded.
# Add additional load paths for your own custom dirs
- # config.load_paths += %W( #{root}/extras )
+ # config.load_paths += %W( #{config.root}/extras )
# Only load the plugins named here, in the order given (default is alphabetical).
# :all can be used as a placeholder for all plugins not explicitly named
diff --git a/railties/lib/rails/generators/test_case.rb b/railties/lib/rails/generators/test_case.rb
new file mode 100644
index 0000000000..643d7856c5
--- /dev/null
+++ b/railties/lib/rails/generators/test_case.rb
@@ -0,0 +1,239 @@
+require 'active_support/test_case'
+require 'active_support/core_ext/class/inheritable_attributes'
+require 'active_support/core_ext/hash/reverse_merge'
+require 'rails/generators'
+require 'fileutils'
+
+module Rails
+ module Generators
+ # Disable color in output. Easier to debug.
+ no_color!
+
+ # This class provides a TestCase for testing generators. To setup, you need
+ # just to configure the destination and set which generator is being tested:
+ #
+ # class AppGeneratorTest < Rails::Generators::TestCase
+ # tests AppGenerator
+ # destination File.expand_path("../tmp", File.dirname(__FILE__))
+ # end
+ #
+ # If you want to ensure your destination root is clean before running each test,
+ # you can set a setup callback:
+ #
+ # class AppGeneratorTest < Rails::Generators::TestCase
+ # tests AppGenerator
+ # destination File.expand_path("../tmp", File.dirname(__FILE__))
+ # setup :prepare_destination
+ # end
+ #
+ class TestCase < ActiveSupport::TestCase
+ include FileUtils
+
+ extlib_inheritable_accessor :destination_root, :current_path, :generator_class,
+ :default_arguments, :instance_writer => false
+
+ # Generators frequently change the current path using +FileUtils.cd+.
+ # So we need to store the path at file load and revert back to it after each test.
+ self.current_path = File.expand_path(Dir.pwd)
+ self.default_arguments = []
+
+ setup :destination_root_is_set?, :ensure_current_path
+ teardown :ensure_current_path
+
+ # Sets which generator should be tested:
+ #
+ # tests AppGenerator
+ #
+ def self.tests(klass)
+ self.generator_class = klass
+ end
+
+ # Sets default arguments on generator invocation. This can be overwritten when
+ # invoking it.
+ #
+ # arguments %w(app_name --skip-activerecord)
+ #
+ def self.arguments(array)
+ self.default_arguments = array
+ end
+
+ # Sets the destination of generator files:
+ #
+ # destination File.expand_path("../tmp", File.dirname(__FILE__))
+ #
+ def self.destination(path)
+ self.destination_root = path
+ end
+
+ # Captures the given stream and returns it:
+ #
+ # stream = capture(:stdout){ puts "Cool" }
+ # stream #=> "Cool\n"
+ #
+ def capture(stream)
+ begin
+ stream = stream.to_s
+ eval "$#{stream} = StringIO.new"
+ yield
+ result = eval("$#{stream}").string
+ ensure
+ eval("$#{stream} = #{stream.upcase}")
+ end
+
+ result
+ end
+ alias :silence :capture
+
+ # Asserts a given file exists. You need to supply an absolute path or a path relative
+ # to the configured destination:
+ #
+ # assert_file "config/environment.rb"
+ #
+ # You can also give extra arguments. If the argument is a regexp, it will check if the
+ # regular expression matches the given file content. If it's a string, it compares the
+ # file with the given string:
+ #
+ # assert_file "config/environment.rb", /initialize/
+ #
+ # Finally, when a block is given, it yields the file content:
+ #
+ # assert_file "app/controller/products_controller.rb" do |controller|
+ # assert_instance_method :index, content do |index|
+ # assert_match /Product\.all/, index
+ # end
+ # end
+ #
+ def assert_file(relative, *contents)
+ absolute = File.expand_path(relative, destination_root)
+ assert File.exists?(absolute), "Expected file #{relative.inspect} to exist, but does not"
+
+ read = File.read(absolute) if block_given? || !contents.empty?
+ yield read if block_given?
+
+ contents.each do |content|
+ case content
+ when String
+ assert_equal content, read
+ when Regexp
+ assert_match content, read
+ end
+ end
+ end
+ alias :assert_directory :assert_file
+
+ # Asserts a given file does not exist. You need to supply an absolute path or a
+ # path relative to the configured destination:
+ #
+ # assert_no_file "config/random.rb"
+ #
+ def assert_no_file(relative)
+ absolute = File.expand_path(relative, destination_root)
+ assert !File.exists?(absolute), "Expected file #{relative.inspect} to not exist, but does"
+ end
+ alias :assert_no_directory :assert_no_file
+
+ # Asserts a given file does not exist. You need to supply an absolute path or a
+ # path relative to the configured destination:
+ #
+ # assert_migration "db/migrate/create_products.rb"
+ #
+ # This method manipulates the given path and tries to find any migration which
+ # matches the migration name. For example, the call above is converted to:
+ #
+ # assert_file "db/migrate/003_create_products.rb"
+ #
+ # Consequently, assert_migration accepts the same arguments has assert_file.
+ #
+ def assert_migration(relative, *contents, &block)
+ file_name = migration_file_name(relative)
+ assert file_name, "Expected migration #{relative} to exist, but was not found"
+ assert_file file_name, *contents, &block
+ end
+
+ # Asserts a given migration does not exist. You need to supply an absolute path or a
+ # path relative to the configured destination:
+ #
+ # assert_no_file "config/random.rb"
+ #
+ def assert_no_migration(relative)
+ file_name = migration_file_name(relative)
+ assert_nil file_name, "Expected migration #{relative} to not exist, but found #{file_name}"
+ end
+
+ # Asserts the given class method exists in the given content. This method does not detect
+ # class methods inside (class << self), only class methods which starts with "self.".
+ # When a block is given, it yields the content of the method.
+ #
+ # assert_migration "db/migrate/create_products.rb" do |migration|
+ # assert_class_method :up, migration do |up|
+ # assert_match /create_table/, up
+ # end
+ # end
+ #
+ def assert_class_method(method, content, &block)
+ assert_instance_method "self.#{method}", content, &block
+ end
+
+ # Asserts the given method exists in the given content. When a block is given,
+ # it yields the content of the method.
+ #
+ # assert_file "app/controller/products_controller.rb" do |controller|
+ # assert_instance_method :index, content do |index|
+ # assert_match /Product\.all/, index
+ # end
+ # end
+ #
+ def assert_instance_method(method, content)
+ assert content =~ /def #{method}(\(.+\))?(.*?)\n end/m, "Expected to have method #{method}"
+ yield $2.strip if block_given?
+ end
+ alias :assert_method :assert_instance_method
+
+ # Runs the generator configured for this class. The first argument is an array like
+ # command line arguments:
+ #
+ # class AppGeneratorTest < Rails::Generators::TestCase
+ # tests AppGenerator
+ # destination File.expand_path("../tmp", File.dirname(__FILE__))
+ # teardown :cleanup_destination_root
+ #
+ # test "database.yml is not created when skipping activerecord" do
+ # run_generator %w(myapp --skip-activerecord)
+ # assert_no_file "config/database.yml"
+ # end
+ # end
+ #
+ # You can provide a configuration hash as second argument. This method returns the output
+ # printed by the generator.
+ def run_generator(args=self.default_arguments, config={})
+ capture(:stdout) { self.generator_class.start(args, config.reverse_merge(:destination_root => destination_root)) }
+ end
+
+ # Instantiate the generator.
+ def generator(args=self.default_arguments, options={}, config={})
+ @generator ||= self.generator_class.new(args, options, config.reverse_merge(:destination_root => destination_root))
+ end
+
+ protected
+
+ def destination_root_is_set? #:nodoc:
+ raise "You need to configure your Rails::Generators::TestCase destination root." unless destination_root
+ end
+
+ def ensure_current_path #:nodoc:
+ cd current_path
+ end
+
+ def prepare_destination
+ rm_rf(destination_root)
+ mkdir_p(destination_root)
+ end
+
+ def migration_file_name(relative) #:nodoc:
+ absolute = File.expand_path(relative, destination_root)
+ dirname, file_name = File.dirname(absolute), File.basename(absolute).sub(/\.rb$/, '')
+ Dir.glob("#{dirname}/[0-9]*_*.rb").grep(/\d+_#{file_name}.rb$/).first
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb b/railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb
index e4bf4035da..9380aa49b6 100644
--- a/railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb
+++ b/railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb
@@ -16,7 +16,7 @@ class <%= controller_class_name %>ControllerTest < ActionController::TestCase
test "should create <%= file_name %>" do
assert_difference('<%= class_name %>.count') do
- post :create, :<%= file_name %> => { }
+ post :create, :<%= file_name %> => <%= table_name %>(:one).attributes
end
assert_redirected_to <%= file_name %>_path(assigns(:<%= file_name %>))
@@ -33,7 +33,7 @@ class <%= controller_class_name %>ControllerTest < ActionController::TestCase
end
test "should update <%= file_name %>" do
- put :update, :id => <%= table_name %>(:one).to_param, :<%= file_name %> => { }
+ put :update, :id => <%= table_name %>(:one).to_param, :<%= file_name %> => <%= table_name %>(:one).attributes
assert_redirected_to <%= file_name %>_path(assigns(:<%= file_name %>))
end
diff --git a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/version.rb b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/version.rb
deleted file mode 100644
index 650253d648..0000000000
--- a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/version.rb
+++ /dev/null
@@ -1,3 +0,0 @@
-class Thor
- VERSION = "0.12.1".freeze
-end
diff --git a/railties/lib/rails/vendor/thor-0.12.1/CHANGELOG.rdoc b/railties/lib/rails/vendor/thor-0.12.3/CHANGELOG.rdoc
index 606a0cdb52..d02fc43e84 100644
--- a/railties/lib/rails/vendor/thor-0.12.1/CHANGELOG.rdoc
+++ b/railties/lib/rails/vendor/thor-0.12.3/CHANGELOG.rdoc
@@ -1,9 +1,7 @@
-== TODO
-
-* Improve spec coverage for Thor::Runner
-
-== 0.12, released 2009-11-06
+== 0.12, released 2010-01-02
+* Removed rr in favor to rspec mock framework
+* Improved output for thor -T
* [#7] Do not force white color on status
* [#8] Yield a block with the filename on directory
@@ -17,7 +15,7 @@
* thor help now show information about any class/task. All those calls are
possible:
-
+
thor help describe
thor help describe:amazing
@@ -47,7 +45,7 @@
are in the 'standard' group. Running 'thor -T' will only show the standard
tasks - adding --all will show all tasks. You can also filter on a specific
group using the --group option: thor -T --group advanced
-
+
== 0.9.6, released 2008-09-13
* Generic improvements
diff --git a/railties/lib/rails/vendor/thor-0.12.1/LICENSE b/railties/lib/rails/vendor/thor-0.12.3/LICENSE
index 98722da459..98722da459 100644
--- a/railties/lib/rails/vendor/thor-0.12.1/LICENSE
+++ b/railties/lib/rails/vendor/thor-0.12.3/LICENSE
diff --git a/railties/lib/rails/vendor/thor-0.12.1/README.rdoc b/railties/lib/rails/vendor/thor-0.12.3/README.rdoc
index ee545f3d97..ee545f3d97 100644
--- a/railties/lib/rails/vendor/thor-0.12.1/README.rdoc
+++ b/railties/lib/rails/vendor/thor-0.12.3/README.rdoc
diff --git a/railties/lib/rails/vendor/thor-0.12.1/Thorfile b/railties/lib/rails/vendor/thor-0.12.3/Thorfile
index ff1cb4498a..ff1cb4498a 100644
--- a/railties/lib/rails/vendor/thor-0.12.1/Thorfile
+++ b/railties/lib/rails/vendor/thor-0.12.3/Thorfile
diff --git a/railties/lib/rails/vendor/thor-0.12.1/lib/thor.rb b/railties/lib/rails/vendor/thor-0.12.3/lib/thor.rb
index 68944f140d..d4d8fbd64d 100644
--- a/railties/lib/rails/vendor/thor-0.12.1/lib/thor.rb
+++ b/railties/lib/rails/vendor/thor-0.12.3/lib/thor.rb
@@ -78,14 +78,14 @@ class Thor
@method_options
end
- # Adds an option to the set of class options. If :for is given as option,
+ # Adds an option to the set of method options. If :for is given as option,
# it allows you to change the options from a previous defined task.
#
# def previous_task
# # magic
# end
#
- # method_options :foo => :bar, :for => :previous_task
+ # method_option :foo => :bar, :for => :previous_task
#
# def next_task
# # magic
@@ -101,7 +101,6 @@ class Thor
# :default - Default value for this argument. It cannot be required and have default values.
# :aliases - Aliases for this option.
# :type - The type of the argument, can be :string, :hash, :array, :numeric or :boolean.
- # :group - The group for this options. Use by class options to output options in different levels.
# :banner - String to show on usage notes.
#
def method_option(name, options={})
@@ -140,49 +139,48 @@ class Thor
end
end
- # Prints help information. If a task name is given, it shows information
- # only about the specific task.
+ # Prints help information for the given task.
#
# ==== Parameters
- # meth<String>:: An optional task name to print usage information about.
+ # shell<Thor::Shell>
+ # task_name<String>
+ #
+ def task_help(shell, task_name)
+ task = all_tasks[task_name]
+ raise UndefinedTaskError, "task '#{task_name}' could not be found in namespace '#{self.namespace}'" unless task
+
+ shell.say "Usage:"
+ shell.say " #{banner(task)}"
+ shell.say
+ class_options_help(shell, nil => task.options.map { |_, o| o })
+ shell.say task.description
+ end
+
+ # Prints help information for this class.
#
- # ==== Options
- # namespace:: When true, shows the namespace in the output before the usage.
- # skip_inherited:: When true, does not show tasks from superclass.
+ # ==== Parameters
+ # shell<Thor::Shell>
#
- def help(shell, meth=nil, options={})
- meth, options = nil, meth if meth.is_a?(Hash)
-
- if meth
- task = all_tasks[meth]
- raise UndefinedTaskError, "task '#{meth}' could not be found in namespace '#{self.namespace}'" unless task
-
- shell.say "Usage:"
- shell.say " #{banner(task, options[:namespace], false)}"
- shell.say
- class_options_help(shell, "Class", :Method => task.options.map { |_, o| o })
- shell.say task.description
- else
- list = (options[:short] ? tasks : all_tasks).map do |_, task|
- item = [ banner(task, options[:namespace]) ]
- item << "# #{task.short_description}" if task.short_description
- item << " "
- end
-
- options[:ident] ||= 2
- if options[:short]
- shell.print_list(list, :ident => options[:ident])
- else
- shell.say "Tasks:"
- shell.print_list(list, :ident => options[:ident])
- end
+ def help(shell)
+ list = printable_tasks
+ Thor::Util.thor_classes_in(self).each do |klass|
+ list += klass.printable_tasks(false)
+ end
+ list.sort!{ |a,b| a[0] <=> b[0] }
- Thor::Util.thor_classes_in(self).each do |subclass|
- namespace = options[:namespace] == true || subclass.namespace.gsub(/^#{self.namespace}:/, '')
- subclass.help(shell, options.merge(:short => true, :namespace => namespace))
- end
+ shell.say "Tasks:"
+ shell.print_table(list, :ident => 2, :truncate => true)
+ shell.say
+ class_options_help(shell)
+ end
- class_options_help(shell, "Class") unless options[:short]
+ # Returns tasks ready to be printed.
+ def printable_tasks(all=true)
+ (all ? all_tasks : tasks).map do |_, task|
+ item = []
+ item << banner(task)
+ item << (task.description ? "# #{task.description.gsub(/\s+/m,' ')}" : "")
+ item
end
end
@@ -193,8 +191,8 @@ class Thor
# the task that is going to be invoked and a boolean which indicates if
# the namespace should be displayed as arguments.
#
- def banner(task, namespace=true, show_options=true)
- task.formatted_usage(self, namespace, show_options)
+ def banner(task)
+ "thor " + task.formatted_usage(self)
end
def baseclass #:nodoc:
@@ -237,6 +235,6 @@ class Thor
desc "help [TASK]", "Describe available tasks or one specific task"
def help(task=nil)
- self.class.help(shell, task, :namespace => task && task.include?(?:))
+ task ? self.class.task_help(shell, task) : self.class.help(shell)
end
end
diff --git a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/actions.rb b/railties/lib/rails/vendor/thor-0.12.3/lib/thor/actions.rb
index 4bfb7c2870..da98444bf2 100644
--- a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/actions.rb
+++ b/railties/lib/rails/vendor/thor-0.12.3/lib/thor/actions.rb
@@ -1,4 +1,5 @@
require 'fileutils'
+require 'thor/core_ext/file_binary_read'
Dir[File.join(File.dirname(__FILE__), "actions", "*.rb")].each do |action|
require action
@@ -38,17 +39,17 @@ class Thor
# Add runtime options that help actions execution.
#
def add_runtime_options!
- class_option :pretend, :type => :boolean, :aliases => "-p", :group => :runtime,
- :desc => "Run but do not make any changes"
-
class_option :force, :type => :boolean, :aliases => "-f", :group => :runtime,
:desc => "Overwrite files that already exist"
- class_option :skip, :type => :boolean, :aliases => "-s", :group => :runtime,
- :desc => "Skip files that already exist"
+ class_option :pretend, :type => :boolean, :aliases => "-p", :group => :runtime,
+ :desc => "Run but do not make any changes"
class_option :quiet, :type => :boolean, :aliases => "-q", :group => :runtime,
:desc => "Supress status output"
+
+ class_option :skip, :type => :boolean, :aliases => "-s", :group => :runtime,
+ :desc => "Skip files that already exist"
end
end
@@ -114,7 +115,7 @@ class Thor
@source_paths ||= self.class.source_paths_for_search
end
- # Receives a file or directory and search for it in the source paths.
+ # Receives a file or directory and search for it in the source paths.
#
def find_in_source_paths(file)
relative_root = relative_to_original_destination_root(destination_root, false)
@@ -222,7 +223,7 @@ class Thor
run "#{command}", config.merge(:with => Thor::Util.ruby_command)
end
- # Run a thor command. A hash of options can be given and it's converted to
+ # Run a thor command. A hash of options can be given and it's converted to
# switches.
#
# ==== Parameters
diff --git a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/actions/create_file.rb b/railties/lib/rails/vendor/thor-0.12.3/lib/thor/actions/create_file.rb
index a3d9296823..6e0eeb43e2 100644
--- a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/actions/create_file.rb
+++ b/railties/lib/rails/vendor/thor-0.12.3/lib/thor/actions/create_file.rb
@@ -42,7 +42,7 @@ class Thor
# Boolean:: true if it is identical, false otherwise.
#
def identical?
- exists? && File.read(destination) == render
+ exists? && File.binread(destination) == render
end
# Holds the content to be added to the file.
@@ -58,7 +58,7 @@ class Thor
def invoke!
invoke_with_conflict_check do
FileUtils.mkdir_p(File.dirname(destination))
- File.open(destination, 'w'){ |f| f.write render }
+ File.open(destination, 'wb') { |f| f.write render }
end
given_destination
end
diff --git a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/actions/directory.rb b/railties/lib/rails/vendor/thor-0.12.3/lib/thor/actions/directory.rb
index 2e0b459fa3..2e0b459fa3 100644
--- a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/actions/directory.rb
+++ b/railties/lib/rails/vendor/thor-0.12.3/lib/thor/actions/directory.rb
diff --git a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/actions/empty_directory.rb b/railties/lib/rails/vendor/thor-0.12.3/lib/thor/actions/empty_directory.rb
index 484cb820f8..484cb820f8 100644
--- a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/actions/empty_directory.rb
+++ b/railties/lib/rails/vendor/thor-0.12.3/lib/thor/actions/empty_directory.rb
diff --git a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/actions/file_manipulation.rb b/railties/lib/rails/vendor/thor-0.12.3/lib/thor/actions/file_manipulation.rb
index 8a45c83f25..44d6836c10 100644
--- a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/actions/file_manipulation.rb
+++ b/railties/lib/rails/vendor/thor-0.12.3/lib/thor/actions/file_manipulation.rb
@@ -23,7 +23,7 @@ class Thor
source = File.expand_path(find_in_source_paths(source.to_s))
create_file destination, nil, config do
- content = File.read(source)
+ content = File.binread(source)
content = block.call(content) if block
content
end
@@ -48,7 +48,7 @@ class Thor
#
def get(source, destination=nil, config={}, &block)
source = File.expand_path(find_in_source_paths(source.to_s)) unless source =~ /^http\:\/\//
- render = open(source).read
+ render = File.binread(source)
destination ||= if block_given?
block.arity == 1 ? block.call(render) : block.call
@@ -80,7 +80,7 @@ class Thor
context = instance_eval('binding')
create_file destination, nil, config do
- content = ERB.new(::File.read(source), nil, '-').result(context)
+ content = ERB.new(::File.binread(source), nil, '-').result(context)
content = block.call(content) if block
content
end
@@ -193,7 +193,7 @@ class Thor
say_status :gsub, relative_to_original_destination_root(path), config.fetch(:verbose, true)
unless options[:pretend]
- content = File.read(path)
+ content = File.binread(path)
content.gsub!(flag, *args, &block)
File.open(path, 'wb') { |file| file.write(content) }
end
diff --git a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/actions/inject_into_file.rb b/railties/lib/rails/vendor/thor-0.12.3/lib/thor/actions/inject_into_file.rb
index 6b0b42ea02..350ab73862 100644
--- a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/actions/inject_into_file.rb
+++ b/railties/lib/rails/vendor/thor-0.12.3/lib/thor/actions/inject_into_file.rb
@@ -11,7 +11,7 @@ class Thor
# data<String>:: Data to add to the file. Can be given as a block.
# config<Hash>:: give :verbose => false to not log the status and the flag
# for injection (:after or :before).
- #
+ #
# ==== Examples
#
# inject_into_file "config/environment.rb", "config.gem :thor", :after => "Rails::Initializer.run do |config|\n"
@@ -90,7 +90,7 @@ class Thor
#
def replace!(regexp, string)
unless base.options[:pretend]
- content = File.read(destination)
+ content = File.binread(destination)
content.gsub!(regexp, string)
File.open(destination, 'wb') { |file| file.write(content) }
end
diff --git a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/base.rb b/railties/lib/rails/vendor/thor-0.12.3/lib/thor/base.rb
index 700d794123..aae4cdb89c 100644
--- a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/base.rb
+++ b/railties/lib/rails/vendor/thor-0.12.3/lib/thor/base.rb
@@ -92,6 +92,8 @@ class Thor
end
module ClassMethods
+ attr_accessor :debugging
+
# Adds an argument to the class and creates an attr_accessor for it.
#
# Arguments are different from options in several aspects. The first one
@@ -347,10 +349,11 @@ class Thor
# Default way to start generators from the command line.
#
def start(given_args=ARGV, config={})
+ self.debugging = given_args.include?("--debug")
config[:shell] ||= Thor::Base.shell.new
yield
rescue Thor::Error => e
- if given_args.include?("--debug")
+ if debugging
raise e
else
config[:shell].error e.message
@@ -361,48 +364,43 @@ class Thor
protected
# Prints the class options per group. If an option does not belong to
- # any group, it uses the ungrouped name value. This method provide to
- # hooks to add extra options, one of them if the third argument called
- # extra_group that should be a hash in the format :group => Array[Options].
- #
- # The second is by returning a lambda used to print values. The lambda
- # requires two options: the group name and the array of options.
+ # any group, it's printed as Class option.
#
- def class_options_help(shell, ungrouped_name=nil, extra_group=nil) #:nodoc:
- groups = {}
-
+ def class_options_help(shell, groups={}) #:nodoc:
+ # Group options by group
class_options.each do |_, value|
groups[value.group] ||= []
groups[value.group] << value
end
- printer = proc do |group_name, options|
- list = []
- padding = options.collect{ |o| o.aliases.size }.max.to_i * 4
+ # Deal with default group
+ global_options = groups.delete(nil) || []
+ print_options(shell, global_options)
+
+ # Print all others
+ groups.each do |group_name, options|
+ print_options(shell, options, group_name)
+ end
+ end
+
+ # Receives a set of options and print them.
+ def print_options(shell, options, group_name=nil)
+ return if options.empty?
- options.each do |option|
- item = [ option.usage(padding) ]
- item.push(option.description ? "# #{option.description}" : "")
+ list = []
+ padding = options.collect{ |o| o.aliases.size }.max.to_i * 4
- list << item
- list << [ "", "# Default: #{option.default}" ] if option.show_default?
- end
+ options.each do |option|
+ item = [ option.usage(padding) ]
+ item.push(option.description ? "# #{option.description}" : "")
- unless list.empty?
- shell.say(group_name ? "#{group_name} options:" : "Options:")
- shell.print_table(list, :ident => 2)
- shell.say ""
- end
+ list << item
+ list << [ "", "# Default: #{option.default}" ] if option.show_default?
end
- # Deal with default group
- global_options = groups.delete(nil) || []
- printer.call(ungrouped_name, global_options) if global_options
-
- # Print all others
- groups = extra_group.merge(groups) if extra_group
- groups.each(&printer)
- printer
+ shell.say(group_name ? "#{group_name} options:" : "Options:")
+ shell.print_table(list, :ident => 2)
+ shell.say ""
end
# Raises an error if the word given is a Thor reserved word.
diff --git a/railties/lib/rails/vendor/thor-0.12.3/lib/thor/core_ext/file_binary_read.rb b/railties/lib/rails/vendor/thor-0.12.3/lib/thor/core_ext/file_binary_read.rb
new file mode 100644
index 0000000000..d6af7e44b0
--- /dev/null
+++ b/railties/lib/rails/vendor/thor-0.12.3/lib/thor/core_ext/file_binary_read.rb
@@ -0,0 +1,9 @@
+class File #:nodoc:
+
+ unless File.respond_to?(:binread)
+ def self.binread(file)
+ File.open(file, 'rb') { |f| f.read }
+ end
+ end
+
+end
diff --git a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/core_ext/hash_with_indifferent_access.rb b/railties/lib/rails/vendor/thor-0.12.3/lib/thor/core_ext/hash_with_indifferent_access.rb
index 40d201d9e4..78bc5cf4bf 100644
--- a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/core_ext/hash_with_indifferent_access.rb
+++ b/railties/lib/rails/vendor/thor-0.12.3/lib/thor/core_ext/hash_with_indifferent_access.rb
@@ -65,7 +65,7 @@ class Thor
else
self[$1] == args.first
end
- else
+ else
self[method]
end
end
diff --git a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/core_ext/ordered_hash.rb b/railties/lib/rails/vendor/thor-0.12.3/lib/thor/core_ext/ordered_hash.rb
index 27fea5bb35..27fea5bb35 100644
--- a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/core_ext/ordered_hash.rb
+++ b/railties/lib/rails/vendor/thor-0.12.3/lib/thor/core_ext/ordered_hash.rb
diff --git a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/error.rb b/railties/lib/rails/vendor/thor-0.12.3/lib/thor/error.rb
index f9b31a35d1..f9b31a35d1 100644
--- a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/error.rb
+++ b/railties/lib/rails/vendor/thor-0.12.3/lib/thor/error.rb
diff --git a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/group.rb b/railties/lib/rails/vendor/thor-0.12.3/lib/thor/group.rb
index 021a067a3e..a585b37b73 100644
--- a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/group.rb
+++ b/railties/lib/rails/vendor/thor-0.12.3/lib/thor/group.rb
@@ -41,16 +41,12 @@ class Thor::Group
# ==== Options
# short:: When true, shows only usage.
#
- def help(shell, options={})
- if options[:short]
- shell.say banner
- else
- shell.say "Usage:"
- shell.say " #{banner}"
- shell.say
- class_options_help(shell)
- shell.say self.desc if self.desc
- end
+ def help(shell)
+ shell.say "Usage:"
+ shell.say " #{banner}\n"
+ shell.say
+ class_options_help(shell)
+ shell.say self.desc if self.desc
end
# Stores invocations for this class merging with superclass values.
@@ -132,7 +128,7 @@ class Thor::Group
names.each do |name|
unless class_options.key?(name)
- raise ArgumentError, "You have to define the option #{name.inspect} " <<
+ raise ArgumentError, "You have to define the option #{name.inspect} " <<
"before setting invoke_from_option."
end
@@ -177,15 +173,11 @@ class Thor::Group
# Overwrite class options help to allow invoked generators options to be
# shown recursively when invoking a generator.
#
- def class_options_help(shell, ungrouped_name=nil, extra_group=nil) #:nodoc:
- group_options = {}
-
- get_options_from_invocations(group_options, class_options) do |klass|
- klass.send(:get_options_from_invocations, group_options, class_options)
+ def class_options_help(shell, groups={}) #:nodoc:
+ get_options_from_invocations(groups, class_options) do |klass|
+ klass.send(:get_options_from_invocations, groups, class_options)
end
-
- group_options.merge!(extra_group) if extra_group
- super(shell, ungrouped_name, group_options)
+ super(shell, groups)
end
# Get invocations array and merge options from invocations. Those
@@ -218,13 +210,26 @@ class Thor::Group
end
end
+ # Returns tasks ready to be printed.
+ def printable_tasks(*)
+ item = []
+ item << banner
+ item << (desc ? "# #{desc.gsub(/\s+/m,' ')}" : "")
+ [item]
+ end
+
protected
# The banner for this class. You can customize it if you are invoking the
# thor class by another ways which is not the Thor::Runner.
#
def banner
- "#{self.namespace} #{self.arguments.map {|a| a.usage }.join(' ')}"
+ "thor #{self_task.formatted_usage(self, false)}"
+ end
+
+ # Represents the whole class as a task.
+ def self_task #:nodoc:
+ Thor::Task::Dynamic.new(self.namespace, class_options)
end
def baseclass #:nodoc:
@@ -243,7 +248,6 @@ class Thor::Group
# Shortcut to invoke with padding and block handling. Use internally by
# invoke and invoke_from_option class methods.
- #
def _invoke_for_class_method(klass, task=nil, *args, &block) #:nodoc:
shell.padding += 1
diff --git a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/invocation.rb b/railties/lib/rails/vendor/thor-0.12.3/lib/thor/invocation.rb
index 32e6a72454..32e6a72454 100644
--- a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/invocation.rb
+++ b/railties/lib/rails/vendor/thor-0.12.3/lib/thor/invocation.rb
diff --git a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/parser.rb b/railties/lib/rails/vendor/thor-0.12.3/lib/thor/parser.rb
index 57a3f6e1a5..57a3f6e1a5 100644
--- a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/parser.rb
+++ b/railties/lib/rails/vendor/thor-0.12.3/lib/thor/parser.rb
diff --git a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/parser/argument.rb b/railties/lib/rails/vendor/thor-0.12.3/lib/thor/parser/argument.rb
index aa8ace4719..aa8ace4719 100644
--- a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/parser/argument.rb
+++ b/railties/lib/rails/vendor/thor-0.12.3/lib/thor/parser/argument.rb
diff --git a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/parser/arguments.rb b/railties/lib/rails/vendor/thor-0.12.3/lib/thor/parser/arguments.rb
index fb5d965e06..fb5d965e06 100644
--- a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/parser/arguments.rb
+++ b/railties/lib/rails/vendor/thor-0.12.3/lib/thor/parser/arguments.rb
diff --git a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/parser/option.rb b/railties/lib/rails/vendor/thor-0.12.3/lib/thor/parser/option.rb
index e09b4901e2..9e40ec73fa 100644
--- a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/parser/option.rb
+++ b/railties/lib/rails/vendor/thor-0.12.3/lib/thor/parser/option.rb
@@ -36,7 +36,7 @@ class Thor
# string (--foo=value) or booleans (just --foo).
#
# By default all options are optional, unless :required is given.
- #
+ #
def self.parse(key, value)
if key.is_a?(Array)
name, *aliases = key
diff --git a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/parser/options.rb b/railties/lib/rails/vendor/thor-0.12.3/lib/thor/parser/options.rb
index 75092308b5..75092308b5 100644
--- a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/parser/options.rb
+++ b/railties/lib/rails/vendor/thor-0.12.3/lib/thor/parser/options.rb
diff --git a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/rake_compat.rb b/railties/lib/rails/vendor/thor-0.12.3/lib/thor/rake_compat.rb
index 0d0757fdda..0d0757fdda 100644
--- a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/rake_compat.rb
+++ b/railties/lib/rails/vendor/thor-0.12.3/lib/thor/rake_compat.rb
diff --git a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/runner.rb b/railties/lib/rails/vendor/thor-0.12.3/lib/thor/runner.rb
index 079f9e0c65..f197081e3f 100644
--- a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/runner.rb
+++ b/railties/lib/rails/vendor/thor-0.12.3/lib/thor/runner.rb
@@ -36,7 +36,7 @@ class Thor::Runner < Thor #:nodoc:
def install(name)
initialize_thorfiles
- # If a directory name is provided as the argument, look for a 'main.thor'
+ # If a directory name is provided as the argument, look for a 'main.thor'
# task in said directory.
begin
if File.directory?(File.expand_path(name))
@@ -124,11 +124,7 @@ class Thor::Runner < Thor #:nodoc:
method_options :internal => :boolean
def installed
initialize_thorfiles(nil, true)
-
- klasses = Thor::Base.subclasses
- klasses -= [Thor, Thor::Runner] unless options["internal"]
-
- display_klasses(true, klasses)
+ display_klasses(true, options["internal"])
end
desc "list [SEARCH]", "List the available thor tasks (--substring means .*SEARCH)"
@@ -144,11 +140,15 @@ class Thor::Runner < Thor #:nodoc:
(options[:all] || k.group == group) && k.namespace =~ search
end
- display_klasses(false, klasses)
+ display_klasses(false, false, klasses)
end
private
+ def self.banner(task)
+ "thor " + task.formatted_usage(self, false)
+ end
+
def thor_root
Thor::Util.thor_root
end
@@ -156,7 +156,7 @@ class Thor::Runner < Thor #:nodoc:
def thor_yaml
@thor_yaml ||= begin
yaml_file = File.join(thor_root, "thor.yml")
- yaml = YAML.load_file(yaml_file) if File.exists?(yaml_file)
+ yaml = YAML.load_file(yaml_file) if File.exists?(yaml_file)
yaml || {}
end
end
@@ -215,9 +215,6 @@ class Thor::Runner < Thor #:nodoc:
# 5. c:\ <-- no Thorfiles found!
#
def thorfiles(relevant_to=nil, skip_lookup=false)
- # TODO Remove this dealing with deprecated thor when :namespaces: is available as constants
- save_yaml(thor_yaml) if Thor::Util.convert_constants_to_namespaces(thor_yaml)
-
thorfiles = []
unless skip_lookup
@@ -253,47 +250,54 @@ class Thor::Runner < Thor #:nodoc:
# Display information about the given klasses. If with_module is given,
# it shows a table with information extracted from the yaml file.
#
- def display_klasses(with_modules=false, klasses=Thor.subclasses)
- klasses -= [Thor, Thor::Runner] unless with_modules
+ def display_klasses(with_modules=false, show_internal=false, klasses=Thor::Base.subclasses)
+ klasses -= [Thor, Thor::Runner, Thor::Group] unless show_internal
+
raise Error, "No Thor tasks available" if klasses.empty?
+ show_modules if with_modules && !thor_yaml.empty?
- if with_modules && !thor_yaml.empty?
- info = []
- labels = ["Modules", "Namespaces"]
+ # Remove subclasses
+ klasses.dup.each do |klass|
+ klasses -= Thor::Util.thor_classes_in(klass)
+ end
- info << labels
- info << [ "-" * labels[0].size, "-" * labels[1].size ]
+ list = Hash.new { |h,k| h[k] = [] }
+ groups = klasses.select { |k| k.ancestors.include?(Thor::Group) }
- thor_yaml.each do |name, hash|
- info << [ name, hash[:namespaces].join(", ") ]
- end
+ # Get classes which inherit from Thor
+ (klasses - groups).each { |k| list[k.namespace] += k.printable_tasks(false) }
- print_table info
- say ""
- end
+ # Get classes which inherit from Thor::Base
+ groups.map! { |k| k.printable_tasks(false).first }
+ list["root"] = groups
- unless klasses.empty?
- klasses.dup.each do |klass|
- klasses -= Thor::Util.thor_classes_in(klass)
- end
+ # Order namespaces with default coming first
+ list = list.sort{ |a,b| a[0].sub(/^default/, '') <=> b[0].sub(/^default/, '') }
+ list.each { |n, tasks| display_tasks(n, tasks) unless tasks.empty? }
+ end
- klasses.each { |k| display_tasks(k) }
- else
- say "\033[1;34mNo Thor tasks available\033[0m"
- end
+ def display_tasks(namespace, list) #:nodoc:
+ list.sort!{ |a,b| a[0] <=> b[0] }
+
+ say shell.set_color(namespace, :blue, true)
+ say "-" * namespace.size
+
+ print_table(list, :truncate => true)
+ say
end
- # Display tasks from the given Thor class.
- #
- def display_tasks(klass)
- unless klass.tasks.empty?
- base = klass.namespace
+ def show_modules #:nodoc:
+ info = []
+ labels = ["Modules", "Namespaces"]
- color = base == "default" ? :magenta : :blue
- say shell.set_color(base, color, true)
- say "-" * base.length
+ info << labels
+ info << [ "-" * labels[0].size, "-" * labels[1].size ]
- klass.help(shell, :short => true, :ident => 0, :namespace => true)
+ thor_yaml.each do |name, hash|
+ info << [ name, hash[:namespaces].join(", ") ]
end
+
+ print_table info
+ say ""
end
end
diff --git a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/shell.rb b/railties/lib/rails/vendor/thor-0.12.3/lib/thor/shell.rb
index 1dc8f0e5b4..64a173de83 100644
--- a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/shell.rb
+++ b/railties/lib/rails/vendor/thor-0.12.3/lib/thor/shell.rb
@@ -22,7 +22,7 @@ class Thor
end
module Shell
- SHELL_DELEGATED_METHODS = [:ask, :yes?, :no?, :say, :say_status, :print_list, :print_table]
+ SHELL_DELEGATED_METHODS = [:ask, :yes?, :no?, :say, :say_status, :print_table]
# Add shell to initialize config values.
#
diff --git a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/shell/basic.rb b/railties/lib/rails/vendor/thor-0.12.3/lib/thor/shell/basic.rb
index f6be3575ca..a11f45b4e9 100644
--- a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/shell/basic.rb
+++ b/railties/lib/rails/vendor/thor-0.12.3/lib/thor/shell/basic.rb
@@ -75,30 +75,6 @@ class Thor
!yes?(statement, color)
end
- # Prints a list of items.
- #
- # ==== Parameters
- # list<Array[String, String, ...]>
- #
- # ==== Options
- # mode:: Can be :rows or :inline. Defaults to :rows.
- # ident:: Ident each item with the value given.
- #
- def print_list(list, options={})
- return if list.empty?
-
- ident = " " * (options[:ident] || 0)
- content = case options[:mode]
- when :inline
- last = list.pop
- "#{list.join(", ")}, and #{last}"
- else # rows
- ident + list.join("\n#{ident}")
- end
-
- $stdout.puts content
- end
-
# Prints a table.
#
# ==== Parameters
@@ -110,20 +86,26 @@ class Thor
def print_table(table, options={})
return if table.empty?
- formats = []
+ formats, ident = [], options[:ident].to_i
+ options[:truncate] = terminal_width if options[:truncate] == true
+
0.upto(table.first.length - 2) do |i|
maxima = table.max{ |a,b| a[i].size <=> b[i].size }[i].size
formats << "%-#{maxima + 2}s"
end
- formats[0] = formats[0].insert(0, " " * options[:ident]) if options[:ident]
+ formats[0] = formats[0].insert(0, " " * ident)
formats << "%s"
table.each do |row|
+ sentence = ""
+
row.each_with_index do |column, i|
- $stdout.print formats[i] % column.to_s
+ sentence << formats[i] % column.to_s
end
- $stdout.puts
+
+ sentence = truncate(sentence, options[:truncate]) if options[:truncate]
+ $stdout.puts sentence
end
end
@@ -214,6 +196,44 @@ HELP
base && base.options[:quiet]
end
+ # This code was copied from Rake, available under MIT-LICENSE
+ # Copyright (c) 2003, 2004 Jim Weirich
+ def terminal_width
+ if ENV['THOR_COLUMNS']
+ result = ENV['THOR_COLUMNS'].to_i
+ else
+ result = unix? ? dynamic_width : 80
+ end
+ (result < 10) ? 80 : result
+ rescue
+ 80
+ end
+
+ # Calculate the dynamic width of the terminal
+ def dynamic_width
+ @dynamic_width ||= (dynamic_width_stty.nonzero? || dynamic_width_tput)
+ end
+
+ def dynamic_width_stty
+ %x{stty size 2>/dev/null}.split[1].to_i
+ end
+
+ def dynamic_width_tput
+ %x{tput cols 2>/dev/null}.to_i
+ end
+
+ def unix?
+ RUBY_PLATFORM =~ /(aix|darwin|linux|(net|free|open)bsd|cygwin|solaris|irix|hpux)/i
+ end
+
+ def truncate(string, width)
+ if string.length <= width
+ string
+ else
+ ( string[0, width-3] || "" ) + "..."
+ end
+ end
+
end
end
end
diff --git a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/shell/color.rb b/railties/lib/rails/vendor/thor-0.12.3/lib/thor/shell/color.rb
index 24704f7885..b2bc66dfba 100644
--- a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/shell/color.rb
+++ b/railties/lib/rails/vendor/thor-0.12.3/lib/thor/shell/color.rb
@@ -63,7 +63,7 @@ class Thor
#
def show_diff(destination, content) #:nodoc:
if diff_lcs_loaded? && ENV['THOR_DIFF'].nil? && ENV['RAILS_DIFF'].nil?
- actual = File.read(destination).to_s.split("\n")
+ actual = File.binread(destination).to_s.split("\n")
content = content.to_s.split("\n")
Diff::LCS.sdiff(actual, content).each do |diff|
diff --git a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/task.rb b/railties/lib/rails/vendor/thor-0.12.3/lib/thor/task.rb
index 91c7564d3f..5c8877591b 100644
--- a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/task.rb
+++ b/railties/lib/rails/vendor/thor-0.12.3/lib/thor/task.rb
@@ -1,11 +1,11 @@
class Thor
class Task < Struct.new(:name, :description, :usage, :options)
+ FILE_REGEXP = /^#{Regexp.escape(File.expand_path(__FILE__))}:[\w:]+ `run'$/
# A dynamic task that handles method missing scenarios.
- #
class Dynamic < Task
- def initialize(name)
- super(name.to_s, "A dynamically-generated task", name.to_s)
+ def initialize(name, options=nil)
+ super(name.to_s, "A dynamically-generated task", name.to_s, options)
end
def run(instance, args=[])
@@ -25,84 +25,73 @@ class Thor
self.options = other.options.dup if other.options
end
- def short_description
- description.split("\n").first if description
- end
-
# By default, a task invokes a method in the thor class. You can change this
# implementation to create custom tasks.
- #
def run(instance, args=[])
raise UndefinedTaskError, "the '#{name}' task of #{instance.class} is private" unless public_method?(instance)
instance.send(name, *args)
rescue ArgumentError => e
+ raise e if instance.class.respond_to?(:debugging) && instance.class.debugging
parse_argument_error(instance, e, caller)
rescue NoMethodError => e
+ raise e if instance.class.respond_to?(:debugging) && instance.class.debugging
parse_no_method_error(instance, e)
end
- # Returns the formatted usage. If a class is given, the class arguments are
- # injected in the usage.
- #
- def formatted_usage(klass=nil, namespace=false, show_options=true)
- formatted = if namespace.is_a?(String)
- "#{namespace}:"
- elsif klass && namespace
- "#{klass.namespace.gsub(/^default/,'')}:"
+ # Returns the formatted usage by injecting given required arguments
+ # and required options into the given usage.
+ def formatted_usage(klass, namespace=nil)
+ namespace = klass.namespace if namespace.nil?
+
+ # Add namespace
+ formatted = if namespace
+ "#{namespace.gsub(/^(default|thor:runner:)/,'')}:"
else
""
end
- formatted << formatted_arguments(klass)
- formatted << " #{formatted_options}" if show_options
- formatted.strip!
- formatted
- end
-
- # Injects the class arguments into the task usage.
- #
- def formatted_arguments(klass)
- if klass && !klass.arguments.empty?
+ # Add usage with required arguments
+ formatted << if klass && !klass.arguments.empty?
usage.to_s.gsub(/^#{name}/) do |match|
- match << " " << klass.arguments.map{ |a| a.usage }.join(' ')
+ match << " " << klass.arguments.map{ |a| a.usage }.compact.join(' ')
end
else
usage.to_s
end
- end
- # Returns the options usage for this task.
- #
- def formatted_options
- @formatted_options ||= options.map{ |_, o| o.usage }.sort.join(" ")
+ # Add required options
+ formatted << " #{required_options}"
+
+ # Strip and go!
+ formatted.strip
end
protected
+ def required_options
+ @required_options ||= options.map{ |_, o| o.usage if o.required? }.compact.sort.join(" ")
+ end
+
# Given a target, checks if this class name is not a private/protected method.
- #
def public_method?(instance) #:nodoc:
collection = instance.private_methods + instance.protected_methods
(collection & [name.to_s, name.to_sym]).empty?
end
- # Clean everything that comes from the Thor gempath and remove the caller.
- #
- def sans_backtrace(backtrace, caller) #:nodoc:
- dirname = /^#{Regexp.escape(File.dirname(__FILE__))}/
- saned = backtrace.reject { |frame| frame =~ dirname }
- saned -= caller
+ # For Ruby <= 1.8.7, we have to match the method name that we are trying to call.
+ # In Ruby >= 1.9.1, we have to match the method run in this file.
+ def backtrace_match?(backtrace) #:nodoc:
+ method_name = /`#{Regexp.escape(name.split(':').last)}'/
+ backtrace =~ method_name || backtrace =~ FILE_REGEXP
end
def parse_argument_error(instance, e, caller) #:nodoc:
- backtrace = sans_backtrace(e.backtrace, caller)
-
- if backtrace.empty? && e.message =~ /wrong number of arguments/
+ if e.message =~ /wrong number of arguments/ && backtrace_match?(e.backtrace.first.to_s)
if instance.is_a?(Thor::Group)
raise e, "'#{name}' was called incorrectly. Are you sure it has arity equals to 0?"
else
raise InvocationError, "'#{name}' was called incorrectly. Call as " <<
- "'#{formatted_usage(instance.class, true)}'"
+ "'#{formatted_usage(instance.class)}'"
end
else
raise e
diff --git a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/util.rb b/railties/lib/rails/vendor/thor-0.12.3/lib/thor/util.rb
index ebae0a3193..c2aed89ccf 100644
--- a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/util.rb
+++ b/railties/lib/rails/vendor/thor-0.12.3/lib/thor/util.rb
@@ -76,8 +76,10 @@ class Thor
# Returns the thor classes declared inside the given class.
#
def self.thor_classes_in(klass)
+ stringfied_constants = klass.constants.map { |c| c.to_s }
Thor::Base.subclasses.select do |subclass|
- klass.constants.include?(subclass.name.gsub("#{klass.name}::", ''))
+ next unless subclass.name
+ stringfied_constants.include?(subclass.name.gsub("#{klass.name}::", ''))
end
end
@@ -155,7 +157,7 @@ class Thor
# inside the sandbox to avoid namespacing conflicts.
#
def self.load_thorfile(path, content=nil)
- content ||= File.read(path)
+ content ||= File.binread(path)
begin
Thor::Sandbox.class_eval(content, path)
@@ -164,26 +166,6 @@ class Thor
end
end
- # Receives a yaml (hash) and updates all constants entries to namespace.
- # This was added to deal with deprecated versions of Thor.
- #
- # TODO Deprecate this method in the future.
- #
- # ==== Returns
- # TrueClass|FalseClass:: Returns true if any change to the yaml file was made.
- #
- def self.convert_constants_to_namespaces(yaml)
- yaml_changed = false
-
- yaml.each do |k, v|
- next unless v[:constants] && v[:namespaces].nil?
- yaml_changed = true
- yaml[k][:namespaces] = v[:constants].map{|c| Thor::Util.namespace_from_thor_class(c)}
- end
-
- yaml_changed
- end
-
def self.user_home
@@user_home ||= if ENV["HOME"]
ENV["HOME"]
diff --git a/railties/lib/rails/vendor/thor-0.12.3/lib/thor/version.rb b/railties/lib/rails/vendor/thor-0.12.3/lib/thor/version.rb
new file mode 100644
index 0000000000..3c9dd6f808
--- /dev/null
+++ b/railties/lib/rails/vendor/thor-0.12.3/lib/thor/version.rb
@@ -0,0 +1,3 @@
+class Thor
+ VERSION = "0.12.3".freeze
+end
diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb
index adb867ca6d..79dfacdcdb 100644
--- a/railties/test/application/configuration_test.rb
+++ b/railties/test/application/configuration_test.rb
@@ -24,6 +24,11 @@ module ApplicationTests
assert_equal Pathname.new(app_path), Rails.application.root
end
+ test "the application root can be seen from the application singleton" do
+ require "#{app_path}/config/environment"
+ assert_equal Pathname.new(app_path), AppTemplate::Application.root
+ end
+
test "the application root can be set" do
copy_app
add_to_config <<-RUBY
diff --git a/railties/test/generators/actions_test.rb b/railties/test/generators/actions_test.rb
index b69f23c965..27b6a49566 100644
--- a/railties/test/generators/actions_test.rb
+++ b/railties/test/generators/actions_test.rb
@@ -1,8 +1,10 @@
-require 'abstract_unit'
require 'generators/generators_test_helper'
require 'rails/generators/rails/app/app_generator'
class ActionsTest < GeneratorsTestCase
+ tests Rails::Generators::AppGenerator
+ arguments [destination_root]
+
def setup
super
@git_plugin_uri = 'git://github.com/technoweenie/restful-authentication.git'
@@ -178,14 +180,6 @@ class ActionsTest < GeneratorsTestCase
protected
- def run_generator
- silence(:stdout) { Rails::Generators::AppGenerator.start [destination_root] }
- end
-
- def generator(config={})
- @generator ||= Rails::Generators::Base.new([], {}, { :destination_root => destination_root }.merge!(config))
- end
-
def action(*args, &block)
silence(:stdout){ generator.send(*args, &block) }
end
diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb
index 2cba42551e..7dd798db75 100644
--- a/railties/test/generators/app_generator_test.rb
+++ b/railties/test/generators/app_generator_test.rb
@@ -3,6 +3,7 @@ require 'generators/generators_test_helper'
require 'rails/generators/rails/app/app_generator'
class AppGeneratorTest < GeneratorsTestCase
+ arguments [destination_root]
def setup
super
@@ -49,17 +50,27 @@ class AppGeneratorTest < GeneratorsTestCase
end
def test_invalid_database_option_raises_an_error
- content = capture(:stderr){ run_generator(["-d", "unknown"]) }
+ content = capture(:stderr){ run_generator([destination_root, "-d", "unknown"]) }
assert_match /Invalid value for \-\-database option/, content
end
+ def test_invalid_application_name_raises_an_error
+ content = capture(:stderr){ Rails::Generators::AppGenerator.start [File.join(destination_root, "43-things")] }
+ assert_equal "Invalid application name 43-things. Please give a name which does not start with numbers.\n", content
+ end
+
+ def test_invalid_application_name_is_fixed
+ silence(:stdout){ Rails::Generators::AppGenerator.start [File.join(destination_root, "things-43")] }
+ assert_file "things-43/config/environment.rb", /Things43::Application/
+ end
+
def test_config_database_is_added_by_default
run_generator
assert_file "config/database.yml", /sqlite3/
end
def test_config_database_is_not_added_if_skip_activerecord_is_given
- run_generator ["--skip-activerecord"]
+ run_generator [destination_root, "--skip-activerecord"]
assert_no_file "config/database.yml"
end
@@ -76,13 +87,13 @@ class AppGeneratorTest < GeneratorsTestCase
end
def test_prototype_and_test_unit_are_skipped_if_required
- run_generator ["--skip-prototype", "--skip-testunit"]
+ run_generator [destination_root, "--skip-prototype", "--skip-testunit"]
assert_no_file "public/javascripts/prototype.js"
assert_no_file "test"
end
def test_shebang_is_added_to_files
- run_generator ["--ruby", "foo/bar/baz"]
+ run_generator [destination_root, "--ruby", "foo/bar/baz"]
%w(
about
@@ -97,7 +108,7 @@ class AppGeneratorTest < GeneratorsTestCase
end
def test_shebang_when_is_the_same_as_default_use_env
- run_generator ["--ruby", Thor::Util.ruby_command]
+ run_generator [destination_root, "--ruby", Thor::Util.ruby_command]
%w(
about
@@ -113,11 +124,11 @@ class AppGeneratorTest < GeneratorsTestCase
def test_template_from_dir_pwd
FileUtils.cd(Rails.root)
- assert_match /It works from file!/, run_generator(["-m", "lib/template.rb"])
+ assert_match /It works from file!/, run_generator([destination_root, "-m", "lib/template.rb"])
end
def test_template_raises_an_error_with_invalid_path
- content = capture(:stderr){ run_generator(["-m", "non/existant/path"]) }
+ content = capture(:stderr){ run_generator([destination_root, "-m", "non/existant/path"]) }
assert_match /The template \[.*\] could not be loaded/, content
assert_match /non\/existant\/path/, content
end
@@ -127,7 +138,7 @@ class AppGeneratorTest < GeneratorsTestCase
template = %{ say "It works!" }
template.instance_eval "def read; self; end" # Make the string respond to read
- generator(:template => path, :database => "sqlite3").expects(:open).with(path).returns(template)
+ generator([destination_root], :template => path, :database => "sqlite3").expects(:open).with(path).returns(template)
assert_match /It works!/, silence(:stdout){ generator.invoke }
end
@@ -151,28 +162,20 @@ class AppGeneratorTest < GeneratorsTestCase
end
def test_dev_option
- run_generator ["--dev"]
+ run_generator [destination_root, "--dev"]
rails_path = File.expand_path('../../..', Rails.root)
dev_gem = %(gem "rails", :path => #{rails_path.inspect})
assert_file 'Gemfile', /^#{Regexp.escape(dev_gem)}$/
end
def test_edge_option
- run_generator ["--edge"]
+ run_generator [destination_root, "--edge"]
edge_gem = %(gem "rails", :git => "git://github.com/rails/rails.git")
assert_file 'Gemfile', /^#{Regexp.escape(edge_gem)}$/
end
protected
- def run_generator(args=[])
- silence(:stdout) { Rails::Generators::AppGenerator.start [destination_root].concat(args) }
- end
-
- def generator(options={})
- @generator ||= Rails::Generators::AppGenerator.new([destination_root], options, :destination_root => destination_root)
- end
-
def action(*args, &block)
silence(:stdout){ generator.send(*args, &block) }
end
diff --git a/railties/test/generators/controller_generator_test.rb b/railties/test/generators/controller_generator_test.rb
index 56bc688ad0..8e2fd3b9ed 100644
--- a/railties/test/generators/controller_generator_test.rb
+++ b/railties/test/generators/controller_generator_test.rb
@@ -1,12 +1,12 @@
-require 'abstract_unit'
require 'generators/generators_test_helper'
require 'rails/generators/rails/controller/controller_generator'
class ControllerGeneratorTest < GeneratorsTestCase
+ arguments %w(Account foo bar)
def test_help_does_not_show_invoked_generators_options_if_they_already_exist
content = run_generator ["--help"]
- assert_no_match /Helper options:/, content
+ assert_no_match /Helper options\:/, content
end
def test_controller_skeleton_is_created
@@ -66,15 +66,8 @@ class ControllerGeneratorTest < GeneratorsTestCase
run_generator
assert_file "app/controllers/account_controller.rb" do |controller|
- assert_instance_method controller, :foo
- assert_instance_method controller, :bar
+ assert_instance_method :foo, controller
+ assert_instance_method :bar, controller
end
end
-
- protected
-
- def run_generator(args=["Account", "foo", "bar"])
- silence(:stdout) { Rails::Generators::ControllerGenerator.start args, :destination_root => destination_root }
- end
-
end
diff --git a/railties/test/generators/generator_generator_test.rb b/railties/test/generators/generator_generator_test.rb
index aea3f4da51..28377f23b0 100644
--- a/railties/test/generators/generator_generator_test.rb
+++ b/railties/test/generators/generator_generator_test.rb
@@ -1,8 +1,8 @@
-require 'abstract_unit'
require 'generators/generators_test_helper'
require 'rails/generators/rails/generator/generator_generator'
class GeneratorGeneratorTest < GeneratorsTestCase
+ arguments %w(awesome)
def test_generator_skeleton_is_created
run_generator
@@ -16,11 +16,4 @@ class GeneratorGeneratorTest < GeneratorsTestCase
assert_file "lib/generators/awesome/awesome_generator.rb",
/class AwesomeGenerator < Rails::Generators::NamedBase/
end
-
- protected
-
- def run_generator(args=["awesome"], config={})
- silence(:stdout) { Rails::Generators::GeneratorGenerator.start args, config.merge(:destination_root => destination_root) }
- end
-
end
diff --git a/railties/test/generators/generators_test_helper.rb b/railties/test/generators/generators_test_helper.rb
index 4ce48a453b..35567f7929 100644
--- a/railties/test/generators/generators_test_helper.rb
+++ b/railties/test/generators/generators_test_helper.rb
@@ -10,93 +10,23 @@ end
Rails.application.config.root = Rails.root
require 'rails/generators'
+require 'rails/generators/test_case'
+
require 'rubygems'
require 'active_record'
require 'action_dispatch'
-CURRENT_PATH = File.expand_path(Dir.pwd)
-Rails::Generators.no_color!
-
-class GeneratorsTestCase < ActiveSupport::TestCase
- include FileUtils
-
- def destination_root
- File.join(Rails.root, "tmp")
- end
+class GeneratorsTestCase < Rails::Generators::TestCase
+ destination File.join(Rails.root, "tmp")
+ setup :prepare_destination
- def setup
- cd CURRENT_PATH
- rm_rf(destination_root)
- mkdir_p(destination_root)
+ def self.inherited(base)
+ base.tests Rails::Generators.const_get(base.name.sub(/Test$/, ''))
+ rescue
+ # Do nothing.
end
def test_truth
- # don't complain, test/unit
- end
-
- def capture(stream)
- begin
- stream = stream.to_s
- eval "$#{stream} = StringIO.new"
- yield
- result = eval("$#{stream}").string
- ensure
- eval("$#{stream} = #{stream.upcase}")
- end
-
- result
- end
- alias :silence :capture
-
- def assert_file(relative, *contents)
- absolute = File.join(destination_root, relative)
- assert File.exists?(absolute), "Expected file #{relative.inspect} to exist, but does not"
-
- read = File.read(absolute) if block_given? || !contents.empty?
- yield read if block_given?
-
- contents.each do |content|
- case content
- when String
- assert_equal content, read
- when Regexp
- assert_match content, read
- end
- end
- end
-
- def assert_no_file(relative)
- absolute = File.join(destination_root, relative)
- assert !File.exists?(absolute), "Expected file #{relative.inspect} to not exist, but does"
+ # Don't cry test/unit
end
-
- def assert_migration(relative, *contents, &block)
- file_name = migration_file_name(relative)
- assert file_name, "Expected migration #{relative} to exist, but was not found"
- assert_file File.join(File.dirname(relative), file_name), *contents, &block
- end
-
- def assert_no_migration(relative)
- file_name = migration_file_name(relative)
- assert_nil file_name, "Expected migration #{relative} to not exist, but found #{file_name}"
- end
-
- def assert_class_method(content, method, &block)
- assert_instance_method content, "self.#{method}", &block
- end
-
- def assert_instance_method(content, method)
- assert content =~ /def #{method}(\(.+\))?(.*?)\n end/m, "Expected to have method #{method}"
- yield $2.strip if block_given?
- end
-
- protected
-
- def migration_file_name(relative)
- absolute = File.join(destination_root, relative)
- dirname, file_name = File.dirname(absolute), File.basename(absolute).sub(/\.rb$/, '')
-
- migration = Dir.glob("#{dirname}/[0-9]*_*.rb").grep(/\d+_#{file_name}.rb$/).first
- File.basename(migration) if migration
- end
-end
+end \ No newline at end of file
diff --git a/railties/test/generators/helper_generator_test.rb b/railties/test/generators/helper_generator_test.rb
index f8bfc517a2..cf18782986 100644
--- a/railties/test/generators/helper_generator_test.rb
+++ b/railties/test/generators/helper_generator_test.rb
@@ -1,4 +1,3 @@
-require 'abstract_unit'
require 'generators/generators_test_helper'
require 'rails/generators/rails/helper/helper_generator'
@@ -6,6 +5,7 @@ ObjectHelper = Class.new
AnotherObjectHelperTest = Class.new
class HelperGeneratorTest < GeneratorsTestCase
+ arguments %w(admin)
def test_helper_skeleton_is_created
run_generator
@@ -50,11 +50,4 @@ class HelperGeneratorTest < GeneratorsTestCase
end
end
end
-
- protected
-
- def run_generator(args=["admin"])
- silence(:stdout) { Rails::Generators::HelperGenerator.start args, :destination_root => destination_root }
- end
-
end
diff --git a/railties/test/generators/integration_test_generator_test.rb b/railties/test/generators/integration_test_generator_test.rb
index 6a504ceea2..88e18be5b2 100644
--- a/railties/test/generators/integration_test_generator_test.rb
+++ b/railties/test/generators/integration_test_generator_test.rb
@@ -1,18 +1,11 @@
-require 'abstract_unit'
require 'generators/generators_test_helper'
require 'rails/generators/rails/integration_test/integration_test_generator'
class IntegrationTestGeneratorTest < GeneratorsTestCase
+ arguments %w(integration)
def test_integration_test_skeleton_is_created
run_generator
assert_file "test/integration/integration_test.rb", /class IntegrationTest < ActionController::IntegrationTest/
end
-
- protected
-
- def run_generator(args=["integration"])
- silence(:stdout) { Rails::Generators::IntegrationTestGenerator.start args, :destination_root => destination_root }
- end
-
end
diff --git a/railties/test/generators/mailer_generator_test.rb b/railties/test/generators/mailer_generator_test.rb
index 251474ad16..ee4346eb71 100644
--- a/railties/test/generators/mailer_generator_test.rb
+++ b/railties/test/generators/mailer_generator_test.rb
@@ -1,8 +1,8 @@
-require 'abstract_unit'
require 'generators/generators_test_helper'
require 'rails/generators/rails/mailer/mailer_generator'
class MailerGeneratorTest < GeneratorsTestCase
+ arguments %w(notifier foo bar)
def test_mailer_skeleton_is_created
run_generator
@@ -42,11 +42,4 @@ class MailerGeneratorTest < GeneratorsTestCase
assert_file "app/models/notifier.rb", /def foo/
assert_file "app/models/notifier.rb", /def bar/
end
-
- protected
-
- def run_generator(args=["notifier", "foo", "bar"])
- silence(:stdout) { Rails::Generators::MailerGenerator.start args, :destination_root => destination_root }
- end
-
end
diff --git a/railties/test/generators/metal_generator_test.rb b/railties/test/generators/metal_generator_test.rb
index 80bf342892..5d6a277561 100644
--- a/railties/test/generators/metal_generator_test.rb
+++ b/railties/test/generators/metal_generator_test.rb
@@ -1,8 +1,8 @@
-require 'abstract_unit'
require 'generators/generators_test_helper'
require 'rails/generators/rails/metal/metal_generator'
class MetalGeneratorTest < GeneratorsTestCase
+ arguments %w(foo)
def test_metal_skeleton_is_created
run_generator
@@ -13,11 +13,4 @@ class MetalGeneratorTest < GeneratorsTestCase
content = capture(:stderr){ run_generator ["object"] }
assert_match /The name 'Object' is either already used in your application or reserved/, content
end
-
- protected
-
- def run_generator(args=["foo"])
- silence(:stdout) { Rails::Generators::MetalGenerator.start args, :destination_root => destination_root }
- end
-
end
diff --git a/railties/test/generators/migration_generator_test.rb b/railties/test/generators/migration_generator_test.rb
index 35172a8be4..2fd3e5c056 100644
--- a/railties/test/generators/migration_generator_test.rb
+++ b/railties/test/generators/migration_generator_test.rb
@@ -1,32 +1,30 @@
-require 'abstract_unit'
require 'generators/generators_test_helper'
require 'rails/generators/rails/migration/migration_generator'
class MigrationGeneratorTest < GeneratorsTestCase
-
def test_migration
- @migration = "change_title_body_from_posts"
- run_generator
- assert_migration "db/migrate/#{@migration}.rb", /class ChangeTitleBodyFromPosts < ActiveRecord::Migration/
+ migration = "change_title_body_from_posts"
+ run_generator [migration]
+ assert_migration "db/migrate/#{migration}.rb", /class ChangeTitleBodyFromPosts < ActiveRecord::Migration/
end
def test_migration_with_class_name
- @migration = "ChangeTitleBodyFromPosts"
- run_generator
- assert_migration "db/migrate/change_title_body_from_posts.rb", /class #{@migration} < ActiveRecord::Migration/
+ migration = "ChangeTitleBodyFromPosts"
+ run_generator [migration]
+ assert_migration "db/migrate/change_title_body_from_posts.rb", /class #{migration} < ActiveRecord::Migration/
end
def test_add_migration_with_attributes
- @migration = "add_title_body_to_posts"
- run_generator [@migration, "title:string", "body:text"]
+ migration = "add_title_body_to_posts"
+ run_generator [migration, "title:string", "body:text"]
- assert_migration "db/migrate/#{@migration}.rb" do |content|
- assert_class_method content, :up do |up|
+ assert_migration "db/migrate/#{migration}.rb" do |content|
+ assert_class_method :up, content do |up|
assert_match /add_column :posts, :title, :string/, up
assert_match /add_column :posts, :body, :text/, up
end
- assert_class_method content, :down do |down|
+ assert_class_method :down, content do |down|
assert_match /remove_column :posts, :title/, down
assert_match /remove_column :posts, :body/, down
end
@@ -34,26 +32,19 @@ class MigrationGeneratorTest < GeneratorsTestCase
end
def test_remove_migration_with_attributes
- @migration = "remove_title_body_from_posts"
- run_generator [@migration, "title:string", "body:text"]
+ migration = "remove_title_body_from_posts"
+ run_generator [migration, "title:string", "body:text"]
- assert_migration "db/migrate/#{@migration}.rb" do |content|
- assert_class_method content, :up do |up|
+ assert_migration "db/migrate/#{migration}.rb" do |content|
+ assert_class_method :up, content do |up|
assert_match /remove_column :posts, :title/, up
assert_match /remove_column :posts, :body/, up
end
- assert_class_method content, :down do |down|
+ assert_class_method :down, content do |down|
assert_match /add_column :posts, :title, :string/, down
assert_match /add_column :posts, :body, :text/, down
end
end
end
-
- protected
-
- def run_generator(args=[@migration])
- silence(:stdout) { Rails::Generators::MigrationGenerator.start args, :destination_root => destination_root }
- end
-
end
diff --git a/railties/test/generators/model_generator_test.rb b/railties/test/generators/model_generator_test.rb
index e073b11e1e..051a43706b 100644
--- a/railties/test/generators/model_generator_test.rb
+++ b/railties/test/generators/model_generator_test.rb
@@ -1,8 +1,8 @@
-require 'abstract_unit'
require 'generators/generators_test_helper'
require 'rails/generators/rails/model/model_generator'
class ModelGeneratorTest < GeneratorsTestCase
+ arguments %w(Account name:string age:integer)
def test_help_shows_invoked_generators_options
content = run_generator ["--help"]
@@ -84,13 +84,13 @@ class ModelGeneratorTest < GeneratorsTestCase
run_generator ["product", "name:string", "supplier_id:integer"]
assert_migration "db/migrate/create_products.rb" do |m|
- assert_class_method m, :up do |up|
+ assert_class_method :up, m do |up|
assert_match /create_table :products/, up
assert_match /t\.string :name/, up
assert_match /t\.integer :supplier_id/, up
end
- assert_class_method m, :down do |down|
+ assert_class_method :down, m do |down|
assert_match /drop_table :products/, down
end
end
@@ -126,7 +126,7 @@ class ModelGeneratorTest < GeneratorsTestCase
run_generator ["account", "--no-timestamps"]
assert_migration "db/migrate/create_accounts.rb" do |m|
- assert_class_method m, :up do |up|
+ assert_class_method :up, m do |up|
assert_no_match /t.timestamps/, up
end
end
@@ -171,11 +171,4 @@ class ModelGeneratorTest < GeneratorsTestCase
content = capture(:stderr){ run_generator ["object"] }
assert_match /The name 'Object' is either already used in your application or reserved/, content
end
-
- protected
-
- def run_generator(args=["Account", "name:string", "age:integer"], config={})
- silence(:stdout) { Rails::Generators::ModelGenerator.start args, config.merge(:destination_root => destination_root) }
- end
-
end
diff --git a/railties/test/generators/named_base_test.rb b/railties/test/generators/named_base_test.rb
index 98cbf9b8f6..8c1df3b992 100644
--- a/railties/test/generators/named_base_test.rb
+++ b/railties/test/generators/named_base_test.rb
@@ -1,4 +1,3 @@
-require 'abstract_unit'
require 'generators/generators_test_helper'
require 'rails/generators/rails/scaffold_controller/scaffold_controller_generator'
@@ -13,9 +12,10 @@ module ActiveRecord
end
class NamedBaseTest < GeneratorsTestCase
+ tests Rails::Generators::ScaffoldControllerGenerator
def test_named_generator_attributes
- g = Rails::Generators::ScaffoldControllerGenerator.new ["admin/foo"]
+ g = generator ["admin/foo"]
assert_equal 'admin/foo', g.name
assert_equal %w(admin), g.class_path
assert_equal 1, g.class_nesting_depth
@@ -28,12 +28,12 @@ class NamedBaseTest < GeneratorsTestCase
def test_named_generator_attributes_without_pluralized
ActiveRecord::Base.pluralize_table_names = false
- g = Rails::Generators::ScaffoldControllerGenerator.new ["admin/foo"]
+ g = generator ["admin/foo"]
assert_equal "admin_#{g.singular_name}", g.table_name
end
def test_scaffold_plural_names
- g = Rails::Generators::ScaffoldControllerGenerator.new ["ProductLine"]
+ g = generator ["ProductLine"]
assert_equal "ProductLines", g.controller_name
assert_equal "ProductLines", g.controller_class_name
assert_equal "product_lines", g.controller_file_name
diff --git a/railties/test/generators/observer_generator_test.rb b/railties/test/generators/observer_generator_test.rb
index 6fed2998dd..44d9e4a9f3 100644
--- a/railties/test/generators/observer_generator_test.rb
+++ b/railties/test/generators/observer_generator_test.rb
@@ -1,8 +1,8 @@
-require 'abstract_unit'
require 'generators/generators_test_helper'
require 'rails/generators/rails/observer/observer_generator'
class ObserverGeneratorTest < GeneratorsTestCase
+ arguments %w(account)
def test_invokes_default_orm
run_generator
@@ -23,11 +23,4 @@ class ObserverGeneratorTest < GeneratorsTestCase
content = run_generator ["account", "--test-framework=rspec"]
assert_match /rspec \[not found\]/, content
end
-
- protected
-
- def run_generator(args=["account"])
- silence(:stdout) { Rails::Generators::ObserverGenerator.start args, :destination_root => destination_root }
- end
-
end
diff --git a/railties/test/generators/performance_test_generator_test.rb b/railties/test/generators/performance_test_generator_test.rb
index d19128f79a..099575ea1d 100644
--- a/railties/test/generators/performance_test_generator_test.rb
+++ b/railties/test/generators/performance_test_generator_test.rb
@@ -1,18 +1,11 @@
-require 'abstract_unit'
require 'generators/generators_test_helper'
require 'rails/generators/rails/performance_test/performance_test_generator'
class PerformanceTestGeneratorTest < GeneratorsTestCase
+ arguments %w(performance)
def test_performance_test_skeleton_is_created
run_generator
assert_file "test/performance/performance_test.rb", /class PerformanceTest < ActionController::PerformanceTest/
end
-
- protected
-
- def run_generator(args=["performance"])
- silence(:stdout) { Rails::Generators::PerformanceTestGenerator.start args, :destination_root => destination_root }
- end
-
end
diff --git a/railties/test/generators/plugin_generator_test.rb b/railties/test/generators/plugin_generator_test.rb
index f5b8b6ffb6..f84b8b6d50 100644
--- a/railties/test/generators/plugin_generator_test.rb
+++ b/railties/test/generators/plugin_generator_test.rb
@@ -1,8 +1,8 @@
-require 'abstract_unit'
require 'generators/generators_test_helper'
require 'rails/generators/rails/plugin/plugin_generator'
class PluginGeneratorTest < GeneratorsTestCase
+ arguments %w(plugin_fu)
def test_plugin_skeleton_is_created
run_generator
@@ -46,11 +46,4 @@ class PluginGeneratorTest < GeneratorsTestCase
run_generator
run_generator ["plugin_fu"], :behavior => :revoke
end
-
- protected
-
- def run_generator(args=["plugin_fu"], config={})
- silence(:stdout) { Rails::Generators::PluginGenerator.start args, config.merge(:destination_root => destination_root) }
- end
-
end
diff --git a/railties/test/generators/resource_generator_test.rb b/railties/test/generators/resource_generator_test.rb
index dff3908ea1..15c0ca0f01 100644
--- a/railties/test/generators/resource_generator_test.rb
+++ b/railties/test/generators/resource_generator_test.rb
@@ -1,8 +1,8 @@
-require 'abstract_unit'
require 'generators/generators_test_helper'
require 'rails/generators/rails/resource/resource_generator'
class ResourceGeneratorTest < GeneratorsTestCase
+ arguments %w(account)
def setup
super
@@ -50,8 +50,8 @@ class ResourceGeneratorTest < GeneratorsTestCase
run_generator ["account", "--actions", "index", "new"]
assert_file "app/controllers/accounts_controller.rb" do |controller|
- assert_instance_method controller, :index
- assert_instance_method controller, :new
+ assert_instance_method :index, controller
+ assert_instance_method :new, controller
end
assert_file "app/views/accounts/index.html.erb"
@@ -96,11 +96,4 @@ class ResourceGeneratorTest < GeneratorsTestCase
assert_no_match /resources :accounts$/, route
end
end
-
- protected
-
- def run_generator(args=["account"], config={})
- silence(:stdout) { Rails::Generators::ResourceGenerator.start args, config.merge(:destination_root => destination_root) }
- end
-
end
diff --git a/railties/test/generators/scaffold_controller_generator_test.rb b/railties/test/generators/scaffold_controller_generator_test.rb
index 02155c295c..7593c14dd9 100644
--- a/railties/test/generators/scaffold_controller_generator_test.rb
+++ b/railties/test/generators/scaffold_controller_generator_test.rb
@@ -1,4 +1,3 @@
-require 'abstract_unit'
require 'generators/generators_test_helper'
require 'rails/generators/rails/scaffold_controller/scaffold_controller_generator'
@@ -8,6 +7,7 @@ module Unknown
end
class ScaffoldControllerGeneratorTest < GeneratorsTestCase
+ arguments %w(User name:string age:integer)
def test_controller_skeleton_is_created
run_generator
@@ -15,35 +15,35 @@ class ScaffoldControllerGeneratorTest < GeneratorsTestCase
assert_file "app/controllers/users_controller.rb" do |content|
assert_match /class UsersController < ApplicationController/, content
- assert_instance_method content, :index do |m|
+ assert_instance_method :index, content do |m|
assert_match /@users = User\.all/, m
end
- assert_instance_method content, :show do |m|
+ assert_instance_method :show, content do |m|
assert_match /@user = User\.find\(params\[:id\]\)/, m
end
- assert_instance_method content, :new do |m|
+ assert_instance_method :new, content do |m|
assert_match /@user = User\.new/, m
end
- assert_instance_method content, :edit do |m|
+ assert_instance_method :edit, content do |m|
assert_match /@user = User\.find\(params\[:id\]\)/, m
end
- assert_instance_method content, :create do |m|
+ assert_instance_method :create, content do |m|
assert_match /@user = User\.new\(params\[:user\]\)/, m
assert_match /@user\.save/, m
assert_match /@user\.errors/, m
end
- assert_instance_method content, :update do |m|
+ assert_instance_method :update, content do |m|
assert_match /@user = User\.find\(params\[:id\]\)/, m
assert_match /@user\.update_attributes\(params\[:user\]\)/, m
assert_match /@user\.errors/, m
end
- assert_instance_method content, :destroy do |m|
+ assert_instance_method :destroy, content do |m|
assert_match /@user = User\.find\(params\[:id\]\)/, m
assert_match /@user\.destroy/, m
end
@@ -108,7 +108,7 @@ class ScaffoldControllerGeneratorTest < GeneratorsTestCase
assert_file "app/controllers/users_controller.rb" do |content|
assert_match /class UsersController < ApplicationController/, content
- assert_instance_method content, :index do |m|
+ assert_instance_method :index, content do |m|
assert_match /@users = User\.all/, m
end
end
@@ -127,7 +127,7 @@ class ScaffoldControllerGeneratorTest < GeneratorsTestCase
assert_file "app/controllers/users_controller.rb" do |content|
assert_match /class UsersController < ApplicationController/, content
- assert_instance_method content, :index do |m|
+ assert_instance_method :index, content do |m|
assert_match /@users = User\.find\(:all\)/, m
assert_no_match /@users = User\.all/, m
end
@@ -135,11 +135,4 @@ class ScaffoldControllerGeneratorTest < GeneratorsTestCase
ensure
Unknown::Generators.send :remove_const, :ActiveModel
end
-
- protected
-
- def run_generator(args=["User", "name:string", "age:integer"])
- silence(:stdout) { Rails::Generators::ScaffoldControllerGenerator.start args, :destination_root => destination_root }
- end
-
end
diff --git a/railties/test/generators/scaffold_generator_test.rb b/railties/test/generators/scaffold_generator_test.rb
index 0b961cee19..4ddc7b1c89 100644
--- a/railties/test/generators/scaffold_generator_test.rb
+++ b/railties/test/generators/scaffold_generator_test.rb
@@ -1,8 +1,8 @@
-require 'abstract_unit'
require 'generators/generators_test_helper'
require 'rails/generators/rails/scaffold/scaffold_generator'
class ScaffoldGeneratorTest < GeneratorsTestCase
+ arguments %w(product_line title:string price:integer)
def setup
super
@@ -32,35 +32,35 @@ class ScaffoldGeneratorTest < GeneratorsTestCase
assert_file "app/controllers/product_lines_controller.rb" do |content|
assert_match /class ProductLinesController < ApplicationController/, content
- assert_instance_method content, :index do |m|
+ assert_instance_method :index, content do |m|
assert_match /@product_lines = ProductLine\.all/, m
end
- assert_instance_method content, :show do |m|
+ assert_instance_method :show, content do |m|
assert_match /@product_line = ProductLine\.find\(params\[:id\]\)/, m
end
- assert_instance_method content, :new do |m|
+ assert_instance_method :new, content do |m|
assert_match /@product_line = ProductLine\.new/, m
end
- assert_instance_method content, :edit do |m|
+ assert_instance_method :edit, content do |m|
assert_match /@product_line = ProductLine\.find\(params\[:id\]\)/, m
end
- assert_instance_method content, :create do |m|
+ assert_instance_method :create, content do |m|
assert_match /@product_line = ProductLine\.new\(params\[:product_line\]\)/, m
assert_match /@product_line\.save/, m
assert_match /@product_line\.errors/, m
end
- assert_instance_method content, :update do |m|
+ assert_instance_method :update, content do |m|
assert_match /@product_line = ProductLine\.find\(params\[:id\]\)/, m
assert_match /@product_line\.update_attributes\(params\[:product_line\]\)/, m
assert_match /@product_line\.errors/, m
end
- assert_instance_method content, :destroy do |m|
+ assert_instance_method :destroy, content do |m|
assert_match /@product_line = ProductLine\.find\(params\[:id\]\)/, m
assert_match /@product_line\.destroy/, m
end
@@ -89,7 +89,7 @@ class ScaffoldGeneratorTest < GeneratorsTestCase
def test_scaffold_on_revoke
run_generator
- run_generator :behavior => :revoke
+ run_generator ["product_line"], :behavior => :revoke
# Model
assert_no_file "app/models/product_line.rb"
@@ -117,14 +117,4 @@ class ScaffoldGeneratorTest < GeneratorsTestCase
# Stylesheets (should not be removed)
assert_file "public/stylesheets/scaffold.css"
end
-
- protected
-
- def run_generator(config={})
- silence(:stdout) do
- Rails::Generators::ScaffoldGenerator.start ["product_line", "title:string", "price:integer"],
- config.merge(:destination_root => destination_root)
- end
- end
-
end
diff --git a/railties/test/generators/session_migration_generator_test.rb b/railties/test/generators/session_migration_generator_test.rb
index 34fb996b7f..251ffb19ed 100644
--- a/railties/test/generators/session_migration_generator_test.rb
+++ b/railties/test/generators/session_migration_generator_test.rb
@@ -1,9 +1,7 @@
-require 'abstract_unit'
require 'generators/generators_test_helper'
require 'rails/generators/rails/session_migration/session_migration_generator'
class SessionMigrationGeneratorTest < GeneratorsTestCase
-
def test_session_migration_with_default_name
run_generator
assert_migration "db/migrate/add_sessions_table.rb", /class AddSessionsTable < ActiveRecord::Migration/
@@ -24,11 +22,4 @@ class SessionMigrationGeneratorTest < GeneratorsTestCase
ensure
ActiveRecord::SessionStore::Session.table_name = "sessions"
end
-
- protected
-
- def run_generator(args=[])
- silence(:stdout) { Rails::Generators::SessionMigrationGenerator.start args, :destination_root => destination_root }
- end
-
end
diff --git a/railties/test/generators/stylesheets_generator_test.rb b/railties/test/generators/stylesheets_generator_test.rb
index 15263d4bb8..d9079327ba 100644
--- a/railties/test/generators/stylesheets_generator_test.rb
+++ b/railties/test/generators/stylesheets_generator_test.rb
@@ -1,9 +1,7 @@
-require 'abstract_unit'
require 'generators/generators_test_helper'
require 'rails/generators/rails/stylesheets/stylesheets_generator'
class StylesheetsGeneratorTest < GeneratorsTestCase
-
def test_copy_stylesheets
run_generator
assert_file "public/stylesheets/scaffold.css"
@@ -11,14 +9,7 @@ class StylesheetsGeneratorTest < GeneratorsTestCase
def test_stylesheets_are_not_deleted_on_revoke
run_generator
- run_generator :behavior => :revoke
+ run_generator [], :behavior => :revoke
assert_file "public/stylesheets/scaffold.css"
end
-
- protected
-
- def run_generator(config={})
- silence(:stdout) { Rails::Generators::StylesheetsGenerator.start [], config.merge(:destination_root => destination_root) }
- end
-
end
diff --git a/railties/test/generators_test.rb b/railties/test/generators_test.rb
index 4b7b80c7f5..2df218debc 100644
--- a/railties/test/generators_test.rb
+++ b/railties/test/generators_test.rb
@@ -9,6 +9,11 @@ class GeneratorsTest < GeneratorsTestCase
Gem.stubs(:respond_to?).with(:loaded_specs).returns(false)
end
+ def test_invoke_add_generators_to_raw_lookups
+ TestUnit::Generators::ModelGenerator.expects(:start).with(["Account"], {})
+ Rails::Generators.invoke("test_unit:model", ["Account"])
+ end
+
def test_invoke_when_generator_is_not_found
output = capture(:stdout){ Rails::Generators.invoke :unknown }
assert_equal "Could not find generator unknown.\n", output
@@ -51,12 +56,6 @@ class GeneratorsTest < GeneratorsTestCase
assert_equal "foobar:foobar", klass.namespace
end
- def test_find_by_namespace_add_generators_to_raw_lookups
- klass = Rails::Generators.find_by_namespace("test_unit:model")
- assert klass
- assert_equal "test_unit:generators:model", klass.namespace
- end
-
def test_find_by_namespace_lookup_to_the_rails_root_folder
klass = Rails::Generators.find_by_namespace(:fixjour)
assert klass
@@ -96,7 +95,7 @@ class GeneratorsTest < GeneratorsTestCase
end
def test_builtin_generators
- assert Rails::Generators.builtin.include? %w(rails model)
+ assert Rails::Generators.builtin.include?("rails:model")
end
def test_rails_generators_help_with_builtin_information
@@ -107,7 +106,7 @@ class GeneratorsTest < GeneratorsTestCase
def test_rails_generators_with_others_information
output = capture(:stdout){ Rails::Generators.help }.split("\n").last
- assert_equal "Others: active_record:fixjour, fixjour, foobar, mspec, rails:javascripts.", output
+ assert_equal "Others: active_record:fixjour, fixjour, foobar:foobar, mspec, rails:javascripts, xspec.", output
end
def test_warning_is_shown_if_generator_cant_be_loaded
@@ -178,6 +177,8 @@ class GeneratorsTest < GeneratorsTestCase
end
assert_equal false, klass.class_options[:generate].default
+ ensure
+ Rails::Generators.subclasses.delete(klass)
end
def test_source_paths_for_not_namespaced_generators