aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.travis.yml3
-rw-r--r--CONTRIBUTING.md2
-rw-r--r--Gemfile2
-rw-r--r--RELEASING_RAILS.rdoc11
-rw-r--r--actionmailer/CHANGELOG.md29
-rw-r--r--actionmailer/lib/action_mailer/base.rb2
-rw-r--r--actionmailer/lib/action_mailer/test_helper.rb51
-rw-r--r--actionmailer/lib/rails/generators/mailer/templates/mailer.rb2
-rw-r--r--actionmailer/test/base_test.rb2
-rw-r--r--actionmailer/test/test_helper_test.rb47
-rw-r--r--actionpack/CHANGELOG.md36
-rw-r--r--actionpack/actionpack.gemspec2
-rw-r--r--actionpack/lib/abstract_controller/base.rb16
-rw-r--r--actionpack/lib/abstract_controller/callbacks.rb42
-rw-r--r--actionpack/lib/action_controller.rb1
-rw-r--r--actionpack/lib/action_controller/base.rb1
-rw-r--r--actionpack/lib/action_controller/metal/conditional_get.rb4
-rw-r--r--actionpack/lib/action_controller/metal/hide_actions.rb40
-rw-r--r--actionpack/lib/action_controller/metal/request_forgery_protection.rb9
-rw-r--r--actionpack/lib/action_dispatch/http/cache.rb7
-rw-r--r--actionpack/lib/action_dispatch/testing/test_response.rb7
-rw-r--r--actionpack/test/abstract/callbacks_test.rb8
-rw-r--r--actionpack/test/controller/base_test.rb39
-rw-r--r--actionpack/test/controller/filters_test.rb34
-rw-r--r--actionpack/test/controller/integration_test.rb23
-rw-r--r--actionpack/test/controller/render_test.rb65
-rw-r--r--actionpack/test/controller/request_forgery_protection_test.rb60
-rw-r--r--actionpack/test/dispatch/static_test.rb1
-rw-r--r--actionview/CHANGELOG.md5
-rw-r--r--actionview/lib/action_view/helpers/atom_feed_helper.rb5
-rw-r--r--actionview/lib/action_view/helpers/tags/base.rb11
-rw-r--r--actionview/lib/action_view/model_naming.rb2
-rw-r--r--actionview/lib/action_view/record_identifier.rb2
-rw-r--r--actionview/test/actionpack/controller/layout_test.rb1
-rw-r--r--actionview/test/template/atom_feed_helper_test.rb24
-rw-r--r--actionview/test/template/form_helper_test.rb28
-rw-r--r--activejob/CHANGELOG.md43
-rw-r--r--activejob/lib/active_job/test_helper.rb42
-rw-r--r--activejob/test/cases/test_helper_test.rb59
-rw-r--r--activemodel/CHANGELOG.md6
-rw-r--r--activemodel/lib/active_model/dirty.rb1
-rw-r--r--activemodel/lib/active_model/errors.rb4
-rw-r--r--activemodel/lib/active_model/secure_password.rb7
-rw-r--r--activemodel/lib/active_model/validations/acceptance.rb8
-rw-r--r--activemodel/test/cases/dirty_test.rb4
-rw-r--r--activemodel/test/cases/validations/acceptance_validation_test.rb6
-rw-r--r--activemodel/test/cases/validations/callbacks_test.rb26
-rw-r--r--activerecord/CHANGELOG.md36
-rw-r--r--activerecord/RUNNING_UNIT_TESTS.rdoc6
-rw-r--r--activerecord/lib/active_record/associations.rb4
-rw-r--r--activerecord/lib/active_record/attribute.rb8
-rw-r--r--activerecord/lib/active_record/attribute_methods/before_type_cast.rb5
-rw-r--r--activerecord/lib/active_record/attribute_methods/dirty.rb4
-rw-r--r--activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb33
-rw-r--r--activerecord/lib/active_record/attributes.rb7
-rw-r--r--activerecord/lib/active_record/autosave_association.rb15
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb14
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/quoting.rb19
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/transaction.rb12
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_adapter.rb5
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb2
-rw-r--r--activerecord/lib/active_record/core.rb12
-rw-r--r--activerecord/lib/active_record/locking/optimistic.rb22
-rw-r--r--activerecord/lib/active_record/log_subscriber.rb4
-rw-r--r--activerecord/lib/active_record/persistence.rb12
-rw-r--r--activerecord/lib/active_record/relation.rb5
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb4
-rw-r--r--activerecord/lib/active_record/sanitization.rb7
-rw-r--r--activerecord/lib/active_record/secure_token.rb30
-rw-r--r--activerecord/lib/active_record/statement_cache.rb4
-rw-r--r--activerecord/lib/active_record/transactions.rb4
-rw-r--r--activerecord/lib/active_record/type/integer.rb8
-rw-r--r--activerecord/lib/active_record/type/time.rb13
-rw-r--r--activerecord/lib/active_record/type/time_value.rb4
-rw-r--r--activerecord/lib/active_record/type/value.rb3
-rw-r--r--activerecord/lib/rails/generators/active_record/migration/templates/create_table_migration.rb5
-rw-r--r--activerecord/lib/rails/generators/active_record/migration/templates/migration.rb3
-rw-r--r--activerecord/lib/rails/generators/active_record/model/templates/model.rb3
-rw-r--r--activerecord/test/cases/adapters/postgresql/array_test.rb4
-rw-r--r--activerecord/test/cases/adapters/postgresql/citext_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/full_text_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/hstore_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/json_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/ltree_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/money_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/network_test.rb6
-rw-r--r--activerecord/test/cases/adapters/postgresql/quoting_test.rb9
-rw-r--r--activerecord/test/cases/adapters/postgresql/schema_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/uuid_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/xml_test.rb2
-rw-r--r--activerecord/test/cases/adapters/sqlite3/quoting_test.rb4
-rw-r--r--activerecord/test/cases/attribute_methods_test.rb59
-rw-r--r--activerecord/test/cases/autosave_association_test.rb10
-rw-r--r--activerecord/test/cases/date_time_test.rb2
-rw-r--r--activerecord/test/cases/dirty_test.rb21
-rw-r--r--activerecord/test/cases/helper.rb3
-rw-r--r--activerecord/test/cases/locking_test.rb6
-rw-r--r--activerecord/test/cases/log_subscriber_test.rb7
-rw-r--r--activerecord/test/cases/migration/column_positioning_test.rb2
-rw-r--r--activerecord/test/cases/migration/foreign_key_test.rb2
-rw-r--r--activerecord/test/cases/migration_test.rb2
-rw-r--r--activerecord/test/cases/multiparameter_attributes_test.rb4
-rw-r--r--activerecord/test/cases/multiple_db_test.rb7
-rw-r--r--activerecord/test/cases/persistence_test.rb10
-rw-r--r--activerecord/test/cases/quoting_test.rb13
-rw-r--r--activerecord/test/cases/relation/predicate_builder_test.rb2
-rw-r--r--activerecord/test/cases/relation/where_test.rb30
-rw-r--r--activerecord/test/cases/schema_dumper_test.rb36
-rw-r--r--activerecord/test/cases/secure_token_test.rb14
-rw-r--r--activerecord/test/cases/xml_serialization_test.rb2
-rw-r--r--activesupport/CHANGELOG.md18
-rw-r--r--activesupport/lib/active_support/core_ext/array/access.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/kernel/reporting.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/securerandom.rb23
-rw-r--r--activesupport/lib/active_support/duration.rb2
-rw-r--r--activesupport/lib/active_support/hash_with_indifferent_access.rb2
-rw-r--r--activesupport/lib/active_support/inflector/methods.rb138
-rw-r--r--activesupport/lib/active_support/inflector/transliterate.rb14
-rw-r--r--activesupport/lib/active_support/testing/isolation.rb4
-rw-r--r--activesupport/lib/active_support/values/time_zone.rb5
-rw-r--r--activesupport/test/core_ext/duration_test.rb9
-rw-r--r--activesupport/test/core_ext/hash_ext_test.rb8
-rw-r--r--activesupport/test/core_ext/securerandom.rb20
-rw-r--r--activesupport/test/hash_with_indifferent_access_test.rb3
-rw-r--r--guides/assets/images/getting_started/article_with_comments.pngbin15190 -> 22560 bytes
-rw-r--r--guides/rails_guides/markdown/renderer.rb2
-rw-r--r--guides/source/2_2_release_notes.md2
-rw-r--r--guides/source/2_3_release_notes.md2
-rw-r--r--guides/source/3_0_release_notes.md2
-rw-r--r--guides/source/3_1_release_notes.md2
-rw-r--r--guides/source/3_2_release_notes.md2
-rw-r--r--guides/source/4_0_release_notes.md2
-rw-r--r--guides/source/4_1_release_notes.md2
-rw-r--r--guides/source/4_2_release_notes.md2
-rw-r--r--guides/source/action_controller_overview.md2
-rw-r--r--guides/source/action_mailer_basics.md2
-rw-r--r--guides/source/action_view_overview.md2
-rw-r--r--guides/source/active_job_basics.md20
-rw-r--r--guides/source/active_model_basics.md6
-rw-r--r--guides/source/active_record_basics.md4
-rw-r--r--guides/source/active_record_callbacks.md2
-rw-r--r--guides/source/active_record_migrations.md2
-rw-r--r--guides/source/active_record_postgresql.md2
-rw-r--r--guides/source/active_record_querying.md12
-rw-r--r--guides/source/active_record_validations.md2
-rw-r--r--guides/source/active_support_core_extensions.md66
-rw-r--r--guides/source/active_support_instrumentation.md2
-rw-r--r--guides/source/api_documentation_guidelines.md2
-rw-r--r--guides/source/asset_pipeline.md4
-rw-r--r--guides/source/association_basics.md2
-rw-r--r--guides/source/autoloading_and_reloading_constants.md21
-rw-r--r--guides/source/caching_with_rails.md6
-rw-r--r--guides/source/command_line.md2
-rw-r--r--guides/source/configuring.md4
-rw-r--r--guides/source/contributing_to_ruby_on_rails.md4
-rw-r--r--guides/source/debugging_rails_applications.md2
-rw-r--r--guides/source/development_dependencies_install.md2
-rw-r--r--guides/source/engines.md2
-rw-r--r--guides/source/form_helpers.md6
-rw-r--r--guides/source/generators.md2
-rw-r--r--guides/source/getting_started.md25
-rw-r--r--guides/source/i18n.md4
-rw-r--r--guides/source/initialization.md2
-rw-r--r--guides/source/layouts_and_rendering.md4
-rw-r--r--guides/source/maintenance_policy.md2
-rw-r--r--guides/source/nested_model_forms.md2
-rw-r--r--guides/source/plugins.md2
-rw-r--r--guides/source/profiling.md2
-rw-r--r--guides/source/rails_application_templates.md2
-rw-r--r--guides/source/rails_on_rack.md2
-rw-r--r--guides/source/routing.md2
-rw-r--r--guides/source/ruby_on_rails_guides_guidelines.md2
-rw-r--r--guides/source/security.md3
-rw-r--r--guides/source/testing.md63
-rw-r--r--guides/source/upgrading_ruby_on_rails.md2
-rw-r--r--guides/source/working_with_javascript_in_rails.md2
-rw-r--r--railties/CHANGELOG.md15
-rw-r--r--railties/lib/rails/code_statistics.rb4
-rw-r--r--railties/lib/rails/code_statistics_calculator.rb8
-rw-r--r--railties/lib/rails/commands/dbconsole.rb1
-rw-r--r--railties/lib/rails/commands/runner.rb1
-rw-r--r--railties/lib/rails/generators/actions.rb1
-rw-r--r--railties/lib/rails/generators/erb/scaffold/templates/index.html.erb2
-rw-r--r--railties/lib/rails/generators/generated_attribute.rb6
-rw-r--r--railties/lib/rails/generators/rails/app/templates/Gemfile4
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/application.rb1
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt14
-rw-r--r--railties/lib/rails/generators/rails/migration/migration_generator.rb2
-rw-r--r--railties/lib/rails/generators/rails/model/USAGE7
-rw-r--r--railties/lib/rails/generators/rails/model/model_generator.rb2
-rw-r--r--railties/lib/rails/railtie.rb2
-rw-r--r--railties/lib/rails/tasks/statistics.rake4
-rw-r--r--railties/test/code_statistics_calculator_test.rb74
-rw-r--r--railties/test/generators/app_generator_test.rb7
-rw-r--r--railties/test/generators/generators_test_helper.rb2
-rw-r--r--railties/test/generators/mailer_generator_test.rb6
-rw-r--r--railties/test/generators/migration_generator_test.rb24
-rw-r--r--railties/test/generators/model_generator_test.rb11
-rw-r--r--railties/test/generators/namespaced_generators_test.rb4
-rw-r--r--railties/test/isolation/abstract_unit.rb5
202 files changed, 1459 insertions, 741 deletions
diff --git a/.travis.yml b/.travis.yml
index ab7d968852..8bbc7c8f7e 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,6 +3,7 @@ sudo: false
script: 'ci/travis.rb'
before_install:
- "rvm current | grep 'jruby' && export AR_JDBC=true || echo"
+ - gem install bundler
before_script:
- bundle update
cache: bundler
@@ -44,7 +45,7 @@ notifications:
on_failure: always
rooms:
- secure: "YA1alef1ESHWGFNVwvmVGCkMe4cUy4j+UcNvMUESraceiAfVyRMAovlQBGs6\n9kBRm7DHYBUXYC2ABQoJbQRLDr/1B5JPf/M8+Qd7BKu8tcDC03U01SMHFLpO\naOs/HLXcDxtnnpL07tGVsm0zhMc5N8tq4/L3SHxK7Vi+TacwQzI="
-bundler_args: --without test
+bundler_args: --without test --jobs 3 --retry 3
services:
- memcached
- redis
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 9ba2e53ef2..421e7088cb 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -6,7 +6,7 @@ Ruby on Rails is a volunteer effort. We encourage you to pitch in. [Join the tea
* If you want to contribute to Rails documentation, please read the [Contributing to the Rails Documentation](http://edgeguides.rubyonrails.org/contributing_to_ruby_on_rails.html#contributing-to-the-rails-documentation) section of the aforementioned guide.
-*We only accept bug reports and pull requests in GitHub*.
+*We only accept bug reports and pull requests on GitHub*.
* If you have a question about how to use Ruby on Rails, please [ask the rubyonrails-talk mailing list](https://groups.google.com/forum/?fromgroups#!forum/rubyonrails-talk).
diff --git a/Gemfile b/Gemfile
index eb0a324ab2..0bcfab7b79 100644
--- a/Gemfile
+++ b/Gemfile
@@ -64,7 +64,7 @@ group :test do
gem 'ruby-prof', '~> 0.11.2'
end
- platforms :mri_21 do
+ platforms :mri_21, :mri_22 do
gem 'stackprof'
end
diff --git a/RELEASING_RAILS.rdoc b/RELEASING_RAILS.rdoc
index ee5e6a91c1..0e09234b82 100644
--- a/RELEASING_RAILS.rdoc
+++ b/RELEASING_RAILS.rdoc
@@ -25,17 +25,6 @@ for Rails. You can check the status of his tests here:
Do not release with Red AWDwR tests.
-=== Are the supported plugins working? If not, make it work.
-
-Some Rails plugins are important and need to be supported until Rails 5.
-As these plugins are outside the Rails repository it is easy to break them without knowing
-after some refactoring or bug fix, so it is important to check if the following plugins
-are working with the versions that will be released:
-
-* https://github.com/rails/protected_attributes
-
-Do not release red plugins tests.
-
=== Do we have any Git dependencies? If so, contact those authors.
Having Git dependencies indicates that we depend on unreleased code.
diff --git a/actionmailer/CHANGELOG.md b/actionmailer/CHANGELOG.md
index c5e51b6fbd..008760f2fc 100644
--- a/actionmailer/CHANGELOG.md
+++ b/actionmailer/CHANGELOG.md
@@ -1,3 +1,25 @@
+* Add `assert_enqueued_emails` and `assert_no_enqueued_emails`.
+
+ def test_emails
+ assert_enqueued_emails 2 do
+ ContactMailer.welcome.deliver_later
+ ContactMailer.welcome.deliver_later
+ end
+ end
+
+ def test_no_emails
+ assert_no_enqueued_emails do
+ # No emails enqueued here
+ end
+ end
+
+ *George Claghorn*
+
+* Add `_mailer` suffix to mailers created via generator, following the same
+ naming convention used in controllers and jobs.
+
+ *Carlos Souza*
+
* Remove deprecate `*_path` helpers in email views.
*Rafael Mendonça França*
@@ -29,11 +51,4 @@
*Rafael Mendonça França*
-* Add `_mailer` suffix to mailers created via generator, following the same
-naming convention used in controllers and jobs.
-
- Closes #18074.
-
- *Carlos Souza*
-
Please check [4-2-stable](https://github.com/rails/rails/blob/4-2-stable/actionmailer/CHANGELOG.md) for previous changes.
diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb
index 53cc1fdb31..7eae76f93b 100644
--- a/actionmailer/lib/action_mailer/base.rb
+++ b/actionmailer/lib/action_mailer/base.rb
@@ -58,7 +58,7 @@ module ActionMailer
#
# 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.erb+ view
- # file as well as the +welcome.text.html.erb+ view file in a +multipart/alternative+ email.
+ # file as well as the +welcome.html.erb+ view file in a +multipart/alternative+ email.
#
# If you want to explicitly render only certain templates, pass a block:
#
diff --git a/actionmailer/lib/action_mailer/test_helper.rb b/actionmailer/lib/action_mailer/test_helper.rb
index 6ddacf7b79..524e6e3af1 100644
--- a/actionmailer/lib/action_mailer/test_helper.rb
+++ b/actionmailer/lib/action_mailer/test_helper.rb
@@ -1,7 +1,11 @@
+require 'active_job'
+
module ActionMailer
# Provides helper methods for testing Action Mailer, including #assert_emails
# and #assert_no_emails
module TestHelper
+ include ActiveJob::TestHelper
+
# Asserts that the number of emails sent matches the given number.
#
# def test_emails
@@ -58,5 +62,52 @@ module ActionMailer
def assert_no_emails(&block)
assert_emails 0, &block
end
+
+ # Asserts that the number of emails enqueued for later delivery matches
+ # the given number.
+ #
+ # def test_emails
+ # assert_enqueued_emails 0
+ # ContactMailer.welcome.deliver_later
+ # assert_enqueued_emails 1
+ # ContactMailer.welcome.deliver_later
+ # assert_enqueued_emails 2
+ # end
+ #
+ # If a block is passed, that block should cause the specified number of
+ # emails to be enqueued.
+ #
+ # def test_emails_again
+ # assert_enqueued_emails 1 do
+ # ContactMailer.welcome.deliver_later
+ # end
+ #
+ # assert_enqueued_emails 2 do
+ # ContactMailer.welcome.deliver_later
+ # ContactMailer.welcome.deliver_later
+ # end
+ # end
+ def assert_enqueued_emails(number, &block)
+ assert_enqueued_jobs number, only: ActionMailer::DeliveryJob, &block
+ end
+
+ # Asserts that no emails are enqueued for later delivery.
+ #
+ # def test_no_emails
+ # assert_no_enqueued_emails
+ # ContactMailer.welcome.deliver_later
+ # assert_enqueued_emails 1
+ # end
+ #
+ # If a block is provided, it should not cause any emails to be enqueued.
+ #
+ # def test_no_emails
+ # assert_no_enqueued_emails do
+ # # No emails should be enqueued from this block
+ # end
+ # end
+ def assert_no_enqueued_emails(&block)
+ assert_no_enqueued_jobs only: ActionMailer::DeliveryJob, &block
+ end
end
end
diff --git a/actionmailer/lib/rails/generators/mailer/templates/mailer.rb b/actionmailer/lib/rails/generators/mailer/templates/mailer.rb
index b9be70a2f0..348d314758 100644
--- a/actionmailer/lib/rails/generators/mailer/templates/mailer.rb
+++ b/actionmailer/lib/rails/generators/mailer/templates/mailer.rb
@@ -5,7 +5,7 @@ class <%= class_name %>Mailer < ApplicationMailer
# Subject can be set in your I18n file at config/locales/en.yml
# with the following lookup:
#
- # en.<%= file_path.tr("/",".") %>.<%= action %>.subject
+ # en.<%= file_path.tr("/",".") %>_mailer.<%= action %>.subject
#
def <%= action %>
@greeting = "Hi"
diff --git a/actionmailer/test/base_test.rb b/actionmailer/test/base_test.rb
index 5d9eda2555..b218d923e5 100644
--- a/actionmailer/test/base_test.rb
+++ b/actionmailer/test/base_test.rb
@@ -287,7 +287,7 @@ class BaseTest < ActiveSupport::TestCase
end
end
- assert_nothing_raised { LateAttachmentAccessorMailer.welcome }
+ assert_nothing_raised { LateAttachmentAccessorMailer.welcome.message }
end
# Implicit multipart
diff --git a/actionmailer/test/test_helper_test.rb b/actionmailer/test/test_helper_test.rb
index 96b75ff2e0..21e7154c43 100644
--- a/actionmailer/test/test_helper_test.rb
+++ b/actionmailer/test/test_helper_test.rb
@@ -119,6 +119,53 @@ class TestHelperMailerTest < ActionMailer::TestCase
assert_match(/0 .* but 1/, error.message)
end
+
+ def test_assert_enqueued_emails
+ assert_nothing_raised do
+ assert_enqueued_emails 1 do
+ TestHelperMailer.test.deliver_later
+ end
+ end
+ end
+
+ def test_assert_enqueued_emails_too_few_sent
+ error = assert_raise ActiveSupport::TestCase::Assertion do
+ assert_enqueued_emails 2 do
+ TestHelperMailer.test.deliver_later
+ end
+ end
+
+ assert_match(/2 .* but 1/, error.message)
+ end
+
+ def test_assert_enqueued_emails_too_many_sent
+ error = assert_raise ActiveSupport::TestCase::Assertion do
+ assert_enqueued_emails 1 do
+ TestHelperMailer.test.deliver_later
+ TestHelperMailer.test.deliver_later
+ end
+ end
+
+ assert_match(/1 .* but 2/, error.message)
+ end
+
+ def test_assert_no_enqueued_emails
+ assert_nothing_raised do
+ assert_no_enqueued_emails do
+ TestHelperMailer.test.deliver_now
+ end
+ end
+ end
+
+ def test_assert_no_enqueued_emails_failure
+ error = assert_raise ActiveSupport::TestCase::Assertion do
+ assert_no_enqueued_emails do
+ TestHelperMailer.test.deliver_later
+ end
+ end
+
+ assert_match(/0 .* but 1/, error.message)
+ end
end
class AnotherTestHelperMailerTest < ActionMailer::TestCase
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md
index 6c4ce6195e..6cd0b2d15d 100644
--- a/actionpack/CHANGELOG.md
+++ b/actionpack/CHANGELOG.md
@@ -1,3 +1,39 @@
+* Default headers, removed in controller actions, are no longer reapplied on
+ the test response.
+
+ *Jonas Baumann*
+
+* Deprecate all *_filter callbacks in favor of *_action callbacks.
+
+ *Rafael Mendonça França*
+
+* Allow you to pass `prepend: false` to protect_from_forgery to have the
+ verification callback appended instead of prepended to the chain.
+ This allows you to let the verification step depend on prior callbacks.
+ Example:
+
+ class ApplicationController < ActionController::Base
+ before_action :authenticate
+ protect_from_forgery prepend: false, unless: -> { @authenticated_by.oauth? }
+
+ private
+ def authenticate
+ if oauth_request?
+ # authenticate with oauth
+ @authenticated_by = 'oauth'.inquiry
+ else
+ # authenticate with cookies
+ @authenticated_by = 'cookie'.inquiry
+ end
+ end
+ end
+
+ *Josef Šimánek*
+
+* Remove `ActionController::HideActions`.
+
+ *Ravil Bayramgalin*
+
* Remove `respond_to`/`respond_with` placeholder methods, this functionality
has been extracted to the `responders` gem.
diff --git a/actionpack/actionpack.gemspec b/actionpack/actionpack.gemspec
index f83823dd75..d9b23ad4a9 100644
--- a/actionpack/actionpack.gemspec
+++ b/actionpack/actionpack.gemspec
@@ -21,7 +21,7 @@ Gem::Specification.new do |s|
s.add_dependency 'activesupport', version
- s.add_dependency 'rack', '~> 1.6.0'
+ s.add_dependency 'rack', '~> 1.6'
s.add_dependency 'rack-test', '~> 0.6.2'
s.add_dependency 'rails-html-sanitizer', '~> 1.0', '>= 1.0.1'
s.add_dependency 'rails-dom-testing', '~> 1.0', '>= 1.0.5'
diff --git a/actionpack/lib/abstract_controller/base.rb b/actionpack/lib/abstract_controller/base.rb
index 8c7cec3561..c95b9a4097 100644
--- a/actionpack/lib/abstract_controller/base.rb
+++ b/actionpack/lib/abstract_controller/base.rb
@@ -57,21 +57,11 @@ module AbstractController
controller.public_instance_methods(true)
end
- # The list of hidden actions. Defaults to an empty array.
- # This can be modified by other modules or subclasses
- # to specify particular actions as hidden.
- #
- # ==== Returns
- # * <tt>Array</tt> - An array of method names that should not be considered actions.
- def hidden_actions
- []
- end
-
# A list of method names that should be considered actions. This
# includes all public instance methods on a controller, less
# any internal methods (see internal_methods), adding back in
# any methods that are internal, but still exist on the class
- # itself. Finally, hidden_actions are removed.
+ # itself.
#
# ==== Returns
# * <tt>Set</tt> - A set of all methods that should be considered actions.
@@ -82,9 +72,7 @@ module AbstractController
# Except for public instance methods of Base and its ancestors
internal_methods +
# Be sure to include shadowed public instance methods of this class
- public_instance_methods(false)).uniq.map(&:to_s) -
- # And always exclude explicitly hidden actions
- hidden_actions.to_a
+ public_instance_methods(false)).uniq.map(&:to_s)
methods.to_set
end
diff --git a/actionpack/lib/abstract_controller/callbacks.rb b/actionpack/lib/abstract_controller/callbacks.rb
index 32de82780f..db999c2765 100644
--- a/actionpack/lib/abstract_controller/callbacks.rb
+++ b/actionpack/lib/abstract_controller/callbacks.rb
@@ -1,3 +1,5 @@
+require 'active_support/deprecation'
+
module AbstractController
module Callbacks
extend ActiveSupport::Concern
@@ -28,6 +30,16 @@ module AbstractController
# The basic idea is that <tt>:only => :index</tt> gets converted to
# <tt>:if => proc {|c| c.action_name == "index" }</tt>.
#
+ # Note that <tt>:only</tt> has priority over <tt>:if</tt> in case they
+ # are used together.
+ #
+ # only: :index, if: -> { true } # the :if option will be ignored.
+ #
+ # Note that <tt>:if</tt> has priority over <tt>:except</tt> in case they
+ # are used together.
+ #
+ # except: :index, if: -> { true } # the :except option will be ignored.
+ #
# ==== Options
# * <tt>only</tt> - The callback should be run only for this action
# * <tt>except</tt> - The callback should be run for all actions except this action
@@ -55,7 +67,11 @@ module AbstractController
skip_after_action(*names)
skip_around_action(*names)
end
- alias_method :skip_filter, :skip_action_callback
+
+ def skip_filter(*names)
+ ActiveSupport::Deprecation.warn("#{callback}_filter is deprecated and will removed in Rails 5.1. Use #{callback}_action instead.")
+ skip_action_callback(*names)
+ end
# Take callback names and an optional callback proc, normalize them,
# then call the block with each callback. This allows us to abstract
@@ -170,14 +186,22 @@ module AbstractController
set_callback(:process_action, callback, name, options)
end
end
- alias_method :"#{callback}_filter", :"#{callback}_action"
+
+ define_method "#{callback}_filter" do |*names, &blk|
+ ActiveSupport::Deprecation.warn("#{callback}_filter is deprecated and will removed in Rails 5.1. Use #{callback}_action instead.")
+ send("#{callback}_action", *names, &blk)
+ end
define_method "prepend_#{callback}_action" do |*names, &blk|
_insert_callbacks(names, blk) do |name, options|
set_callback(:process_action, callback, name, options.merge(:prepend => true))
end
end
- alias_method :"prepend_#{callback}_filter", :"prepend_#{callback}_action"
+
+ define_method "prepend_#{callback}_filter" do |*names, &blk|
+ ActiveSupport::Deprecation.warn("prepend_#{callback}_filter is deprecated and will removed in Rails 5.1. Use prepend_#{callback}_action instead.")
+ send("prepend_#{callback}_action", *names, &blk)
+ end
# Skip a before, after or around callback. See _insert_callbacks
# for details on the allowed parameters.
@@ -186,11 +210,19 @@ module AbstractController
skip_callback(:process_action, callback, name, options)
end
end
- alias_method :"skip_#{callback}_filter", :"skip_#{callback}_action"
+
+ define_method "skip_#{callback}_filter" do |*names, &blk|
+ ActiveSupport::Deprecation.warn("skip_#{callback}_filter is deprecated and will removed in Rails 5.1. Use skip_#{callback}_action instead.")
+ send("skip_#{callback}_action", *names, &blk)
+ end
# *_action is the same as append_*_action
alias_method :"append_#{callback}_action", :"#{callback}_action"
- alias_method :"append_#{callback}_filter", :"#{callback}_action"
+
+ define_method "append_#{callback}_filter" do |*names, &blk|
+ ActiveSupport::Deprecation.warn("append_#{callback}_filter is deprecated and will removed in Rails 5.1. Use append_#{callback}_action instead.")
+ send("append_#{callback}_action", *names, &blk)
+ end
end
end
end
diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb
index 91ac7eef01..e977bbce99 100644
--- a/actionpack/lib/action_controller.rb
+++ b/actionpack/lib/action_controller.rb
@@ -22,7 +22,6 @@ module ActionController
autoload :ForceSSL
autoload :Head
autoload :Helpers
- autoload :HideActions
autoload :HttpAuthentication
autoload :ImplicitRender
autoload :Instrumentation
diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb
index 5cb11bc479..e6038396f9 100644
--- a/actionpack/lib/action_controller/base.rb
+++ b/actionpack/lib/action_controller/base.rb
@@ -206,7 +206,6 @@ module ActionController
AbstractController::AssetPaths,
Helpers,
- HideActions,
UrlFor,
Redirecting,
ActionView::Layouts,
diff --git a/actionpack/lib/action_controller/metal/conditional_get.rb b/actionpack/lib/action_controller/metal/conditional_get.rb
index b210ee3423..febbc72861 100644
--- a/actionpack/lib/action_controller/metal/conditional_get.rb
+++ b/actionpack/lib/action_controller/metal/conditional_get.rb
@@ -51,7 +51,7 @@ module ActionController
#
# def show
# @article = Article.find(params[:id])
- # fresh_when(etag: @article, last_modified: @article.created_at, public: true)
+ # fresh_when(etag: @article, last_modified: @article.updated_at, public: true)
# end
#
# This will render the show template if the request isn't sending a matching ETag or
@@ -115,7 +115,7 @@ module ActionController
# def show
# @article = Article.find(params[:id])
#
- # if stale?(etag: @article, last_modified: @article.created_at)
+ # if stale?(etag: @article, last_modified: @article.updated_at)
# @statistics = @article.really_expensive_call
# respond_to do |format|
# # all the supported formats
diff --git a/actionpack/lib/action_controller/metal/hide_actions.rb b/actionpack/lib/action_controller/metal/hide_actions.rb
deleted file mode 100644
index af36ffa240..0000000000
--- a/actionpack/lib/action_controller/metal/hide_actions.rb
+++ /dev/null
@@ -1,40 +0,0 @@
-
-module ActionController
- # Adds the ability to prevent public methods on a controller to be called as actions.
- module HideActions
- extend ActiveSupport::Concern
-
- included do
- class_attribute :hidden_actions
- self.hidden_actions = Set.new.freeze
- end
-
- private
-
- # Overrides AbstractController::Base#action_method? to return false if the
- # action name is in the list of hidden actions.
- def method_for_action(action_name)
- self.class.visible_action?(action_name) && super
- end
-
- module ClassMethods
- # Sets all of the actions passed in as hidden actions.
- #
- # ==== Parameters
- # * <tt>args</tt> - A list of actions
- def hide_action(*args)
- self.hidden_actions = hidden_actions.dup.merge(args.map(&:to_s)).freeze
- end
-
- def visible_action?(action_name)
- not hidden_actions.include?(action_name)
- end
-
- # Overrides AbstractController::Base#action_methods to remove any methods
- # that are listed as hidden methods.
- def action_methods
- @action_methods ||= Set.new(super.reject { |name| hidden_actions.include?(name) }).freeze
- end
- end
- end
-end
diff --git a/actionpack/lib/action_controller/metal/request_forgery_protection.rb b/actionpack/lib/action_controller/metal/request_forgery_protection.rb
index b9a1e7d242..7facbe79aa 100644
--- a/actionpack/lib/action_controller/metal/request_forgery_protection.rb
+++ b/actionpack/lib/action_controller/metal/request_forgery_protection.rb
@@ -87,6 +87,11 @@ module ActionController #:nodoc:
#
# * <tt>:only/:except</tt> - Only apply forgery protection to a subset of actions. Like <tt>only: [ :create, :create_all ]</tt>.
# * <tt>:if/:unless</tt> - Turn off the forgery protection entirely depending on the passed proc or method reference.
+ # * <tt>:prepend</tt> - By default, the verification of the authentication token is added to the front of the
+ # callback chain. If you need to make the verification depend on other callbacks, like authentication methods
+ # (say cookies vs oauth), this might not work for you. Pass <tt>prepend: false</tt> to just add the
+ # verification callback in the position of the protect_from_forgery call. This means any callbacks added
+ # before are run first.
# * <tt>:with</tt> - Set the method to handle unverified request.
#
# Valid unverified request handling methods are:
@@ -94,9 +99,11 @@ module ActionController #:nodoc:
# * <tt>:reset_session</tt> - Resets the session.
# * <tt>:null_session</tt> - Provides an empty session during request but doesn't reset it completely. Used as default if <tt>:with</tt> option is not specified.
def protect_from_forgery(options = {})
+ options = options.reverse_merge(prepend: true)
+
self.forgery_protection_strategy = protection_method_class(options[:with] || :null_session)
self.request_forgery_protection_token ||= :authenticity_token
- prepend_before_action :verify_authenticity_token, options
+ before_action :verify_authenticity_token, options
append_after_action :verify_same_origin_request
end
diff --git a/actionpack/lib/action_dispatch/http/cache.rb b/actionpack/lib/action_dispatch/http/cache.rb
index 63a3cbc90b..747d295261 100644
--- a/actionpack/lib/action_dispatch/http/cache.rb
+++ b/actionpack/lib/action_dispatch/http/cache.rb
@@ -69,17 +69,17 @@ module ActionDispatch
end
def date
- if date_header = headers['Date']
+ if date_header = headers[DATE]
Time.httpdate(date_header)
end
end
def date?
- headers.include?('Date')
+ headers.include?(DATE)
end
def date=(utc_time)
- headers['Date'] = utc_time.httpdate
+ headers[DATE] = utc_time.httpdate
end
def etag=(etag)
@@ -89,6 +89,7 @@ module ActionDispatch
private
+ DATE = 'Date'.freeze
LAST_MODIFIED = "Last-Modified".freeze
ETAG = "ETag".freeze
CACHE_CONTROL = "Cache-Control".freeze
diff --git a/actionpack/lib/action_dispatch/testing/test_response.rb b/actionpack/lib/action_dispatch/testing/test_response.rb
index 82039e72e7..369ea1467c 100644
--- a/actionpack/lib/action_dispatch/testing/test_response.rb
+++ b/actionpack/lib/action_dispatch/testing/test_response.rb
@@ -25,5 +25,12 @@ module ActionDispatch
# Was there a server-side error?
alias_method :error?, :server_error?
+
+ def merge_default_headers(original, *args)
+ # Default headers are already applied, no need to merge them a second time.
+ # This makes sure that default headers, removed in controller actions, will
+ # not be reapplied to the test response.
+ original
+ end
end
end
diff --git a/actionpack/test/abstract/callbacks_test.rb b/actionpack/test/abstract/callbacks_test.rb
index 8cba049485..07571602e4 100644
--- a/actionpack/test/abstract/callbacks_test.rb
+++ b/actionpack/test/abstract/callbacks_test.rb
@@ -267,9 +267,11 @@ module AbstractController
end
class AliasedCallbacks < ControllerWithCallbacks
- before_filter :first
- after_filter :second
- around_filter :aroundz
+ ActiveSupport::Deprecation.silence do
+ before_filter :first
+ after_filter :second
+ around_filter :aroundz
+ end
def first
@text = "Hello world"
diff --git a/actionpack/test/controller/base_test.rb b/actionpack/test/controller/base_test.rb
index 950788743e..001493afc0 100644
--- a/actionpack/test/controller/base_test.rb
+++ b/actionpack/test/controller/base_test.rb
@@ -1,31 +1,11 @@
require 'abstract_unit'
require 'active_support/logger'
require 'controller/fake_models'
-require 'pp' # require 'pp' early to prevent hidden_methods from not picking up the pretty-print methods until too late
# Provide some controller to run the tests on.
module Submodule
class ContainedEmptyController < ActionController::Base
end
-
- class ContainedNonEmptyController < ActionController::Base
- def public_action
- render :nothing => true
- end
-
- hide_action :hidden_action
- def hidden_action
- raise "Noooo!"
- end
-
- def another_hidden_action
- end
- hide_action :another_hidden_action
- end
-
- class SubclassedController < ContainedNonEmptyController
- hide_action :public_action # Hiding it here should not affect the superclass.
- end
end
class EmptyController < ActionController::Base
@@ -35,10 +15,6 @@ class NonEmptyController < ActionController::Base
def public_action
render :nothing => true
end
-
- hide_action :hidden_action
- def hidden_action
- end
end
class DefaultUrlOptionsController < ActionController::Base
@@ -108,10 +84,7 @@ class ControllerInstanceTests < ActiveSupport::TestCase
def setup
@empty = EmptyController.new
@contained = Submodule::ContainedEmptyController.new
- @empty_controllers = [@empty, @contained, Submodule::SubclassedController.new]
-
- @non_empty_controllers = [NonEmptyController.new,
- Submodule::ContainedNonEmptyController.new]
+ @empty_controllers = [@empty, @contained]
end
def test_performed?
@@ -124,10 +97,6 @@ class ControllerInstanceTests < ActiveSupport::TestCase
@empty_controllers.each do |c|
assert_equal Set.new, c.class.action_methods, "#{c.controller_path} should be empty!"
end
-
- @non_empty_controllers.each do |c|
- assert_equal Set.new(%w(public_action)), c.class.action_methods, "#{c.controller_path} should not be empty!"
- end
end
def test_temporary_anonymous_controllers
@@ -161,12 +130,6 @@ class PerformActionTest < ActionController::TestCase
assert_equal "The action 'non_existent' could not be found for EmptyController", exception.message
end
- def test_get_on_hidden_should_fail
- use_controller NonEmptyController
- assert_raise(AbstractController::ActionNotFound) { get :hidden_action }
- assert_raise(AbstractController::ActionNotFound) { get :another_hidden_action }
- end
-
def test_action_missing_should_work
use_controller ActionMissingController
get :arbitrary_action
diff --git a/actionpack/test/controller/filters_test.rb b/actionpack/test/controller/filters_test.rb
index 2e08a6af9f..b9fb6be4e3 100644
--- a/actionpack/test/controller/filters_test.rb
+++ b/actionpack/test/controller/filters_test.rb
@@ -225,6 +225,30 @@ class FilterTest < ActionController::TestCase
skip_before_action :clean_up_tmp, if: -> { true }
end
+ class SkipFilterUsingOnlyAndIf < ConditionalFilterController
+ before_action :clean_up_tmp
+ before_action :ensure_login
+
+ skip_before_action :ensure_login, only: :login, if: -> { false }
+ skip_before_action :clean_up_tmp, only: :login, if: -> { true }
+
+ def login
+ render text: 'ok'
+ end
+ end
+
+ class SkipFilterUsingIfAndExcept < ConditionalFilterController
+ before_action :clean_up_tmp
+ before_action :ensure_login
+
+ skip_before_action :ensure_login, if: -> { false }, except: :login
+ skip_before_action :clean_up_tmp, if: -> { true }, except: :login
+
+ def login
+ render text: 'ok'
+ end
+ end
+
class ClassController < ConditionalFilterController
before_action ConditionalClassFilter
end
@@ -596,6 +620,16 @@ class FilterTest < ActionController::TestCase
assert_equal %w( ensure_login ), assigns["ran_filter"]
end
+ def test_if_is_ignored_when_used_with_only
+ test_process(SkipFilterUsingOnlyAndIf, 'login')
+ assert_nil assigns['ran_filter']
+ end
+
+ def test_except_is_ignored_when_used_with_if
+ test_process(SkipFilterUsingIfAndExcept, 'login')
+ assert_equal %w(ensure_login), assigns["ran_filter"]
+ end
+
def test_skipping_class_actions
test_process(ClassController)
assert_equal true, assigns["ran_class_action"]
diff --git a/actionpack/test/controller/integration_test.rb b/actionpack/test/controller/integration_test.rb
index 5535c7ae78..5c0651bd73 100644
--- a/actionpack/test/controller/integration_test.rb
+++ b/actionpack/test/controller/integration_test.rb
@@ -279,6 +279,11 @@ class IntegrationProcessTest < ActionDispatch::IntegrationTest
def redirect
redirect_to action_url('get')
end
+
+ def remove_default_header
+ response.headers.except! 'X-Frame-Options'
+ head :ok
+ end
end
def test_get
@@ -506,6 +511,24 @@ class IntegrationProcessTest < ActionDispatch::IntegrationTest
end
end
+ def test_removed_default_headers_on_test_response_are_not_reapplied
+ with_test_route_set do
+ begin
+ header_to_remove = 'X-Frame-Options'
+ original_default_headers = ActionDispatch::Response.default_headers
+ ActionDispatch::Response.default_headers = {
+ 'X-Content-Type-Options' => 'nosniff',
+ header_to_remove => 'SAMEORIGIN',
+ }
+ get '/remove_default_header'
+ assert_includes headers, 'X-Content-Type-Options'
+ assert_not_includes headers, header_to_remove, "Should not contain removed default header"
+ ensure
+ ActionDispatch::Response.default_headers = original_default_headers
+ end
+ end
+ end
+
private
def with_test_route_set
with_routing do |set|
diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb
index 929b161eb6..108a35f59f 100644
--- a/actionpack/test/controller/render_test.rb
+++ b/actionpack/test/controller/render_test.rb
@@ -58,27 +58,6 @@ class TestController < ActionController::Base
end
end
- def conditional_hello_with_public_header
- if stale?(:last_modified => Time.now.utc.beginning_of_day, :etag => [:foo, 123], :public => true)
- render :action => 'hello_world'
- end
- end
-
- def conditional_hello_with_public_header_with_record
- record = Struct.new(:updated_at, :cache_key).new(Time.now.utc.beginning_of_day, "foo/123")
-
- if stale?(record, :public => true)
- render :action => 'hello_world'
- end
- end
-
- def conditional_hello_with_public_header_and_expires_at
- expires_in 1.minute
- if stale?(:last_modified => Time.now.utc.beginning_of_day, :etag => [:foo, 123], :public => true)
- render :action => 'hello_world'
- end
- end
-
def conditional_hello_with_expires_in
expires_in 60.1.seconds
render :action => 'hello_world'
@@ -129,50 +108,6 @@ class TestController < ActionController::Base
fresh_when(:last_modified => Time.now.utc.beginning_of_day, :etag => [ :foo, 123 ])
end
- def heading
- head :ok
- end
-
- # :ported:
- def double_render
- render :text => "hello"
- render :text => "world"
- end
-
- def double_redirect
- redirect_to :action => "double_render"
- redirect_to :action => "double_render"
- end
-
- def render_and_redirect
- render :text => "hello"
- redirect_to :action => "double_render"
- end
-
- def render_to_string_and_render
- @stuff = render_to_string :text => "here is some cached stuff"
- render :text => "Hi web users! #{@stuff}"
- end
-
- def render_to_string_with_inline_and_render
- render_to_string :inline => "<%= 'dlrow olleh'.reverse %>"
- render :template => "test/hello_world"
- end
-
- def rendering_with_conflicting_local_vars
- @name = "David"
- render :action => "potential_conflicts"
- end
-
- def hello_world_from_rxml_using_action
- render :action => "hello_world_from_rxml", :handlers => [:builder]
- end
-
- # :deprecated:
- def hello_world_from_rxml_using_template
- render :template => "test/hello_world_from_rxml", :handlers => [:builder]
- end
-
def head_created
head :created
end
diff --git a/actionpack/test/controller/request_forgery_protection_test.rb b/actionpack/test/controller/request_forgery_protection_test.rb
index 3e0bfe8d14..ea2d35c3f8 100644
--- a/actionpack/test/controller/request_forgery_protection_test.rb
+++ b/actionpack/test/controller/request_forgery_protection_test.rb
@@ -103,6 +103,31 @@ class RequestForgeryProtectionControllerUsingNullSession < ActionController::Bas
end
end
+class PrependProtectForgeryBaseController < ActionController::Base
+ before_action :custom_action
+ attr_accessor :called_callbacks
+
+ def index
+ render inline: 'OK'
+ end
+
+ protected
+
+ def add_called_callback(name)
+ @called_callbacks ||= []
+ @called_callbacks << name
+ end
+
+
+ def custom_action
+ add_called_callback("custom_action")
+ end
+
+ def verify_authenticity_token
+ add_called_callback("verify_authenticity_token")
+ end
+end
+
class FreeCookieController < RequestForgeryProtectionControllerUsingResetSession
self.allow_forgery_protection = false
@@ -431,6 +456,41 @@ class RequestForgeryProtectionControllerUsingExceptionTest < ActionController::T
end
end
+class PrependProtectForgeryBaseControllerTest < ActionController::TestCase
+ PrependTrueController = Class.new(PrependProtectForgeryBaseController) do
+ protect_from_forgery prepend: true
+ end
+
+ PrependFalseController = Class.new(PrependProtectForgeryBaseController) do
+ protect_from_forgery prepend: false
+ end
+
+ PrependDefaultController = Class.new(PrependProtectForgeryBaseController) do
+ protect_from_forgery
+ end
+
+ def test_verify_authenticity_token_is_prepended
+ @controller = PrependTrueController.new
+ get :index
+ expected_callback_order = ["verify_authenticity_token", "custom_action"]
+ assert_equal(expected_callback_order, @controller.called_callbacks)
+ end
+
+ def test_verify_authenticity_token_is_not_prepended
+ @controller = PrependFalseController.new
+ get :index
+ expected_callback_order = ["custom_action", "verify_authenticity_token"]
+ assert_equal(expected_callback_order, @controller.called_callbacks)
+ end
+
+ def test_verify_authenticity_token_is_prepended_by_default
+ @controller = PrependDefaultController.new
+ get :index
+ expected_callback_order = ["verify_authenticity_token", "custom_action"]
+ assert_equal(expected_callback_order, @controller.called_callbacks)
+ end
+end
+
class FreeCookieControllerTest < ActionController::TestCase
def setup
@controller = FreeCookieController.new
diff --git a/actionpack/test/dispatch/static_test.rb b/actionpack/test/dispatch/static_test.rb
index 7f1207eaed..9a6747fb8d 100644
--- a/actionpack/test/dispatch/static_test.rb
+++ b/actionpack/test/dispatch/static_test.rb
@@ -1,6 +1,5 @@
# encoding: utf-8
require 'abstract_unit'
-require 'rbconfig'
require 'zlib'
module StaticTests
diff --git a/actionview/CHANGELOG.md b/actionview/CHANGELOG.md
index ab3cb7eb19..743c01e393 100644
--- a/actionview/CHANGELOG.md
+++ b/actionview/CHANGELOG.md
@@ -1,6 +1,6 @@
* Change the default template handler from `ERB` to `Raw`.
- Files without a template handler in their extension will be rended using the raw
+ Files without a template handler in their extension will be rendered using the raw
handler instead of ERB.
*Rafael Mendonça França*
@@ -28,5 +28,8 @@
*Angelo Capilleri*
+* Allow entries without a link tag in AtomFeedHelper.
+
+ *Daniel Gomez de Souza*
Please check [4-2-stable](https://github.com/rails/rails/blob/4-2-stable/actionview/CHANGELOG.md) for previous changes.
diff --git a/actionview/lib/action_view/helpers/atom_feed_helper.rb b/actionview/lib/action_view/helpers/atom_feed_helper.rb
index 227ad4cdfa..d8be4e5678 100644
--- a/actionview/lib/action_view/helpers/atom_feed_helper.rb
+++ b/actionview/lib/action_view/helpers/atom_feed_helper.rb
@@ -174,7 +174,7 @@ module ActionView
#
# * <tt>:published</tt>: Time first published. Defaults to the created_at attribute on the record if one such exists.
# * <tt>:updated</tt>: Time of update. Defaults to the updated_at attribute on the record if one such exists.
- # * <tt>:url</tt>: The URL for this entry. Defaults to the polymorphic_url for the record.
+ # * <tt>:url</tt>: The URL for this entry or false or nil for not having a link tag. Defaults to the polymorphic_url for the record.
# * <tt>:id</tt>: The ID for this entry. Defaults to "tag:#{@view.request.host},#{@feed_options[:schema_date]}:#{record.class}/#{record.id}"
# * <tt>:type</tt>: The TYPE for this entry. Defaults to "text/html".
def entry(record, options = {})
@@ -191,7 +191,8 @@ module ActionView
type = options.fetch(:type, 'text/html')
- @xml.link(:rel => 'alternate', :type => type, :href => options[:url] || @view.polymorphic_url(record))
+ url = options.fetch(:url) { @view.polymorphic_url(record) }
+ @xml.link(:rel => 'alternate', :type => type, :href => url) if url
yield AtomBuilder.new(@xml)
end
diff --git a/actionview/lib/action_view/helpers/tags/base.rb b/actionview/lib/action_view/helpers/tags/base.rb
index f8abb19698..7740c60eac 100644
--- a/actionview/lib/action_view/helpers/tags/base.rb
+++ b/actionview/lib/action_view/helpers/tags/base.rb
@@ -32,12 +32,19 @@ module ActionView
unless object.nil?
method_before_type_cast = @method_name + "_before_type_cast"
- object.respond_to?(method_before_type_cast) ?
- object.send(method_before_type_cast) :
+ if value_came_from_user?(object) && object.respond_to?(method_before_type_cast)
+ object.public_send(method_before_type_cast)
+ else
value(object)
+ end
end
end
+ def value_came_from_user?(object)
+ method_name = "#{@method_name}_came_from_user?"
+ !object.respond_to?(method_name) || object.public_send(method_name)
+ end
+
def retrieve_object(object)
if object
object
diff --git a/actionview/lib/action_view/model_naming.rb b/actionview/lib/action_view/model_naming.rb
index d42e436b17..b6ed13424e 100644
--- a/actionview/lib/action_view/model_naming.rb
+++ b/actionview/lib/action_view/model_naming.rb
@@ -1,5 +1,5 @@
module ActionView
- module ModelNaming
+ module ModelNaming #:nodoc:
# Converts the given object to an ActiveModel compliant one.
def convert_to_model(object)
object.respond_to?(:to_model) ? object.to_model : object
diff --git a/actionview/lib/action_view/record_identifier.rb b/actionview/lib/action_view/record_identifier.rb
index c8484bed34..6c6e69101b 100644
--- a/actionview/lib/action_view/record_identifier.rb
+++ b/actionview/lib/action_view/record_identifier.rb
@@ -103,7 +103,7 @@ module ActionView
# make sure yourself that your dom ids are valid, in case you overwrite this method.
def record_key_for_dom_id(record)
key = convert_to_model(record).to_key
- key ? key.join('_') : key
+ key ? key.join(JOIN) : key
end
end
end
diff --git a/actionview/test/actionpack/controller/layout_test.rb b/actionview/test/actionpack/controller/layout_test.rb
index bd345fe873..7b8a83e2fe 100644
--- a/actionview/test/actionpack/controller/layout_test.rb
+++ b/actionview/test/actionpack/controller/layout_test.rb
@@ -1,5 +1,4 @@
require 'abstract_unit'
-require 'rbconfig'
require 'active_support/core_ext/array/extract_options'
# The view_paths array must be set on Base and not LayoutTest so that LayoutTest's inherited
diff --git a/actionview/test/template/atom_feed_helper_test.rb b/actionview/test/template/atom_feed_helper_test.rb
index 68b44c4f0d..9e06621c83 100644
--- a/actionview/test/template/atom_feed_helper_test.rb
+++ b/actionview/test/template/atom_feed_helper_test.rb
@@ -62,6 +62,23 @@ class ScrollsController < ActionController::Base
end
end
EOT
+ FEEDS["entry_url_false_option"] = <<-EOT
+ atom_feed do |feed|
+ feed.title("My great blog!")
+ feed.updated((@scrolls.first.created_at))
+
+ @scrolls.each do |scroll|
+ feed.entry(scroll, :url => false) do |entry|
+ entry.title(scroll.title)
+ entry.content(scroll.body, :type => 'html')
+
+ entry.author do |author|
+ author.name("DHH")
+ end
+ end
+ end
+ end
+ EOT
FEEDS["xml_block"] = <<-EOT
atom_feed do |feed|
feed.title("My great blog!")
@@ -337,6 +354,13 @@ class AtomFeedTest < ActionController::TestCase
end
end
+ def test_feed_entry_url_false_option_adds_no_link
+ with_restful_routing(:scrolls) do
+ get :index, :id => 'entry_url_false_option'
+ assert_select "entry link", false
+ end
+ end
+
private
def with_restful_routing(resources)
with_routing do |set|
diff --git a/actionview/test/template/form_helper_test.rb b/actionview/test/template/form_helper_test.rb
index fff1e1e572..dccf6a2090 100644
--- a/actionview/test/template/form_helper_test.rb
+++ b/actionview/test/template/form_helper_test.rb
@@ -99,7 +99,9 @@ class FormHelperTest < ActionView::TestCase
}.new
end
def @post.to_key; [123]; end
- def @post.id_before_type_cast; 123; end
+ def @post.id; 0; end
+ def @post.id_before_type_cast; "omg"; end
+ def @post.id_came_from_user?; true; end
def @post.to_param; '123'; end
@post.persisted = true
@@ -900,9 +902,29 @@ class FormHelperTest < ActionView::TestCase
)
end
- def test_text_area_with_value_before_type_cast
+ def test_inputs_use_before_type_cast_to_retain_information_from_validations_like_numericality
assert_dom_equal(
- %{<textarea id="post_id" name="post[id]">\n123</textarea>},
+ %{<textarea id="post_id" name="post[id]">\nomg</textarea>},
+ text_area("post", "id")
+ )
+ end
+
+ def test_inputs_dont_use_before_type_cast_when_value_did_not_come_from_user
+ class << @post
+ undef id_came_from_user?
+ def id_came_from_user?; false; end
+ end
+
+ assert_dom_equal(
+ %{<textarea id="post_id" name="post[id]">\n0</textarea>},
+ text_area("post", "id")
+ )
+ end
+
+ def test_inputs_use_before_typecast_when_object_doesnt_respond_to_came_from_user
+ class << @post; undef id_came_from_user?; end
+ assert_dom_equal(
+ %{<textarea id="post_id" name="post[id]">\nomg</textarea>},
text_area("post", "id")
)
end
diff --git a/activejob/CHANGELOG.md b/activejob/CHANGELOG.md
index afdd42be33..794d05d1b4 100644
--- a/activejob/CHANGELOG.md
+++ b/activejob/CHANGELOG.md
@@ -1,25 +1,40 @@
-* `ActiveJob::Base.deserialize` delegates to the job class
+* Add `:only` option to `assert_enqueued_jobs`, to check the number of times
+ a specific kind of job is enqueued.
+ Example:
+
+ def test_logging_job
+ assert_enqueued_jobs 1, only: LoggingJob do
+ LoggingJob.perform_later
+ HelloJob.perform_later('jeremy')
+ end
+ end
+
+ *George Claghorn*
+
+* `ActiveJob::Base.deserialize` delegates to the job class.
Since `ActiveJob::Base#deserialize` can be overridden by subclasses (like
`ActiveJob::Base#serialize`) this allows jobs to attach arbitrary metadata
- when they get serialized and read it back when they get performed. Example:
+ when they get serialized and read it back when they get performed.
- class DeliverWebhookJob < ActiveJob::Base
- def serialize
- super.merge('attempt_number' => (@attempt_number || 0) + 1)
- end
+ Example:
- def deserialize(job_data)
- super
- @attempt_number = job_data['attempt_number']
- end
+ class DeliverWebhookJob < ActiveJob::Base
+ def serialize
+ super.merge('attempt_number' => (@attempt_number || 0) + 1)
+ end
+
+ def deserialize(job_data)
+ super
+ @attempt_number = job_data['attempt_number']
+ end
- rescue_from(TimeoutError) do |exception|
- raise exception if @attempt_number > 5
- retry_job(wait: 10)
+ rescue_from(TimeoutError) do |exception|
+ raise exception if @attempt_number > 5
+ retry_job(wait: 10)
+ end
end
- end
*Isaac Seymour*
diff --git a/activejob/lib/active_job/test_helper.rb b/activejob/lib/active_job/test_helper.rb
index 2efcea7f2e..d18656e398 100644
--- a/activejob/lib/active_job/test_helper.rb
+++ b/activejob/lib/active_job/test_helper.rb
@@ -42,16 +42,24 @@ module ActiveJob
# HelloJob.perform_later('rafael')
# end
# end
- def assert_enqueued_jobs(number)
+ #
+ # The number of times a specific job is enqueued can be asserted.
+ #
+ # def test_logging_job
+ # assert_enqueued_jobs 2, only: LoggingJob do
+ # LoggingJob.perform_later
+ # HelloJob.perform_later('jeremy')
+ # end
+ # end
+ def assert_enqueued_jobs(number, only: nil)
if block_given?
- original_count = enqueued_jobs.size
+ original_count = enqueued_jobs_size(only: only)
yield
- new_count = enqueued_jobs.size
- assert_equal original_count + number, new_count,
- "#{number} jobs expected, but #{new_count - original_count} were enqueued"
+ new_count = enqueued_jobs_size(only: only)
+ assert_equal original_count + number, new_count, "#{number} jobs expected, but #{new_count - original_count} were enqueued"
else
- enqueued_jobs_size = enqueued_jobs.size
- assert_equal number, enqueued_jobs_size, "#{number} jobs expected, but #{enqueued_jobs_size} were enqueued"
+ actual_count = enqueued_jobs_size(only: only)
+ assert_equal number, actual_count, "#{number} jobs expected, but #{actual_count} were enqueued"
end
end
@@ -71,11 +79,19 @@ module ActiveJob
# end
# end
#
+ # It can be asserted that no jobs of a specific kind are enqueued:
+ #
+ # def test_no_logging
+ # assert_no_enqueued_jobs only: LoggingJob do
+ # HelloJob.perform_later('jeremy')
+ # end
+ # end
+ #
# Note: This assertion is simply a shortcut for:
#
# assert_enqueued_jobs 0, &block
- def assert_no_enqueued_jobs(&block)
- assert_enqueued_jobs 0, &block
+ def assert_no_enqueued_jobs(only: nil, &block)
+ assert_enqueued_jobs 0, only: only, &block
end
# Asserts that the number of performed jobs matches the given number.
@@ -215,6 +231,14 @@ module ActiveJob
def clear_performed_jobs
performed_jobs.clear
end
+
+ def enqueued_jobs_size(only: nil)
+ if only
+ enqueued_jobs.select { |job| job[:job] == only }.size
+ else
+ enqueued_jobs.size
+ end
+ end
end
end
end
diff --git a/activejob/test/cases/test_helper_test.rb b/activejob/test/cases/test_helper_test.rb
index 784ede3674..f34638e7d8 100644
--- a/activejob/test/cases/test_helper_test.rb
+++ b/activejob/test/cases/test_helper_test.rb
@@ -87,6 +87,65 @@ class EnqueuedJobsTest < ActiveJob::TestCase
assert_match(/0 .* but 1/, error.message)
end
+ def test_assert_enqueued_jobs_with_only_option
+ assert_nothing_raised do
+ assert_enqueued_jobs 1, only: HelloJob do
+ HelloJob.perform_later('jeremy')
+ LoggingJob.perform_later
+ end
+ end
+ end
+
+ def test_assert_enqueued_jobs_with_only_option_and_none_sent
+ error = assert_raise ActiveSupport::TestCase::Assertion do
+ assert_enqueued_jobs 1, only: HelloJob do
+ LoggingJob.perform_later
+ end
+ end
+
+ assert_match(/1 .* but 0/, error.message)
+ end
+
+ def test_assert_enqueued_jobs_with_only_option_and_too_few_sent
+ error = assert_raise ActiveSupport::TestCase::Assertion do
+ assert_enqueued_jobs 5, only: HelloJob do
+ HelloJob.perform_later('jeremy')
+ 4.times { LoggingJob.perform_later }
+ end
+ end
+
+ assert_match(/5 .* but 1/, error.message)
+ end
+
+ def test_assert_enqueued_jobs_with_only_option_and_too_many_sent
+ error = assert_raise ActiveSupport::TestCase::Assertion do
+ assert_enqueued_jobs 1, only: HelloJob do
+ 2.times { HelloJob.perform_later('jeremy') }
+ end
+ end
+
+ assert_match(/1 .* but 2/, error.message)
+ end
+
+ def test_assert_no_enqueued_jobs_with_only_option
+ assert_nothing_raised do
+ assert_no_enqueued_jobs only: HelloJob do
+ LoggingJob.perform_later
+ end
+ end
+ end
+
+ def test_assert_no_enqueued_jobs_with_only_option_failure
+ error = assert_raise ActiveSupport::TestCase::Assertion do
+ assert_no_enqueued_jobs only: HelloJob do
+ HelloJob.perform_later('jeremy')
+ LoggingJob.perform_later
+ end
+ end
+
+ assert_match(/0 .* but 1/, error.message)
+ end
+
def test_assert_enqueued_job
assert_enqueued_with(job: LoggingJob, queue: 'default') do
LoggingJob.set(wait_until: Date.tomorrow.noon).perform_later
diff --git a/activemodel/CHANGELOG.md b/activemodel/CHANGELOG.md
index f86b4804c8..fb4b972282 100644
--- a/activemodel/CHANGELOG.md
+++ b/activemodel/CHANGELOG.md
@@ -1,3 +1,9 @@
+* Change validates_acceptance_of to accept true by default.
+
+ The default for validates_acceptance_of is now "1" and true.
+ In the past, only "1" was the default and you were required to add
+ accept: true.
+
* Remove deprecated `ActiveModel::Dirty#reset_#{attribute}` and
`ActiveModel::Dirty#reset_changes`.
diff --git a/activemodel/lib/active_model/dirty.rb b/activemodel/lib/active_model/dirty.rb
index afba9bab0d..614bc6a5d8 100644
--- a/activemodel/lib/active_model/dirty.rb
+++ b/activemodel/lib/active_model/dirty.rb
@@ -189,6 +189,7 @@ module ActiveModel
def changes_include?(attr_name)
attributes_changed_by_setter.include?(attr_name)
end
+ alias attribute_changed_by_setter? changes_include?
# Removes current changes and makes them accessible through +previous_changes+.
def changes_applied # :doc:
diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb
index 55687cb3c7..477edbd120 100644
--- a/activemodel/lib/active_model/errors.rb
+++ b/activemodel/lib/active_model/errors.rb
@@ -388,8 +388,8 @@ module ActiveModel
# Translates an error message in its default scope
# (<tt>activemodel.errors.messages</tt>).
#
- # Error messages are first looked up in <tt>models.MODEL.attributes.ATTRIBUTE.MESSAGE</tt>,
- # if it's not there, it's looked up in <tt>models.MODEL.MESSAGE</tt> and if
+ # Error messages are first looked up in <tt>activemodel.errors.models.MODEL.attributes.ATTRIBUTE.MESSAGE</tt>,
+ # if it's not there, it's looked up in <tt>activemodel.errors.models.MODEL.MESSAGE</tt> and if
# that is not there also, it returns the translation of the default message
# (e.g. <tt>activemodel.errors.messages.MESSAGE</tt>). The translated model
# name, translated attribute name and the value are available for
diff --git a/activemodel/lib/active_model/secure_password.rb b/activemodel/lib/active_model/secure_password.rb
index 96e88f1b6c..871031ece4 100644
--- a/activemodel/lib/active_model/secure_password.rb
+++ b/activemodel/lib/active_model/secure_password.rb
@@ -77,13 +77,6 @@ module ActiveModel
validates_length_of :password, maximum: ActiveModel::SecurePassword::MAX_PASSWORD_LENGTH_ALLOWED
validates_confirmation_of :password, allow_blank: true
end
-
- # This code is necessary as long as the protected_attributes gem is supported.
- if respond_to?(:attributes_protected_by_default)
- def self.attributes_protected_by_default #:nodoc:
- super + ['password_digest']
- end
- end
end
end
diff --git a/activemodel/lib/active_model/validations/acceptance.rb b/activemodel/lib/active_model/validations/acceptance.rb
index ac5e79859b..ee160fb483 100644
--- a/activemodel/lib/active_model/validations/acceptance.rb
+++ b/activemodel/lib/active_model/validations/acceptance.rb
@@ -3,12 +3,12 @@ module ActiveModel
module Validations
class AcceptanceValidator < EachValidator # :nodoc:
def initialize(options)
- super({ allow_nil: true, accept: "1" }.merge!(options))
+ super({ allow_nil: true, accept: ["1", true] }.merge!(options))
setup!(options[:class])
end
def validate_each(record, attribute, value)
- unless value == options[:accept]
+ unless acceptable_option?(value)
record.errors.add(attribute, :accepted, options.except(:accept, :allow_nil))
end
end
@@ -20,6 +20,10 @@ module ActiveModel
klass.send(:attr_reader, *attr_readers)
klass.send(:attr_writer, *attr_writers)
end
+
+ def acceptable_option?(value)
+ Array(options[:accept]).include?(value)
+ end
end
module HelperMethods
diff --git a/activemodel/test/cases/dirty_test.rb b/activemodel/test/cases/dirty_test.rb
index 8ffd62fd86..66ed8a350a 100644
--- a/activemodel/test/cases/dirty_test.rb
+++ b/activemodel/test/cases/dirty_test.rb
@@ -45,10 +45,6 @@ class DirtyTest < ActiveModel::TestCase
def reload
clear_changes_information
end
-
- def deprecated_reload
- reset_changes
- end
end
setup do
diff --git a/activemodel/test/cases/validations/acceptance_validation_test.rb b/activemodel/test/cases/validations/acceptance_validation_test.rb
index e78aa1adaf..b7872ea6bf 100644
--- a/activemodel/test/cases/validations/acceptance_validation_test.rb
+++ b/activemodel/test/cases/validations/acceptance_validation_test.rb
@@ -65,4 +65,10 @@ class AcceptanceValidationTest < ActiveModel::TestCase
ensure
Person.clear_validators!
end
+
+ def test_validates_acceptance_of_true
+ Topic.validates_acceptance_of(:terms_of_service)
+
+ assert Topic.new(terms_of_service: true).valid?
+ end
end
diff --git a/activemodel/test/cases/validations/callbacks_test.rb b/activemodel/test/cases/validations/callbacks_test.rb
index 4b0dd58efb..cc50ffbbef 100644
--- a/activemodel/test/cases/validations/callbacks_test.rb
+++ b/activemodel/test/cases/validations/callbacks_test.rb
@@ -50,6 +50,14 @@ class DogWithMissingName < Dog
validates_presence_of :name
end
+class DogValidatorWithOnCondition < Dog
+ before_validation :set_before_validation_marker, on: :create
+ after_validation :set_after_validation_marker, on: :create
+
+ def set_before_validation_marker; self.history << 'before_validation_marker'; end
+ def set_after_validation_marker; self.history << 'after_validation_marker' ; end
+end
+
class DogValidatorWithIfCondition < Dog
before_validation :set_before_validation_marker1, if: -> { true }
before_validation :set_before_validation_marker2, if: -> { false }
@@ -73,6 +81,24 @@ class CallbacksWithMethodNamesShouldBeCalled < ActiveModel::TestCase
assert_equal ["before_validation_marker1", "after_validation_marker1"], d.history
end
+ def test_on_condition_is_respected_for_validation_with_matching_context
+ d = DogValidatorWithOnCondition.new
+ d.valid?(:create)
+ assert_equal ["before_validation_marker", "after_validation_marker"], d.history
+ end
+
+ def test_on_condition_is_respected_for_validation_without_matching_context
+ d = DogValidatorWithOnCondition.new
+ d.valid?(:save)
+ assert_equal [], d.history
+ end
+
+ def test_on_condition_is_respected_for_validation_without_context
+ d = DogValidatorWithOnCondition.new
+ d.valid?
+ assert_equal [], d.history
+ end
+
def test_before_validation_and_after_validation_callbacks_should_be_called
d = DogWithMethodCallbacks.new
d.valid?
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 3c4f06ccf8..d8f29a77b4 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,3 +1,37 @@
+* Don't run SQL if attribute value is not changed for update_attribute method.
+
+ *Prathamesh Sonpatki*
+
+* `time` columns can now affected by `time_zone_aware_attributes`. If you have
+ set `config.time_zone` to a value other than `'UTC'`, they will be treated
+ as in that time zone by default in Rails 5.1. If this is not the desired
+ behavior, you can set
+
+ ActiveRecord::Base.time_zone_aware_types = [:datetime]
+
+ A deprecation warning will be emitted if you have a `:time` column, and have
+ not explicitly opted out.
+
+ Fixes #3145
+
+ *Sean Griffin*
+
+* `nil` as a value for a binary column in a query no longer logs as
+ "<NULL binary data>", and instead logs as just "nil".
+
+ *Sean Griffin*
+
+* `attribute_will_change!` will no longer cause non-persistable attributes to
+ be sent to the database.
+
+ Fixes #18407.
+
+ *Sean Griffin*
+
+* Remove support for the `protected_attributes` gem.
+
+ *Carlos Antonio da Silva*, *Roberto Miranda*
+
* Fix accessing of fixtures having non-string labels like Fixnum.
*Prathamesh Sonpatki*
@@ -274,7 +308,7 @@
This option enables to define the column name of associated object's type for polymorphic associations.
- *Ulisses Almeida, Kassio Borges*
+ *Ulisses Almeida*, *Kassio Borges*
* Remove deprecated behavior allowing nested arrays to be passed as query
values.
diff --git a/activerecord/RUNNING_UNIT_TESTS.rdoc b/activerecord/RUNNING_UNIT_TESTS.rdoc
index 7e3460365b..bae40604b1 100644
--- a/activerecord/RUNNING_UNIT_TESTS.rdoc
+++ b/activerecord/RUNNING_UNIT_TESTS.rdoc
@@ -16,7 +16,7 @@ To run a set of tests:
You can also run tests that depend upon a specific database backend. For
example:
- $ bundle exec rake test_sqlite3
+ $ bundle exec rake test:sqlite3
Simply executing <tt>bundle exec rake test</tt> is equivalent to the following:
@@ -24,6 +24,10 @@ Simply executing <tt>bundle exec rake test</tt> is equivalent to the following:
$ bundle exec rake test:mysql2
$ bundle exec rake test:postgresql
$ bundle exec rake test:sqlite3
+
+Using the SQLite3 adapter with an in-memory database is the fastest way
+to run the tests:
+
$ bundle exec rake test:sqlite3_mem
There should be tests available for each database backend listed in the {Config
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index 14af55f327..35bc09bb10 100644
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -1245,6 +1245,10 @@ module ActiveRecord
# that is the inverse of this <tt>has_many</tt> association. Does not work in combination
# with <tt>:through</tt> or <tt>:as</tt> options.
# See ActiveRecord::Associations::ClassMethods's overview on Bi-directional associations for more detail.
+ # [:extend]
+ # Specifies a module or array of modules that will be extended into the association object returned.
+ # Useful for defining methods on associations, especially when they should be shared between multiple
+ # association objects.
#
# Option examples:
# has_many :comments, -> { order "posted_on" }
diff --git a/activerecord/lib/active_record/attribute.rb b/activerecord/lib/active_record/attribute.rb
index 88536eaac0..51e4fae62e 100644
--- a/activerecord/lib/active_record/attribute.rb
+++ b/activerecord/lib/active_record/attribute.rb
@@ -74,6 +74,10 @@ module ActiveRecord
true
end
+ def came_from_user?
+ false
+ end
+
def ==(other)
self.class == other.class &&
name == other.name &&
@@ -99,6 +103,10 @@ module ActiveRecord
def type_cast(value)
type.type_cast_from_user(value)
end
+
+ def came_from_user?
+ true
+ end
end
class WithCastValue < Attribute # :nodoc:
diff --git a/activerecord/lib/active_record/attribute_methods/before_type_cast.rb b/activerecord/lib/active_record/attribute_methods/before_type_cast.rb
index fd61febd57..56c1898551 100644
--- a/activerecord/lib/active_record/attribute_methods/before_type_cast.rb
+++ b/activerecord/lib/active_record/attribute_methods/before_type_cast.rb
@@ -28,6 +28,7 @@ module ActiveRecord
included do
attribute_method_suffix "_before_type_cast"
+ attribute_method_suffix "_came_from_user?"
end
# Returns the value of the attribute identified by +attr_name+ before
@@ -66,6 +67,10 @@ module ActiveRecord
def attribute_before_type_cast(attribute_name)
read_attribute_before_type_cast(attribute_name)
end
+
+ def attribute_came_from_user?(attribute_name)
+ @attributes[attribute_name].came_from_user?
+ end
end
end
end
diff --git a/activerecord/lib/active_record/attribute_methods/dirty.rb b/activerecord/lib/active_record/attribute_methods/dirty.rb
index d5702accaf..bce9c5e1e3 100644
--- a/activerecord/lib/active_record/attribute_methods/dirty.rb
+++ b/activerecord/lib/active_record/attribute_methods/dirty.rb
@@ -108,7 +108,7 @@ module ActiveRecord
end
def save_changed_attribute(attr, old_value)
- if attribute_changed?(attr)
+ if attribute_changed_by_setter?(attr)
clear_attribute_changes(attr) unless _field_changed?(attr, old_value)
else
set_attribute_was(attr, old_value) if _field_changed?(attr, old_value)
@@ -134,7 +134,7 @@ module ActiveRecord
# Serialized attributes should always be written in case they've been
# changed in place.
def keys_for_partial_write
- changed
+ changed & persistable_attribute_names
end
def _field_changed?(attr, old_value)
diff --git a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
index 777f7ab4d7..98671178cb 100644
--- a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
+++ b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
@@ -13,7 +13,7 @@ module ActiveRecord
value.map { |v| type_cast_from_user(v) }
elsif value.respond_to?(:in_time_zone)
begin
- value.in_time_zone || super
+ user_input_in_time_zone(value) || super
rescue ArgumentError
nil
end
@@ -39,6 +39,9 @@ module ActiveRecord
class_attribute :skip_time_zone_conversion_for_attributes, instance_writer: false
self.skip_time_zone_conversion_for_attributes = []
+
+ class_attribute :time_zone_aware_types, instance_writer: false
+ self.time_zone_aware_types = [:datetime, :not_explicitly_configured]
end
module ClassMethods
@@ -59,9 +62,31 @@ module ActiveRecord
end
def create_time_zone_conversion_attribute?(name, cast_type)
- time_zone_aware_attributes &&
- !self.skip_time_zone_conversion_for_attributes.include?(name.to_sym) &&
- (:datetime == cast_type.type)
+ enabled_for_column = time_zone_aware_attributes &&
+ !self.skip_time_zone_conversion_for_attributes.include?(name.to_sym)
+ result = enabled_for_column &&
+ time_zone_aware_types.include?(cast_type.type)
+
+ if enabled_for_column &&
+ !result &&
+ cast_type.type == :time &&
+ time_zone_aware_types.include?(:not_explicitly_configured)
+ ActiveSupport::Deprecation.warn(<<-MESSAGE)
+ Time columns will become time zone aware in Rails 5.1. This
+ still cause `String`s to be parsed as if they were in `Time.zone`,
+ and `Time`s to be converted to `Time.zone`.
+
+ To keep the old behavior, you must add the following to your initializer:
+
+ config.active_record.time_zone_aware_types = [:datetime]
+
+ To silence this deprecation warning, add the following:
+
+ config.active_record.time_zone_aware_types << :time
+ MESSAGE
+ end
+
+ result
end
end
end
diff --git a/activerecord/lib/active_record/attributes.rb b/activerecord/lib/active_record/attributes.rb
index aafb990bc1..b263a89d79 100644
--- a/activerecord/lib/active_record/attributes.rb
+++ b/activerecord/lib/active_record/attributes.rb
@@ -9,6 +9,8 @@ module ActiveRecord
class_attribute :user_provided_defaults, instance_accessor: false # :internal:
self.user_provided_columns = {}
self.user_provided_defaults = {}
+
+ delegate :persistable_attribute_names, to: :class
end
module ClassMethods # :nodoc:
@@ -96,6 +98,10 @@ module ActiveRecord
@columns_hash ||= Hash[columns.map { |c| [c.name, c] }]
end
+ def persistable_attribute_names # :nodoc:
+ @persistable_attribute_names ||= connection.schema_cache.columns_hash(table_name).keys
+ end
+
def reset_column_information # :nodoc:
super
clear_caches_calculated_from_columns
@@ -130,6 +136,7 @@ module ActiveRecord
@columns_hash = nil
@content_columns = nil
@default_attributes = nil
+ @persistable_attribute_names = nil
end
def raw_default_values
diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb
index fa6c5e9e8c..fcaaffb852 100644
--- a/activerecord/lib/active_record/autosave_association.rb
+++ b/activerecord/lib/active_record/autosave_association.rb
@@ -278,11 +278,18 @@ module ActiveRecord
# go through nested autosave associations that are loaded in memory (without loading
# any new ones), and return true if is changed for autosave
def nested_records_changed_for_autosave?
- self.class._reflections.values.any? do |reflection|
- if reflection.options[:autosave]
- association = association_instance_get(reflection.name)
- association && Array.wrap(association.target).any?(&:changed_for_autosave?)
+ @_nested_records_changed_for_autosave_already_called ||= false
+ return false if @_nested_records_changed_for_autosave_already_called
+ begin
+ @_nested_records_changed_for_autosave_already_called = true
+ self.class._reflections.values.any? do |reflection|
+ if reflection.options[:autosave]
+ association = association_instance_get(reflection.name)
+ association && Array.wrap(association.target).any?(&:changed_for_autosave?)
+ end
end
+ ensure
+ @_nested_records_changed_for_autosave_already_called = false
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
index 1371317e3c..d99dc9a5db 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
@@ -453,6 +453,10 @@ module ActiveRecord
c.verify!
end
c
+ rescue
+ remove c
+ c.disconnect!
+ raise
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
index 59cdd8e98c..787d07c4c2 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
@@ -285,10 +285,16 @@ module ActiveRecord
def insert_fixture(fixture, table_name)
columns = schema_cache.columns_hash(table_name)
- key_list = []
- value_list = fixture.map do |name, value|
- key_list << quote_column_name(name)
- quote(value, columns[name])
+ binds = fixture.map do |name, value|
+ [columns[name], value]
+ end
+ key_list = fixture.keys.map { |name| quote_column_name(name) }
+ value_list = prepare_binds_for_database(binds).map do |_, value|
+ begin
+ quote(value)
+ rescue TypeError
+ quote(YAML.dump(value))
+ end
end
execute "INSERT INTO #{quote_table_name(table_name)} (#{key_list.join(', ')}) VALUES (#{value_list.join(', ')})", 'Fixture Insert'
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
index 143d7d9574..c18caa2a2f 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
@@ -10,6 +10,12 @@ module ActiveRecord
return value.quoted_id if value.respond_to?(:quoted_id)
if column
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ Passing a column to `quote` has been deprecated. It is only used
+ for type casting, which should be handled elsewhere. See
+ https://github.com/rails/arel/commit/6160bfbda1d1781c3b08a33ec4955f170e95be11
+ for more information.
+ MSG
value = column.cast_type.type_cast_for_database(value)
end
@@ -90,6 +96,16 @@ module ActiveRecord
value.to_s(:db)
end
+ def prepare_binds_for_database(binds) # :nodoc:
+ binds.map do |column, value|
+ if column
+ column_name = column.name
+ value = column.cast_type.type_cast_for_database(value)
+ end
+ [column_name, value]
+ end
+ end
+
private
def types_which_need_no_typecasting
@@ -109,8 +125,7 @@ module ActiveRecord
when Date, Time then "'#{quoted_date(value)}'"
when Symbol then "'#{quote_string(value.to_s)}'"
when Class then "'#{value}'"
- else
- "'#{quote_string(YAML.dump(value))}'"
+ else raise TypeError, "can't quote #{value.class.name}"
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb
index f6ef3b0675..7535e9147a 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb
@@ -69,11 +69,11 @@ module ActiveRecord
def rollback_records
ite = records.uniq
while record = ite.shift
- record.rolledback! full_rollback?
+ record.rolledback!(force_restore_state: full_rollback?)
end
ensure
ite.each do |i|
- i.rolledback!(full_rollback?, false)
+ i.rolledback!(force_restore_state: full_rollback?, should_run_callbacks: false)
end
end
@@ -88,7 +88,7 @@ module ActiveRecord
end
ensure
ite.each do |i|
- i.committed!(false)
+ i.committed!(should_run_callbacks: false)
end
end
@@ -117,8 +117,6 @@ module ActiveRecord
def commit
connection.release_savepoint(savepoint_name)
super
- parent = connection.transaction_manager.current_transaction
- records.each { |r| parent.add_record(r) }
end
def full_rollback?; false; end
@@ -166,7 +164,9 @@ module ActiveRecord
end
def commit_transaction
- @stack.pop.commit
+ transaction = @stack.pop
+ transaction.commit
+ transaction.records.each { |r| current_transaction.add_record(r) }
end
def rollback_transaction
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
index c3a8bf5c74..c941c9f1eb 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
@@ -113,7 +113,8 @@ module ActiveRecord
class BindCollector < Arel::Collectors::Bind
def compile(bvs, conn)
- super(bvs.map { |bv| conn.quote(*bv.reverse) })
+ casted_binds = conn.prepare_binds_for_database(bvs)
+ super(casted_binds.map { |_, value| conn.quote(value) })
end
end
@@ -392,7 +393,7 @@ module ActiveRecord
end
def column_name_for_operation(operation, node) # :nodoc:
- node.to_sql
+ visitor.accept(node, collector).value
end
protected
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb
index c203e6c604..e45a2f59d9 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb
@@ -18,7 +18,7 @@ module ActiveRecord
end
attr_reader :subtype, :delimiter
- delegate :type, to: :subtype
+ delegate :type, :user_input_in_time_zone, to: :subtype
def initialize(subtype, delimiter = ',')
@subtype = subtype
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index 5b070cae4f..f4f9747359 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -64,7 +64,7 @@ module ActiveRecord
# <tt>SET client_min_messages TO <min_messages></tt> call on the connection.
# * <tt>:variables</tt> - An optional hash of additional parameters that
# will be used in <tt>SET SESSION key = val</tt> calls on the connection.
- # * <tt>:insert_returning</tt> - An optional boolean to control the use or <tt>RETURNING</tt> for <tt>INSERT</tt> statements
+ # * <tt>:insert_returning</tt> - An optional boolean to control the use of <tt>RETURNING</tt> for <tt>INSERT</tt> statements
# defaults to true.
#
# Any further options are used as connection parameters to libpq. See
diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb
index 5a5139256d..a7aff9f724 100644
--- a/activerecord/lib/active_record/core.rb
+++ b/activerecord/lib/active_record/core.rb
@@ -274,16 +274,14 @@ module ActiveRecord
# ==== Example:
# # Instantiates a single new object
# User.new(first_name: 'Jamie')
- def initialize(attributes = nil, options = {})
+ def initialize(attributes = nil)
@attributes = self.class._default_attributes.dup
self.class.define_attribute_methods
init_internals
initialize_internals_callback
- # +options+ argument is only needed to make protected_attributes gem easier to hook.
- # Remove it when we drop support to this gem.
- init_attributes(attributes, options) if attributes
+ assign_attributes(attributes) if attributes
yield self if block_given?
_run_initialize_callbacks
@@ -557,12 +555,6 @@ module ActiveRecord
def initialize_internals_callback
end
- # This method is needed to make protected_attributes gem easier to hook.
- # Remove it when we drop support to this gem.
- def init_attributes(attributes, options)
- assign_attributes(attributes)
- end
-
def thaw
if frozen?
@attributes = @attributes.dup
diff --git a/activerecord/lib/active_record/locking/optimistic.rb b/activerecord/lib/active_record/locking/optimistic.rb
index 9f053453bd..6f2b65c137 100644
--- a/activerecord/lib/active_record/locking/optimistic.rb
+++ b/activerecord/lib/active_record/locking/optimistic.rb
@@ -66,6 +66,15 @@ module ActiveRecord
send(lock_col + '=', previous_lock_value + 1)
end
+ def _create_record(attribute_names = self.attribute_names, *) # :nodoc:
+ if locking_enabled?
+ # We always want to persist the locking version, even if we don't detect
+ # a change from the default, since the database might have no default
+ attribute_names |= [self.class.locking_column]
+ end
+ super
+ end
+
def _update_record(attribute_names = self.attribute_names) #:nodoc:
return super unless locking_enabled?
return 0 if attribute_names.empty?
@@ -116,12 +125,8 @@ module ActiveRecord
relation = super
if locking_enabled?
- column_name = self.class.locking_column
- column = self.class.columns_hash[column_name]
- substitute = self.class.connection.substitute_at(column)
-
- relation = relation.where(self.class.arel_table[column_name].eq(substitute))
- relation.bind_values << [column, self[column_name].to_i]
+ locking_column = self.class.locking_column
+ relation = relation.where(locking_column => _read_attribute(locking_column))
end
relation
@@ -185,11 +190,6 @@ module ActiveRecord
super.to_i
end
- def changed?(old_value, *)
- # Ensure we save if the default was `nil`
- super || old_value == 0
- end
-
def init_with(coder)
__setobj__(coder['subtype'])
end
diff --git a/activerecord/lib/active_record/log_subscriber.rb b/activerecord/lib/active_record/log_subscriber.rb
index eb64d197f0..a5c7279db9 100644
--- a/activerecord/lib/active_record/log_subscriber.rb
+++ b/activerecord/lib/active_record/log_subscriber.rb
@@ -22,10 +22,10 @@ module ActiveRecord
def render_bind(column, value)
if column
- if column.binary?
+ if column.binary? && value
# This specifically deals with the PG adapter that casts bytea columns into a Hash.
value = value[:value] if value.is_a?(Hash)
- value = value ? "<#{value.bytesize} bytes of binary data>" : "<NULL binary data>"
+ value = "<#{value.bytesize} bytes of binary data>"
end
[column.name, value]
diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb
index cf6673db2e..714d36e7c0 100644
--- a/activerecord/lib/active_record/persistence.rb
+++ b/activerecord/lib/active_record/persistence.rb
@@ -246,7 +246,7 @@ module ActiveRecord
name = name.to_s
verify_readonly_attribute(name)
send("#{name}=", value)
- save(validate: false)
+ save(validate: false) if changed?
end
# Updates the attributes of the model from the passed-in hash and saves the
@@ -495,15 +495,7 @@ module ActiveRecord
end
def relation_for_destroy
- pk = self.class.primary_key
- column = self.class.columns_hash[pk]
- substitute = self.class.connection.substitute_at(column)
-
- relation = self.class.unscoped.where(
- self.class.arel_table[pk].eq(substitute))
-
- relation.bind_values = [[column, id]]
- relation
+ self.class.unscoped.where(self.class.primary_key => id)
end
def create_or_update(*args)
diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb
index ab3debc03b..dd78814c6a 100644
--- a/activerecord/lib/active_record/relation.rb
+++ b/activerecord/lib/active_record/relation.rb
@@ -558,8 +558,9 @@ module ActiveRecord
end
arel = relation.arel
- binds = (arel.bind_values + relation.bind_values).dup
- binds.map! { |bv| connection.quote(*bv.reverse) }
+ binds = arel.bind_values + relation.bind_values
+ binds = connection.prepare_binds_for_database(binds)
+ binds.map! { |_, value| connection.quote(value) }
collect = visitor.accept(arel.ast, Arel::Collectors::Bind.new)
collect.substitute_binds(binds).join
end
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index f054e17017..d6e6cb4d05 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -1154,13 +1154,11 @@ module ActiveRecord
end
end
- # This function is recursive just for better readablity.
- # #where argument doesn't support more than one level nested hash in real world.
def add_relations_to_bind_values(attributes)
if attributes.is_a?(Hash)
attributes.each_value do |value|
if value.is_a?(ActiveRecord::Relation)
- self.bind_values += value.bind_values
+ self.bind_values += value.arel.bind_values + value.bind_values
else
add_relations_to_bind_values(value)
end
diff --git a/activerecord/lib/active_record/sanitization.rb b/activerecord/lib/active_record/sanitization.rb
index 768a72a947..313e767dcb 100644
--- a/activerecord/lib/active_record/sanitization.rb
+++ b/activerecord/lib/active_record/sanitization.rb
@@ -3,14 +3,11 @@ module ActiveRecord
extend ActiveSupport::Concern
module ClassMethods
- def quote_value(value, column) #:nodoc:
- connection.quote(value, column)
- end
-
# Used to sanitize objects before they're used in an SQL SELECT statement. Delegates to <tt>connection.quote</tt>.
def sanitize(object) #:nodoc:
connection.quote(object)
end
+ alias_method :quote_value, :sanitize
protected
@@ -156,7 +153,7 @@ module ActiveRecord
# TODO: Deprecate this
def quoted_id
- self.class.quote_value(id, column_for_attribute(self.class.primary_key))
+ self.class.quote_value(@attributes[self.class.primary_key].value_for_database)
end
end
end
diff --git a/activerecord/lib/active_record/secure_token.rb b/activerecord/lib/active_record/secure_token.rb
index 23d4292cbb..07031b6371 100644
--- a/activerecord/lib/active_record/secure_token.rb
+++ b/activerecord/lib/active_record/secure_token.rb
@@ -5,7 +5,7 @@ module ActiveRecord
module ClassMethods
# Example using has_secure_token
#
- # # Schema: User(toke:string, auth_token:string)
+ # # Schema: User(token:string, auth_token:string)
# class User < ActiveRecord::Base
# has_secure_token
# has_secure_token :auth_token
@@ -13,35 +13,25 @@ module ActiveRecord
#
# user = User.new
# user.save
- # user.token # => "44539a6a59835a4ee9d7b112"
- # user.auth_token # => "e2426a93718d1817a43abbaa"
+ # user.token # => "4kUgL2pdQMSCQtjE"
+ # user.auth_token # => "77TMHrHJFvFDwodq8w7Ev2m7"
# user.regenerate_token # => true
# user.regenerate_auth_token # => true
#
- # SecureRandom is used to generate the 24-character unique token, so collisions are highly unlikely.
- # We'll check to see if the generated token has been used already using #exists?, and retry up to 10
- # times to find another unused token. After that a RuntimeError is raised if the problem persists.
+ # SecureRandom::base58 is used to generate the 24-character unique token, so collisions are highly unlikely.
#
# Note that it's still possible to generate a race condition in the database in the same way that
# validates_presence_of can. You're encouraged to add a unique index in the database to deal with
# this even more unlikely scenario.
def has_secure_token(attribute = :token)
- # Load securerandom only when has_secure_key is used.
- require 'securerandom'
- define_method("regenerate_#{attribute}") { update! attribute => self.class.generate_unique_secure_token(attribute) }
- before_create { self.send("#{attribute}=", self.class.generate_unique_secure_token(attribute)) }
+ # Load securerandom only when has_secure_token is used.
+ require 'active_support/core_ext/securerandom'
+ define_method("regenerate_#{attribute}") { update! attribute => self.class.generate_unique_secure_token }
+ before_create { self.send("#{attribute}=", self.class.generate_unique_secure_token) }
end
- def generate_unique_secure_token(attribute)
- 10.times do |i|
- SecureRandom.hex(12).tap do |token|
- if exists?(attribute => token)
- raise "Couldn't generate a unique token in 10 attempts!" if i == 9
- else
- return token
- end
- end
- end
+ def generate_unique_secure_token
+ SecureRandom.base58(24)
end
end
end
diff --git a/activerecord/lib/active_record/statement_cache.rb b/activerecord/lib/active_record/statement_cache.rb
index 192a19f05d..3047a81ec4 100644
--- a/activerecord/lib/active_record/statement_cache.rb
+++ b/activerecord/lib/active_record/statement_cache.rb
@@ -47,8 +47,8 @@ module ActiveRecord
def sql_for(binds, connection)
val = @values.dup
- binds = binds.dup
- @indexes.each { |i| val[i] = connection.quote(*binds.shift.reverse) }
+ binds = connection.prepare_binds_for_database(binds)
+ @indexes.each { |i| val[i] = connection.quote(binds.shift.last) }
val.join
end
end
diff --git a/activerecord/lib/active_record/transactions.rb b/activerecord/lib/active_record/transactions.rb
index 9cef50029b..0fd2862b2c 100644
--- a/activerecord/lib/active_record/transactions.rb
+++ b/activerecord/lib/active_record/transactions.rb
@@ -300,7 +300,7 @@ module ActiveRecord
#
# Ensure that it is not called if the object was never persisted (failed create),
# but call it after the commit of a destroyed object.
- def committed!(should_run_callbacks = true) #:nodoc:
+ def committed!(should_run_callbacks: true) #:nodoc:
_run_commit_callbacks if should_run_callbacks && destroyed? || persisted?
ensure
force_clear_transaction_record_state
@@ -308,7 +308,7 @@ module ActiveRecord
# Call the +after_rollback+ callbacks. The +force_restore_state+ argument indicates if the record
# state should be rolled back to the beginning or just to the last savepoint.
- def rolledback!(force_restore_state = false, should_run_callbacks = true) #:nodoc:
+ def rolledback!(force_restore_state: false, should_run_callbacks: true) #:nodoc:
_run_rollback_callbacks if should_run_callbacks
ensure
restore_transaction_record_state(force_restore_state)
diff --git a/activerecord/lib/active_record/type/integer.rb b/activerecord/lib/active_record/type/integer.rb
index fc260a081a..394fe008f9 100644
--- a/activerecord/lib/active_record/type/integer.rb
+++ b/activerecord/lib/active_record/type/integer.rb
@@ -3,6 +3,10 @@ module ActiveRecord
class Integer < Value # :nodoc:
include Numeric
+ # Column storage size in bytes.
+ # 4 bytes means a MySQL int or Postgres integer as opposed to smallint etc.
+ DEFAULT_LIMIT = 4
+
def initialize(*)
super
@range = min_value...max_value
@@ -38,12 +42,12 @@ module ActiveRecord
def ensure_in_range(value)
unless range.cover?(value)
- raise RangeError, "#{value} is out of range for #{self.class} with limit #{limit || 4}"
+ raise RangeError, "#{value} is out of range for #{self.class} with limit #{limit || DEFAULT_LIMIT}"
end
end
def max_value
- limit = self.limit || 4
+ limit = self.limit || DEFAULT_LIMIT
1 << (limit * 8 - 1) # 8 bits per byte with one bit for sign
end
diff --git a/activerecord/lib/active_record/type/time.rb b/activerecord/lib/active_record/type/time.rb
index 41f7d97f0c..cab1c7bf1e 100644
--- a/activerecord/lib/active_record/type/time.rb
+++ b/activerecord/lib/active_record/type/time.rb
@@ -7,6 +7,19 @@ module ActiveRecord
:time
end
+ def user_input_in_time_zone(value)
+ return unless value.present?
+
+ case value
+ when ::String
+ value = "2000-01-01 #{value}"
+ when ::Time
+ value = value.change(year: 2000, day: 1, month: 1)
+ end
+
+ super(value)
+ end
+
private
def cast_value(value)
diff --git a/activerecord/lib/active_record/type/time_value.rb b/activerecord/lib/active_record/type/time_value.rb
index d611d72dd4..8d9ac25643 100644
--- a/activerecord/lib/active_record/type/time_value.rb
+++ b/activerecord/lib/active_record/type/time_value.rb
@@ -9,6 +9,10 @@ module ActiveRecord
"'#{value.to_s(:db)}'"
end
+ def user_input_in_time_zone(value)
+ value.in_time_zone
+ end
+
private
def new_time(year, mon, mday, hour, min, sec, microsec, offset = nil)
diff --git a/activerecord/lib/active_record/type/value.rb b/activerecord/lib/active_record/type/value.rb
index 60ae47db3d..859b51ca90 100644
--- a/activerecord/lib/active_record/type/value.rb
+++ b/activerecord/lib/active_record/type/value.rb
@@ -3,8 +3,7 @@ module ActiveRecord
class Value # :nodoc:
attr_reader :precision, :scale, :limit
- # Valid options are +precision+, +scale+, and +limit+. They are only
- # used when dumping schema.
+ # Valid options are +precision+, +scale+, and +limit+.
def initialize(options = {})
options.assert_valid_keys(:precision, :scale, :limit)
@precision = options[:precision]
diff --git a/activerecord/lib/rails/generators/active_record/migration/templates/create_table_migration.rb b/activerecord/lib/rails/generators/active_record/migration/templates/create_table_migration.rb
index fd94a2d038..5b3e57dcf6 100644
--- a/activerecord/lib/rails/generators/active_record/migration/templates/create_table_migration.rb
+++ b/activerecord/lib/rails/generators/active_record/migration/templates/create_table_migration.rb
@@ -4,6 +4,8 @@ class <%= migration_class_name %> < ActiveRecord::Migration
<% attributes.each do |attribute| -%>
<% if attribute.password_digest? -%>
t.string :password_digest<%= attribute.inject_options %>
+<% elsif attribute.token? -%>
+ t.string :<%= attribute.name %><%= attribute.inject_options %>
<% else -%>
t.<%= attribute.type %> :<%= attribute.name %><%= attribute.inject_options %>
<% end -%>
@@ -12,6 +14,9 @@ class <%= migration_class_name %> < ActiveRecord::Migration
t.timestamps
<% end -%>
end
+<% attributes.select(&:token?).each do |attribute| -%>
+ add_index :<%= table_name %>, :<%= attribute.index_name %><%= attribute.inject_index_options %>, unique: true
+<% end -%>
<% attributes_with_index.each do |attribute| -%>
add_index :<%= table_name %>, :<%= attribute.index_name %><%= attribute.inject_index_options %>
<% end -%>
diff --git a/activerecord/lib/rails/generators/active_record/migration/templates/migration.rb b/activerecord/lib/rails/generators/active_record/migration/templates/migration.rb
index ae9c74fd05..23a377db6a 100644
--- a/activerecord/lib/rails/generators/active_record/migration/templates/migration.rb
+++ b/activerecord/lib/rails/generators/active_record/migration/templates/migration.rb
@@ -4,6 +4,9 @@ class <%= migration_class_name %> < ActiveRecord::Migration
<% attributes.each do |attribute| -%>
<%- if attribute.reference? -%>
add_reference :<%= table_name %>, :<%= attribute.name %><%= attribute.inject_options %>
+ <%- elsif attribute.token? -%>
+ add_column :<%= table_name %>, :<%= attribute.name %>, :string<%= attribute.inject_options %>
+ add_index :<%= table_name %>, :<%= attribute.index_name %><%= attribute.inject_index_options %>, unique: true
<%- else -%>
add_column :<%= table_name %>, :<%= attribute.name %>, :<%= attribute.type %><%= attribute.inject_options %>
<%- if attribute.has_index? -%>
diff --git a/activerecord/lib/rails/generators/active_record/model/templates/model.rb b/activerecord/lib/rails/generators/active_record/model/templates/model.rb
index 539d969fce..55dc65c8ad 100644
--- a/activerecord/lib/rails/generators/active_record/model/templates/model.rb
+++ b/activerecord/lib/rails/generators/active_record/model/templates/model.rb
@@ -3,6 +3,9 @@ class <%= class_name %> < <%= parent_class_name.classify %>
<% attributes.select(&:reference?).each do |attribute| -%>
belongs_to :<%= attribute.name %><%= ', polymorphic: true' if attribute.polymorphic? %><%= ', required: true' if attribute.required? %>
<% end -%>
+<% attributes.select(&:token?).each do |attribute| -%>
+ has_secure_token<% if attribute.name != "token" %> :<%= attribute.name %><% end %>
+<% end -%>
<% if attributes.any?(&:password_digest?) -%>
has_secure_password
<% end -%>
diff --git a/activerecord/test/cases/adapters/postgresql/array_test.rb b/activerecord/test/cases/adapters/postgresql/array_test.rb
index 77055f5b7a..331f33455e 100644
--- a/activerecord/test/cases/adapters/postgresql/array_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/array_test.rb
@@ -112,8 +112,8 @@ class PostgresqlArrayTest < ActiveRecord::TestCase
def test_schema_dump_with_shorthand
output = dump_table_schema "pg_arrays"
- assert_match %r[t.string\s+"tags",\s+array: true], output
- assert_match %r[t.integer\s+"ratings",\s+array: true], output
+ assert_match %r[t\.string\s+"tags",\s+array: true], output
+ assert_match %r[t\.integer\s+"ratings",\s+array: true], output
end
def test_select_with_strings
diff --git a/activerecord/test/cases/adapters/postgresql/citext_test.rb b/activerecord/test/cases/adapters/postgresql/citext_test.rb
index 5a8083f7a7..cfbe842948 100644
--- a/activerecord/test/cases/adapters/postgresql/citext_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/citext_test.rb
@@ -72,7 +72,7 @@ if ActiveRecord::Base.connection.supports_extensions?
def test_schema_dump_with_shorthand
output = dump_table_schema("citexts")
- assert_match %r[t.citext "cival"], output
+ assert_match %r[t\.citext "cival"], output
end
end
end
diff --git a/activerecord/test/cases/adapters/postgresql/full_text_test.rb b/activerecord/test/cases/adapters/postgresql/full_text_test.rb
index a370a5adc6..e7e5d8e32a 100644
--- a/activerecord/test/cases/adapters/postgresql/full_text_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/full_text_test.rb
@@ -39,6 +39,6 @@ class PostgresqlFullTextTest < ActiveRecord::TestCase
def test_schema_dump_with_shorthand
output = dump_table_schema("tsvectors")
- assert_match %r{t.tsvector "text_vector"}, output
+ assert_match %r{t\.tsvector "text_vector"}, output
end
end
diff --git a/activerecord/test/cases/adapters/postgresql/hstore_test.rb b/activerecord/test/cases/adapters/postgresql/hstore_test.rb
index a0aa10630c..2ca123409a 100644
--- a/activerecord/test/cases/adapters/postgresql/hstore_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/hstore_test.rb
@@ -317,7 +317,7 @@ if ActiveRecord::Base.connection.supports_extensions?
def test_schema_dump_with_shorthand
output = dump_table_schema("hstores")
- assert_match %r[t.hstore "tags",\s+default: {}], output
+ assert_match %r[t\.hstore "tags",\s+default: {}], output
end
private
diff --git a/activerecord/test/cases/adapters/postgresql/json_test.rb b/activerecord/test/cases/adapters/postgresql/json_test.rb
index 7be7e00463..5f6cda1986 100644
--- a/activerecord/test/cases/adapters/postgresql/json_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/json_test.rb
@@ -66,7 +66,7 @@ module PostgresqlJSONSharedTestCases
def test_schema_dumping
output = dump_table_schema("json_data_type")
- assert_match(/t.#{column_type.to_s}\s+"payload",\s+default: {}/, output)
+ assert_match(/t\.#{column_type.to_s}\s+"payload",\s+default: {}/, output)
end
def test_cast_value_on_write
diff --git a/activerecord/test/cases/adapters/postgresql/ltree_test.rb b/activerecord/test/cases/adapters/postgresql/ltree_test.rb
index 771a825840..c5371348ea 100644
--- a/activerecord/test/cases/adapters/postgresql/ltree_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/ltree_test.rb
@@ -48,6 +48,6 @@ class PostgresqlLtreeTest < ActiveRecord::TestCase
def test_schema_dump_with_shorthand
output = dump_table_schema("ltrees")
- assert_match %r[t.ltree "path"], output
+ assert_match %r[t\.ltree "path"], output
end
end
diff --git a/activerecord/test/cases/adapters/postgresql/money_test.rb b/activerecord/test/cases/adapters/postgresql/money_test.rb
index f3a24eee85..78afd49b0b 100644
--- a/activerecord/test/cases/adapters/postgresql/money_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/money_test.rb
@@ -56,7 +56,7 @@ class PostgresqlMoneyTest < ActiveRecord::TestCase
def test_schema_dumping
output = dump_table_schema("postgresql_moneys")
assert_match %r{t\.money\s+"wealth",\s+scale: 2$}, output
- assert_match %r{t\.money\s+"depth",\s+scale: 2,\s+default: 150.55$}, output
+ assert_match %r{t\.money\s+"depth",\s+scale: 2,\s+default: 150\.55$}, output
end
def test_create_and_update_money
diff --git a/activerecord/test/cases/adapters/postgresql/network_test.rb b/activerecord/test/cases/adapters/postgresql/network_test.rb
index daa590f369..741876d3ca 100644
--- a/activerecord/test/cases/adapters/postgresql/network_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/network_test.rb
@@ -85,8 +85,8 @@ class PostgresqlNetworkTest < ActiveRecord::TestCase
def test_schema_dump_with_shorthand
output = dump_table_schema("postgresql_network_addresses")
- assert_match %r{t.inet\s+"inet_address",\s+default: "192.168.1.1"}, output
- assert_match %r{t.cidr\s+"cidr_address",\s+default: "192.168.1.0/24"}, output
- assert_match %r{t.macaddr\s+"mac_address",\s+default: "ff:ff:ff:ff:ff:ff"}, output
+ assert_match %r{t\.inet\s+"inet_address",\s+default: "192\.168\.1\.1"}, output
+ assert_match %r{t\.cidr\s+"cidr_address",\s+default: "192\.168\.1\.0/24"}, output
+ assert_match %r{t\.macaddr\s+"mac_address",\s+default: "ff:ff:ff:ff:ff:ff"}, output
end
end
diff --git a/activerecord/test/cases/adapters/postgresql/quoting_test.rb b/activerecord/test/cases/adapters/postgresql/quoting_test.rb
index 9ac0036d66..894cf1ffa2 100644
--- a/activerecord/test/cases/adapters/postgresql/quoting_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/quoting_test.rb
@@ -34,13 +34,14 @@ module ActiveRecord
def test_quote_range
range = "1,2]'; SELECT * FROM users; --".."a"
- c = PostgreSQLColumn.new(nil, nil, OID::Range.new(Type::Integer.new, :int8range))
- assert_equal "'[1,0]'", @conn.quote(range, c)
+ type = OID::Range.new(Type::Integer.new, :int8range)
+ assert_equal "'[1,0]'", @conn.quote(type.type_cast_for_database(range))
end
def test_quote_bit_string
- c = PostgreSQLColumn.new(nil, 1, OID::Bit.new)
- assert_equal nil, @conn.quote("'); SELECT * FROM users; /*\n01\n*/--", c)
+ value = "'); SELECT * FROM users; /*\n01\n*/--"
+ type = OID::Bit.new
+ assert_equal nil, @conn.quote(type.type_cast_for_database(value))
end
end
end
diff --git a/activerecord/test/cases/adapters/postgresql/schema_test.rb b/activerecord/test/cases/adapters/postgresql/schema_test.rb
index e99f1e2867..642b63357f 100644
--- a/activerecord/test/cases/adapters/postgresql/schema_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/schema_test.rb
@@ -454,7 +454,7 @@ class SchemaForeignKeyTest < ActiveRecord::TestCase
end
@connection.add_foreign_key "wagons", "my_schema.trains", column: "train_id"
output = dump_table_schema "wagons"
- assert_match %r{\s+add_foreign_key "wagons", "my_schema.trains", column: "train_id"$}, output
+ assert_match %r{\s+add_foreign_key "wagons", "my_schema\.trains", column: "train_id"$}, output
ensure
@connection.execute "DROP TABLE IF EXISTS wagons"
@connection.execute "DROP TABLE IF EXISTS my_schema.trains"
diff --git a/activerecord/test/cases/adapters/postgresql/uuid_test.rb b/activerecord/test/cases/adapters/postgresql/uuid_test.rb
index 7d2fae69d5..d328f17a3f 100644
--- a/activerecord/test/cases/adapters/postgresql/uuid_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/uuid_test.rb
@@ -114,7 +114,7 @@ class PostgresqlUUIDTest < ActiveRecord::TestCase
def test_schema_dump_with_shorthand
output = dump_table_schema "uuid_data_type"
- assert_match %r{t.uuid "guid"}, output
+ assert_match %r{t\.uuid "guid"}, output
end
def test_uniqueness_validation_ignores_uuid
diff --git a/activerecord/test/cases/adapters/postgresql/xml_test.rb b/activerecord/test/cases/adapters/postgresql/xml_test.rb
index 5aba118518..ac8464a65d 100644
--- a/activerecord/test/cases/adapters/postgresql/xml_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/xml_test.rb
@@ -50,6 +50,6 @@ class PostgresqlXMLTest < ActiveRecord::TestCase
def test_schema_dump_with_shorthand
output = dump_table_schema("xml_data_type")
- assert_match %r{t.xml "payload"}, output
+ assert_match %r{t\.xml "payload"}, output
end
end
diff --git a/activerecord/test/cases/adapters/sqlite3/quoting_test.rb b/activerecord/test/cases/adapters/sqlite3/quoting_test.rb
index df497e761c..274e358e4a 100644
--- a/activerecord/test/cases/adapters/sqlite3/quoting_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/quoting_test.rb
@@ -85,9 +85,9 @@ module ActiveRecord
def test_quoting_binary_strings
value = "hello".encode('ascii-8bit')
- column = Column.new(nil, 1, SQLite3String.new)
+ type = SQLite3String.new
- assert_equal "'hello'", @conn.quote(value, column)
+ assert_equal "'hello'", @conn.quote(type.type_cast_for_database(value))
end
end
end
diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb
index 01ee1234a2..4036294121 100644
--- a/activerecord/test/cases/attribute_methods_test.rb
+++ b/activerecord/test/cases/attribute_methods_test.rb
@@ -657,7 +657,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
end
end
- def test_setting_time_zone_aware_attribute_in_current_time_zone
+ def test_setting_time_zone_aware_datetime_in_current_time_zone
utc_time = Time.utc(2008, 1, 1)
in_time_zone "Pacific Time (US & Canada)" do
record = @target.new
@@ -676,6 +676,47 @@ class AttributeMethodsTest < ActiveRecord::TestCase
end
end
+ def test_setting_time_zone_aware_time_in_current_time_zone
+ in_time_zone "Pacific Time (US & Canada)" do
+ record = @target.new
+ time_string = "10:00:00"
+ expected_time = Time.zone.parse("2000-01-01 #{time_string}")
+
+ record.bonus_time = time_string
+ assert_equal expected_time, record.bonus_time
+ assert_equal ActiveSupport::TimeZone["Pacific Time (US & Canada)"], record.bonus_time.time_zone
+
+ record.bonus_time = ''
+ assert_nil record.bonus_time
+ end
+ end
+
+ def test_setting_time_zone_aware_time_with_dst
+ in_time_zone "Pacific Time (US & Canada)" do
+ current_time = Time.zone.local(2014, 06, 15, 10)
+ record = @target.new(bonus_time: current_time)
+ time_before_save = record.bonus_time
+
+ record.save
+ record.reload
+
+ assert_equal time_before_save, record.bonus_time
+ assert_equal ActiveSupport::TimeZone["Pacific Time (US & Canada)"], record.bonus_time.time_zone
+ end
+ end
+
+ def test_removing_time_zone_aware_types
+ with_time_zone_aware_types(:datetime) do
+ in_time_zone "Pacific Time (US & Canada)" do
+ record = @target.new(bonus_time: "10:00:00")
+ expected_time = Time.utc(2000, 01, 01, 10)
+
+ assert_equal expected_time, record.bonus_time
+ assert record.bonus_time.utc?
+ end
+ end
+ end
+
def test_setting_time_zone_conversion_for_attributes_should_write_value_on_class_variable
Topic.skip_time_zone_conversion_for_attributes = [:field_a]
Minimalistic.skip_time_zone_conversion_for_attributes = [:field_b]
@@ -888,6 +929,14 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert_not_equal ['id'], @target.column_names
end
+ def test_came_from_user
+ model = @target.first
+
+ assert_not model.id_came_from_user?
+ model.id = "omg"
+ assert model.id_came_from_user?
+ end
+
private
def new_topic_like_ar_class(&block)
@@ -900,6 +949,14 @@ class AttributeMethodsTest < ActiveRecord::TestCase
klass
end
+ def with_time_zone_aware_types(*types)
+ old_types = ActiveRecord::Base.time_zone_aware_types
+ ActiveRecord::Base.time_zone_aware_types = types
+ yield
+ ensure
+ ActiveRecord::Base.time_zone_aware_types = old_types
+ end
+
def cached_columns
Topic.columns.map(&:name)
end
diff --git a/activerecord/test/cases/autosave_association_test.rb b/activerecord/test/cases/autosave_association_test.rb
index 04d5a2869c..52765881d0 100644
--- a/activerecord/test/cases/autosave_association_test.rb
+++ b/activerecord/test/cases/autosave_association_test.rb
@@ -1030,6 +1030,16 @@ class TestAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCase
assert_equal 'The Vile Insanity', @pirate.reload.ship.name
end
+ def test_changed_for_autosave_should_handle_cycles
+ @ship.pirate = @pirate
+ assert_queries(0) { @ship.save! }
+
+ @parrot = @pirate.parrots.create(name: "some_name")
+ @parrot.name="changed_name"
+ assert_queries(1) { @ship.save! }
+ assert_queries(0) { @ship.save! }
+ end
+
def test_should_automatically_save_bang_the_associated_model
@pirate.ship.name = 'The Vile Insanity'
@pirate.save!
diff --git a/activerecord/test/cases/date_time_test.rb b/activerecord/test/cases/date_time_test.rb
index 330232cee2..4cbff564aa 100644
--- a/activerecord/test/cases/date_time_test.rb
+++ b/activerecord/test/cases/date_time_test.rb
@@ -55,7 +55,7 @@ class DateTimeTest < ActiveRecord::TestCase
now = DateTime.now
with_timezone_config default: :local do
task = Task.new starting: now
- assert now, task.starting
+ assert_equal now, task.starting
end
end
end
diff --git a/activerecord/test/cases/dirty_test.rb b/activerecord/test/cases/dirty_test.rb
index 5b71bd7e67..192ba6f7cd 100644
--- a/activerecord/test/cases/dirty_test.rb
+++ b/activerecord/test/cases/dirty_test.rb
@@ -708,6 +708,27 @@ class DirtyTest < ActiveRecord::TestCase
assert model.first_name_changed?
end
+ test "attribute_will_change! doesn't try to save non-persistable attributes" do
+ klass = Class.new(ActiveRecord::Base) do
+ self.table_name = 'people'
+ attribute :non_persisted_attribute, ActiveRecord::Type::String.new
+ end
+
+ record = klass.new(first_name: "Sean")
+ record.non_persisted_attribute_will_change!
+
+ assert record.non_persisted_attribute_changed?
+ assert record.save
+ end
+
+ test "mutating and then assigning doesn't remove the change" do
+ pirate = Pirate.create!(catchphrase: "arrrr")
+ pirate.catchphrase << " matey!"
+ pirate.catchphrase = "arrrr matey!"
+
+ assert pirate.catchphrase_changed?(from: "arrrr", to: "arrrr matey!")
+ end
+
private
def with_partial_writes(klass, on = true)
old = klass.partial_writes?
diff --git a/activerecord/test/cases/helper.rb b/activerecord/test/cases/helper.rb
index 925491acbd..0a577fa2f5 100644
--- a/activerecord/test/cases/helper.rb
+++ b/activerecord/test/cases/helper.rb
@@ -30,6 +30,9 @@ ARTest.connect
# Quote "type" if it's a reserved word for the current connection.
QUOTED_TYPE = ActiveRecord::Base.connection.quote_column_name('type')
+# FIXME: Remove this when the deprecation cycle on TZ aware types by default ends.
+ActiveRecord::Base.time_zone_aware_types << :time
+
def current_adapter?(*types)
types.any? do |type|
ActiveRecord::ConnectionAdapters.const_defined?(type) &&
diff --git a/activerecord/test/cases/locking_test.rb b/activerecord/test/cases/locking_test.rb
index ee43f07dd7..848174df06 100644
--- a/activerecord/test/cases/locking_test.rb
+++ b/activerecord/test/cases/locking_test.rb
@@ -215,10 +215,12 @@ class OptimisticLockingTest < ActiveRecord::TestCase
def test_lock_with_custom_column_without_default_sets_version_to_zero
t1 = LockWithCustomColumnWithoutDefault.new
assert_equal 0, t1.custom_lock_version
+ assert_nil t1.custom_lock_version_before_type_cast
- t1.save
- t1 = LockWithCustomColumnWithoutDefault.find(t1.id)
+ t1.save!
+ t1.reload
assert_equal 0, t1.custom_lock_version
+ assert [0, "0"].include?(t1.custom_lock_version_before_type_cast)
end
def test_readonly_attributes
diff --git a/activerecord/test/cases/log_subscriber_test.rb b/activerecord/test/cases/log_subscriber_test.rb
index a578e81844..97c0350911 100644
--- a/activerecord/test/cases/log_subscriber_test.rb
+++ b/activerecord/test/cases/log_subscriber_test.rb
@@ -125,12 +125,5 @@ class LogSubscriberTest < ActiveRecord::TestCase
wait
assert_match(/<16 bytes of binary data>/, @logger.logged(:debug).join)
end
-
- def test_nil_binary_data_is_logged
- binary = Binary.create(data: "")
- binary.update_attributes(data: nil)
- wait
- assert_match(/<NULL binary data>/, @logger.logged(:debug).join)
- end
end
end
diff --git a/activerecord/test/cases/migration/column_positioning_test.rb b/activerecord/test/cases/migration/column_positioning_test.rb
index 62186e13a5..4637970ce0 100644
--- a/activerecord/test/cases/migration/column_positioning_test.rb
+++ b/activerecord/test/cases/migration/column_positioning_test.rb
@@ -3,7 +3,7 @@ require 'cases/helper'
module ActiveRecord
class Migration
class ColumnPositioningTest < ActiveRecord::TestCase
- attr_reader :connection, :table_name
+ attr_reader :connection
alias :conn :connection
def setup
diff --git a/activerecord/test/cases/migration/foreign_key_test.rb b/activerecord/test/cases/migration/foreign_key_test.rb
index ad35d690bd..f8b1bf8c9d 100644
--- a/activerecord/test/cases/migration/foreign_key_test.rb
+++ b/activerecord/test/cases/migration/foreign_key_test.rb
@@ -225,7 +225,7 @@ module ActiveRecord
def silence_stream(stream)
old_stream = stream.dup
- stream.reopen(RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ ? 'NUL:' : '/dev/null')
+ stream.reopen(IO::NULL)
stream.sync = true
yield
ensure
diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb
index 5829ef2100..7c2d3e81d6 100644
--- a/activerecord/test/cases/migration_test.rb
+++ b/activerecord/test/cases/migration_test.rb
@@ -939,7 +939,7 @@ class CopyMigrationsTest < ActiveRecord::TestCase
def silence_stream(stream)
old_stream = stream.dup
- stream.reopen(RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ ? 'NUL:' : '/dev/null')
+ stream.reopen(IO::NULL)
stream.sync = true
yield
ensure
diff --git a/activerecord/test/cases/multiparameter_attributes_test.rb b/activerecord/test/cases/multiparameter_attributes_test.rb
index 14d4ef457d..4aaf6f8b5f 100644
--- a/activerecord/test/cases/multiparameter_attributes_test.rb
+++ b/activerecord/test/cases/multiparameter_attributes_test.rb
@@ -250,8 +250,8 @@ class MultiParameterAttributeTest < ActiveRecord::TestCase
}
topic = Topic.find(1)
topic.attributes = attributes
- assert_equal Time.utc(2000, 1, 1, 16, 24, 0), topic.bonus_time
- assert topic.bonus_time.utc?
+ assert_equal Time.zone.local(2000, 1, 1, 16, 24, 0), topic.bonus_time
+ assert_not topic.bonus_time.utc?
end
end
end
diff --git a/activerecord/test/cases/multiple_db_test.rb b/activerecord/test/cases/multiple_db_test.rb
index 3831de6ae3..15c60d5562 100644
--- a/activerecord/test/cases/multiple_db_test.rb
+++ b/activerecord/test/cases/multiple_db_test.rb
@@ -93,6 +93,13 @@ class MultipleDbTest < ActiveRecord::TestCase
assert_not_equal Entrant.arel_engine.connection, Course.arel_engine.connection
end
+ def test_count_on_custom_connection
+ ActiveRecord::Base.remove_connection
+ assert_equal 1, College.count
+ ensure
+ ActiveRecord::Base.establish_connection :arunit
+ end
+
unless in_memory_db?
def test_associations_should_work_when_model_has_no_connection
begin
diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb
index d6816041bc..2803ad2de0 100644
--- a/activerecord/test/cases/persistence_test.rb
+++ b/activerecord/test/cases/persistence_test.rb
@@ -356,6 +356,16 @@ class PersistenceTest < ActiveRecord::TestCase
assert_equal("David", topic_reloaded.author_name)
end
+ def test_update_attribute_does_not_run_sql_if_attribute_is_not_changed
+ klass = Class.new(Topic) do
+ def self.name; 'Topic'; end
+ end
+ topic = klass.create(title: 'Another New Topic')
+ assert_queries(0) do
+ topic.update_attribute(:title, 'Another New Topic')
+ end
+ end
+
def test_delete
topic = Topic.find(1)
assert_equal topic, topic.delete, 'topic.delete did not return self'
diff --git a/activerecord/test/cases/quoting_test.rb b/activerecord/test/cases/quoting_test.rb
index 1d6ae2f67f..ad09340518 100644
--- a/activerecord/test/cases/quoting_test.rb
+++ b/activerecord/test/cases/quoting_test.rb
@@ -125,14 +125,11 @@ module ActiveRecord
end
def test_crazy_object
- crazy = Class.new.new
- expected = "'#{YAML.dump(crazy)}'"
- assert_equal expected, @quoter.quote(crazy, nil)
- end
-
- def test_crazy_object_calls_quote_string
- crazy = Class.new { def initialize; @lol = 'lo\l' end }.new
- assert_match "lo\\\\l", @quoter.quote(crazy, nil)
+ crazy = Object.new
+ e = assert_raises(TypeError) do
+ @quoter.quote(crazy, nil)
+ end
+ assert_equal "can't quote Object", e.message
end
def test_quote_string_no_column
diff --git a/activerecord/test/cases/relation/predicate_builder_test.rb b/activerecord/test/cases/relation/predicate_builder_test.rb
index 0cc081fced..8f62014622 100644
--- a/activerecord/test/cases/relation/predicate_builder_test.rb
+++ b/activerecord/test/cases/relation/predicate_builder_test.rb
@@ -8,7 +8,7 @@ module ActiveRecord
Arel::Nodes::InfixOperation.new('~', column, Arel.sql(value.source))
end)
- assert_match %r{["`]topics["`].["`]title["`] ~ rails}i, Topic.where(title: /rails/).to_sql
+ assert_match %r{["`]topics["`]\.["`]title["`] ~ rails}i, Topic.where(title: /rails/).to_sql
ensure
Topic.reset_column_information
end
diff --git a/activerecord/test/cases/relation/where_test.rb b/activerecord/test/cases/relation/where_test.rb
index d675953da6..b0573579da 100644
--- a/activerecord/test/cases/relation/where_test.rb
+++ b/activerecord/test/cases/relation/where_test.rb
@@ -1,13 +1,15 @@
require "cases/helper"
-require 'models/author'
-require 'models/price_estimate'
-require 'models/treasure'
-require 'models/post'
-require 'models/comment'
-require 'models/edge'
-require 'models/topic'
-require 'models/binary'
-require 'models/vertex'
+require "models/author"
+require "models/binary"
+require "models/cake_designer"
+require "models/chef"
+require "models/comment"
+require "models/edge"
+require "models/post"
+require "models/price_estimate"
+require "models/topic"
+require "models/treasure"
+require "models/vertex"
module ActiveRecord
class WhereTest < ActiveRecord::TestCase
@@ -26,6 +28,16 @@ module ActiveRecord
}
end
+ def test_where_copies_arel_bind_params
+ chef = Chef.create!
+ CakeDesigner.create!(chef: chef)
+
+ cake_designers = CakeDesigner.joins(:chef).where(chefs: { id: chef.id })
+ chefs = Chef.where(employable: cake_designers)
+
+ assert_equal [chef], chefs.to_a
+ end
+
def test_rewhere_on_root
assert_equal posts(:welcome), Post.rewhere(title: 'Welcome to the weblog').first
end
diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb
index b52c66356c..c8ea702488 100644
--- a/activerecord/test/cases/schema_dumper_test.rb
+++ b/activerecord/test/cases/schema_dumper_test.rb
@@ -204,25 +204,25 @@ class SchemaDumperTest < ActiveRecord::TestCase
if current_adapter?(:MysqlAdapter, :Mysql2Adapter)
def test_schema_dump_should_add_default_value_for_mysql_text_field
output = standard_dump
- assert_match %r{t.text\s+"body",\s+limit: 65535,\s+null: false$}, output
+ assert_match %r{t\.text\s+"body",\s+limit: 65535,\s+null: false$}, output
end
def test_schema_dump_includes_length_for_mysql_binary_fields
output = standard_dump
- assert_match %r{t.binary\s+"var_binary",\s+limit: 255$}, output
- assert_match %r{t.binary\s+"var_binary_large",\s+limit: 4095$}, output
+ assert_match %r{t\.binary\s+"var_binary",\s+limit: 255$}, output
+ assert_match %r{t\.binary\s+"var_binary_large",\s+limit: 4095$}, output
end
def test_schema_dump_includes_length_for_mysql_blob_and_text_fields
output = standard_dump
- assert_match %r{t.binary\s+"tiny_blob",\s+limit: 255$}, output
- assert_match %r{t.binary\s+"normal_blob",\s+limit: 65535$}, output
- assert_match %r{t.binary\s+"medium_blob",\s+limit: 16777215$}, output
- assert_match %r{t.binary\s+"long_blob",\s+limit: 4294967295$}, output
- assert_match %r{t.text\s+"tiny_text",\s+limit: 255$}, output
- assert_match %r{t.text\s+"normal_text",\s+limit: 65535$}, output
- assert_match %r{t.text\s+"medium_text",\s+limit: 16777215$}, output
- assert_match %r{t.text\s+"long_text",\s+limit: 4294967295$}, output
+ assert_match %r{t\.binary\s+"tiny_blob",\s+limit: 255$}, output
+ assert_match %r{t\.binary\s+"normal_blob",\s+limit: 65535$}, output
+ assert_match %r{t\.binary\s+"medium_blob",\s+limit: 16777215$}, output
+ assert_match %r{t\.binary\s+"long_blob",\s+limit: 4294967295$}, output
+ assert_match %r{t\.text\s+"tiny_text",\s+limit: 255$}, output
+ assert_match %r{t\.text\s+"normal_text",\s+limit: 65535$}, output
+ assert_match %r{t\.text\s+"medium_text",\s+limit: 16777215$}, output
+ assert_match %r{t\.text\s+"long_text",\s+limit: 4294967295$}, output
end
def test_schema_dumps_index_type
@@ -235,19 +235,19 @@ class SchemaDumperTest < ActiveRecord::TestCase
if mysql_56?
def test_schema_dump_includes_datetime_precision
output = standard_dump
- assert_match %r{t.datetime\s+"written_on",\s+precision: 6$}, output
+ assert_match %r{t\.datetime\s+"written_on",\s+precision: 6$}, output
end
end
def test_schema_dump_includes_decimal_options
output = dump_all_table_schema([/^[^n]/])
- assert_match %r{precision: 3,[[:space:]]+scale: 2,[[:space:]]+default: 2.78}, output
+ assert_match %r{precision: 3,[[:space:]]+scale: 2,[[:space:]]+default: 2\.78}, output
end
if current_adapter?(:PostgreSQLAdapter)
def test_schema_dump_includes_bigint_default
output = standard_dump
- assert_match %r{t.integer\s+"bigint_default",\s+limit: 8,\s+default: 0}, output
+ assert_match %r{t\.integer\s+"bigint_default",\s+limit: 8,\s+default: 0}, output
end
if ActiveRecord::Base.connection.supports_extensions?
@@ -271,11 +271,11 @@ class SchemaDumperTest < ActiveRecord::TestCase
output = standard_dump
# Oracle supports precision up to 38 and it identifies decimals with scale 0 as integers
if current_adapter?(:OracleAdapter)
- assert_match %r{t.integer\s+"atoms_in_universe",\s+precision: 38}, output
+ assert_match %r{t\.integer\s+"atoms_in_universe",\s+precision: 38}, output
elsif current_adapter?(:FbAdapter)
- assert_match %r{t.integer\s+"atoms_in_universe",\s+precision: 18}, output
+ assert_match %r{t\.integer\s+"atoms_in_universe",\s+precision: 18}, output
else
- assert_match %r{t.decimal\s+"atoms_in_universe",\s+precision: 55}, output
+ assert_match %r{t\.decimal\s+"atoms_in_universe",\s+precision: 55}, output
end
end
@@ -284,7 +284,7 @@ class SchemaDumperTest < ActiveRecord::TestCase
match = output.match(%r{create_table "goofy_string_id"(.*)do.*\n(.*)\n})
assert_not_nil(match, "goofy_string_id table not found")
assert_match %r(id: false), match[1], "no table id not preserved"
- assert_match %r{t.string\s+"id",.*?null: false$}, match[2], "non-primary key id column not preserved"
+ assert_match %r{t\.string\s+"id",.*?null: false$}, match[2], "non-primary key id column not preserved"
end
def test_schema_dump_keeps_id_false_when_id_is_false_and_unique_not_null_column_added
diff --git a/activerecord/test/cases/secure_token_test.rb b/activerecord/test/cases/secure_token_test.rb
index dc12b528dc..3f7455d12d 100644
--- a/activerecord/test/cases/secure_token_test.rb
+++ b/activerecord/test/cases/secure_token_test.rb
@@ -22,18 +22,4 @@ class SecureTokenTest < ActiveRecord::TestCase
assert_not_equal @user.token, old_token
assert_not_equal @user.auth_token, old_auth_token
end
-
- def test_raise_after_ten_unsuccessful_attempts_to_generate_a_unique_token
- User.stubs(:exists?).returns(*Array.new(10, true))
- assert_raises(RuntimeError) do
- @user.save
- end
- end
-
- def test_return_unique_token_after_nine_unsuccessful_attempts
- User.stubs(:exists?).returns(*Array.new(10) { |i| i == 9 ? false : true })
- @user.save
- assert_not_nil @user.token
- assert_not_nil @user.auth_token
- end
end
diff --git a/activerecord/test/cases/xml_serialization_test.rb b/activerecord/test/cases/xml_serialization_test.rb
index c34e7d5a30..b30b50f597 100644
--- a/activerecord/test/cases/xml_serialization_test.rb
+++ b/activerecord/test/cases/xml_serialization_test.rb
@@ -19,7 +19,7 @@ class XmlSerializationTest < ActiveRecord::TestCase
def test_should_serialize_default_root_with_namespace
@xml = Contact.new.to_xml :namespace=>"http://xml.rubyonrails.org/contact"
- assert_match %r{^<contact xmlns="http://xml.rubyonrails.org/contact">}, @xml
+ assert_match %r{^<contact xmlns="http://xml\.rubyonrails\.org/contact">}, @xml
assert_match %r{</contact>$}, @xml
end
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index 2756f7e0e2..2caf0b27ee 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -1,3 +1,19 @@
+* Duplicate frozen array when assigning it to a HashWithIndifferentAccess so
+ that it doesn't raise a `RuntimeError` when calling `map!` on it in `convert_value`.
+
+ Fixes #18550.
+
+ *Aditya Kapoor*
+
+* Add missing time zone definitions for Russian Federation and sync them
+ with `zone.tab` file from tzdata version 2014j (latest).
+
+ *Andrey Novikov*
+
+* Add `SecureRandom.base58` for generation of random base58 strings.
+
+ *Matthew Draper*, *Guillermo Iguaran*
+
* Add `#prev_day` and `#next_day` counterparts to `#yesterday` and
`#tomorrow` for `Date`, `Time`, and `DateTime`.
@@ -105,6 +121,8 @@
* Add support for error dispatcher classes in `ActiveSupport::Rescuable`.
Now it acts closer to Ruby's rescue.
+ Example:
+
class BaseController < ApplicationController
module ErrorDispatcher
def self.===(other)
diff --git a/activesupport/lib/active_support/core_ext/array/access.rb b/activesupport/lib/active_support/core_ext/array/access.rb
index 45b89d2705..ca66d806ef 100644
--- a/activesupport/lib/active_support/core_ext/array/access.rb
+++ b/activesupport/lib/active_support/core_ext/array/access.rb
@@ -21,7 +21,7 @@ class Array
# %w( a b c ).to(-10) # => []
def to(position)
if position >= 0
- first position + 1
+ take position + 1
else
self[0..position]
end
diff --git a/activesupport/lib/active_support/core_ext/kernel/reporting.rb b/activesupport/lib/active_support/core_ext/kernel/reporting.rb
index eb44646848..9189e6d977 100644
--- a/activesupport/lib/active_support/core_ext/kernel/reporting.rb
+++ b/activesupport/lib/active_support/core_ext/kernel/reporting.rb
@@ -1,4 +1,3 @@
-require 'rbconfig'
require 'tempfile'
module Kernel
diff --git a/activesupport/lib/active_support/core_ext/securerandom.rb b/activesupport/lib/active_support/core_ext/securerandom.rb
new file mode 100644
index 0000000000..6cdbea1f37
--- /dev/null
+++ b/activesupport/lib/active_support/core_ext/securerandom.rb
@@ -0,0 +1,23 @@
+require 'securerandom'
+
+module SecureRandom
+ BASE58_ALPHABET = ('0'..'9').to_a + ('A'..'Z').to_a + ('a'..'z').to_a - ['0', 'O', 'I', 'l']
+ # SecureRandom.base58 generates a random base58 string.
+ #
+ # The argument _n_ specifies the length, of the random string to be generated.
+ #
+ # If _n_ is not specified or is nil, 16 is assumed. It may be larger in the future.
+ #
+ # The result may contain alphanumeric characters except 0, O, I and l
+ #
+ # p SecureRandom.base58 #=> "4kUgL2pdQMSCQtjE"
+ # p SecureRandom.base58(24) #=> "77TMHrHJFvFDwodq8w7Ev2m7"
+ #
+ def self.base58(n = 16)
+ SecureRandom.random_bytes(n).unpack("C*").map do |byte|
+ idx = byte % 64
+ idx = SecureRandom.random_number(58) if idx >= 58
+ BASE58_ALPHABET[idx]
+ end.join
+ end
+end
diff --git a/activesupport/lib/active_support/duration.rb b/activesupport/lib/active_support/duration.rb
index 2818b8d68b..5a64fc52cc 100644
--- a/activesupport/lib/active_support/duration.rb
+++ b/activesupport/lib/active_support/duration.rb
@@ -91,7 +91,7 @@ module ActiveSupport
reduce(::Hash.new(0)) { |h,(l,r)| h[l] += r; h }.
sort_by {|unit, _ | [:years, :months, :days, :minutes, :seconds].index(unit)}.
map {|unit, val| "#{val} #{val == 1 ? unit.to_s.chop : unit.to_s}"}.
- to_sentence(:locale => :en)
+ to_sentence(locale: ::I18n.default_locale)
end
def as_json(options = nil) #:nodoc:
diff --git a/activesupport/lib/active_support/hash_with_indifferent_access.rb b/activesupport/lib/active_support/hash_with_indifferent_access.rb
index 1468c62151..4f71f13971 100644
--- a/activesupport/lib/active_support/hash_with_indifferent_access.rb
+++ b/activesupport/lib/active_support/hash_with_indifferent_access.rb
@@ -267,7 +267,7 @@ module ActiveSupport
value.nested_under_indifferent_access
end
elsif value.is_a?(Array)
- unless options[:for] == :assignment
+ if options[:for] != :assignment || value.frozen?
value = value.dup
end
value.map! { |e| convert_value(e, options) }
diff --git a/activesupport/lib/active_support/inflector/methods.rb b/activesupport/lib/active_support/inflector/methods.rb
index 74b3a7c2a9..fe8a2ac9ba 100644
--- a/activesupport/lib/active_support/inflector/methods.rb
+++ b/activesupport/lib/active_support/inflector/methods.rb
@@ -22,49 +22,49 @@ module ActiveSupport
# pluralized using rules defined for that language. By default,
# this parameter is set to <tt>:en</tt>.
#
- # 'post'.pluralize # => "posts"
- # 'octopus'.pluralize # => "octopi"
- # 'sheep'.pluralize # => "sheep"
- # 'words'.pluralize # => "words"
- # 'CamelOctopus'.pluralize # => "CamelOctopi"
- # 'ley'.pluralize(:es) # => "leyes"
+ # pluralize('post') # => "posts"
+ # pluralize('octopus') # => "octopi"
+ # pluralize('sheep') # => "sheep"
+ # pluralize('words') # => "words"
+ # pluralize('CamelOctopus') # => "CamelOctopi"
+ # pluralize('ley', :es) # => "leyes"
def pluralize(word, locale = :en)
apply_inflections(word, inflections(locale).plurals)
end
- # The reverse of +pluralize+, returns the singular form of a word in a
+ # The reverse of #pluralize, returns the singular form of a word in a
# string.
#
# If passed an optional +locale+ parameter, the word will be
# singularized using rules defined for that language. By default,
# this parameter is set to <tt>:en</tt>.
#
- # 'posts'.singularize # => "post"
- # 'octopi'.singularize # => "octopus"
- # 'sheep'.singularize # => "sheep"
- # 'word'.singularize # => "word"
- # 'CamelOctopi'.singularize # => "CamelOctopus"
- # 'leyes'.singularize(:es) # => "ley"
+ # singularize('posts') # => "post"
+ # singularize('octopi') # => "octopus"
+ # singularize('sheep') # => "sheep"
+ # singularize('word') # => "word"
+ # singularize('CamelOctopi') # => "CamelOctopus"
+ # singularize('leyes', :es) # => "ley"
def singularize(word, locale = :en)
apply_inflections(word, inflections(locale).singulars)
end
- # By default, +camelize+ converts strings to UpperCamelCase. If the argument
- # to +camelize+ is set to <tt>:lower</tt> then +camelize+ produces
+ # Converts strings to UpperCamelCase.
+ # If the +uppercase_first_letter+ parameter is set to false, then produces
# lowerCamelCase.
#
- # +camelize+ will also convert '/' to '::' which is useful for converting
+ # Also converts '/' to '::' which is useful for converting
# paths to namespaces.
#
- # 'active_model'.camelize # => "ActiveModel"
- # 'active_model'.camelize(:lower) # => "activeModel"
- # 'active_model/errors'.camelize # => "ActiveModel::Errors"
- # 'active_model/errors'.camelize(:lower) # => "activeModel::Errors"
+ # camelize('active_model') # => "ActiveModel"
+ # camelize('active_model', false) # => "activeModel"
+ # camelize('active_model/errors') # => "ActiveModel::Errors"
+ # camelize('active_model/errors', false) # => "activeModel::Errors"
#
# As a rule of thumb you can think of +camelize+ as the inverse of
- # +underscore+, though there are cases where that does not hold:
+ # #underscore, though there are cases where that does not hold:
#
- # 'SSLError'.underscore.camelize # => "SslError"
+ # camelize(underscore('SSLError')) # => "SslError"
def camelize(term, uppercase_first_letter = true)
string = term.to_s
if uppercase_first_letter
@@ -81,13 +81,13 @@ module ActiveSupport
#
# Changes '::' to '/' to convert namespaces to paths.
#
- # 'ActiveModel'.underscore # => "active_model"
- # 'ActiveModel::Errors'.underscore # => "active_model/errors"
+ # underscore('ActiveModel') # => "active_model"
+ # underscore('ActiveModel::Errors') # => "active_model/errors"
#
# As a rule of thumb you can think of +underscore+ as the inverse of
- # +camelize+, though there are cases where that does not hold:
+ # #camelize, though there are cases where that does not hold:
#
- # 'SSLError'.underscore.camelize # => "SslError"
+ # camelize(underscore('SSLError')) # => "SslError"
def underscore(camel_cased_word)
return camel_cased_word unless camel_cased_word =~ /[A-Z-]|::/
word = camel_cased_word.to_s.gsub(/::/, '/')
@@ -101,14 +101,14 @@ module ActiveSupport
# Tweaks an attribute name for display to end users.
#
- # Specifically, +humanize+ performs these transformations:
+ # Specifically, performs these transformations:
#
- # * Applies human inflection rules to the argument.
- # * Deletes leading underscores, if any.
- # * Removes a "_id" suffix if present.
- # * Replaces underscores with spaces, if any.
- # * Downcases all words except acronyms.
- # * Capitalizes the first word.
+ # * Applies human inflection rules to the argument.
+ # * Deletes leading underscores, if any.
+ # * Removes a "_id" suffix if present.
+ # * Replaces underscores with spaces, if any.
+ # * Downcases all words except acronyms.
+ # * Capitalizes the first word.
#
# The capitalization of the first word can be turned off by setting the
# +:capitalize+ option to false (default is true).
@@ -148,34 +148,34 @@ module ActiveSupport
#
# +titleize+ is also aliased as +titlecase+.
#
- # 'man from the boondocks'.titleize # => "Man From The Boondocks"
- # 'x-men: the last stand'.titleize # => "X Men: The Last Stand"
- # 'TheManWithoutAPast'.titleize # => "The Man Without A Past"
- # 'raiders_of_the_lost_ark'.titleize # => "Raiders Of The Lost Ark"
+ # titleize('man from the boondocks') # => "Man From The Boondocks"
+ # titleize('x-men: the last stand') # => "X Men: The Last Stand"
+ # titleize('TheManWithoutAPast') # => "The Man Without A Past"
+ # titleize('raiders_of_the_lost_ark') # => "Raiders Of The Lost Ark"
def titleize(word)
humanize(underscore(word)).gsub(/\b(?<!['’`])[a-z]/) { $&.capitalize }
end
- # Create the name of a table like Rails does for models to table names. This
- # method uses the +pluralize+ method on the last word in the string.
+ # Creates the name of a table like Rails does for models to table names.
+ # This method uses the #pluralize method on the last word in the string.
#
- # 'RawScaledScorer'.tableize # => "raw_scaled_scorers"
- # 'egg_and_ham'.tableize # => "egg_and_hams"
- # 'fancyCategory'.tableize # => "fancy_categories"
+ # tableize('RawScaledScorer') # => "raw_scaled_scorers"
+ # tableize('egg_and_ham') # => "egg_and_hams"
+ # tableize('fancyCategory') # => "fancy_categories"
def tableize(class_name)
pluralize(underscore(class_name))
end
- # Create a class name from a plural table name like Rails does for table
+ # Creates a class name from a plural table name like Rails does for table
# names to models. Note that this returns a string and not a Class (To
- # convert to an actual class follow +classify+ with +constantize+).
+ # convert to an actual class follow +classify+ with #constantize).
#
- # 'egg_and_hams'.classify # => "EggAndHam"
- # 'posts'.classify # => "Post"
+ # classify('egg_and_hams') # => "EggAndHam"
+ # classify('posts') # => "Post"
#
# Singular names are not handled correctly:
#
- # 'calculus'.classify # => "Calculu"
+ # classify('calculus') # => "Calculu"
def classify(table_name)
# strip out any leading schema name
camelize(singularize(table_name.to_s.sub(/.*\./, '')))
@@ -183,19 +183,19 @@ module ActiveSupport
# Replaces underscores with dashes in the string.
#
- # 'puni_puni'.dasherize # => "puni-puni"
+ # dasherize('puni_puni') # => "puni-puni"
def dasherize(underscored_word)
underscored_word.tr('_', '-')
end
# Removes the module part from the expression in the string.
#
- # 'ActiveRecord::CoreExtensions::String::Inflections'.demodulize # => "Inflections"
- # 'Inflections'.demodulize # => "Inflections"
- # '::Inflections'.demodulize # => "Inflections"
- # ''.demodulize # => ""
+ # demodulize('ActiveRecord::CoreExtensions::String::Inflections') # => "Inflections"
+ # demodulize('Inflections') # => "Inflections"
+ # demodulize('::Inflections') # => "Inflections"
+ # demodulize('') # => ""
#
- # See also +deconstantize+.
+ # See also #deconstantize.
def demodulize(path)
path = path.to_s
if i = path.rindex('::')
@@ -207,13 +207,13 @@ module ActiveSupport
# Removes the rightmost segment from the constant expression in the string.
#
- # 'Net::HTTP'.deconstantize # => "Net"
- # '::Net::HTTP'.deconstantize # => "::Net"
- # 'String'.deconstantize # => ""
- # '::String'.deconstantize # => ""
- # ''.deconstantize # => ""
+ # deconstantize('Net::HTTP') # => "Net"
+ # deconstantize('::Net::HTTP') # => "::Net"
+ # deconstantize('String') # => ""
+ # deconstantize('::String') # => ""
+ # deconstantize('') # => ""
#
- # See also +demodulize+.
+ # See also #demodulize.
def deconstantize(path)
path.to_s[0, path.rindex('::') || 0] # implementation based on the one in facets' Module#spacename
end
@@ -222,9 +222,9 @@ module ActiveSupport
# +separate_class_name_and_id_with_underscore+ sets whether
# the method should put '_' between the name and 'id'.
#
- # 'Message'.foreign_key # => "message_id"
- # 'Message'.foreign_key(false) # => "messageid"
- # 'Admin::Post'.foreign_key # => "post_id"
+ # foreign_key('Message') # => "message_id"
+ # foreign_key('Message', false) # => "messageid"
+ # foreign_key('Admin::Post') # => "post_id"
def foreign_key(class_name, separate_class_name_and_id_with_underscore = true)
underscore(demodulize(class_name)) + (separate_class_name_and_id_with_underscore ? "_id" : "id")
end
@@ -280,8 +280,8 @@ module ActiveSupport
# Tries to find a constant with the name specified in the argument string.
#
- # 'Module'.safe_constantize # => Module
- # 'Test::Unit'.safe_constantize # => Test::Unit
+ # safe_constantize('Module') # => Module
+ # safe_constantize('Test::Unit') # => Test::Unit
#
# The name is assumed to be the one of a top-level constant, no matter
# whether it starts with "::" or not. No lexical context is taken into
@@ -290,16 +290,16 @@ module ActiveSupport
# C = 'outside'
# module M
# C = 'inside'
- # C # => 'inside'
- # 'C'.safe_constantize # => 'outside', same as ::C
+ # C # => 'inside'
+ # safe_constantize('C') # => 'outside', same as ::C
# end
#
# +nil+ is returned when the name is not in CamelCase or the constant (or
# part of it) is unknown.
#
- # 'blargle'.safe_constantize # => nil
- # 'UnknownModule'.safe_constantize # => nil
- # 'UnknownModule::Foo::Bar'.safe_constantize # => nil
+ # safe_constantize('blargle') # => nil
+ # safe_constantize('UnknownModule') # => nil
+ # safe_constantize('UnknownModule::Foo::Bar') # => nil
def safe_constantize(camel_cased_word)
constantize(camel_cased_word)
rescue NameError => e
diff --git a/activesupport/lib/active_support/inflector/transliterate.rb b/activesupport/lib/active_support/inflector/transliterate.rb
index 1cde417fc5..edea142e82 100644
--- a/activesupport/lib/active_support/inflector/transliterate.rb
+++ b/activesupport/lib/active_support/inflector/transliterate.rb
@@ -67,17 +67,8 @@ module ActiveSupport
# Replaces special characters in a string so that it may be used as part of
# a 'pretty' URL.
#
- # class Person
- # def to_param
- # "#{id}-#{name.parameterize}"
- # end
- # end
- #
- # @person = Person.find(1)
- # # => #<Person id: 1, name: "Donald E. Knuth">
- #
- # <%= link_to(@person.name, person_path(@person)) %>
- # # => <a href="/person/1-donald-e-knuth">Donald E. Knuth</a>
+ # parameterize("Donald E. Knuth") # => "donald-e-knuth"
+ # parameterize("^trés|Jolie-- ") # => "tres-jolie"
def parameterize(string, sep = '-')
# replace accented chars with their ascii equivalents
parameterized_string = transliterate(string)
@@ -92,6 +83,5 @@ module ActiveSupport
end
parameterized_string.downcase
end
-
end
end
diff --git a/activesupport/lib/active_support/testing/isolation.rb b/activesupport/lib/active_support/testing/isolation.rb
index 68bda35980..247df7423b 100644
--- a/activesupport/lib/active_support/testing/isolation.rb
+++ b/activesupport/lib/active_support/testing/isolation.rb
@@ -1,5 +1,3 @@
-require 'rbconfig'
-
module ActiveSupport
module Testing
module Isolation
@@ -12,7 +10,7 @@ module ActiveSupport
end
def self.forking_env?
- !ENV["NO_FORK"] && ((RbConfig::CONFIG['host_os'] !~ /mswin|mingw/) && (RUBY_PLATFORM !~ /java/))
+ !ENV["NO_FORK"] && Process.respond_to?(:fork)
end
@@class_setup_mutex = Mutex.new
diff --git a/activesupport/lib/active_support/values/time_zone.rb b/activesupport/lib/active_support/values/time_zone.rb
index 17629eabb3..728b53849d 100644
--- a/activesupport/lib/active_support/values/time_zone.rb
+++ b/activesupport/lib/active_support/values/time_zone.rb
@@ -111,9 +111,11 @@ module ActiveSupport
"Jerusalem" => "Asia/Jerusalem",
"Harare" => "Africa/Harare",
"Pretoria" => "Africa/Johannesburg",
+ "Kaliningrad" => "Europe/Kaliningrad",
"Moscow" => "Europe/Moscow",
"St. Petersburg" => "Europe/Moscow",
- "Volgograd" => "Europe/Moscow",
+ "Volgograd" => "Europe/Volgograd",
+ "Samara" => "Europe/Samara",
"Kuwait" => "Asia/Kuwait",
"Riyadh" => "Asia/Riyadh",
"Nairobi" => "Africa/Nairobi",
@@ -170,6 +172,7 @@ module ActiveSupport
"Guam" => "Pacific/Guam",
"Port Moresby" => "Pacific/Port_Moresby",
"Magadan" => "Asia/Magadan",
+ "Srednekolymsk" => "Asia/Srednekolymsk",
"Solomon Is." => "Pacific/Guadalcanal",
"New Caledonia" => "Pacific/Noumea",
"Fiji" => "Pacific/Fiji",
diff --git a/activesupport/test/core_ext/duration_test.rb b/activesupport/test/core_ext/duration_test.rb
index 2b893c7cd0..c283b546e6 100644
--- a/activesupport/test/core_ext/duration_test.rb
+++ b/activesupport/test/core_ext/duration_test.rb
@@ -70,6 +70,15 @@ class DurationTest < ActiveSupport::TestCase
assert_equal '14 days', 1.fortnight.inspect
end
+ def test_inspect_locale
+ current_locale = I18n.default_locale
+ I18n.default_locale = :de
+ I18n.backend.store_translations(:de, { support: { array: { last_word_connector: ' und ' } } })
+ assert_equal '10 years, 1 month und 1 day', (10.years + 1.month + 1.day).inspect
+ ensure
+ I18n.default_locale = current_locale
+ end
+
def test_minus_with_duration_does_not_break_subtraction_of_date_from_date
assert_nothing_raised { Date.today - Date.today }
end
diff --git a/activesupport/test/core_ext/hash_ext_test.rb b/activesupport/test/core_ext/hash_ext_test.rb
index 3d2f50ce49..e10bee5e00 100644
--- a/activesupport/test/core_ext/hash_ext_test.rb
+++ b/activesupport/test/core_ext/hash_ext_test.rb
@@ -1549,6 +1549,14 @@ class HashToXmlTest < ActiveSupport::TestCase
assert_not_same hash_wia, hash_wia.with_indifferent_access
end
+
+ def test_allows_setting_frozen_array_values_with_indifferent_access
+ value = [1, 2, 3].freeze
+ hash = HashWithIndifferentAccess.new
+ hash[:key] = value
+ assert_equal hash[:key], value
+ end
+
def test_should_copy_the_default_value_when_converting_to_hash_with_indifferent_access
hash = Hash.new(3)
hash_wia = hash.with_indifferent_access
diff --git a/activesupport/test/core_ext/securerandom.rb b/activesupport/test/core_ext/securerandom.rb
new file mode 100644
index 0000000000..dfacb7fe9f
--- /dev/null
+++ b/activesupport/test/core_ext/securerandom.rb
@@ -0,0 +1,20 @@
+require 'abstract_unit'
+require 'active_support/core_ext/securerandom'
+
+class SecureRandomTest < ActiveSupport::TestCase
+ def test_base58
+ s1 = SecureRandom.base58
+ s2 = SecureRandom.base58
+
+ assert_not_equal s1, s2
+ assert_equal 16, s1.length
+ end
+
+ def test_base58_with_length
+ s1 = SecureRandom.base58(24)
+ s2 = SecureRandom.base58(24)
+
+ assert_not_equal s1, s2
+ assert_equal 24, s1.length
+ end
+end
diff --git a/activesupport/test/hash_with_indifferent_access_test.rb b/activesupport/test/hash_with_indifferent_access_test.rb
index 843994147b..1facd691fa 100644
--- a/activesupport/test/hash_with_indifferent_access_test.rb
+++ b/activesupport/test/hash_with_indifferent_access_test.rb
@@ -7,4 +7,5 @@ class HashWithIndifferentAccessTest < ActiveSupport::TestCase
hash.reverse_merge! key: :new_value
assert_equal :old_value, hash[:key]
end
-end \ No newline at end of file
+
+end
diff --git a/guides/assets/images/getting_started/article_with_comments.png b/guides/assets/images/getting_started/article_with_comments.png
index 117a78a39f..c489e4c00e 100644
--- a/guides/assets/images/getting_started/article_with_comments.png
+++ b/guides/assets/images/getting_started/article_with_comments.png
Binary files differ
diff --git a/guides/rails_guides/markdown/renderer.rb b/guides/rails_guides/markdown/renderer.rb
index 50a791cda5..554d94ad50 100644
--- a/guides/rails_guides/markdown/renderer.rb
+++ b/guides/rails_guides/markdown/renderer.rb
@@ -25,7 +25,7 @@ HTML
def paragraph(text)
if text =~ /^(TIP|IMPORTANT|CAUTION|WARNING|NOTE|INFO|TODO)[.:]/
convert_notes(text)
- elsif text.include?('DO NOT READ THIS FILE IN GITHUB')
+ elsif text.include?('DO NOT READ THIS FILE ON GITHUB')
elsif text =~ /^\[<sup>(\d+)\]:<\/sup> (.+)$/
linkback = %(<a href="#footnote-#{$1}-ref"><sup>#{$1}</sup></a>)
%(<p class="footnote" id="footnote-#{$1}">#{linkback} #{$2}</p>)
diff --git a/guides/source/2_2_release_notes.md b/guides/source/2_2_release_notes.md
index 019da08687..be00087f63 100644
--- a/guides/source/2_2_release_notes.md
+++ b/guides/source/2_2_release_notes.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
Ruby on Rails 2.2 Release Notes
===============================
diff --git a/guides/source/2_3_release_notes.md b/guides/source/2_3_release_notes.md
index 4ac1529e76..328656f4a4 100644
--- a/guides/source/2_3_release_notes.md
+++ b/guides/source/2_3_release_notes.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
Ruby on Rails 2.3 Release Notes
===============================
diff --git a/guides/source/3_0_release_notes.md b/guides/source/3_0_release_notes.md
index 3d7966e50b..9ad32e8168 100644
--- a/guides/source/3_0_release_notes.md
+++ b/guides/source/3_0_release_notes.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
Ruby on Rails 3.0 Release Notes
===============================
diff --git a/guides/source/3_1_release_notes.md b/guides/source/3_1_release_notes.md
index 8728750966..e187e5f9ab 100644
--- a/guides/source/3_1_release_notes.md
+++ b/guides/source/3_1_release_notes.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
Ruby on Rails 3.1 Release Notes
===============================
diff --git a/guides/source/3_2_release_notes.md b/guides/source/3_2_release_notes.md
index 0b28aac9ce..6ddf77d9c0 100644
--- a/guides/source/3_2_release_notes.md
+++ b/guides/source/3_2_release_notes.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
Ruby on Rails 3.2 Release Notes
===============================
diff --git a/guides/source/4_0_release_notes.md b/guides/source/4_0_release_notes.md
index 5f52c33746..67f4a3c02c 100644
--- a/guides/source/4_0_release_notes.md
+++ b/guides/source/4_0_release_notes.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
Ruby on Rails 4.0 Release Notes
===============================
diff --git a/guides/source/4_1_release_notes.md b/guides/source/4_1_release_notes.md
index dbc151c0ca..8d5557be6e 100644
--- a/guides/source/4_1_release_notes.md
+++ b/guides/source/4_1_release_notes.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
Ruby on Rails 4.1 Release Notes
===============================
diff --git a/guides/source/4_2_release_notes.md b/guides/source/4_2_release_notes.md
index faff1add9f..366d9d26b4 100644
--- a/guides/source/4_2_release_notes.md
+++ b/guides/source/4_2_release_notes.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
Ruby on Rails 4.2 Release Notes
===============================
diff --git a/guides/source/action_controller_overview.md b/guides/source/action_controller_overview.md
index 36d1b6de83..80000baf66 100644
--- a/guides/source/action_controller_overview.md
+++ b/guides/source/action_controller_overview.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
Action Controller Overview
==========================
diff --git a/guides/source/action_mailer_basics.md b/guides/source/action_mailer_basics.md
index c586675ee5..e55ff16495 100644
--- a/guides/source/action_mailer_basics.md
+++ b/guides/source/action_mailer_basics.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
Action Mailer Basics
====================
diff --git a/guides/source/action_view_overview.md b/guides/source/action_view_overview.md
index a6bde4f517..665a2b71ff 100644
--- a/guides/source/action_view_overview.md
+++ b/guides/source/action_view_overview.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
Action View Overview
====================
diff --git a/guides/source/active_job_basics.md b/guides/source/active_job_basics.md
index 31c9406d5c..4d1625b28d 100644
--- a/guides/source/active_job_basics.md
+++ b/guides/source/active_job_basics.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
Active Job Basics
=================
@@ -78,7 +78,8 @@ end
Enqueue a job like so:
```ruby
-# Enqueue a job to be performed as soon the queueing system is free.
+# Enqueue a job to be performed as soon the queueing system is
+# free.
MyJob.perform_later record
```
@@ -114,8 +115,9 @@ You can easily set your queueing backend:
# config/application.rb
module YourApp
class Application < Rails::Application
- # Be sure to have the adapter's gem in your Gemfile and follow
- # the adapter's specific installation and deployment instructions.
+ # Be sure to have the adapter's gem in your Gemfile
+ # and follow the adapter's specific installation
+ # and deployment instructions.
config.active_job.queue_adapter = :sidekiq
end
end
@@ -153,8 +155,8 @@ class GuestsCleanupJob < ActiveJob::Base
end
# Now your job will run on queue production_low_priority on your
-# production environment and on staging_low_priority on your staging
-# environment
+# production environment and on staging_low_priority
+# on your staging environment
```
The default queue name prefix delimiter is '\_'. This can be changed by setting
@@ -176,8 +178,8 @@ class GuestsCleanupJob < ActiveJob::Base
end
# Now your job will run on queue production.low_priority on your
-# production environment and on staging.low_priority on your staging
-# environment
+# production environment and on staging.low_priority
+# on your staging environment
```
If you want more control on what queue a job will be run you can pass a `:queue`
@@ -295,7 +297,7 @@ end
```
This works with any class that mixes in `GlobalID::Identification`, which
-by default has been mixed into Active Model classes.
+by default has been mixed into Active Record classes.
Exceptions
diff --git a/guides/source/active_model_basics.md b/guides/source/active_model_basics.md
index 8dee1cc5ec..4b2bfaee2f 100644
--- a/guides/source/active_model_basics.md
+++ b/guides/source/active_model_basics.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
Active Model Basics
===================
@@ -396,7 +396,7 @@ class Person
end
```
-With the `to_xml` you have a XML representing the model.
+With the `to_xml` you have an XML representing the model.
```ruby
person = Person.new
@@ -405,7 +405,7 @@ person.name = "Bob"
person.to_xml # => "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<person>\n <name>Bob</name>\n</person>\n"
```
-From a XML string you define the attributes of the model.
+From an XML string you define the attributes of the model.
You need to have the `attributes=` method defined on your class:
```ruby
diff --git a/guides/source/active_record_basics.md b/guides/source/active_record_basics.md
index 9d2ba328ea..a5196e481e 100644
--- a/guides/source/active_record_basics.md
+++ b/guides/source/active_record_basics.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
Active Record Basics
====================
@@ -62,7 +62,7 @@ Convention over Configuration in Active Record
When writing applications using other programming languages or frameworks, it
may be necessary to write a lot of configuration code. This is particularly true
for ORM frameworks in general. However, if you follow the conventions adopted by
-Rails, you'll need to write very little configuration (in some case no
+Rails, you'll need to write very little configuration (in some cases no
configuration at all) when creating Active Record models. The idea is that if
you configure your applications in the very same way most of the time then this
should be the default way. Thus, explicit configuration would be needed
diff --git a/guides/source/active_record_callbacks.md b/guides/source/active_record_callbacks.md
index 9d3a8c3af6..e65ab802c0 100644
--- a/guides/source/active_record_callbacks.md
+++ b/guides/source/active_record_callbacks.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
Active Record Callbacks
=======================
diff --git a/guides/source/active_record_migrations.md b/guides/source/active_record_migrations.md
index 8ae282bad2..97cabc1728 100644
--- a/guides/source/active_record_migrations.md
+++ b/guides/source/active_record_migrations.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
Active Record Migrations
========================
diff --git a/guides/source/active_record_postgresql.md b/guides/source/active_record_postgresql.md
index fa0f31cbbd..4d9c1776f4 100644
--- a/guides/source/active_record_postgresql.md
+++ b/guides/source/active_record_postgresql.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
Active Record and PostgreSQL
============================
diff --git a/guides/source/active_record_querying.md b/guides/source/active_record_querying.md
index f8c64cbd0c..373a98bb85 100644
--- a/guides/source/active_record_querying.md
+++ b/guides/source/active_record_querying.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
Active Record Query Interface
=============================
@@ -257,6 +257,12 @@ It is equivalent to writing:
Client.where(first_name: 'Lifo').take
```
+The SQL equivalent of the above is:
+
+```sql
+SELECT * FROM clients WHERE (clients.first_name = 'Lifo') LIMIT 1
+```
+
The `find_by!` method behaves exactly like `find_by`, except that it will raise `ActiveRecord::RecordNotFound` if no matching record is found. For example:
```ruby
@@ -1128,7 +1134,7 @@ This would generate a query which contains a `LEFT OUTER JOIN` whereas the
If there was no `where` condition, this would generate the normal set of two queries.
NOTE: Using `where` like this will only work when you pass it a Hash. For
-SQL-fragments you need use `references` to force joined tables:
+SQL-fragments you need to use `references` to force joined tables:
```ruby
Article.includes(:comments).where("comments.visible = true").references(:comments)
@@ -1269,7 +1275,7 @@ User.active.where(state: 'finished')
# SELECT "users".* FROM "users" WHERE "users"."state" = 'active' AND "users"."state" = 'finished'
```
-If we do want the `last where clause` to win then `Relation#merge` can
+If we do want the last `where` clause to win then `Relation#merge` can
be used.
```ruby
diff --git a/guides/source/active_record_validations.md b/guides/source/active_record_validations.md
index 8c832bafff..c9af70934a 100644
--- a/guides/source/active_record_validations.md
+++ b/guides/source/active_record_validations.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
Active Record Validations
=========================
diff --git a/guides/source/active_support_core_extensions.md b/guides/source/active_support_core_extensions.md
index ba839e1723..0fbd6ed7e1 100644
--- a/guides/source/active_support_core_extensions.md
+++ b/guides/source/active_support_core_extensions.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
Active Support Core Extensions
==============================
@@ -467,7 +467,7 @@ C.new(0, 1).instance_variable_names # => ["@x", "@y"]
NOTE: Defined in `active_support/core_ext/object/instance_variables.rb`.
-### Silencing Warnings, Streams, and Exceptions
+### Silencing Warnings and Exceptions
The methods `silence_warnings` and `enable_warnings` change the value of `$VERBOSE` accordingly for the duration of their block, and reset it afterwards:
@@ -475,26 +475,10 @@ The methods `silence_warnings` and `enable_warnings` change the value of `$VERBO
silence_warnings { Object.const_set "RAILS_DEFAULT_LOGGER", logger }
```
-You can silence any stream while a block runs with `silence_stream`:
-
-```ruby
-silence_stream(STDOUT) do
- # STDOUT is silent here
-end
-```
-
-The `quietly` method addresses the common use case where you want to silence STDOUT and STDERR, even in subprocesses:
-
-```ruby
-quietly { system 'bundle install' }
-```
-
-For example, the railties test suite uses that one in a few places to prevent command messages from being echoed intermixed with the progress status.
-
Silencing exceptions is also possible with `suppress`. This method receives an arbitrary number of exception classes. If an exception is raised during the execution of the block and is `kind_of?` any of the arguments, `suppress` captures it and returns silently. Otherwise the exception is reraised:
```ruby
-# If the user is locked the increment is lost, no big deal.
+# If the user is locked, the increment is lost, no big deal.
suppress(ActiveRecord::StaleObjectError) do
current_user.increment! :visits
end
@@ -3813,50 +3797,6 @@ WARNING. If the argument is an `IO` it needs to respond to `rewind` to be able t
NOTE: Defined in `active_support/core_ext/marshal.rb`.
-Extensions to `Logger`
-----------------------
-
-### `around_[level]`
-
-Takes two arguments, a `before_message` and `after_message` and calls the current level method on the `Logger` instance, passing in the `before_message`, then the specified message, then the `after_message`:
-
-```ruby
-logger = Logger.new("log/development.log")
-logger.around_info("before", "after") { |logger| logger.info("during") }
-```
-
-### `silence`
-
-Silences every log level lesser to the specified one for the duration of the given block. Log level orders are: debug, info, error and fatal.
-
-```ruby
-logger = Logger.new("log/development.log")
-logger.silence(Logger::INFO) do
- logger.debug("In space, no one can hear you scream.")
- logger.info("Scream all you want, small mailman!")
-end
-```
-
-### `datetime_format=`
-
-Modifies the datetime format output by the formatter class associated with this logger. If the formatter class does not have a `datetime_format` method then this is ignored.
-
-```ruby
-class Logger::FormatWithTime < Logger::Formatter
- cattr_accessor(:datetime_format) { "%Y%m%d%H%m%S" }
-
- def self.call(severity, timestamp, progname, msg)
- "#{timestamp.strftime(datetime_format)} -- #{String === msg ? msg : msg.inspect}\n"
- end
-end
-
-logger = Logger.new("log/development.log")
-logger.formatter = Logger::FormatWithTime
-logger.info("<- is the current time")
-```
-
-NOTE: Defined in `active_support/core_ext/logger.rb`.
-
Extensions to `NameError`
-------------------------
diff --git a/guides/source/active_support_instrumentation.md b/guides/source/active_support_instrumentation.md
index 0aa74e387d..9d9f40e956 100644
--- a/guides/source/active_support_instrumentation.md
+++ b/guides/source/active_support_instrumentation.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
Active Support Instrumentation
==============================
diff --git a/guides/source/api_documentation_guidelines.md b/guides/source/api_documentation_guidelines.md
index d481700709..b385bdbe83 100644
--- a/guides/source/api_documentation_guidelines.md
+++ b/guides/source/api_documentation_guidelines.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
API Documentation Guidelines
============================
diff --git a/guides/source/asset_pipeline.md b/guides/source/asset_pipeline.md
index 64d1c31083..52ea605a72 100644
--- a/guides/source/asset_pipeline.md
+++ b/guides/source/asset_pipeline.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
The Asset Pipeline
==================
@@ -149,7 +149,7 @@ clients to fetch them again, even when the content of those assets has not chang
Fingerprinting fixes these problems by avoiding query strings, and by ensuring
that filenames are consistent based on their content.
-Fingerprinting is enabled by default for production and disabled for all other
+Fingerprinting is enabled by default for both the development and production
environments. You can enable or disable it in your configuration through the
`config.assets.digest` option.
diff --git a/guides/source/association_basics.md b/guides/source/association_basics.md
index 95c7e747ef..dab885a8fb 100644
--- a/guides/source/association_basics.md
+++ b/guides/source/association_basics.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
Active Record Associations
==========================
diff --git a/guides/source/autoloading_and_reloading_constants.md b/guides/source/autoloading_and_reloading_constants.md
index 489ea681e2..c4fac1cff5 100644
--- a/guides/source/autoloading_and_reloading_constants.md
+++ b/guides/source/autoloading_and_reloading_constants.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
Autoloading and Reloading Constants
===================================
@@ -80,7 +80,8 @@ end
```
The *nesting* at any given place is the collection of enclosing nested class and
-module objects outwards. For example, in the previous example, the nesting at
+module objects outwards. The nesting at any given place can be inspected with
+`Module.nesting`. For example, in the previous example, the nesting at
(1) is
```ruby
@@ -153,8 +154,6 @@ the blocks that may be passed to `Class.new` and `Module.new` do not get the
class or module being defined pushed to their nesting. That's one of the
differences between defining classes and modules in one way or another.
-The nesting at any given place can be inspected with `Module.nesting`.
-
### Class and Module Definitions are Constant Assignments
Let's suppose the following snippet creates a class (rather than reopening it):
@@ -236,7 +235,7 @@ end
```
`Post` is not syntax for a class. Rather, `Post` is a regular Ruby constant. If
-all is good, the constant evaluates to an object that responds to `all`.
+all is good, the constant is evaluated to an object that responds to `all`.
That is why we talk about *constant* autoloading, Rails has the ability to
load constants on the fly.
@@ -330,7 +329,7 @@ checked.
Rails autoloading **does not emulate this algorithm**, but its starting point is
the name of the constant to be autoloaded, and the parent. See more in
-[Qualified References](#qualified-references).
+[Qualified References](#autoloading-algorithms-qualified-references).
Vocabulary
@@ -685,12 +684,12 @@ creates an empty module and assigns it to the `Admin` constant on the fly.
### Generic Procedure
Relative references are reported to be missing in the cref where they were hit,
-and qualified references are reported to be missing in their parent. (See
+and qualified references are reported to be missing in their parent (see
[Resolution Algorithm for Relative
Constants](#resolution-algorithm-for-relative-constants) at the beginning of
this guide for the definition of *cref*, and [Resolution Algorithm for Qualified
Constants](#resolution-algorithm-for-qualified-constants) for the definition of
-*parent*.)
+*parent*).
The procedure to autoload constant `C` in an arbitrary situation is as follows:
@@ -868,8 +867,8 @@ end
```
To resolve `User` Ruby checks `Admin` in the former case, but it does not in
-the latter because it does not belong to the nesting. (See [Nesting](#nesting)
-and [Resolution Algorithms](#resolution-algorithms).)
+the latter because it does not belong to the nesting (see [Nesting](#nesting)
+and [Resolution Algorithms](#resolution-algorithms)).
Unfortunately Rails autoloading does not know the nesting in the spot where the
constant was missing and so it is not able to act as Ruby would. In particular,
@@ -1284,7 +1283,7 @@ c.user # NameError: uninitialized constant C::User
```
because it detects that a parent namespace already has the constant (see [Qualified
-References](#autoloading-algorithms-qualified-references).)
+References](#autoloading-algorithms-qualified-references)).
As with pure Ruby, within the body of a direct descendant of `BasicObject` use
always absolute constant paths:
diff --git a/guides/source/caching_with_rails.md b/guides/source/caching_with_rails.md
index 61b991df61..0fa20f7ccf 100644
--- a/guides/source/caching_with_rails.md
+++ b/guides/source/caching_with_rails.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
Caching with Rails: An overview
===============================
@@ -184,6 +184,10 @@ class ProductsController < ApplicationController
end
```
+The second time the same query is run against the database, it's not actually going to hit the database. The first time the result is returned from the query it is stored in the query cache (in memory) and the second time it's pulled from memory.
+
+However, it's important to note that query caches are created at the start of an action and destroyed at the end of that action and thus persist only for the duration of the action. If you'd like to store query results in a more persistent fashion, you can in Rails by using low level caching.
+
Cache Stores
------------
diff --git a/guides/source/command_line.md b/guides/source/command_line.md
index 7567a38aef..78f26ccefa 100644
--- a/guides/source/command_line.md
+++ b/guides/source/command_line.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
The Rails Command Line
======================
diff --git a/guides/source/configuring.md b/guides/source/configuring.md
index 622f79a50f..9c0f2ddc8a 100644
--- a/guides/source/configuring.md
+++ b/guides/source/configuring.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
Configuring Rails Applications
==============================
@@ -692,7 +692,7 @@ development:
pool: 5
```
-Prepared Statements are enabled by default on PostgreSQL. You can be disable prepared statements by setting `prepared_statements` to `false`:
+Prepared Statements are enabled by default on PostgreSQL. You can disable prepared statements by setting `prepared_statements` to `false`:
```yaml
production:
diff --git a/guides/source/contributing_to_ruby_on_rails.md b/guides/source/contributing_to_ruby_on_rails.md
index db3f19f8ac..7381521658 100644
--- a/guides/source/contributing_to_ruby_on_rails.md
+++ b/guides/source/contributing_to_ruby_on_rails.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
Contributing to Ruby on Rails
=============================
@@ -26,7 +26,7 @@ NOTE: Bugs in the most recent released version of Ruby on Rails are likely to ge
### Creating a Bug Report
-If you've found a problem in Ruby on Rails which is not a security risk, do a search in GitHub under [Issues](https://github.com/rails/rails/issues) in case it has already been reported. If you are unable to find any open GitHub issues addressing the problem you found, your next step will be to [open a new one](https://github.com/rails/rails/issues/new). (See the next section for reporting security issues.)
+If you've found a problem in Ruby on Rails which is not a security risk, do a search on GitHub under [Issues](https://github.com/rails/rails/issues) in case it has already been reported. If you are unable to find any open GitHub issues addressing the problem you found, your next step will be to [open a new one](https://github.com/rails/rails/issues/new). (See the next section for reporting security issues.)
Your issue report should contain a title and a clear description of the issue at the bare minimum. You should include as much relevant information as possible and should at least post a code sample that demonstrates the issue. It would be even better if you could include a unit test that shows how the expected behavior is not occurring. Your goal should be to make it easy for yourself - and others - to replicate the bug and figure out a fix.
diff --git a/guides/source/debugging_rails_applications.md b/guides/source/debugging_rails_applications.md
index cef9ac083b..a788dd48ad 100644
--- a/guides/source/debugging_rails_applications.md
+++ b/guides/source/debugging_rails_applications.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
Debugging Rails Applications
============================
diff --git a/guides/source/development_dependencies_install.md b/guides/source/development_dependencies_install.md
index 9eacc3a2fe..989b29956c 100644
--- a/guides/source/development_dependencies_install.md
+++ b/guides/source/development_dependencies_install.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
Development Dependencies Install
================================
diff --git a/guides/source/engines.md b/guides/source/engines.md
index 731178787f..6eb558885f 100644
--- a/guides/source/engines.md
+++ b/guides/source/engines.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
Getting Started with Engines
============================
diff --git a/guides/source/form_helpers.md b/guides/source/form_helpers.md
index a8dcd3ee4f..4c02c99cc7 100644
--- a/guides/source/form_helpers.md
+++ b/guides/source/form_helpers.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
Form Helpers
============
@@ -275,7 +275,7 @@ There are a few things to note here:
The resulting HTML is:
```html
-<form accept-charset="UTF-8" action="/articles/create" method="post" class="nifty_form">
+<form accept-charset="UTF-8" action="/articles" method="post" class="nifty_form">
<input id="article_title" name="article[title]" type="text" />
<textarea id="article_body" name="article[body]" cols="60" rows="12"></textarea>
<input name="commit" type="submit" value="Create" />
@@ -300,7 +300,7 @@ You can create a similar binding without actually creating `<form>` tags with th
which produces the following output:
```html
-<form accept-charset="UTF-8" action="/people/create" class="new_person" id="new_person" method="post">
+<form accept-charset="UTF-8" action="/people" class="new_person" id="new_person" method="post">
<input id="person_name" name="person[name]" type="text" />
<input id="contact_detail_phone_number" name="contact_detail[phone_number]" type="text" />
</form>
diff --git a/guides/source/generators.md b/guides/source/generators.md
index 05bf07b4c8..14f451cbc9 100644
--- a/guides/source/generators.md
+++ b/guides/source/generators.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
Creating and Customizing Rails Generators & Templates
=====================================================
diff --git a/guides/source/getting_started.md b/guides/source/getting_started.md
index d80622ef00..5945d48e98 100644
--- a/guides/source/getting_started.md
+++ b/guides/source/getting_started.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
Getting Started with Rails
==========================
@@ -1271,8 +1271,8 @@ bottom of the template:
```html+erb
...
-<%= link_to 'Back', articles_path %> |
-<%= link_to 'Edit', edit_article_path(@article) %>
+<%= link_to 'Edit', edit_article_path(@article) %> |
+<%= link_to 'Back', articles_path %>
```
And here's how our app looks so far:
@@ -1485,6 +1485,9 @@ Without this file, the confirmation dialog box wouldn't appear.
![Confirm Dialog](images/getting_started/confirm_dialog.png)
+TIP: Learn more about jQuery Unobtrusive Adapter (jQuery UJS) on
+[Working With Javascript in Rails](working_with_javascript_in_rails.html) guide.
+
Congratulations, you can now create, show, list, update and destroy
articles.
@@ -1681,8 +1684,8 @@ So first, we'll wire up the Article show template
</p>
<% end %>
-<%= link_to 'Back', articles_path %> |
-<%= link_to 'Edit', edit_article_path(@article) %>
+<%= link_to 'Edit', edit_article_path(@article) %> |
+<%= link_to 'Back', articles_path %>
```
This adds a form on the `Article` show page that creates a new comment by
@@ -1762,8 +1765,8 @@ add that to the `app/views/articles/show.html.erb`.
</p>
<% end %>
-<%= link_to 'Edit Article', edit_article_path(@article) %> |
-<%= link_to 'Back to Articles', articles_path %>
+<%= link_to 'Edit', edit_article_path(@article) %> |
+<%= link_to 'Back', articles_path %>
```
Now you can add articles and comments to your blog and have them show up in the
@@ -1828,8 +1831,8 @@ following:
</p>
<% end %>
-<%= link_to 'Edit Article', edit_article_path(@article) %> |
-<%= link_to 'Back to Articles', articles_path %>
+<%= link_to 'Edit', edit_article_path(@article) %> |
+<%= link_to 'Back', articles_path %>
```
This will now render the partial in `app/views/comments/_comment.html.erb` once
@@ -1878,8 +1881,8 @@ Then you make the `app/views/articles/show.html.erb` look like the following:
<h2>Add a comment:</h2>
<%= render 'comments/form' %>
-<%= link_to 'Edit Article', edit_article_path(@article) %> |
-<%= link_to 'Back to Articles', articles_path %>
+<%= link_to 'Edit', edit_article_path(@article) %> |
+<%= link_to 'Back', articles_path %>
```
The second render just defines the partial template we want to render,
diff --git a/guides/source/i18n.md b/guides/source/i18n.md
index bd6babff41..fd2051e675 100644
--- a/guides/source/i18n.md
+++ b/guides/source/i18n.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
Rails Internationalization (I18n) API
=====================================
@@ -530,7 +530,7 @@ Thus the following calls are equivalent:
```ruby
I18n.t 'activerecord.errors.messages.record_invalid'
-I18n.t 'errors.messages.record_invalid', scope: :active_record
+I18n.t 'errors.messages.record_invalid', scope: :activerecord
I18n.t :record_invalid, scope: 'activerecord.errors.messages'
I18n.t :record_invalid, scope: [:activerecord, :errors, :messages]
```
diff --git a/guides/source/initialization.md b/guides/source/initialization.md
index a93ceb7fb5..8fbb234698 100644
--- a/guides/source/initialization.md
+++ b/guides/source/initialization.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
The Rails Initialization Process
================================
diff --git a/guides/source/layouts_and_rendering.md b/guides/source/layouts_and_rendering.md
index c3cde49630..eb3c188d38 100644
--- a/guides/source/layouts_and_rendering.md
+++ b/guides/source/layouts_and_rendering.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
Layouts and Rendering in Rails
==============================
@@ -255,7 +255,7 @@ extension for the layout file.
#### Rendering HTML
-You can send a HTML string back to the browser by using the `:html` option to
+You can send an HTML string back to the browser by using the `:html` option to
`render`:
```ruby
diff --git a/guides/source/maintenance_policy.md b/guides/source/maintenance_policy.md
index 45cdc549f7..50308f505a 100644
--- a/guides/source/maintenance_policy.md
+++ b/guides/source/maintenance_policy.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
Maintenance Policy for Ruby on Rails
====================================
diff --git a/guides/source/nested_model_forms.md b/guides/source/nested_model_forms.md
index 44236ad239..1937369776 100644
--- a/guides/source/nested_model_forms.md
+++ b/guides/source/nested_model_forms.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
Rails Nested Model Forms
========================
diff --git a/guides/source/plugins.md b/guides/source/plugins.md
index bd884441ac..10738320ef 100644
--- a/guides/source/plugins.md
+++ b/guides/source/plugins.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
The Basics of Creating Rails Plugins
====================================
diff --git a/guides/source/profiling.md b/guides/source/profiling.md
index 695b09647f..ce093f78ba 100644
--- a/guides/source/profiling.md
+++ b/guides/source/profiling.md
@@ -1,4 +1,4 @@
-*DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.**
+*DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
A Guide to Profiling Rails Applications
=======================================
diff --git a/guides/source/rails_application_templates.md b/guides/source/rails_application_templates.md
index 0db777b9bb..b3e1874048 100644
--- a/guides/source/rails_application_templates.md
+++ b/guides/source/rails_application_templates.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
Rails Application Templates
===========================
diff --git a/guides/source/rails_on_rack.md b/guides/source/rails_on_rack.md
index 561a3d9392..21be74beba 100644
--- a/guides/source/rails_on_rack.md
+++ b/guides/source/rails_on_rack.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
Rails on Rack
=============
diff --git a/guides/source/routing.md b/guides/source/routing.md
index 893cedeefc..a689e131ff 100644
--- a/guides/source/routing.md
+++ b/guides/source/routing.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
Rails Routing from the Outside In
=================================
diff --git a/guides/source/ruby_on_rails_guides_guidelines.md b/guides/source/ruby_on_rails_guides_guidelines.md
index 0ecf8a80df..1323742488 100644
--- a/guides/source/ruby_on_rails_guides_guidelines.md
+++ b/guides/source/ruby_on_rails_guides_guidelines.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
Ruby on Rails Guides Guidelines
===============================
diff --git a/guides/source/security.md b/guides/source/security.md
index e4cc79df55..e486edde31 100644
--- a/guides/source/security.md
+++ b/guides/source/security.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
Ruby on Rails Security Guide
============================
@@ -1029,7 +1029,6 @@ Additional Resources
The security landscape shifts and it is important to keep up to date, because missing a new vulnerability can be catastrophic. You can find additional resources about (Rails) security here:
-* The Ruby on Rails security project posts security news regularly: [http://www.rorsecurity.info](http://www.rorsecurity.info)
* Subscribe to the Rails security [mailing list](http://groups.google.com/group/rubyonrails-security)
* [Keep up to date on the other application layers](http://secunia.com/) (they have a weekly newsletter, too)
* A [good security blog](http://ha.ckers.org/blog/) including the [Cross-Site scripting Cheat Sheet](http://ha.ckers.org/xss.html)
diff --git a/guides/source/testing.md b/guides/source/testing.md
index 21b0b37efa..94cfcf12b7 100644
--- a/guides/source/testing.md
+++ b/guides/source/testing.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
A Guide to Testing Rails Applications
=====================================
@@ -528,6 +528,19 @@ All of request types have equivalent methods that you can use. In a typical C.R.
NOTE: Functional tests do not verify whether the specified request type is accepted by the action, we're more concerned with the result. Request tests exist for this use case to make your tests more purposeful.
+### Testing XHR (AJAX) requests
+
+`xhr` accepts method (listed in the section above), action name and parameters:
+
+```ruby
+test "ajax request responds with no layout" do
+ xhr :get, :show, id: articles(:first).id
+
+ assert_template :index
+ assert_template layout: nil
+end
+```
+
### The Four Hashes of the Apocalypse
After a request has been made and processed, you will have 4 Hash objects ready for use:
@@ -740,10 +753,8 @@ class ArticlesControllerTest < ActionController::TestCase
# called after every single test
def teardown
- # as we are re-initializing @article before every test
- # setting it to nil here is not essential but I hope
- # you understand how you can use the teardown method
- @article = nil
+ # when controller is using cache it may be a good idea to reset it afterwards
+ Rails.cache.clear
end
test "should show article" do
@@ -769,6 +780,41 @@ end
Similar to other callbacks in Rails, the `setup` and `teardown` methods can also be used by passing a block, lambda, or method name as a symbol to call.
+### Test helpers
+
+To avoid code duplication, you can add your own test helpers.
+Sign in helper can be a good example:
+
+```ruby
+test/test_helper.rb
+
+module SignInHelper
+ def sign_in(user)
+ session[:user_id] = user.id
+ end
+end
+
+class ActionController::TestCase
+ include SignInHelper
+end
+```
+
+```ruby
+require 'test_helper'
+
+class ProfileControllerTest < ActionController::TestCase
+
+ test "should show profile" do
+ # helper is now reusable from any controller test case
+ sign_in users(:david)
+
+ get :show
+ assert_response :success
+ assert_equal users(:david), assigns(:user)
+ end
+end
+```
+
Testing Routes
--------------
@@ -1050,9 +1096,10 @@ require 'test_helper'
class UserMailerTest < ActionMailer::TestCase
test "invite" do
# Send the email, then test that it got queued
- email = UserMailer.create_invite('me@example.com',
- 'friend@example.com', Time.now).deliver_now
- assert_not ActionMailer::Base.deliveries.empty?
+ assert_emails 1 do
+ email = UserMailer.create_invite('me@example.com',
+ 'friend@example.com', Time.now).deliver_now
+ end
# Test the body of the sent email contains what we expect it to
assert_equal ['me@example.com'], email.from
diff --git a/guides/source/upgrading_ruby_on_rails.md b/guides/source/upgrading_ruby_on_rails.md
index c0c94475e0..d7fbe575c2 100644
--- a/guides/source/upgrading_ruby_on_rails.md
+++ b/guides/source/upgrading_ruby_on_rails.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
A Guide for Upgrading Ruby on Rails
===================================
diff --git a/guides/source/working_with_javascript_in_rails.md b/guides/source/working_with_javascript_in_rails.md
index 5131f809d7..367abddcfa 100644
--- a/guides/source/working_with_javascript_in_rails.md
+++ b/guides/source/working_with_javascript_in_rails.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE IN GITHUB, GUIDES ARE PUBLISHED IN http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
Working with JavaScript in Rails
================================
diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md
index 623f44d56a..30b86db8bb 100644
--- a/railties/CHANGELOG.md
+++ b/railties/CHANGELOG.md
@@ -1,3 +1,18 @@
+* Add the `method_source` gem to the default Gemfile for apps
+
+ *Sean Griffin*
+
+* Drop old test locations from `rake stats`
+ - test/functional
+ - test/unit
+
+ *Ravil Bayramgalin*
+
+* Update `rake stats` to correctly count declarative tests
+ as methods in `_test.rb` files.
+
+ *Ravil Bayramgalin*
+
* Remove deprecated `test:all` and `test:all:db` tasks.
*Rafael Mendonça França*
diff --git a/railties/lib/rails/code_statistics.rb b/railties/lib/rails/code_statistics.rb
index 27779857b7..0bdf63943f 100644
--- a/railties/lib/rails/code_statistics.rb
+++ b/railties/lib/rails/code_statistics.rb
@@ -7,9 +7,7 @@ class CodeStatistics #:nodoc:
'Model tests',
'Mailer tests',
'Job tests',
- 'Integration tests',
- 'Functional tests (old)',
- 'Unit tests (old)']
+ 'Integration tests']
def initialize(*pairs)
@pairs = pairs
diff --git a/railties/lib/rails/code_statistics_calculator.rb b/railties/lib/rails/code_statistics_calculator.rb
index 60e4aef9b7..a142236dbe 100644
--- a/railties/lib/rails/code_statistics_calculator.rb
+++ b/railties/lib/rails/code_statistics_calculator.rb
@@ -24,6 +24,8 @@ class CodeStatisticsCalculator #:nodoc:
}
}
+ PATTERNS[:minitest] = PATTERNS[:rb].merge method: /^\s*(def|test)\s+['"_a-z]/
+
def initialize(lines = 0, code_lines = 0, classes = 0, methods = 0)
@lines = lines
@code_lines = code_lines
@@ -74,6 +76,10 @@ class CodeStatisticsCalculator #:nodoc:
private
def file_type(file_path)
- File.extname(file_path).sub(/\A\./, '').downcase.to_sym
+ if file_path.end_with? '_test.rb'
+ :minitest
+ else
+ File.extname(file_path).sub(/\A\./, '').downcase.to_sym
+ end
end
end
diff --git a/railties/lib/rails/commands/dbconsole.rb b/railties/lib/rails/commands/dbconsole.rb
index 3f313c1d8c..5175e31f14 100644
--- a/railties/lib/rails/commands/dbconsole.rb
+++ b/railties/lib/rails/commands/dbconsole.rb
@@ -1,7 +1,6 @@
require 'erb'
require 'yaml'
require 'optparse'
-require 'rbconfig'
module Rails
class DBConsole
diff --git a/railties/lib/rails/commands/runner.rb b/railties/lib/rails/commands/runner.rb
index 3a71f8d3f8..86bce9b2fe 100644
--- a/railties/lib/rails/commands/runner.rb
+++ b/railties/lib/rails/commands/runner.rb
@@ -1,5 +1,4 @@
require 'optparse'
-require 'rbconfig'
options = { environment: (ENV['RAILS_ENV'] || ENV['RACK_ENV'] || "development").dup }
code_or_file = nil
diff --git a/railties/lib/rails/generators/actions.rb b/railties/lib/rails/generators/actions.rb
index 5373121835..e35015f630 100644
--- a/railties/lib/rails/generators/actions.rb
+++ b/railties/lib/rails/generators/actions.rb
@@ -1,5 +1,4 @@
require 'open-uri'
-require 'rbconfig'
module Rails
module Generators
diff --git a/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb b/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb
index 5e194783ff..c3b8ef1181 100644
--- a/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb
+++ b/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb
@@ -1,6 +1,6 @@
<p id="notice"><%%= notice %></p>
-<h1>Listing <%= plural_table_name.titleize %></h1>
+<h1><%= plural_table_name.titleize %></h1>
<table>
<thead>
diff --git a/railties/lib/rails/generators/generated_attribute.rb b/railties/lib/rails/generators/generated_attribute.rb
index 77a3dbf9a2..8145a26e22 100644
--- a/railties/lib/rails/generators/generated_attribute.rb
+++ b/railties/lib/rails/generators/generated_attribute.rb
@@ -142,7 +142,11 @@ module Rails
end
def password_digest?
- name == 'password' && type == :digest
+ name == 'password' && type == :digest
+ end
+
+ def token?
+ type == :token
end
def inject_options
diff --git a/railties/lib/rails/generators/rails/app/templates/Gemfile b/railties/lib/rails/generators/rails/app/templates/Gemfile
index 3659edcfcd..143673f711 100644
--- a/railties/lib/rails/generators/rails/app/templates/Gemfile
+++ b/railties/lib/rails/generators/rails/app/templates/Gemfile
@@ -36,6 +36,10 @@ group :development, :test do
# Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
gem 'spring'
<% end -%>
+
+ # Adds `Method#source` and `Method#comment` to get the source code of a
+ # method from the console
+ gem 'method_source'
<% end -%>
end
diff --git a/railties/lib/rails/generators/rails/app/templates/config/application.rb b/railties/lib/rails/generators/rails/app/templates/config/application.rb
index c59ffb3491..20ee229e97 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/application.rb
+++ b/railties/lib/rails/generators/rails/app/templates/config/application.rb
@@ -3,6 +3,7 @@ require File.expand_path('../boot', __FILE__)
<% if include_all_railties? -%>
require 'rails/all'
<% else -%>
+require "rails"
# Pick the frameworks you want:
require "active_model/railtie"
require "active_job/railtie"
diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt
index 99d7bfb3c9..75666d20c5 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt
@@ -14,12 +14,6 @@ Rails.application.configure do
config.consider_all_requests_local = false
config.action_controller.perform_caching = true
- # Enable Rack::Cache to put a simple HTTP cache in front of your application
- # Add `rack-cache` to your Gemfile before enabling this.
- # For large-scale production use, consider using a caching reverse proxy like
- # NGINX, varnish or squid.
- # config.action_dispatch.rack_cache = true
-
# Disable serving static files from the `/public` folder by default since
# Apache or NGINX already handles this.
config.serve_static_files = ENV['RAILS_SERVE_STATIC_FILES'].present?
@@ -39,6 +33,9 @@ Rails.application.configure do
# `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb
<%- end -%>
+ # Enable serving of images, stylesheets, and JavaScripts from an asset server.
+ # config.action_controller.asset_host = 'http://assets.example.com'
+
# Specifies the header that your server uses for sending files.
# config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache
# config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX
@@ -59,8 +56,9 @@ Rails.application.configure do
# Use a different cache store in production.
# config.cache_store = :mem_cache_store
- # Enable serving of images, stylesheets, and JavaScripts from an asset server.
- # config.action_controller.asset_host = 'http://assets.example.com'
+ # Use a real queuing backend for Active Job (and separate queues per environment)
+ # config.active_job.queue_adapter = :resque
+ # config.active_job.queue_name_prefix = "<%= app_name %>_#{Rails.env}"
<%- unless options.skip_action_mailer? -%>
# Ignore bad email addresses and do not raise email delivery errors.
diff --git a/railties/lib/rails/generators/rails/migration/migration_generator.rb b/railties/lib/rails/generators/rails/migration/migration_generator.rb
index 965c42db36..fca2a8fef4 100644
--- a/railties/lib/rails/generators/rails/migration/migration_generator.rb
+++ b/railties/lib/rails/generators/rails/migration/migration_generator.rb
@@ -2,7 +2,7 @@ module Rails
module Generators
class MigrationGenerator < NamedBase # :nodoc:
argument :attributes, type: :array, default: [], banner: "field[:type][:index] field[:type][:index]"
- hook_for :orm, required: true
+ hook_for :orm, required: true, desc: "ORM to be invoked"
end
end
end
diff --git a/railties/lib/rails/generators/rails/model/USAGE b/railties/lib/rails/generators/rails/model/USAGE
index 8c3b63c3b4..b8f7845896 100644
--- a/railties/lib/rails/generators/rails/model/USAGE
+++ b/railties/lib/rails/generators/rails/model/USAGE
@@ -79,10 +79,15 @@ Available field types:
`rails generate model product supplier:references{polymorphic}:index`
If you require a `password_digest` string column for use with
- has_secure_password, you should specify `password:digest`:
+ has_secure_password, you can specify `password:digest`:
`rails generate model user password:digest`
+ If you require a `token` string column for use with
+ has_secure_token, you can specify `auth_token:token`:
+
+ `rails generate model user auth_token:token`
+
Examples:
`rails generate model account`
diff --git a/railties/lib/rails/generators/rails/model/model_generator.rb b/railties/lib/rails/generators/rails/model/model_generator.rb
index 87bab129bb..ec78fd855d 100644
--- a/railties/lib/rails/generators/rails/model/model_generator.rb
+++ b/railties/lib/rails/generators/rails/model/model_generator.rb
@@ -6,7 +6,7 @@ module Rails
include Rails::Generators::ModelHelpers
argument :attributes, type: :array, default: [], banner: "field[:type][:index] field[:type][:index]"
- hook_for :orm, required: true
+ hook_for :orm, required: true, desc: "ORM to be invoked"
end
end
end
diff --git a/railties/lib/rails/railtie.rb b/railties/lib/rails/railtie.rb
index 2b33beaa2b..8c24d1d56d 100644
--- a/railties/lib/rails/railtie.rb
+++ b/railties/lib/rails/railtie.rb
@@ -93,7 +93,7 @@ module Rails
# end
# end
#
- # By default, Rails load generators from your load path. However, if you want to place
+ # By default, Rails loads generators from your load path. However, if you want to place
# your generators at a different location, you can specify in your Railtie a block which
# will load them during normal generators lookup:
#
diff --git a/railties/lib/rails/tasks/statistics.rake b/railties/lib/rails/tasks/statistics.rake
index ba6168e208..735c36eb3a 100644
--- a/railties/lib/rails/tasks/statistics.rake
+++ b/railties/lib/rails/tasks/statistics.rake
@@ -14,10 +14,8 @@ STATS_DIRECTORIES = [
%w(Helper\ tests test/helpers),
%w(Model\ tests test/models),
%w(Mailer\ tests test/mailers),
- %w(Job\ tests test/jobs),
+ %w(Job\ tests test/jobs),
%w(Integration\ tests test/integration),
- %w(Functional\ tests\ (old) test/functional),
- %w(Unit\ tests \ (old) test/unit)
].collect do |name, dir|
[ name, "#{File.dirname(Rake.application.rakefile_location)}/#{dir}" ]
end.select { |name, dir| File.directory?(dir) }
diff --git a/railties/test/code_statistics_calculator_test.rb b/railties/test/code_statistics_calculator_test.rb
index b3eabf5024..46445a001a 100644
--- a/railties/test/code_statistics_calculator_test.rb
+++ b/railties/test/code_statistics_calculator_test.rb
@@ -6,6 +6,43 @@ class CodeStatisticsCalculatorTest < ActiveSupport::TestCase
@code_statistics_calculator = CodeStatisticsCalculator.new
end
+ test 'calculate statistics using #add_by_file_path' do
+ code = <<-RUBY
+ def foo
+ puts 'foo'
+ # bar
+ end
+ RUBY
+
+ temp_file 'stats.rb', code do |path|
+ @code_statistics_calculator.add_by_file_path path
+
+ assert_equal 4, @code_statistics_calculator.lines
+ assert_equal 3, @code_statistics_calculator.code_lines
+ assert_equal 0, @code_statistics_calculator.classes
+ assert_equal 1, @code_statistics_calculator.methods
+ end
+ end
+
+ test 'count number of methods in MiniTest file' do
+ code = <<-RUBY
+ class FooTest < ActionController::TestCase
+ test 'expectation' do
+ assert true
+ end
+
+ def test_expectation
+ assert true
+ end
+ end
+ RUBY
+
+ temp_file 'foo_test.rb', code do |path|
+ @code_statistics_calculator.add_by_file_path path
+ assert_equal 2, @code_statistics_calculator.methods
+ end
+ end
+
test 'add statistics to another using #add' do
code_statistics_calculator_1 = CodeStatisticsCalculator.new(1, 2, 3, 4)
@code_statistics_calculator.add(code_statistics_calculator_1)
@@ -45,30 +82,6 @@ class CodeStatisticsCalculatorTest < ActiveSupport::TestCase
assert_equal 6, @code_statistics_calculator.methods
end
- test 'calculate statistics using #add_by_file_path' do
- tmp_path = File.expand_path(File.join(File.dirname(__FILE__), 'fixtures', 'tmp'))
- FileUtils.mkdir_p(tmp_path)
-
- code = <<-'CODE'
- def foo
- puts 'foo'
- # bar
- end
- CODE
-
- file_path = "#{tmp_path}/stats.rb"
- File.open(file_path, 'w') { |f| f.write(code) }
-
- @code_statistics_calculator.add_by_file_path(file_path)
-
- assert_equal 4, @code_statistics_calculator.lines
- assert_equal 3, @code_statistics_calculator.code_lines
- assert_equal 0, @code_statistics_calculator.classes
- assert_equal 1, @code_statistics_calculator.methods
-
- FileUtils.rm_rf(tmp_path)
- end
-
test 'calculate number of Ruby methods' do
code = <<-'CODE'
def foo
@@ -285,4 +298,17 @@ class Animal
assert_equal 0, @code_statistics_calculator.classes
assert_equal 0, @code_statistics_calculator.methods
end
+
+ private
+ def temp_file(name, content)
+ dir = File.expand_path '../fixtures/tmp', __FILE__
+ path = "#{dir}/#{name}"
+
+ FileUtils.mkdir_p dir
+ File.write path, content
+
+ yield path
+ ensure
+ FileUtils.rm_rf path
+ end
end
diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb
index 40fd7b77f1..5d270555ed 100644
--- a/railties/test/generators/app_generator_test.rb
+++ b/railties/test/generators/app_generator_test.rb
@@ -409,6 +409,13 @@ class AppGeneratorTest < Rails::Generators::TestCase
end
end
+ def test_inclusion_of_method_source
+ run_generator
+ assert_file "Gemfile" do |content|
+ assert_gem 'method_source'
+ end
+ end
+
def test_inclusion_of_doc
run_generator
assert_file 'Gemfile', /gem 'sdoc',\s+'~> 0.4.0',\s+group: :doc/
diff --git a/railties/test/generators/generators_test_helper.rb b/railties/test/generators/generators_test_helper.rb
index 94099fcd2e..7c5183e6c8 100644
--- a/railties/test/generators/generators_test_helper.rb
+++ b/railties/test/generators/generators_test_helper.rb
@@ -52,7 +52,7 @@ module GeneratorsTestHelper
def silence_stream(stream)
old_stream = stream.dup
- stream.reopen(RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ ? 'NUL:' : '/dev/null')
+ stream.reopen(IO::NULL)
stream.sync = true
yield
ensure
diff --git a/railties/test/generators/mailer_generator_test.rb b/railties/test/generators/mailer_generator_test.rb
index 8d2d97f64f..febd2fd12e 100644
--- a/railties/test/generators/mailer_generator_test.rb
+++ b/railties/test/generators/mailer_generator_test.rb
@@ -26,8 +26,8 @@ class MailerGeneratorTest < Rails::Generators::TestCase
def test_mailer_with_i18n_helper
run_generator
assert_file "app/mailers/notifier_mailer.rb" do |mailer|
- assert_match(/en\.notifier\.foo\.subject/, mailer)
- assert_match(/en\.notifier\.bar\.subject/, mailer)
+ assert_match(/en\.notifier_mailer\.foo\.subject/, mailer)
+ assert_match(/en\.notifier_mailer\.bar\.subject/, mailer)
end
end
@@ -126,7 +126,7 @@ class MailerGeneratorTest < Rails::Generators::TestCase
run_generator ["Farm::Animal", "moos"]
assert_file "app/mailers/farm/animal_mailer.rb" do |mailer|
assert_match(/class Farm::AnimalMailer < ApplicationMailer/, mailer)
- assert_match(/en\.farm\.animal\.moos\.subject/, mailer)
+ assert_match(/en\.farm\.animal_mailer\.moos\.subject/, mailer)
end
assert_file "test/mailers/previews/farm/animal_mailer_preview.rb" do |preview|
assert_match(/\# Preview all emails at http:\/\/localhost\:3000\/rails\/mailers\/farm\/animal/, preview)
diff --git a/railties/test/generators/migration_generator_test.rb b/railties/test/generators/migration_generator_test.rb
index 413d457d54..57bc220558 100644
--- a/railties/test/generators/migration_generator_test.rb
+++ b/railties/test/generators/migration_generator_test.rb
@@ -276,6 +276,30 @@ class MigrationGeneratorTest < Rails::Generators::TestCase
end
end
+ def test_create_table_migration_with_token_option
+ run_generator ["create_users", "token:token", "auth_token:token"]
+ assert_migration "db/migrate/create_users.rb" do |content|
+ assert_method :change, content do |change|
+ assert_match(/create_table :users/, change)
+ assert_match(/ t\.string :token/, change)
+ assert_match(/ t\.string :auth_token/, change)
+ assert_match(/add_index :users, :token, unique: true/, change)
+ assert_match(/add_index :users, :auth_token, unique: true/, change)
+ end
+ end
+ end
+
+ def test_add_migration_with_token_option
+ migration = "add_token_to_users"
+ run_generator [migration, "auth_token:token"]
+ assert_migration "db/migrate/#{migration}.rb" do |content|
+ assert_method :change, content do |change|
+ assert_match(/add_column :users, :auth_token, :string/, change)
+ assert_match(/add_index :users, :auth_token, unique: true/, change)
+ end
+ end
+ end
+
private
def with_singular_table_name
diff --git a/railties/test/generators/model_generator_test.rb b/railties/test/generators/model_generator_test.rb
index f3b699101f..17a13fbf1f 100644
--- a/railties/test/generators/model_generator_test.rb
+++ b/railties/test/generators/model_generator_test.rb
@@ -438,6 +438,17 @@ class ModelGeneratorTest < Rails::Generators::TestCase
end
end
+ def test_token_option_adds_has_secure_token
+ run_generator ["user", "token:token", "auth_token:token"]
+ expected_file = <<-FILE.strip_heredoc
+ class User < ActiveRecord::Base
+ has_secure_token
+ has_secure_token :auth_token
+ end
+ FILE
+ assert_file "app/models/user.rb", expected_file
+ end
+
private
def assert_generated_fixture(path, parsed_contents)
fixture_file = File.new File.expand_path(path, destination_root)
diff --git a/railties/test/generators/namespaced_generators_test.rb b/railties/test/generators/namespaced_generators_test.rb
index a4dad1f2b4..d0ea01dfb0 100644
--- a/railties/test/generators/namespaced_generators_test.rb
+++ b/railties/test/generators/namespaced_generators_test.rb
@@ -156,8 +156,8 @@ class NamespacedMailerGeneratorTest < NamespacedGeneratorTestCase
def test_mailer_with_i18n_helper
run_generator
assert_file "app/mailers/test_app/notifier_mailer.rb" do |mailer|
- assert_match(/en\.notifier\.foo\.subject/, mailer)
- assert_match(/en\.notifier\.bar\.subject/, mailer)
+ assert_match(/en\.notifier_mailer\.foo\.subject/, mailer)
+ assert_match(/en\.notifier_mailer\.bar\.subject/, mailer)
end
end
diff --git a/railties/test/isolation/abstract_unit.rb b/railties/test/isolation/abstract_unit.rb
index 39e8a5f756..2c4d9c5995 100644
--- a/railties/test/isolation/abstract_unit.rb
+++ b/railties/test/isolation/abstract_unit.rb
@@ -69,7 +69,8 @@ module TestHelpers
def assert_welcome(resp)
assert_equal 200, resp[0]
- assert resp[1]["Content-Type"] = "text/html"
+ assert_match 'text/html', resp[1]["Content-Type"]
+ assert_match 'charset=utf-8', resp[1]["Content-Type"]
assert extract_body(resp).match(/Welcome aboard/)
end
@@ -341,7 +342,7 @@ class ActiveSupport::TestCase
def silence_stream(stream)
old_stream = stream.dup
- stream.reopen(RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ ? 'NUL:' : '/dev/null')
+ stream.reopen(IO::NULL)
stream.sync = true
yield
ensure