aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRizwan Reza <rizwanreza@gmail.com>2010-05-17 02:40:15 +0430
committerRizwan Reza <rizwanreza@gmail.com>2010-05-17 02:40:15 +0430
commitd148a6f6ba5f8ee65905f12cd2601fcc377d4852 (patch)
tree4bcb5e7ad47cfb9a9bb14ffe7c9e003d4646d64c
parente1c773006969abea3c0619fbdc7e32c121b6085f (diff)
parent6b4e0cc526f55b5532cf99292c94f0a4db53b16f (diff)
downloadrails-d148a6f6ba5f8ee65905f12cd2601fcc377d4852.tar.gz
rails-d148a6f6ba5f8ee65905f12cd2601fcc377d4852.tar.bz2
rails-d148a6f6ba5f8ee65905f12cd2601fcc377d4852.zip
Merge branch 'master' of git://github.com/rails/rails
-rw-r--r--Gemfile3
-rw-r--r--actionmailer/CHANGELOG4
-rw-r--r--actionmailer/actionmailer.gemspec2
-rw-r--r--actionmailer/lib/action_mailer/base.rb147
-rw-r--r--actionmailer/lib/action_mailer/railtie.rb6
-rw-r--r--actionmailer/test/old_base/mail_service_test.rb6
-rw-r--r--actionpack/Rakefile19
-rw-r--r--actionpack/lib/action_dispatch/middleware/flash.rb10
-rw-r--r--actionpack/lib/action_dispatch/middleware/session/cookie_store.rb2
-rw-r--r--actionpack/lib/action_dispatch/middleware/show_exceptions.rb12
-rw-r--r--actionpack/lib/action_dispatch/testing/assertions/selector.rb16
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/form_helper.rb61
-rw-r--r--actionpack/lib/action_view/helpers/form_options_helper.rb27
-rw-r--r--actionpack/lib/action_view/helpers/form_tag_helper.rb10
-rw-r--r--actionpack/lib/action_view/render/partials.rb27
-rw-r--r--actionpack/lib/action_view/template/error.rb5
-rw-r--r--actionpack/lib/action_view/template/handlers/erb.rb2
-rw-r--r--actionpack/test/controller/assert_select_test.rb22
-rw-r--r--actionpack/test/controller/capture_test.rb5
-rw-r--r--actionpack/test/controller/flash_test.rb20
-rw-r--r--actionpack/test/controller/render_test.rb13
-rw-r--r--actionpack/test/dispatch/request/xml_params_parsing_test.rb2
-rw-r--r--actionpack/test/fixtures/test/_customer_counter_with_as.erb1
-rw-r--r--actionpack/test/fixtures/test/proper_block_detection.erb1
-rw-r--r--actionpack/test/template/asset_tag_helper_test.rb3
-rw-r--r--actionpack/test/template/capture_helper_test.rb2
-rw-r--r--actionpack/test/template/form_helper_test.rb117
-rw-r--r--actionpack/test/template/form_options_helper_test.rb56
-rw-r--r--actionpack/test/template/form_tag_helper_test.rb14
-rw-r--r--actionpack/test/template/render_test.rb1
-rw-r--r--activemodel/lib/active_model/dirty.rb9
-rw-r--r--activemodel/lib/active_model/serializers/xml.rb2
-rw-r--r--activemodel/lib/active_model/validations.rb10
-rw-r--r--activemodel/lib/active_model/validations/acceptance.rb2
-rw-r--r--activemodel/lib/active_model/validations/confirmation.rb2
-rw-r--r--activemodel/lib/active_model/validations/exclusion.rb2
-rw-r--r--activemodel/lib/active_model/validations/format.rb2
-rw-r--r--activemodel/lib/active_model/validations/inclusion.rb2
-rw-r--r--activemodel/lib/active_model/validations/length.rb2
-rw-r--r--activemodel/lib/active_model/validations/numericality.rb2
-rw-r--r--activemodel/lib/active_model/validations/presence.rb2
-rw-r--r--activemodel/lib/active_model/validations/with.rb53
-rw-r--r--activemodel/lib/active_model/validator.rb3
-rw-r--r--activemodel/test/cases/serializeration/xml_serialization_test.rb4
-rw-r--r--activemodel/test/cases/validations/validates_test.rb4
-rw-r--r--activemodel/test/cases/validations_test.rb15
-rw-r--r--activemodel/test/models/automobile.rb12
-rw-r--r--activerecord/CHANGELOG10
-rwxr-xr-xactiverecord/lib/active_record/associations.rb2
-rw-r--r--activerecord/lib/active_record/attribute_methods/dirty.rb2
-rw-r--r--activerecord/lib/active_record/attribute_methods/primary_key.rb2
-rwxr-xr-xactiverecord/lib/active_record/base.rb9
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb6
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb23
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql_adapter.rb51
-rw-r--r--activerecord/lib/active_record/fixtures.rb1
-rw-r--r--activerecord/lib/active_record/locking/optimistic.rb11
-rw-r--r--activerecord/lib/active_record/named_scope.rb6
-rw-r--r--activerecord/lib/active_record/railtie.rb17
-rw-r--r--activerecord/lib/active_record/relation/batches.rb7
-rw-r--r--activerecord/lib/active_record/relation/calculations.rb11
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb6
-rw-r--r--activerecord/lib/active_record/schema_dumper.rb3
-rw-r--r--activerecord/lib/active_record/validations.rb53
-rw-r--r--activerecord/test/cases/active_schema_test_mysql.rb17
-rw-r--r--activerecord/test/cases/associations/eager_load_nested_include_test.rb14
-rw-r--r--activerecord/test/cases/associations/has_many_associations_test.rb4
-rw-r--r--activerecord/test/cases/associations/join_model_test.rb2
-rw-r--r--activerecord/test/cases/autosave_association_test.rb4
-rwxr-xr-xactiverecord/test/cases/base_test.rb12
-rw-r--r--activerecord/test/cases/batches_test.rb14
-rw-r--r--activerecord/test/cases/calculations_test.rb3
-rw-r--r--activerecord/test/cases/dirty_test.rb9
-rw-r--r--activerecord/test/cases/finder_test.rb4
-rw-r--r--activerecord/test/cases/fixtures_test.rb5
-rw-r--r--activerecord/test/cases/method_scoping_test.rb31
-rw-r--r--activerecord/test/cases/migration_test.rb20
-rw-r--r--activerecord/test/cases/named_scope_test.rb7
-rw-r--r--activerecord/test/cases/validations_test.rb23
-rw-r--r--activerecord/test/models/post.rb1
-rw-r--r--activerecord/test/models/reply.rb5
-rw-r--r--activeresource/lib/active_resource/base.rb14
-rw-r--r--activeresource/lib/active_resource/schema.rb2
-rw-r--r--activeresource/test/cases/base_test.rb50
-rw-r--r--activeresource/test/fixtures/sound.rb5
-rw-r--r--activesupport/CHANGELOG2
-rw-r--r--activesupport/lib/active_support/core_ext/array/random_access.rb12
-rw-r--r--activesupport/lib/active_support/core_ext/date/calculations.rb21
-rw-r--r--activesupport/lib/active_support/core_ext/logger.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/object/with_options.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/time/calculations.rb14
-rw-r--r--activesupport/lib/active_support/lazy_load_hooks.rb18
-rw-r--r--activesupport/test/buffered_logger_test.rb4
-rw-r--r--activesupport/test/caching_test.rb4
-rw-r--r--activesupport/test/core_ext/array_ext_test.rb12
-rw-r--r--activesupport/test/core_ext/date_ext_test.rb26
-rw-r--r--activesupport/test/core_ext/date_time_ext_test.rb8
-rw-r--r--activesupport/test/core_ext/time_ext_test.rb18
-rw-r--r--activesupport/test/lazy_load_hooks_test.rb67
-rw-r--r--railties/guides/source/active_record_basics.textile10
-rw-r--r--railties/guides/source/active_support_core_extensions.textile22
-rw-r--r--railties/guides/source/initialization.textile2
-rw-r--r--railties/lib/rails/application.rb9
-rw-r--r--railties/lib/rails/application/bootstrap.rb8
-rw-r--r--railties/lib/rails/application/configuration.rb6
-rw-r--r--railties/lib/rails/application/finisher.rb11
-rw-r--r--railties/lib/rails/configuration.rb31
-rw-r--r--railties/lib/rails/engine.rb34
-rw-r--r--railties/lib/rails/generators/actions.rb3
-rw-r--r--railties/lib/rails/generators/generated_attribute.rb13
-rw-r--r--railties/lib/rails/generators/rails/app/app_generator.rb2
-rw-r--r--railties/lib/rails/generators/test_case.rb12
-rw-r--r--railties/lib/rails/info.rb33
-rw-r--r--railties/lib/rails/railtie.rb3
-rw-r--r--railties/lib/rails/railtie/configuration.rb25
-rw-r--r--railties/lib/rails/tasks/framework.rake22
-rw-r--r--railties/lib/rails/tasks/routes.rake6
-rw-r--r--railties/test/application/initializers/hooks_test.rb (renamed from railties/test/application/initializers/initializers_test.rb)35
-rw-r--r--railties/test/generators/actions_test.rb14
-rw-r--r--railties/test/generators/app_generator_test.rb9
-rw-r--r--railties/test/generators/generated_attribute_test.rb40
-rw-r--r--railties/test/generators_test.rb13
-rw-r--r--railties/test/rails_info_test.rb21
-rw-r--r--railties/test/railties/engine_test.rb26
125 files changed, 1357 insertions, 475 deletions
diff --git a/Gemfile b/Gemfile
index f5968827a1..e94afc828e 100644
--- a/Gemfile
+++ b/Gemfile
@@ -9,6 +9,7 @@ gem "mocha", ">= 0.9.8"
group :mri do
gem 'json'
gem 'yajl-ruby'
+ gem "nokogiri", ">= 1.4.0"
if RUBY_VERSION < '1.9'
gem "system_timer"
@@ -19,7 +20,7 @@ group :mri do
end
# AR
-gem "sqlite3-ruby", ">= 1.2.5", :require => 'sqlite3'
+gem "sqlite3-ruby", "= 1.3.0.beta.2", :require => 'sqlite3'
group :db do
gem "pg", ">= 0.9.0"
diff --git a/actionmailer/CHANGELOG b/actionmailer/CHANGELOG
index 383ad2105d..208299cfd1 100644
--- a/actionmailer/CHANGELOG
+++ b/actionmailer/CHANGELOG
@@ -1,3 +1,7 @@
+* Changed encoding behaviour of mail, so updated tests in actionmailer and bumped mail version to 2.2.1 [ML]
+
+* Added ability to pass Proc objects to the defaults hash [ML]
+
*Rails 3.0.0 [beta 3] (April 13th, 2010)*
* Removed all quoting.rb type files from ActionMailer and put Mail 2.2.0 in instead [ML]
diff --git a/actionmailer/actionmailer.gemspec b/actionmailer/actionmailer.gemspec
index 01a886c5ee..280771e4db 100644
--- a/actionmailer/actionmailer.gemspec
+++ b/actionmailer/actionmailer.gemspec
@@ -20,6 +20,6 @@ Gem::Specification.new do |s|
s.has_rdoc = true
s.add_dependency('actionpack', version)
- s.add_dependency('mail', '~> 2.2.0')
+ s.add_dependency('mail', '~> 2.2.1')
s.add_dependency('text-format', '~> 1.0.0')
end
diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb
index 88298966eb..3c41691af7 100644
--- a/actionmailer/lib/action_mailer/base.rb
+++ b/actionmailer/lib/action_mailer/base.rb
@@ -3,6 +3,7 @@ require 'action_mailer/tmail_compat'
require 'action_mailer/collector'
require 'active_support/core_ext/array/wrap'
require 'active_support/core_ext/object/blank'
+require 'active_support/core_ext/proc'
module ActionMailer #:nodoc:
# Action Mailer allows you to send email from your application using a mailer model and views.
@@ -22,16 +23,16 @@ module ActionMailer #:nodoc:
# class Notifier < ActionMailer::Base
# default :from => 'no-reply@example.com',
# :return_path => 'system@example.com'
- #
+ #
# def welcome(recipient)
# @account = recipient
# mail(:to => recipient.email_address_with_name,
# :bcc => ["bcc@example.com", "Order Watcher <watcher@example.com>"])
# end
# end
- #
+ #
# Within the mailer method, you have access to the following methods:
- #
+ #
# * <tt>attachments[]=</tt> - Allows you to add attachments to your email in an intuitive
# manner; <tt>attachments['filename.png'] = File.read('path/to/filename.png')</tt>
#
@@ -46,16 +47,16 @@ module ActionMailer #:nodoc:
# as <tt>headers({'X-No-Spam' => 'True', 'In-Reply-To' => '1234@message.id'})</tt>
#
# * <tt>mail</tt> - Allows you to specify your email to send.
- #
+ #
# The hash passed to the mail method allows you to specify any header that a Mail::Message
# will accept (any valid Email header including optional fields).
#
# The mail method, if not passed a block, will inspect your views and send all the views with
# the same name as the method, so the above action would send the +welcome.text.plain.erb+ view
# file as well as the +welcome.text.html.erb+ view file in a +multipart/alternative+ email.
- #
+ #
# If you want to explicitly render only certain templates, pass a block:
- #
+ #
# mail(:to => user.emai) do |format|
# format.text
# format.html
@@ -79,7 +80,7 @@ module ActionMailer #:nodoc:
#
# Like Action Controller, each mailer class has a corresponding view directory in which each
# method of the class looks for a template with its name.
- #
+ #
# To define a template to be used with a mailing, create an <tt>.erb</tt> file with the same
# name as the method in your mailer model. For example, in the mailer defined above, the template at
# <tt>app/views/notifier/signup_notification.text.plain.erb</tt> would be used to generate the email.
@@ -104,7 +105,7 @@ module ActionMailer #:nodoc:
#
# = Generating URLs
#
- # URLs can be generated in mailer views using <tt>url_for</tt> or named routes. Unlike controllers from
+ # URLs can be generated in mailer views using <tt>url_for</tt> or named routes. Unlike controllers from
# Action Pack, the mailer instance doesn't have any context about the incoming request, so you'll need
# to provide all of the details needed to generate a URL.
#
@@ -176,7 +177,7 @@ module ActionMailer #:nodoc:
# mail(:to => recipient, :subject => "New account information")
# end
# end
- #
+ #
# Which will (if it had both a <tt>welcome.text.plain.erb</tt> and <tt>welcome.text.html.erb</tt>
# tempalte in the view directory), send a complete <tt>multipart/mixed</tt> email with two parts,
# the first part being a <tt>multipart/alternative</tt> with the text and HTML email parts inside,
@@ -184,71 +185,71 @@ module ActionMailer #:nodoc:
# with the filename +free_book.pdf+.
#
# = Observing and Intercepting Mails
- #
+ #
# Action Mailer provides hooks into the Mail observer and interceptor methods. These allow you to
# register objects that are called during the mail delivery life cycle.
- #
+ #
# An observer object must implement the <tt>:delivered_email(message)</tt> method which will be
# called once for every email sent after the email has been sent.
- #
+ #
# An interceptor object must implement the <tt>:delivering_email(message)</tt> method which will be
# called before the email is sent, allowing you to make modifications to the email before it hits
# the delivery agents. Your object should make and needed modifications directly to the passed
# in Mail::Message instance.
#
# = Default Hash
- #
+ #
# Action Mailer provides some intelligent defaults for your emails, these are usually specified in a
# default method inside the class definition:
- #
+ #
# class Notifier < ActionMailer::Base
# default :sender => 'system@example.com'
# end
- #
+ #
# You can pass in any header value that a <tt>Mail::Message</tt>, out of the box, <tt>ActionMailer::Base</tt>
# sets the following:
- #
+ #
# * <tt>:mime_version => "1.0"</tt>
# * <tt>:charset => "UTF-8",</tt>
# * <tt>:content_type => "text/plain",</tt>
# * <tt>:parts_order => [ "text/plain", "text/enriched", "text/html" ]</tt>
- #
+ #
# <tt>parts_order</tt> and <tt>charset</tt> are not actually valid <tt>Mail::Message</tt> header fields,
# but Action Mailer translates them appropriately and sets the correct values.
- #
+ #
# As you can pass in any header, you need to either quote the header as a string, or pass it in as
# an underscorised symbol, so the following will work:
- #
+ #
# class Notifier < ActionMailer::Base
# default 'Content-Transfer-Encoding' => '7bit',
# :content_description => 'This is a description'
# end
- #
+ #
# Finally, Action Mailer also supports passing <tt>Proc</tt> objects into the default hash, so you
# can define methods that evaluate as the message is being generated:
- #
+ #
# class Notifier < ActionMailer::Base
# default 'X-Special-Header' => Proc.new { my_method }
- #
+ #
# private
- #
+ #
# def my_method
# 'some complex call'
# end
# end
- #
+ #
# Note that the proc is evaluated right at the start of the mail message generation, so if you
- # set something in the defaults using a proc, and then set the same thing inside of your
+ # set something in the defaults using a proc, and then set the same thing inside of your
# mailer method, it will get over written by the mailer method.
- #
+ #
# = Configuration options
#
- # These options are specified on the class level, like
+ # These options are specified on the class level, like
# <tt>ActionMailer::Base.template_root = "/my/templates"</tt>
#
# * <tt>default</tt> - You can pass this in at a class level as well as within the class itself as
# per the above section.
- #
+ #
# * <tt>logger</tt> - the logger is used for generating information on the mailing run if available.
# Can be set to nil for no logging. Compatible with both Ruby's own Logger and Log4r loggers.
#
@@ -288,16 +289,16 @@ module ActionMailer #:nodoc:
# * <tt>deliveries</tt> - Keeps an array of all the emails sent out through the Action Mailer with
# <tt>delivery_method :test</tt>. Most useful for unit and functional testing.
#
- # * <tt>default_charset</tt> - This is now deprecated, use the +default+ method above to
+ # * <tt>default_charset</tt> - This is now deprecated, use the +default+ method above to
# set the default +:charset+.
#
- # * <tt>default_content_type</tt> - This is now deprecated, use the +default+ method above
+ # * <tt>default_content_type</tt> - This is now deprecated, use the +default+ method above
# to set the default +:content_type+.
#
- # * <tt>default_mime_version</tt> - This is now deprecated, use the +default+ method above
+ # * <tt>default_mime_version</tt> - This is now deprecated, use the +default+ method above
# to set the default +:mime_version+.
#
- # * <tt>default_implicit_parts_order</tt> - This is now deprecated, use the +default+ method above
+ # * <tt>default_implicit_parts_order</tt> - This is now deprecated, use the +default+ method above
# to set the default +:parts_order+. Parts Order is used when a message is built implicitly
# (i.e. multiple parts are assembled from templates which specify the content type in their
# filenames) this variable controls how the parts are ordered.
@@ -315,7 +316,7 @@ module ActionMailer #:nodoc:
include ActionMailer::OldApi
include ActionMailer::DeprecatedApi
-
+
delegate :register_observer, :to => Mail
delegate :register_interceptor, :to => Mail
@@ -418,17 +419,17 @@ module ActionMailer #:nodoc:
# Allows you to pass random and unusual headers to the new +Mail::Message+ object
# which will add them to itself.
- #
+ #
# headers['X-Special-Domain-Specific-Header'] = "SecretValue"
- #
+ #
# You can also pass a hash into headers of header field names and values, which
# will then be set on the Mail::Message object:
- #
+ #
# headers 'X-Special-Domain-Specific-Header' => "SecretValue",
# 'In-Reply-To' => incoming.message_id
- #
+ #
# The resulting Mail::Message will have the following in it's header:
- #
+ #
# X-Special-Domain-Specific-Header: SecretValue
def headers(args=nil)
if args
@@ -439,45 +440,45 @@ module ActionMailer #:nodoc:
end
# Allows you to add attachments to an email, like so:
- #
+ #
# mail.attachments['filename.jpg'] = File.read('/path/to/filename.jpg')
- #
+ #
# If you do this, then Mail will take the file name and work out the mime type
- # set the Content-Type, Content-Disposition, Content-Transfer-Encoding and
+ # set the Content-Type, Content-Disposition, Content-Transfer-Encoding and
# base64 encode the contents of the attachment all for you.
- #
+ #
# You can also specify overrides if you want by passing a hash instead of a string:
- #
+ #
# mail.attachments['filename.jpg'] = {:mime_type => 'application/x-gzip',
# :content => File.read('/path/to/filename.jpg')}
- #
+ #
# If you want to use a different encoding than Base64, you can pass an encoding in,
# but then it is up to you to pass in the content pre-encoded, and don't expect
# Mail to know how to decode this data:
- #
+ #
# file_content = SpecialEncode(File.read('/path/to/filename.jpg'))
# mail.attachments['filename.jpg'] = {:mime_type => 'application/x-gzip',
# :encoding => 'SpecialEncoding',
# :content => file_content }
- #
+ #
# You can also search for specific attachments:
- #
+ #
# # By Filename
# mail.attachments['filename.jpg'] #=> Mail::Part object or nil
- #
+ #
# # or by index
# mail.attachments[0] #=> Mail::Part (first attachment)
- #
+ #
def attachments
@_message.attachments
end
# The main method that creates the message and renders the email templates. There are
# two ways to call this method, with a block, or without a block.
- #
+ #
# Both methods accept a headers hash. This hash allows you to specify the most used headers
# in an email message, these are:
- #
+ #
# * <tt>:subject</tt> - The subject of the message, if this is omitted, Action Mailer will
# ask the Rails I18n class for a translated <tt>:subject</tt> in the scope of
# <tt>[:actionmailer, mailer_scope, action_name]</tt> or if this is missing, will translate the
@@ -491,25 +492,25 @@ module ActionMailer #:nodoc:
# addresses, or an array of addresses.
# * <tt>:reply_to</tt> - Who to set the Reply-To header of the email to.
# * <tt>:date</tt> - The date to say the email was sent on.
- #
- # You can set default values for any of the above headers (except :date) by using the <tt>default</tt>
+ #
+ # You can set default values for any of the above headers (except :date) by using the <tt>default</tt>
# class method:
- #
+ #
# class Notifier < ActionMailer::Base
# self.default :from => 'no-reply@test.lindsaar.net',
# :bcc => 'email_logger@test.lindsaar.net',
# :reply_to => 'bounces@test.lindsaar.net'
# end
- #
+ #
# If you need other headers not listed above, use the <tt>headers['name'] = value</tt> method.
#
# When a <tt>:return_path</tt> is specified as header, that value will be used as the 'envelope from'
# address for the Mail message. Setting this is useful when you want delivery notifications
- # sent to a different address than the one in <tt>:from</tt>. Mail will actually use the
+ # sent to a different address than the one in <tt>:from</tt>. Mail will actually use the
# <tt>:return_path</tt> in preference to the <tt>:sender</tt> in preference to the <tt>:from</tt>
# field for the 'envelope from' value.
#
- # If you do not pass a block to the +mail+ method, it will find all templates in the
+ # If you do not pass a block to the +mail+ method, it will find all templates in the
# view paths using by default the mailer name and the method name that it is being
# called from, it will then create parts for each of these templates intelligently,
# making educated guesses on correct content type and sequence, and return a fully
@@ -533,19 +534,19 @@ module ActionMailer #:nodoc:
# And now it will look for all templates at "app/views/notifications" with name "another".
#
# If you do pass a block, you can render specific templates of your choice:
- #
+ #
# mail(:to => 'mikel@test.lindsaar.net') do |format|
# format.text
# format.html
# end
- #
+ #
# You can even render text directly without using a template:
- #
+ #
# mail(:to => 'mikel@test.lindsaar.net') do |format|
# format.text { render :text => "Hello Mikel!" }
# format.html { render :text => "<h1>Hello Mikel!</h1>" }
# end
- #
+ #
# Which will render a <tt>multipart/alternative</tt> email with <tt>text/plain</tt> and
# <tt>text/html</tt> parts.
#
@@ -570,7 +571,7 @@ module ActionMailer #:nodoc:
default_values = self.class.default.merge(self.class.default) do |k,v|
v.respond_to?(:call) ? v.bind(self).call : v
end
-
+
# Handle defaults
headers = headers.reverse_merge(default_values)
headers[:subject] ||= default_i18n_subject
@@ -684,6 +685,28 @@ module ActionMailer #:nodoc:
container.add_part(part)
end
+ module DeprecatedUrlOptions
+ def default_url_options
+ deprecated_url_options
+ end
+
+ def default_url_options=(val)
+ deprecated_url_options
+ end
+
+ def deprecated_url_options
+ raise "You can no longer call ActionMailer::Base.default_url_options " \
+ "directly. You need to set config.action_mailer.default_url_options. " \
+ "If you are using ActionMailer standalone, you need to include the " \
+ "url_helpers of a router directly."
+ end
+ end
+
+ # This module will complain if the user tries to set default_url_options
+ # directly instead of through the config object. In ActionMailer's Railtie,
+ # we include the url_helpers of the router, which will override this module
+ extend DeprecatedUrlOptions
+
ActiveSupport.run_load_hooks(:action_mailer, self)
end
end
diff --git a/actionmailer/lib/action_mailer/railtie.rb b/actionmailer/lib/action_mailer/railtie.rb
index 882e078d1b..0730167a3e 100644
--- a/actionmailer/lib/action_mailer/railtie.rb
+++ b/actionmailer/lib/action_mailer/railtie.rb
@@ -5,10 +5,6 @@ module ActionMailer
class Railtie < Rails::Railtie
config.action_mailer = ActiveSupport::OrderedOptions.new
- initializer "action_mailer.url_for", :before => :load_environment_config do |app|
- ActiveSupport.on_load(:action_mailer) { include app.routes.url_helpers }
- end
-
require "action_mailer/railties/log_subscriber"
log_subscriber :action_mailer, ActionMailer::Railties::LogSubscriber.new
@@ -18,6 +14,8 @@ module ActionMailer
initializer "action_mailer.set_configs" do |app|
ActiveSupport.on_load(:action_mailer) do
+ include app.routes.url_helpers
+
app.config.action_mailer.each do |k,v|
send "#{k}=", v
end
diff --git a/actionmailer/test/old_base/mail_service_test.rb b/actionmailer/test/old_base/mail_service_test.rb
index 9da9132fe1..f91e7f893c 100644
--- a/actionmailer/test/old_base/mail_service_test.rb
+++ b/actionmailer/test/old_base/mail_service_test.rb
@@ -674,7 +674,7 @@ The body
EOF
mail = Mail.new(msg)
assert_equal "testing testing \326\244", mail.subject
- assert_equal "Subject: testing testing =?UTF-8?Q?_=D6=A4=?=\r\n", mail[:subject].encoded
+ assert_equal "Subject: =?UTF-8?Q?testing_testing_=D6=A4?=\r\n", mail[:subject].encoded
end
def test_unquote_7bit_subject
@@ -863,7 +863,7 @@ EOF
def test_multipart_with_utf8_subject
mail = TestMailer.multipart_with_utf8_subject(@recipient)
- regex = Regexp.escape('Subject: Foo =?UTF-8?Q?=C3=A1=C3=AB=C3=B4=?= =?UTF-8?Q?_=C3=AE=C3=BC=?=')
+ regex = Regexp.escape('Subject: =?UTF-8?Q?Foo_=C3=A1=C3=AB=C3=B4_=C3=AE=C3=BC?=')
assert_match(/#{regex}/, mail.encoded)
string = "Foo áëô îü"
assert_match(string, mail.subject)
@@ -871,7 +871,7 @@ EOF
def test_implicitly_multipart_with_utf8
mail = TestMailer.implicitly_multipart_with_utf8
- regex = Regexp.escape('Subject: Foo =?UTF-8?Q?=C3=A1=C3=AB=C3=B4=?= =?UTF-8?Q?_=C3=AE=C3=BC=?=')
+ regex = Regexp.escape('Subject: =?UTF-8?Q?Foo_=C3=A1=C3=AB=C3=B4_=C3=AE=C3=BC?=')
assert_match(/#{regex}/, mail.encoded)
string = "Foo áëô îü"
assert_match(string, mail.subject)
diff --git a/actionpack/Rakefile b/actionpack/Rakefile
index b9ace8658a..f3bd7dfc10 100644
--- a/actionpack/Rakefile
+++ b/actionpack/Rakefile
@@ -88,23 +88,4 @@ task :lines do
end
puts "Total: Lines #{total_lines}, LOC #{total_codelines}"
-end
-
-# Publishing ------------------------------------------------------
-
-task :update_scriptaculous do
- for js in %w( controls dragdrop effects )
- system("svn export --force http://dev.rubyonrails.org/svn/rails/spinoffs/scriptaculous/src/#{js}.js #{File.dirname(__FILE__)}/lib/action_view/helpers/javascripts/#{js}.js")
- end
-end
-
-desc "Updates actionpack to the latest version of the javascript spinoffs"
-task :update_js => [ :update_scriptaculous ]
-
-# Publishing ------------------------------------------------------
-
-desc "Publish the API documentation"
-task :pdoc => [:rdoc] do
- require 'rake/contrib/sshpublisher'
- Rake::SshDirPublisher.new("wrath.rubyonrails.org", "public_html/ap", "doc").upload
end \ No newline at end of file
diff --git a/actionpack/lib/action_dispatch/middleware/flash.rb b/actionpack/lib/action_dispatch/middleware/flash.rb
index 99b36366d6..adde183cdb 100644
--- a/actionpack/lib/action_dispatch/middleware/flash.rb
+++ b/actionpack/lib/action_dispatch/middleware/flash.rb
@@ -49,6 +49,16 @@ module ActionDispatch
def [](k)
@flash[k]
end
+
+ # Convenience accessor for flash.now[:alert]=
+ def alert=(message)
+ self[:alert] = message
+ end
+
+ # Convenience accessor for flash.now[:notice]=
+ def notice=(message)
+ self[:notice] = message
+ end
end
class FlashHash < Hash
diff --git a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb
index 88ba941676..7114f42003 100644
--- a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb
+++ b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb
@@ -177,7 +177,7 @@ module ActionDispatch
if key.blank?
raise ArgumentError, 'A key is required to write a ' +
'cookie containing the session data. Use ' +
- 'config.action_controller.session_store :cookie_store, { :key => ' +
+ 'config.session_store :cookie_store, { :key => ' +
'"_myapp_session" } in 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 12a93d6a24..2dd2ec9fe9 100644
--- a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
+++ b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
@@ -133,14 +133,10 @@ module ActionDispatch
return unless logger
ActiveSupport::Deprecation.silence do
- if ActionView::Template::Error === exception
- logger.fatal(exception.to_s)
- else
- logger.fatal(
- "\n#{exception.class} (#{exception.message}):\n " +
- clean_backtrace(exception).join("\n ") + "\n\n"
- )
- end
+ message = "\n#{exception.class} (#{exception.message}):\n"
+ message << exception.annoted_source_code if exception.respond_to?(:annoted_source_code)
+ message << exception.backtrace.join("\n ")
+ logger.fatal("#{message}\n\n")
end
end
diff --git a/actionpack/lib/action_dispatch/testing/assertions/selector.rb b/actionpack/lib/action_dispatch/testing/assertions/selector.rb
index a6b1126e2b..9deabf5b3c 100644
--- a/actionpack/lib/action_dispatch/testing/assertions/selector.rb
+++ b/actionpack/lib/action_dispatch/testing/assertions/selector.rb
@@ -298,10 +298,14 @@ module ActionDispatch
# found one but expecting two.
message ||= content_mismatch if matches.empty?
# Test minimum/maximum occurrence.
- min, max = equals[:minimum], equals[:maximum]
- message = message || %(Expected #{count_description(min, max)} matching "#{selector.to_s}", found #{matches.size}.)
- assert matches.size >= min, message if min
- assert matches.size <= max, message if max
+ min, max, count = equals[:minimum], equals[:maximum], equals[:count]
+ message = message || %(Expected #{count_description(min, max, count)} matching "#{selector.to_s}", found #{matches.size}.)
+ if count
+ assert matches.size == count, message
+ else
+ assert matches.size >= min, message if min
+ assert matches.size <= max, message if max
+ end
# If a block is given call that block. Set @selected to allow
# nested assert_select, which can be nested several levels deep.
@@ -318,11 +322,13 @@ module ActionDispatch
matches
end
- def count_description(min, max) #:nodoc:
+ def count_description(min, max, count) #:nodoc:
pluralize = lambda {|word, quantity| word << (quantity == 1 ? '' : 's')}
if min && max && (max != min)
"between #{min} and #{max} elements"
+ elsif min && max && max == min && count
+ "exactly #{count} #{pluralize['element', min]}"
elsif min && !(min == 1 && max == 1)
"at least #{min} #{pluralize['element', min]}"
elsif max
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
index 8731ed0ef3..e1fbc118d5 100644
--- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
@@ -620,7 +620,7 @@ module ActionView
options.symbolize_keys!
src = options[:src] = path_to_image(source)
- options[:alt] ||= File.basename(src, '.*').capitalize
+ options[:alt] = options.fetch(:alt){ File.basename(src, '.*').capitalize }
if size = options.delete(:size)
options[:width], options[:height] = size.split("x") if size =~ %r{^\d+x\d+$}
diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb
index 6e26ae6c29..414a5d4cd9 100644
--- a/actionpack/lib/action_view/helpers/form_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_helper.rb
@@ -573,8 +573,19 @@ module ActionView
# label(:post, :privacy, "Public Post", :value => "public")
# # => <label for="post_privacy_public">Public Post</label>
#
- def label(object_name, method, text = nil, options = {})
- InstanceTag.new(object_name, method, self, options.delete(:object)).to_label_tag(text, options)
+ # label(:post, :terms) do
+ # 'Accept <a href="/terms">Terms</a>.'
+ # end
+ def label(object_name, method, content_or_options = nil, options = nil, &block)
+ if block_given?
+ options = content_or_options if content_or_options.is_a?(Hash)
+ text = nil
+ else
+ text = content_or_options
+ end
+
+ options ||= {}
+ InstanceTag.new(object_name, method, self, options.delete(:object)).to_label_tag(text, options, &block)
end
# Returns an input tag of the "text" type tailored for accessing a specified attribute (identified by +method+) on an object
@@ -823,7 +834,7 @@ module ActionView
module InstanceTagMethods #:nodoc:
extend ActiveSupport::Concern
- include Helpers::TagHelper, Helpers::FormTagHelper
+ include Helpers::CaptureHelper, Context, Helpers::TagHelper, Helpers::FormTagHelper
attr_reader :method_name, :object_name
@@ -844,28 +855,38 @@ module ActionView
end
end
- def to_label_tag(text = nil, options = {})
+ def to_label_tag(text = nil, options = {}, &block)
options = options.stringify_keys
tag_value = options.delete("value")
name_and_id = options.dup
- name_and_id["id"] = name_and_id["for"]
+
+ if name_and_id["for"]
+ name_and_id["id"] = name_and_id["for"]
+ else
+ name_and_id.delete("id")
+ end
+
add_default_name_and_id_for_value(tag_value, name_and_id)
options.delete("index")
options["for"] ||= name_and_id["id"]
- content = if text.blank?
- I18n.t("helpers.label.#{object_name}.#{method_name}", :default => "").presence
+ if block_given?
+ label_tag(name_and_id["id"], options, &block)
else
- text.to_s
- end
+ content = if text.blank?
+ I18n.t("helpers.label.#{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 ||= if object && object.class.respond_to?(:human_attribute_name)
+ object.class.human_attribute_name(method_name)
+ end
- content ||= method_name.humanize
+ content ||= method_name.humanize
- label_tag(name_and_id["id"], content, options)
+ label_tag(name_and_id["id"], content, options)
+ end
end
def to_input_field_tag(field_type, options = {})
@@ -1012,7 +1033,7 @@ module ActionView
pretty_tag_value = tag_value.to_s.gsub(/\s/, "_").gsub(/\W/, "").downcase
specified_id = options["id"]
add_default_name_and_id(options)
- options["id"] += "_#{pretty_tag_value}" unless specified_id
+ options["id"] += "_#{pretty_tag_value}" if specified_id.blank? && options["id"].present?
else
add_default_name_and_id(options)
end
@@ -1021,14 +1042,14 @@ module ActionView
def add_default_name_and_id(options)
if options.has_key?("index")
options["name"] ||= tag_name_with_index(options["index"])
- options["id"] ||= tag_id_with_index(options["index"])
+ options["id"] = options.fetch("id", tag_id_with_index(options["index"]))
options.delete("index")
elsif defined?(@auto_index)
options["name"] ||= tag_name_with_index(@auto_index)
- options["id"] ||= tag_id_with_index(@auto_index)
+ options["id"] = options.fetch("id", tag_id_with_index(@auto_index))
else
options["name"] ||= tag_name + (options.has_key?('multiple') ? '[]' : '')
- options["id"] ||= tag_id
+ options["id"] = options.fetch("id", tag_id)
end
end
@@ -1137,8 +1158,8 @@ module ActionView
@template.fields_for(name, *args, &block)
end
- def label(method, text = nil, options = {})
- @template.label(@object_name, method, text, objectify_options(options))
+ def label(method, text = nil, options = {}, &block)
+ @template.label(@object_name, method, text, objectify_options(options), &block)
end
def check_box(method, options = {}, checked_value = "1", unchecked_value = "0")
diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb
index 8f8db548c3..fe71d2cdf7 100644
--- a/actionpack/lib/action_view/helpers/form_options_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_options_helper.rb
@@ -270,6 +270,15 @@ module ActionView
# options_for_select([ "VISA", "MasterCard", "Discover" ], ["VISA", "Discover"])
# <option selected="selected">VISA</option>\n<option>MasterCard</option>\n<option selected="selected">Discover</option>
#
+ # You can optionally provide html attributes as the last element of the array.
+ #
+ # Examples:
+ # options_for_select([ "Denmark", ["USA", {:class=>'bold'}], "Sweden" ], ["USA", "Sweden"])
+ # <option value="Denmark">Denmark</option>\n<option value="USA" class="bold" selected="selected">USA</option>\n<option value="Sweden" selected="selected">Sweden</option>
+ #
+ # options_for_select([["Dollar", "$", {:class=>"bold"}], ["Kroner", "DKK", {:onclick => "alert('HI');"}]])
+ # <option value="$" class="bold">Dollar</option>\n<option value="DKK" onclick="alert('HI');">Kroner</option>
+ #
# If you wish to specify disabled option tags, set +selected+ to be a hash, with <tt>:disabled</tt> being either a value
# or array of values to be disabled. In this case, you can use <tt>:selected</tt> to specify selected option tags.
#
@@ -291,10 +300,11 @@ module ActionView
selected, disabled = extract_selected_and_disabled(selected)
options_for_select = container.inject([]) do |options, element|
+ html_attributes = option_html_attributes(element)
text, value = option_text_and_value(element)
selected_attribute = ' selected="selected"' if option_value_selected?(value, selected)
disabled_attribute = ' disabled="disabled"' if disabled && option_value_selected?(value, disabled)
- options << %(<option value="#{html_escape(value.to_s)}"#{selected_attribute}#{disabled_attribute}>#{html_escape(text.to_s)}</option>)
+ options << %(<option value="#{html_escape(value.to_s)}"#{selected_attribute}#{disabled_attribute}#{html_attributes}>#{html_escape(text.to_s)}</option>)
end
options_for_select.join("\n").html_safe
@@ -486,9 +496,22 @@ module ActionView
end
private
+ def option_html_attributes(element)
+ return "" unless Array === element
+ html_attributes = []
+ element.select { |e| Hash === e }.reduce({}, :merge).each do |k, v|
+ html_attributes << " #{k}=\"#{html_escape(v.to_s)}\""
+ end
+ html_attributes.join
+ end
+
def option_text_and_value(option)
# Options are [text, value] pairs or strings used for both.
- if !option.is_a?(String) and option.respond_to?(:first) and option.respond_to?(:last)
+ case
+ when Array === option
+ option = option.reject { |e| Hash === e }
+ [option.first, option.last]
+ when !option.is_a?(String) && option.respond_to?(:first) && option.respond_to?(:last)
[option.first, option.last]
else
[option, option]
diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb
index b840f77fb5..2a3f826c15 100644
--- a/actionpack/lib/action_view/helpers/form_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb
@@ -142,7 +142,7 @@ module ActionView
tag :input, { "type" => "text", "name" => name, "id" => sanitize_to_id(name), "value" => value }.update(options.stringify_keys)
end
- # Creates a label field
+ # Creates a label element. Accepts a block.
#
# ==== Options
# * Creates standard HTML attributes for the tag.
@@ -156,8 +156,12 @@ module ActionView
#
# label_tag 'name', nil, :class => 'small_label'
# # => <label for="name" class="small_label">Name</label>
- def label_tag(name, text = nil, options = {})
- content_tag :label, text || name.to_s.humanize, { "for" => sanitize_to_id(name) }.update(options.stringify_keys)
+ def label_tag(name = nil, content_or_options = nil, options = nil, &block)
+ options = content_or_options if block_given? && content_or_options.is_a?(Hash)
+ options ||= {}
+ options.stringify_keys!
+ options["for"] = sanitize_to_id(name) unless name.blank? || options.has_key?("for")
+ content_tag :label, content_or_options || name.to_s.humanize, options, &block
end
# Creates a hidden form input field used to transmit data that would be lost due to HTTP's statelessness or
diff --git a/actionpack/lib/action_view/render/partials.rb b/actionpack/lib/action_view/render/partials.rb
index 974345633c..85f67d4f14 100644
--- a/actionpack/lib/action_view/render/partials.rb
+++ b/actionpack/lib/action_view/render/partials.rb
@@ -241,15 +241,21 @@ module ActionView
end
def collection_with_template(template = @template)
- segments, locals, as, template = [], @locals, @options[:as] || @template.variable_name, @template
+ segments, locals, template = [], @locals, @template
- counter_name = template.counter_name
- locals[counter_name] = -1
+ if @options[:as]
+ as = @options[:as]
+ counter = "#{as}_counter".to_sym
+ else
+ as = template.variable_name
+ counter = template.counter_name
+ end
+
+ locals[counter] = -1
@collection.each do |object|
- locals[counter_name] += 1
+ locals[counter] += 1
locals[as] = object
-
segments << template.render(@view, locals)
end
@@ -257,13 +263,18 @@ module ActionView
end
def collection_without_template(collection_paths = @collection_paths)
- segments, locals, as = [], @locals, @options[:as]
- index, template = -1, nil
+ segments, locals = [], @locals
+ index, template = -1, nil
+
+ if @options[:as]
+ as = @options[:as]
+ counter = "#{as}_counter"
+ end
@collection.each_with_index do |object, i|
template = find_template(collection_paths[i])
- locals[template.counter_name] = (index += 1)
locals[as || template.variable_name] = object
+ locals[counter || template.counter_name] = (index += 1)
segments << template.render(@view, locals)
end
diff --git a/actionpack/lib/action_view/template/error.rb b/actionpack/lib/action_view/template/error.rb
index a947d746e3..6866eabf77 100644
--- a/actionpack/lib/action_view/template/error.rb
+++ b/actionpack/lib/action_view/template/error.rb
@@ -84,9 +84,8 @@ module ActionView
end
end
- def to_s
- "\n#{self.class} (#{message}) #{source_location}:\n\n" +
- "#{source_extract(4)}\n #{backtrace.join("\n ")}\n\n"
+ def annoted_source_code
+ source_extract(4)
end
private
diff --git a/actionpack/lib/action_view/template/handlers/erb.rb b/actionpack/lib/action_view/template/handlers/erb.rb
index 705c2bf82e..237746437a 100644
--- a/actionpack/lib/action_view/template/handlers/erb.rb
+++ b/actionpack/lib/action_view/template/handlers/erb.rb
@@ -28,7 +28,7 @@ module ActionView
src << "@output_buffer.safe_concat('" << escape_text(text) << "');"
end
- BLOCK_EXPR = /(do|\{)(\s*\|[^|]*\|)?\s*\Z/
+ BLOCK_EXPR = /\s+(do|\{)(\s*\|[^|]*\|)?\s*\Z/
def add_expr_literal(src, code)
if code =~ BLOCK_EXPR
diff --git a/actionpack/test/controller/assert_select_test.rb b/actionpack/test/controller/assert_select_test.rb
index 4ef6fa4000..7012c4c9b0 100644
--- a/actionpack/test/controller/assert_select_test.rb
+++ b/actionpack/test/controller/assert_select_test.rb
@@ -80,10 +80,15 @@ class AssertSelectTest < ActionController::TestCase
def test_assert_select
render_html %Q{<div id="1"></div><div id="2"></div>}
assert_select "div", 2
- assert_failure(/Expected at least 3 elements matching \"div\", found 2/) { assert_select "div", 3 }
assert_failure(/Expected at least 1 element matching \"p\", found 0/) { assert_select "p" }
end
+ def test_equality_integer
+ render_html %Q{<div id="1"></div><div id="2"></div>}
+ assert_failure(/Expected exactly 3 elements matching \"div\", found 2/) { assert_select "div", 3 }
+ assert_failure(/Expected exactly 0 elements matching \"div\", found 2/) { assert_select "div", 0 }
+ end
+
def test_equality_true_false
render_html %Q{<div id="1"></div><div id="2"></div>}
assert_nothing_raised { assert_select "div" }
@@ -94,6 +99,11 @@ class AssertSelectTest < ActionController::TestCase
assert_nothing_raised { assert_select "p", false }
end
+ def test_equality_false_message
+ render_html %Q{<div id="1"></div><div id="2"></div>}
+ assert_failure(/Expected exactly 0 elements matching \"div\", found 2/) { assert_select "div", false }
+ end
+
def test_equality_string_and_regexp
render_html %Q{<div id="1">foo</div><div id="2">foo</div>}
assert_nothing_raised { assert_select "div", "foo" }
@@ -128,7 +138,7 @@ class AssertSelectTest < ActionController::TestCase
def test_counts
render_html %Q{<div id="1">foo</div><div id="2">foo</div>}
assert_nothing_raised { assert_select "div", 2 }
- assert_failure(/Expected at least 3 elements matching \"div\", found 2/) do
+ assert_failure(/Expected exactly 3 elements matching \"div\", found 2/) do
assert_select "div", 3
end
assert_nothing_raised { assert_select "div", 1..2 }
@@ -136,7 +146,7 @@ class AssertSelectTest < ActionController::TestCase
assert_select "div", 3..4
end
assert_nothing_raised { assert_select "div", :count=>2 }
- assert_failure(/Expected at least 3 elements matching \"div\", found 2/) do
+ assert_failure(/Expected exactly 3 elements matching \"div\", found 2/) do
assert_select "div", :count=>3
end
assert_nothing_raised { assert_select "div", :minimum=>1 }
@@ -201,7 +211,7 @@ class AssertSelectTest < ActionController::TestCase
assert_nothing_raised { assert_select "div", "foo" }
assert_nothing_raised { assert_select "div", "bar" }
assert_nothing_raised { assert_select "div", /\w*/ }
- assert_nothing_raised { assert_select "div", /\w*/, :count=>2 }
+ assert_nothing_raised { assert_select "div", :text => /\w*/, :count=>2 }
assert_raise(Assertion) { assert_select "div", :text=>"foo", :count=>2 }
assert_nothing_raised { assert_select "div", :html=>"<span>bar</span>" }
assert_nothing_raised { assert_select "div", :html=>"<span>bar</span>" }
@@ -266,8 +276,8 @@ class AssertSelectTest < ActionController::TestCase
def test_css_select
render_html %Q{<div id="1"></div><div id="2"></div>}
- assert 2, css_select("div").size
- assert 0, css_select("p").size
+ assert_equal 2, css_select("div").size
+ assert_equal 0, css_select("p").size
end
def test_nested_css_select
diff --git a/actionpack/test/controller/capture_test.rb b/actionpack/test/controller/capture_test.rb
index 06a5af6b32..d1dbd535c4 100644
--- a/actionpack/test/controller/capture_test.rb
+++ b/actionpack/test/controller/capture_test.rb
@@ -61,6 +61,11 @@ class CaptureTest < ActionController::TestCase
assert_equal expected_content_for_output, @response.body
end
+ def test_proper_block_detection
+ @todo = "some todo"
+ get :proper_block_detection
+ end
+
private
def expected_content_for_output
"<title>Putting stuff in the title!</title>\n\nGreat stuff!"
diff --git a/actionpack/test/controller/flash_test.rb b/actionpack/test/controller/flash_test.rb
index 3c651ebebc..c662ce264b 100644
--- a/actionpack/test/controller/flash_test.rb
+++ b/actionpack/test/controller/flash_test.rb
@@ -81,6 +81,16 @@ class FlashTest < ActionController::TestCase
redirect_to '/somewhere', :notice => "Good luck in the somewheres!"
end
+ def render_with_flash_now_alert
+ flash.now.alert = "Beware the nowheres now!"
+ render :inline => "hello"
+ end
+
+ def render_with_flash_now_notice
+ flash.now.notice = "Good luck in the somewheres now!"
+ render :inline => "hello"
+ end
+
def redirect_with_other_flashes
redirect_to '/wonderland', :flash => { :joyride => "Horses!" }
end
@@ -183,6 +193,16 @@ class FlashTest < ActionController::TestCase
assert_equal "Good luck in the somewheres!", @controller.send(:flash)[:notice]
end
+ def test_render_with_flash_now_alert
+ get :render_with_flash_now_alert
+ assert_equal "Beware the nowheres now!", @controller.send(:flash)[:alert]
+ end
+
+ def test_render_with_flash_now_notice
+ get :render_with_flash_now_notice
+ assert_equal "Good luck in the somewheres now!", @controller.send(:flash)[:notice]
+ end
+
def test_redirect_to_with_other_flashes
get :redirect_with_other_flashes
assert_equal "Horses!", @controller.send(:flash)[:joyride]
diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb
index 2f3997518f..52049f2a8a 100644
--- a/actionpack/test/controller/render_test.rb
+++ b/actionpack/test/controller/render_test.rb
@@ -547,6 +547,10 @@ class TestController < ActionController::Base
render :partial => "customer_counter", :collection => [ Customer.new("david"), Customer.new("mary") ]
end
+ def partial_collection_with_as_and_counter
+ render :partial => "customer_counter_with_as", :collection => [ Customer.new("david"), Customer.new("mary") ], :as => :client
+ end
+
def partial_collection_with_locals
render :partial => "customer_greeting", :collection => [ Customer.new("david"), Customer.new("mary") ], :locals => { :greeting => "Bonjour" }
end
@@ -1242,6 +1246,11 @@ class RenderTest < ActionController::TestCase
assert_equal "david0mary1", @response.body
end
+ def test_partial_collection_with_as_and_counter
+ get :partial_collection_with_as_and_counter
+ assert_equal "david0mary1", @response.body
+ end
+
def test_partial_collection_with_locals
get :partial_collection_with_locals
assert_equal "Bonjour: davidBonjour: mary", @response.body
@@ -1379,7 +1388,7 @@ class EtagRenderTest < ActionController::TestCase
def test_render_against_etag_request_should_have_no_content_length_when_match
@request.if_none_match = etag_for("hello david")
get :render_hello_world_from_variable
- assert !@response.headers.has_key?("Content-Length"), @response.headers['Content-Length']
+ assert !@response.headers.has_key?("Content-Length")
end
def test_render_against_etag_request_should_200_when_no_match
@@ -1515,4 +1524,4 @@ class LastModifiedRenderTest < ActionController::TestCase
get :conditional_hello_with_bangs
assert_response :success
end
-end \ No newline at end of file
+end
diff --git a/actionpack/test/dispatch/request/xml_params_parsing_test.rb b/actionpack/test/dispatch/request/xml_params_parsing_test.rb
index f2ce2c5b93..d44c642420 100644
--- a/actionpack/test/dispatch/request/xml_params_parsing_test.rb
+++ b/actionpack/test/dispatch/request/xml_params_parsing_test.rb
@@ -21,7 +21,7 @@ class XmlParamsParsingTest < ActionController::IntegrationTest
def call(env)
bar = env['action_dispatch.request.request_parameters']['foo']
result = "<ok>#{bar}</ok>"
- [200, {"Content-Type" => "application/xml", "Content-Length" => result.length.to_s}, result]
+ [200, {"Content-Type" => "application/xml", "Content-Length" => result.length.to_s}, [result]]
end
end
req = Rack::MockRequest.new(ActionDispatch::ParamsParser.new(Linted.new))
diff --git a/actionpack/test/fixtures/test/_customer_counter_with_as.erb b/actionpack/test/fixtures/test/_customer_counter_with_as.erb
new file mode 100644
index 0000000000..1241eb604d
--- /dev/null
+++ b/actionpack/test/fixtures/test/_customer_counter_with_as.erb
@@ -0,0 +1 @@
+<%= client.name %><%= client_counter %> \ No newline at end of file
diff --git a/actionpack/test/fixtures/test/proper_block_detection.erb b/actionpack/test/fixtures/test/proper_block_detection.erb
new file mode 100644
index 0000000000..23564dbcee
--- /dev/null
+++ b/actionpack/test/fixtures/test/proper_block_detection.erb
@@ -0,0 +1 @@
+<%= @todo %>
diff --git a/actionpack/test/template/asset_tag_helper_test.rb b/actionpack/test/template/asset_tag_helper_test.rb
index 124bf734ac..b6a6f52876 100644
--- a/actionpack/test/template/asset_tag_helper_test.rb
+++ b/actionpack/test/template/asset_tag_helper_test.rb
@@ -154,7 +154,8 @@ class AssetTagHelperTest < ActionView::TestCase
%(image_tag(".pdf.png")) => %(<img alt=".pdf" src="/images/.pdf.png" />),
%(image_tag("http://www.rubyonrails.com/images/rails.png")) => %(<img alt="Rails" src="http://www.rubyonrails.com/images/rails.png" />),
%(image_tag("mouse.png", :mouseover => "/images/mouse_over.png")) => %(<img alt="Mouse" onmouseover="this.src='/images/mouse_over.png'" onmouseout="this.src='/images/mouse.png'" src="/images/mouse.png" />),
- %(image_tag("mouse.png", :mouseover => image_path("mouse_over.png"))) => %(<img alt="Mouse" onmouseover="this.src='/images/mouse_over.png'" onmouseout="this.src='/images/mouse.png'" src="/images/mouse.png" />)
+ %(image_tag("mouse.png", :mouseover => image_path("mouse_over.png"))) => %(<img alt="Mouse" onmouseover="this.src='/images/mouse_over.png'" onmouseout="this.src='/images/mouse.png'" src="/images/mouse.png" />),
+ %(image_tag("mouse.png", :alt => nil)) => %(<img src="/images/mouse.png" />)
}
FaviconLinkToTag = {
diff --git a/actionpack/test/template/capture_helper_test.rb b/actionpack/test/template/capture_helper_test.rb
index bf541c17d3..9f3d68a639 100644
--- a/actionpack/test/template/capture_helper_test.rb
+++ b/actionpack/test/template/capture_helper_test.rb
@@ -74,7 +74,7 @@ class CaptureHelperTest < ActionView::TestCase
@av.output_buffer.force_encoding(alt_encoding)
@av.with_output_buffer do
- assert alt_encoding, @av.output_buffer.encoding
+ assert_equal alt_encoding, @av.output_buffer.encoding
end
end
end
diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb
index 2234fbece9..d1e1338a17 100644
--- a/actionpack/test/template/form_helper_test.rb
+++ b/actionpack/test/template/form_helper_test.rb
@@ -139,6 +139,10 @@ class FormHelperTest < ActionView::TestCase
assert_dom_equal('<label for="post_title_great_title">The title goes here</label>', label("post", "title", "The title goes here", :value => "great title"))
end
+ def test_label_with_block
+ assert_dom_equal('<label for="post_title">The title, please:</label>', label(:post, :title) { "The title, please:" })
+ end
+
def test_text_field
assert_dom_equal(
'<input id="post_title" name="post[title]" size="30" type="text" value="Hello World" />', text_field("post", "title")
@@ -419,6 +423,90 @@ class FormHelperTest < ActionView::TestCase
check_box("post", "secret", :id => "i mean it")
end
+ def test_nil_id
+ assert_dom_equal(
+ '<input name="post[title]" size="30" type="text" value="Hello World" />', text_field("post", "title", "id" => nil)
+ )
+ assert_dom_equal(
+ '<textarea cols="40" name="post[body]" rows="20">Back to the hill and over it again!</textarea>',
+ text_area("post", "body", "id" => nil)
+ )
+ assert_dom_equal(
+ '<input name="post[secret]" type="hidden" value="0" /><input checked="checked" name="post[secret]" type="checkbox" value="1" />',
+ check_box("post", "secret", "id" => nil)
+ )
+ assert_dom_equal(
+ '<input type="radio" name="post[secret]" value="0" />',
+ radio_button("post", "secret", "0", "id" => nil)
+ )
+ assert_dom_equal(
+ '<select name="post[secret]"></select>',
+ select("post", "secret", [], {}, "id" => nil)
+ )
+ assert_dom_equal text_field("post", "title", "id" => nil),
+ text_field("post", "title", :id => nil)
+ assert_dom_equal text_area("post", "body", "id" => nil),
+ text_area("post", "body", :id => nil)
+ assert_dom_equal check_box("post", "secret", "id" => nil),
+ check_box("post", "secret", :id => nil)
+ assert_dom_equal radio_button("post", "secret", "0", "id" => nil),
+ radio_button("post", "secret", "0", :id => nil)
+ end
+
+ def test_index
+ assert_dom_equal(
+ '<input name="post[5][title]" size="30" id="post_5_title" type="text" value="Hello World" />',
+ text_field("post", "title", "index" => 5)
+ )
+ assert_dom_equal(
+ '<textarea cols="40" name="post[5][body]" id="post_5_body" rows="20">Back to the hill and over it again!</textarea>',
+ text_area("post", "body", "index" => 5)
+ )
+ assert_dom_equal(
+ '<input name="post[5][secret]" type="hidden" value="0" /><input checked="checked" name="post[5][secret]" type="checkbox" value="1" id="post_5_secret" />',
+ check_box("post", "secret", "index" => 5)
+ )
+ assert_dom_equal(
+ text_field("post", "title", "index" => 5),
+ text_field("post", "title", "index" => 5)
+ )
+ assert_dom_equal(
+ text_area("post", "body", "index" => 5),
+ text_area("post", "body", "index" => 5)
+ )
+ assert_dom_equal(
+ check_box("post", "secret", "index" => 5),
+ check_box("post", "secret", "index" => 5)
+ )
+ end
+
+ def test_index_with_nil_id
+ assert_dom_equal(
+ '<input name="post[5][title]" size="30" type="text" value="Hello World" />',
+ text_field("post", "title", "index" => 5, 'id' => nil)
+ )
+ assert_dom_equal(
+ '<textarea cols="40" name="post[5][body]" rows="20">Back to the hill and over it again!</textarea>',
+ text_area("post", "body", "index" => 5, 'id' => nil)
+ )
+ assert_dom_equal(
+ '<input name="post[5][secret]" type="hidden" value="0" /><input checked="checked" name="post[5][secret]" type="checkbox" value="1" />',
+ check_box("post", "secret", "index" => 5, 'id' => nil)
+ )
+ assert_dom_equal(
+ text_field("post", "title", "index" => 5, 'id' => nil),
+ text_field("post", "title", :index => 5, :id => nil)
+ )
+ assert_dom_equal(
+ text_area("post", "body", "index" => 5, 'id' => nil),
+ text_area("post", "body", :index => 5, :id => nil)
+ )
+ assert_dom_equal(
+ check_box("post", "secret", "index" => 5, 'id' => nil),
+ check_box("post", "secret", :index => 5, :id => nil)
+ )
+ end
+
def test_auto_index
pid = @post.id
assert_dom_equal(
@@ -445,10 +533,33 @@ class FormHelperTest < ActionView::TestCase
)
end
+ def test_auto_index_with_nil_id
+ pid = @post.id
+ assert_dom_equal(
+ "<input name=\"post[#{pid}][title]\" size=\"30\" type=\"text\" value=\"Hello World\" />",
+ text_field("post[]","title", :id => nil)
+ )
+ assert_dom_equal(
+ "<textarea cols=\"40\" name=\"post[#{pid}][body]\" rows=\"20\">Back to the hill and over it again!</textarea>",
+ text_area("post[]", "body", :id => nil)
+ )
+ assert_dom_equal(
+ "<input name=\"post[#{pid}][secret]\" type=\"hidden\" value=\"0\" /><input checked=\"checked\" name=\"post[#{pid}][secret]\" type=\"checkbox\" value=\"1\" />",
+ check_box("post[]", "secret", :id => nil)
+ )
+ assert_dom_equal(
+"<input checked=\"checked\" name=\"post[#{pid}][title]\" type=\"radio\" value=\"Hello World\" />",
+ radio_button("post[]", "title", "Hello World", :id => nil)
+ )
+ assert_dom_equal("<input name=\"post[#{pid}][title]\" type=\"radio\" value=\"Goodbye World\" />",
+ radio_button("post[]", "title", "Goodbye World", :id => nil)
+ )
+ end
+
def test_form_for
assert_deprecated do
form_for(:post, @post, :html => { :id => 'create-post' }) do |f|
- concat f.label(:title)
+ concat f.label(:title) { "The Title" }
concat f.text_field(:title)
concat f.text_area(:body)
concat f.check_box(:secret)
@@ -458,7 +569,7 @@ class FormHelperTest < ActionView::TestCase
expected =
"<form action='http://www.example.com' id='create-post' method='post'>" +
- "<label for='post_title'>Title</label>" +
+ "<label for='post_title'>The Title</label>" +
"<input name='post[title]' size='30' type='text' id='post_title' value='Hello World' />" +
"<textarea name='post[body]' id='post_body' rows='20' cols='40'>Back to the hill and over it again!</textarea>" +
"<input name='post[secret]' type='hidden' value='0' />" +
@@ -485,7 +596,7 @@ class FormHelperTest < ActionView::TestCase
"<input name='other_name[title]' size='30' id='other_name_title' value='Hello World' type='text' />" +
"<textarea name='other_name[body]' id='other_name_body' rows='20' cols='40'>Back to the hill and over it again!</textarea>" +
"<input name='other_name[secret]' value='0' type='hidden' />" +
- "<input name='other_name[secret]' checked='checked' id='other_name_secret' value='1' type='checkbox' />" +
+ "<input name='other_name[secret]' checked='checked' id='other_name_secret' value='1' type='checkbox' />" +
"<input name='commit' id='other_name_submit' value='Create post' type='submit' /></form>"
assert_dom_equal expected, output_buffer
diff --git a/actionpack/test/template/form_options_helper_test.rb b/actionpack/test/template/form_options_helper_test.rb
index 98503c32fd..19b73aa810 100644
--- a/actionpack/test/template/form_options_helper_test.rb
+++ b/actionpack/test/template/form_options_helper_test.rb
@@ -767,6 +767,62 @@ class FormOptionsHelperTest < ActionView::TestCase
html
end
+ def test_options_for_select_with_element_attributes
+ assert_dom_equal(
+ "<option value=\"&lt;Denmark&gt;\" class=\"bold\">&lt;Denmark&gt;</option>\n<option value=\"USA\" onclick=\"alert('Hello World');\">USA</option>\n<option value=\"Sweden\">Sweden</option>\n<option value=\"Germany\">Germany</option>",
+ options_for_select([ [ "<Denmark>", { :class => 'bold' } ], [ "USA", { :onclick => "alert('Hello World');" } ], [ "Sweden" ], "Germany" ])
+ )
+ end
+
+ def test_options_for_select_with_element_attributes_and_selection
+ assert_dom_equal(
+ "<option value=\"&lt;Denmark&gt;\">&lt;Denmark&gt;</option>\n<option value=\"USA\" class=\"bold\" selected=\"selected\">USA</option>\n<option value=\"Sweden\">Sweden</option>",
+ options_for_select([ "<Denmark>", [ "USA", { :class => 'bold' } ], "Sweden" ], "USA")
+ )
+ end
+
+ def test_options_for_select_with_element_attributes_and_selection_array
+ assert_dom_equal(
+ "<option value=\"&lt;Denmark&gt;\">&lt;Denmark&gt;</option>\n<option value=\"USA\" class=\"bold\" selected=\"selected\">USA</option>\n<option value=\"Sweden\" selected=\"selected\">Sweden</option>",
+ options_for_select([ "<Denmark>", [ "USA", { :class => 'bold' } ], "Sweden" ], [ "USA", "Sweden" ])
+ )
+ end
+
+ def test_option_html_attributes_from_without_hash
+ assert_dom_equal(
+ "",
+ option_html_attributes([ 'foo', 'bar' ])
+ )
+ end
+
+ def test_option_html_attributes_with_single_element_hash
+ assert_dom_equal(
+ " class=\"fancy\"",
+ option_html_attributes([ 'foo', 'bar', { :class => 'fancy' } ])
+ )
+ end
+
+ def test_option_html_attributes_with_multiple_element_hash
+ assert_dom_equal(
+ " class=\"fancy\" onclick=\"alert('Hello World');\"",
+ option_html_attributes([ 'foo', 'bar', { :class => 'fancy', 'onclick' => "alert('Hello World');" } ])
+ )
+ end
+
+ def test_option_html_attributes_with_multiple_hashes
+ assert_dom_equal(
+ " class=\"fancy\" onclick=\"alert('Hello World');\"",
+ option_html_attributes([ 'foo', 'bar', { :class => 'fancy' }, { 'onclick' => "alert('Hello World');" } ])
+ )
+ end
+
+ def test_option_html_attributes_with_special_characters
+ assert_dom_equal(
+ " onclick=\"alert(&quot;&lt;code&gt;&quot;)\"",
+ option_html_attributes([ 'foo', 'bar', { :onclick => %(alert("<code>")) } ])
+ )
+ end
+
def test_grouped_collection_select
@continents = [
Continent.new("<Africa>", [Country.new("<sa>", "<South Africa>"), Country.new("so", "Somalia")] ),
diff --git a/actionpack/test/template/form_tag_helper_test.rb b/actionpack/test/template/form_tag_helper_test.rb
index abb0e1df93..1c095b621e 100644
--- a/actionpack/test/template/form_tag_helper_test.rb
+++ b/actionpack/test/template/form_tag_helper_test.rb
@@ -288,6 +288,20 @@ class FormTagHelperTest < ActionView::TestCase
assert_match VALID_HTML_ID, label_elem['for']
end
+ def test_label_tag_with_block
+ assert_dom_equal('<label>Blocked</label>', label_tag { "Blocked" })
+ end
+
+ def test_label_tag_with_block_and_argument
+ output = label_tag("clock") { "Grandfather" }
+ assert_dom_equal('<label for="clock">Grandfather</label>', output)
+ end
+
+ def test_label_tag_with_block_and_argument_and_options
+ output = label_tag("clock", :id => "label_clock") { "Grandfather" }
+ assert_dom_equal('<label for="clock" id="label_clock">Grandfather</label>', output)
+ end
+
def test_boolean_options
assert_dom_equal %(<input checked="checked" disabled="disabled" id="admin" name="admin" readonly="readonly" type="checkbox" value="1" />), check_box_tag("admin", 1, true, 'disabled' => true, :readonly => "yes")
assert_dom_equal %(<input checked="checked" id="admin" name="admin" type="checkbox" value="1" />), check_box_tag("admin", 1, true, :disabled => false, :readonly => nil)
diff --git a/actionpack/test/template/render_test.rb b/actionpack/test/template/render_test.rb
index c9a50da418..d0212024ae 100644
--- a/actionpack/test/template/render_test.rb
+++ b/actionpack/test/template/render_test.rb
@@ -111,6 +111,7 @@ module RenderTestCases
assert_match %r!method.*doesnt_exist!, e.message
assert_equal "", e.sub_template_message
assert_equal "1", e.line_number
+ assert_equal "1: <%= doesnt_exist %>", e.annoted_source_code.strip
assert_equal File.expand_path("#{FIXTURE_LOAD_PATH}/test/_raise.html.erb"), e.file_name
end
diff --git a/activemodel/lib/active_model/dirty.rb b/activemodel/lib/active_model/dirty.rb
index a7ee15a7f6..bbcc345e4b 100644
--- a/activemodel/lib/active_model/dirty.rb
+++ b/activemodel/lib/active_model/dirty.rb
@@ -124,11 +124,12 @@ module ActiveModel
@previously_changed
end
+ # Map of change <tt>attr => original value</tt>.
+ def changed_attributes
+ @changed_attributes ||= {}
+ end
+
private
- # Map of change <tt>attr => original value</tt>.
- def changed_attributes
- @changed_attributes ||= {}
- end
# Handle <tt>*_changed?</tt> for +method_missing+.
def attribute_changed?(attr)
diff --git a/activemodel/lib/active_model/serializers/xml.rb b/activemodel/lib/active_model/serializers/xml.rb
index df7026b3ec..934af2b8a8 100644
--- a/activemodel/lib/active_model/serializers/xml.rb
+++ b/activemodel/lib/active_model/serializers/xml.rb
@@ -90,7 +90,7 @@ module ActiveModel
@builder = options[:builder]
@builder.instruct! unless options[:skip_instruct]
- root = (options[:root] || @serializable.class.model_name.singular).to_s
+ root = (options[:root] || @serializable.class.model_name.element).to_s
root = ActiveSupport::XmlMini.rename_key(root, options)
args = [root]
diff --git a/activemodel/lib/active_model/validations.rb b/activemodel/lib/active_model/validations.rb
index 7c705b8899..f472f50f9b 100644
--- a/activemodel/lib/active_model/validations.rb
+++ b/activemodel/lib/active_model/validations.rb
@@ -46,6 +46,10 @@ module ActiveModel
included do
extend ActiveModel::Translation
+
+ extend HelperMethods
+ include HelperMethods
+
define_callbacks :validate, :scope => :name
attr_accessor :validation_context
@@ -138,12 +142,6 @@ module ActiveModel
def attribute_method?(attribute)
method_defined?(attribute)
end
- private
-
- def _merge_attributes(attr_names)
- options = attr_names.extract_options!
- options.merge(:attributes => attr_names.flatten)
- end
end
# Returns the Errors object that holds all information about attribute error messages.
diff --git a/activemodel/lib/active_model/validations/acceptance.rb b/activemodel/lib/active_model/validations/acceptance.rb
index fbd622eb6d..26b12b504b 100644
--- a/activemodel/lib/active_model/validations/acceptance.rb
+++ b/activemodel/lib/active_model/validations/acceptance.rb
@@ -21,7 +21,7 @@ module ActiveModel
end
end
- module ClassMethods
+ module HelperMethods
# Encapsulates the pattern of wanting to validate the acceptance of a terms of service check box (or similar agreement). Example:
#
# class Person < ActiveRecord::Base
diff --git a/activemodel/lib/active_model/validations/confirmation.rb b/activemodel/lib/active_model/validations/confirmation.rb
index 8041d4b61f..51445343f2 100644
--- a/activemodel/lib/active_model/validations/confirmation.rb
+++ b/activemodel/lib/active_model/validations/confirmation.rb
@@ -12,7 +12,7 @@ module ActiveModel
end
end
- module ClassMethods
+ module HelperMethods
# Encapsulates the pattern of wanting to validate a password or email address field with a confirmation. Example:
#
# Model:
diff --git a/activemodel/lib/active_model/validations/exclusion.rb b/activemodel/lib/active_model/validations/exclusion.rb
index 2f51edfa9a..2ee78f5dd2 100644
--- a/activemodel/lib/active_model/validations/exclusion.rb
+++ b/activemodel/lib/active_model/validations/exclusion.rb
@@ -12,7 +12,7 @@ module ActiveModel
end
end
- module ClassMethods
+ module HelperMethods
# Validates that the value of the specified attribute is not in a particular enumerable object.
#
# class Person < ActiveRecord::Base
diff --git a/activemodel/lib/active_model/validations/format.rb b/activemodel/lib/active_model/validations/format.rb
index 9a9e7eca4d..c34c860d4d 100644
--- a/activemodel/lib/active_model/validations/format.rb
+++ b/activemodel/lib/active_model/validations/format.rb
@@ -24,7 +24,7 @@ module ActiveModel
end
end
- module ClassMethods
+ module HelperMethods
# Validates whether the value of the specified attribute is of the correct form, going by the regular expression provided.
# You can require that the attribute matches the regular expression:
#
diff --git a/activemodel/lib/active_model/validations/inclusion.rb b/activemodel/lib/active_model/validations/inclusion.rb
index 96dc8b6e15..446646d247 100644
--- a/activemodel/lib/active_model/validations/inclusion.rb
+++ b/activemodel/lib/active_model/validations/inclusion.rb
@@ -12,7 +12,7 @@ module ActiveModel
end
end
- module ClassMethods
+ module HelperMethods
# Validates whether the value of the specified attribute is available in a particular enumerable object.
#
# class Person < ActiveRecord::Base
diff --git a/activemodel/lib/active_model/validations/length.rb b/activemodel/lib/active_model/validations/length.rb
index 95da3e93ea..d7218f4f52 100644
--- a/activemodel/lib/active_model/validations/length.rb
+++ b/activemodel/lib/active_model/validations/length.rb
@@ -51,7 +51,7 @@ module ActiveModel
end
end
- module ClassMethods
+ module HelperMethods
# Validates that the specified attribute matches the length restrictions supplied. Only one option can be used at a time:
#
diff --git a/activemodel/lib/active_model/validations/numericality.rb b/activemodel/lib/active_model/validations/numericality.rb
index f974999bef..716010e88b 100644
--- a/activemodel/lib/active_model/validations/numericality.rb
+++ b/activemodel/lib/active_model/validations/numericality.rb
@@ -70,7 +70,7 @@ module ActiveModel
end
- module ClassMethods
+ module HelperMethods
# Validates whether the value of the specified attribute is numeric by trying to convert it to
# a float with Kernel.Float (if <tt>only_integer</tt> is false) or applying it to the regular expression
# <tt>/\A[\+\-]?\d+\Z/</tt> (if <tt>only_integer</tt> is set to true).
diff --git a/activemodel/lib/active_model/validations/presence.rb b/activemodel/lib/active_model/validations/presence.rb
index 4a71cf79b5..b319f4834b 100644
--- a/activemodel/lib/active_model/validations/presence.rb
+++ b/activemodel/lib/active_model/validations/presence.rb
@@ -8,7 +8,7 @@ module ActiveModel
end
end
- module ClassMethods
+ module HelperMethods
# Validates that the specified attributes are not blank (as defined by Object#blank?). Happens by default on save. Example:
#
# class Person < ActiveRecord::Base
diff --git a/activemodel/lib/active_model/validations/with.rb b/activemodel/lib/active_model/validations/with.rb
index 83d3ea80d6..6dbde5bfad 100644
--- a/activemodel/lib/active_model/validations/with.rb
+++ b/activemodel/lib/active_model/validations/with.rb
@@ -1,5 +1,13 @@
module ActiveModel
module Validations
+ module HelperMethods
+ private
+ def _merge_attributes(attr_names)
+ options = attr_names.extract_options!
+ options.merge(:attributes => attr_names.flatten)
+ end
+ end
+
module ClassMethods
# Passes the record off to the class or classes specified and allows them
@@ -75,5 +83,50 @@ module ActiveModel
end
end
end
+
+ # Passes the record off to the class or classes specified and allows them
+ # to add errors based on more complex conditions.
+ #
+ # class Person
+ # include ActiveModel::Validations
+ #
+ # validates :instance_validations
+ #
+ # def instance_validations
+ # validates_with MyValidator
+ # end
+ # end
+ #
+ # Please consult the class method documentation for more information on
+ # creating your own validator.
+ #
+ # You may also pass it multiple classes, like so:
+ #
+ # class Person
+ # include ActiveModel::Validations
+ #
+ # validates :instance_validations, :on => :create
+ #
+ # def instance_validations
+ # validates_with MyValidator, MyOtherValidator
+ # end
+ # end
+ #
+ # Standard configuration options (:on, :if and :unless), which are
+ # available on the class version of validates_with, should instead be
+ # placed on the <tt>validates</tt> method as these are applied and tested
+ # in the callback
+ #
+ # If you pass any additional configuration options, they will be passed
+ # to the class and available as <tt>options</tt>, please refer to the
+ # class version of this method for more information
+ #
+ def validates_with(*args, &block)
+ options = args.extract_options!
+ args.each do |klass|
+ validator = klass.new(options, &block)
+ validator.validate(self)
+ end
+ end
end
end \ No newline at end of file
diff --git a/activemodel/lib/active_model/validator.rb b/activemodel/lib/active_model/validator.rb
index 906d239bcc..56179c1a6c 100644
--- a/activemodel/lib/active_model/validator.rb
+++ b/activemodel/lib/active_model/validator.rb
@@ -88,6 +88,9 @@ module ActiveModel #:nodoc:
# klass.send :attr_accessor, :custom_attribute
# end
# end
+ #
+ # This setup method is only called when used with validation macros or the
+ # class level <tt>validates_with</tt> method.
#
class Validator
attr_reader :options
diff --git a/activemodel/test/cases/serializeration/xml_serialization_test.rb b/activemodel/test/cases/serializeration/xml_serialization_test.rb
index 3ba826a8d0..4e8e4efa25 100644
--- a/activemodel/test/cases/serializeration/xml_serialization_test.rb
+++ b/activemodel/test/cases/serializeration/xml_serialization_test.rb
@@ -37,8 +37,8 @@ class XmlSerializationTest < ActiveModel::TestCase
test "should serialize namespaced root" do
@xml = Admin::Contact.new(@contact.attributes).to_xml
- assert_match %r{^<admin-contact>}, @xml
- assert_match %r{</admin-contact>$}, @xml
+ assert_match %r{^<contact>}, @xml
+ assert_match %r{</contact>$}, @xml
end
test "should serialize default root with namespace" do
diff --git a/activemodel/test/cases/validations/validates_test.rb b/activemodel/test/cases/validations/validates_test.rb
index d15fb4a524..b85e491a9c 100644
--- a/activemodel/test/cases/validations/validates_test.rb
+++ b/activemodel/test/cases/validations/validates_test.rb
@@ -45,7 +45,7 @@ class ValidatesTest < ActiveModel::TestCase
Person.validates :karma, :presence => true, :email => true, :if => :condition_is_true
person = Person.new
person.valid?
- assert ["can't be blank", "is not an email"], person.errors[:karma].sort
+ assert_equal ["can't be blank", "is not an email"], person.errors[:karma].sort
end
def test_validates_with_unless_shared_conditions
@@ -111,4 +111,4 @@ class ValidatesTest < ActiveModel::TestCase
person.valid?
assert_equal ['Local validator please'], person.errors[:title]
end
-end \ No newline at end of file
+end
diff --git a/activemodel/test/cases/validations_test.rb b/activemodel/test/cases/validations_test.rb
index 578ffc27dd..af195af080 100644
--- a/activemodel/test/cases/validations_test.rb
+++ b/activemodel/test/cases/validations_test.rb
@@ -4,6 +4,7 @@ require 'cases/helper'
require 'models/topic'
require 'models/reply'
require 'models/custom_reader'
+require 'models/automobile'
class ValidationsTest < ActiveModel::TestCase
@@ -227,7 +228,7 @@ class ValidationsTest < ActiveModel::TestCase
t = Topic.new
assert t.invalid?
- assert ["NO BLANKS HERE"], t.errors[:title]
+ assert_equal ["NO BLANKS HERE"], t.errors[:title]
end
def test_list_of_validators_for_model
@@ -252,4 +253,16 @@ class ValidationsTest < ActiveModel::TestCase
Topic.validates_length_of :title, :minimum => 10
assert_equal 10, Topic.validators_on(:title).first.options[:minimum]
end
+
+ def test_validations_on_the_instance_level
+ auto = Automobile.new
+
+ assert auto.invalid?
+ assert_equal 2, auto.errors.size
+
+ auto.make = 'Toyota'
+ auto.model = 'Corolla'
+
+ assert auto.valid?
+ end
end
diff --git a/activemodel/test/models/automobile.rb b/activemodel/test/models/automobile.rb
new file mode 100644
index 0000000000..021ea61c80
--- /dev/null
+++ b/activemodel/test/models/automobile.rb
@@ -0,0 +1,12 @@
+class Automobile
+ include ActiveModel::Validations
+
+ validate :validations
+
+ attr_accessor :make, :model
+
+ def validations
+ validates_presence_of :make
+ validates_length_of :model, :within => 2..10
+ end
+end \ No newline at end of file
diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG
index 3f42fa34ef..d8f68d7c28 100644
--- a/activerecord/CHANGELOG
+++ b/activerecord/CHANGELOG
@@ -1,5 +1,15 @@
*Rails 3.0.0 [beta 4/release candidate] (unreleased)*
+* Add index length support for MySQL. #1852 [Emili Parreno, Pratik Naik]
+
+ Example:
+
+ add_index(:accounts, :name, :name => 'by_name', :length => 10)
+ => CREATE INDEX by_name ON accounts(name(10))
+
+ add_index(:accounts, [:name, :surname], :name => 'by_name_surname', :length => {:name => 10, :surname => 15})
+ => CREATE INDEX by_name_surname ON accounts(name(10), surname(15))
+
* find_or_create_by_attr(value, ...) works when attr is protected. #4457 [Santiago Pastorino, Marc-André Lafortune]
* New callbacks: after_commit and after_rollback. Do expensive operations like image thumbnailing after_commit instead of after_save. #2991 [Brian Durand]
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index 9d39a0c49b..f23d881c7b 100755
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -1399,7 +1399,7 @@ module ActiveRecord
primary_key = reflection.source_reflection.primary_key_name
send(through.name).select("DISTINCT #{through.quoted_table_name}.#{primary_key}").map!(&:"#{primary_key}")
else
- send(reflection.name).select("#{reflection.quoted_table_name}.#{reflection.klass.primary_key}").map!(&:id)
+ send(reflection.name).select("#{reflection.quoted_table_name}.#{reflection.klass.primary_key}").except(:includes).map!(&:id)
end
end
end
diff --git a/activerecord/lib/active_record/attribute_methods/dirty.rb b/activerecord/lib/active_record/attribute_methods/dirty.rb
index dd44bd8d51..cf4594ad7f 100644
--- a/activerecord/lib/active_record/attribute_methods/dirty.rb
+++ b/activerecord/lib/active_record/attribute_methods/dirty.rb
@@ -8,7 +8,7 @@ module ActiveRecord
include AttributeMethods::Write
included do
- if self < Timestamp
+ if self < ::ActiveRecord::Timestamp
raise "You cannot include Dirty after Timestamp"
end
diff --git a/activerecord/lib/active_record/attribute_methods/primary_key.rb b/activerecord/lib/active_record/attribute_methods/primary_key.rb
index 411330dda2..82d94b848a 100644
--- a/activerecord/lib/active_record/attribute_methods/primary_key.rb
+++ b/activerecord/lib/active_record/attribute_methods/primary_key.rb
@@ -6,7 +6,7 @@ module ActiveRecord
# Returns this record's primary key value wrapped in an Array
# or nil if the record is a new_record?
def to_key
- new_record? ? nil : [ send(self.class.primary_key) ]
+ new_record? ? nil : [ id ]
end
module ClassMethods
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index 650a91b385..c02af328c1 100755
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -668,7 +668,6 @@ module ActiveRecord #:nodoc:
name = "#{full_table_name_prefix}#{contained}#{undecorated_table_name(base.name)}#{table_name_suffix}"
end
- @quoted_table_name = nil
set_table_name(name)
name
end
@@ -702,6 +701,7 @@ module ActiveRecord #:nodoc:
# set_table_name "project"
# end
def set_table_name(value = nil, &block)
+ @quoted_table_name = nil
define_attr_method :table_name, value, &block
end
alias :table_name= :set_table_name
@@ -1190,7 +1190,11 @@ module ActiveRecord #:nodoc:
# default_scope order('last_name, first_name')
# end
def default_scope(options = {})
- self.default_scoping << construct_finder_arel(options)
+ self.default_scoping << construct_finder_arel(options, default_scoping.pop)
+ end
+
+ def clear_default_scope
+ self.default_scoping.clear
end
def scoped_methods #:nodoc:
@@ -1463,6 +1467,7 @@ module ActiveRecord #:nodoc:
@attributes_cache = {}
@new_record = true
ensure_proper_type
+ @changed_attributes = other.changed_attributes.dup
if scope = self.class.send(:current_scoped_methods)
create_with = scope.scope_for_create
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
index 6c477e48ce..7d58bc2adf 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
@@ -258,7 +258,7 @@ module ActiveRecord
end
end
- class IndexDefinition < Struct.new(:table, :name, :unique, :columns) #:nodoc:
+ class IndexDefinition < Struct.new(:table, :name, :unique, :columns, :lengths) #:nodoc:
end
# Abstract representation of a column definition. Instances of this type
@@ -494,7 +494,7 @@ module ActiveRecord
end
%w( string text integer float decimal datetime timestamp time date binary boolean ).each do |column_type|
- class_eval <<-EOV
+ class_eval <<-EOV, __FILE__, __LINE__ + 1
def #{column_type}(*args) # def string(*args)
options = args.extract_options! # options = args.extract_options!
column_names = args # column_names = args
@@ -694,7 +694,7 @@ module ActiveRecord
# t.string(:goat)
# t.string(:goat, :sheep)
%w( string text integer float decimal datetime timestamp time date binary boolean ).each do |column_type|
- class_eval <<-EOV
+ class_eval <<-EOV, __FILE__, __LINE__ + 1
def #{column_type}(*args) # def string(*args)
options = args.extract_options! # options = args.extract_options!
column_names = args # column_names = args
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
index e8cba1bd41..1255ef09be 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -256,18 +256,32 @@ module ActiveRecord
# name.
#
# ===== Examples
+ #
# ====== Creating a simple index
# add_index(:suppliers, :name)
# generates
# CREATE INDEX suppliers_name_index ON suppliers(name)
+ #
# ====== Creating a unique index
# add_index(:accounts, [:branch_id, :party_id], :unique => true)
# generates
# CREATE UNIQUE INDEX accounts_branch_id_party_id_index ON accounts(branch_id, party_id)
+ #
# ====== Creating a named index
# add_index(:accounts, [:branch_id, :party_id], :unique => true, :name => 'by_branch_party')
# generates
# CREATE UNIQUE INDEX by_branch_party ON accounts(branch_id, party_id)
+ #
+ # ====== Creating an index with specific key length
+ # add_index(:accounts, :name, :name => 'by_name', :length => 10)
+ # generates
+ # CREATE INDEX by_name ON accounts(name(10))
+ #
+ # add_index(:accounts, [:name, :surname], :name => 'by_name_surname', :length => {:name => 10, :surname => 15})
+ # generates
+ # CREATE INDEX by_name_surname ON accounts(name(10), surname(15))
+ #
+ # Note: SQLite doesn't support index length
def add_index(table_name, column_name, options = {})
column_names = Array.wrap(column_name)
index_name = index_name(table_name, :column => column_names)
@@ -278,7 +292,9 @@ module ActiveRecord
else
index_type = options
end
- quoted_column_names = column_names.map { |e| quote_column_name(e) }.join(", ")
+
+ quoted_column_names = quoted_columns_for_index(column_names, options).join(", ")
+
execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{quoted_column_names})"
end
@@ -430,6 +446,11 @@ module ActiveRecord
end
protected
+ # Overridden by the mysql adapter for supporting index lengths
+ def quoted_columns_for_index(column_names, options = {})
+ column_names.map {|name| quote_column_name(name) }
+ end
+
def options_include_default?(options)
options.include?(:default) && !(options[:null] == false && options[:default].nil?)
end
diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
index 55d9d20bb5..e12924e63f 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
@@ -15,26 +15,26 @@ module MysqlCompat #:nodoc:
# Ruby driver has a version string and returns null values in each_hash
# C driver >= 2.7 returns null values in each_hash
if Mysql.const_defined?(:VERSION) && (Mysql::VERSION.is_a?(String) || Mysql::VERSION >= 20700)
- target.class_eval <<-'end_eval'
- def all_hashes # def all_hashes
- rows = [] # rows = []
- each_hash { |row| rows << row } # each_hash { |row| rows << row }
- rows # rows
- end # end
+ target.class_eval <<-'end_eval', __FILE__, __LINE__ + 1
+ def all_hashes # def all_hashes
+ rows = [] # rows = []
+ each_hash { |row| rows << row } # each_hash { |row| rows << row }
+ rows # rows
+ end # end
end_eval
# adapters before 2.7 don't have a version constant
# and don't return null values in each_hash
else
- target.class_eval <<-'end_eval'
- def all_hashes # def all_hashes
- rows = [] # rows = []
- all_fields = fetch_fields.inject({}) { |fields, f| # all_fields = fetch_fields.inject({}) { |fields, f|
- fields[f.name] = nil; fields # fields[f.name] = nil; fields
- } # }
- each_hash { |row| rows << all_fields.dup.update(row) } # each_hash { |row| rows << all_fields.dup.update(row) }
- rows # rows
- end # end
+ target.class_eval <<-'end_eval', __FILE__, __LINE__ + 1
+ def all_hashes # def all_hashes
+ rows = [] # rows = []
+ all_fields = fetch_fields.inject({}) { |fields, f| # all_fields = fetch_fields.inject({}) { |fields, f|
+ fields[f.name] = nil; fields # fields[f.name] = nil; fields
+ } # }
+ each_hash { |row| rows << all_fields.dup.update(row) } # each_hash { |row| rows << all_fields.dup.update(row) }
+ rows # rows
+ end # end
end_eval
end
@@ -461,10 +461,11 @@ module ActiveRecord
if current_index != row[2]
next if row[2] == "PRIMARY" # skip the primary key
current_index = row[2]
- indexes << IndexDefinition.new(row[0], row[2], row[1] == "0", [])
+ indexes << IndexDefinition.new(row[0], row[2], row[1] == "0", [], [])
end
indexes.last.columns << row[4]
+ indexes.last.lengths << row[7]
end
result.free
indexes
@@ -512,7 +513,7 @@ module ActiveRecord
def change_column(table_name, column_name, type, options = {}) #:nodoc:
column = column_for(table_name, column_name)
- unless options_include_default?(options)
+ if has_default?(type) && !options_include_default?(options)
options[:default] = column.default
end
@@ -594,6 +595,18 @@ module ActiveRecord
end
protected
+ def quoted_columns_for_index(column_names, options = {})
+ length = options[:length] if options.is_a?(Hash)
+
+ quoted_column_names = case length
+ when Hash
+ column_names.map {|name| length[name] ? "#{quote_column_name(name)}(#{length[name]})" : quote_column_name(name) }
+ when Fixnum
+ column_names.map {|name| "#{quote_column_name(name)}(#{length})"}
+ else
+ column_names.map {|name| quote_column_name(name) }
+ end
+ end
def translate_exception(exception, message)
return super unless exception.respond_to?(:errno)
@@ -662,6 +675,10 @@ module ActiveRecord
end
column
end
+
+ def has_default?(sql_type)
+ sql_type =~ :binary || sql_type == :text #mysql forbids defaults on blob and text columns
+ end
end
end
end
diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb
index 4bf33c3856..8099aaa7f7 100644
--- a/activerecord/lib/active_record/fixtures.rb
+++ b/activerecord/lib/active_record/fixtures.rb
@@ -891,6 +891,7 @@ module ActiveRecord
instances.size == 1 ? instances.first : instances
end
+ private table_name
end
end
diff --git a/activerecord/lib/active_record/locking/optimistic.rb b/activerecord/lib/active_record/locking/optimistic.rb
index 71057efa15..ceb0902fde 100644
--- a/activerecord/lib/active_record/locking/optimistic.rb
+++ b/activerecord/lib/active_record/locking/optimistic.rb
@@ -113,12 +113,11 @@ module ActiveRecord
lock_col = self.class.locking_column
previous_value = send(lock_col).to_i
- affected_rows = connection.delete(
- "DELETE FROM #{self.class.quoted_table_name} " +
- "WHERE #{connection.quote_column_name(self.class.primary_key)} = #{quoted_id} " +
- "AND #{self.class.quoted_locking_column} = #{quote_value(previous_value)}",
- "#{self.class.name} Destroy"
- )
+ table = self.class.arel_table
+ predicate = table[self.class.primary_key].eq(id)
+ predicate = predicate.and(table[self.class.locking_column].eq(previous_value))
+
+ affected_rows = self.class.unscoped.where(predicate).delete_all
unless affected_rows == 1
raise ActiveRecord::StaleObjectError, "Attempted to delete a stale object: #{self.class.name}"
diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb
index aeed52e72a..3d8f4a030b 100644
--- a/activerecord/lib/active_record/named_scope.rb
+++ b/activerecord/lib/active_record/named_scope.rb
@@ -99,11 +99,7 @@ module ActiveRecord
block_given? ? relation.extending(Module.new(&block)) : relation
end
- singleton_class.instance_eval do
- define_method name do |*args|
- scopes[name].call(*args)
- end
- end
+ singleton_class.send :define_method, name, &scopes[name]
end
def named_scope(*args, &block)
diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb
index 898df0a67a..a32fb7d399 100644
--- a/activerecord/lib/active_record/railtie.rb
+++ b/activerecord/lib/active_record/railtie.rb
@@ -15,6 +15,12 @@ module ActiveRecord
config.generators.orm :active_record, :migration => true,
:timestamps => true
+ config.app_middleware.insert_after "::ActionDispatch::Callbacks",
+ "ActiveRecord::QueryCache"
+
+ config.app_middleware.insert_after "::ActionDispatch::Callbacks",
+ "ActiveRecord::ConnectionAdapters::ConnectionManagement"
+
rake_tasks do
load "active_record/railties/databases.rake"
end
@@ -58,16 +64,9 @@ module ActiveRecord
end
end
- # Setup database middleware after initializers have run
- initializer "active_record.initialize_database_middleware", :after => "action_controller.set_configs" do |app|
- middleware = app.config.middleware
- middleware.insert_after "::ActionDispatch::Callbacks", ActiveRecord::QueryCache
- middleware.insert_after "::ActionDispatch::Callbacks", ActiveRecord::ConnectionAdapters::ConnectionManagement
- end
-
initializer "active_record.set_dispatch_hooks", :before => :set_clear_dependencies_hook do |app|
- ActiveSupport.on_load(:active_record) do
- unless app.config.cache_classes
+ unless app.config.cache_classes
+ ActiveSupport.on_load(:active_record) do
ActionDispatch::Callbacks.after do
ActiveRecord::Base.reset_subclasses
ActiveRecord::Base.clear_reloadable_connections!
diff --git a/activerecord/lib/active_record/relation/batches.rb b/activerecord/lib/active_record/relation/batches.rb
index 1c61e7d450..412be895c4 100644
--- a/activerecord/lib/active_record/relation/batches.rb
+++ b/activerecord/lib/active_record/relation/batches.rb
@@ -71,7 +71,12 @@ module ActiveRecord
yield records
break if records.size < batch_size
- records = relation.where(primary_key.gt(records.last.id)).all
+
+ if primary_key_offset = records.last.id
+ records = relation.where(primary_key.gt(primary_key_offset)).all
+ else
+ raise "Primary key not included in the custom select clause"
+ end
end
end
diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb
index 858d298470..44baeb6c84 100644
--- a/activerecord/lib/active_record/relation/calculations.rb
+++ b/activerecord/lib/active_record/relation/calculations.rb
@@ -45,9 +45,8 @@ module ActiveRecord
calculate(:count, column_name, options)
end
- # Calculates the average value on a given column. The value is returned as
- # a float, or +nil+ if there's no row. See +calculate+ for examples with
- # options.
+ # Calculates the average value on a given column. Returns +nil+ if there's
+ # no row. See +calculate+ for examples with options.
#
# Person.average('age') # => 35.8
def average(column_name, options = {})
@@ -241,9 +240,9 @@ module ActiveRecord
def type_cast_calculated_value(value, column, operation = nil)
if value.is_a?(String) || value.nil?
case operation
- when 'count' then value.to_i
- when 'sum' then type_cast_using_column(value || '0', column)
- when 'average' then value && (value.is_a?(Fixnum) ? value.to_f : value).to_d
+ when 'count' then value.to_i
+ when 'sum' then type_cast_using_column(value || '0', column)
+ when 'average' then value.try(:to_d)
else type_cast_using_column(value, column)
end
else
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index 8d8bb659e1..6782554854 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -10,7 +10,7 @@ module ActiveRecord
attr_accessor :"#{query_method}_values"
next if [:where, :having].include?(query_method)
- class_eval <<-CEVAL, __FILE__
+ class_eval <<-CEVAL, __FILE__, __LINE__ + 1
def #{query_method}(*args, &block)
new_relation = clone
new_relation.send(:apply_modules, Module.new(&block)) if block_given?
@@ -22,7 +22,7 @@ module ActiveRecord
end
[:where, :having].each do |query_method|
- class_eval <<-CEVAL, __FILE__
+ class_eval <<-CEVAL, __FILE__, __LINE__ + 1
def #{query_method}(*args, &block)
new_relation = clone
new_relation.send(:apply_modules, Module.new(&block)) if block_given?
@@ -36,7 +36,7 @@ module ActiveRecord
ActiveRecord::Relation::SINGLE_VALUE_METHODS.each do |query_method|
attr_accessor :"#{query_method}_value"
- class_eval <<-CEVAL, __FILE__
+ class_eval <<-CEVAL, __FILE__, __LINE__ + 1
def #{query_method}(value = true, &block)
new_relation = clone
new_relation.send(:apply_modules, Module.new(&block)) if block_given?
diff --git a/activerecord/lib/active_record/schema_dumper.rb b/activerecord/lib/active_record/schema_dumper.rb
index c8e1b4f53a..cd54653581 100644
--- a/activerecord/lib/active_record/schema_dumper.rb
+++ b/activerecord/lib/active_record/schema_dumper.rb
@@ -178,6 +178,9 @@ HEADER
statment_parts << (':name => ' + index.name.inspect)
statment_parts << ':unique => true' if index.unique
+ index_lengths = index.lengths.compact if index.lengths.is_a?(Array)
+ statment_parts << (':length => ' + Hash[*index.columns.zip(index.lengths).flatten].inspect) if index_lengths.present?
+
' ' + statment_parts.join(', ')
end
diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb
index 55c4236874..be64e00bd1 100644
--- a/activerecord/lib/active_record/validations.rb
+++ b/activerecord/lib/active_record/validations.rb
@@ -36,49 +36,40 @@ module ActiveRecord
# The validation process on save can be skipped by passing false. The regular Base#save method is
# replaced with this when the validations module is mixed in, which it is by default.
- def save(options=nil)
- return super if valid?(options)
- false
- end
-
- def save_without_validation!
- save!(:validate => false)
+ def save(options={})
+ perform_validations(options) ? super : false
end
# Attempts to save the record just like Base#save but will raise a RecordInvalid exception instead of returning false
# if the record is not valid.
- def save!(options = nil)
- return super if valid?(options)
- raise RecordInvalid.new(self)
+ def save!(options={})
+ perform_validations(options) ? super : raise(RecordInvalid.new(self))
end
# Runs all the specified validations and returns true if no errors were added otherwise false.
- def valid?(options = nil)
- perform_validation = case options
- when NilClass
- true
- when Hash
- options[:validate] != false
- else
- ActiveSupport::Deprecation.warn "save(#{options}) is deprecated, please give save(:validate => #{options}) instead", caller
- options
- end
+ def valid?(context = nil)
+ context ||= (new_record? ? :create : :update)
+ super(context)
- if perform_validation
- errors.clear
+ deprecated_callback_method(:validate)
+ deprecated_callback_method(:"validate_on_#{context}")
- self.validation_context = new_record? ? :create : :update
- _run_validate_callbacks
+ errors.empty?
+ end
- deprecated_callback_method(:validate)
+ protected
- if new_record?
- deprecated_callback_method(:validate_on_create)
- else
- deprecated_callback_method(:validate_on_update)
- end
+ def perform_validations(options={})
+ perform_validation = case options
+ when Hash
+ options[:validate] != false
+ else
+ ActiveSupport::Deprecation.warn "save(#{options}) is deprecated, please give save(:validate => #{options}) instead", caller
+ options
+ end
- errors.empty?
+ if perform_validation
+ valid?(options.is_a?(Hash) ? options[:context] : nil)
else
true
end
diff --git a/activerecord/test/cases/active_schema_test_mysql.rb b/activerecord/test/cases/active_schema_test_mysql.rb
index 9aff538ce9..f4d123be15 100644
--- a/activerecord/test/cases/active_schema_test_mysql.rb
+++ b/activerecord/test/cases/active_schema_test_mysql.rb
@@ -15,6 +15,23 @@ class ActiveSchemaTest < ActiveRecord::TestCase
end
end
+ def test_add_index
+ expected = "CREATE INDEX `index_people_on_last_name` ON `people` (`last_name`)"
+ assert_equal expected, add_index(:people, :last_name, :length => nil)
+
+ expected = "CREATE INDEX `index_people_on_last_name` ON `people` (`last_name`(10))"
+ assert_equal expected, add_index(:people, :last_name, :length => 10)
+
+ expected = "CREATE INDEX `index_people_on_last_name_and_first_name` ON `people` (`last_name`(15), `first_name`(15))"
+ assert_equal expected, add_index(:people, [:last_name, :first_name], :length => 15)
+
+ expected = "CREATE INDEX `index_people_on_last_name_and_first_name` ON `people` (`last_name`(15), `first_name`)"
+ assert_equal expected, add_index(:people, [:last_name, :first_name], :length => {:last_name => 15})
+
+ expected = "CREATE INDEX `index_people_on_last_name_and_first_name` ON `people` (`last_name`(15), `first_name`(10))"
+ assert_equal expected, add_index(:people, [:last_name, :first_name], :length => {:last_name => 15, :first_name => 10})
+ end
+
def test_drop_table
assert_equal "DROP TABLE `people`", drop_table(:people)
end
diff --git a/activerecord/test/cases/associations/eager_load_nested_include_test.rb b/activerecord/test/cases/associations/eager_load_nested_include_test.rb
index e8db6d5dab..2beb3f8365 100644
--- a/activerecord/test/cases/associations/eager_load_nested_include_test.rb
+++ b/activerecord/test/cases/associations/eager_load_nested_include_test.rb
@@ -17,7 +17,7 @@ module Remembered
module ClassMethods
def remembered; @@remembered ||= []; end
- def rand; @@remembered.rand; end
+ def random_element; @@remembered.random_element; end
end
end
@@ -79,14 +79,14 @@ class EagerLoadPolyAssocsTest < ActiveRecord::TestCase
[Circle, Square, Triangle, NonPolyOne, NonPolyTwo].map(&:create!)
end
1.upto(NUM_SIMPLE_OBJS) do
- PaintColor.create!(:non_poly_one_id => NonPolyOne.rand.id)
- PaintTexture.create!(:non_poly_two_id => NonPolyTwo.rand.id)
+ PaintColor.create!(:non_poly_one_id => NonPolyOne.random_element.id)
+ PaintTexture.create!(:non_poly_two_id => NonPolyTwo.random_element.id)
end
1.upto(NUM_SHAPE_EXPRESSIONS) do
- shape_type = [Circle, Square, Triangle].rand
- paint_type = [PaintColor, PaintTexture].rand
- ShapeExpression.create!(:shape_type => shape_type.to_s, :shape_id => shape_type.rand.id,
- :paint_type => paint_type.to_s, :paint_id => paint_type.rand.id)
+ shape_type = [Circle, Square, Triangle].random_element
+ paint_type = [PaintColor, PaintTexture].random_element
+ ShapeExpression.create!(:shape_type => shape_type.to_s, :shape_id => shape_type.random_element.id,
+ :paint_type => paint_type.to_s, :paint_id => paint_type.random_element.id)
end
end
diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb
index b55b08bf9d..6e47967696 100644
--- a/activerecord/test/cases/associations/has_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_associations_test.rb
@@ -900,6 +900,10 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert !company.clients.loaded?
end
+ def test_get_ids_ignores_include_option
+ assert_equal [readers(:michael_welcome).id], posts(:welcome).readers_with_person_ids
+ end
+
def test_get_ids_for_unloaded_finder_sql_associations_loads_them
company = companies(:first_firm)
assert !company.clients_using_sql.loaded?
diff --git a/activerecord/test/cases/associations/join_model_test.rb b/activerecord/test/cases/associations/join_model_test.rb
index 8bdf8bcd55..dca72be4fd 100644
--- a/activerecord/test/cases/associations/join_model_test.rb
+++ b/activerecord/test/cases/associations/join_model_test.rb
@@ -289,7 +289,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
end
def test_has_many_array_methods_called_by_method_missing
- assert true, authors(:david).categories.any? { |category| category.name == 'General' }
+ assert authors(:david).categories.any? { |category| category.name == 'General' }
assert_nothing_raised { authors(:david).categories.sort }
end
diff --git a/activerecord/test/cases/autosave_association_test.rb b/activerecord/test/cases/autosave_association_test.rb
index 2995cc6f72..5cc5dff633 100644
--- a/activerecord/test/cases/autosave_association_test.rb
+++ b/activerecord/test/cases/autosave_association_test.rb
@@ -765,7 +765,7 @@ class TestAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCase
@ship.destroy
@pirate.reload.catchphrase = "Arr"
@pirate.save
- assert 'Arr', @pirate.reload.catchphrase
+ assert_equal 'Arr', @pirate.reload.catchphrase
end
def test_should_automatically_save_the_associated_model
@@ -885,7 +885,7 @@ class TestAutosaveAssociationOnABelongsToAssociation < ActiveRecord::TestCase
@pirate.destroy
@ship.reload.name = "The Vile Insanity"
@ship.save
- assert 'The Vile Insanity', @ship.reload.name
+ assert_equal 'The Vile Insanity', @ship.reload.name
end
def test_should_automatically_save_the_associated_model
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index bbc4e543d5..b7ae619787 100755
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -1793,6 +1793,18 @@ class BasicsTest < ActiveRecord::TestCase
assert_equal "bar", k.table_name
end
+ def test_quoted_table_name_after_set_table_name
+ klass = Class.new(ActiveRecord::Base)
+
+ klass.set_table_name "foo"
+ assert_equal "foo", klass.table_name
+ assert_equal klass.connection.quote_table_name("foo"), klass.quoted_table_name
+
+ klass.set_table_name "bar"
+ assert_equal "bar", klass.table_name
+ assert_equal klass.connection.quote_table_name("bar"), klass.quoted_table_name
+ end
+
def test_set_table_name_with_block
k = Class.new( ActiveRecord::Base )
k.set_table_name { "ks" }
diff --git a/activerecord/test/cases/batches_test.rb b/activerecord/test/cases/batches_test.rb
index 83deabb5b7..dcc49e12ca 100644
--- a/activerecord/test/cases/batches_test.rb
+++ b/activerecord/test/cases/batches_test.rb
@@ -17,6 +17,20 @@ class EachTest < ActiveRecord::TestCase
end
end
+ def test_each_should_raise_if_select_is_set_without_id
+ assert_raise(RuntimeError) do
+ Post.find_each(:select => :title, :batch_size => 1) { |post| post }
+ end
+ end
+
+ def test_each_should_execute_if_id_is_in_select
+ assert_queries(4) do
+ Post.find_each(:select => "id, title, type", :batch_size => 2) do |post|
+ assert_kind_of Post, post
+ end
+ end
+ end
+
def test_each_should_raise_if_the_order_is_set
assert_raise(RuntimeError) do
Post.find_each(:order => "title") { |post| post }
diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb
index 28a1ae5feb..8473150338 100644
--- a/activerecord/test/cases/calculations_test.rb
+++ b/activerecord/test/cases/calculations_test.rb
@@ -20,8 +20,7 @@ class CalculationsTest < ActiveRecord::TestCase
def test_should_average_field
value = Account.average(:credit_limit)
- assert_kind_of BigDecimal, value
- assert_equal BigDecimal.new('53.0'), value
+ assert_equal 53.0, value
end
def test_should_return_nil_as_average
diff --git a/activerecord/test/cases/dirty_test.rb b/activerecord/test/cases/dirty_test.rb
index 7a17ef1ee0..3ea2948f62 100644
--- a/activerecord/test/cases/dirty_test.rb
+++ b/activerecord/test/cases/dirty_test.rb
@@ -338,6 +338,15 @@ class DirtyTest < ActiveRecord::TestCase
assert !pirate.changed?
end
+ def test_cloned_objects_should_not_copy_dirty_flag_from_creator
+ pirate = Pirate.create!(:catchphrase => "shiver me timbers")
+ pirate_clone = pirate.clone
+ pirate_clone.reset_catchphrase!
+ pirate.catchphrase = "I love Rum"
+ assert pirate.catchphrase_changed?
+ assert !pirate_clone.catchphrase_changed?
+ end
+
def test_reverted_changes_are_not_dirty
phrase = "shiver me timbers"
pirate = Pirate.create!(:catchphrase => phrase)
diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb
index e78db8969d..c73ad50a71 100644
--- a/activerecord/test/cases/finder_test.rb
+++ b/activerecord/test/cases/finder_test.rb
@@ -722,10 +722,10 @@ class FinderTest < ActiveRecord::TestCase
def test_find_all_by_one_attribute_with_options
topics = Topic.find_all_by_content("Have a nice day", :order => "id DESC")
- assert topics(:first), topics.last
+ assert_equal topics(:first), topics.last
topics = Topic.find_all_by_content("Have a nice day", :order => "id")
- assert topics(:first), topics.first
+ assert_equal topics(:first), topics.first
end
def test_find_all_by_array_attribute
diff --git a/activerecord/test/cases/fixtures_test.rb b/activerecord/test/cases/fixtures_test.rb
index 3ce23209cc..8008b86f81 100644
--- a/activerecord/test/cases/fixtures_test.rb
+++ b/activerecord/test/cases/fixtures_test.rb
@@ -256,6 +256,11 @@ class FixturesWithoutInstantiationTest < ActiveRecord::TestCase
def test_fixtures_from_root_yml_without_instantiation
assert !defined?(@unknown), "@unknown is not defined"
end
+
+ def test_visibility_of_accessor_method
+ assert_equal false, respond_to?(:topics, false), "should be private method"
+ assert_equal true, respond_to?(:topics, true), "confirm to respond surely"
+ end
def test_accessor_methods
assert_equal "The First Topic", topics(:first).title
diff --git a/activerecord/test/cases/method_scoping_test.rb b/activerecord/test/cases/method_scoping_test.rb
index a3b496a0e6..3a6354ec6d 100644
--- a/activerecord/test/cases/method_scoping_test.rb
+++ b/activerecord/test/cases/method_scoping_test.rb
@@ -587,6 +587,18 @@ class HasAndBelongsToManyScopingTest< ActiveRecord::TestCase
end
end
+class ClearDefaultScopeTest < ActiveRecord::TestCase
+ fixtures :developers
+
+ def test_should_clear_default_scope
+ klass = Class.new(DeveloperCalledDavid)
+ klass.__send__ :clear_default_scope
+ expected = Developer.all.collect { |dev| dev.name }
+ actual = klass.all.collect { |dev| dev.name }
+ assert_equal expected, actual
+ end
+end
+
class DefaultScopingTest < ActiveRecord::TestCase
fixtures :developers, :posts
@@ -615,15 +627,30 @@ class DefaultScopingTest < ActiveRecord::TestCase
def test_default_scoping_with_inheritance
# Inherit a class having a default scope and define a new default scope
klass = Class.new(DeveloperOrderedBySalary)
- klass.send :default_scope, {}
+ klass.send :default_scope, :limit => 1
# Scopes added on children should append to parent scope
- assert klass.scoped.order_values.blank?
+ assert_equal 1, klass.scoped.limit_value
+ assert_equal ['salary DESC'], klass.scoped.order_values
# Parent should still have the original scope
+ assert_equal nil, DeveloperOrderedBySalary.scoped.limit_value
assert_equal ['salary DESC'], DeveloperOrderedBySalary.scoped.order_values
end
+ def test_default_scope_called_twice_merges_conditions
+ Developer.destroy_all
+ Developer.create!(:name => "David", :salary => 80000)
+ Developer.create!(:name => "David", :salary => 100000)
+ Developer.create!(:name => "Brian", :salary => 100000)
+
+ klass = Class.new(Developer)
+ klass.__send__ :default_scope, :conditions => { :name => "David" }
+ klass.__send__ :default_scope, :conditions => { :salary => 100000 }
+ assert_equal 1, klass.count
+ assert_equal "David", klass.first.name
+ assert_equal 100000, klass.first.salary
+ end
def test_method_scope
expected = Developer.find(:all, :order => 'name DESC').collect { |dev| dev.salary }
received = DeveloperOrderedBySalary.all_ordered_by_name.collect { |dev| dev.salary }
diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb
index a3d1ceaa1f..768a44f6df 100644
--- a/activerecord/test/cases/migration_test.rb
+++ b/activerecord/test/cases/migration_test.rb
@@ -92,6 +92,14 @@ if ActiveRecord::Base.connection.supports_migrations?
assert_nothing_raised { Person.connection.remove_index("people", "last_name_and_first_name") }
assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) }
assert_nothing_raised { Person.connection.remove_index("people", ["last_name", "first_name"]) }
+ assert_nothing_raised { Person.connection.add_index("people", ["last_name"], :length => 10) }
+ assert_nothing_raised { Person.connection.remove_index("people", "last_name") }
+ assert_nothing_raised { Person.connection.add_index("people", ["last_name"], :length => {:last_name => 10}) }
+ assert_nothing_raised { Person.connection.remove_index("people", ["last_name"]) }
+ assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"], :length => 10) }
+ assert_nothing_raised { Person.connection.remove_index("people", ["last_name", "first_name"]) }
+ assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"], :length => {:last_name => 10, :first_name => 20}) }
+ assert_nothing_raised { Person.connection.remove_index("people", ["last_name", "first_name"]) }
end
# quoting
@@ -852,6 +860,18 @@ if ActiveRecord::Base.connection.supports_migrations?
assert_equal "Tester", Person.new.first_name
end
+ unless current_adapter?(:PostgreSQLAdapter)
+ def test_change_column_type_default_should_change
+ old_columns = Person.connection.columns(Person.table_name, "#{name} Columns")
+ assert !old_columns.find { |c| c.name == 'data' }
+
+ assert_nothing_raised do
+ Person.connection.add_column "people", "data", :string, :default => ''
+ Person.connection.change_column "people", "data", :binary
+ end
+ end
+ end
+
def test_change_column_quotes_column_names
Person.connection.create_table :testings do |t|
t.column :select, :string
diff --git a/activerecord/test/cases/named_scope_test.rb b/activerecord/test/cases/named_scope_test.rb
index e4cafad11e..2007f5492f 100644
--- a/activerecord/test/cases/named_scope_test.rb
+++ b/activerecord/test/cases/named_scope_test.rb
@@ -142,6 +142,11 @@ class NamedScopeTest < ActiveRecord::TestCase
assert_equal authors(:david).posts & Post.containing_the_letter_a, authors(:david).posts.containing_the_letter_a
end
+ def test_named_scope_with_STI
+ assert_equal 3,Post.containing_the_letter_a.count
+ assert_equal 1,SpecialPost.containing_the_letter_a.count
+ end
+
def test_has_many_through_associations_have_access_to_named_scopes
assert_not_equal Comment.containing_the_letter_e, authors(:david).comments
assert !Comment.containing_the_letter_e.empty?
@@ -296,7 +301,7 @@ class NamedScopeTest < ActiveRecord::TestCase
end
def test_rand_should_select_a_random_object_from_proxy
- assert_kind_of Topic, Topic.approved.rand
+ assert_kind_of Topic, Topic.approved.random_element
end
def test_should_use_where_in_query_for_named_scope
diff --git a/activerecord/test/cases/validations_test.rb b/activerecord/test/cases/validations_test.rb
index e1fb911cc9..3a34df2426 100644
--- a/activerecord/test/cases/validations_test.rb
+++ b/activerecord/test/cases/validations_test.rb
@@ -62,6 +62,23 @@ class ValidationsTest < ActiveRecord::TestCase
assert_equal ["is Wrong Update"], r.errors[:title], "A reply with a bad content should contain an error"
end
+ def test_error_on_given_context
+ r = WrongReply.new
+ assert !r.valid?(:special_case)
+ assert "Invalid", r.errors[:title].join
+
+ r.title = "secret"
+ r.content = "Good"
+ assert r.valid?(:special_case)
+
+ r.title = nil
+ assert !r.save(:context => :special_case)
+ assert "Invalid", r.errors[:title].join
+
+ r.title = "secret"
+ assert r.save(:context => :special_case)
+ end
+
def test_invalid_record_exception
assert_raise(ActiveRecord::RecordInvalid) { WrongReply.create! }
assert_raise(ActiveRecord::RecordInvalid) { WrongReply.new.save! }
@@ -135,12 +152,6 @@ class ValidationsTest < ActiveRecord::TestCase
end
end
- def test_create_without_validation_bang
- count = WrongReply.count
- assert_nothing_raised { WrongReply.new.save_without_validation! }
- assert count+1, WrongReply.count
- end
-
def test_validates_acceptance_of_with_non_existant_table
Object.const_set :IncorporealModel, Class.new(ActiveRecord::Base)
diff --git a/activerecord/test/models/post.rb b/activerecord/test/models/post.rb
index d092c4bf09..dd06822cfd 100644
--- a/activerecord/test/models/post.rb
+++ b/activerecord/test/models/post.rb
@@ -69,6 +69,7 @@ class Post < ActiveRecord::Base
has_many :authors, :through => :categorizations
has_many :readers
+ has_many :readers_with_person, :include => :person, :class_name => "Reader"
has_many :people, :through => :readers
has_many :people_with_callbacks, :source=>:person, :through => :readers,
:before_add => lambda {|owner, reader| log(:added, :before, reader.first_name) },
diff --git a/activerecord/test/models/reply.rb b/activerecord/test/models/reply.rb
index 264a49b465..6cc9ee038a 100644
--- a/activerecord/test/models/reply.rb
+++ b/activerecord/test/models/reply.rb
@@ -17,6 +17,7 @@ class WrongReply < Reply
validate :check_empty_title
validate :check_content_mismatch, :on => :create
validate :check_wrong_update, :on => :update
+ validate :check_title_is_secret, :on => :special_case
def check_empty_title
errors[:title] << "Empty" unless attribute_present?("title")
@@ -39,6 +40,10 @@ class WrongReply < Reply
def check_wrong_update
errors[:title] << "is Wrong Update" if attribute_present?("title") && title == "Wrong Update"
end
+
+ def check_title_is_secret
+ errors[:title] << "Invalid" unless title == "secret"
+ end
end
class SillyReply < Reply
diff --git a/activeresource/lib/active_resource/base.rb b/activeresource/lib/active_resource/base.rb
index ad994214f6..b976844c1c 100644
--- a/activeresource/lib/active_resource/base.rb
+++ b/activeresource/lib/active_resource/base.rb
@@ -551,11 +551,9 @@ module ActiveResource
@headers ||= {}
end
- # Do not include any modules in the default element name. This makes it easier to seclude ARes objects
- # in a separate namespace without having to set element_name repeatedly.
- attr_accessor_with_default(:element_name) { ActiveSupport::Inflector.underscore(to_s.split("::").last) } #:nodoc:
-
+ attr_accessor_with_default(:element_name) { model_name.element } #:nodoc:
attr_accessor_with_default(:collection_name) { ActiveSupport::Inflector.pluralize(element_name) } #:nodoc:
+
attr_accessor_with_default(:primary_key, 'id') #:nodoc:
# Gets the \prefix for a resource's nested URL (e.g., <tt>prefix/collectionname/1.xml</tt>)
@@ -1295,6 +1293,14 @@ module ActiveResource
end
end
+ def to_json(options={})
+ super({ :root => self.class.element_name }.merge(options))
+ end
+
+ def to_xml(options={})
+ super({ :root => self.class.element_name }.merge(options))
+ end
+
protected
def connection(refresh = false)
self.class.connection(refresh)
diff --git a/activeresource/lib/active_resource/schema.rb b/activeresource/lib/active_resource/schema.rb
index 8368b652c2..5758ac9502 100644
--- a/activeresource/lib/active_resource/schema.rb
+++ b/activeresource/lib/active_resource/schema.rb
@@ -42,7 +42,7 @@ module ActiveResource # :nodoc:
# TODO: We should eventually support all of these:
# %w( string text integer float decimal datetime timestamp time date binary boolean ).each do |attr_type|
KNOWN_ATTRIBUTE_TYPES.each do |attr_type|
- class_eval <<-EOV
+ class_eval <<-EOV, __FILE__, __LINE__ + 1
def #{attr_type.to_s}(*args)
options = args.extract_options!
attr_names = args
diff --git a/activeresource/test/cases/base_test.rb b/activeresource/test/cases/base_test.rb
index 2ed7e1c95f..4e21e84596 100644
--- a/activeresource/test/cases/base_test.rb
+++ b/activeresource/test/cases/base_test.rb
@@ -2,6 +2,7 @@ require 'abstract_unit'
require "fixtures/person"
require "fixtures/customer"
require "fixtures/street_address"
+require "fixtures/sound"
require "fixtures/beast"
require "fixtures/proxy"
require 'active_support/json'
@@ -563,6 +564,10 @@ class BaseTest < Test::Unit::TestCase
assert_equal '/people/Greg/addresses/1.xml', StreetAddress.element_path(1, 'person_id' => 'Greg')
end
+ def test_module_element_path
+ assert_equal '/sounds/1.xml', Asset::Sound.element_path(1)
+ end
+
def test_custom_element_path_with_redefined_to_param
Person.module_eval do
alias_method :original_to_param_element_path, :to_param
@@ -1009,25 +1014,66 @@ class BaseTest < Test::Unit::TestCase
def test_to_xml
matz = Person.find(1)
- xml = matz.encode
+ encode = matz.encode
+ xml = matz.to_xml
+
+ assert encode, xml
assert xml.include?('<?xml version="1.0" encoding="UTF-8"?>')
assert xml.include?('<name>Matz</name>')
assert xml.include?('<id type="integer">1</id>')
end
+ def test_to_xml_with_element_name
+ old_elem_name = Person.element_name
+ matz = Person.find(1)
+ Person.element_name = 'ruby_creator'
+ encode = matz.encode
+ xml = matz.to_xml
+
+ assert encode, xml
+ assert xml.include?('<?xml version="1.0" encoding="UTF-8"?>')
+ assert xml.include?('<ruby-creator>')
+ assert xml.include?('<name>Matz</name>')
+ assert xml.include?('<id type="integer">1</id>')
+ assert xml.include?('</ruby-creator>')
+ ensure
+ Person.element_name = old_elem_name
+ end
+
def test_to_json
Person.include_root_in_json = true
Person.format = :json
joe = Person.find(6)
- json = joe.encode
+ encode = joe.encode
+ json = joe.to_json
Person.format = :xml
+ assert encode, json
assert_match %r{^\{"person":\{"person":\{}, json
assert_match %r{"id":6}, json
assert_match %r{"name":"Joe"}, json
assert_match %r{\}\}\}$}, json
end
+ def test_to_json_with_element_name
+ old_elem_name = Person.element_name
+ Person.include_root_in_json = true
+ Person.format = :json
+ joe = Person.find(6)
+ Person.element_name = 'ruby_creator'
+ encode = joe.encode
+ json = joe.to_json
+ Person.format = :xml
+
+ assert encode, json
+ assert_match %r{^\{"ruby_creator":\{"person":\{}, json
+ assert_match %r{"id":6}, json
+ assert_match %r{"name":"Joe"}, json
+ assert_match %r{\}\}\}$}, json
+ ensure
+ Person.element_name = old_elem_name
+ end
+
def test_to_param_quacks_like_active_record
new_person = Person.new
assert_nil new_person.to_param
diff --git a/activeresource/test/fixtures/sound.rb b/activeresource/test/fixtures/sound.rb
new file mode 100644
index 0000000000..5c0ef5d55c
--- /dev/null
+++ b/activeresource/test/fixtures/sound.rb
@@ -0,0 +1,5 @@
+module Asset
+ class Sound < ActiveResource::Base
+ self.site = "http://37s.sunrise.i:3000"
+ end
+end
diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG
index 6146cc6a97..7afd9926b5 100644
--- a/activesupport/CHANGELOG
+++ b/activesupport/CHANGELOG
@@ -1,5 +1,7 @@
*Rails 3.0.0 [beta 4/release candidate] (unreleased)*
+* Defines prev_(month|year) in Date and Time, and deprecates last_(month|year). [fxn]
+
* Aliases Date#sunday to Date#end_of_week. [fxn]
* Backports Date#>> from 1.9 so that calculations do the right thing around the calendar reform. [fxn]
diff --git a/activesupport/lib/active_support/core_ext/array/random_access.rb b/activesupport/lib/active_support/core_ext/array/random_access.rb
index 343003f6f7..5338836b29 100644
--- a/activesupport/lib/active_support/core_ext/array/random_access.rb
+++ b/activesupport/lib/active_support/core_ext/array/random_access.rb
@@ -1,6 +1,16 @@
class Array
+ # This method is deprecated because it masks Kernel#rand within the Array class itself,
+ # which may be used by a 3rd party library extending Array in turn. See
+ #
+ # https://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets/4555
+ #
+ def rand # :nodoc:
+ ActiveSupport::Deprecation.warn "Array#rand is deprecated, use random_element instead", caller
+ random_element
+ end
+
# Returns a random element from the array.
- def rand
+ def random_element
self[Kernel.rand(length)]
end
end
diff --git a/activesupport/lib/active_support/core_ext/date/calculations.rb b/activesupport/lib/active_support/core_ext/date/calculations.rb
index 3038729d34..755d96ce91 100644
--- a/activesupport/lib/active_support/core_ext/date/calculations.rb
+++ b/activesupport/lib/active_support/core_ext/date/calculations.rb
@@ -2,6 +2,7 @@ require 'date'
require 'active_support/duration'
require 'active_support/core_ext/time/zones'
require 'active_support/core_ext/object/acts_like'
+require 'active_support/deprecation'
class Date
if RUBY_VERSION < '1.9'
@@ -146,20 +147,30 @@ class Date
advance(:years => years)
end
- # Short-hand for years_ago(1)
- def last_year
- years_ago(1)
+ def last_year # :nodoc:
+ ActiveSupport::Deprecation.warn("Date#last_year has been deprecated, please use Date#prev_year instead", caller)
+ prev_year
end
+ # Shorthand for years_ago(1)
+ def prev_year
+ years_ago(1)
+ end unless method_defined?(:prev_year)
+
# Short-hand for years_since(1)
def next_year
years_since(1)
end unless method_defined?(:next_year)
+ def last_month # :nodoc:
+ ActiveSupport::Deprecation.warn("Date#last_month has been deprecated, please use Date#prev_month instead", caller)
+ prev_month
+ end
+
# Short-hand for months_ago(1)
- def last_month
+ def prev_month
months_ago(1)
- end
+ end unless method_defined?(:prev_month)
# Short-hand for months_since(1)
def next_month
diff --git a/activesupport/lib/active_support/core_ext/logger.rb b/activesupport/lib/active_support/core_ext/logger.rb
index 22749229a3..c4994ca2ee 100644
--- a/activesupport/lib/active_support/core_ext/logger.rb
+++ b/activesupport/lib/active_support/core_ext/logger.rb
@@ -3,7 +3,7 @@ require 'active_support/core_ext/class/attribute_accessors'
# Adds the 'around_level' method to Logger.
class Logger #:nodoc:
def self.define_around_helper(level)
- module_eval <<-end_eval
+ module_eval <<-end_eval, __FILE__, __LINE__ + 1
def around_#{level}(before_message, after_message, &block) # def around_debug(before_message, after_message, &block)
self.#{level}(before_message) # self.debug(before_message)
return_value = block.call(self) # return_value = block.call(self)
diff --git a/activesupport/lib/active_support/core_ext/object/with_options.rb b/activesupport/lib/active_support/core_ext/object/with_options.rb
index dd38b7d261..3209cf7f11 100644
--- a/activesupport/lib/active_support/core_ext/object/with_options.rb
+++ b/activesupport/lib/active_support/core_ext/object/with_options.rb
@@ -1,3 +1,5 @@
+require 'active_support/option_merger'
+
class Object
# An elegant way to factor duplication out of options passed to a series of
# method calls. Each method called in the block, with the block variable as
diff --git a/activesupport/lib/active_support/core_ext/time/calculations.rb b/activesupport/lib/active_support/core_ext/time/calculations.rb
index 2b47ecd543..e27b08ec2e 100644
--- a/activesupport/lib/active_support/core_ext/time/calculations.rb
+++ b/activesupport/lib/active_support/core_ext/time/calculations.rb
@@ -2,6 +2,7 @@ require 'active_support/duration'
require 'active_support/core_ext/date/acts_like'
require 'active_support/core_ext/date/calculations'
require 'active_support/core_ext/date_time/conversions'
+require 'active_support/deprecation'
class Time
COMMON_YEAR_DAYS_IN_MONTH = [nil, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
@@ -132,8 +133,13 @@ class Time
advance(:years => years)
end
+ def last_year # :nodoc:
+ ActiveSupport::Deprecation.warn("Time#last_year has been deprecated, please use Time#prev_year instead", caller)
+ prev_year
+ end
+
# Short-hand for years_ago(1)
- def last_year
+ def prev_year
years_ago(1)
end
@@ -142,9 +148,13 @@ class Time
years_since(1)
end
+ def last_month # :nodoc:
+ ActiveSupport::Deprecation.warn("Time#last_month has been deprecated, please use Time#prev_month instead", caller)
+ prev_month
+ end
# Short-hand for months_ago(1)
- def last_month
+ def prev_month
months_ago(1)
end
diff --git a/activesupport/lib/active_support/lazy_load_hooks.rb b/activesupport/lib/active_support/lazy_load_hooks.rb
index 642a4c105c..3664431a28 100644
--- a/activesupport/lib/active_support/lazy_load_hooks.rb
+++ b/activesupport/lib/active_support/lazy_load_hooks.rb
@@ -2,16 +2,26 @@ module ActiveSupport
@load_hooks = Hash.new {|h,k| h[k] = [] }
@loaded = {}
- def self.on_load(name, &block)
+ def self.on_load(name, options = {}, &block)
if base = @loaded[name]
- base.instance_eval(&block)
+ execute_hook(base, options, block)
else
- @load_hooks[name] << block
+ @load_hooks[name] << [block, options]
+ end
+ end
+
+ def self.execute_hook(base, options, block)
+ if options[:yield]
+ block.call(base)
+ else
+ base.instance_eval(&block)
end
end
def self.run_load_hooks(name, base = Object)
- @load_hooks[name].each { |hook| base.instance_eval(&hook) }
@loaded[name] = base
+ @load_hooks[name].each do |hook, options|
+ execute_hook(base, options, hook)
+ end
end
end \ No newline at end of file
diff --git a/activesupport/test/buffered_logger_test.rb b/activesupport/test/buffered_logger_test.rb
index 7a0ec2e910..5b072d4102 100644
--- a/activesupport/test/buffered_logger_test.rb
+++ b/activesupport/test/buffered_logger_test.rb
@@ -73,7 +73,7 @@ class BufferedLoggerTest < Test::Unit::TestCase
end
@logger.flush
- assert !@output.string.empty?, @logger.send(:buffer).size
+ assert !@output.string.empty?, @logger.send(:buffer).size.to_s
end
define_method "test_disabling_auto_flush_with_#{disable.inspect}_should_flush_at_max_buffer_size_as_failsafe" do
@@ -86,7 +86,7 @@ class BufferedLoggerTest < Test::Unit::TestCase
end
@logger.info 'there it is.'
- assert !@output.string.empty?, @logger.send(:buffer).size
+ assert !@output.string.empty?, @logger.send(:buffer).size.to_s
end
end
diff --git a/activesupport/test/caching_test.rb b/activesupport/test/caching_test.rb
index d9ff1207e7..3e14c754b7 100644
--- a/activesupport/test/caching_test.rb
+++ b/activesupport/test/caching_test.rb
@@ -375,7 +375,7 @@ module LocalCacheBehavior
def test_local_cache_of_write_nil
@cache.with_local_cache do
- assert true, @cache.write('foo', nil)
+ assert @cache.write('foo', nil)
assert_nil @cache.read('foo')
@peek.write('foo', 'bar')
assert_nil @cache.read('foo')
@@ -394,7 +394,7 @@ module LocalCacheBehavior
@cache.with_local_cache do
@cache.write('foo', 'bar')
@peek.delete('foo')
- assert true, @cache.exist?('foo')
+ assert @cache.exist?('foo')
end
end
diff --git a/activesupport/test/core_ext/array_ext_test.rb b/activesupport/test/core_ext/array_ext_test.rb
index e7617466c2..ebd6806416 100644
--- a/activesupport/test/core_ext/array_ext_test.rb
+++ b/activesupport/test/core_ext/array_ext_test.rb
@@ -358,15 +358,19 @@ class ArrayUniqByTests < Test::Unit::TestCase
end
end
-class ArrayExtRandomTests < Test::Unit::TestCase
+class ArrayExtRandomTests < ActiveSupport::TestCase
def test_random_element_from_array
- assert_nil [].rand
+ assert_nil [].random_element
Kernel.expects(:rand).with(1).returns(0)
- assert_equal 'x', ['x'].rand
+ assert_equal 'x', ['x'].random_element
Kernel.expects(:rand).with(3).returns(1)
- assert_equal 2, [1, 2, 3].rand
+ assert_equal 2, [1, 2, 3].random_element
+ end
+
+ def test_deprecated_rand_on_array
+ assert_deprecated { [].rand }
end
end
diff --git a/activesupport/test/core_ext/date_ext_test.rb b/activesupport/test/core_ext/date_ext_test.rb
index 2b66fd03d0..1bf118e3b7 100644
--- a/activesupport/test/core_ext/date_ext_test.rb
+++ b/activesupport/test/core_ext/date_ext_test.rb
@@ -1,7 +1,7 @@
require 'abstract_unit'
require 'active_support/time'
-class DateExtCalculationsTest < Test::Unit::TestCase
+class DateExtCalculationsTest < ActiveSupport::TestCase
def test_to_s
date = Date.new(2005, 2, 21)
assert_equal "2005-02-21", date.to_s
@@ -145,16 +145,20 @@ class DateExtCalculationsTest < Test::Unit::TestCase
assert_equal Date.new(2005,2,28), Date.new(2004,2,29).years_since(1) # 1 year since leap day
end
- def test_last_year
- assert_equal Date.new(2004,6,5), Date.new(2005,6,5).last_year
+ def test_last_year_is_deprecated
+ assert_deprecated { Date.today.last_year }
end
- def test_last_year_in_leap_years
- assert_equal Date.new(1999,2,28), Date.new(2000,2,29).last_year
+ def test_prev_year
+ assert_equal Date.new(2004,6,5), Date.new(2005,6,5).prev_year
end
- def test_last_year_in_calendar_reform
- assert_equal Date.new(1582,10,4), Date.new(1583,10,14).last_year
+ def test_prev_year_in_leap_years
+ assert_equal Date.new(1999,2,28), Date.new(2000,2,29).prev_year
+ end
+
+ def test_prev_year_in_calendar_reform
+ assert_equal Date.new(1582,10,4), Date.new(1583,10,14).prev_year
end
def test_next_year
@@ -225,8 +229,12 @@ class DateExtCalculationsTest < Test::Unit::TestCase
assert_equal Date.new(2005, 9, 30), Date.new(2005, 8, 31).next_month
end
- def test_last_month_on_31st
- assert_equal Date.new(2004, 2, 29), Date.new(2004, 3, 31).last_month
+ def test_last_month_is_deprecated
+ assert_deprecated { Date.today.last_month }
+ end
+
+ def test_prev_month_on_31st
+ assert_equal Date.new(2004, 2, 29), Date.new(2004, 3, 31).prev_month
end
def test_yesterday_constructor
diff --git a/activesupport/test/core_ext/date_time_ext_test.rb b/activesupport/test/core_ext/date_time_ext_test.rb
index f9af059acd..4780760a19 100644
--- a/activesupport/test/core_ext/date_time_ext_test.rb
+++ b/activesupport/test/core_ext/date_time_ext_test.rb
@@ -127,8 +127,8 @@ class DateTimeExtCalculationsTest < Test::Unit::TestCase
assert_equal DateTime.civil(2005,2,28,10), DateTime.civil(2004,2,29,10,0,0).years_since(1) # 1 year since leap day
end
- def test_last_year
- assert_equal DateTime.civil(2004,6,5,10), DateTime.civil(2005,6,5,10,0,0).last_year
+ def test_prev_year
+ assert_equal DateTime.civil(2004,6,5,10), DateTime.civil(2005,6,5,10,0,0).prev_year
end
def test_next_year
@@ -200,8 +200,8 @@ class DateTimeExtCalculationsTest < Test::Unit::TestCase
assert_equal DateTime.civil(2005, 9, 30), DateTime.civil(2005, 8, 31).next_month
end
- def test_last_month_on_31st
- assert_equal DateTime.civil(2004, 2, 29), DateTime.civil(2004, 3, 31).last_month
+ def test_prev_month_on_31st
+ assert_equal DateTime.civil(2004, 2, 29), DateTime.civil(2004, 3, 31).prev_month
end
def test_xmlschema
diff --git a/activesupport/test/core_ext/time_ext_test.rb b/activesupport/test/core_ext/time_ext_test.rb
index 342d6ab577..30ee1d1652 100644
--- a/activesupport/test/core_ext/time_ext_test.rb
+++ b/activesupport/test/core_ext/time_ext_test.rb
@@ -1,7 +1,7 @@
require 'abstract_unit'
require 'active_support/time'
-class TimeExtCalculationsTest < Test::Unit::TestCase
+class TimeExtCalculationsTest < ActiveSupport::TestCase
def test_seconds_since_midnight
assert_equal 1,Time.local(2005,1,1,0,0,1).seconds_since_midnight
assert_equal 60,Time.local(2005,1,1,0,1,0).seconds_since_midnight
@@ -166,8 +166,12 @@ class TimeExtCalculationsTest < Test::Unit::TestCase
# assert_equal Time.local(2182,6,5,10), Time.local(2005,6,5,10,0,0).years_since(177)
end
- def test_last_year
- assert_equal Time.local(2004,6,5,10), Time.local(2005,6,5,10,0,0).last_year
+ def test_last_year_is_deprecated
+ assert_deprecated { Time.now.last_year }
+ end
+
+ def test_prev_year
+ assert_equal Time.local(2004,6,5,10), Time.local(2005,6,5,10,0,0).prev_year
end
def test_next_year
@@ -615,8 +619,12 @@ class TimeExtCalculationsTest < Test::Unit::TestCase
assert_equal Time.local(2005, 9, 30), Time.local(2005, 8, 31).next_month
end
- def test_last_month_on_31st
- assert_equal Time.local(2004, 2, 29), Time.local(2004, 3, 31).last_month
+ def test_last_month_is_deprecated
+ assert_deprecated { Time.now.last_month }
+ end
+
+ def test_prev_month_on_31st
+ assert_equal Time.local(2004, 2, 29), Time.local(2004, 3, 31).prev_month
end
def test_xmlschema_is_available
diff --git a/activesupport/test/lazy_load_hooks_test.rb b/activesupport/test/lazy_load_hooks_test.rb
new file mode 100644
index 0000000000..58ccc14324
--- /dev/null
+++ b/activesupport/test/lazy_load_hooks_test.rb
@@ -0,0 +1,67 @@
+require 'abstract_unit'
+
+class LazyLoadHooksTest < ActiveSupport::TestCase
+ def test_basic_hook
+ i = 0
+ ActiveSupport.on_load(:basic_hook) { i += 1 }
+ ActiveSupport.run_load_hooks(:basic_hook)
+ assert_equal 1, i
+ end
+
+ def test_hook_registered_after_run
+ i = 0
+ ActiveSupport.run_load_hooks(:registered_after)
+ assert_equal 0, i
+ ActiveSupport.on_load(:registered_after) { i += 1 }
+ assert_equal 1, i
+ end
+
+ def test_hook_receives_a_context
+ i = 0
+ ActiveSupport.on_load(:contextual) { i += incr }
+ assert_equal 0, i
+ ActiveSupport.run_load_hooks(:contextual, FakeContext.new(2))
+ assert_equal 2, i
+ end
+
+ def test_hook_receives_a_context_afterward
+ i = 0
+ ActiveSupport.run_load_hooks(:contextual_after, FakeContext.new(2))
+ assert_equal 0, i
+ ActiveSupport.on_load(:contextual_after) { i += incr }
+ assert_equal 2, i
+ end
+
+ def test_hook_with_yield_true
+ i = 0
+ ActiveSupport.on_load(:contextual_yield, :yield => true) do |obj|
+ i += obj.incr + incr_amt
+ end
+ assert_equal 0, i
+ ActiveSupport.run_load_hooks(:contextual_yield, FakeContext.new(2))
+ assert_equal 7, i
+ end
+
+ def test_hook_with_yield_true_afterward
+ i = 0
+ ActiveSupport.run_load_hooks(:contextual_yield_after, FakeContext.new(2))
+ assert_equal 0, i
+ ActiveSupport.on_load(:contextual_yield_after, :yield => true) do |obj|
+ i += obj.incr + incr_amt
+ end
+ assert_equal 7, i
+ end
+
+private
+
+ def incr_amt
+ 5
+ end
+
+ class FakeContext
+ attr_reader :incr
+ def initialize(incr)
+ @incr = incr
+ end
+ end
+end \ No newline at end of file
diff --git a/railties/guides/source/active_record_basics.textile b/railties/guides/source/active_record_basics.textile
index 226f1b134b..d81e461e63 100644
--- a/railties/guides/source/active_record_basics.textile
+++ b/railties/guides/source/active_record_basics.textile
@@ -104,6 +104,14 @@ class Product < ActiveRecord::Base
set_table_name "PRODUCT"
end
</ruby>
+If you do so, you will have to define manually the class name that is hosting the fixtures (class_name.yml) using the +set_fixture_class+ method in your test definition:
+<ruby>
+class FunnyJoke < ActiveSupport::TestCase
+ set_fixture_class :funny_jokes => 'Joke'
+ fixtures :funny_jokes
+ ...
+end
+</ruby>
It's also possible to override the column that should be used as the table's primary key. Use the +ActiveRecord::Base.set_primary_key+ method for that:
<ruby>
@@ -201,4 +209,4 @@ Active Record callbacks allow you to attach code to certain events in the life-c
h3. Migrations
-Rails provides a domain-specific language for managing a database schema called migrations. Migrations are stored in files which are executed against any database that Active Record support using rake. Rails keeps track of which files have been committed to the database and provides rollback features. You can learn more about migrations in the "Active Record Migrations guide":migrations.html \ No newline at end of file
+Rails provides a domain-specific language for managing a database schema called migrations. Migrations are stored in files which are executed against any database that Active Record support using rake. Rails keeps track of which files have been committed to the database and provides rollback features. You can learn more about migrations in the "Active Record Migrations guide":migrations.html
diff --git a/railties/guides/source/active_support_core_extensions.textile b/railties/guides/source/active_support_core_extensions.textile
index 72194e567a..08fddd2926 100644
--- a/railties/guides/source/active_support_core_extensions.textile
+++ b/railties/guides/source/active_support_core_extensions.textile
@@ -2660,13 +2660,13 @@ Active Support defines +Date.current+ to be today in the current time zone. That
h5. Named dates
-h6. +last_year+, +next_year+
+h6. +prev_year+, +next_year+
-The methods +last_year+ and +next_year+ return a date with the same day/month in the last or next year:
+In Ruby 1.9 +prev_year+ and +next_year+ return a date with the same day/month in the last or next year:
<ruby>
d = Date.new(2010, 5, 8) # => Sat, 08 May 2010
-d.last_year # => Fri, 08 May 2009
+d.prev_year # => Fri, 08 May 2009
d.next_year # => Sun, 08 May 2011
</ruby>
@@ -2674,29 +2674,33 @@ If date is the 29th of February of a leap year, you obtain the 28th:
<ruby>
d = Date.new(2000, 2, 29) # => Tue, 29 Feb 2000
-d.last_year # => Sun, 28 Feb 1999
+d.prev_year # => Sun, 28 Feb 1999
d.next_year # => Wed, 28 Feb 2001
</ruby>
-h6. +last_month+, +next_month+
+Active Support defines these methods as well for Ruby 1.8.
-The methods +last_month+ and +next_month+ return the date with the same day in the last or next month:
+h6. +prev_month+, +next_month+
+
+In Ruby 1.9 +prev_month+ and +next_month+ return the date with the same day in the last or next month:
<ruby>
d = Date.new(2010, 5, 8) # => Sat, 08 May 2010
-d.last_month # => Thu, 08 Apr 2010
+d.prev_month # => Thu, 08 Apr 2010
d.next_month # => Tue, 08 Jun 2010
</ruby>
If such a day does not exist, the last day of the corresponding month is returned:
<ruby>
-Date.new(2000, 5, 31).last_month # => Sun, 30 Apr 2000
-Date.new(2000, 3, 31).last_month # => Tue, 29 Feb 2000
+Date.new(2000, 5, 31).prev_month # => Sun, 30 Apr 2000
+Date.new(2000, 3, 31).prev_month # => Tue, 29 Feb 2000
Date.new(2000, 5, 31).next_month # => Fri, 30 Jun 2000
Date.new(2000, 1, 31).next_month # => Tue, 29 Feb 2000
</ruby>
+Active Support defines these methods as well for Ruby 1.8.
+
h6. +beginning_of_week+, +end_of_week+
The methods +beginning_of_week+ and +end_of_week+ return the dates for the beginning and end of week, assuming weeks start on Monday:
diff --git a/railties/guides/source/initialization.textile b/railties/guides/source/initialization.textile
index 9ce27fa331..96d6998e1c 100644
--- a/railties/guides/source/initialization.textile
+++ b/railties/guides/source/initialization.textile
@@ -2379,7 +2379,6 @@ Now that we've referenced that class, it will be required for us. You'll notice
* initialize_subscriber
* set_clear_dependencies_hook
* initialize_dependency_mechanism
-* bootstrap_load_path
These are all defined using the +initializer+ method:
@@ -2930,7 +2929,6 @@ With +@@autoloads+ being
* initialize_subscriber
* set_clear_dependencies_hook
* initialize_dependency_mechanism
-* bootstrap_load_path
h4. Active Support Initializers
diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb
index d39f9a2ae9..a3b3a56bc8 100644
--- a/railties/lib/rails/application.rb
+++ b/railties/lib/rails/application.rb
@@ -12,7 +12,7 @@ module Rails
# points to it.
#
# In other words, Rails::Application is Singleton and whenever you are accessing
- # Rails::Application.config or YourApplication::Application.config, you are actually
+ # Rails::Application.config or YourApplication::Application.config, you are actually
# accessing YourApplication::Application.instance.config.
#
# == Initialization
@@ -40,7 +40,7 @@ module Rails
#
# The Application is also responsible for building the middleware stack and setting up
# both application and engines metals.
- #
+ #
class Application < Engine
autoload :Bootstrap, 'rails/application/bootstrap'
autoload :Configurable, 'rails/application/configurable'
@@ -69,6 +69,7 @@ module Rails
raise "You cannot have more than one Rails::Application" if Rails.application
super
Rails.application = base.instance
+ ActiveSupport.run_load_hooks(:before_configuration, base.instance)
end
def respond_to?(*args)
@@ -82,7 +83,7 @@ module Rails
end
end
- delegate :metal_loader, :to => :config
+ delegate :middleware, :metal_loader, :to => :config
def require_environment!
environment = paths.config.environment.to_a.first
@@ -125,7 +126,7 @@ module Rails
end
def app
- @app ||= middleware.build(routes)
+ @app ||= config.middleware.build(routes)
end
def call(env)
diff --git a/railties/lib/rails/application/bootstrap.rb b/railties/lib/rails/application/bootstrap.rb
index 022e1a91d8..e62eed8a87 100644
--- a/railties/lib/rails/application/bootstrap.rb
+++ b/railties/lib/rails/application/bootstrap.rb
@@ -10,7 +10,8 @@ module Rails
require environment if environment
end
- initializer :load_all_active_support do
+ initializer :load_active_support do
+ require 'active_support/dependencies'
require "active_support/all" unless config.active_support.bare
end
@@ -18,7 +19,6 @@ module Rails
# Used by Passenger to ensure everything's loaded before forking and
# to avoid autoload race conditions in JRuby.
initializer :preload_frameworks do
- require 'active_support/dependencies'
ActiveSupport::Autoload.eager_autoload! if config.preload_frameworks
end
@@ -66,8 +66,8 @@ module Rails
ActiveSupport::Dependencies.mechanism = config.cache_classes ? :require : :load
end
- initializer :bootstrap_load_path do
- # This is just an initializer used as hook so all load paths are loaded together
+ initializer :bootstrap_hook do |app|
+ ActiveSupport.run_load_hooks(:before_initialize, app)
end
end
end
diff --git a/railties/lib/rails/application/configuration.rb b/railties/lib/rails/application/configuration.rb
index 1ad77fdfec..9353fbefef 100644
--- a/railties/lib/rails/application/configuration.rb
+++ b/railties/lib/rails/application/configuration.rb
@@ -33,7 +33,7 @@ module Rails
end
def middleware
- @middleware ||= default_middleware_stack
+ @middleware ||= app_middleware.merge_into(default_middleware_stack)
end
def metal_loader
@@ -150,10 +150,10 @@ module Rails
middleware.use('::ActionDispatch::Cookies')
middleware.use(lambda { session_store }, lambda { session_options })
middleware.use('::ActionDispatch::Flash', :if => lambda { session_store })
- middleware.use(lambda { metal_loader.build_middleware(metals) }, :if => lambda { metal_loader.metals.any? })
- middleware.use('ActionDispatch::ParamsParser')
+ middleware.use('::ActionDispatch::ParamsParser')
middleware.use('::Rack::MethodOverride')
middleware.use('::ActionDispatch::Head')
+ middleware.use(lambda { metal_loader.build_middleware(metals) }, :if => lambda { metal_loader.metals.any? })
end
end
end
diff --git a/railties/lib/rails/application/finisher.rb b/railties/lib/rails/application/finisher.rb
index 94507bb387..fbab4d5515 100644
--- a/railties/lib/rails/application/finisher.rb
+++ b/railties/lib/rails/application/finisher.rb
@@ -35,12 +35,17 @@ module Rails
app
end
- initializer :after_initialize do
- config.after_initialize_blocks.each do |block|
- block.call(self)
+ initializer :eager_load! do
+ if config.cache_classes && !$rails_rake_task
+ ActiveSupport.run_load_hooks(:before_eager_load, self)
+ railties.all(&:eager_load!)
end
end
+ initializer :finisher_hook do
+ ActiveSupport.run_load_hooks(:after_initialize, self)
+ end
+
# Disable dependency loading during request cycle
initializer :disable_dependency_loading do
if config.cache_classes && !config.dependency_loading
diff --git a/railties/lib/rails/configuration.rb b/railties/lib/rails/configuration.rb
index bd404f4a14..ee0fca6592 100644
--- a/railties/lib/rails/configuration.rb
+++ b/railties/lib/rails/configuration.rb
@@ -5,6 +5,37 @@ require 'rails/rack'
module Rails
module Configuration
+ class MiddlewareStackProxy #:nodoc:
+ def initialize
+ @operations = []
+ end
+
+ def insert_before(*args, &block)
+ @operations << [:insert_before, args, block]
+ end
+
+ alias :insert :insert_before
+
+ def insert_after(*args, &block)
+ @operations << [:insert_after, args, block]
+ end
+
+ def swap(*args, &block)
+ @operations << [:swap, args, block]
+ end
+
+ def use(*args, &block)
+ @operations << [:use, args, block]
+ end
+
+ def merge_into(other)
+ @operations.each do |operation, args, block|
+ other.send(operation, *args, &block)
+ end
+ other
+ end
+ end
+
class Generators #:nodoc:
attr_accessor :aliases, :options, :templates, :fallbacks, :colorize_logging
diff --git a/railties/lib/rails/engine.rb b/railties/lib/rails/engine.rb
index ab0ead65a9..b44755820c 100644
--- a/railties/lib/rails/engine.rb
+++ b/railties/lib/rails/engine.rb
@@ -45,7 +45,7 @@ module Rails
# app.middleware.use MyEngine::Middleware
# end
# end
- #
+ #
# == Paths
#
# Since Rails 3.0, both your Application and Engines do not have hardcoded paths.
@@ -125,15 +125,24 @@ module Rails
end
end
- delegate :middleware, :paths, :root, :to => :config
+ delegate :paths, :root, :to => :config
def load_tasks
super
config.paths.lib.tasks.to_a.sort.each { |ext| load(ext) }
end
+ def eager_load!
+ config.eager_load_paths.each do |load_path|
+ matcher = /\A#{Regexp.escape(load_path)}\/(.*)\.rb\Z/
+ Dir.glob("#{load_path}/**/*.rb").sort.each do |file|
+ require_dependency file.sub(matcher, '\1')
+ end
+ end
+ end
+
# Add configured load paths to ruby load paths and remove duplicates.
- initializer :set_load_path, :before => :bootstrap_load_path do
+ initializer :set_load_path, :before => :bootstrap_hook do
config.load_paths.reverse_each do |path|
$LOAD_PATH.unshift(path) if File.directory?(path)
end
@@ -142,7 +151,10 @@ module Rails
# Set the paths from which Rails will automatically load source files,
# and the load_once paths.
- initializer :set_autoload_paths, :before => :bootstrap_load_path do |app|
+ #
+ # This needs to be an initializer, since it needs to run once
+ # per engine and get the engine as a block parameter
+ initializer :set_autoload_paths, :before => :bootstrap_hook do |app|
ActiveSupport::Dependencies.load_paths.unshift(*config.load_paths)
if reloadable?(app)
@@ -200,17 +212,9 @@ module Rails
end
end
- initializer :load_app_classes do |app|
- next if $rails_rake_task
-
- if app.config.cache_classes
- config.eager_load_paths.each do |load_path|
- matcher = /\A#{Regexp.escape(load_path)}\/(.*)\.rb\Z/
- Dir.glob("#{load_path}/**/*.rb").sort.each do |file|
- require_dependency file.sub(matcher, '\1')
- end
- end
- end
+ initializer :engines_blank_point do
+ # We need this initializer so all extra initializers added in engines are
+ # consistently executed after all the initializers above across all engines.
end
protected
diff --git a/railties/lib/rails/generators/actions.rb b/railties/lib/rails/generators/actions.rb
index a31932906d..7af329bbf0 100644
--- a/railties/lib/rails/generators/actions.rb
+++ b/railties/lib/rails/generators/actions.rb
@@ -54,7 +54,8 @@ module Rails
name, version = args
# Deal with deprecated options
- { :env => :only, :lib => :require_as }.each do |old, new|
+ { :env => :group, :only => :group,
+ :lib => :require, :require_as => :require }.each do |old, new|
next unless options[old]
options[new] = options.delete(old)
ActiveSupport::Deprecation.warn "#{old.inspect} option in gem is deprecated, use #{new.inspect} instead"
diff --git a/railties/lib/rails/generators/generated_attribute.rb b/railties/lib/rails/generators/generated_attribute.rb
index e962308585..f01934f946 100644
--- a/railties/lib/rails/generators/generated_attribute.rb
+++ b/railties/lib/rails/generators/generated_attribute.rb
@@ -9,12 +9,13 @@ module Rails
def field_type
@field_type ||= case type
- when :integer, :float, :decimal then :text_field
- when :datetime, :timestamp, :time then :datetime_select
- when :date then :date_select
- when :string then :text_field
- when :text then :text_area
- when :boolean then :check_box
+ when :integer, :float, :decimal then :text_field
+ when :time then :time_select
+ when :datetime, :timestamp then :datetime_select
+ when :date then :date_select
+ when :string then :text_field
+ when :text then :text_area
+ when :boolean then :check_box
else
:text_field
end
diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb
index 0a0b033738..ee44acc2fa 100644
--- a/railties/lib/rails/generators/rails/app/app_generator.rb
+++ b/railties/lib/rails/generators/rails/app/app_generator.rb
@@ -149,7 +149,7 @@ module Rails
# can change in Ruby 1.8.7 when we FileUtils.cd.
RAILS_DEV_PATH = File.expand_path("../../../../../..", File.dirname(__FILE__))
- RESERVED_NAMES = %w[generate console server dbconsole
+ RESERVED_NAMES = %w[generate g console c server s dbconsole db
application destroy benchmarker profiler
plugin runner test]
diff --git a/railties/lib/rails/generators/test_case.rb b/railties/lib/rails/generators/test_case.rb
index 6b97c69d8d..952400e049 100644
--- a/railties/lib/rails/generators/test_case.rb
+++ b/railties/lib/rails/generators/test_case.rb
@@ -189,6 +189,18 @@ module Rails
end
alias :assert_method :assert_instance_method
+ # Asserts the given field name gets translated to an attribute type
+ # properly.
+ #
+ # assert_field_type :date, :date_select
+ #
+ def assert_field_type(name, attribute_type)
+ assert_equal(
+ Rails::Generators::GeneratedAttribute.new('test', name.to_s).field_type,
+ attribute_type
+ )
+ end
+
# Runs the generator configured for this class. The first argument is an array like
# command line arguments:
#
diff --git a/railties/lib/rails/info.rb b/railties/lib/rails/info.rb
index 5a496f6536..e9c3ebe685 100644
--- a/railties/lib/rails/info.rb
+++ b/railties/lib/rails/info.rb
@@ -35,20 +35,6 @@ module Rails
end
end
- def edge_rails_revision(info = git_info)
- info[/commit ([a-z0-9-]+)/, 1] || freeze_edge_version
- end
-
- def freeze_edge_version
- if File.exist?(rails_vendor_root)
- begin
- Dir[File.join(rails_vendor_root, 'REVISION_*')].first.scan(/_(\d+)$/).first.first
- rescue
- Dir[File.join(rails_vendor_root, 'TAG_*')].first.scan(/_(.+)$/).first.first rescue 'unknown'
- end
- end
- end
-
def to_s
column_width = properties.names.map {|name| name.length}.max
info = properties.map do |name, value|
@@ -75,20 +61,6 @@ module Rails
table << '</table>'
end
end
-
- protected
- def rails_vendor_root
- @rails_vendor_root ||= "#{Rails.root}/vendor/rails"
- end
-
- def git_info
- env_lang, ENV['LC_ALL'] = ENV['LC_ALL'], 'C'
- Dir.chdir(rails_vendor_root) do
- silence_stderr { `git log -n 1` }
- end
- ensure
- ENV['LC_ALL'] = env_lang
- end
end
# The Ruby version and platform, e.g. "1.8.2 (powerpc-darwin8.2.0)".
@@ -120,11 +92,6 @@ module Rails
Rails.configuration.middleware.active.map(&:inspect)
end
- # The Rails Git revision, if it's checked out into vendor/rails.
- property 'Edge Rails revision' do
- edge_rails_revision
- end
-
# The application's location on the filesystem.
property 'Application root' do
File.expand_path(Rails.root)
diff --git a/railties/lib/rails/railtie.rb b/railties/lib/rails/railtie.rb
index b6b57bc5b5..1dba6e1538 100644
--- a/railties/lib/rails/railtie.rb
+++ b/railties/lib/rails/railtie.rb
@@ -197,6 +197,9 @@ module Rails
end
end
+ def eager_load!
+ end
+
def rake_tasks
self.class.rake_tasks
end
diff --git a/railties/lib/rails/railtie/configuration.rb b/railties/lib/rails/railtie/configuration.rb
index 16eccaccc4..4e6f94c534 100644
--- a/railties/lib/rails/railtie/configuration.rb
+++ b/railties/lib/rails/railtie/configuration.rb
@@ -7,6 +7,15 @@ module Rails
@@options ||= {}
end
+ # This allows you to modify the application's middlewares from Engines.
+ #
+ # All operations you run on the app_middleware will be replayed on the
+ # application once it is defined and the default_middlewares are
+ # created
+ def app_middleware
+ @@app_middleware ||= Rails::Configuration::MiddlewareStackProxy.new
+ end
+
# Holds generators configuration:
#
# config.generators do |g|
@@ -28,12 +37,20 @@ module Rails
end
end
- def after_initialize_blocks
- @@after_initialize_blocks ||= []
+ def before_configuration(&block)
+ ActiveSupport.on_load(:before_configuration, :yield => true, &block)
+ end
+
+ def before_eager_load(&block)
+ ActiveSupport.on_load(:before_eager_load, :yield => true, &block)
+ end
+
+ def before_initialize(&block)
+ ActiveSupport.on_load(:before_initialize, :yield => true, &block)
end
- def after_initialize(&blk)
- after_initialize_blocks << blk if blk
+ def after_initialize(&block)
+ ActiveSupport.on_load(:after_initialize, :yield => true, &block)
end
def to_prepare_blocks
diff --git a/railties/lib/rails/tasks/framework.rake b/railties/lib/rails/tasks/framework.rake
index 738f7f5301..063a393bfc 100644
--- a/railties/lib/rails/tasks/framework.rake
+++ b/railties/lib/rails/tasks/framework.rake
@@ -30,6 +30,28 @@ namespace :rails do
generator.apply template, :verbose => false
end
+ namespace :templates do
+ desc "Copy all the templates from rails to the application directory for customization. Already existing local copies will be overwritten"
+ task :copy do
+ generators_lib = File.expand_path("../../generators", __FILE__)
+ project_templates = "#{Rails.root}/lib/templates"
+
+ default_templates = { "erb" => %w{controller mailer scaffold},
+ "rails" => %w{controller helper metal scaffold_controller stylesheets} }
+
+ default_templates.each do |type, names|
+ local_template_type_dir = File.join(project_templates, type)
+ FileUtils.mkdir_p local_template_type_dir
+
+ names.each do |name|
+ dst_name = File.join(local_template_type_dir, name)
+ src_name = File.join(generators_lib, type, name, "templates")
+ FileUtils.cp_r src_name, dst_name
+ end
+ end
+ end
+ end
+
namespace :update do
def invoke_from_app_generator(method)
app_generator.invoke(method)
diff --git a/railties/lib/rails/tasks/routes.rake b/railties/lib/rails/tasks/routes.rake
index 42e01d5e51..41619bc1f8 100644
--- a/railties/lib/rails/tasks/routes.rake
+++ b/railties/lib/rails/tasks/routes.rake
@@ -3,7 +3,11 @@ task :routes => :environment do
Rails::Application.reload_routes!
all_routes = ENV['CONTROLLER'] ? Rails.application.routes.routes.select { |route| route.defaults[:controller] == ENV['CONTROLLER'] } : Rails.application.routes.routes
routes = all_routes.collect do |route|
- name = Rails.application.routes.named_routes.routes.index(route).to_s
+ # TODO: The :index method is deprecated in 1.9 in favor of :key
+ # but we don't have :key in 1.8.7. We can remove this check when
+ # stop supporting 1.8.x
+ key_method = Hash.method_defined?('key') ? 'key' : 'index'
+ name = Rails.application.routes.named_routes.routes.send(key_method, route).to_s
reqs = route.requirements.empty? ? "" : route.requirements.inspect
{:name => name, :verb => route.verb.to_s, :path => route.path, :reqs => reqs}
end
diff --git a/railties/test/application/initializers/initializers_test.rb b/railties/test/application/initializers/hooks_test.rb
index 2e6a707175..198abbe861 100644
--- a/railties/test/application/initializers/initializers_test.rb
+++ b/railties/test/application/initializers/hooks_test.rb
@@ -16,29 +16,34 @@ module ApplicationTests
assert $foo
end
- test "after_initialize block works correctly" do
+ test "hooks block works correctly without cache classes (before_eager_load is not called)" do
add_to_config <<-RUBY
+ $initialization_callbacks = []
config.root = "#{app_path}"
- config.after_initialize { $test_after_initialize_block1 = "success" }
- config.after_initialize { $test_after_initialize_block2 = "congratulations" }
+ config.cache_classes = false
+ config.before_configuration { $initialization_callbacks << 1 }
+ config.before_initialize { $initialization_callbacks << 2 }
+ config.before_eager_load { Boom }
+ config.after_initialize { $initialization_callbacks << 3 }
RUBY
- require "#{app_path}/config/environment"
- assert_equal "success", $test_after_initialize_block1
- assert_equal "congratulations", $test_after_initialize_block2
+ require "#{app_path}/config/environment"
+ assert_equal [1,2,3], $initialization_callbacks
end
- test "after_initialize block works correctly when no block is passed" do
+ test "hooks block works correctly with cache classes" do
add_to_config <<-RUBY
+ $initialization_callbacks = []
config.root = "#{app_path}"
- config.after_initialize { $test_after_initialize_block1 = "success" }
- config.after_initialize # don't pass a block, this is what we're testing!
- config.after_initialize { $test_after_initialize_block2 = "congratulations" }
+ config.cache_classes = true
+ config.before_configuration { $initialization_callbacks << 1 }
+ config.before_initialize { $initialization_callbacks << 2 }
+ config.before_eager_load { $initialization_callbacks << 3 }
+ config.after_initialize { $initialization_callbacks << 4 }
RUBY
- require "#{app_path}/config/environment"
- assert_equal "success", $test_after_initialize_block1
- assert_equal "congratulations", $test_after_initialize_block2
+ require "#{app_path}/config/environment"
+ assert_equal [1,2,3,4], $initialization_callbacks
end
test "after_initialize runs after frameworks have been initialized" do
@@ -61,7 +66,7 @@ module ApplicationTests
RUBY
require "#{app_path}/config/environment"
- assert [:to_prepare, :after_initialize], $order
+ assert_equal [:to_prepare, :after_initialize], $order
end
test "after_initialize happens after to_prepare in production" do
@@ -75,7 +80,7 @@ module ApplicationTests
require "#{app_path}/config/application"
Rails.env.replace "production"
require "#{app_path}/config/environment"
- assert [:to_prepare, :after_initialize], $order
+ assert_equal [:to_prepare, :after_initialize], $order
end
end
end
diff --git a/railties/test/generators/actions_test.rb b/railties/test/generators/actions_test.rb
index e6fab93a87..65fbf61902 100644
--- a/railties/test/generators/actions_test.rb
+++ b/railties/test/generators/actions_test.rb
@@ -86,8 +86,13 @@ class ActionsTest < Rails::Generators::TestCase
action :gem, 'mislav-will-paginate', :lib => 'will-paginate', :source => 'http://gems.github.com'
end
- assert_file 'Gemfile', /gem "mislav\-will\-paginate", :require_as => "will\-paginate"/
+ assert_deprecated do
+ action :gem, 'thoughtbot-factory_girl', :require_as => 'factory_girl', :source => 'http://gems.github.com'
+ end
+
+ assert_file 'Gemfile', /gem "mislav\-will\-paginate", :require => "will\-paginate"/
assert_file 'Gemfile', /source "http:\/\/gems\.github\.com"/
+ assert_file 'Gemfile', /gem "thoughtbot-factory_girl", :require => "factory_girl"/
end
def test_gem_with_env_should_include_all_dependencies_in_gemfile
@@ -97,7 +102,12 @@ class ActionsTest < Rails::Generators::TestCase
action :gem, 'rspec', :env => %w(development test)
end
- assert_file 'Gemfile', /gem "rspec", :only => \["development", "test"\]/
+ assert_deprecated do
+ action :gem, 'rspec-rails', :only => %w(development test)
+ end
+
+ assert_file 'Gemfile', /gem "rspec", :group => \["development", "test"\]/
+ assert_file 'Gemfile', /gem "rspec-rails", :group => \["development", "test"\]/
end
def test_gem_with_version_should_include_version_in_gemfile
diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb
index 8743defe82..3975a39ab1 100644
--- a/railties/test/generators/app_generator_test.rb
+++ b/railties/test/generators/app_generator_test.rb
@@ -70,8 +70,13 @@ class AppGeneratorTest < Rails::Generators::TestCase
end
def test_name_collision_raises_an_error
- content = capture(:stderr){ run_generator [File.join(destination_root, "generate")] }
- assert_equal "Invalid application name generate. Please give a name which does not match one of the reserved rails words.\n", content
+ reserved_words = %w[generate g console c server s dbconsole db
+ application destroy benchmarker profiler
+ plugin runner test]
+ reserved_words.each do |reserved|
+ content = capture(:stderr){ run_generator [File.join(destination_root, reserved)] }
+ assert_equal "Invalid application name #{reserved}. Please give a name which does not match one of the reserved rails words.\n", content
+ end
end
def test_invalid_database_option_raises_an_error
diff --git a/railties/test/generators/generated_attribute_test.rb b/railties/test/generators/generated_attribute_test.rb
new file mode 100644
index 0000000000..dacb06fb13
--- /dev/null
+++ b/railties/test/generators/generated_attribute_test.rb
@@ -0,0 +1,40 @@
+require 'generators/generators_test_helper'
+require 'rails/generators/generated_attribute'
+
+class GeneratedAttributeTest < Rails::Generators::TestCase
+ include GeneratorsTestHelper
+
+ def test_field_type_returns_text_field
+ %w(integer float decimal string).each do |name|
+ assert_field_type name, :text_field
+ end
+ end
+
+ def test_field_type_returns_datetime_select
+ %w(datetime timestamp).each do |name|
+ assert_field_type name, :datetime_select
+ end
+ end
+
+ def test_field_type_returns_time_select
+ assert_field_type 'time', :time_select
+ end
+
+ def test_field_type_returns_date_select
+ assert_field_type 'date', :date_select
+ end
+
+ def test_field_type_returns_text_area
+ assert_field_type 'text', :text_area
+ end
+
+ def test_field_type_returns_check_box
+ assert_field_type 'boolean', :check_box
+ end
+
+ def test_field_type_with_unknown_type_returns_text_field
+ %w(foo bar baz).each do |name|
+ assert_field_type name, :text_field
+ end
+ end
+end
diff --git a/railties/test/generators_test.rb b/railties/test/generators_test.rb
index 16f8f43b99..74a09d4bde 100644
--- a/railties/test/generators_test.rb
+++ b/railties/test/generators_test.rb
@@ -108,21 +108,10 @@ class GeneratorsTest < Rails::Generators::TestCase
assert_match /^ fixjour$/, output
end
- def test_rails_generators_does_not_show_activerecord_info_if_its_the_default
- output = capture(:stdout){ Rails::Generators.help }
- assert_no_match /ActiveRecord:/, output
- assert_no_match /^ active_record:model$/, output
- assert_no_match /^ active_record:fixjour$/, output
- end
-
- def test_rails_generators_shows_activerecord_info_if_its_not_the_default
- Rails::Generators.options[:rails][:orm] = :data_mapper
+ def test_rails_generators_does_not_show_activerecord_hooks
output = capture(:stdout){ Rails::Generators.help }
assert_match /ActiveRecord:/, output
- assert_match /^ active_record:model$/, output
assert_match /^ active_record:fixjour$/, output
- ensure
- Rails::Generators.options[:rails][:orm] = :active_record
end
def test_no_color_sets_proper_shell
diff --git a/railties/test/rails_info_test.rb b/railties/test/rails_info_test.rb
index fc28d7e912..1da66062aa 100644
--- a/railties/test/rails_info_test.rb
+++ b/railties/test/rails_info_test.rb
@@ -14,27 +14,6 @@ class InfoTest < ActiveSupport::TestCase
silence_warnings { load 'rails/info.rb' }
end
- def test_edge_rails_revision_not_set_when_svn_info_is_empty
- Rails::Info.property 'Test that this will not be defined' do
- Rails::Info.edge_rails_revision ''
- end
- assert !property_defined?('Test that this will not be defined')
- end
-
- def test_edge_rails_revision_extracted_from_svn_info
- Rails::Info.property 'Test Edge Rails revision' do
- Rails::Info.edge_rails_revision <<-EOS
- commit 420c4b3d8878156d04f45e47050ddc62ae00c68c
- Author: David Heinemeier Hansson <david@loudthinking.com>
- Date: Sun Apr 13 17:33:27 2008 -0500
-
- Added Rails.public_path to control where HTML and assets are expected to be loaded from
-EOS
- end
-
- assert_property 'Test Edge Rails revision', '420c4b3d8878156d04f45e47050ddc62ae00c68c'
- end
-
def test_property_with_block_swallows_exceptions_and_ignores_property
assert_nothing_raised do
Rails::Info.module_eval do
diff --git a/railties/test/railties/engine_test.rb b/railties/test/railties/engine_test.rb
index 40ac11fa03..b3f65fd00d 100644
--- a/railties/test/railties/engine_test.rb
+++ b/railties/test/railties/engine_test.rb
@@ -28,5 +28,31 @@ module RailtiesTest
boot_rails
assert !Rails::Engine.respond_to?(:config)
end
+
+ test "initializers are executed after application configuration initializers" do
+ @plugin.write "lib/bukkits.rb", <<-RUBY
+ class Bukkits
+ class Engine < ::Rails::Engine
+ initializer "dummy_initializer" do
+ end
+ end
+ end
+ RUBY
+
+ boot_rails
+
+ initializers = Rails.application.initializers
+ index = initializers.index { |i| i.name == "dummy_initializer" }
+ selection = initializers[(index-3)..(index)].map(&:name).map(&:to_s)
+
+ assert_equal %w(
+ load_config_initializers
+ load_config_initializers
+ engines_blank_point
+ dummy_initializer
+ ), selection
+
+ assert index < initializers.index { |i| i.name == :build_middleware_stack }
+ end
end
end