aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.travis.yml2
-rw-r--r--CODE_OF_CONDUCT.md12
-rw-r--r--Gemfile5
-rw-r--r--Gemfile.lock80
-rw-r--r--README.md2
-rw-r--r--actionmailer/test/abstract_unit.rb5
-rw-r--r--actionmailer/test/base_test.rb111
-rw-r--r--actionmailer/test/delivery_methods_test.rb26
-rw-r--r--actionmailer/test/i18n_with_controller_test.rb13
-rw-r--r--actionmailer/test/message_delivery_test.rb1
-rw-r--r--actionpack/CHANGELOG.md5
-rw-r--r--actionpack/actionpack.gemspec2
-rw-r--r--actionpack/lib/abstract_controller/rendering.rb17
-rw-r--r--actionpack/lib/action_controller.rb1
-rw-r--r--actionpack/lib/action_controller/api.rb1
-rw-r--r--actionpack/lib/action_controller/base.rb3
-rw-r--r--actionpack/lib/action_controller/caching.rb2
-rw-r--r--actionpack/lib/action_controller/metal.rb93
-rw-r--r--actionpack/lib/action_controller/metal/conditional_get.rb1
-rw-r--r--actionpack/lib/action_controller/metal/cookies.rb2
-rw-r--r--actionpack/lib/action_controller/metal/etag_with_template_digest.rb2
-rw-r--r--actionpack/lib/action_controller/metal/head.rb9
-rw-r--r--actionpack/lib/action_controller/metal/http_authentication.rb6
-rw-r--r--actionpack/lib/action_controller/metal/instrumentation.rb1
-rw-r--r--actionpack/lib/action_controller/metal/live.rb21
-rw-r--r--actionpack/lib/action_controller/metal/mime_responds.rb1
-rw-r--r--actionpack/lib/action_controller/metal/rack_delegation.rb38
-rw-r--r--actionpack/lib/action_controller/metal/redirecting.rb1
-rw-r--r--actionpack/lib/action_controller/metal/rendering.rb12
-rw-r--r--actionpack/lib/action_controller/metal/request_forgery_protection.rb6
-rw-r--r--actionpack/lib/action_controller/metal/strong_parameters.rb49
-rw-r--r--actionpack/lib/action_controller/metal/testing.rb2
-rw-r--r--actionpack/lib/action_controller/test_case.rb54
-rw-r--r--actionpack/lib/action_dispatch/http/cache.rb49
-rw-r--r--actionpack/lib/action_dispatch/http/filter_parameters.rb4
-rw-r--r--actionpack/lib/action_dispatch/http/filter_redirect.rb2
-rw-r--r--actionpack/lib/action_dispatch/http/headers.rb34
-rw-r--r--actionpack/lib/action_dispatch/http/mime_negotiation.rb23
-rw-r--r--actionpack/lib/action_dispatch/http/parameters.rb36
-rw-r--r--actionpack/lib/action_dispatch/http/request.rb113
-rw-r--r--actionpack/lib/action_dispatch/http/response.rb32
-rw-r--r--actionpack/lib/action_dispatch/http/url.rb6
-rw-r--r--actionpack/lib/action_dispatch/journey/nodes/node.rb4
-rw-r--r--actionpack/lib/action_dispatch/journey/route.rb10
-rw-r--r--actionpack/lib/action_dispatch/middleware/cookies.rb26
-rw-r--r--actionpack/lib/action_dispatch/middleware/debug_exceptions.rb19
-rw-r--r--actionpack/lib/action_dispatch/middleware/flash.rb10
-rw-r--r--actionpack/lib/action_dispatch/middleware/params_parser.rb6
-rw-r--r--actionpack/lib/action_dispatch/middleware/public_exceptions.rb2
-rw-r--r--actionpack/lib/action_dispatch/middleware/session/abstract_store.rb12
-rw-r--r--actionpack/lib/action_dispatch/middleware/session/cookie_store.rb34
-rw-r--r--actionpack/lib/action_dispatch/middleware/show_exceptions.rb14
-rw-r--r--actionpack/lib/action_dispatch/middleware/stack.rb4
-rw-r--r--actionpack/lib/action_dispatch/request/session.rb48
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb36
-rw-r--r--actionpack/lib/action_dispatch/routing/route_set.rb92
-rw-r--r--actionpack/lib/action_dispatch/testing/integration.rb4
-rw-r--r--actionpack/test/abstract/translation_test.rb22
-rw-r--r--actionpack/test/abstract_unit.rb91
-rw-r--r--actionpack/test/controller/base_test.rb2
-rw-r--r--actionpack/test/controller/integration_test.rb266
-rw-r--r--actionpack/test/controller/live_stream_test.rb4
-rw-r--r--actionpack/test/controller/new_base/bare_metal_test.rb24
-rw-r--r--actionpack/test/controller/new_base/middleware_test.rb2
-rw-r--r--actionpack/test/controller/new_base/render_html_test.rb2
-rw-r--r--actionpack/test/controller/new_base/render_plain_test.rb2
-rw-r--r--actionpack/test/controller/parameters/always_permitted_parameters_test.rb1
-rw-r--r--actionpack/test/controller/render_test.rb10
-rw-r--r--actionpack/test/controller/request_forgery_protection_test.rb109
-rw-r--r--actionpack/test/controller/required_params_test.rb17
-rw-r--r--actionpack/test/controller/resources_test.rb32
-rw-r--r--actionpack/test/controller/routing_test.rb48
-rw-r--r--actionpack/test/controller/test_case_test.rb20
-rw-r--r--actionpack/test/dispatch/callbacks_test.rb2
-rw-r--r--actionpack/test/dispatch/cookies_test.rb4
-rw-r--r--actionpack/test/dispatch/debug_exceptions_test.rb57
-rw-r--r--actionpack/test/dispatch/exception_wrapper_test.rb2
-rw-r--r--actionpack/test/dispatch/header_test.rb14
-rw-r--r--actionpack/test/dispatch/live_response_test.rb2
-rw-r--r--actionpack/test/dispatch/mount_test.rb2
-rw-r--r--actionpack/test/dispatch/request/session_test.rb40
-rw-r--r--actionpack/test/dispatch/routing/inspector_test.rb9
-rw-r--r--actionpack/test/dispatch/routing_test.rb11
-rw-r--r--actionpack/test/dispatch/session/abstract_store_test.rb6
-rw-r--r--actionpack/test/dispatch/session/cookie_store_test.rb32
-rw-r--r--actionpack/test/dispatch/session/test_session_test.rb10
-rw-r--r--actionpack/test/dispatch/test_request_test.rb4
-rw-r--r--actionpack/test/journey/nodes/symbol_test.rb2
-rw-r--r--actionview/CHANGELOG.md10
-rw-r--r--actionview/lib/action_view/layouts.rb15
-rw-r--r--actionview/lib/action_view/lookup_context.rb16
-rw-r--r--actionview/lib/action_view/renderer/streaming_template_renderer.rb2
-rw-r--r--actionview/lib/action_view/renderer/template_renderer.rb19
-rw-r--r--actionview/lib/action_view/rendering.rb2
-rw-r--r--actionview/test/abstract_unit.rb4
-rw-r--r--actionview/test/template/asset_tag_helper_test.rb10
-rw-r--r--actionview/test/template/form_helper_test.rb55
-rw-r--r--actionview/test/template/form_options_helper_i18n_test.rb5
-rw-r--r--actionview/test/template/lookup_context_test.rb66
-rw-r--r--actionview/test/template/number_helper_test.rb2
-rw-r--r--actionview/test/template/template_test.rb10
-rw-r--r--actionview/test/template/test_case_test.rb23
-rw-r--r--actionview/test/template/translation_helper_test.rb13
-rw-r--r--actionview/test/template/url_helper_test.rb1
-rw-r--r--activejob/CHANGELOG.md5
-rw-r--r--activejob/Rakefile2
-rw-r--r--activejob/lib/active_job.rb1
-rw-r--r--activejob/lib/active_job/async_job.rb75
-rw-r--r--activejob/lib/active_job/queue_adapters.rb9
-rw-r--r--activejob/lib/active_job/queue_adapters/async_adapter.rb23
-rw-r--r--activejob/test/adapters/async.rb5
-rw-r--r--activejob/test/cases/async_job_test.rb42
-rw-r--r--activejob/test/helper.rb1
-rw-r--r--activejob/test/integration/queuing_test.rb2
-rw-r--r--activejob/test/jobs/queue_as_job.rb10
-rw-r--r--activejob/test/support/integration/adapters/async.rb9
-rw-r--r--activemodel/lib/active_model/serialization.rb9
-rw-r--r--activemodel/test/cases/helper.rb2
-rw-r--r--activerecord/CHANGELOG.md41
-rw-r--r--activerecord/lib/active_record/associations.rb6
-rw-r--r--activerecord/lib/active_record/associations/collection_proxy.rb25
-rw-r--r--activerecord/lib/active_record/associations/has_one_association.rb2
-rw-r--r--activerecord/lib/active_record/attribute_methods/read.rb4
-rw-r--r--activerecord/lib/active_record/autosave_association.rb4
-rw-r--r--activerecord/lib/active_record/base.rb1
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb3
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_adapter.rb5
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb43
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql_adapter.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/json.rb27
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb5
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb10
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb4
-rw-r--r--activerecord/lib/active_record/enum.rb3
-rw-r--r--activerecord/lib/active_record/errors.rb7
-rw-r--r--activerecord/lib/active_record/migration.rb25
-rw-r--r--activerecord/lib/active_record/migration/command_recorder.rb32
-rw-r--r--activerecord/lib/active_record/type.rb2
-rw-r--r--activerecord/lib/active_record/type/internal/abstract_json.rb33
-rw-r--r--activerecord/lib/active_record/validations/uniqueness.rb6
-rw-r--r--activerecord/test/cases/adapters/mysql2/json_test.rb172
-rw-r--r--activerecord/test/cases/adapters/postgresql/schema_authorization_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/schema_test.rb85
-rw-r--r--activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb17
-rw-r--r--activerecord/test/cases/associations/belongs_to_associations_test.rb1
-rw-r--r--activerecord/test/cases/associations/eager_test.rb58
-rw-r--r--activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb7
-rw-r--r--activerecord/test/cases/associations/has_many_through_associations_test.rb12
-rw-r--r--activerecord/test/cases/associations/has_one_associations_test.rb8
-rw-r--r--activerecord/test/cases/associations_test.rb1
-rw-r--r--activerecord/test/cases/attribute_methods_test.rb6
-rw-r--r--activerecord/test/cases/attribute_test.rb1
-rw-r--r--activerecord/test/cases/batches_test.rb25
-rw-r--r--activerecord/test/cases/fixtures_test.rb18
-rw-r--r--activerecord/test/cases/helper.rb2
-rw-r--r--activerecord/test/cases/invertible_migration_test.rb65
-rw-r--r--activerecord/test/cases/migration/change_table_test.rb1
-rw-r--r--activerecord/test/cases/migration/pending_migrations_test.rb1
-rw-r--r--activerecord/test/cases/migration_test.rb8
-rw-r--r--activerecord/test/cases/persistence_test.rb1
-rw-r--r--activerecord/test/cases/query_cache_test.rb69
-rw-r--r--activerecord/test/cases/readonly_test.rb1
-rw-r--r--activerecord/test/cases/reflection_test.rb40
-rw-r--r--activerecord/test/cases/scoping/named_scoping_test.rb10
-rw-r--r--activerecord/test/cases/validations/i18n_validation_test.rb10
-rw-r--r--activerecord/test/cases/validations/uniqueness_validation_test.rb23
-rw-r--r--activerecord/test/models/developer.rb1
-rw-r--r--activerecord/test/models/ship.rb1
-rw-r--r--activerecord/test/schema/schema.rb1
-rw-r--r--activesupport/CHANGELOG.md10
-rw-r--r--activesupport/activesupport.gemspec2
-rw-r--r--activesupport/lib/active_support/array_inquirer.rb2
-rw-r--r--activesupport/lib/active_support/cache.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/file/atomic.rb56
-rw-r--r--activesupport/lib/active_support/core_ext/kernel/reporting.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/string/strip.rb4
-rw-r--r--activesupport/lib/active_support/dependencies.rb10
-rw-r--r--activesupport/lib/active_support/deprecation/proxy_wrappers.rb2
-rw-r--r--activesupport/lib/active_support/deprecation/reporting.rb15
-rw-r--r--activesupport/lib/active_support/log_subscriber/test_helper.rb1
-rw-r--r--activesupport/lib/active_support/number_helper.rb8
-rw-r--r--activesupport/lib/active_support/number_helper/number_to_delimited_converter.rb9
-rw-r--r--activesupport/lib/active_support/testing/method_call_assertions.rb6
-rw-r--r--activesupport/lib/active_support/time_with_zone.rb12
-rw-r--r--activesupport/test/abstract_unit.rb2
-rw-r--r--activesupport/test/array_inquirer_test.rb9
-rw-r--r--activesupport/test/number_helper_test.rb1
-rw-r--r--activesupport/test/testing/method_call_assertions_test.rb6
-rw-r--r--guides/CHANGELOG.md4
-rw-r--r--guides/bug_report_templates/action_controller_master.rb1
-rw-r--r--guides/bug_report_templates/active_record_master.rb1
-rw-r--r--guides/bug_report_templates/generic_master.rb1
-rw-r--r--guides/source/4_2_release_notes.md11
-rw-r--r--guides/source/action_controller_overview.md4
-rw-r--r--guides/source/action_view_overview.md4
-rw-r--r--guides/source/active_record_migrations.md37
-rw-r--r--guides/source/active_record_querying.md6
-rw-r--r--guides/source/active_support_instrumentation.md32
-rw-r--r--guides/source/api_app.md5
-rw-r--r--guides/source/api_documentation_guidelines.md5
-rw-r--r--guides/source/configuring.md4
-rw-r--r--guides/source/contributing_to_ruby_on_rails.md3
-rw-r--r--guides/source/debugging_rails_applications.md30
-rw-r--r--guides/source/getting_started.md7
-rw-r--r--guides/source/layouts_and_rendering.md7
-rw-r--r--guides/source/plugins.md1
-rw-r--r--guides/source/security.md20
-rw-r--r--guides/source/testing.md24
-rw-r--r--railties/CHANGELOG.md4
-rw-r--r--railties/Rakefile2
-rw-r--r--railties/lib/rails/commands/server.rb2
-rw-r--r--railties/lib/rails/generators/app_base.rb12
-rw-r--r--railties/lib/rails/generators/rails/app/templates/app/assets/manifest.js.tt8
-rw-r--r--railties/lib/rails/generators/rails/plugin/plugin_generator.rb13
-rw-r--r--railties/lib/rails/generators/rails/plugin/templates/rails/dummy_manifest.js11
-rw-r--r--railties/lib/rails/generators/rails/plugin/templates/rails/engine_manifest.js6
-rw-r--r--railties/lib/rails/generators/rails/plugin/templates/test/test_helper.rb3
-rw-r--r--railties/lib/rails/source_annotation_extractor.rb2
-rw-r--r--railties/lib/rails/test_unit/minitest_plugin.rb2
-rw-r--r--railties/test/application/asset_debugging_test.rb5
-rw-r--r--railties/test/application/assets_test.rb28
-rw-r--r--railties/test/application/initializers/frameworks_test.rb1
-rw-r--r--railties/test/commands/server_test.rb7
-rw-r--r--railties/test/generators/actions_test.rb37
-rw-r--r--railties/test/generators/generators_test_helper.rb2
-rw-r--r--railties/test/generators/named_base_test.rb1
-rw-r--r--railties/test/generators_test.rb24
-rw-r--r--railties/test/path_generation_test.rb18
-rw-r--r--tasks/release.rb2
230 files changed, 2514 insertions, 1486 deletions
diff --git a/.travis.yml b/.travis.yml
index 605d1ff247..c648bd2ca7 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -21,7 +21,7 @@ env:
- "GEM=aj:integration"
- "GEM=guides"
rvm:
- - 2.2.2
+ - 2.2.3
- ruby-head
matrix:
allow_failures:
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 0000000000..078d5f1219
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,12 @@
+# Contributor Code of Conduct
+
+The Rails team is committed to fostering a welcoming community.
+
+**Our Code of Conduct can be found here**:
+
+http://rubyonrails.org/conduct/
+
+For a history of updates, see the page history here:
+
+https://github.com/rails/rails.github.com/commits/master/conduct/index.html
+
diff --git a/Gemfile b/Gemfile
index 95718d67ae..a398387947 100644
--- a/Gemfile
+++ b/Gemfile
@@ -7,6 +7,7 @@ gem 'rake', '>= 10.3'
# Active Job depends on the URI::GID::MissingModelIDError, which isn't released yet.
gem 'globalid', github: 'rails/globalid'
+gem 'rack', github: 'rack/rack'
# This needs to be with require false as it is
# loaded after loading the test library to
@@ -20,8 +21,9 @@ gem 'turbolinks'
gem 'arel', github: 'rails/arel', branch: 'master'
gem 'mail', github: 'mikel/mail'
-gem 'sprockets', '~> 3.0.0.rc.1'
+gem 'sprockets', github: 'rails/sprockets', branch: 'master'
gem 'sprockets-rails', github: 'rails/sprockets-rails', branch: 'master'
+gem 'sass-rails', github: 'rails/sass-rails', branch: 'master'
# require: false so bcrypt is loaded only when has_secure_password is used.
# This is to avoid ActiveModel (and by extension the entire framework)
@@ -31,6 +33,7 @@ gem 'bcrypt', '~> 3.1.10', require: false
# This needs to be with require false to avoid
# it being automatically loaded by sprockets
gem 'uglifier', '>= 1.3.0', require: false
+gem 'sass', '>= 3.3', require: false
group :doc do
gem 'sdoc', '~> 0.4.0'
diff --git a/Gemfile.lock b/Gemfile.lock
index 52e221c6fc..ae2f00df2e 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -26,6 +26,13 @@ GIT
mime-types (>= 1.16, < 3)
GIT
+ remote: git://github.com/rack/rack.git
+ revision: 2fe4a79667603a0c3d636daec06281459b10f7f8
+ specs:
+ rack (2.0.0.alpha)
+ json
+
+GIT
remote: git://github.com/rails/arel.git
revision: d5432b4616ff43fbb14540d351eed351e21bb20e
branch: master
@@ -50,15 +57,34 @@ GIT
thor (>= 0.14, < 2.0)
GIT
+ remote: git://github.com/rails/sass-rails.git
+ revision: 805cb17722b8a13ff00dffe20283a6ba2c9a45dc
+ branch: master
+ specs:
+ sass-rails (6.0.0)
+ railties (>= 4.0.0, < 5.0)
+ sass (~> 3.3)
+ sprockets (>= 4.0)
+ sprockets-rails (< 4.0)
+
+GIT
remote: git://github.com/rails/sprockets-rails.git
- revision: ad4a43bd1bb19c86a8bf94a2ad5e477686161490
+ revision: e65e088575be4f6b994823b80a277affb0a57a5e
branch: master
specs:
- sprockets-rails (3.0.0)
+ sprockets-rails (3.0.0.beta2)
actionpack (>= 4.0)
activesupport (>= 4.0)
sprockets (>= 3.0.0)
+GIT
+ remote: git://github.com/rails/sprockets.git
+ revision: a61550db7184fc83964fb983547ff09da9efa9be
+ branch: master
+ specs:
+ sprockets (4.0.0)
+ rack (~> 2.x)
+
PATH
remote: .
specs:
@@ -71,7 +97,7 @@ PATH
actionpack (5.0.0.alpha)
actionview (= 5.0.0.alpha)
activesupport (= 5.0.0.alpha)
- rack (~> 1.6)
+ rack (~> 2.x)
rack-test (~> 0.6.3)
rails-dom-testing (~> 1.0, >= 1.0.5)
rails-html-sanitizer (~> 1.0, >= 1.0.2)
@@ -92,7 +118,7 @@ PATH
activesupport (= 5.0.0.alpha)
arel (= 7.0.0.alpha)
activesupport (5.0.0.alpha)
- concurrent-ruby (~> 0.9.0)
+ concurrent-ruby (~> 0.9.1)
i18n (~> 0.7)
json (~> 1.7, >= 1.7.7)
method_source
@@ -120,7 +146,7 @@ PATH
GEM
remote: https://rubygems.org/
specs:
- amq-protocol (1.9.2)
+ amq-protocol (2.0.0)
backburner (1.0.0)
beaneater (~> 1.0)
dante (> 0.1.5)
@@ -130,11 +156,10 @@ GEM
beaneater (1.0.0)
benchmark-ips (2.3.0)
builder (3.2.2)
- bunny (1.7.0)
+ bunny (2.0.0)
amq-protocol (>= 1.9.2)
- byebug (5.0.0)
- columnize (= 0.9.0)
- celluloid (0.16.1)
+ byebug (6.0.2)
+ celluloid (0.16.0)
timers (~> 4.0.0)
coffee-rails (4.1.0)
coffee-script (>= 2.2.0)
@@ -143,7 +168,6 @@ GEM
coffee-script-source
execjs
coffee-script-source (1.9.1.1)
- columnize (0.9.0)
concurrent-ruby (0.9.1)
connection_pool (2.2.0)
dalli (2.7.4)
@@ -154,7 +178,7 @@ GEM
activerecord (>= 3.0, < 5.0)
delayed_job (>= 3.0, < 4.1)
erubis (2.7.0)
- execjs (2.5.2)
+ execjs (2.6.0)
hitimes (1.2.2)
hitimes (1.2.2-x86-mingw32)
i18n (0.7.0)
@@ -162,7 +186,7 @@ GEM
kindlerb (0.1.1)
mustache
nokogiri
- loofah (2.0.2)
+ loofah (2.0.3)
nokogiri (>= 1.5.9)
metaclass (0.0.4)
method_source (0.8.2)
@@ -179,19 +203,16 @@ GEM
nokogiri (1.6.6.2)
mini_portile (~> 0.6.0)
pg (0.18.2)
- psych (2.0.13)
+ psych (2.0.15)
que (0.10.0)
racc (1.4.12)
- rack (1.6.4)
rack-cache (1.2)
rack (>= 0.4)
- rack-protection (1.5.3)
- rack
rack-test (0.6.3)
rack (>= 1.0)
rails-deprecated_sanitizer (1.0.3)
activesupport (>= 4.2.0.alpha)
- rails-dom-testing (1.0.6)
+ rails-dom-testing (1.0.7)
activesupport (>= 4.2.0.beta, < 5.0)
nokogiri (~> 1.6.0)
rails-deprecated_sanitizer (>= 1.0.1)
@@ -215,6 +236,7 @@ GEM
resque (~> 1.25)
rufus-scheduler (~> 3.0)
rufus-scheduler (3.1.3)
+ sass (3.4.17)
sdoc (0.4.1)
json (~> 1.7, >= 1.7.7)
rdoc (~> 4.0)
@@ -228,25 +250,20 @@ GEM
redis (~> 3.2, >= 3.2.1)
redis-namespace (~> 1.5, >= 1.5.2)
sigdump (0.2.3)
- sinatra (1.4.6)
- rack (~> 1.4)
- rack-protection (~> 1.4)
- tilt (>= 1.3, < 3)
- sneakers (1.0.4)
- bunny (~> 1.7.0)
+ sinatra (1.0)
+ rack (>= 1.0)
+ sneakers (1.1.1)
+ bunny (>= 1.7.0, <= 2.0.0)
serverengine (~> 1.5.5)
thor
thread (~> 0.1.7)
- sprockets (3.0.3)
- rack (~> 1.0)
sqlite3 (1.3.10)
stackprof (0.2.7)
- sucker_punch (1.5.0)
- celluloid (~> 0.16.0)
+ sucker_punch (1.5.1)
+ celluloid (= 0.16.0)
thor (0.19.1)
thread (0.1.7)
thread_safe (0.3.5)
- tilt (2.0.1)
timers (4.0.1)
hitimes
turbolinks (2.5.3)
@@ -297,17 +314,20 @@ DEPENDENCIES
que
queue_classic!
racc (>= 1.4.6)
+ rack!
rack-cache (~> 1.2)
rails!
rake (>= 10.3)
redcarpet (~> 3.2.3)
resque
resque-scheduler
+ sass (>= 3.3)
+ sass-rails!
sdoc (~> 0.4.0)
sequel
sidekiq
sneakers
- sprockets (~> 3.0.0.rc.1)
+ sprockets!
sprockets-rails!
sqlite3 (~> 1.3.6)
stackprof
@@ -317,4 +337,4 @@ DEPENDENCIES
w3c_validators
BUNDLED WITH
- 1.10.5
+ 1.10.6
diff --git a/README.md b/README.md
index bbe3a6731d..ca796cebe7 100644
--- a/README.md
+++ b/README.md
@@ -76,6 +76,8 @@ and may also be used independently outside Rails.
We encourage you to contribute to Ruby on Rails! Please check out the
[Contributing to Ruby on Rails guide](http://edgeguides.rubyonrails.org/contributing_to_ruby_on_rails.html) for guidelines about how to proceed. [Join us!](http://contributors.rubyonrails.org)
+Everyone interacting in Rails and its sub-project’s codebases, issue trackers, chat rooms, and mailing lists is expected to follow the Rails [code of conduct](http://rubyonrails.org/conduct/).
+
## Code Status
[![Build Status](https://travis-ci.org/rails/rails.svg?branch=master)](https://travis-ci.org/rails/rails)
diff --git a/actionmailer/test/abstract_unit.rb b/actionmailer/test/abstract_unit.rb
index 706249a2f6..85d3629514 100644
--- a/actionmailer/test/abstract_unit.rb
+++ b/actionmailer/test/abstract_unit.rb
@@ -9,6 +9,7 @@ silence_warnings do
end
require 'active_support/testing/autorun'
+require 'active_support/testing/method_call_assertions'
require 'action_mailer'
require 'action_mailer/test_case'
@@ -40,4 +41,6 @@ def jruby_skip(message = '')
skip message if defined?(JRUBY_VERSION)
end
-require 'mocha/setup' # FIXME: stop using mocha
+class ActiveSupport::TestCase
+ include ActiveSupport::Testing::MethodCallAssertions
+end
diff --git a/actionmailer/test/base_test.rb b/actionmailer/test/base_test.rb
index 59c5638f96..6b2e6a531c 100644
--- a/actionmailer/test/base_test.rb
+++ b/actionmailer/test/base_test.rb
@@ -505,9 +505,10 @@ class BaseTest < ActiveSupport::TestCase
end
test "calling deliver on the action should deliver the mail object" do
- BaseMailer.expects(:deliver_mail).once
- mail = BaseMailer.welcome.deliver_now
- assert_equal 'The first email on new API!', mail.subject
+ assert_called(BaseMailer, :deliver_mail) do
+ mail = BaseMailer.welcome.deliver_now
+ assert_equal 'The first email on new API!', mail.subject
+ end
end
test "calling deliver on the action should increment the deliveries collection if using the test mailer" do
@@ -517,9 +518,11 @@ class BaseTest < ActiveSupport::TestCase
test "calling deliver, ActionMailer should yield back to mail to let it call :do_delivery on itself" do
mail = Mail::Message.new
- mail.expects(:do_delivery).once
- BaseMailer.expects(:welcome).returns(mail)
- BaseMailer.welcome.deliver
+ assert_called(mail, :do_delivery) do
+ assert_called(BaseMailer, :welcome, returns: mail) do
+ BaseMailer.welcome.deliver
+ end
+ end
end
# Rendering
@@ -607,8 +610,9 @@ class BaseTest < ActiveSupport::TestCase
mail_side_effects do
ActionMailer::Base.register_observer(MyObserver)
mail = BaseMailer.welcome
- MyObserver.expects(:delivered_email).with(mail)
- mail.deliver_now
+ assert_called_with(MyObserver, :delivered_email, [mail]) do
+ mail.deliver_now
+ end
end
end
@@ -616,8 +620,9 @@ class BaseTest < ActiveSupport::TestCase
mail_side_effects do
ActionMailer::Base.register_observer("BaseTest::MyObserver")
mail = BaseMailer.welcome
- MyObserver.expects(:delivered_email).with(mail)
- mail.deliver_now
+ assert_called_with(MyObserver, :delivered_email, [mail]) do
+ mail.deliver_now
+ end
end
end
@@ -625,8 +630,9 @@ class BaseTest < ActiveSupport::TestCase
mail_side_effects do
ActionMailer::Base.register_observer(:"base_test/my_observer")
mail = BaseMailer.welcome
- MyObserver.expects(:delivered_email).with(mail)
- mail.deliver_now
+ assert_called_with(MyObserver, :delivered_email, [mail]) do
+ mail.deliver_now
+ end
end
end
@@ -634,9 +640,11 @@ class BaseTest < ActiveSupport::TestCase
mail_side_effects do
ActionMailer::Base.register_observers("BaseTest::MyObserver", MySecondObserver)
mail = BaseMailer.welcome
- MyObserver.expects(:delivered_email).with(mail)
- MySecondObserver.expects(:delivered_email).with(mail)
- mail.deliver_now
+ assert_called_with(MyObserver, :delivered_email, [mail]) do
+ assert_called_with(MySecondObserver, :delivered_email, [mail]) do
+ mail.deliver_now
+ end
+ end
end
end
@@ -654,8 +662,9 @@ class BaseTest < ActiveSupport::TestCase
mail_side_effects do
ActionMailer::Base.register_interceptor(MyInterceptor)
mail = BaseMailer.welcome
- MyInterceptor.expects(:delivering_email).with(mail)
- mail.deliver_now
+ assert_called_with(MyInterceptor, :delivering_email, [mail]) do
+ mail.deliver_now
+ end
end
end
@@ -663,8 +672,9 @@ class BaseTest < ActiveSupport::TestCase
mail_side_effects do
ActionMailer::Base.register_interceptor("BaseTest::MyInterceptor")
mail = BaseMailer.welcome
- MyInterceptor.expects(:delivering_email).with(mail)
- mail.deliver_now
+ assert_called_with(MyInterceptor, :delivering_email, [mail]) do
+ mail.deliver_now
+ end
end
end
@@ -672,8 +682,9 @@ class BaseTest < ActiveSupport::TestCase
mail_side_effects do
ActionMailer::Base.register_interceptor(:"base_test/my_interceptor")
mail = BaseMailer.welcome
- MyInterceptor.expects(:delivering_email).with(mail)
- mail.deliver_now
+ assert_called_with(MyInterceptor, :delivering_email, [mail]) do
+ mail.deliver_now
+ end
end
end
@@ -681,18 +692,21 @@ class BaseTest < ActiveSupport::TestCase
mail_side_effects do
ActionMailer::Base.register_interceptors("BaseTest::MyInterceptor", MySecondInterceptor)
mail = BaseMailer.welcome
- MyInterceptor.expects(:delivering_email).with(mail)
- MySecondInterceptor.expects(:delivering_email).with(mail)
- mail.deliver_now
+ assert_called_with(MyInterceptor, :delivering_email, [mail]) do
+ assert_called_with(MySecondInterceptor, :delivering_email, [mail]) do
+ mail.deliver_now
+ end
+ end
end
end
test "being able to put proc's into the defaults hash and they get evaluated on mail sending" do
mail1 = ProcMailer.welcome['X-Proc-Method']
yesterday = 1.day.ago
- Time.stubs(:now).returns(yesterday)
- mail2 = ProcMailer.welcome['X-Proc-Method']
- assert(mail1.to_s.to_i > mail2.to_s.to_i)
+ Time.stub(:now, yesterday) do
+ mail2 = ProcMailer.welcome['X-Proc-Method']
+ assert(mail1.to_s.to_i > mail2.to_s.to_i)
+ end
end
test 'default values which have to_proc (e.g. symbols) should not be considered procs' do
@@ -877,33 +891,50 @@ class BasePreviewInterceptorsTest < ActiveSupport::TestCase
test "you can register a preview interceptor to the mail object that gets passed the mail object before previewing" do
ActionMailer::Base.register_preview_interceptor(MyInterceptor)
mail = BaseMailer.welcome
- BaseMailerPreview.any_instance.stubs(:welcome).returns(mail)
- MyInterceptor.expects(:previewing_email).with(mail)
- BaseMailerPreview.call(:welcome)
+ stub_any_instance(BaseMailerPreview) do |instance|
+ instance.stub(:welcome, mail) do
+ assert_called_with(MyInterceptor, :previewing_email, [mail]) do
+ BaseMailerPreview.call(:welcome)
+ end
+ end
+ end
end
test "you can register a preview interceptor using its stringified name to the mail object that gets passed the mail object before previewing" do
ActionMailer::Base.register_preview_interceptor("BasePreviewInterceptorsTest::MyInterceptor")
mail = BaseMailer.welcome
- BaseMailerPreview.any_instance.stubs(:welcome).returns(mail)
- MyInterceptor.expects(:previewing_email).with(mail)
- BaseMailerPreview.call(:welcome)
+ stub_any_instance(BaseMailerPreview) do |instance|
+ instance.stub(:welcome, mail) do
+ assert_called_with(MyInterceptor, :previewing_email, [mail]) do
+ BaseMailerPreview.call(:welcome)
+ end
+ end
+ end
end
test "you can register an interceptor using its symbolized underscored name to the mail object that gets passed the mail object before previewing" do
ActionMailer::Base.register_preview_interceptor(:"base_preview_interceptors_test/my_interceptor")
mail = BaseMailer.welcome
- BaseMailerPreview.any_instance.stubs(:welcome).returns(mail)
- MyInterceptor.expects(:previewing_email).with(mail)
- BaseMailerPreview.call(:welcome)
+ stub_any_instance(BaseMailerPreview) do |instance|
+ instance.stub(:welcome, mail) do
+ assert_called_with(MyInterceptor, :previewing_email, [mail]) do
+ BaseMailerPreview.call(:welcome)
+ end
+ end
+ end
end
test "you can register multiple preview interceptors to the mail object that both get passed the mail object before previewing" do
ActionMailer::Base.register_preview_interceptors("BasePreviewInterceptorsTest::MyInterceptor", MySecondInterceptor)
mail = BaseMailer.welcome
- BaseMailerPreview.any_instance.stubs(:welcome).returns(mail)
- MyInterceptor.expects(:previewing_email).with(mail)
- MySecondInterceptor.expects(:previewing_email).with(mail)
- BaseMailerPreview.call(:welcome)
+ stub_any_instance(BaseMailerPreview) do |instance|
+ instance.stub(:welcome, mail) do
+ assert_called_with(MyInterceptor, :previewing_email, [mail]) do
+ assert_called_with(MySecondInterceptor, :previewing_email, [mail]) do
+ BaseMailerPreview.call(:welcome)
+ end
+ end
+ end
+ end
end
end
diff --git a/actionmailer/test/delivery_methods_test.rb b/actionmailer/test/delivery_methods_test.rb
index 78507ce7dc..ceaa4ca83e 100644
--- a/actionmailer/test/delivery_methods_test.rb
+++ b/actionmailer/test/delivery_methods_test.rb
@@ -102,16 +102,21 @@ class MailDeliveryTest < ActiveSupport::TestCase
end
test "ActionMailer should be told when Mail gets delivered" do
- DeliveryMailer.expects(:deliver_mail).once
- DeliveryMailer.welcome.deliver_now
+ DeliveryMailer.delivery_method = :test
+ assert_called(DeliveryMailer, :deliver_mail) do
+ DeliveryMailer.welcome.deliver_now
+ end
end
test "delivery method can be customized per instance" do
- Mail::SMTP.any_instance.expects(:deliver!)
- email = DeliveryMailer.welcome.deliver_now
- assert_instance_of Mail::SMTP, email.delivery_method
- email = DeliveryMailer.welcome(delivery_method: :test).deliver_now
- assert_instance_of Mail::TestMailer, email.delivery_method
+ stub_any_instance(Mail::SMTP, instance: Mail::SMTP.new({})) do |instance|
+ assert_called(instance, :deliver!) do
+ email = DeliveryMailer.welcome.deliver_now
+ assert_instance_of Mail::SMTP, email.delivery_method
+ email = DeliveryMailer.welcome(delivery_method: :test).deliver_now
+ assert_instance_of Mail::TestMailer, email.delivery_method
+ end
+ end
end
test "delivery method can be customized in subclasses not changing the parent" do
@@ -176,8 +181,11 @@ class MailDeliveryTest < ActiveSupport::TestCase
old_perform_deliveries = DeliveryMailer.perform_deliveries
begin
DeliveryMailer.perform_deliveries = false
- Mail::Message.any_instance.expects(:deliver!).never
- DeliveryMailer.welcome.deliver_now
+ stub_any_instance(Mail::Message) do |instance|
+ assert_not_called(instance, :deliver!) do
+ DeliveryMailer.welcome.deliver_now
+ end
+ end
ensure
DeliveryMailer.perform_deliveries = old_perform_deliveries
end
diff --git a/actionmailer/test/i18n_with_controller_test.rb b/actionmailer/test/i18n_with_controller_test.rb
index 5ffde06a80..04e00cf481 100644
--- a/actionmailer/test/i18n_with_controller_test.rb
+++ b/actionmailer/test/i18n_with_controller_test.rb
@@ -53,12 +53,15 @@ class ActionMailerI18nWithControllerTest < ActionDispatch::IntegrationTest
end
def test_send_mail
- Mail::SMTP.any_instance.expects(:deliver!)
- with_translation 'de', email_subject: '[Anmeldung] Willkommen' do
- ActiveSupport::Deprecation.silence do
- get '/test/send_mail'
+ stub_any_instance(Mail::SMTP, instance: Mail::SMTP.new({})) do |instance|
+ assert_called(instance, :deliver!) do
+ with_translation 'de', email_subject: '[Anmeldung] Willkommen' do
+ ActiveSupport::Deprecation.silence do
+ get '/test/send_mail'
+ end
+ assert_equal "Mail sent - Subject: [Anmeldung] Willkommen", @response.body
+ end
end
- assert_equal "Mail sent - Subject: [Anmeldung] Willkommen", @response.body
end
end
diff --git a/actionmailer/test/message_delivery_test.rb b/actionmailer/test/message_delivery_test.rb
index 862ce26187..b834cdd08c 100644
--- a/actionmailer/test/message_delivery_test.rb
+++ b/actionmailer/test/message_delivery_test.rb
@@ -1,6 +1,5 @@
require 'abstract_unit'
require 'active_job'
-require 'minitest/mock'
require 'mailers/delayed_mailer'
class MessageDeliveryTest < ActiveSupport::TestCase
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md
index 8eea4ccd41..1cfd633606 100644
--- a/actionpack/CHANGELOG.md
+++ b/actionpack/CHANGELOG.md
@@ -1,3 +1,8 @@
+* Update `ActionController::TestSession#fetch` to behave more like
+ `ActionDispatch::Request::Session#fetch` when using non-string keys.
+
+ *Jeremy Friesen*
+
* Using strings or symbols for middleware class names is deprecated. Convert
things like this:
diff --git a/actionpack/actionpack.gemspec b/actionpack/actionpack.gemspec
index 1bba9df969..28d8bc3091 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'
+ s.add_dependency 'rack', '~> 2.x'
s.add_dependency 'rack-test', '~> 0.6.3'
s.add_dependency 'rails-html-sanitizer', '~> 1.0', '>= 1.0.2'
s.add_dependency 'rails-dom-testing', '~> 1.0', '>= 1.0.5'
diff --git a/actionpack/lib/abstract_controller/rendering.rb b/actionpack/lib/abstract_controller/rendering.rb
index 887196b3d2..6db0941b52 100644
--- a/actionpack/lib/abstract_controller/rendering.rb
+++ b/actionpack/lib/abstract_controller/rendering.rb
@@ -23,7 +23,11 @@ module AbstractController
def render(*args, &block)
options = _normalize_render(*args, &block)
self.response_body = render_to_body(options)
- _process_format(rendered_format, options) if rendered_format
+ if options[:html]
+ _set_content_type Mime::HTML.to_s
+ else
+ _set_content_type _get_content_type(rendered_format)
+ end
self.response_body
end
@@ -99,7 +103,14 @@ module AbstractController
# Process the rendered format.
# :api: private
- def _process_format(format, options = {})
+ def _process_format(format)
+ end
+
+ def _get_content_type(rendered_format) # :nodoc:
+ rendered_format.to_s
+ end
+
+ def _set_content_type(type) # :nodoc:
end
# Normalize args and options.
@@ -107,7 +118,7 @@ module AbstractController
def _normalize_render(*args, &block)
options = _normalize_args(*args, &block)
#TODO: remove defined? when we restore AP <=> AV dependency
- if defined?(request) && request && request.variant.present?
+ if defined?(request) && request.variant.present?
options[:variant] = request.variant
end
_normalize_options(options)
diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb
index 89fc4520d3..3d3af555c9 100644
--- a/actionpack/lib/action_controller.rb
+++ b/actionpack/lib/action_controller.rb
@@ -30,7 +30,6 @@ module ActionController
autoload :Instrumentation
autoload :MimeResponds
autoload :ParamsWrapper
- autoload :RackDelegation
autoload :Redirecting
autoload :Renderers
autoload :Rendering
diff --git a/actionpack/lib/action_controller/api.rb b/actionpack/lib/action_controller/api.rb
index b4594bf302..1a46d49a49 100644
--- a/actionpack/lib/action_controller/api.rb
+++ b/actionpack/lib/action_controller/api.rb
@@ -115,7 +115,6 @@ module ActionController
Rendering,
Renderers::All,
ConditionalGet,
- RackDelegation,
BasicImplicitRender,
StrongParameters,
diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb
index c0cd4ed2c6..0727bb8369 100644
--- a/actionpack/lib/action_controller/base.rb
+++ b/actionpack/lib/action_controller/base.rb
@@ -213,7 +213,6 @@ module ActionController
Renderers::All,
ConditionalGet,
EtagWithTemplateDigest,
- RackDelegation,
Caching,
MimeResponds,
ImplicitRender,
@@ -252,7 +251,7 @@ module ActionController
# Define some internal variables that should not be propagated to the view.
PROTECTED_IVARS = AbstractController::Rendering::DEFAULT_PROTECTED_INSTANCE_VARIABLES + [
- :@_status, :@_headers, :@_params, :@_response, :@_request,
+ :@_params, :@_response, :@_request,
:@_view_runtime, :@_stream, :@_url_options, :@_action_has_layout ]
def _protected_ivars # :nodoc:
diff --git a/actionpack/lib/action_controller/caching.rb b/actionpack/lib/action_controller/caching.rb
index a4e4992cfe..0b8fa2ea09 100644
--- a/actionpack/lib/action_controller/caching.rb
+++ b/actionpack/lib/action_controller/caching.rb
@@ -1,6 +1,5 @@
require 'fileutils'
require 'uri'
-require 'set'
module ActionController
# \Caching is a cheap way of speeding up slow applications by keeping the result of
@@ -46,7 +45,6 @@ module ActionController
end
end
- include RackDelegation
include AbstractController::Callbacks
include ConfigMethods
diff --git a/actionpack/lib/action_controller/metal.rb b/actionpack/lib/action_controller/metal.rb
index 914b0d4b30..030a1f3478 100644
--- a/actionpack/lib/action_controller/metal.rb
+++ b/actionpack/lib/action_controller/metal.rb
@@ -1,6 +1,8 @@
require 'active_support/core_ext/array/extract_options'
require 'action_dispatch/middleware/stack'
require 'active_support/deprecation'
+require 'action_dispatch/http/request'
+require 'action_dispatch/http/response'
module ActionController
# Extend ActionDispatch middleware stack to make it aware of options
@@ -132,23 +134,30 @@ module ActionController
@controller_name ||= name.demodulize.sub(/Controller$/, '').underscore
end
+ def self.make_response!(request)
+ ActionDispatch::Response.new.tap do |res|
+ res.request = request
+ end
+ end
+
+ def self.build_with_env(env = {}) #:nodoc:
+ new.tap { |c|
+ c.set_request! ActionDispatch::Request.new(env)
+ c.set_response! make_response!(c.request)
+ }
+ end
+
# Delegates to the class' <tt>controller_name</tt>
def controller_name
self.class.controller_name
end
- # The details below can be overridden to support a specific
- # Request and Response object. The default ActionController::Base
- # implementation includes RackDelegation, which makes a request
- # and response object available. You might wish to control the
- # environment and response manually for performance reasons.
-
- attr_internal :headers, :response, :request
+ attr_internal :response, :request
delegate :session, :to => "@_request"
+ delegate :headers, :status=, :location=, :content_type=,
+ :status, :location, :content_type, :to => "@_response"
def initialize
- @_headers = {"Content-Type" => "text/html"}
- @_status = 200
@_request = nil
@_response = nil
@_routes = nil
@@ -163,63 +172,46 @@ module ActionController
@_params = val
end
- # Basic implementations for content_type=, location=, and headers are
- # provided to reduce the dependency on the RackDelegation module
- # in Renderer and Redirector.
-
- def content_type=(type)
- headers["Content-Type"] = type.to_s
- end
-
- def content_type
- headers["Content-Type"]
- end
-
- def location
- headers["Location"]
- end
-
- def location=(url)
- headers["Location"] = url
- end
+ alias :response_code :status # :nodoc:
# Basic url_for that can be overridden for more robust functionality
def url_for(string)
string
end
- def status
- @_status
- end
- alias :response_code :status # :nodoc:
-
- def status=(status)
- @_status = Rack::Utils.status_code(status)
- end
-
def response_body=(body)
body = [body] unless body.nil? || body.respond_to?(:each)
+ response.body = body
super
end
# Tests if render or redirect has already happened.
def performed?
- response_body || (response && response.committed?)
+ response_body || response.committed?
end
- def dispatch(name, request) #:nodoc:
+ def dispatch(name, request, response) #:nodoc:
set_request!(request)
+ set_response!(response)
process(name)
to_a
end
+ def set_response!(response) # :nodoc:
+ @_response = response
+ end
+
def set_request!(request) #:nodoc:
@_request = request
@_request.controller_instance = self
end
def to_a #:nodoc:
- response ? response.to_a : [status, headers, response_body]
+ response.to_a
+ end
+
+ def reset_session
+ @_request.reset_session
end
class_attribute :middleware_stack
@@ -247,15 +239,32 @@ module ActionController
req = ActionDispatch::Request.new env
action(req.path_parameters[:action]).call(env)
end
+ class << self; deprecate :call; end
# Returns a Rack endpoint for the given action name.
def self.action(name)
if middleware_stack.any?
middleware_stack.build(name) do |env|
- new.dispatch(name, ActionDispatch::Request.new(env))
+ req = ActionDispatch::Request.new(env)
+ res = make_response! req
+ new.dispatch(name, req, res)
end
else
- lambda { |env| new.dispatch(name, ActionDispatch::Request.new(env)) }
+ lambda { |env|
+ req = ActionDispatch::Request.new(env)
+ res = make_response! req
+ new.dispatch(name, req, res)
+ }
+ end
+ end
+
+ # Direct dispatch to the controller. Instantiates the controller, then
+ # executes the action named +name+.
+ def self.dispatch(name, req, res)
+ if middleware_stack.any?
+ middleware_stack.build(name) { |env| new.dispatch(name, req, res) }.call req.env
+ else
+ new.dispatch(name, req, res)
end
end
end
diff --git a/actionpack/lib/action_controller/metal/conditional_get.rb b/actionpack/lib/action_controller/metal/conditional_get.rb
index bb3ad9b850..89d589c486 100644
--- a/actionpack/lib/action_controller/metal/conditional_get.rb
+++ b/actionpack/lib/action_controller/metal/conditional_get.rb
@@ -4,7 +4,6 @@ module ActionController
module ConditionalGet
extend ActiveSupport::Concern
- include RackDelegation
include Head
included do
diff --git a/actionpack/lib/action_controller/metal/cookies.rb b/actionpack/lib/action_controller/metal/cookies.rb
index d787f014cd..f8efb2b076 100644
--- a/actionpack/lib/action_controller/metal/cookies.rb
+++ b/actionpack/lib/action_controller/metal/cookies.rb
@@ -2,8 +2,6 @@ module ActionController #:nodoc:
module Cookies
extend ActiveSupport::Concern
- include RackDelegation
-
included do
helper_method :cookies
end
diff --git a/actionpack/lib/action_controller/metal/etag_with_template_digest.rb b/actionpack/lib/action_controller/metal/etag_with_template_digest.rb
index f9303efe6c..669cf55bca 100644
--- a/actionpack/lib/action_controller/metal/etag_with_template_digest.rb
+++ b/actionpack/lib/action_controller/metal/etag_with_template_digest.rb
@@ -25,7 +25,7 @@ module ActionController
class_attribute :etag_with_template_digest
self.etag_with_template_digest = true
- ActiveSupport.on_load :action_view, yield: true do |action_view_base|
+ ActiveSupport.on_load :action_view, yield: true do
etag do |options|
determine_template_etag(options) if etag_with_template_digest
end
diff --git a/actionpack/lib/action_controller/metal/head.rb b/actionpack/lib/action_controller/metal/head.rb
index f445094bdc..b2110bf946 100644
--- a/actionpack/lib/action_controller/metal/head.rb
+++ b/actionpack/lib/action_controller/metal/head.rb
@@ -28,7 +28,7 @@ module ActionController
end
status ||= :ok
-
+
location = options.delete(:location)
content_type = options.delete(:content_type)
@@ -43,12 +43,9 @@ module ActionController
if include_content?(self.response_code)
self.content_type = content_type || (Mime[formats.first] if formats)
- self.response.charset = false if self.response
- else
- headers.delete('Content-Type')
- headers.delete('Content-Length')
+ self.response.charset = false
end
-
+
true
end
diff --git a/actionpack/lib/action_controller/metal/http_authentication.rb b/actionpack/lib/action_controller/metal/http_authentication.rb
index bbb38cf8fc..15d4562abb 100644
--- a/actionpack/lib/action_controller/metal/http_authentication.rb
+++ b/actionpack/lib/action_controller/metal/http_authentication.rb
@@ -203,7 +203,7 @@ module ActionController
password = password_procedure.call(credentials[:username])
return false unless password
- method = request.env['rack.methodoverride.original_method'] || request.env['REQUEST_METHOD']
+ method = request.get_header('rack.methodoverride.original_method') || request.get_header('REQUEST_METHOD')
uri = credentials[:uri]
[true, false].any? do |trailing_question_mark|
@@ -260,8 +260,8 @@ module ActionController
end
def secret_token(request)
- key_generator = request.env["action_dispatch.key_generator"]
- http_auth_salt = request.env["action_dispatch.http_auth_salt"]
+ key_generator = request.key_generator
+ http_auth_salt = request.http_auth_salt
key_generator.generate_key(http_auth_salt)
end
diff --git a/actionpack/lib/action_controller/metal/instrumentation.rb b/actionpack/lib/action_controller/metal/instrumentation.rb
index a3e1a71b0a..3dbf34eb2a 100644
--- a/actionpack/lib/action_controller/metal/instrumentation.rb
+++ b/actionpack/lib/action_controller/metal/instrumentation.rb
@@ -11,7 +11,6 @@ module ActionController
extend ActiveSupport::Concern
include AbstractController::Logger
- include ActionController::RackDelegation
attr_internal :view_runtime
diff --git a/actionpack/lib/action_controller/metal/live.rb b/actionpack/lib/action_controller/metal/live.rb
index 58150cd9a9..69583f8ab4 100644
--- a/actionpack/lib/action_controller/metal/live.rb
+++ b/actionpack/lib/action_controller/metal/live.rb
@@ -33,6 +33,20 @@ module ActionController
# the main thread. Make sure your actions are thread safe, and this shouldn't
# be a problem (don't share state across threads, etc).
module Live
+ extend ActiveSupport::Concern
+
+ module ClassMethods
+ def make_response!(request)
+ if request.env["HTTP_VERSION"] == "HTTP/1.0"
+ super
+ else
+ Live::Response.new.tap do |res|
+ res.request = request
+ end
+ end
+ end
+ end
+
# This class provides the ability to write an SSE (Server Sent Event)
# to an IO stream. The class is initialized with a stream and can be used
# to either write a JSON string or an object which can be converted to JSON.
@@ -311,12 +325,7 @@ module ActionController
end
def set_response!(request)
- if request.env["HTTP_VERSION"] == "HTTP/1.0"
- super
- else
- @_response = Live::Response.new
- @_response.request = request
- end
+ @_response = self.class.make_response! request
end
end
end
diff --git a/actionpack/lib/action_controller/metal/mime_responds.rb b/actionpack/lib/action_controller/metal/mime_responds.rb
index 1db68db20f..e62da0fa70 100644
--- a/actionpack/lib/action_controller/metal/mime_responds.rb
+++ b/actionpack/lib/action_controller/metal/mime_responds.rb
@@ -191,6 +191,7 @@ module ActionController #:nodoc:
if format = collector.negotiate_format(request)
_process_format(format)
+ _set_content_type _get_content_type format
response = collector.response
response ? response.call : render({})
else
diff --git a/actionpack/lib/action_controller/metal/rack_delegation.rb b/actionpack/lib/action_controller/metal/rack_delegation.rb
deleted file mode 100644
index ae9d89cc8c..0000000000
--- a/actionpack/lib/action_controller/metal/rack_delegation.rb
+++ /dev/null
@@ -1,38 +0,0 @@
-require 'action_dispatch/http/request'
-require 'action_dispatch/http/response'
-
-module ActionController
- module RackDelegation
- extend ActiveSupport::Concern
-
- delegate :headers, :status=, :location=, :content_type=,
- :status, :location, :content_type, :response_code, :to => "@_response"
-
- module ClassMethods
- def build_with_env(env = {}) #:nodoc:
- new.tap { |c| c.set_request! ActionDispatch::Request.new(env) }
- end
- end
-
- def set_request!(request) #:nodoc:
- super
- set_response!(request)
- end
-
- def response_body=(body)
- response.body = body if response
- super
- end
-
- def reset_session
- @_request.reset_session
- end
-
- private
-
- def set_response!(request)
- @_response = ActionDispatch::Response.new
- @_response.request = request
- end
- end
-end
diff --git a/actionpack/lib/action_controller/metal/redirecting.rb b/actionpack/lib/action_controller/metal/redirecting.rb
index acaa8227c9..0febc905f1 100644
--- a/actionpack/lib/action_controller/metal/redirecting.rb
+++ b/actionpack/lib/action_controller/metal/redirecting.rb
@@ -11,7 +11,6 @@ module ActionController
extend ActiveSupport::Concern
include AbstractController::Logger
- include ActionController::RackDelegation
include ActionController::UrlFor
# Redirects the browser to the target specified in +options+. This parameter can be any one of:
diff --git a/actionpack/lib/action_controller/metal/rendering.rb b/actionpack/lib/action_controller/metal/rendering.rb
index a3b0645dc0..c8934b367f 100644
--- a/actionpack/lib/action_controller/metal/rendering.rb
+++ b/actionpack/lib/action_controller/metal/rendering.rb
@@ -56,14 +56,12 @@ module ActionController
nil
end
- def _process_format(format, options = {})
- super
+ def _get_content_type(rendered_format)
+ self.content_type || super
+ end
- if options[:plain]
- self.content_type = Mime::TEXT
- else
- self.content_type ||= format.to_s
- end
+ def _set_content_type(format)
+ self.content_type = format
end
# Normalize arguments by catching blocks and setting them on :update.
diff --git a/actionpack/lib/action_controller/metal/request_forgery_protection.rb b/actionpack/lib/action_controller/metal/request_forgery_protection.rb
index d21a778d8d..e5f3cb8e8d 100644
--- a/actionpack/lib/action_controller/metal/request_forgery_protection.rb
+++ b/actionpack/lib/action_controller/metal/request_forgery_protection.rb
@@ -136,7 +136,7 @@ module ActionController #:nodoc:
# This is the method that defines the application behavior when a request is found to be unverified.
def handle_unverified_request
request = @controller.request
- request.session = NullSessionHash.new(request.env)
+ request.session = NullSessionHash.new(request)
request.env['action_dispatch.request.flash_hash'] = nil
request.env['rack.session.options'] = { skip: true }
request.cookie_jar = NullCookieJar.build(request, {})
@@ -145,8 +145,8 @@ module ActionController #:nodoc:
protected
class NullSessionHash < Rack::Session::Abstract::SessionHash #:nodoc:
- def initialize(env)
- super(nil, env)
+ def initialize(req)
+ super(nil, req)
@data = {}
@loaded = true
end
diff --git a/actionpack/lib/action_controller/metal/strong_parameters.rb b/actionpack/lib/action_controller/metal/strong_parameters.rb
index fc8e345d43..bf5c7003ff 100644
--- a/actionpack/lib/action_controller/metal/strong_parameters.rb
+++ b/actionpack/lib/action_controller/metal/strong_parameters.rb
@@ -240,19 +240,58 @@ module ActionController
self
end
- # Ensures that a parameter is present. If it's present, returns
- # the parameter at the given +key+, otherwise raises an
- # <tt>ActionController::ParameterMissing</tt> error.
+ # This method accepts both a single key and an array of keys.
+ #
+ # When passed a single key, if it exists and its associated value is
+ # either present or the singleton +false+, returns said value:
#
# ActionController::Parameters.new(person: { name: 'Francesco' }).require(:person)
# # => {"name"=>"Francesco"}
#
+ # Otherwise raises <tt>ActionController::ParameterMissing</tt>:
+ #
+ # ActionController::Parameters.new.require(:person)
+ # # ActionController::ParameterMissing: param is missing or the value is empty: person
+ #
# ActionController::Parameters.new(person: nil).require(:person)
- # # => ActionController::ParameterMissing: param is missing or the value is empty: person
+ # # ActionController::ParameterMissing: param is missing or the value is empty: person
+ #
+ # ActionController::Parameters.new(person: "\t").require(:person)
+ # # ActionController::ParameterMissing: param is missing or the value is empty: person
#
# ActionController::Parameters.new(person: {}).require(:person)
- # # => ActionController::ParameterMissing: param is missing or the value is empty: person
+ # # ActionController::ParameterMissing: param is missing or the value is empty: person
+ #
+ # When given an array of keys, the method tries to require each one of them
+ # in order. If it succeeds, an array with the respective return values is
+ # returned:
+ #
+ # params = ActionController::Parameters.new(user: { ... }, profile: { ... })
+ # user_params, profile_params = params.require(:user, :profile)
+ #
+ # Otherwise, the method reraises the first exception found:
+ #
+ # params = ActionController::Parameters.new(user: {}, profile: {})
+ # user_params, profile_params = params.require(:user, :profile)
+ # # ActionController::ParameterMissing: param is missing or the value is empty: user
+ #
+ # Technically this method can be used to fetch terminal values:
+ #
+ # # CAREFUL
+ # params = ActionController::Parameters.new(person: { name: 'Finn' })
+ # name = params.require(:person).require(:name) # CAREFUL
+ #
+ # but take into account that at some point those ones have to be permitted:
+ #
+ # def person_params
+ # params.require(:person).permit(:name).tap do |person_params|
+ # person_params.require(:name) # SAFER
+ # end
+ # end
+ #
+ # for example.
def require(key)
+ return key.map { |k| require(k) } if key.is_a?(Array)
value = self[key]
if value.present? || value == false
value
diff --git a/actionpack/lib/action_controller/metal/testing.rb b/actionpack/lib/action_controller/metal/testing.rb
index d01927b7cb..47d940f692 100644
--- a/actionpack/lib/action_controller/metal/testing.rb
+++ b/actionpack/lib/action_controller/metal/testing.rb
@@ -2,8 +2,6 @@ module ActionController
module Testing
extend ActiveSupport::Concern
- include RackDelegation
-
# TODO : Rewrite tests using controller.headers= to use Rack env
def headers=(new_headers)
@_response ||= ActionDispatch::Response.new
diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb
index e012fa617e..39cbc0cd70 100644
--- a/actionpack/lib/action_controller/test_case.rb
+++ b/actionpack/lib/action_controller/test_case.rb
@@ -36,11 +36,11 @@ module ActionController
end
def query_string=(string)
- @env[Rack::QUERY_STRING] = string
+ set_header Rack::QUERY_STRING, string
end
- def request_parameters=(params)
- @env["action_dispatch.request.request_parameters"] = params
+ def content_type=(type)
+ set_header 'CONTENT_TYPE', type
end
def assign_parameters(routes, controller_path, action, parameters, generated_path, query_string_keys)
@@ -67,10 +67,12 @@ module ActionController
end
else
if ENCODER.should_multipart?(non_path_parameters)
- @env['CONTENT_TYPE'] = ENCODER.content_type
+ self.content_type = ENCODER.content_type
data = ENCODER.build_multipart non_path_parameters
else
- @env['CONTENT_TYPE'] ||= 'application/x-www-form-urlencoded'
+ get_header('CONTENT_TYPE') do |k|
+ set_header k, 'application/x-www-form-urlencoded'
+ end
# FIXME: setting `request_parametes` is normally handled by the
# params parser middleware, and we should remove this roundtripping
@@ -92,11 +94,13 @@ module ActionController
end
end
- @env['CONTENT_LENGTH'] = data.length.to_s
- @env['rack.input'] = StringIO.new(data)
+ set_header 'CONTENT_LENGTH', data.length.to_s
+ set_header 'rack.input', StringIO.new(data)
end
- @env["PATH_INFO"] ||= generated_path
+ get_header("PATH_INFO") do |k|
+ set_header k, generated_path
+ end
path_parameters[:controller] = controller_path
path_parameters[:action] = action
@@ -170,8 +174,8 @@ module ActionController
clear
end
- def fetch(*args, &block)
- @data.fetch(*args, &block)
+ def fetch(key, *args, &block)
+ @data.fetch(key.to_s, *args, &block)
end
private
@@ -450,7 +454,7 @@ module ActionController
end
if body.present?
- @request.env['RAW_POST_DATA'] = body
+ @request.set_header 'RAW_POST_DATA', body
end
if http_method.present?
@@ -472,15 +476,15 @@ module ActionController
end
self.cookies.update @request.cookies
- @request.env['HTTP_COOKIE'] = cookies.to_header
- @request.env['action_dispatch.cookies'] = nil
+ @request.set_header 'HTTP_COOKIE', cookies.to_header
+ @request.delete_header 'action_dispatch.cookies'
@request = TestRequest.new scrub_env!(@request.env), @request.session
@response = build_response @response_klass
@response.request = @request
@controller.recycle!
- @request.env['REQUEST_METHOD'] = http_method
+ @request.set_header 'REQUEST_METHOD', http_method
parameters = parameters.symbolize_keys
@@ -494,24 +498,28 @@ module ActionController
@request.flash.update(flash || {})
if xhr
- @request.env['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest'
- @request.env['HTTP_ACCEPT'] ||= [Mime::JS, Mime::HTML, Mime::XML, 'text/xml', Mime::ALL].join(', ')
+ @request.set_header 'HTTP_X_REQUESTED_WITH', 'XMLHttpRequest'
+ @request.get_header('HTTP_ACCEPT') do |k|
+ @request.set_header k, [Mime::JS, Mime::HTML, Mime::XML, 'text/xml', Mime::ALL].join(', ')
+ end
end
@controller.request = @request
@controller.response = @response
- @request.env["SCRIPT_NAME"] ||= @controller.config.relative_url_root
+ @request.get_header("SCRIPT_NAME") do |k|
+ @request.set_header k, @controller.config.relative_url_root
+ end
@controller.recycle!
@controller.process(action)
- @request.env.delete 'HTTP_COOKIE'
+ @request.delete_header 'HTTP_COOKIE'
- if cookies = @request.env['action_dispatch.cookies']
+ if @request.have_cookie_jar?
unless @response.committed?
- cookies.write(@response)
- self.cookies.update(cookies.instance_variable_get(:@cookies))
+ @request.cookie_jar.write(@response)
+ self.cookies.update(@request.cookie_jar.instance_variable_get(:@cookies))
end
end
@response.prepare!
@@ -523,8 +531,8 @@ module ActionController
end
if xhr
- @request.env.delete 'HTTP_X_REQUESTED_WITH'
- @request.env.delete 'HTTP_ACCEPT'
+ @request.delete_header 'HTTP_X_REQUESTED_WITH'
+ @request.delete_header 'HTTP_ACCEPT'
end
@request.query_string = ''
diff --git a/actionpack/lib/action_dispatch/http/cache.rb b/actionpack/lib/action_dispatch/http/cache.rb
index cc1cb3f0f0..08ebd2e8b2 100644
--- a/actionpack/lib/action_dispatch/http/cache.rb
+++ b/actionpack/lib/action_dispatch/http/cache.rb
@@ -8,13 +8,13 @@ module ActionDispatch
HTTP_IF_NONE_MATCH = 'HTTP_IF_NONE_MATCH'.freeze
def if_modified_since
- if since = env[HTTP_IF_MODIFIED_SINCE]
+ if since = get_header(HTTP_IF_MODIFIED_SINCE)
Time.rfc2822(since) rescue nil
end
end
def if_none_match
- env[HTTP_IF_NONE_MATCH]
+ get_header HTTP_IF_NONE_MATCH
end
def if_none_match_etags
@@ -51,41 +51,45 @@ module ActionDispatch
end
module Response
- attr_reader :cache_control, :etag
- alias :etag? :etag
+ attr_reader :cache_control
def last_modified
- if last = headers[LAST_MODIFIED]
+ if last = get_header(LAST_MODIFIED)
Time.httpdate(last)
end
end
def last_modified?
- headers.include?(LAST_MODIFIED)
+ have_header? LAST_MODIFIED
end
def last_modified=(utc_time)
- headers[LAST_MODIFIED] = utc_time.httpdate
+ set_header LAST_MODIFIED, utc_time.httpdate
end
def date
- if date_header = headers[DATE]
+ if date_header = get_header(DATE)
Time.httpdate(date_header)
end
end
def date?
- headers.include?(DATE)
+ have_header? DATE
end
def date=(utc_time)
- headers[DATE] = utc_time.httpdate
+ set_header DATE, utc_time.httpdate
end
def etag=(etag)
key = ActiveSupport::Cache.expand_cache_key(etag)
- @etag = self[ETAG] = %("#{Digest::MD5.hexdigest(key)}")
+ set_header ETAG, %("#{Digest::MD5.hexdigest(key)}")
+ end
+
+ def etag
+ get_header ETAG
end
+ alias :etag? :etag
private
@@ -96,7 +100,7 @@ module ActionDispatch
SPECIAL_KEYS = Set.new(%w[extras no-cache max-age public must-revalidate])
def cache_control_segments
- if cache_control = self[CACHE_CONTROL]
+ if cache_control = get_header(CACHE_CONTROL)
cache_control.delete(' ').split(',')
else
[]
@@ -123,12 +127,11 @@ module ActionDispatch
def prepare_cache_control!
@cache_control = cache_control_headers
- @etag = self[ETAG]
end
def handle_conditional_get!
if etag? || last_modified? || !@cache_control.empty?
- set_conditional_cache_control!
+ set_conditional_cache_control!(@cache_control)
end
end
@@ -138,24 +141,24 @@ module ActionDispatch
PRIVATE = "private".freeze
MUST_REVALIDATE = "must-revalidate".freeze
- def set_conditional_cache_control!
+ def set_conditional_cache_control!(cache_control)
control = {}
cc_headers = cache_control_headers
if extras = cc_headers.delete(:extras)
- @cache_control[:extras] ||= []
- @cache_control[:extras] += extras
- @cache_control[:extras].uniq!
+ cache_control[:extras] ||= []
+ cache_control[:extras] += extras
+ cache_control[:extras].uniq!
end
control.merge! cc_headers
- control.merge! @cache_control
+ control.merge! cache_control
if control.empty?
- self[CACHE_CONTROL] = DEFAULT_CACHE_CONTROL
+ set_header CACHE_CONTROL, DEFAULT_CACHE_CONTROL
elsif control[:no_cache]
- self[CACHE_CONTROL] = NO_CACHE
+ set_header CACHE_CONTROL, NO_CACHE
if control[:extras]
- self[CACHE_CONTROL] += ", #{control[:extras].join(', ')}"
+ set_header(CACHE_CONTROL, get_header(CACHE_CONTROL) + ", #{control[:extras].join(', ')}")
end
else
extras = control[:extras]
@@ -167,7 +170,7 @@ module ActionDispatch
options << MUST_REVALIDATE if control[:must_revalidate]
options.concat(extras) if extras
- self[CACHE_CONTROL] = options.join(", ")
+ set_header CACHE_CONTROL, options.join(", ")
end
end
end
diff --git a/actionpack/lib/action_dispatch/http/filter_parameters.rb b/actionpack/lib/action_dispatch/http/filter_parameters.rb
index 3170389b36..e70e90018c 100644
--- a/actionpack/lib/action_dispatch/http/filter_parameters.rb
+++ b/actionpack/lib/action_dispatch/http/filter_parameters.rb
@@ -50,13 +50,13 @@ module ActionDispatch
protected
def parameter_filter
- parameter_filter_for @env.fetch("action_dispatch.parameter_filter") {
+ parameter_filter_for get_header("action_dispatch.parameter_filter") {
return NULL_PARAM_FILTER
}
end
def env_filter
- user_key = @env.fetch("action_dispatch.parameter_filter") {
+ user_key = get_header("action_dispatch.parameter_filter") {
return NULL_ENV_FILTER
}
parameter_filter_for(Array(user_key) + ENV_MATCH)
diff --git a/actionpack/lib/action_dispatch/http/filter_redirect.rb b/actionpack/lib/action_dispatch/http/filter_redirect.rb
index 94c1f2b41f..f4b806b8b5 100644
--- a/actionpack/lib/action_dispatch/http/filter_redirect.rb
+++ b/actionpack/lib/action_dispatch/http/filter_redirect.rb
@@ -16,7 +16,7 @@ module ActionDispatch
def location_filters
if request
- request.env['action_dispatch.redirect_filter'] || []
+ request.get_header('action_dispatch.redirect_filter') || []
else
[]
end
diff --git a/actionpack/lib/action_dispatch/http/headers.rb b/actionpack/lib/action_dispatch/http/headers.rb
index bc5410dc38..fbdec6c132 100644
--- a/actionpack/lib/action_dispatch/http/headers.rb
+++ b/actionpack/lib/action_dispatch/http/headers.rb
@@ -30,27 +30,32 @@ module ActionDispatch
HTTP_HEADER = /\A[A-Za-z0-9-]+\z/
include Enumerable
- attr_reader :env
- def initialize(env = {}) # :nodoc:
- @env = env
+ def self.from_hash(hash)
+ new ActionDispatch::Request.new hash
+ end
+
+ def initialize(request) # :nodoc:
+ @req = request
end
# Returns the value for the given key mapped to @env.
def [](key)
- @env[env_name(key)]
+ @req.get_header env_name(key)
end
# Sets the given value for the key mapped to @env.
def []=(key, value)
- @env[env_name(key)] = value
+ @req.set_header env_name(key), value
end
def key?(key)
- @env.key? env_name(key)
+ @req.has_header? env_name(key)
end
alias :include? :key?
+ DEFAULT = Object.new # :nodoc:
+
# Returns the value for the given key mapped to @env.
#
# If the key is not found and an optional code block is not provided,
@@ -58,18 +63,22 @@ module ActionDispatch
#
# If the code block is provided, then it will be run and
# its result returned.
- def fetch(key, *args, &block)
- @env.fetch env_name(key), *args, &block
+ def fetch(key, default = DEFAULT)
+ @req.get_header(env_name(key)) do
+ return default unless default == DEFAULT
+ return yield if block_given?
+ raise NameError, key
+ end
end
def each(&block)
- @env.each(&block)
+ @req.each_header(&block)
end
# Returns a new Http::Headers instance containing the contents of
# <tt>headers_or_env</tt> and the original instance.
def merge(headers_or_env)
- headers = Http::Headers.new(env.dup)
+ headers = @req.dup.headers
headers.merge!(headers_or_env)
headers
end
@@ -79,11 +88,14 @@ module ActionDispatch
# <tt>headers_or_env</tt>.
def merge!(headers_or_env)
headers_or_env.each do |key, value|
- self[env_name(key)] = value
+ @req.set_header env_name(key), value
end
end
+ def env; @req.env.dup; end
+
private
+
# Converts a HTTP header name to an environment variable name if it is
# not contained within the headers hash.
def env_name(key)
diff --git a/actionpack/lib/action_dispatch/http/mime_negotiation.rb b/actionpack/lib/action_dispatch/http/mime_negotiation.rb
index ff336b7354..e01d5ecc8f 100644
--- a/actionpack/lib/action_dispatch/http/mime_negotiation.rb
+++ b/actionpack/lib/action_dispatch/http/mime_negotiation.rb
@@ -15,12 +15,13 @@ module ActionDispatch
# For backward compatibility, the post \format is extracted from the
# X-Post-Data-Format HTTP header if present.
def content_mime_type
- @env["action_dispatch.request.content_type"] ||= begin
- if @env['CONTENT_TYPE'] =~ /^([^,\;]*)/
+ get_header("action_dispatch.request.content_type") do |k|
+ v = if get_header('CONTENT_TYPE') =~ /^([^,\;]*)/
Mime::Type.lookup($1.strip.downcase)
else
nil
end
+ set_header k, v
end
end
@@ -30,14 +31,15 @@ module ActionDispatch
# Returns the accepted MIME type for the request.
def accepts
- @env["action_dispatch.request.accepts"] ||= begin
- header = @env['HTTP_ACCEPT'].to_s.strip
+ get_header("action_dispatch.request.accepts") do |k|
+ header = get_header('HTTP_ACCEPT').to_s.strip
- if header.empty?
+ v = if header.empty?
[content_mime_type]
else
Mime::Type.parse(header)
end
+ set_header k, v
end
end
@@ -52,14 +54,14 @@ module ActionDispatch
end
def formats
- @env["action_dispatch.request.formats"] ||= begin
+ get_header("action_dispatch.request.formats") do |k|
params_readable = begin
parameters[:format]
rescue ActionController::BadRequest
false
end
- if params_readable
+ v = if params_readable
Array(Mime[parameters[:format]])
elsif use_accept_header && valid_accept_header
accepts
@@ -68,6 +70,7 @@ module ActionDispatch
else
[Mime::HTML]
end
+ set_header k, v
end
end
@@ -102,7 +105,7 @@ module ActionDispatch
# end
def format=(extension)
parameters[:format] = extension.to_s
- @env["action_dispatch.request.formats"] = [Mime::Type.lookup_by_extension(parameters[:format])]
+ set_header "action_dispatch.request.formats", [Mime::Type.lookup_by_extension(parameters[:format])]
end
# Sets the \formats by string extensions. This differs from #format= by allowing you
@@ -121,9 +124,9 @@ module ActionDispatch
# end
def formats=(extensions)
parameters[:format] = extensions.first.to_s
- @env["action_dispatch.request.formats"] = extensions.collect do |extension|
+ set_header "action_dispatch.request.formats", extensions.collect { |extension|
Mime::Type.lookup_by_extension(extension)
- end
+ }
end
# Receives an array of mimes and return the first user sent mime that
diff --git a/actionpack/lib/action_dispatch/http/parameters.rb b/actionpack/lib/action_dispatch/http/parameters.rb
index 4defb7f858..3c9f8cd9e4 100644
--- a/actionpack/lib/action_dispatch/http/parameters.rb
+++ b/actionpack/lib/action_dispatch/http/parameters.rb
@@ -1,6 +1,3 @@
-require 'active_support/core_ext/hash/keys'
-require 'active_support/core_ext/hash/indifferent_access'
-
module ActionDispatch
module Http
module Parameters
@@ -8,20 +5,23 @@ module ActionDispatch
# Returns both GET and POST \parameters in a single hash.
def parameters
- @env["action_dispatch.request.parameters"] ||= begin
- params = begin
- request_parameters.merge(query_parameters)
- rescue EOFError
- query_parameters.dup
- end
- params.merge!(path_parameters)
- end
+ params = get_header("action_dispatch.request.parameters")
+ return params if params
+
+ params = begin
+ request_parameters.merge(query_parameters)
+ rescue EOFError
+ query_parameters.dup
+ end
+ params.merge!(path_parameters)
+ set_header("action_dispatch.request.parameters", params)
+ params
end
alias :params :parameters
def path_parameters=(parameters) #:nodoc:
- @env.delete('action_dispatch.request.parameters')
- @env[PARAMETERS_KEY] = parameters
+ delete_header('action_dispatch.request.parameters')
+ set_header PARAMETERS_KEY, parameters
end
# Returns a hash with the \parameters used to form the \path of the request.
@@ -29,15 +29,7 @@ module ActionDispatch
#
# {'action' => 'my_action', 'controller' => 'my_controller'}
def path_parameters
- @env[PARAMETERS_KEY] ||= {}
- end
-
- private
-
- # Convert nested Hash to HashWithIndifferentAccess.
- #
- def normalize_encode_params(params)
- ActionDispatch::Request::Utils.normalize_encode_params params
+ get_header(PARAMETERS_KEY) || {}
end
end
end
diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb
index de28cd0998..45600d0a61 100644
--- a/actionpack/lib/action_dispatch/http/request.rb
+++ b/actionpack/lib/action_dispatch/http/request.rb
@@ -20,8 +20,6 @@ module ActionDispatch
include ActionDispatch::Http::FilterParameters
include ActionDispatch::Http::URL
- HTTP_X_REQUEST_ID = "HTTP_X_REQUEST_ID".freeze # :nodoc:
-
autoload :Session, 'action_dispatch/request/session'
autoload :Utils, 'action_dispatch/request/utils'
@@ -31,17 +29,20 @@ module ActionDispatch
PATH_TRANSLATED REMOTE_HOST
REMOTE_IDENT REMOTE_USER REMOTE_ADDR
SERVER_NAME SERVER_PROTOCOL
+ ORIGINAL_SCRIPT_NAME
HTTP_ACCEPT HTTP_ACCEPT_CHARSET HTTP_ACCEPT_ENCODING
HTTP_ACCEPT_LANGUAGE HTTP_CACHE_CONTROL HTTP_FROM
HTTP_NEGOTIATE HTTP_PRAGMA HTTP_CLIENT_IP
HTTP_X_FORWARDED_FOR HTTP_VERSION
+ HTTP_X_REQUEST_ID HTTP_X_FORWARDED_HOST
+ SERVER_ADDR
].freeze
ENV_METHODS.each do |env|
class_eval <<-METHOD, __FILE__, __LINE__ + 1
def #{env.sub(/^HTTP_/n, '').downcase} # def accept_charset
- @env["#{env}".freeze] # @env["HTTP_ACCEPT_CHARSET".freeze]
+ get_header "#{env}".freeze # get_header "HTTP_ACCEPT_CHARSET".freeze
end # end
METHOD
end
@@ -67,8 +68,27 @@ module ActionDispatch
end
end
+ PASS_NOT_FOUND = Class.new { # :nodoc:
+ def self.action(_); self; end
+ def self.call(_); [404, {'X-Cascade' => 'pass'}, []]; end
+ }
+
+ def controller_class
+ check_path_parameters!
+ params = path_parameters
+
+ if params.key?(:controller)
+ controller_param = params[:controller].underscore
+ params[:action] ||= 'index'
+ const_name = "#{controller_param.camelize}Controller"
+ ActiveSupport::Dependencies.constantize(const_name)
+ else
+ PASS_NOT_FOUND
+ end
+ end
+
def key?(key)
- @env.key?(key)
+ has_header? key
end
# List of HTTP request methods from the following RFCs:
@@ -109,44 +129,44 @@ module ActionDispatch
end
def routes # :nodoc:
- env["action_dispatch.routes".freeze]
+ get_header("action_dispatch.routes".freeze)
end
def routes=(routes) # :nodoc:
- env["action_dispatch.routes".freeze] = routes
- end
-
- def original_script_name # :nodoc:
- env['ORIGINAL_SCRIPT_NAME'.freeze]
+ set_header("action_dispatch.routes".freeze, routes)
end
def engine_script_name(_routes) # :nodoc:
- env[_routes.env_key]
+ get_header(_routes.env_key)
end
def engine_script_name=(name) # :nodoc:
- env[routes.env_key] = name.dup
+ set_header(routes.env_key, name.dup)
end
def request_method=(request_method) #:nodoc:
if check_method(request_method)
- @request_method = env["REQUEST_METHOD"] = request_method
+ @request_method = set_header("REQUEST_METHOD", request_method)
end
end
def controller_instance # :nodoc:
- env['action_controller.instance'.freeze]
+ get_header('action_controller.instance'.freeze)
end
def controller_instance=(controller) # :nodoc:
- env['action_controller.instance'.freeze] = controller
+ set_header('action_controller.instance'.freeze, controller)
+ end
+
+ def http_auth_salt
+ get_header "action_dispatch.http_auth_salt"
end
def show_exceptions? # :nodoc:
# We're treating `nil` as "unset", and we want the default setting to be
# `true`. This logic should be extracted to `env_config` and calculated
# once.
- !(env['action_dispatch.show_exceptions'.freeze] == false)
+ !(get_header('action_dispatch.show_exceptions'.freeze) == false)
end
# Returns a symbol form of the #request_method
@@ -158,7 +178,7 @@ module ActionDispatch
# even if it was overridden by middleware. See #request_method for
# more information.
def method
- @method ||= check_method(env["rack.methodoverride.original_method"] || env['REQUEST_METHOD'])
+ @method ||= check_method(get_header("rack.methodoverride.original_method") || get_header('REQUEST_METHOD'))
end
# Returns a symbol form of the #method
@@ -170,7 +190,7 @@ module ActionDispatch
#
# request.headers["Content-Type"] # => "text/plain"
def headers
- @headers ||= Http::Headers.new(@env)
+ @headers ||= Http::Headers.new(self)
end
# Returns a +String+ with the last requested path including their params.
@@ -181,7 +201,7 @@ module ActionDispatch
# # get '/foo?bar'
# request.original_fullpath # => '/foo?bar'
def original_fullpath
- @original_fullpath ||= (env["ORIGINAL_FULLPATH"] || fullpath)
+ @original_fullpath ||= (get_header("ORIGINAL_FULLPATH") || fullpath)
end
# Returns the +String+ full path including params of the last URL requested.
@@ -220,7 +240,7 @@ module ActionDispatch
# (case-insensitive), which may need to be manually added depending on the
# choice of JavaScript libraries and frameworks.
def xml_http_request?
- @env['HTTP_X_REQUESTED_WITH'] =~ /XMLHttpRequest/i
+ get_header('HTTP_X_REQUESTED_WITH') =~ /XMLHttpRequest/i
end
alias :xhr? :xml_http_request?
@@ -232,11 +252,11 @@ module ActionDispatch
# Returns the IP address of client as a +String+,
# usually set by the RemoteIp middleware.
def remote_ip
- @remote_ip ||= (@env["action_dispatch.remote_ip"] || ip).to_s
+ @remote_ip ||= (get_header("action_dispatch.remote_ip") || ip).to_s
end
def remote_ip=(remote_ip)
- @env["action_dispatch.remote_ip".freeze] = remote_ip
+ set_header "action_dispatch.remote_ip".freeze, remote_ip
end
ACTION_DISPATCH_REQUEST_ID = "action_dispatch.request_id".freeze # :nodoc:
@@ -248,43 +268,39 @@ module ActionDispatch
# This unique ID is useful for tracing a request from end-to-end as part of logging or debugging.
# This relies on the rack variable set by the ActionDispatch::RequestId middleware.
def request_id
- env[ACTION_DISPATCH_REQUEST_ID]
+ get_header ACTION_DISPATCH_REQUEST_ID
end
def request_id=(id) # :nodoc:
- env[ACTION_DISPATCH_REQUEST_ID] = id
+ set_header ACTION_DISPATCH_REQUEST_ID, id
end
alias_method :uuid, :request_id
- def x_request_id # :nodoc:
- @env[HTTP_X_REQUEST_ID]
- end
-
# Returns the lowercase name of the HTTP server software.
def server_software
- (@env['SERVER_SOFTWARE'] && /^([a-zA-Z]+)/ =~ @env['SERVER_SOFTWARE']) ? $1.downcase : nil
+ (get_header('SERVER_SOFTWARE') && /^([a-zA-Z]+)/ =~ get_header('SERVER_SOFTWARE')) ? $1.downcase : nil
end
# Read the request \body. This is useful for web services that need to
# work with raw requests directly.
def raw_post
- unless @env.include? 'RAW_POST_DATA'
+ unless has_header? 'RAW_POST_DATA'
raw_post_body = body
- @env['RAW_POST_DATA'] = raw_post_body.read(content_length)
+ set_header('RAW_POST_DATA', raw_post_body.read(content_length))
raw_post_body.rewind if raw_post_body.respond_to?(:rewind)
end
- @env['RAW_POST_DATA']
+ get_header 'RAW_POST_DATA'
end
# The request body is an IO input stream. If the RAW_POST_DATA environment
# variable is already set, wrap it in a StringIO.
def body
- if raw_post = @env['RAW_POST_DATA']
+ if raw_post = get_header('RAW_POST_DATA')
raw_post.force_encoding(Encoding::BINARY)
StringIO.new(raw_post)
else
- @env['rack.input']
+ body_stream
end
end
@@ -295,7 +311,7 @@ module ActionDispatch
end
def body_stream #:nodoc:
- @env['rack.input']
+ get_header('rack.input')
end
# TODO This should be broken apart into AD::Request::Session and probably
@@ -306,20 +322,22 @@ module ActionDispatch
else
self.session = {}
end
- @env['action_dispatch.request.flash_hash'] = nil
+ set_header('action_dispatch.request.flash_hash', nil)
end
def session=(session) #:nodoc:
- Session.set @env, session
+ Session.set self, session
end
def session_options=(options)
- Session::Options.set @env, options
+ Session::Options.set self, options
end
# Override Rack's GET method to support indifferent access
def GET
- @env["action_dispatch.request.query_parameters"] ||= normalize_encode_params(super || {})
+ get_header("action_dispatch.request.query_parameters") do |k|
+ set_header k, Request::Utils.normalize_encode_params(super || {})
+ end
rescue Rack::Utils::ParameterTypeError, Rack::Utils::InvalidParameterError => e
raise ActionController::BadRequest.new(:query, e)
end
@@ -327,7 +345,9 @@ module ActionDispatch
# Override Rack's POST method to support indifferent access
def POST
- @env["action_dispatch.request.request_parameters"] ||= normalize_encode_params(super || {})
+ get_header("action_dispatch.request.request_parameters") do
+ self.request_parameters = Request::Utils.normalize_encode_params(super || {})
+ end
rescue Rack::Utils::ParameterTypeError, Rack::Utils::InvalidParameterError => e
raise ActionController::BadRequest.new(:request, e)
end
@@ -336,10 +356,10 @@ module ActionDispatch
# Returns the authorization header regardless of whether it was specified directly or through one of the
# proxy alternatives.
def authorization
- @env['HTTP_AUTHORIZATION'] ||
- @env['X-HTTP_AUTHORIZATION'] ||
- @env['X_HTTP_AUTHORIZATION'] ||
- @env['REDIRECT_X_HTTP_AUTHORIZATION']
+ get_header('HTTP_AUTHORIZATION') ||
+ get_header('X-HTTP_AUTHORIZATION') ||
+ get_header('X_HTTP_AUTHORIZATION') ||
+ get_header('REDIRECT_X_HTTP_AUTHORIZATION')
end
# True if the request came from localhost, 127.0.0.1.
@@ -348,11 +368,12 @@ module ActionDispatch
end
def request_parameters=(params)
- env["action_dispatch.request.request_parameters".freeze] = params
+ raise if params.nil?
+ set_header("action_dispatch.request.request_parameters".freeze, params)
end
def logger
- env["action_dispatch.logger".freeze]
+ get_header("action_dispatch.logger".freeze)
end
private
diff --git a/actionpack/lib/action_dispatch/http/response.rb b/actionpack/lib/action_dispatch/http/response.rb
index fd92e89231..4aee489912 100644
--- a/actionpack/lib/action_dispatch/http/response.rb
+++ b/actionpack/lib/action_dispatch/http/response.rb
@@ -65,7 +65,7 @@ module ActionDispatch # :nodoc:
CONTENT_TYPE = "Content-Type".freeze
SET_COOKIE = "Set-Cookie".freeze
LOCATION = "Location".freeze
- NO_CONTENT_CODES = [204, 304]
+ NO_CONTENT_CODES = [100, 101, 102, 204, 205, 304]
cattr_accessor(:default_charset) { "utf-8" }
cattr_accessor(:default_headers)
@@ -150,6 +150,11 @@ module ActionDispatch # :nodoc:
yield self if block_given?
end
+ def have_header?(key); headers.key? key; end
+ def get_header(key); headers[key]; end
+ def set_header(key, v); headers[key] = v; end
+ def delete_header(key); headers.delete key; end
+
def await_commit
synchronize do
@cv.wait_until { @committed }
@@ -256,25 +261,9 @@ module ActionDispatch # :nodoc:
parts
end
- def set_cookie(key, value)
- ::Rack::Utils.set_cookie_header!(header, key, value)
- end
-
- def delete_cookie(key, value={})
- ::Rack::Utils.delete_cookie_header!(header, key, value)
- end
-
# The location header we'll be responding with.
- def location
- headers[LOCATION]
- end
alias_method :redirect_url, :location
- # Sets the location header we'll be responding with.
- def location=(url)
- headers[LOCATION] = url
- end
-
def close
stream.close if stream.respond_to?(:close)
end
@@ -305,7 +294,7 @@ module ActionDispatch # :nodoc:
# assert_equal 'AuthorOfNewPage', r.cookies['author']
def cookies
cookies = {}
- if header = self[SET_COOKIE]
+ if header = get_header(SET_COOKIE)
header = header.split("\n") if header.respond_to?(:to_str)
header.each do |cookie|
if pair = cookie.split(';').first
@@ -341,14 +330,14 @@ module ActionDispatch # :nodoc:
end
def assign_default_content_type_and_charset!
- return if self[CONTENT_TYPE].present?
+ return if get_header(CONTENT_TYPE).present?
@content_type ||= Mime::HTML
type = @content_type.to_s.dup
type << "; charset=#{charset}" if append_charset?
- self[CONTENT_TYPE] = type
+ set_header CONTENT_TYPE, type
end
def append_charset?
@@ -392,10 +381,9 @@ module ActionDispatch # :nodoc:
end
def rack_response(status, header)
- header[SET_COOKIE] = header[SET_COOKIE].join("\n") if header[SET_COOKIE].respond_to?(:join)
-
if NO_CONTENT_CODES.include?(@status)
header.delete CONTENT_TYPE
+ header.delete 'Content-Length'
[status, header, []]
else
[status, header, RackBody.new(self)]
diff --git a/actionpack/lib/action_dispatch/http/url.rb b/actionpack/lib/action_dispatch/http/url.rb
index 6fcf49030b..e413954066 100644
--- a/actionpack/lib/action_dispatch/http/url.rb
+++ b/actionpack/lib/action_dispatch/http/url.rb
@@ -229,10 +229,10 @@ module ActionDispatch
# req = Request.new 'HTTP_HOST' => 'example.com:8080'
# req.raw_host_with_port # => "example.com:8080"
def raw_host_with_port
- if forwarded = env["HTTP_X_FORWARDED_HOST"].presence
+ if forwarded = x_forwarded_host.presence
forwarded.split(/,\s?/).last
else
- env['HTTP_HOST'] || "#{env['SERVER_NAME'] || env['SERVER_ADDR']}:#{env['SERVER_PORT']}"
+ get_header('HTTP_HOST') || "#{server_name || server_addr}:#{get_header('SERVER_PORT')}"
end
end
@@ -348,7 +348,7 @@ module ActionDispatch
end
def server_port
- @env['SERVER_PORT'].to_i
+ get_header('SERVER_PORT').to_i
end
# Returns the \domain part of a \host, such as "rubyonrails.org" in "www.rubyonrails.org". You can specify
diff --git a/actionpack/lib/action_dispatch/journey/nodes/node.rb b/actionpack/lib/action_dispatch/journey/nodes/node.rb
index 542e8c6577..d069bf0205 100644
--- a/actionpack/lib/action_dispatch/journey/nodes/node.rb
+++ b/actionpack/lib/action_dispatch/journey/nodes/node.rb
@@ -41,6 +41,7 @@ module ActionDispatch
def literal?; false; end
def terminal?; false; end
def star?; false; end
+ def cat?; false; end
end
class Terminal < Node # :nodoc:
@@ -72,11 +73,13 @@ module ActionDispatch
class Symbol < Terminal # :nodoc:
attr_accessor :regexp
alias :symbol :regexp
+ attr_reader :name
DEFAULT_EXP = /[^\.\/\?]+/
def initialize(left)
super
@regexp = DEFAULT_EXP
+ @name = left.tr '*:'.freeze, ''.freeze
end
def default_regexp?
@@ -115,6 +118,7 @@ module ActionDispatch
end
class Cat < Binary # :nodoc:
+ def cat?; true; end
def type; :CAT; end
end
diff --git a/actionpack/lib/action_dispatch/journey/route.rb b/actionpack/lib/action_dispatch/journey/route.rb
index 209cd20d80..f5c9abf1cc 100644
--- a/actionpack/lib/action_dispatch/journey/route.rb
+++ b/actionpack/lib/action_dispatch/journey/route.rb
@@ -1,13 +1,11 @@
module ActionDispatch
module Journey # :nodoc:
class Route # :nodoc:
- attr_reader :app, :path, :defaults, :name
+ attr_reader :app, :path, :defaults, :name, :precedence
attr_reader :constraints
alias :conditions :constraints
- attr_accessor :precedence
-
module VerbMatchers
VERBS = %w{ DELETE GET HEAD OPTIONS LINK PATCH POST PUT TRACE UNLINK }
VERBS.each do |v|
@@ -51,13 +49,13 @@ module ActionDispatch
def self.build(name, app, path, constraints, required_defaults, defaults)
request_method_match = verb_matcher(constraints.delete(:request_method))
- new name, app, path, constraints, required_defaults, defaults, request_method_match
+ new name, app, path, constraints, required_defaults, defaults, request_method_match, 0
end
##
# +path+ is a path constraint.
# +constraints+ is a hash of constraints to be applied to this route.
- def initialize(name, app, path, constraints, required_defaults, defaults, request_method_match)
+ def initialize(name, app, path, constraints, required_defaults, defaults, request_method_match, precedence)
@name = name
@app = app
@path = path
@@ -70,7 +68,7 @@ module ActionDispatch
@required_parts = nil
@parts = nil
@decorated_ast = nil
- @precedence = 0
+ @precedence = precedence
@path_formatter = @path.build_formatter
end
diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb
index cf4f654ed6..6d0387cf74 100644
--- a/actionpack/lib/action_dispatch/middleware/cookies.rb
+++ b/actionpack/lib/action_dispatch/middleware/cookies.rb
@@ -1,6 +1,4 @@
require 'active_support/core_ext/hash/keys'
-require 'active_support/core_ext/module/attribute_accessors'
-require 'active_support/core_ext/object/blank'
require 'active_support/key_generator'
require 'active_support/message_verifier'
require 'active_support/json'
@@ -8,48 +6,50 @@ require 'active_support/json'
module ActionDispatch
class Request < Rack::Request
def cookie_jar
- env['action_dispatch.cookies'.freeze] ||= Cookies::CookieJar.build(self, cookies)
+ get_header('action_dispatch.cookies'.freeze) do
+ self.cookie_jar = Cookies::CookieJar.build(self, cookies)
+ end
end
# :stopdoc:
def have_cookie_jar?
- env.key? 'action_dispatch.cookies'.freeze
+ has_header? 'action_dispatch.cookies'.freeze
end
def cookie_jar=(jar)
- env['action_dispatch.cookies'.freeze] = jar
+ set_header 'action_dispatch.cookies'.freeze, jar
end
def key_generator
- env[Cookies::GENERATOR_KEY]
+ get_header Cookies::GENERATOR_KEY
end
def signed_cookie_salt
- env[Cookies::SIGNED_COOKIE_SALT]
+ get_header Cookies::SIGNED_COOKIE_SALT
end
def encrypted_cookie_salt
- env[Cookies::ENCRYPTED_COOKIE_SALT]
+ get_header Cookies::ENCRYPTED_COOKIE_SALT
end
def encrypted_signed_cookie_salt
- env[Cookies::ENCRYPTED_SIGNED_COOKIE_SALT]
+ get_header Cookies::ENCRYPTED_SIGNED_COOKIE_SALT
end
def secret_token
- env[Cookies::SECRET_TOKEN]
+ get_header Cookies::SECRET_TOKEN
end
def secret_key_base
- env[Cookies::SECRET_KEY_BASE]
+ get_header Cookies::SECRET_KEY_BASE
end
def cookies_serializer
- env[Cookies::COOKIES_SERIALIZER]
+ get_header Cookies::COOKIES_SERIALIZER
end
def cookies_digest
- env[Cookies::COOKIES_DIGEST]
+ get_header Cookies::COOKIES_DIGEST
end
# :startdoc:
end
diff --git a/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb b/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb
index 226a688fe2..66bb74b9c5 100644
--- a/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb
+++ b/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb
@@ -55,18 +55,17 @@ module ActionDispatch
response
rescue Exception => exception
raise exception unless request.show_exceptions?
- render_exception(env, exception)
+ render_exception(request, exception)
end
private
- def render_exception(env, exception)
- backtrace_cleaner = env['action_dispatch.backtrace_cleaner']
+ def render_exception(request, exception)
+ backtrace_cleaner = request.get_header('action_dispatch.backtrace_cleaner')
wrapper = ExceptionWrapper.new(backtrace_cleaner, exception)
- log_error(env, wrapper)
+ log_error(request, wrapper)
- if env['action_dispatch.show_detailed_exceptions']
- request = Request.new(env)
+ if request.get_header('action_dispatch.show_detailed_exceptions')
traces = wrapper.traces
trace_to_show = 'Application Trace'
@@ -108,8 +107,8 @@ module ActionDispatch
[status, {'Content-Type' => "#{format}; charset=#{Response.default_charset}", 'Content-Length' => body.bytesize.to_s}, [body]]
end
- def log_error(env, wrapper)
- logger = logger(env)
+ def log_error(request, wrapper)
+ logger = logger(request)
return unless logger
exception = wrapper.exception
@@ -125,8 +124,8 @@ module ActionDispatch
end
end
- def logger(env)
- env['action_dispatch.logger'] || stderr_logger
+ def logger(request)
+ request.logger || stderr_logger
end
def stderr_logger
diff --git a/actionpack/lib/action_dispatch/middleware/flash.rb b/actionpack/lib/action_dispatch/middleware/flash.rb
index 23da169b22..6041f84834 100644
--- a/actionpack/lib/action_dispatch/middleware/flash.rb
+++ b/actionpack/lib/action_dispatch/middleware/flash.rb
@@ -6,15 +6,17 @@ module ActionDispatch
# read a notice you put there or <tt>flash["notice"] = "hello"</tt>
# to put a new one.
def flash
- @env[Flash::KEY] ||= Flash::FlashHash.from_session_value(session["flash"])
+ flash = flash_hash
+ return flash if flash
+ self.flash = Flash::FlashHash.from_session_value(session["flash"])
end
def flash=(flash)
- @env[Flash::KEY] = flash
+ set_header Flash::KEY, flash
end
def flash_hash # :nodoc:
- @env[Flash::KEY]
+ get_header Flash::KEY
end
end
@@ -274,7 +276,7 @@ module ActionDispatch
req = ActionDispatch::Request.new env
@app.call(env)
ensure
- session = Request::Session.find(env) || {}
+ session = Request::Session.find(req) || {}
flash_hash = req.flash_hash
if flash_hash && (flash_hash.present? || session.key?('flash'))
diff --git a/actionpack/lib/action_dispatch/middleware/params_parser.rb b/actionpack/lib/action_dispatch/middleware/params_parser.rb
index 402ad778fa..9cde9c9b98 100644
--- a/actionpack/lib/action_dispatch/middleware/params_parser.rb
+++ b/actionpack/lib/action_dispatch/middleware/params_parser.rb
@@ -37,7 +37,9 @@ module ActionDispatch
def call(env)
request = Request.new(env)
- request.request_parameters = parse_formatted_parameters(request, @parsers)
+ parse_formatted_parameters(request, @parsers) do |params|
+ request.request_parameters = params
+ end
@app.call(env)
end
@@ -48,7 +50,7 @@ module ActionDispatch
strategy = parsers.fetch(request.content_mime_type) { return nil }
- strategy.call(request.raw_post)
+ yield strategy.call(request.raw_post)
rescue => e # JSON or Ruby code block errors
logger(request).debug "Error occurred while parsing request parameters.\nContents:\n\n#{request.raw_post}"
diff --git a/actionpack/lib/action_dispatch/middleware/public_exceptions.rb b/actionpack/lib/action_dispatch/middleware/public_exceptions.rb
index 7cde76b30e..0f27984550 100644
--- a/actionpack/lib/action_dispatch/middleware/public_exceptions.rb
+++ b/actionpack/lib/action_dispatch/middleware/public_exceptions.rb
@@ -17,8 +17,8 @@ module ActionDispatch
end
def call(env)
- status = env["PATH_INFO"][1..-1].to_i
request = ActionDispatch::Request.new(env)
+ status = request.path_info[1..-1].to_i
content_type = request.formats.first
body = { :status => status, :error => Rack::Utils::HTTP_STATUS_CODES.fetch(status, Rack::Utils::HTTP_STATUS_CODES[500]) }
diff --git a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb
index 84df55fd5a..b924df789f 100644
--- a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb
+++ b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb
@@ -36,6 +36,11 @@ module ActionDispatch
@default_options.delete(:sidbits)
@default_options.delete(:secure_random)
end
+
+ private
+ def make_request(env)
+ ActionDispatch::Request.new env
+ end
end
module StaleSessionCheck
@@ -65,8 +70,8 @@ module ActionDispatch
end
module SessionObject # :nodoc:
- def prepare_session(env)
- Request::Session.create(self, env, @default_options)
+ def prepare_session(req)
+ Request::Session.create(self, req, @default_options)
end
def loaded_session?(session)
@@ -81,8 +86,7 @@ module ActionDispatch
private
- def set_cookie(env, session_id, cookie)
- request = ActionDispatch::Request.new(env)
+ def set_cookie(request, session_id, cookie)
request.cookie_jar[key] = cookie
end
end
diff --git a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb
index d8f9614904..e225f356df 100644
--- a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb
+++ b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb
@@ -71,16 +71,16 @@ module ActionDispatch
super(app, options.merge!(:cookie_only => true))
end
- def destroy_session(env, session_id, options)
+ def destroy_session(req, session_id, options)
new_sid = generate_sid unless options[:drop]
# Reset hash and Assign the new session id
- env["action_dispatch.request.unsigned_session_cookie"] = new_sid ? { "session_id" => new_sid } : {}
+ req.set_header("action_dispatch.request.unsigned_session_cookie", new_sid ? { "session_id" => new_sid } : {})
new_sid
end
- def load_session(env)
+ def load_session(req)
stale_session_check! do
- data = unpacked_cookie_data(env)
+ data = unpacked_cookie_data(req)
data = persistent_session_id!(data)
[data["session_id"], data]
end
@@ -88,20 +88,21 @@ module ActionDispatch
private
- def extract_session_id(env)
+ def extract_session_id(req)
stale_session_check! do
- unpacked_cookie_data(env)["session_id"]
+ unpacked_cookie_data(req)["session_id"]
end
end
- def unpacked_cookie_data(env)
- env["action_dispatch.request.unsigned_session_cookie"] ||= begin
- stale_session_check! do
- if data = get_cookie(env)
+ def unpacked_cookie_data(req)
+ req.get_header("action_dispatch.request.unsigned_session_cookie") do |k|
+ v = stale_session_check! do
+ if data = get_cookie(req)
data.stringify_keys!
end
data || {}
end
+ req.set_header k, v
end
end
@@ -111,21 +112,20 @@ module ActionDispatch
data
end
- def set_session(env, sid, session_data, options)
+ def set_session(req, sid, session_data, options)
session_data["session_id"] = sid
session_data
end
- def set_cookie(env, session_id, cookie)
- cookie_jar(env)[@key] = cookie
+ def set_cookie(request, session_id, cookie)
+ cookie_jar(request)[@key] = cookie
end
- def get_cookie(env)
- cookie_jar(env)[@key]
+ def get_cookie(req)
+ cookie_jar(req)[@key]
end
- def cookie_jar(env)
- request = ActionDispatch::Request.new(env)
+ def cookie_jar(request)
request.cookie_jar.signed_or_encrypted
end
end
diff --git a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
index 12d8dab8eb..64695f9738 100644
--- a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
+++ b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
@@ -31,7 +31,7 @@ module ActionDispatch
@app.call(env)
rescue Exception => exception
if request.show_exceptions?
- render_exception(env, exception)
+ render_exception(request, exception)
else
raise exception
end
@@ -39,14 +39,14 @@ module ActionDispatch
private
- def render_exception(env, exception)
- backtrace_cleaner = env['action_dispatch.backtrace_cleaner']
+ def render_exception(request, exception)
+ backtrace_cleaner = request.get_header 'action_dispatch.backtrace_cleaner'
wrapper = ExceptionWrapper.new(backtrace_cleaner, exception)
status = wrapper.status_code
- env["action_dispatch.exception"] = wrapper.exception
- env["action_dispatch.original_path"] = env["PATH_INFO"]
- env["PATH_INFO"] = "/#{status}"
- response = @exceptions_app.call(env)
+ request.set_header "action_dispatch.exception", wrapper.exception
+ request.set_header "action_dispatch.original_path", request.path_info
+ request.path_info = "/#{status}"
+ response = @exceptions_app.call(request.env)
response[1]['X-Cascade'] == 'pass' ? pass_response(status) : response
rescue Exception => failsafe_error
$stderr.puts "Error during failsafe response: #{failsafe_error}\n #{failsafe_error.backtrace * "\n "}"
diff --git a/actionpack/lib/action_dispatch/middleware/stack.rb b/actionpack/lib/action_dispatch/middleware/stack.rb
index 0430ce3b9a..90e2ae6802 100644
--- a/actionpack/lib/action_dispatch/middleware/stack.rb
+++ b/actionpack/lib/action_dispatch/middleware/stack.rb
@@ -87,7 +87,7 @@ module ActionDispatch
middlewares.freeze.reverse.inject(app) { |a, e| e.build(a) }
end
- protected
+ private
def assert_index(index, where)
index = get_class index
@@ -96,8 +96,6 @@ module ActionDispatch
i
end
- private
-
def get_class(klass)
if klass.is_a?(String) || klass.is_a?(Symbol)
classcache = ActiveSupport::Dependencies::Reference
diff --git a/actionpack/lib/action_dispatch/request/session.rb b/actionpack/lib/action_dispatch/request/session.rb
index a8a3cd20b9..b946ccb49f 100644
--- a/actionpack/lib/action_dispatch/request/session.rb
+++ b/actionpack/lib/action_dispatch/request/session.rb
@@ -4,38 +4,38 @@ module ActionDispatch
class Request < Rack::Request
# Session is responsible for lazily loading the session from store.
class Session # :nodoc:
- ENV_SESSION_KEY = Rack::Session::Abstract::ENV_SESSION_KEY # :nodoc:
- ENV_SESSION_OPTIONS_KEY = Rack::Session::Abstract::ENV_SESSION_OPTIONS_KEY # :nodoc:
+ ENV_SESSION_KEY = Rack::RACK_SESSION # :nodoc:
+ ENV_SESSION_OPTIONS_KEY = Rack::RACK_SESSION_OPTIONS # :nodoc:
# Singleton object used to determine if an optional param wasn't specified
Unspecified = Object.new
# Creates a session hash, merging the properties of the previous session if any
- def self.create(store, env, default_options)
- session_was = find env
- session = Request::Session.new(store, env)
+ def self.create(store, req, default_options)
+ session_was = find req
+ session = Request::Session.new(store, req)
session.merge! session_was if session_was
- set(env, session)
- Options.set(env, Request::Session::Options.new(store, default_options))
+ set(req, session)
+ Options.set(req, Request::Session::Options.new(store, default_options))
session
end
- def self.find(env)
- env[ENV_SESSION_KEY]
+ def self.find(req)
+ req.get_header ENV_SESSION_KEY
end
- def self.set(env, session)
- env[ENV_SESSION_KEY] = session
+ def self.set(req, session)
+ req.set_header ENV_SESSION_KEY, session
end
class Options #:nodoc:
- def self.set(env, options)
- env[ENV_SESSION_OPTIONS_KEY] = options
+ def self.set(req, options)
+ req.set_header ENV_SESSION_OPTIONS_KEY, options
end
- def self.find(env)
- env[ENV_SESSION_OPTIONS_KEY]
+ def self.find(req)
+ req.get_header ENV_SESSION_OPTIONS_KEY
end
def initialize(by, default_options)
@@ -47,9 +47,9 @@ module ActionDispatch
@delegate[key]
end
- def id(env)
+ def id(req)
@delegate.fetch(:id) {
- @by.send(:extract_session_id, env)
+ @by.send(:extract_session_id, req)
}
end
@@ -58,26 +58,26 @@ module ActionDispatch
def values_at(*args); @delegate.values_at(*args); end
end
- def initialize(by, env)
+ def initialize(by, req)
@by = by
- @env = env
+ @req = req
@delegate = {}
@loaded = false
@exists = nil # we haven't checked yet
end
def id
- options.id(@env)
+ options.id(@req)
end
def options
- Options.find @env
+ Options.find @req
end
def destroy
clear
options = self.options || {}
- @by.send(:destroy_session, @env, options.id(@env), options)
+ @by.send(:destroy_session, @req, options.id(@req), options)
# Load the new sid to be written with the response
@loaded = false
@@ -181,7 +181,7 @@ module ActionDispatch
def exists?
return @exists unless @exists.nil?
- @exists = @by.send(:session_exists?, @env)
+ @exists = @by.send(:session_exists?, @req)
end
def loaded?
@@ -209,7 +209,7 @@ module ActionDispatch
end
def load!
- id, session = @by.load_session @env
+ id, session = @by.load_session @req
options[:id] = id
@delegate.replace(stringify_keys(session))
@loaded = true
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index 0afe1852f2..1acfb2bfe8 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -150,9 +150,9 @@ module ActionDispatch
conditions,
required_defaults,
defaults,
- request_method)
+ request_method,
+ precedence)
- route.precedence = precedence
route
end
@@ -187,18 +187,16 @@ module ActionDispatch
def build_path(ast, requirements, anchor)
pattern = Journey::Path::Pattern.new(ast, requirements, JOINED_SEPARATORS, anchor)
- builder = Journey::GTG::Builder.new ast
-
# Get all the symbol nodes followed by literals that are not the
# dummy node.
- symbols = ast.grep(Journey::Nodes::Symbol).find_all { |n|
- builder.followpos(n).first.literal?
- }
+ symbols = ast.find_all { |n|
+ n.cat? && n.left.symbol? && n.right.cat? && n.right.left.literal?
+ }.map(&:left)
# Get all the symbol nodes preceded by literals.
- symbols.concat ast.find_all(&:literal?).map { |n|
- builder.followpos(n).first
- }.find_all(&:symbol?)
+ symbols.concat ast.find_all { |n|
+ n.cat? && n.left.literal? && n.right.cat? && n.right.left.symbol?
+ }.map { |n| n.right.left }
symbols.each { |x|
x.regexp = /(?:#{Regexp.union(x.regexp, '-')})+/
@@ -285,12 +283,16 @@ module ActionDispatch
end
def app(blocks)
- if to.respond_to?(:call)
- Constraints.new(to, blocks, Constraints::CALL)
- elsif blocks.any?
- Constraints.new(dispatcher(defaults.key?(:controller)), blocks, Constraints::SERVE)
+ if to.is_a?(Class) && to < ActionController::Metal
+ Routing::RouteSet::StaticDispatcher.new to
else
- dispatcher(defaults.key?(:controller))
+ if to.respond_to?(:call)
+ Constraints.new(to, blocks, Constraints::CALL)
+ elsif blocks.any?
+ Constraints.new(dispatcher(defaults.key?(:controller)), blocks, Constraints::SERVE)
+ else
+ dispatcher(defaults.key?(:controller))
+ end
end
end
@@ -370,7 +372,7 @@ module ActionDispatch
end
def dispatcher(raise_on_name_error)
- @set.dispatcher raise_on_name_error
+ Routing::RouteSet::Dispatcher.new raise_on_name_error
end
end
@@ -637,7 +639,7 @@ module ActionDispatch
# Query if the following named route was already defined.
def has_named_route?(name)
- @set.named_routes.routes[name.to_sym]
+ @set.named_routes.key? name
end
private
diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb
index 30e308119d..e4b8d5993e 100644
--- a/actionpack/lib/action_dispatch/routing/route_set.rb
+++ b/actionpack/lib/action_dispatch/routing/route_set.rb
@@ -1,6 +1,5 @@
require 'action_dispatch/journey'
require 'forwardable'
-require 'thread_safe'
require 'active_support/concern'
require 'active_support/core_ext/object/to_query'
require 'active_support/core_ext/hash/slice'
@@ -23,64 +22,43 @@ module ActionDispatch
class Dispatcher < Routing::Endpoint
def initialize(raise_on_name_error)
@raise_on_name_error = raise_on_name_error
- @controller_class_names = ThreadSafe::Cache.new
end
def dispatcher?; true; end
def serve(req)
- req.check_path_parameters!
- params = req.path_parameters
-
- prepare_params!(params)
-
- controller = controller(params, @raise_on_name_error) do
+ params = req.path_parameters
+ controller = controller req
+ res = controller.make_response! req
+ dispatch(controller, params[:action], req, res)
+ rescue NameError => e
+ if @raise_on_name_error
+ raise ActionController::RoutingError, e.message, e.backtrace
+ else
return [404, {'X-Cascade' => 'pass'}, []]
end
-
- dispatch(controller, params[:action], req)
end
- def prepare_params!(params)
- normalize_controller!(params)
- merge_default_action!(params)
- end
+ private
- # If this is a default_controller (i.e. a controller specified by the user)
- # we should raise an error in case it's not found, because it usually means
- # a user error. However, if the controller was retrieved through a dynamic
- # segment, as in :controller(/:action), we should simply return nil and
- # delegate the control back to Rack cascade. Besides, if this is not a default
- # controller, it means we should respect the @scope[:module] parameter.
- def controller(params, raise_on_name_error=true)
- controller_reference params.fetch(:controller) { yield }
- rescue NameError => e
- raise ActionController::RoutingError, e.message, e.backtrace if raise_on_name_error
- yield
+ def controller(req)
+ req.controller_class
end
- protected
-
- attr_reader :controller_class_names
-
- def controller_reference(controller_param)
- const_name = controller_class_names[controller_param] ||= "#{controller_param.camelize}Controller"
- ActiveSupport::Dependencies.constantize(const_name)
+ def dispatch(controller, action, req, res)
+ controller.dispatch(action, req, res)
end
+ end
- private
-
- def dispatch(controller, action, req)
- controller.action(action).call(req.env)
+ class StaticDispatcher < Dispatcher
+ def initialize(controller_class)
+ super(false)
+ @controller_class = controller_class
end
- def normalize_controller!(params)
- params[:controller] = params[:controller].underscore if params.key?(:controller)
- end
+ private
- def merge_default_action!(params)
- params[:action] ||= 'index'
- end
+ def controller(_); @controller_class; end
end
# A NamedRouteCollection instance is a collection of named routes, and also
@@ -89,6 +67,7 @@ module ActionDispatch
class NamedRouteCollection
include Enumerable
attr_reader :routes, :url_helpers_module, :path_helpers_module
+ private :routes
def initialize
@routes = {}
@@ -201,9 +180,9 @@ module ActionDispatch
private
def optimized_helper(args)
- params = parameterize_args(args) { |k|
+ params = parameterize_args(args) do
raise_generation_error(args)
- }
+ end
@route.format params
end
@@ -315,7 +294,7 @@ module ActionDispatch
attr_accessor :formatter, :set, :named_routes, :default_scope, :router
attr_accessor :disable_clear_and_finalize, :resources_path_names
- attr_accessor :default_url_options, :dispatcher_class
+ attr_accessor :default_url_options
attr_reader :env_key
alias :routes :set
@@ -358,7 +337,6 @@ module ActionDispatch
@set = Journey::Routes.new
@router = Journey::Router.new @set
@formatter = Journey::Formatter.new self
- @dispatcher_class = Routing::RouteSet::Dispatcher
end
def relative_url_root
@@ -373,6 +351,11 @@ module ActionDispatch
ActionDispatch::Request
end
+ def make_request(env)
+ request_class.new env
+ end
+ private :make_request
+
def draw(&block)
clear! unless @disable_clear_and_finalize
eval_block(block)
@@ -416,10 +399,6 @@ module ActionDispatch
@prepend.each { |blk| eval_block(blk) }
end
- def dispatcher(raise_on_name_error)
- dispatcher_class.new(raise_on_name_error)
- end
-
module MountedHelpers
extend ActiveSupport::Concern
include UrlFor
@@ -730,7 +709,7 @@ module ActionDispatch
end
def call(env)
- req = request_class.new(env)
+ req = make_request(env)
req.path_info = Journey::Router::Utils.normalize_path(req.path_info)
@router.serve(req)
end
@@ -746,7 +725,7 @@ module ActionDispatch
raise ActionController::RoutingError, e.message
end
- req = request_class.new(env)
+ req = make_request(env)
@router.recognize(req) do |route, params|
params.merge!(extras)
params.each do |key, value|
@@ -759,14 +738,13 @@ module ActionDispatch
req.path_parameters = old_params.merge params
app = route.app
if app.matches?(req) && app.dispatcher?
- dispatcher = app.app
-
- dispatcher.controller(params, false) do
+ begin
+ req.controller_class
+ rescue NameError
raise ActionController::RoutingError, "A route matches #{path.inspect}, but references missing controller: #{params[:controller].camelize}Controller"
end
- dispatcher.prepare_params!(params)
- return params
+ return req.path_parameters
end
end
diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb
index 0cdc6d4e77..4dfd4f3f71 100644
--- a/actionpack/lib/action_dispatch/testing/integration.rb
+++ b/actionpack/lib/action_dispatch/testing/integration.rb
@@ -359,10 +359,10 @@ module ActionDispatch
# this modifies the passed request_env directly
if headers.present?
- Http::Headers.new(request_env).merge!(headers)
+ Http::Headers.from_hash(request_env).merge!(headers)
end
if env.present?
- Http::Headers.new(request_env).merge!(env)
+ Http::Headers.from_hash(request_env).merge!(env)
end
session = Rack::Test::Session.new(_mock_session)
diff --git a/actionpack/test/abstract/translation_test.rb b/actionpack/test/abstract/translation_test.rb
index 8289252dfc..1435928578 100644
--- a/actionpack/test/abstract/translation_test.rb
+++ b/actionpack/test/abstract/translation_test.rb
@@ -24,7 +24,6 @@ module AbstractController
},
},
})
- @controller.stubs(action_name: :index)
end
def test_action_controller_base_responds_to_translate
@@ -44,25 +43,34 @@ module AbstractController
end
def test_lazy_lookup
- assert_equal 'bar', @controller.t('.foo')
+ @controller.stub :action_name, :index do
+ assert_equal 'bar', @controller.t('.foo')
+ end
end
def test_lazy_lookup_with_symbol
- assert_equal 'bar', @controller.t(:'.foo')
+ @controller.stub :action_name, :index do
+ assert_equal 'bar', @controller.t(:'.foo')
+ end
end
def test_lazy_lookup_fallback
- assert_equal 'no_action_tr', @controller.t(:'.no_action')
+ @controller.stub :action_name, :index do
+ assert_equal 'no_action_tr', @controller.t(:'.no_action')
+ end
end
def test_default_translation
- assert_equal 'bar', @controller.t('one.two')
+ @controller.stub :action_name, :index do
+ assert_equal 'bar', @controller.t('one.two')
+ end
end
def test_localize
time, expected = Time.gm(2000), 'Sat, 01 Jan 2000 00:00:00 +0000'
- I18n.stubs(:localize).with(time).returns(expected)
- assert_equal expected, @controller.l(time)
+ I18n.stub :localize, expected do
+ assert_equal expected, @controller.l(time)
+ end
end
end
end
diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb
index 60e2cea8a2..1954324222 100644
--- a/actionpack/test/abstract_unit.rb
+++ b/actionpack/test/abstract_unit.rb
@@ -19,7 +19,6 @@ begin
rescue LoadError
puts "'drb/unix' is not available"
end
-require 'tempfile'
PROCESS_COUNT = (ENV['N'] || 4).to_i
@@ -42,6 +41,8 @@ module Rails
def env
@_env ||= ActiveSupport::StringInquirer.new(ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "test")
end
+
+ def root; end;
end
end
@@ -117,23 +118,36 @@ class ActionDispatch::IntegrationTest < ActiveSupport::TestCase
get ':controller(/:action)'
end
- # Stub Rails dispatcher so it does not get controller references and
- # simply return the controller#action as Rack::Body.
- class StubDispatcher < ::ActionDispatch::Routing::RouteSet::Dispatcher
- protected
- def controller_reference(controller_param)
- controller_param
+ class DeadEndRoutes < ActionDispatch::Routing::RouteSet
+ # Stub Rails dispatcher so it does not get controller references and
+ # simply return the controller#action as Rack::Body.
+ class NullController < ::ActionController::Metal
+ def initialize(controller_name)
+ @controller = controller_name
+ end
+
+ def make_response!(request)
+ self.class.make_response! request
+ end
+
+ def dispatch(action, req, res)
+ [200, {'Content-Type' => 'text/html'}, ["#{@controller}##{action}"]]
+ end
+ end
+
+ class NullControllerRequest < DelegateClass(ActionDispatch::Request)
+ def controller_class
+ NullController.new params[:controller]
+ end
end
- def dispatch(controller, action, env)
- [200, {'Content-Type' => 'text/html'}, ["#{controller}##{action}"]]
+ def make_request env
+ NullControllerRequest.new super
end
end
- def self.stub_controllers(config = nil)
- route_set = ActionDispatch::Routing::RouteSet.new(*[config].compact)
- route_set.dispatcher_class = StubDispatcher
- yield route_set
+ def self.stub_controllers(config = ActionDispatch::Routing::RouteSet::DEFAULT_CONFIG)
+ yield DeadEndRoutes.new(config)
end
def with_routing(&block)
@@ -323,39 +337,37 @@ module RoutingTestHelpers
end
class TestSet < ActionDispatch::Routing::RouteSet
- attr_reader :strict
-
- def initialize(block, strict = false)
- @block = block
- @strict = strict
- super()
- end
-
- class Dispatcher < ActionDispatch::Routing::RouteSet::Dispatcher
- def initialize(defaults, set, block)
- super(defaults)
+ class Request < DelegateClass(ActionDispatch::Request)
+ def initialize(target, helpers, block, strict)
+ super(target)
+ @helpers = helpers
@block = block
- @set = set
- end
-
- def controller(params, default_controller=true)
- super(params, @set.strict)
+ @strict = strict
end
- def controller_reference(controller_param)
+ def controller_class
+ helpers = @helpers
block = @block
- set = @set
- super if @set.strict
- Class.new(ActionController::Base) {
- include set.url_helpers
+ Class.new(@strict ? super : ActionController::Base) {
+ include helpers
define_method(:process) { |name| block.call(self) }
def to_a; [200, {}, []]; end
}
end
end
- def dispatcher defaults
- TestSet::Dispatcher.new defaults, self, @block
+ attr_reader :strict
+
+ def initialize(block, strict = false)
+ @block = block
+ @strict = strict
+ super()
+ end
+
+ private
+
+ def make_request(env)
+ Request.new super, url_helpers, @block, strict
end
end
end
@@ -369,13 +381,11 @@ class ThreadsController < ResourcesController; end
class MessagesController < ResourcesController; end
class CommentsController < ResourcesController; end
class ReviewsController < ResourcesController; end
-class LogosController < ResourcesController; end
class AccountsController < ResourcesController; end
class AdminController < ResourcesController; end
class ProductsController < ResourcesController; end
class ImagesController < ResourcesController; end
-class PreferencesController < ResourcesController; end
module Backoffice
class ProductsController < ResourcesController; end
@@ -397,6 +407,7 @@ def jruby_skip(message = '')
end
require 'mocha/setup' # FIXME: stop using mocha
+require 'active_support/testing/method_call_assertions'
class ForkingExecutor
class Server
@@ -468,3 +479,7 @@ if RUBY_ENGINE == "ruby" && PROCESS_COUNT > 0
# Use N processes (N defaults to 4)
Minitest.parallel_executor = ForkingExecutor.new(PROCESS_COUNT)
end
+
+class ActiveSupport::TestCase
+ include ActiveSupport::Testing::MethodCallAssertions
+end
diff --git a/actionpack/test/controller/base_test.rb b/actionpack/test/controller/base_test.rb
index d9374ce9c3..fb60dbd993 100644
--- a/actionpack/test/controller/base_test.rb
+++ b/actionpack/test/controller/base_test.rb
@@ -93,6 +93,8 @@ end
class ControllerInstanceTests < ActiveSupport::TestCase
def setup
@empty = EmptyController.new
+ @empty.set_request!(ActionDispatch::Request.new({}))
+ @empty.set_response!(EmptyController.make_response!(@empty.request))
@contained = Submodule::ContainedEmptyController.new
@empty_controllers = [@empty, @contained]
end
diff --git a/actionpack/test/controller/integration_test.rb b/actionpack/test/controller/integration_test.rb
index dc4c32b07e..de7e800ac1 100644
--- a/actionpack/test/controller/integration_test.rb
+++ b/actionpack/test/controller/integration_test.rb
@@ -26,289 +26,335 @@ class SessionTest < ActiveSupport::TestCase
end
def test_follow_redirect_raises_when_no_redirect
- @session.stubs(:redirect?).returns(false)
- assert_raise(RuntimeError) { @session.follow_redirect! }
+ @session.stub :redirect?, false do
+ assert_raise(RuntimeError) { @session.follow_redirect! }
+ end
end
def test_request_via_redirect_uses_given_method
path = "/somepath"; args = {:id => '1'}; headers = {"X-Test-Header" => "testvalue"}
- @session.expects(:process).with(:put, path, params: args, headers: headers)
- @session.stubs(:redirect?).returns(false)
- @session.request_via_redirect(:put, path, params: args, headers: headers)
+ assert_called_with @session, :process, [:put, path, params: args, headers: headers] do
+ @session.stub :redirect?, false do
+ @session.request_via_redirect(:put, path, params: args, headers: headers)
+ end
+ end
end
def test_deprecated_request_via_redirect_uses_given_method
path = "/somepath"; args = { id: '1' }; headers = { "X-Test-Header" => "testvalue" }
- @session.expects(:process).with(:put, path, params: args, headers: headers)
- @session.stubs(:redirect?).returns(false)
- assert_deprecated { @session.request_via_redirect(:put, path, args, headers) }
+ assert_called_with @session, :process, [:put, path, params: args, headers: headers] do
+ @session.stub :redirect?, false do
+ assert_deprecated { @session.request_via_redirect(:put, path, args, headers) }
+ end
+ end
end
def test_request_via_redirect_follows_redirects
path = "/somepath"; args = {:id => '1'}; headers = {"X-Test-Header" => "testvalue"}
- @session.stubs(:redirect?).returns(true, true, false)
- @session.expects(:follow_redirect!).times(2)
- @session.request_via_redirect(:get, path, params: args, headers: headers)
+ value_series = [true, true, false]
+ assert_called @session, :follow_redirect!, times: 2 do
+ @session.stub :redirect?, ->{ value_series.shift } do
+ @session.request_via_redirect(:get, path, params: args, headers: headers)
+ end
+ end
end
def test_request_via_redirect_returns_status
path = "/somepath"; args = {:id => '1'}; headers = {"X-Test-Header" => "testvalue"}
- @session.stubs(:redirect?).returns(false)
- @session.stubs(:status).returns(200)
- assert_equal 200, @session.request_via_redirect(:get, path, params: args, headers: headers)
+ @session.stub :redirect?, false do
+ @session.stub :status, 200 do
+ assert_equal 200, @session.request_via_redirect(:get, path, params: args, headers: headers)
+ end
+ end
end
def test_deprecated_get_via_redirect
path = "/somepath"; args = { id: '1' }; headers = { "X-Test-Header" => "testvalue" }
- @session.expects(:request_via_redirect).with(:get, path, args, headers)
- assert_deprecated do
- @session.get_via_redirect(path, args, headers)
+ assert_called_with @session, :request_via_redirect, [:get, path, args, headers] do
+ assert_deprecated do
+ @session.get_via_redirect(path, args, headers)
+ end
end
end
def test_deprecated_post_via_redirect
path = "/somepath"; args = { id: '1' }; headers = { "X-Test-Header" => "testvalue" }
- @session.expects(:request_via_redirect).with(:post, path, args, headers)
- assert_deprecated do
- @session.post_via_redirect(path, args, headers)
+ assert_called_with @session, :request_via_redirect, [:post, path, args, headers] do
+ assert_deprecated do
+ @session.post_via_redirect(path, args, headers)
+ end
end
end
def test_deprecated_patch_via_redirect
path = "/somepath"; args = { id: '1' }; headers = { "X-Test-Header" => "testvalue" }
- @session.expects(:request_via_redirect).with(:patch, path, args, headers)
- assert_deprecated do
- @session.patch_via_redirect(path, args, headers)
+ assert_called_with @session, :request_via_redirect, [:patch, path, args, headers] do
+ assert_deprecated do
+ @session.patch_via_redirect(path, args, headers)
+ end
end
end
def test_deprecated_put_via_redirect
path = "/somepath"; args = { id: '1' }; headers = { "X-Test-Header" => "testvalue" }
- @session.expects(:request_via_redirect).with(:put, path, args, headers)
- assert_deprecated do
- @session.put_via_redirect(path, args, headers)
+ assert_called_with @session, :request_via_redirect, [:put, path, args, headers] do
+ assert_deprecated do
+ @session.put_via_redirect(path, args, headers)
+ end
end
end
def test_deprecated_delete_via_redirect
path = "/somepath"; args = { id: '1' }; headers = { "X-Test-Header" => "testvalue" }
- @session.expects(:request_via_redirect).with(:delete, path, args, headers)
- assert_deprecated do
- @session.delete_via_redirect(path, args, headers)
+ assert_called_with @session, :request_via_redirect, [:delete, path, args, headers] do
+ assert_deprecated do
+ @session.delete_via_redirect(path, args, headers)
+ end
end
end
def test_get
path = "/index"; params = "blah"; headers = { location: 'blah' }
- @session.expects(:process).with(:get, path, params: params, headers: headers)
- @session.get(path, params: params, headers: headers)
+
+ assert_called_with @session, :process, [:get, path, params: params, headers: headers] do
+ @session.get(path, params: params, headers: headers)
+ end
end
def test_get_with_env_and_headers
path = "/index"; params = "blah"; headers = { location: 'blah' }; env = { 'HTTP_X_REQUESTED_WITH' => 'XMLHttpRequest' }
- @session.expects(:process).with(:get, path, params: params, headers: headers, env: env)
- @session.get(path, params: params, headers: headers, env: env)
+ assert_called_with @session, :process, [:get, path, params: params, headers: headers, env: env] do
+ @session.get(path, params: params, headers: headers, env: env)
+ end
end
def test_deprecated_get
path = "/index"; params = "blah"; headers = { location: 'blah' }
- @session.expects(:process).with(:get, path, params: params, headers: headers)
- assert_deprecated {
- @session.get(path, params, headers)
- }
+
+ assert_called_with @session, :process, [:get, path, params: params, headers: headers] do
+ assert_deprecated {
+ @session.get(path, params, headers)
+ }
+ end
end
def test_post
path = "/index"; params = "blah"; headers = { location: 'blah' }
- @session.expects(:process).with(:post, path, params: params, headers: headers)
- assert_deprecated {
- @session.post(path, params, headers)
- }
+ assert_called_with @session, :process, [:post, path, params: params, headers: headers] do
+ assert_deprecated {
+ @session.post(path, params, headers)
+ }
+ end
end
def test_deprecated_post
path = "/index"; params = "blah"; headers = { location: 'blah' }
- @session.expects(:process).with(:post, path, params: params, headers: headers)
- @session.post(path, params: params, headers: headers)
+ assert_called_with @session, :process, [:post, path, params: params, headers: headers] do
+ @session.post(path, params: params, headers: headers)
+ end
end
def test_patch
path = "/index"; params = "blah"; headers = { location: 'blah' }
- @session.expects(:process).with(:patch, path, params: params, headers: headers)
- @session.patch(path, params: params, headers: headers)
+ assert_called_with @session, :process, [:patch, path, params: params, headers: headers] do
+ @session.patch(path, params: params, headers: headers)
+ end
end
def test_deprecated_patch
path = "/index"; params = "blah"; headers = { location: 'blah' }
- @session.expects(:process).with(:patch, path, params: params, headers: headers)
- assert_deprecated {
- @session.patch(path, params, headers)
- }
+ assert_called_with @session, :process, [:patch, path, params: params, headers: headers] do
+ assert_deprecated {
+ @session.patch(path, params, headers)
+ }
+ end
end
def test_put
path = "/index"; params = "blah"; headers = { location: 'blah' }
- @session.expects(:process).with(:put, path, params: params, headers: headers)
- @session.put(path, params: params, headers: headers)
+ assert_called_with @session, :process, [:put, path, params: params, headers: headers] do
+ @session.put(path, params: params, headers: headers)
+ end
end
def test_deprecated_put
path = "/index"; params = "blah"; headers = { location: 'blah' }
- @session.expects(:process).with(:put, path, params: params, headers: headers)
- assert_deprecated {
- @session.put(path, params, headers)
- }
+ assert_called_with @session, :process, [:put, path, params: params, headers: headers] do
+ assert_deprecated {
+ @session.put(path, params, headers)
+ }
+ end
end
def test_delete
path = "/index"; params = "blah"; headers = { location: 'blah' }
- @session.expects(:process).with(:delete, path, params: params, headers: headers)
- assert_deprecated {
- @session.delete(path,params,headers)
- }
+ assert_called_with @session, :process, [:delete, path, params: params, headers: headers] do
+ assert_deprecated {
+ @session.delete(path,params,headers)
+ }
+ end
end
def test_deprecated_delete
path = "/index"; params = "blah"; headers = { location: 'blah' }
- @session.expects(:process).with(:delete, path, params: params, headers: headers)
- @session.delete(path, params: params, headers: headers)
+ assert_called_with @session, :process, [:delete, path, params: params, headers: headers] do
+ @session.delete(path, params: params, headers: headers)
+ end
end
def test_head
path = "/index"; params = "blah"; headers = { location: 'blah' }
- @session.expects(:process).with(:head, path, params: params, headers: headers)
- @session.head(path, params: params, headers: headers)
+ assert_called_with @session, :process, [:head, path, params: params, headers: headers] do
+ @session.head(path, params: params, headers: headers)
+ end
end
def deprecated_test_head
path = "/index"; params = "blah"; headers = { location: 'blah' }
- @session.expects(:process).with(:head, path, params: params, headers: headers)
- assert_deprecated {
- @session.head(path, params, headers)
- }
+ assert_called_with @session, :process, [:head, path, params: params, headers: headers] do
+ assert_deprecated {
+ @session.head(path, params, headers)
+ }
+ end
end
def test_xml_http_request_get
path = "/index"; params = "blah"; headers = { location: 'blah' }
- @session.expects(:process).with(:get, path, params: params, headers: headers, xhr: true)
- @session.get(path, params: params, headers: headers, xhr: true)
+ assert_called_with @session, :process, [:get, path, params: params, headers: headers, xhr: true] do
+ @session.get(path, params: params, headers: headers, xhr: true)
+ end
end
def test_deprecated_xml_http_request_get
path = "/index"; params = "blah"; headers = { location: 'blah' }
- @session.expects(:process).with(:get, path, params: params, headers: headers, xhr: true)
- @session.get(path, params: params, headers: headers, xhr: true)
+ assert_called_with @session, :process, [:get, path, params: params, headers: headers, xhr: true] do
+ @session.get(path, params: params, headers: headers, xhr: true)
+ end
end
def test_deprecated_args_xml_http_request_get
path = "/index"; params = "blah"; headers = { location: 'blah' }
- @session.expects(:process).with(:get, path, params: params, headers: headers, xhr: true)
- assert_deprecated(/xml_http_request/) {
- @session.xml_http_request(:get, path, params, headers)
- }
+ assert_called_with @session, :process, [:get, path, params: params, headers: headers, xhr: true] do
+ assert_deprecated(/xml_http_request/) {
+ @session.xml_http_request(:get, path, params, headers)
+ }
+ end
end
def test_xml_http_request_post
path = "/index"; params = "blah"; headers = { location: 'blah' }
- @session.expects(:process).with(:post, path, params: params, headers: headers, xhr: true)
- @session.post(path, params: params, headers: headers, xhr: true)
+ assert_called_with @session, :process, [:post, path, params: params, headers: headers, xhr: true] do
+ @session.post(path, params: params, headers: headers, xhr: true)
+ end
end
def test_deprecated_xml_http_request_post
path = "/index"; params = "blah"; headers = { location: 'blah' }
- @session.expects(:process).with(:post, path, params: params, headers: headers, xhr: true)
- @session.post(path, params: params, headers: headers, xhr: true)
+ assert_called_with @session, :process, [:post, path, params: params, headers: headers, xhr: true] do
+ @session.post(path, params: params, headers: headers, xhr: true)
+ end
end
def test_deprecated_args_xml_http_request_post
path = "/index"; params = "blah"; headers = { location: 'blah' }
- @session.expects(:process).with(:post, path, params: params, headers: headers, xhr: true)
- assert_deprecated(/xml_http_request/) { @session.xml_http_request(:post,path,params,headers) }
+ assert_called_with @session, :process, [:post, path, params: params, headers: headers, xhr: true] do
+ assert_deprecated(/xml_http_request/) { @session.xml_http_request(:post,path,params,headers) }
+ end
end
def test_xml_http_request_patch
path = "/index"; params = "blah"; headers = { location: 'blah' }
- @session.expects(:process).with(:patch, path, params: params, headers: headers, xhr: true)
- @session.patch(path, params: params, headers: headers, xhr: true)
+ assert_called_with @session, :process, [:patch, path, params: params, headers: headers, xhr: true] do
+ @session.patch(path, params: params, headers: headers, xhr: true)
+ end
end
def test_deprecated_xml_http_request_patch
path = "/index"; params = "blah"; headers = { location: 'blah' }
- @session.expects(:process).with(:patch, path, params: params, headers: headers, xhr: true)
- @session.patch(path, params: params, headers: headers, xhr: true)
+ assert_called_with @session, :process, [:patch, path, params: params, headers: headers, xhr: true] do
+ @session.patch(path, params: params, headers: headers, xhr: true)
+ end
end
def test_deprecated_args_xml_http_request_patch
path = "/index"; params = "blah"; headers = { location: 'blah' }
- @session.expects(:process).with(:patch, path, params: params, headers: headers, xhr: true)
- assert_deprecated(/xml_http_request/) { @session.xml_http_request(:patch,path,params,headers) }
+ assert_called_with @session, :process, [:patch, path, params: params, headers: headers, xhr: true] do
+ assert_deprecated(/xml_http_request/) { @session.xml_http_request(:patch,path,params,headers) }
+ end
end
def test_xml_http_request_put
path = "/index"; params = "blah"; headers = { location: 'blah' }
- @session.expects(:process).with(:put, path, params: params, headers: headers, xhr: true)
- @session.put(path, params: params, headers: headers, xhr: true)
+ assert_called_with @session, :process, [:put, path, params: params, headers: headers, xhr: true] do
+ @session.put(path, params: params, headers: headers, xhr: true)
+ end
end
def test_deprecated_xml_http_request_put
path = "/index"; params = "blah"; headers = { location: 'blah' }
- @session.expects(:process).with(:put, path, params: params, headers: headers, xhr: true)
- @session.put(path, params: params, headers: headers, xhr: true)
+ assert_called_with @session, :process, [:put, path, params: params, headers: headers, xhr: true] do
+ @session.put(path, params: params, headers: headers, xhr: true)
+ end
end
def test_deprecated_args_xml_http_request_put
path = "/index"; params = "blah"; headers = { location: 'blah' }
- @session.expects(:process).with(:put, path, params: params, headers: headers, xhr: true)
- assert_deprecated(/xml_http_request/) { @session.xml_http_request(:put, path, params, headers) }
+ assert_called_with @session, :process, [:put, path, params: params, headers: headers, xhr: true] do
+ assert_deprecated(/xml_http_request/) { @session.xml_http_request(:put, path, params, headers) }
+ end
end
def test_xml_http_request_delete
path = "/index"; params = "blah"; headers = { location: 'blah' }
- @session.expects(:process).with(:delete, path, params: params, headers: headers, xhr: true)
- @session.delete(path, params: params, headers: headers, xhr: true)
+ assert_called_with @session, :process, [:delete, path, params: params, headers: headers, xhr: true] do
+ @session.delete(path, params: params, headers: headers, xhr: true)
+ end
end
def test_deprecated_xml_http_request_delete
path = "/index"; params = "blah"; headers = { location: 'blah' }
- @session.expects(:process).with(:delete, path, params: params, headers: headers, xhr: true)
- assert_deprecated { @session.xml_http_request(:delete, path, params: params, headers: headers) }
+ assert_called_with @session, :process, [:delete, path, params: params, headers: headers, xhr: true] do
+ assert_deprecated { @session.xml_http_request(:delete, path, params: params, headers: headers) }
+ end
end
def test_deprecated_args_xml_http_request_delete
path = "/index"; params = "blah"; headers = { location: 'blah' }
- @session.expects(:process).with(:delete, path, params: params, headers: headers, xhr: true)
- assert_deprecated(/xml_http_request/) { @session.xml_http_request(:delete, path, params, headers) }
+ assert_called_with @session, :process, [:delete, path, params: params, headers: headers, xhr: true] do
+ assert_deprecated(/xml_http_request/) { @session.xml_http_request(:delete, path, params, headers) }
+ end
end
def test_xml_http_request_head
path = "/index"; params = "blah"; headers = { location: 'blah' }
- @session.expects(:process).with(:head, path, params: params, headers: headers, xhr: true)
- @session.head(path, params: params, headers: headers, xhr: true)
+ assert_called_with @session, :process, [:head, path, params: params, headers: headers, xhr: true] do
+ @session.head(path, params: params, headers: headers, xhr: true)
+ end
end
def test_deprecated_xml_http_request_head
path = "/index"; params = "blah"; headers = { location: 'blah' }
- @session.expects(:process).with(:head, path, params: params, headers: headers, xhr: true)
- assert_deprecated(/xml_http_request/) { @session.xml_http_request(:head, path, params: params, headers: headers) }
+ assert_called_with @session, :process, [:head, path, params: params, headers: headers, xhr: true] do
+ assert_deprecated(/xml_http_request/) { @session.xml_http_request(:head, path, params: params, headers: headers) }
+ end
end
def test_deprecated_args_xml_http_request_head
path = "/index"; params = "blah"; headers = { location: 'blah' }
- @session.expects(:process).with(:head, path, params: params, headers: headers, xhr: true)
- assert_deprecated { @session.xml_http_request(:head, path, params, headers) }
+ assert_called_with @session, :process, [:head, path, params: params, headers: headers, xhr: true] do
+ assert_deprecated { @session.xml_http_request(:head, path, params, headers) }
+ end
end
end
class IntegrationTestTest < ActiveSupport::TestCase
def setup
@test = ::ActionDispatch::IntegrationTest.new(:app)
- @test.class.stubs(:fixture_table_names).returns([])
- @session = @test.open_session
end
def test_opens_new_session
@@ -340,14 +386,8 @@ end
# Tests that integration tests don't call Controller test methods for processing.
# Integration tests have their own setup and teardown.
class IntegrationTestUsesCorrectClass < ActionDispatch::IntegrationTest
- def self.fixture_table_names
- []
- end
-
def test_integration_methods_called
reset!
- @integration_session.stubs(:generic_url_rewriter)
- @integration_session.stubs(:process)
%w( get post head patch put delete ).each do |verb|
assert_nothing_raised("'#{verb}' should use integration test methods") { __send__(verb, '/') }
diff --git a/actionpack/test/controller/live_stream_test.rb b/actionpack/test/controller/live_stream_test.rb
index 6ba361f2f7..e9c19b7acf 100644
--- a/actionpack/test/controller/live_stream_test.rb
+++ b/actionpack/test/controller/live_stream_test.rb
@@ -315,7 +315,7 @@ module ActionController
t = Thread.new(@response) { |resp|
resp.await_commit
_, _, body = resp.to_a
- body.each do |part|
+ body.each do
@controller.latch.wait
body.close
break
@@ -339,7 +339,7 @@ module ActionController
t = Thread.new(@response) { |resp|
resp.await_commit
_, _, body = resp.to_a
- body.each do |part|
+ body.each do
body.close
break
end
diff --git a/actionpack/test/controller/new_base/bare_metal_test.rb b/actionpack/test/controller/new_base/bare_metal_test.rb
index 710c428dcc..e61f4d241b 100644
--- a/actionpack/test/controller/new_base/bare_metal_test.rb
+++ b/actionpack/test/controller/new_base/bare_metal_test.rb
@@ -2,8 +2,6 @@ require "abstract_unit"
module BareMetalTest
class BareController < ActionController::Metal
- include ActionController::RackDelegation
-
def index
self.response_body = "Hello world"
end
@@ -28,6 +26,8 @@ module BareMetalTest
test "response_body value is wrapped in an array when the value is a String" do
controller = BareController.new
+ controller.set_request!(ActionDispatch::Request.new({}))
+ controller.set_response!(BareController.make_response!(controller.request))
controller.index
assert_equal ["Hello world"], controller.response_body
end
@@ -37,8 +37,6 @@ module BareMetalTest
controller = BareController.new
controller.set_request! ActionDispatch::Request.new(env)
assert controller.request
- assert controller.response
- assert env['action_controller.instance']
end
end
@@ -123,34 +121,40 @@ module BareMetalTest
end
test "head :no_content (204) does not return any content" do
- content = HeadController.action(:no_content).call(Rack::MockRequest.env_for("/")).third.first
+ content = body(HeadController.action(:no_content).call(Rack::MockRequest.env_for("/")))
assert_empty content
end
test "head :reset_content (205) does not return any content" do
- content = HeadController.action(:reset_content).call(Rack::MockRequest.env_for("/")).third.first
+ content = body(HeadController.action(:reset_content).call(Rack::MockRequest.env_for("/")))
assert_empty content
end
test "head :not_modified (304) does not return any content" do
- content = HeadController.action(:not_modified).call(Rack::MockRequest.env_for("/")).third.first
+ content = body(HeadController.action(:not_modified).call(Rack::MockRequest.env_for("/")))
assert_empty content
end
test "head :continue (100) does not return any content" do
- content = HeadController.action(:continue).call(Rack::MockRequest.env_for("/")).third.first
+ content = body(HeadController.action(:continue).call(Rack::MockRequest.env_for("/")))
assert_empty content
end
test "head :switching_protocols (101) does not return any content" do
- content = HeadController.action(:switching_protocols).call(Rack::MockRequest.env_for("/")).third.first
+ content = body(HeadController.action(:switching_protocols).call(Rack::MockRequest.env_for("/")))
assert_empty content
end
test "head :processing (102) does not return any content" do
- content = HeadController.action(:processing).call(Rack::MockRequest.env_for("/")).third.first
+ content = body(HeadController.action(:processing).call(Rack::MockRequest.env_for("/")))
assert_empty content
end
+
+ def body(rack_response)
+ buf = []
+ rack_response[2].each { |x| buf << x }
+ buf.join
+ end
end
class BareControllerTest < ActionController::TestCase
diff --git a/actionpack/test/controller/new_base/middleware_test.rb b/actionpack/test/controller/new_base/middleware_test.rb
index a30e937bb3..85a1f351f0 100644
--- a/actionpack/test/controller/new_base/middleware_test.rb
+++ b/actionpack/test/controller/new_base/middleware_test.rb
@@ -75,7 +75,7 @@ module MiddlewareTest
test "middleware that is 'use'd is called as part of the Rack application" do
result = @app.call(env_for("/"))
- assert_equal ["Hello World"], result[2]
+ assert_equal ["Hello World"], [].tap { |a| result[2].each { |x| a << x } }
assert_equal "Success", result[1]["Middleware-Test"]
end
diff --git a/actionpack/test/controller/new_base/render_html_test.rb b/actionpack/test/controller/new_base/render_html_test.rb
index fe11501eeb..e9ea57e329 100644
--- a/actionpack/test/controller/new_base/render_html_test.rb
+++ b/actionpack/test/controller/new_base/render_html_test.rb
@@ -179,7 +179,7 @@ module RenderHtml
test "rendering from minimal controller returns response with text/html content type" do
get "/render_html/minimal/index"
- assert_content_type "text/html"
+ assert_content_type "text/html; charset=utf-8"
end
test "rendering from normal controller returns response with text/html content type" do
diff --git a/actionpack/test/controller/new_base/render_plain_test.rb b/actionpack/test/controller/new_base/render_plain_test.rb
index 0e36d36b50..0881442bd0 100644
--- a/actionpack/test/controller/new_base/render_plain_test.rb
+++ b/actionpack/test/controller/new_base/render_plain_test.rb
@@ -157,7 +157,7 @@ module RenderPlain
test "rendering from minimal controller returns response with text/plain content type" do
get "/render_plain/minimal/index"
- assert_content_type "text/plain"
+ assert_content_type "text/plain; charset=utf-8"
end
test "rendering from normal controller returns response with text/plain content type" do
diff --git a/actionpack/test/controller/parameters/always_permitted_parameters_test.rb b/actionpack/test/controller/parameters/always_permitted_parameters_test.rb
index 59be08db54..efaf8a96c3 100644
--- a/actionpack/test/controller/parameters/always_permitted_parameters_test.rb
+++ b/actionpack/test/controller/parameters/always_permitted_parameters_test.rb
@@ -1,6 +1,5 @@
require 'abstract_unit'
require 'action_controller/metal/strong_parameters'
-require 'minitest/mock'
class AlwaysPermittedParametersTest < ActiveSupport::TestCase
def setup
diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb
index 9acdc29aeb..82c7ebf568 100644
--- a/actionpack/test/controller/render_test.rb
+++ b/actionpack/test/controller/render_test.rb
@@ -1,6 +1,5 @@
require 'abstract_unit'
require 'controller/fake_models'
-require 'pathname'
class TestControllerWithExtraEtags < ActionController::Base
etag { nil }
@@ -235,8 +234,6 @@ class MetalTestController < ActionController::Metal
include AbstractController::Rendering
include ActionView::Rendering
include ActionController::Rendering
- include ActionController::RackDelegation
-
def accessing_logger_in_template
render :inline => "<%= logger.class %>"
@@ -295,9 +292,10 @@ class ExpiresInRenderTest < ActionController::TestCase
def test_date_header_when_expires_in
time = Time.mktime(2011,10,30)
- Time.stubs(:now).returns(time)
- get :conditional_hello_with_expires_in
- assert_equal Time.now.httpdate, @response.headers["Date"]
+ Time.stub :now, time do
+ get :conditional_hello_with_expires_in
+ assert_equal Time.now.httpdate, @response.headers["Date"]
+ end
end
end
diff --git a/actionpack/test/controller/request_forgery_protection_test.rb b/actionpack/test/controller/request_forgery_protection_test.rb
index 868520a219..90fd8669c2 100644
--- a/actionpack/test/controller/request_forgery_protection_test.rb
+++ b/actionpack/test/controller/request_forgery_protection_test.rb
@@ -1,5 +1,4 @@
require 'abstract_unit'
-require 'digest/sha1'
require "active_support/log_subscriber/test_helper"
# common controller actions
@@ -132,10 +131,7 @@ end
# common test methods
module RequestForgeryProtectionTests
def setup
- @token = "cf50faa3fe97702ca1ae"
- @controller.stubs(:form_authenticity_token).returns(@token)
- @controller.stubs(:valid_authenticity_token?).with{ |_, t| t == @token }.returns(true)
- @controller.stubs(:valid_authenticity_token?).with{ |_, t| t != @token }.returns(false)
+ @token = Base64.strict_encode64('railstestrailstestrailstestrails')
@old_request_forgery_protection_token = ActionController::Base.request_forgery_protection_token
ActionController::Base.request_forgery_protection_token = :custom_authenticity_token
end
@@ -145,17 +141,21 @@ module RequestForgeryProtectionTests
end
def test_should_render_form_with_token_tag
- assert_not_blocked do
- get :index
+ @controller.stub :form_authenticity_token, @token do
+ assert_not_blocked do
+ get :index
+ end
+ assert_select 'form>input[name=?][value=?]', 'custom_authenticity_token', @token
end
- assert_select 'form>input[name=?][value=?]', 'custom_authenticity_token', @token
end
def test_should_render_button_to_with_token_tag
- assert_not_blocked do
- get :show_button
+ @controller.stub :form_authenticity_token, @token do
+ assert_not_blocked do
+ get :show_button
+ end
+ assert_select 'form>input[name=?][value=?]', 'custom_authenticity_token', @token
end
- assert_select 'form>input[name=?][value=?]', 'custom_authenticity_token', @token
end
def test_should_render_form_without_token_tag_if_remote
@@ -199,17 +199,21 @@ module RequestForgeryProtectionTests
end
def test_should_render_form_with_token_tag_if_remote_and_authenticity_token_requested
- assert_not_blocked do
- get :form_for_remote_with_token
+ @controller.stub :form_authenticity_token, @token do
+ assert_not_blocked do
+ get :form_for_remote_with_token
+ end
+ assert_select 'form>input[name=?][value=?]', 'custom_authenticity_token', @token
end
- assert_select 'form>input[name=?][value=?]', 'custom_authenticity_token', @token
end
def test_should_render_form_with_token_tag_with_authenticity_token_requested
- assert_not_blocked do
- get :form_for_with_token
+ @controller.stub :form_authenticity_token, @token do
+ assert_not_blocked do
+ get :form_for_with_token
+ end
+ assert_select 'form>input[name=?][value=?]', 'custom_authenticity_token', @token
end
- assert_select 'form>input[name=?][value=?]', 'custom_authenticity_token', @token
end
def test_should_allow_get
@@ -249,37 +253,53 @@ module RequestForgeryProtectionTests
end
def test_should_allow_post_with_token
- assert_not_blocked { post :index, params: { custom_authenticity_token: @token } }
+ session[:_csrf_token] = @token
+ @controller.stub :form_authenticity_token, @token do
+ assert_not_blocked { post :index, params: { custom_authenticity_token: @token } }
+ end
end
def test_should_allow_patch_with_token
- assert_not_blocked { patch :index, params: { custom_authenticity_token: @token } }
+ session[:_csrf_token] = @token
+ @controller.stub :form_authenticity_token, @token do
+ assert_not_blocked { patch :index, params: { custom_authenticity_token: @token } }
+ end
end
def test_should_allow_put_with_token
- assert_not_blocked { put :index, params: { custom_authenticity_token: @token } }
+ session[:_csrf_token] = @token
+ @controller.stub :form_authenticity_token, @token do
+ assert_not_blocked { put :index, params: { custom_authenticity_token: @token } }
+ end
end
def test_should_allow_delete_with_token
- assert_not_blocked { delete :index, params: { custom_authenticity_token: @token } }
+ session[:_csrf_token] = @token
+ @controller.stub :form_authenticity_token, @token do
+ assert_not_blocked { delete :index, params: { custom_authenticity_token: @token } }
+ end
end
def test_should_allow_post_with_token_in_header
+ session[:_csrf_token] = @token
@request.env['HTTP_X_CSRF_TOKEN'] = @token
assert_not_blocked { post :index }
end
def test_should_allow_delete_with_token_in_header
+ session[:_csrf_token] = @token
@request.env['HTTP_X_CSRF_TOKEN'] = @token
assert_not_blocked { delete :index }
end
def test_should_allow_patch_with_token_in_header
+ session[:_csrf_token] = @token
@request.env['HTTP_X_CSRF_TOKEN'] = @token
assert_not_blocked { patch :index }
end
def test_should_allow_put_with_token_in_header
+ session[:_csrf_token] = @token
@request.env['HTTP_X_CSRF_TOKEN'] = @token
assert_not_blocked { put :index }
end
@@ -333,6 +353,7 @@ module RequestForgeryProtectionTests
# Allow non-GET requests since GET is all a remote <script> tag can muster.
def test_should_allow_non_get_js_without_xhr_header
+ session[:_csrf_token] = @token
assert_cross_origin_not_blocked { post :same_origin_js, params: { custom_authenticity_token: @token } }
assert_cross_origin_not_blocked { post :same_origin_js, params: { format: 'js', custom_authenticity_token: @token } }
assert_cross_origin_not_blocked do
@@ -402,11 +423,13 @@ class RequestForgeryProtectionControllerUsingResetSessionTest < ActionController
end
test 'should emit a csrf-param meta tag and a csrf-token meta tag' do
- @controller.stubs(:form_authenticity_token).returns(@token + '<=?')
- get :meta
- assert_select 'meta[name=?][content=?]', 'csrf-param', 'custom_authenticity_token'
- assert_select 'meta[name=?]', 'csrf-token'
- assert_match(/cf50faa3fe97702ca1ae&lt;=\?/, @response.body)
+ @controller.stub :form_authenticity_token, @token + '<=?' do
+ get :meta
+ assert_select 'meta[name=?][content=?]', 'csrf-param', 'custom_authenticity_token'
+ assert_select 'meta[name=?]', 'csrf-token'
+ regexp = "#{@token}&lt;=\?"
+ assert_match(/#{regexp}/, @response.body)
+ end
end
end
@@ -485,30 +508,36 @@ class FreeCookieControllerTest < ActionController::TestCase
def setup
@controller = FreeCookieController.new
@token = "cf50faa3fe97702ca1ae"
-
- SecureRandom.stubs(:base64).returns(@token)
super
end
def test_should_not_render_form_with_token_tag
- get :index
- assert_select 'form>div>input[name=?][value=?]', 'authenticity_token', @token, false
+ SecureRandom.stub :base64, @token do
+ get :index
+ assert_select 'form>div>input[name=?][value=?]', 'authenticity_token', @token, false
+ end
end
def test_should_not_render_button_to_with_token_tag
- get :show_button
- assert_select 'form>div>input[name=?][value=?]', 'authenticity_token', @token, false
+ SecureRandom.stub :base64, @token do
+ get :show_button
+ assert_select 'form>div>input[name=?][value=?]', 'authenticity_token', @token, false
+ end
end
def test_should_allow_all_methods_without_token
- [:post, :patch, :put, :delete].each do |method|
- assert_nothing_raised { send(method, :index)}
+ SecureRandom.stub :base64, @token do
+ [:post, :patch, :put, :delete].each do |method|
+ assert_nothing_raised { send(method, :index)}
+ end
end
end
test 'should not emit a csrf-token meta tag' do
- get :meta
- assert @response.body.blank?
+ SecureRandom.stub :base64, @token do
+ get :meta
+ assert @response.body.blank?
+ end
end
end
@@ -529,11 +558,11 @@ class CustomAuthenticityParamControllerTest < ActionController::TestCase
def test_should_not_warn_if_form_authenticity_param_matches_form_authenticity_token
ActionController::Base.logger = @logger
- @controller.stubs(:valid_authenticity_token?).returns(:true)
-
begin
- post :index, params: { custom_token_name: 'foobar' }
- assert_equal 0, @logger.logged(:warn).size
+ @controller.stub :valid_authenticity_token?, :true do
+ post :index, params: { custom_token_name: 'foobar' }
+ assert_equal 0, @logger.logged(:warn).size
+ end
ensure
ActionController::Base.logger = @old_logger
end
diff --git a/actionpack/test/controller/required_params_test.rb b/actionpack/test/controller/required_params_test.rb
index a901e56332..168f64ce41 100644
--- a/actionpack/test/controller/required_params_test.rb
+++ b/actionpack/test/controller/required_params_test.rb
@@ -48,4 +48,21 @@ class ParametersRequireTest < ActiveSupport::TestCase
ActionController::Parameters.new(person: {}).require(:person)
end
end
+
+ test "require array when all required params are present" do
+ safe_params = ActionController::Parameters.new(person: {first_name: 'Gaurish', title: 'Mjallo', city: 'Barcelona'})
+ .require(:person)
+ .require([:first_name, :title])
+
+ assert_kind_of Array, safe_params
+ assert_equal ['Gaurish', 'Mjallo'], safe_params
+ end
+
+ test "require array when a required param is missing" do
+ assert_raises(ActionController::ParameterMissing) do
+ ActionController::Parameters.new(person: {first_name: 'Gaurish', title: nil})
+ .require(:person)
+ .require([:first_name, :title])
+ end
+ end
end
diff --git a/actionpack/test/controller/resources_test.rb b/actionpack/test/controller/resources_test.rb
index dd7c128566..4490abf7b2 100644
--- a/actionpack/test/controller/resources_test.rb
+++ b/actionpack/test/controller/resources_test.rb
@@ -149,7 +149,7 @@ class ResourcesTest < ActionController::TestCase
end
end
- assert_restful_named_routes_for :messages do |options|
+ assert_restful_named_routes_for :messages do
actions.each_key do |action|
assert_named_route "/messages/#{action}", "#{action}_messages_path", :action => action
end
@@ -179,7 +179,7 @@ class ResourcesTest < ActionController::TestCase
end
end
- assert_restful_named_routes_for :messages, :path_prefix => 'threads/1/', :name_prefix => 'thread_', :options => { :thread_id => '1' } do |options|
+ assert_restful_named_routes_for :messages, :path_prefix => 'threads/1/', :name_prefix => 'thread_', :options => { :thread_id => '1' } do
actions.each_key do |action|
assert_named_route "/threads/1/messages/#{action}", "#{action}_thread_messages_path", :action => action
end
@@ -206,7 +206,7 @@ class ResourcesTest < ActionController::TestCase
end
end
- assert_restful_named_routes_for :messages, :path_prefix => 'threads/1/', :name_prefix => 'thread_', :options => { :thread_id => '1' } do |options|
+ assert_restful_named_routes_for :messages, :path_prefix => 'threads/1/', :name_prefix => 'thread_', :options => { :thread_id => '1' } do
actions.each_key do |action|
assert_named_route "/threads/1/messages/#{action}", "#{action}_thread_messages_path", :action => action
end
@@ -236,7 +236,7 @@ class ResourcesTest < ActionController::TestCase
end
end
- assert_restful_named_routes_for :messages, :path_prefix => 'threads/1/', :name_prefix => 'thread_', :options => { :thread_id => '1' } do |options|
+ assert_restful_named_routes_for :messages, :path_prefix => 'threads/1/', :name_prefix => 'thread_', :options => { :thread_id => '1' } do
actions.each_key do |action|
assert_named_route "/threads/1/messages/#{action}.xml", "#{action}_thread_messages_path", :action => action, :format => 'xml'
end
@@ -253,7 +253,7 @@ class ResourcesTest < ActionController::TestCase
assert_recognizes(options.merge(mark_options), :path => mark_path, :method => method)
end
- assert_restful_named_routes_for :messages do |options|
+ assert_restful_named_routes_for :messages do
assert_named_route mark_path, :mark_message_path, mark_options
end
end
@@ -278,7 +278,7 @@ class ResourcesTest < ActionController::TestCase
assert_recognizes(options.merge(mark_options), :path => mark_path, :method => method)
end
- assert_restful_named_routes_for :messages, :path_names => {:new => 'nuevo'} do |options|
+ assert_restful_named_routes_for :messages, :path_names => {:new => 'nuevo'} do
assert_named_route mark_path, :mark_message_path, mark_options
end
end
@@ -304,7 +304,7 @@ class ResourcesTest < ActionController::TestCase
assert_recognizes(options.merge(action_options), :path => action_path, :method => method)
end
- assert_restful_named_routes_for :messages do |options|
+ assert_restful_named_routes_for :messages do
assert_named_route action_path, "#{action}_message_path".to_sym, action_options
end
end
@@ -351,7 +351,7 @@ class ResourcesTest < ActionController::TestCase
assert_recognizes(options.merge(preview_options), :path => preview_path, :method => :post)
end
- assert_restful_named_routes_for :messages do |options|
+ assert_restful_named_routes_for :messages do
assert_named_route preview_path, :preview_new_message_path, preview_options
end
end
@@ -373,7 +373,7 @@ class ResourcesTest < ActionController::TestCase
assert_recognizes(options.merge(preview_options), :path => preview_path, :method => :post)
end
- assert_restful_named_routes_for :messages, :path_prefix => 'threads/1/', :name_prefix => 'thread_', :options => { :thread_id => '1' } do |options|
+ assert_restful_named_routes_for :messages, :path_prefix => 'threads/1/', :name_prefix => 'thread_', :options => { :thread_id => '1' } do
assert_named_route preview_path, :preview_new_thread_message_path, preview_options
end
end
@@ -395,7 +395,7 @@ class ResourcesTest < ActionController::TestCase
assert_recognizes(options.merge(preview_options), :path => preview_path, :method => :post)
end
- assert_restful_named_routes_for :messages, :path_prefix => 'threads/1/', :name_prefix => 'thread_', :options => { :thread_id => '1' } do |options|
+ assert_restful_named_routes_for :messages, :path_prefix => 'threads/1/', :name_prefix => 'thread_', :options => { :thread_id => '1' } do
assert_named_route preview_path, :preview_new_thread_message_path, preview_options
end
end
@@ -519,9 +519,9 @@ class ResourcesTest < ActionController::TestCase
end
def test_should_create_multiple_singleton_resource_routes
- with_singleton_resources :account, :logo do
+ with_singleton_resources :account, :product do
assert_singleton_restful_for :account
- assert_singleton_restful_for :logo
+ assert_singleton_restful_for :product
end
end
@@ -553,7 +553,7 @@ class ResourcesTest < ActionController::TestCase
assert_recognizes(options.merge(reset_options), :path => reset_path, :method => method)
end
- assert_singleton_named_routes_for :account do |options|
+ assert_singleton_named_routes_for :account do
assert_named_route reset_path, :reset_account_path, reset_options
end
end
@@ -577,7 +577,7 @@ class ResourcesTest < ActionController::TestCase
assert_recognizes(options.merge(action_options), :path => action_path, :method => method)
end
- assert_singleton_named_routes_for :account do |options|
+ assert_singleton_named_routes_for :account do
assert_named_route action_path, "#{action}_account_path".to_sym, action_options
end
end
@@ -1070,8 +1070,8 @@ class ResourcesTest < ActionController::TestCase
end
def test_singleton_resource_name_is_not_singularized
- with_singleton_resources(:preferences) do
- assert_singleton_restful_for :preferences
+ with_singleton_resources(:products) do
+ assert_singleton_restful_for :products
end
end
diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb
index a333290ade..4a2b02a003 100644
--- a/actionpack/test/controller/routing_test.rb
+++ b/actionpack/test/controller/routing_test.rb
@@ -327,12 +327,6 @@ class LegacyRouteSetTests < ActiveSupport::TestCase
assert_equal '/stuff', controller.url_for({ :controller => '/stuff', :only_path => true })
end
- def test_ignores_leading_slash
- rs.clear!
- rs.draw { get '/:controller(/:action(/:id))'}
- test_default_setup
- end
-
def test_route_with_colon_first
rs.draw do
get '/:controller/:action/:id', action: 'index', id: nil
@@ -342,6 +336,16 @@ class LegacyRouteSetTests < ActiveSupport::TestCase
assert_equal({controller: 'content', action: 'translate', url: 'example'}, rs.recognize_path('/example'))
end
+ def test_route_with_regexp_for_action
+ rs.draw { get '/:controller/:action', action: /auth[-|_].+/ }
+
+ assert_equal({ action: 'auth_google', controller: 'content' }, rs.recognize_path('/content/auth_google'))
+ assert_equal({ action: 'auth-facebook', controller: 'content' }, rs.recognize_path('/content/auth-facebook'))
+
+ assert_equal '/content/auth_google', url_for(rs, { controller: "content", action: "auth_google" })
+ assert_equal '/content/auth-facebook', url_for(rs, { controller: "content", action: "auth-facebook" })
+ end
+
def test_route_with_regexp_for_controller
rs.draw do
get ':controller/:admintoken(/:action(/:id))', :controller => /admin\/.+/
@@ -1748,40 +1752,10 @@ class RouteSetTest < ActiveSupport::TestCase
include ActionDispatch::RoutingVerbs
- class TestSet < ActionDispatch::Routing::RouteSet
- def initialize(block)
- @block = block
- super()
- end
-
- class Dispatcher < ActionDispatch::Routing::RouteSet::Dispatcher
- def initialize(defaults, set, block)
- super(defaults)
- @block = block
- @set = set
- end
-
- def controller_reference(controller_param)
- block = @block
- set = @set
- Class.new(ActionController::Base) {
- include set.url_helpers
- define_method(:process) { |name| block.call(self) }
- def to_a; [200, {}, []]; end
- }
- end
- end
-
- def dispatcher defaults
- TestSet::Dispatcher.new defaults, self, @block
- end
- end
-
alias :routes :set
def test_generate_with_optional_params_recalls_last_request
- controller = nil
- @set = TestSet.new ->(c) { controller = c }
+ @set = make_set false
set.draw do
get "blog/", :controller => "blog", :action => "index"
diff --git a/actionpack/test/controller/test_case_test.rb b/actionpack/test/controller/test_case_test.rb
index 1c5de983d8..b3c3979c84 100644
--- a/actionpack/test/controller/test_case_test.rb
+++ b/actionpack/test/controller/test_case_test.rb
@@ -4,6 +4,8 @@ require 'active_support/json/decoding'
require 'rails/engine'
class TestCaseTest < ActionController::TestCase
+ def self.fixture_path; end;
+
class TestController < ActionController::Base
def no_op
render plain: 'dummy'
@@ -158,7 +160,7 @@ XML
def setup
super
@controller = TestController.new
- @request.env['PATH_INFO'] = nil
+ @request.delete_header 'PATH_INFO'
@routes = ActionDispatch::Routing::RouteSet.new.tap do |r|
r.draw do
get ':controller(/:action(/:id))'
@@ -849,10 +851,10 @@ XML
end
def test_fixture_path_is_accessed_from_self_instead_of_active_support_test_case
- TestCaseTest.stubs(:fixture_path).returns(FILES_DIR)
-
- uploaded_file = fixture_file_upload('/mona_lisa.jpg', 'image/png')
- assert_equal File.open("#{FILES_DIR}/mona_lisa.jpg", READ_PLAIN).read, uploaded_file.read
+ TestCaseTest.stub :fixture_path, FILES_DIR do
+ uploaded_file = fixture_file_upload('/mona_lisa.jpg', 'image/png')
+ assert_equal File.open("#{FILES_DIR}/mona_lisa.jpg", READ_PLAIN).read, uploaded_file.read
+ end
end
def test_test_uploaded_file_with_binary
@@ -893,13 +895,13 @@ XML
end
def test_fixture_file_upload_relative_to_fixture_path
- TestCaseTest.stubs(:fixture_path).returns(FILES_DIR)
- uploaded_file = fixture_file_upload("mona_lisa.jpg", "image/jpg")
- assert_equal File.open("#{FILES_DIR}/mona_lisa.jpg", READ_PLAIN).read, uploaded_file.read
+ TestCaseTest.stub :fixture_path, FILES_DIR do
+ uploaded_file = fixture_file_upload("mona_lisa.jpg", "image/jpg")
+ assert_equal File.open("#{FILES_DIR}/mona_lisa.jpg", READ_PLAIN).read, uploaded_file.read
+ end
end
def test_fixture_file_upload_ignores_nil_fixture_path
- TestCaseTest.stubs(:fixture_path).returns(nil)
uploaded_file = fixture_file_upload("#{FILES_DIR}/mona_lisa.jpg", "image/jpg")
assert_equal File.open("#{FILES_DIR}/mona_lisa.jpg", READ_PLAIN).read, uploaded_file.read
end
diff --git a/actionpack/test/dispatch/callbacks_test.rb b/actionpack/test/dispatch/callbacks_test.rb
index f767b07e75..5ba76d9ab9 100644
--- a/actionpack/test/dispatch/callbacks_test.rb
+++ b/actionpack/test/dispatch/callbacks_test.rb
@@ -28,7 +28,7 @@ class DispatcherTest < ActiveSupport::TestCase
assert_equal 4, Foo.a
assert_equal 4, Foo.b
- dispatch do |env|
+ dispatch do
raise "error"
end rescue nil
assert_equal 6, Foo.a
diff --git a/actionpack/test/dispatch/cookies_test.rb b/actionpack/test/dispatch/cookies_test.rb
index aca28ae8d1..3454e60697 100644
--- a/actionpack/test/dispatch/cookies_test.rb
+++ b/actionpack/test/dispatch/cookies_test.rb
@@ -321,10 +321,12 @@ class CookiesTest < ActionController::TestCase
end
def test_setting_cookie_with_secure_when_always_write_cookie_is_true
- ActionDispatch::Cookies::CookieJar.any_instance.stubs(:always_write_cookie).returns(true)
+ old_cookie, @request.cookie_jar.always_write_cookie = @request.cookie_jar.always_write_cookie, true
get :authenticate_with_secure
assert_cookie_header "user_name=david; path=/; secure"
assert_equal({"user_name" => "david"}, @response.cookies)
+ ensure
+ @request.cookie_jar.always_write_cookie = old_cookie
end
def test_not_setting_cookie_with_secure
diff --git a/actionpack/test/dispatch/debug_exceptions_test.rb b/actionpack/test/dispatch/debug_exceptions_test.rb
index a867aee7ec..f9f379780c 100644
--- a/actionpack/test/dispatch/debug_exceptions_test.rb
+++ b/actionpack/test/dispatch/debug_exceptions_test.rb
@@ -71,14 +71,6 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest
end
end
- def setup
- app = ActiveSupport::OrderedOptions.new
- app.config = ActiveSupport::OrderedOptions.new
- app.config.assets = ActiveSupport::OrderedOptions.new
- app.config.assets.prefix = '/sprockets'
- Rails.stubs(:application).returns(app)
- end
-
RoutesApp = Struct.new(:routes).new(SharedTestRoutes)
ProductionApp = ActionDispatch::DebugExceptions.new(Boomer.new(false), RoutesApp)
DevelopmentApp = ActionDispatch::DebugExceptions.new(Boomer.new(true), RoutesApp)
@@ -338,36 +330,37 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest
test 'debug exceptions app shows user code that caused the error in source view' do
@app = DevelopmentApp
- Rails.stubs(:root).returns(Pathname.new('.'))
- cleaner = ActiveSupport::BacktraceCleaner.new.tap do |bc|
- bc.add_silencer { |line| line =~ /method_that_raises/ }
- bc.add_silencer { |line| line !~ %r{test/dispatch/debug_exceptions_test.rb} }
- end
+ Rails.stub :root, Pathname.new('.') do
+ cleaner = ActiveSupport::BacktraceCleaner.new.tap do |bc|
+ bc.add_silencer { |line| line =~ /method_that_raises/ }
+ bc.add_silencer { |line| line !~ %r{test/dispatch/debug_exceptions_test.rb} }
+ end
- get '/framework_raises', headers: { 'action_dispatch.backtrace_cleaner' => cleaner }
+ get '/framework_raises', headers: { 'action_dispatch.backtrace_cleaner' => cleaner }
- # Assert correct error
- assert_response 500
- assert_select 'h2', /error in framework/
+ # Assert correct error
+ assert_response 500
+ assert_select 'h2', /error in framework/
- # assert source view line is the call to method_that_raises
- assert_select 'div.source:not(.hidden)' do
- assert_select 'pre .line.active', /method_that_raises/
- end
+ # assert source view line is the call to method_that_raises
+ assert_select 'div.source:not(.hidden)' do
+ assert_select 'pre .line.active', /method_that_raises/
+ end
- # assert first source view (hidden) that throws the error
- assert_select 'div.source:first' do
- assert_select 'pre .line.active', /raise StandardError\.new/
- end
+ # assert first source view (hidden) that throws the error
+ assert_select 'div.source:first' do
+ assert_select 'pre .line.active', /raise StandardError\.new/
+ end
- # assert application trace refers to line that calls method_that_raises is first
- assert_select '#Application-Trace' do
- assert_select 'pre code a:first', %r{test/dispatch/debug_exceptions_test\.rb:\d+:in `call}
- end
+ # assert application trace refers to line that calls method_that_raises is first
+ assert_select '#Application-Trace' do
+ assert_select 'pre code a:first', %r{test/dispatch/debug_exceptions_test\.rb:\d+:in `call}
+ end
- # assert framework trace that that threw the error is first
- assert_select '#Framework-Trace' do
- assert_select 'pre code a:first', /method_that_raises/
+ # assert framework trace that that threw the error is first
+ assert_select '#Framework-Trace' do
+ assert_select 'pre code a:first', /method_that_raises/
+ end
end
end
end
diff --git a/actionpack/test/dispatch/exception_wrapper_test.rb b/actionpack/test/dispatch/exception_wrapper_test.rb
index 3dac582407..f37cce4d45 100644
--- a/actionpack/test/dispatch/exception_wrapper_test.rb
+++ b/actionpack/test/dispatch/exception_wrapper_test.rb
@@ -17,8 +17,6 @@ module ActionDispatch
end
setup do
- Rails.stubs(:root).returns(Pathname.new('.'))
-
@cleaner = ActiveSupport::BacktraceCleaner.new
@cleaner.add_silencer { |line| line !~ /^lib/ }
end
diff --git a/actionpack/test/dispatch/header_test.rb b/actionpack/test/dispatch/header_test.rb
index e2b38c23bc..79600b654b 100644
--- a/actionpack/test/dispatch/header_test.rb
+++ b/actionpack/test/dispatch/header_test.rb
@@ -1,15 +1,19 @@
require "abstract_unit"
class HeaderTest < ActiveSupport::TestCase
+ def make_headers(hash)
+ ActionDispatch::Http::Headers.new ActionDispatch::Request.new hash
+ end
+
setup do
- @headers = ActionDispatch::Http::Headers.new(
+ @headers = make_headers(
"CONTENT_TYPE" => "text/plain",
"HTTP_REFERER" => "/some/page"
)
end
test "#new does not normalize the data" do
- headers = ActionDispatch::Http::Headers.new(
+ headers = make_headers(
"Content-Type" => "application/json",
"HTTP_REFERER" => "/some/page",
"Host" => "http://test.com")
@@ -108,7 +112,7 @@ class HeaderTest < ActiveSupport::TestCase
end
test "env variables with . are not modified" do
- headers = ActionDispatch::Http::Headers.new
+ headers = make_headers({})
headers.merge! "rack.input" => "",
"rack.request.cookie_hash" => "",
"action_dispatch.logger" => ""
@@ -119,7 +123,7 @@ class HeaderTest < ActiveSupport::TestCase
end
test "symbols are treated as strings" do
- headers = ActionDispatch::Http::Headers.new
+ headers = make_headers({})
headers.merge!(:SERVER_NAME => "example.com",
"HTTP_REFERER" => "/",
:Host => "test.com")
@@ -130,7 +134,7 @@ class HeaderTest < ActiveSupport::TestCase
test "headers directly modifies the passed environment" do
env = {"HTTP_REFERER" => "/"}
- headers = ActionDispatch::Http::Headers.new(env)
+ headers = make_headers(env)
headers['Referer'] = "http://example.com/"
headers.merge! "CONTENT_TYPE" => "text/plain"
assert_equal({"HTTP_REFERER"=>"http://example.com/",
diff --git a/actionpack/test/dispatch/live_response_test.rb b/actionpack/test/dispatch/live_response_test.rb
index 5cfa5f7b3b..1c128365c7 100644
--- a/actionpack/test/dispatch/live_response_test.rb
+++ b/actionpack/test/dispatch/live_response_test.rb
@@ -65,7 +65,7 @@ module ActionController
latch = Concurrent::CountDownLatch.new
t = Thread.new {
- @response.stream.each do |chunk|
+ @response.stream.each do
latch.count_down
end
}
diff --git a/actionpack/test/dispatch/mount_test.rb b/actionpack/test/dispatch/mount_test.rb
index 6a439be2b5..d027f09762 100644
--- a/actionpack/test/dispatch/mount_test.rb
+++ b/actionpack/test/dispatch/mount_test.rb
@@ -49,7 +49,7 @@ class TestRoutingMount < ActionDispatch::IntegrationTest
def test_app_name_is_properly_generated_when_engine_is_mounted_in_resources
assert Router.mounted_helpers.method_defined?(:user_fake_mounted_at_resource),
"A mounted helper should be defined with a parent's prefix"
- assert Router.named_routes.routes[:user_fake_mounted_at_resource],
+ assert Router.named_routes.key?(:user_fake_mounted_at_resource),
"A named route should be defined with a parent's prefix"
end
diff --git a/actionpack/test/dispatch/request/session_test.rb b/actionpack/test/dispatch/request/session_test.rb
index 10fb04e230..410e3194e2 100644
--- a/actionpack/test/dispatch/request/session_test.rb
+++ b/actionpack/test/dispatch/request/session_test.rb
@@ -4,40 +4,42 @@ require 'action_dispatch/middleware/session/abstract_store'
module ActionDispatch
class Request
class SessionTest < ActiveSupport::TestCase
+ attr_reader :req
+
+ def setup
+ @req = ActionDispatch::Request.new({})
+ end
+
def test_create_adds_itself_to_env
- env = {}
- s = Session.create(store, env, {})
- assert_equal s, env[Rack::Session::Abstract::ENV_SESSION_KEY]
+ s = Session.create(store, req, {})
+ assert_equal s, req.env[Rack::RACK_SESSION]
end
def test_to_hash
- env = {}
- s = Session.create(store, env, {})
+ s = Session.create(store, req, {})
s['foo'] = 'bar'
assert_equal 'bar', s['foo']
assert_equal({'foo' => 'bar'}, s.to_hash)
end
def test_create_merges_old
- env = {}
- s = Session.create(store, env, {})
+ s = Session.create(store, req, {})
s['foo'] = 'bar'
- s1 = Session.create(store, env, {})
+ s1 = Session.create(store, req, {})
assert_not_equal s, s1
assert_equal 'bar', s1['foo']
end
def test_find
- env = {}
- assert_nil Session.find(env)
+ assert_nil Session.find(req)
- s = Session.create(store, env, {})
- assert_equal s, Session.find(env)
+ s = Session.create(store, req, {})
+ assert_equal s, Session.find(req)
end
def test_destroy
- s = Session.create(store, {}, {})
+ s = Session.create(store, req, {})
s['rails'] = 'ftw'
s.destroy
@@ -46,21 +48,21 @@ module ActionDispatch
end
def test_keys
- s = Session.create(store, {}, {})
+ s = Session.create(store, req, {})
s['rails'] = 'ftw'
s['adequate'] = 'awesome'
assert_equal %w[rails adequate], s.keys
end
def test_values
- s = Session.create(store, {}, {})
+ s = Session.create(store, req, {})
s['rails'] = 'ftw'
s['adequate'] = 'awesome'
assert_equal %w[ftw awesome], s.values
end
def test_clear
- s = Session.create(store, {}, {})
+ s = Session.create(store, req, {})
s['rails'] = 'ftw'
s['adequate'] = 'awesome'
@@ -69,7 +71,7 @@ module ActionDispatch
end
def test_update
- s = Session.create(store, {}, {})
+ s = Session.create(store, req, {})
s['rails'] = 'ftw'
s.update(:rails => 'awesome')
@@ -79,7 +81,7 @@ module ActionDispatch
end
def test_delete
- s = Session.create(store, {}, {})
+ s = Session.create(store, req, {})
s['rails'] = 'ftw'
s.delete('rails')
@@ -88,7 +90,7 @@ module ActionDispatch
end
def test_fetch
- session = Session.create(store, {}, {})
+ session = Session.create(store, req, {})
session['one'] = '1'
assert_equal '1', session.fetch(:one)
diff --git a/actionpack/test/dispatch/routing/inspector_test.rb b/actionpack/test/dispatch/routing/inspector_test.rb
index 4047214843..24bd4b04ec 100644
--- a/actionpack/test/dispatch/routing/inspector_test.rb
+++ b/actionpack/test/dispatch/routing/inspector_test.rb
@@ -12,12 +12,6 @@ module ActionDispatch
class RoutesInspectorTest < ActiveSupport::TestCase
def setup
@set = ActionDispatch::Routing::RouteSet.new
- app = ActiveSupport::OrderedOptions.new
- app.config = ActiveSupport::OrderedOptions.new
- app.config.assets = ActiveSupport::OrderedOptions.new
- app.config.assets.prefix = '/sprockets'
- Rails.stubs(:application).returns(app)
- Rails.stubs(:env).returns("development")
end
def draw(options = {}, &block)
@@ -316,9 +310,6 @@ module ActionDispatch
def test_inspect_routes_shows_resources_route_when_assets_disabled
@set = ActionDispatch::Routing::RouteSet.new
- app = ActiveSupport::OrderedOptions.new
-
- Rails.stubs(:application).returns(app)
output = draw do
get '/cart', to: 'cart#show'
diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb
index 332a550de0..8972f3e74d 100644
--- a/actionpack/test/dispatch/routing_test.rb
+++ b/actionpack/test/dispatch/routing_test.rb
@@ -1,4 +1,3 @@
-# encoding: UTF-8
require 'erb'
require 'abstract_unit'
require 'controller/fake_controllers'
@@ -4190,11 +4189,11 @@ class TestNamedRouteUrlHelpers < ActionDispatch::IntegrationTest
include Routes.url_helpers
test "url helpers do not ignore nil parameters when using non-optimized routes" do
- Routes.stubs(:optimize_routes_generation?).returns(false)
-
- get "/categories/1"
- assert_response :success
- assert_raises(ActionController::UrlGenerationError) { product_path(nil) }
+ Routes.stub :optimize_routes_generation?, false do
+ get "/categories/1"
+ assert_response :success
+ assert_raises(ActionController::UrlGenerationError) { product_path(nil) }
+ end
end
end
diff --git a/actionpack/test/dispatch/session/abstract_store_test.rb b/actionpack/test/dispatch/session/abstract_store_test.rb
index fe1a7b4f86..1c35144e6f 100644
--- a/actionpack/test/dispatch/session/abstract_store_test.rb
+++ b/actionpack/test/dispatch/session/abstract_store_test.rb
@@ -27,7 +27,7 @@ module ActionDispatch
as.call(env)
assert @env
- assert Request::Session.find @env
+ assert Request::Session.find ActionDispatch::Request.new @env
end
def test_new_session_object_is_merged_with_old
@@ -36,11 +36,11 @@ module ActionDispatch
as.call(env)
assert @env
- session = Request::Session.find @env
+ session = Request::Session.find ActionDispatch::Request.new @env
session['foo'] = 'bar'
as.call(@env)
- session1 = Request::Session.find @env
+ session1 = Request::Session.find ActionDispatch::Request.new @env
assert_not_equal session, session1
assert_equal session.to_hash, session1.to_hash
diff --git a/actionpack/test/dispatch/session/cookie_store_test.rb b/actionpack/test/dispatch/session/cookie_store_test.rb
index e432c65c62..f07e215e3a 100644
--- a/actionpack/test/dispatch/session/cookie_store_test.rb
+++ b/actionpack/test/dispatch/session/cookie_store_test.rb
@@ -274,28 +274,32 @@ class CookieStoreTest < ActionDispatch::IntegrationTest
with_test_route_set(:expire_after => 5.hours) do
# First request accesses the session
time = Time.local(2008, 4, 24)
- Time.stubs(:now).returns(time)
- expected_expiry = (time + 5.hours).gmtime.strftime("%a, %d %b %Y %H:%M:%S -0000")
+ cookie_body = nil
- cookies[SessionKey] = SignedBar
+ Time.stub :now, time do
+ expected_expiry = (time + 5.hours).gmtime.strftime("%a, %d %b %Y %H:%M:%S -0000")
- get '/set_session_value'
- assert_response :success
+ cookies[SessionKey] = SignedBar
- cookie_body = response.body
- assert_equal "_myapp_session=#{cookie_body}; path=/; expires=#{expected_expiry}; HttpOnly",
- headers['Set-Cookie']
+ get '/set_session_value'
+ assert_response :success
+
+ cookie_body = response.body
+ assert_equal "_myapp_session=#{cookie_body}; path=/; expires=#{expected_expiry}; HttpOnly",
+ headers['Set-Cookie']
+ end
# Second request does not access the session
time = Time.local(2008, 4, 25)
- Time.stubs(:now).returns(time)
- expected_expiry = (time + 5.hours).gmtime.strftime("%a, %d %b %Y %H:%M:%S -0000")
+ Time.stub :now, time do
+ expected_expiry = (time + 5.hours).gmtime.strftime("%a, %d %b %Y %H:%M:%S -0000")
- get '/no_session_access'
- assert_response :success
+ get '/no_session_access'
+ assert_response :success
- assert_equal "_myapp_session=#{cookie_body}; path=/; expires=#{expected_expiry}; HttpOnly",
- headers['Set-Cookie']
+ assert_equal "_myapp_session=#{cookie_body}; path=/; expires=#{expected_expiry}; HttpOnly",
+ headers['Set-Cookie']
+ end
end
end
diff --git a/actionpack/test/dispatch/session/test_session_test.rb b/actionpack/test/dispatch/session/test_session_test.rb
index 59c030176b..3e61d123e3 100644
--- a/actionpack/test/dispatch/session/test_session_test.rb
+++ b/actionpack/test/dispatch/session/test_session_test.rb
@@ -46,6 +46,16 @@ class ActionController::TestSessionTest < ActiveSupport::TestCase
assert_equal('2', session.fetch(:two, '2'))
end
+ def test_fetch_on_symbol_returns_value
+ session = ActionController::TestSession.new(one: '1')
+ assert_equal('1', session.fetch(:one))
+ end
+
+ def test_fetch_on_string_returns_value
+ session = ActionController::TestSession.new(one: '1')
+ assert_equal('1', session.fetch('one'))
+ end
+
def test_fetch_returns_block_value
session = ActionController::TestSession.new(one: '1')
assert_equal(2, session.fetch('2') { |key| key.to_i })
diff --git a/actionpack/test/dispatch/test_request_test.rb b/actionpack/test/dispatch/test_request_test.rb
index ede1cec4e6..51c469a61a 100644
--- a/actionpack/test/dispatch/test_request_test.rb
+++ b/actionpack/test/dispatch/test_request_test.rb
@@ -53,10 +53,8 @@ class TestRequestTest < ActiveSupport::TestCase
assert_cookies({"user_name" => "david"}, req.cookie_jar)
end
- test "does not complain when Rails.application is nil" do
- Rails.stubs(:application).returns(nil)
+ test "does not complain when there is no application config" do
req = ActionDispatch::TestRequest.create({})
-
assert_equal false, req.env.empty?
end
diff --git a/actionpack/test/journey/nodes/symbol_test.rb b/actionpack/test/journey/nodes/symbol_test.rb
index d411a5018a..adf85b860c 100644
--- a/actionpack/test/journey/nodes/symbol_test.rb
+++ b/actionpack/test/journey/nodes/symbol_test.rb
@@ -5,7 +5,7 @@ module ActionDispatch
module Nodes
class TestSymbol < ActiveSupport::TestCase
def test_default_regexp?
- sym = Symbol.new nil
+ sym = Symbol.new "foo"
assert sym.default_regexp?
sym.regexp = nil
diff --git a/actionview/CHANGELOG.md b/actionview/CHANGELOG.md
index 90c9c2171c..f97ca88b87 100644
--- a/actionview/CHANGELOG.md
+++ b/actionview/CHANGELOG.md
@@ -1,3 +1,13 @@
+* `number_to_currency` and `number_with_delimiter` now accept custom `delimiter_pattern` option
+ to handle placement of delimiter, to support currency formats like INR
+
+ Example:
+
+ number_to_currency(1230000, delimiter_pattern: /(\d+?)(?=(\d\d)+(\d)(?!\d))/, unit: '₹', format: "%u %n")
+ # => '₹ 12,30,000.00'
+
+ *Vipul A M*
+
* Make `disable_with` the default behavior for submit tags. Disables the
button on submit to prevent double submits.
diff --git a/actionview/lib/action_view/layouts.rb b/actionview/lib/action_view/layouts.rb
index 9d636c8c9e..cabba967c9 100644
--- a/actionview/lib/action_view/layouts.rb
+++ b/actionview/lib/action_view/layouts.rb
@@ -277,7 +277,7 @@ module ActionView
remove_possible_method(:_layout)
prefixes = _implied_layout_name =~ /\blayouts/ ? [] : ["layouts"]
- default_behavior = "lookup_context.find_all('#{_implied_layout_name}', #{prefixes.inspect}).first || super"
+ default_behavior = "lookup_context.find_all('#{_implied_layout_name}', #{prefixes.inspect}, false, [], { formats: formats }).first || super"
name_clause = if name
default_behavior
else
@@ -316,7 +316,7 @@ module ActionView
end
self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
- def _layout
+ def _layout(formats)
if _conditional_layout?
#{layout_definition}
else
@@ -372,7 +372,7 @@ module ActionView
end
# This will be overwritten by _write_layout_method
- def _layout; end
+ def _layout(*); end
# Determine the layout for a given name, taking into account the name type.
#
@@ -382,8 +382,8 @@ module ActionView
case name
when String then _normalize_layout(name)
when Proc then name
- when true then Proc.new { _default_layout(true) }
- when :default then Proc.new { _default_layout(false) }
+ when true then Proc.new { |formats| _default_layout(formats, true) }
+ when :default then Proc.new { |formats| _default_layout(formats, false) }
when false, nil then nil
else
raise ArgumentError,
@@ -399,14 +399,15 @@ module ActionView
# Optionally raises an exception if the layout could not be found.
#
# ==== Parameters
+ # * <tt>formats</tt> - The formats accepted to this layout
# * <tt>require_layout</tt> - If set to true and layout is not found,
# an ArgumentError exception is raised (defaults to false)
#
# ==== Returns
# * <tt>template</tt> - The template object for the default layout (or nil)
- def _default_layout(require_layout = false)
+ def _default_layout(formats, require_layout = false)
begin
- value = _layout if action_has_layout?
+ value = _layout(formats) if action_has_layout?
rescue NameError => e
raise e, "Could not render layout: #{e.message}"
end
diff --git a/actionview/lib/action_view/lookup_context.rb b/actionview/lib/action_view/lookup_context.rb
index f4a10aa393..fba9a44cb1 100644
--- a/actionview/lib/action_view/lookup_context.rb
+++ b/actionview/lib/action_view/lookup_context.rb
@@ -229,21 +229,5 @@ module ActionView
super(default_locale)
end
-
- # Uses the first format in the formats array for layout lookup.
- def with_layout_format
- if formats.size == 1
- yield
- else
- old_formats = formats
- _set_detail(:formats, formats[0,1])
-
- begin
- yield
- ensure
- _set_detail(:formats, old_formats)
- end
- end
- end
end
end
diff --git a/actionview/lib/action_view/renderer/streaming_template_renderer.rb b/actionview/lib/action_view/renderer/streaming_template_renderer.rb
index 3ab2cd36fc..f38e2764d0 100644
--- a/actionview/lib/action_view/renderer/streaming_template_renderer.rb
+++ b/actionview/lib/action_view/renderer/streaming_template_renderer.rb
@@ -47,7 +47,7 @@ module ActionView
return [super] unless layout_name && template.supports_streaming?
locals ||= {}
- layout = layout_name && find_layout(layout_name, locals.keys)
+ layout = layout_name && find_layout(layout_name, locals.keys, [formats.first])
Body.new do |buffer|
delayed_render(buffer, template, layout, @view, locals)
diff --git a/actionview/lib/action_view/renderer/template_renderer.rb b/actionview/lib/action_view/renderer/template_renderer.rb
index dbb4855e39..75217e1630 100644
--- a/actionview/lib/action_view/renderer/template_renderer.rb
+++ b/actionview/lib/action_view/renderer/template_renderer.rb
@@ -57,7 +57,7 @@ module ActionView
end
def render_with_layout(path, locals) #:nodoc:
- layout = path && find_layout(path, locals.keys)
+ layout = path && find_layout(path, locals.keys, [formats.first])
content = yield(layout)
if layout
@@ -72,27 +72,28 @@ module ActionView
# This is the method which actually finds the layout using details in the lookup
# context object. If no layout is found, it checks if at least a layout with
# the given name exists across all details before raising the error.
- def find_layout(layout, keys)
- with_layout_format { resolve_layout(layout, keys) }
+ def find_layout(layout, keys, formats)
+ resolve_layout(layout, keys, formats)
end
- def resolve_layout(layout, keys)
+ def resolve_layout(layout, keys, formats)
+ details = @details.dup
+ details[:formats] = formats
+
case layout
when String
begin
if layout =~ /^\//
- with_fallbacks { find_template(layout, nil, false, keys, @details) }
+ with_fallbacks { find_template(layout, nil, false, keys, details) }
else
- find_template(layout, nil, false, keys, @details)
+ find_template(layout, nil, false, keys, details)
end
rescue ActionView::MissingTemplate
all_details = @details.merge(:formats => @lookup_context.default_formats)
raise unless template_exists?(layout, nil, false, keys, all_details)
end
when Proc
- resolve_layout(layout.call, keys)
- when FalseClass
- nil
+ resolve_layout(layout.call(formats), keys, formats)
else
layout
end
diff --git a/actionview/lib/action_view/rendering.rb b/actionview/lib/action_view/rendering.rb
index 86a80a5421..8604637da2 100644
--- a/actionview/lib/action_view/rendering.rb
+++ b/actionview/lib/action_view/rendering.rb
@@ -104,7 +104,7 @@ module ActionView
end
# Assign the rendered format to look up context.
- def _process_format(format, options = {}) #:nodoc:
+ def _process_format(format) #:nodoc:
super
lookup_context.formats = [format.to_sym]
lookup_context.rendered_format = lookup_context.formats.first
diff --git a/actionview/test/abstract_unit.rb b/actionview/test/abstract_unit.rb
index 35f520ed1c..12870acaa6 100644
--- a/actionview/test/abstract_unit.rb
+++ b/actionview/test/abstract_unit.rb
@@ -16,6 +16,7 @@ silence_warnings do
end
require 'active_support/testing/autorun'
+require 'active_support/testing/method_call_assertions'
require 'action_controller'
require 'action_view'
require 'action_view/testing/resolvers'
@@ -281,3 +282,6 @@ def jruby_skip(message = '')
end
require 'mocha/setup' # FIXME: stop using mocha
+class ActiveSupport::TestCase
+ include ActiveSupport::Testing::MethodCallAssertions
+end
diff --git a/actionview/test/template/asset_tag_helper_test.rb b/actionview/test/template/asset_tag_helper_test.rb
index 01fc66bed6..496b33b35e 100644
--- a/actionview/test/template/asset_tag_helper_test.rb
+++ b/actionview/test/template/asset_tag_helper_test.rb
@@ -588,11 +588,13 @@ class AssetTagHelperTest < ActionView::TestCase
end
end
- @controller.request.stubs(:ssl?).returns(false)
- assert_equal "http://assets15.example.com/images/xml.png", image_path("xml.png")
+ @controller.request.stub(:ssl?, false) do
+ assert_equal "http://assets15.example.com/images/xml.png", image_path("xml.png")
+ end
- @controller.request.stubs(:ssl?).returns(true)
- assert_equal "http://localhost/images/xml.png", image_path("xml.png")
+ @controller.request.stub(:ssl?, true) do
+ assert_equal "http://localhost/images/xml.png", image_path("xml.png")
+ end
end
end
diff --git a/actionview/test/template/form_helper_test.rb b/actionview/test/template/form_helper_test.rb
index 37f2d7cad6..aef137935a 100644
--- a/actionview/test/template/form_helper_test.rb
+++ b/actionview/test/template/form_helper_test.rb
@@ -2003,21 +2003,22 @@ class FormHelperTest < ActionView::TestCase
def test_form_for_with_remote_without_html
@post.persisted = false
- @post.stubs(:to_key).returns(nil)
- form_for(@post, remote: true) do |f|
- concat f.text_field(:title)
- concat f.text_area(:body)
- concat f.check_box(:secret)
- end
+ @post.stub(:to_key, nil) do
+ form_for(@post, remote: true) do |f|
+ concat f.text_field(:title)
+ concat f.text_area(:body)
+ concat f.check_box(:secret)
+ end
- expected = whole_form("/posts", "new_post", "new_post", remote: true) do
- "<input name='post[title]' type='text' id='post_title' value='Hello World' />" +
- "<textarea name='post[body]' id='post_body'>\nBack to the hill and over it again!</textarea>" +
- "<input name='post[secret]' type='hidden' value='0' />" +
- "<input name='post[secret]' checked='checked' type='checkbox' id='post_secret' value='1' />"
- end
+ expected = whole_form("/posts", "new_post", "new_post", remote: true) do
+ "<input name='post[title]' type='text' id='post_title' value='Hello World' />" +
+ "<textarea name='post[body]' id='post_body'>\nBack to the hill and over it again!</textarea>" +
+ "<input name='post[secret]' type='hidden' value='0' />" +
+ "<input name='post[secret]' checked='checked' type='checkbox' id='post_secret' value='1' />"
+ end
- assert_dom_equal expected, output_buffer
+ assert_dom_equal expected, output_buffer
+ end
end
def test_form_for_without_object
@@ -2220,16 +2221,17 @@ class FormHelperTest < ActionView::TestCase
def test_submit_with_object_as_new_record_and_locale_strings
with_locale :submit do
@post.persisted = false
- @post.stubs(:to_key).returns(nil)
- form_for(@post) do |f|
- concat f.submit
- end
+ @post.stub(:to_key, nil) do
+ form_for(@post) do |f|
+ concat f.submit
+ end
- expected = whole_form('/posts', 'new_post', 'new_post') do
- "<input name='commit' data-disable-with='Create Post' type='submit' value='Create Post' />"
- end
+ expected = whole_form('/posts', 'new_post', 'new_post') do
+ "<input name='commit' data-disable-with='Create Post' type='submit' value='Create Post' />"
+ end
- assert_dom_equal expected, output_buffer
+ assert_dom_equal expected, output_buffer
+ end
end
end
@@ -2807,11 +2809,12 @@ class FormHelperTest < ActionView::TestCase
def test_nested_fields_label_translation_with_more_than_10_records
@post.comments = Array.new(11) { |id| Comment.new(id + 1) }
- I18n.expects(:t).with('post.comments.body', default: [:"comment.body", ''], scope: "helpers.label").times(11).returns "Write body here"
-
- form_for(@post) do |f|
- f.fields_for(:comments) do |cf|
- concat cf.label(:body)
+ params = 11.times.map { ['post.comments.body', default: [:"comment.body", ''], scope: "helpers.label"] }
+ assert_called_with(I18n, :t, params, returns: "Write body here") do
+ form_for(@post) do |f|
+ f.fields_for(:comments) do |cf|
+ concat cf.label(:body)
+ end
end
end
end
diff --git a/actionview/test/template/form_options_helper_i18n_test.rb b/actionview/test/template/form_options_helper_i18n_test.rb
index 4972ea6511..26ede09a5f 100644
--- a/actionview/test/template/form_options_helper_i18n_test.rb
+++ b/actionview/test/template/form_options_helper_i18n_test.rb
@@ -14,8 +14,9 @@ class FormOptionsHelperI18nTests < ActionView::TestCase
end
def test_select_with_prompt_true_translates_prompt_message
- I18n.expects(:translate).with('helpers.select.prompt', { :default => 'Please select' })
- select('post', 'category', [], :prompt => true)
+ assert_called_with(I18n, :translate, ['helpers.select.prompt', { :default => 'Please select' }]) do
+ select('post', 'category', [], :prompt => true)
+ end
end
def test_select_with_translated_prompt
diff --git a/actionview/test/template/lookup_context_test.rb b/actionview/test/template/lookup_context_test.rb
index 4f7823045e..14dafd7d63 100644
--- a/actionview/test/template/lookup_context_test.rb
+++ b/actionview/test/template/lookup_context_test.rb
@@ -108,10 +108,11 @@ class LookupContextTest < ActiveSupport::TestCase
end
test "found templates respects given formats if one cannot be found from template or handler" do
- ActionView::Template::Handlers::Builder.expects(:default_format).returns(nil)
- @lookup_context.formats = [:text]
- template = @lookup_context.find("hello", %w(test))
- assert_equal [:text], template.formats
+ assert_called(ActionView::Template::Handlers::Builder, :default_format, returns: nil) do
+ @lookup_context.formats = [:text]
+ template = @lookup_context.find("hello", %w(test))
+ assert_equal [:text], template.formats
+ end
end
test "adds fallbacks to view paths when required" do
@@ -210,45 +211,50 @@ end
class LookupContextWithFalseCaching < ActiveSupport::TestCase
def setup
@resolver = ActionView::FixtureResolver.new("test/_foo.erb" => ["Foo", Time.utc(2000)])
- ActionView::Resolver.stubs(:caching?).returns(false)
@lookup_context = ActionView::LookupContext.new(@resolver, {})
end
test "templates are always found in the resolver but timestamp is checked before being compiled" do
- template = @lookup_context.find("foo", %w(test), true)
- assert_equal "Foo", template.source
-
- # Now we are going to change the template, but it won't change the returned template
- # since the timestamp is the same.
- @resolver.hash["test/_foo.erb"][0] = "Bar"
- template = @lookup_context.find("foo", %w(test), true)
- assert_equal "Foo", template.source
-
- # Now update the timestamp.
- @resolver.hash["test/_foo.erb"][1] = Time.now.utc
- template = @lookup_context.find("foo", %w(test), true)
- assert_equal "Bar", template.source
+ ActionView::Resolver.stub(:caching?, false) do
+ template = @lookup_context.find("foo", %w(test), true)
+ assert_equal "Foo", template.source
+
+ # Now we are going to change the template, but it won't change the returned template
+ # since the timestamp is the same.
+ @resolver.hash["test/_foo.erb"][0] = "Bar"
+ template = @lookup_context.find("foo", %w(test), true)
+ assert_equal "Foo", template.source
+
+ # Now update the timestamp.
+ @resolver.hash["test/_foo.erb"][1] = Time.now.utc
+ template = @lookup_context.find("foo", %w(test), true)
+ assert_equal "Bar", template.source
+ end
end
test "if no template was found in the second lookup, with no cache, raise error" do
- template = @lookup_context.find("foo", %w(test), true)
- assert_equal "Foo", template.source
+ ActionView::Resolver.stub(:caching?, false) do
+ template = @lookup_context.find("foo", %w(test), true)
+ assert_equal "Foo", template.source
- @resolver.hash.clear
- assert_raise ActionView::MissingTemplate do
- @lookup_context.find("foo", %w(test), true)
+ @resolver.hash.clear
+ assert_raise ActionView::MissingTemplate do
+ @lookup_context.find("foo", %w(test), true)
+ end
end
end
test "if no template was cached in the first lookup, retrieval should work in the second call" do
- @resolver.hash.clear
- assert_raise ActionView::MissingTemplate do
- @lookup_context.find("foo", %w(test), true)
- end
+ ActionView::Resolver.stub(:caching?, false) do
+ @resolver.hash.clear
+ assert_raise ActionView::MissingTemplate do
+ @lookup_context.find("foo", %w(test), true)
+ end
- @resolver.hash["test/_foo.erb"] = ["Foo", Time.utc(2000)]
- template = @lookup_context.find("foo", %w(test), true)
- assert_equal "Foo", template.source
+ @resolver.hash["test/_foo.erb"] = ["Foo", Time.utc(2000)]
+ template = @lookup_context.find("foo", %w(test), true)
+ assert_equal "Foo", template.source
+ end
end
end
diff --git a/actionview/test/template/number_helper_test.rb b/actionview/test/template/number_helper_test.rb
index b70b750869..631d45586b 100644
--- a/actionview/test/template/number_helper_test.rb
+++ b/actionview/test/template/number_helper_test.rb
@@ -1,3 +1,4 @@
+# encoding: utf-8
require "abstract_unit"
class NumberHelperTest < ActionView::TestCase
@@ -21,6 +22,7 @@ class NumberHelperTest < ActionView::TestCase
assert_equal "&lt;b&gt;1,234,567,890.50&lt;/b&gt; $", number_to_currency("1234567890.50", format: "<b>%n</b> %u")
assert_equal "&lt;b&gt;1,234,567,890.50&lt;/b&gt; $", number_to_currency("-1234567890.50", negative_format: "<b>%n</b> %u")
assert_equal "&lt;b&gt;1,234,567,890.50&lt;/b&gt; $", number_to_currency("-1234567890.50", 'negative_format' => "<b>%n</b> %u")
+ assert_equal '₹ 12,30,000.00', number_to_currency(1230000, delimiter_pattern: /(\d+?)(?=(\d\d)+(\d)(?!\d))/, unit: '₹', format: "%u %n")
end
def test_number_to_percentage
diff --git a/actionview/test/template/template_test.rb b/actionview/test/template/template_test.rb
index d3b51cd629..921011b073 100644
--- a/actionview/test/template/template_test.rb
+++ b/actionview/test/template/template_test.rb
@@ -118,15 +118,17 @@ class TestERBTemplate < ActiveSupport::TestCase
def test_refresh_with_templates
@template = new_template("Hello", :virtual_path => "test/foo/bar")
@template.locals = [:key]
- @context.lookup_context.expects(:find_template).with("bar", %w(test/foo), false, [:key]).returns("template")
- assert_equal "template", @template.refresh(@context)
+ assert_called_with(@context.lookup_context, :find_template,["bar", %w(test/foo), false, [:key]], returns: "template") do
+ assert_equal "template", @template.refresh(@context)
+ end
end
def test_refresh_with_partials
@template = new_template("Hello", :virtual_path => "test/_foo")
@template.locals = [:key]
- @context.lookup_context.expects(:find_template).with("foo", %w(test), true, [:key]).returns("partial")
- assert_equal "partial", @template.refresh(@context)
+ assert_called_with(@context.lookup_context, :find_template,[ "foo", %w(test), true, [:key]], returns: "partial") do
+ assert_equal "partial", @template.refresh(@context)
+ end
end
def test_refresh_raises_an_error_without_virtual_path
diff --git a/actionview/test/template/test_case_test.rb b/actionview/test/template/test_case_test.rb
index 80fbaa8392..b057d43ee0 100644
--- a/actionview/test/template/test_case_test.rb
+++ b/actionview/test/template/test_case_test.rb
@@ -20,6 +20,7 @@ module ActionView
class TestCase
helper ASharedTestHelper
+ DeveloperStruct = Struct.new(:name)
module SharedTests
def self.included(test_case)
@@ -50,7 +51,7 @@ module ActionView
end
test "works without testing a helper module" do
- assert_equal 'Eloy', render('developers/developer', :developer => stub(:name => 'Eloy'))
+ assert_equal 'Eloy', render('developers/developer', :developer => DeveloperStruct.new('Eloy'))
end
test "can render a layout with block" do
@@ -69,13 +70,15 @@ module ActionView
end
test "delegates notice to request.flash[:notice]" do
- view.request.flash.expects(:[]).with(:notice)
- view.notice
+ assert_called_with(view.request.flash, :[], [:notice]) do
+ view.notice
+ end
end
test "delegates alert to request.flash[:alert]" do
- view.request.flash.expects(:[]).with(:alert)
- view.alert
+ assert_called_with(view.request.flash, :[], [:alert]) do
+ view.alert
+ end
end
test "uses controller lookup context" do
@@ -119,7 +122,7 @@ module ActionView
test "helper class that is being tested is always included in view instance" do
@controller.controller_path = 'test'
- @customers = [stub(:name => 'Eloy'), stub(:name => 'Manfred')]
+ @customers = [DeveloperStruct.new('Eloy'), DeveloperStruct.new('Manfred')]
assert_match(/Hello: EloyHello: Manfred/, render(:partial => 'test/from_helper'))
end
end
@@ -255,15 +258,15 @@ module ActionView
end
test "is able to render partials with local variables" do
- assert_equal 'Eloy', render('developers/developer', :developer => stub(:name => 'Eloy'))
+ assert_equal 'Eloy', render('developers/developer', :developer => DeveloperStruct.new('Eloy'))
assert_equal 'Eloy', render(:partial => 'developers/developer',
- :locals => { :developer => stub(:name => 'Eloy') })
+ :locals => { :developer => DeveloperStruct.new('Eloy') })
end
test "is able to render partials from templates and also use instance variables" do
@controller.controller_path = "test"
- @customers = [stub(:name => 'Eloy'), stub(:name => 'Manfred')]
+ @customers = [DeveloperStruct.new('Eloy'), DeveloperStruct.new('Manfred')]
assert_match(/Hello: EloyHello: Manfred/, render(:file => 'test/list'))
end
@@ -272,7 +275,7 @@ module ActionView
view
- @customers = [stub(:name => 'Eloy'), stub(:name => 'Manfred')]
+ @customers = [DeveloperStruct.new('Eloy'), DeveloperStruct.new('Manfred')]
assert_match(/Hello: EloyHello: Manfred/, render(:file => 'test/list'))
end
diff --git a/actionview/test/template/translation_helper_test.rb b/actionview/test/template/translation_helper_test.rb
index 5dc281adb2..749d0dd7fd 100644
--- a/actionview/test/template/translation_helper_test.rb
+++ b/actionview/test/template/translation_helper_test.rb
@@ -42,14 +42,16 @@ class TranslationHelperTest < ActiveSupport::TestCase
end
def test_delegates_setting_to_i18n
- I18n.expects(:translate).with(:foo, :locale => 'en', :raise => true).returns("")
- translate :foo, :locale => 'en'
+ assert_called_with(I18n, :translate, [:foo, :locale => 'en', :raise => true], returns: "") do
+ translate :foo, :locale => 'en'
+ end
end
def test_delegates_localize_to_i18n
@time = Time.utc(2008, 7, 8, 12, 18, 38)
- I18n.expects(:localize).with(@time)
- localize @time
+ assert_called_with(I18n, :localize, [@time]) do
+ localize @time
+ end
end
def test_returns_missing_translation_message_wrapped_into_span
@@ -125,8 +127,9 @@ class TranslationHelperTest < ActiveSupport::TestCase
end
def test_translate_escapes_interpolations_in_translations_with_a_html_suffix
+ word_struct = Struct.new(:to_s)
assert_equal '<a>Hello &lt;World&gt;</a>', translate(:'translations.interpolated_html', :word => '<World>')
- assert_equal '<a>Hello &lt;World&gt;</a>', translate(:'translations.interpolated_html', :word => stub(:to_s => "<World>"))
+ assert_equal '<a>Hello &lt;World&gt;</a>', translate(:'translations.interpolated_html', :word => word_struct.new("<World>"))
end
def test_translate_with_html_count
diff --git a/actionview/test/template/url_helper_test.rb b/actionview/test/template/url_helper_test.rb
index 416d30938a..027b72b354 100644
--- a/actionview/test/template/url_helper_test.rb
+++ b/actionview/test/template/url_helper_test.rb
@@ -1,5 +1,4 @@
require 'abstract_unit'
-require 'minitest/mock'
class UrlHelperTest < ActiveSupport::TestCase
diff --git a/activejob/CHANGELOG.md b/activejob/CHANGELOG.md
index 637096e935..965b556fab 100644
--- a/activejob/CHANGELOG.md
+++ b/activejob/CHANGELOG.md
@@ -1,3 +1,8 @@
+* Implement a simple `AsyncJob` processor and associated `AsyncAdapter` that
+ queue jobs to a `concurrent-ruby` thread pool.
+
+ *Jerry D'Antonio*
+
* Implement `provider_job_id` for `queue_classic` adapter. This requires the
latest, currently unreleased, version of queue_classic.
diff --git a/activejob/Rakefile b/activejob/Rakefile
index 8c86df3c91..d9648a7f16 100644
--- a/activejob/Rakefile
+++ b/activejob/Rakefile
@@ -1,6 +1,6 @@
require 'rake/testtask'
-ACTIVEJOB_ADAPTERS = %w(inline delayed_job qu que queue_classic resque sidekiq sneakers sucker_punch backburner test)
+ACTIVEJOB_ADAPTERS = %w(async inline delayed_job qu que queue_classic resque sidekiq sneakers sucker_punch backburner test)
ACTIVEJOB_ADAPTERS -= %w(queue_classic) if defined?(JRUBY_VERSION)
task default: :test
diff --git a/activejob/lib/active_job.rb b/activejob/lib/active_job.rb
index 3d4f63b261..eb8091a805 100644
--- a/activejob/lib/active_job.rb
+++ b/activejob/lib/active_job.rb
@@ -32,6 +32,7 @@ module ActiveJob
autoload :Base
autoload :QueueAdapters
autoload :ConfiguredJob
+ autoload :AsyncJob
autoload :TestCase
autoload :TestHelper
end
diff --git a/activejob/lib/active_job/async_job.rb b/activejob/lib/active_job/async_job.rb
new file mode 100644
index 0000000000..7fcffc4c24
--- /dev/null
+++ b/activejob/lib/active_job/async_job.rb
@@ -0,0 +1,75 @@
+require 'concurrent'
+require 'thread_safe'
+
+module ActiveJob
+ # == Active Job Async Job
+ #
+ # When enqueueing jobs with Async Job each job will be executed asynchronously
+ # on a +concurrent-ruby+ thread pool. All job data is retained in memory.
+ # Because job data is not saved to a persistent datastore there is no
+ # additional infrastructure needed and jobs process quickly. The lack of
+ # persistence, however, means that all unprocessed jobs will be lost on
+ # application restart. Therefore in-memory queue adapters are unsuitable for
+ # most production environments but are excellent for development and testing.
+ #
+ # Read more about Concurrent Ruby {here}[https://github.com/ruby-concurrency/concurrent-ruby].
+ #
+ # To use Async Job set the queue_adapter config to +:async+.
+ #
+ # Rails.application.config.active_job.queue_adapter = :async
+ #
+ # Async Job supports job queues specified with +queue_as+. Queues are created
+ # automatically as needed and each has its own thread pool.
+ class AsyncJob
+
+ DEFAULT_EXECUTOR_OPTIONS = {
+ min_threads: [2, Concurrent.processor_count].max,
+ max_threads: Concurrent.processor_count * 10,
+ auto_terminate: true,
+ idletime: 60, # 1 minute
+ max_queue: 0, # unlimited
+ fallback_policy: :caller_runs # shouldn't matter -- 0 max queue
+ }.freeze
+
+ QUEUES = ThreadSafe::Cache.new do |hash, queue_name| #:nodoc:
+ hash.compute_if_absent(queue_name) { ActiveJob::AsyncJob.create_thread_pool }
+ end
+
+ class << self
+ # Forces jobs to process immediately when testing the Active Job gem.
+ # This should only be called from within unit tests.
+ def perform_immediately! #:nodoc:
+ @perform_immediately = true
+ end
+
+ # Allows jobs to run asynchronously when testing the Active Job gem.
+ # This should only be called from within unit tests.
+ def perform_asynchronously! #:nodoc:
+ @perform_immediately = false
+ end
+
+ def create_thread_pool #:nodoc:
+ if @perform_immediately
+ Concurrent::ImmediateExecutor.new
+ else
+ Concurrent::ThreadPoolExecutor.new(DEFAULT_EXECUTOR_OPTIONS)
+ end
+ end
+
+ def enqueue(job_data, queue: 'default') #:nodoc:
+ QUEUES[queue].post(job_data) { |job| ActiveJob::Base.execute(job) }
+ end
+
+ def enqueue_at(job_data, timestamp, queue: 'default') #:nodoc:
+ delay = timestamp - Time.current.to_f
+ if delay > 0
+ Concurrent::ScheduledTask.execute(delay, args: [job_data], executor: QUEUES[queue]) do |job|
+ ActiveJob::Base.execute(job)
+ end
+ else
+ enqueue(job_data, queue: queue)
+ end
+ end
+ end
+ end
+end
diff --git a/activejob/lib/active_job/queue_adapters.rb b/activejob/lib/active_job/queue_adapters.rb
index e8ceabaeba..aeb1fe1e73 100644
--- a/activejob/lib/active_job/queue_adapters.rb
+++ b/activejob/lib/active_job/queue_adapters.rb
@@ -12,6 +12,8 @@ module ActiveJob
# * {Sidekiq}[http://sidekiq.org]
# * {Sneakers}[https://github.com/jondot/sneakers]
# * {Sucker Punch}[https://github.com/brandonhilkert/sucker_punch]
+ # * {Active Job Async Job}[http://api.rubyonrails.org/classes/ActiveJob/QueueAdapters/AsyncAdapter.html]
+ # * {Active Job Inline}[http://api.rubyonrails.org/classes/ActiveJob/QueueAdapters/InlineAdapter.html]
#
# === Backends Features
#
@@ -26,6 +28,7 @@ module ActiveJob
# | Sidekiq | Yes | Yes | Yes | Queue | No | Job |
# | Sneakers | Yes | Yes | No | Queue | Queue | No |
# | Sucker Punch | Yes | Yes | No | No | No | No |
+ # | Active Job Async | Yes | Yes | Yes | No | No | No |
# | Active Job Inline | No | Yes | N/A | N/A | N/A | N/A |
#
# ==== Async
@@ -96,9 +99,15 @@ module ActiveJob
#
# N/A: The adapter does not run in a separate process, and therefore doesn't
# support retries.
+ #
+ # === Async and Inline Queue Adapters
+ #
+ # Active Job has two built-in queue adapters intended for development and
+ # testing: +:async+ and +:inline+.
module QueueAdapters
extend ActiveSupport::Autoload
+ autoload :AsyncAdapter
autoload :InlineAdapter
autoload :BackburnerAdapter
autoload :DelayedJobAdapter
diff --git a/activejob/lib/active_job/queue_adapters/async_adapter.rb b/activejob/lib/active_job/queue_adapters/async_adapter.rb
new file mode 100644
index 0000000000..3fc27f56e7
--- /dev/null
+++ b/activejob/lib/active_job/queue_adapters/async_adapter.rb
@@ -0,0 +1,23 @@
+require 'active_job/async_job'
+
+module ActiveJob
+ module QueueAdapters
+ # == Active Job Async adapter
+ #
+ # When enqueueing jobs with the Async adapter the job will be executed
+ # asynchronously using {AsyncJob}[http://api.rubyonrails.org/classes/ActiveJob/AsyncJob.html].
+ #
+ # To use +AsyncJob+ set the queue_adapter config to +:async+.
+ #
+ # Rails.application.config.active_job.queue_adapter = :async
+ class AsyncAdapter
+ def enqueue(job) #:nodoc:
+ ActiveJob::AsyncJob.enqueue(job.serialize, queue: job.queue_name)
+ end
+
+ def enqueue_at(job, timestamp) #:nodoc:
+ ActiveJob::AsyncJob.enqueue_at(job.serialize, timestamp, queue: job.queue_name)
+ end
+ end
+ end
+end
diff --git a/activejob/test/adapters/async.rb b/activejob/test/adapters/async.rb
new file mode 100644
index 0000000000..df58027599
--- /dev/null
+++ b/activejob/test/adapters/async.rb
@@ -0,0 +1,5 @@
+require 'concurrent'
+require 'active_job/async_job'
+
+ActiveJob::Base.queue_adapter = :async
+ActiveJob::AsyncJob.perform_immediately!
diff --git a/activejob/test/cases/async_job_test.rb b/activejob/test/cases/async_job_test.rb
new file mode 100644
index 0000000000..2642cfc608
--- /dev/null
+++ b/activejob/test/cases/async_job_test.rb
@@ -0,0 +1,42 @@
+require 'helper'
+require 'jobs/hello_job'
+require 'jobs/queue_as_job'
+
+class AsyncJobTest < ActiveSupport::TestCase
+ def using_async_adapter?
+ ActiveJob::Base.queue_adapter.is_a? ActiveJob::QueueAdapters::AsyncAdapter
+ end
+
+ setup do
+ ActiveJob::AsyncJob.perform_asynchronously!
+ end
+
+ teardown do
+ ActiveJob::AsyncJob::QUEUES.clear
+ ActiveJob::AsyncJob.perform_immediately!
+ end
+
+ test "#create_thread_pool returns a thread_pool" do
+ thread_pool = ActiveJob::AsyncJob.create_thread_pool
+ assert thread_pool.is_a? Concurrent::ExecutorService
+ assert_not thread_pool.is_a? Concurrent::ImmediateExecutor
+ end
+
+ test "#create_thread_pool returns an ImmediateExecutor after #perform_immediately! is called" do
+ ActiveJob::AsyncJob.perform_immediately!
+ thread_pool = ActiveJob::AsyncJob.create_thread_pool
+ assert thread_pool.is_a? Concurrent::ImmediateExecutor
+ end
+
+ test "enqueuing without specifying a queue uses the default queue" do
+ skip unless using_async_adapter?
+ HelloJob.perform_later
+ assert ActiveJob::AsyncJob::QUEUES.key? 'default'
+ end
+
+ test "enqueuing to a queue that does not exist creates the queue" do
+ skip unless using_async_adapter?
+ QueueAsJob.perform_later
+ assert ActiveJob::AsyncJob::QUEUES.key? QueueAsJob::MY_QUEUE.to_s
+ end
+end
diff --git a/activejob/test/helper.rb b/activejob/test/helper.rb
index 57907042d9..55f690bda8 100644
--- a/activejob/test/helper.rb
+++ b/activejob/test/helper.rb
@@ -3,6 +3,7 @@ require File.expand_path('../../../load_paths', __FILE__)
require 'active_job'
require 'support/job_buffer'
+ActiveSupport.halt_callback_chains_on_return_false = false
GlobalID.app = 'aj'
@adapter = ENV['AJ_ADAPTER'] || 'inline'
diff --git a/activejob/test/integration/queuing_test.rb b/activejob/test/integration/queuing_test.rb
index ca8047ef0b..125ba54302 100644
--- a/activejob/test/integration/queuing_test.rb
+++ b/activejob/test/integration/queuing_test.rb
@@ -11,7 +11,7 @@ class QueuingTest < ActiveSupport::TestCase
end
test 'should not run jobs queued on a non-listening queue' do
- skip if adapter_is?(:inline, :sucker_punch, :que)
+ skip if adapter_is?(:inline, :async, :sucker_punch, :que)
old_queue = TestJob.queue_name
begin
diff --git a/activejob/test/jobs/queue_as_job.rb b/activejob/test/jobs/queue_as_job.rb
new file mode 100644
index 0000000000..897aef52e5
--- /dev/null
+++ b/activejob/test/jobs/queue_as_job.rb
@@ -0,0 +1,10 @@
+require_relative '../support/job_buffer'
+
+class QueueAsJob < ActiveJob::Base
+ MY_QUEUE = :low_priority
+ queue_as MY_QUEUE
+
+ def perform(greeter = "David")
+ JobBuffer.add("#{greeter} says hello")
+ end
+end
diff --git a/activejob/test/support/integration/adapters/async.rb b/activejob/test/support/integration/adapters/async.rb
new file mode 100644
index 0000000000..42beb12b1f
--- /dev/null
+++ b/activejob/test/support/integration/adapters/async.rb
@@ -0,0 +1,9 @@
+module AsyncJobsManager
+ def setup
+ ActiveJob::Base.queue_adapter = :async
+ end
+
+ def clear_jobs
+ ActiveJob::AsyncJob::QUEUES.clear
+ end
+end
diff --git a/activemodel/lib/active_model/serialization.rb b/activemodel/lib/active_model/serialization.rb
index 58d68d9620..341651d00d 100644
--- a/activemodel/lib/active_model/serialization.rb
+++ b/activemodel/lib/active_model/serialization.rb
@@ -31,12 +31,11 @@ module ActiveModel
# of the attributes hash's keys. In order to override this behavior, take a look
# at the private method +read_attribute_for_serialization+.
#
- # Most of the time though, either the JSON or XML serializations are needed.
- # Both of these modules automatically include the
- # <tt>ActiveModel::Serialization</tt> module, so there is no need to
- # explicitly include it.
+ # The JSON serialization is provided by default when you include the
+ # <tt>ActiveModel::Serialization</tt> module, so there is no need to explicitly
+ # include it.
#
- # A minimal implementation including XML and JSON would be:
+ # A minimal implementation including JSON would be:
#
# class Person
# include ActiveModel::Serializers::JSON
diff --git a/activemodel/test/cases/helper.rb b/activemodel/test/cases/helper.rb
index c100646837..0cd80be66f 100644
--- a/activemodel/test/cases/helper.rb
+++ b/activemodel/test/cases/helper.rb
@@ -13,8 +13,6 @@ I18n.enforce_available_locales = false
require 'active_support/testing/autorun'
require 'active_support/testing/method_call_assertions'
-require 'minitest/mock'
-
# Skips the current run on Rubinius using Minitest::Assertions#skip
def rubinius_skip(message = '')
skip message if RUBY_ENGINE == 'rbx'
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index bd86dfb88e..ec3ec06c2b 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,3 +1,44 @@
+* PostgreSQL, `create_schema`, `drop_schema` and `rename_table` now quote
+ schema names.
+
+ Fixes #21418.
+
+ Example:
+
+ create_schema("my.schema")
+ # CREATE SCHEMA "my.schema";
+
+ *Yves Senn*
+
+* PostgreSQL, add `:if_exists` option to `#drop_schema`. This makes it
+ possible to drop a schema that might exist without raising an exception if
+ it doesn't.
+
+ *Yves Senn*
+
+* Only try to nullify has_one target association if the record is persisted.
+
+ Fixes #21223.
+
+ *Agis Anastasopoulos*
+
+* Uniqueness validator raises descriptive error when running on a persisted
+ record without primary key.
+
+ Closes #21304.
+
+ *Yves Senn*
+
+* Add a native JSON data type support in MySQL.
+
+ Example:
+
+ create_table :json_data_type do |t|
+ t.json :settings
+ end
+
+ *Ryuta Kamizono*
+
* Descriptive error message when fixtures contain a missing column.
Closes #21201.
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index a830b0e0e4..19ef37e228 100644
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -619,10 +619,10 @@ module ActiveRecord
# @tag = @post.tags.build name: "ruby"
# @tag.save
#
- # The last line ought to save the through record (a <tt>Taggable</tt>). This will only work if the
+ # The last line ought to save the through record (a <tt>Tagging</tt>). This will only work if the
# <tt>:inverse_of</tt> is set:
#
- # class Taggable < ActiveRecord::Base
+ # class Tagging < ActiveRecord::Base
# belongs_to :post
# belongs_to :tag, inverse_of: :taggings
# end
@@ -643,7 +643,7 @@ module ActiveRecord
# You can turn off the automatic detection of inverse associations by setting
# the <tt>:inverse_of</tt> option to <tt>false</tt> like so:
#
- # class Taggable < ActiveRecord::Base
+ # class Tagging < ActiveRecord::Base
# belongs_to :tag, inverse_of: false
# end
#
diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb
index b5a8c81fe4..29c711082a 100644
--- a/activerecord/lib/active_record/associations/collection_proxy.rb
+++ b/activerecord/lib/active_record/associations/collection_proxy.rb
@@ -227,6 +227,31 @@ module ActiveRecord
@association.last(*args)
end
+ # Gives a record (or N records if a parameter is supplied) from the collection
+ # using the same rules as <tt>ActiveRecord::Base.take</tt>.
+ #
+ # class Person < ActiveRecord::Base
+ # has_many :pets
+ # end
+ #
+ # person.pets
+ # # => [
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
+ # # #<Pet id: 2, name: "Spook", person_id: 1>,
+ # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
+ # # ]
+ #
+ # person.pets.take # => #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>
+ #
+ # person.pets.take(2)
+ # # => [
+ # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
+ # # #<Pet id: 2, name: "Spook", person_id: 1>
+ # # ]
+ #
+ # another_person_without.pets # => []
+ # another_person_without.pets.take # => nil
+ # another_person_without.pets.take(2) # => []
def take(n = nil)
@association.take(n)
end
diff --git a/activerecord/lib/active_record/associations/has_one_association.rb b/activerecord/lib/active_record/associations/has_one_association.rb
index 5a92bc5e8a..1829453d73 100644
--- a/activerecord/lib/active_record/associations/has_one_association.rb
+++ b/activerecord/lib/active_record/associations/has_one_association.rb
@@ -65,7 +65,7 @@ module ActiveRecord
when :destroy
target.destroy
when :nullify
- target.update_columns(reflection.foreign_key => nil)
+ target.update_columns(reflection.foreign_key => nil) if target.persisted?
end
end
end
diff --git a/activerecord/lib/active_record/attribute_methods/read.rb b/activerecord/lib/active_record/attribute_methods/read.rb
index 2363cf7608..5197e21fa4 100644
--- a/activerecord/lib/active_record/attribute_methods/read.rb
+++ b/activerecord/lib/active_record/attribute_methods/read.rb
@@ -56,14 +56,12 @@ module ActiveRecord
end
end
- ID = 'id'.freeze
-
# Returns the value of the attribute identified by <tt>attr_name</tt> after
# it has been typecast (for example, "2004-12-12" in a date column is cast
# to a date object, like Date.new(2004, 12, 12)).
def read_attribute(attr_name, &block)
name = attr_name.to_s
- name = self.class.primary_key if name == ID
+ name = self.class.primary_key if name == 'id'.freeze
_read_attribute(name, &block)
end
diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb
index dbb0e2fab2..d0de42d27c 100644
--- a/activerecord/lib/active_record/autosave_association.rb
+++ b/activerecord/lib/active_record/autosave_association.rb
@@ -234,7 +234,7 @@ module ActiveRecord
super
end
- # Marks this record to be destroyed as part of the parents save transaction.
+ # Marks this record to be destroyed as part of the parent's save transaction.
# This does _not_ actually destroy the record instantly, rather child record will be destroyed
# when <tt>parent.save</tt> is called.
#
@@ -243,7 +243,7 @@ module ActiveRecord
@marked_for_destruction = true
end
- # Returns whether or not this record will be destroyed as part of the parents save transaction.
+ # Returns whether or not this record will be destroyed as part of the parent's save transaction.
#
# Only useful if the <tt>:autosave</tt> option on the parent is enabled for this associated model.
def marked_for_destruction?
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index 55a7e053bc..4b66d8cd36 100644
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -1,5 +1,4 @@
require 'yaml'
-require 'set'
require 'active_support/benchmarkable'
require 'active_support/dependencies'
require 'active_support/descendants_tracker'
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
index d17e272ed1..3115e03ea2 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
@@ -214,6 +214,7 @@ module ActiveRecord
@name = name
end
+ # Returns an array of ColumnDefinition objects for the columns of the table.
def columns; @columns_hash.values; end
# Returns a ColumnDefinition for the column with name +name+.
@@ -369,6 +370,8 @@ module ActiveRecord
self
end
+ # remove the column +name+ from the table.
+ # remove_column(:account_id)
def remove_column(name)
@columns_hash.delete name.to_s
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
index 56227ddd80..ed14c781c6 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
@@ -266,6 +266,11 @@ module ActiveRecord
false
end
+ # Does this adapter support json data type?
+ def supports_json?
+ false
+ end
+
# This is meant to be implemented by the adapters that support extensions
def disable_extension(name)
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
index af156c9c78..7b47539596 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -10,6 +10,10 @@ module ActiveRecord
options[:auto_increment] = true if type == :bigint
super
end
+
+ def json(*args, **options)
+ args.each { |name| column(name, :json, options) }
+ end
end
class ColumnDefinition < ActiveRecord::ConnectionAdapters::ColumnDefinition
@@ -242,17 +246,19 @@ module ActiveRecord
QUOTED_TRUE, QUOTED_FALSE = '1', '0'
NATIVE_DATABASE_TYPES = {
- :primary_key => "int(11) auto_increment PRIMARY KEY",
- :string => { :name => "varchar", :limit => 255 },
- :text => { :name => "text" },
- :integer => { :name => "int", :limit => 4 },
- :float => { :name => "float" },
- :decimal => { :name => "decimal" },
- :datetime => { :name => "datetime" },
- :time => { :name => "time" },
- :date => { :name => "date" },
- :binary => { :name => "blob" },
- :boolean => { :name => "tinyint", :limit => 1 }
+ primary_key: "int(11) auto_increment PRIMARY KEY",
+ string: { name: "varchar", limit: 255 },
+ text: { name: "text" },
+ integer: { name: "int", limit: 4 },
+ float: { name: "float" },
+ decimal: { name: "decimal" },
+ datetime: { name: "datetime" },
+ time: { name: "time" },
+ date: { name: "date" },
+ binary: { name: "blob" },
+ boolean: { name: "tinyint", limit: 1 },
+ bigint: { name: "bigint" },
+ json: { name: "json" },
}
INDEX_TYPES = [:fulltext, :spatial]
@@ -721,8 +727,10 @@ module ActiveRecord
# SHOW VARIABLES LIKE 'name'
def show_variable(name)
- variables = select_all("SHOW VARIABLES LIKE '#{name}'", 'SCHEMA')
+ variables = select_all("select @@#{name} as 'Value'", 'SCHEMA')
variables.first['Value'] unless variables.empty?
+ rescue ActiveRecord::StatementInvalid
+ nil
end
# Returns a table's primary key and belonging sequence.
@@ -790,6 +798,7 @@ module ActiveRecord
m.register_type %r(longblob)i, Type::Binary.new(limit: 2**32 - 1)
m.register_type %r(^float)i, Type::Float.new(limit: 24)
m.register_type %r(^double)i, Type::Float.new(limit: 53)
+ m.register_type %r(^json)i, MysqlJson.new
register_integer_type m, %r(^bigint)i, limit: 8
register_integer_type m, %r(^int)i, limit: 4
@@ -1043,6 +1052,14 @@ module ActiveRecord
end
end
+ class MysqlJson < Type::Internal::AbstractJson # :nodoc:
+ def changed_in_place?(raw_old_value, new_value)
+ # Normalization is required because MySQL JSON data format includes
+ # the space between the elements.
+ super(serialize(deserialize(raw_old_value)), new_value)
+ end
+ end
+
class MysqlString < Type::String # :nodoc:
def serialize(value)
case value
@@ -1063,6 +1080,8 @@ module ActiveRecord
end
end
+ ActiveRecord::Type.register(:json, MysqlJson, adapter: :mysql)
+ ActiveRecord::Type.register(:json, MysqlJson, adapter: :mysql2)
ActiveRecord::Type.register(:string, MysqlString, adapter: :mysql)
ActiveRecord::Type.register(:string, MysqlString, adapter: :mysql2)
end
diff --git a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
index b7db57c9fe..ff43c7ec42 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
@@ -41,6 +41,10 @@ module ActiveRecord
true
end
+ def supports_json?
+ version >= '5.7.8'
+ end
+
# HELPER METHODS ===========================================
def each_hash(result) # :nodoc:
diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
index 2ae462d773..0738c59ddf 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
@@ -223,7 +223,7 @@ module ActiveRecord
return @client_encoding if @client_encoding
result = exec_query(
- "SHOW VARIABLES WHERE Variable_name = 'character_set_client'",
+ "select @@character_set_client",
'SCHEMA')
@client_encoding = ENCODINGS[result.rows.last.last]
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/json.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/json.rb
index 8e1256baad..dbc879ffd4 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/json.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/json.rb
@@ -2,32 +2,7 @@ module ActiveRecord
module ConnectionAdapters
module PostgreSQL
module OID # :nodoc:
- class Json < Type::Value # :nodoc:
- include Type::Helpers::Mutable
-
- def type
- :json
- end
-
- def deserialize(value)
- if value.is_a?(::String)
- ::ActiveSupport::JSON.decode(value) rescue nil
- else
- value
- end
- end
-
- def serialize(value)
- if value.is_a?(::Array) || value.is_a?(::Hash)
- ::ActiveSupport::JSON.encode(value)
- else
- value
- end
- end
-
- def accessor
- ActiveRecord::Store::StringKeyedHashAccessor
- end
+ class Json < Type::Internal::AbstractJson
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
index f175730551..d5879ea7df 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
@@ -31,6 +31,11 @@ module ActiveRecord
Utils.extract_schema_qualified_name(name.to_s).quoted
end
+ # Quotes schema names for use in SQL queries.
+ def quote_schema_name(name)
+ PGconn.quote_ident(name)
+ end
+
def quote_table_name_for_assignment(table, attr)
quote_column_name(attr)
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
index d114cad16b..a3fc8fbc51 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
@@ -68,7 +68,7 @@ module ActiveRecord
execute "DROP DATABASE IF EXISTS #{quote_table_name(name)}"
end
- # Returns the list of all tables in the schema search path or a specified schema.
+ # Returns the list of all tables in the schema search path.
def tables(name = nil)
select_values("SELECT tablename FROM pg_tables WHERE schemaname = ANY(current_schemas(false))", 'SCHEMA')
end
@@ -210,12 +210,12 @@ module ActiveRecord
# Creates a schema for the given schema name.
def create_schema schema_name
- execute "CREATE SCHEMA #{schema_name}"
+ execute "CREATE SCHEMA #{quote_schema_name(schema_name)}"
end
# Drops the schema for the given schema name.
- def drop_schema schema_name
- execute "DROP SCHEMA #{schema_name} CASCADE"
+ def drop_schema(schema_name, options = {})
+ execute "DROP SCHEMA#{' IF EXISTS' if options[:if_exists]} #{quote_schema_name(schema_name)} CASCADE"
end
# Sets the schema search path to a string of comma-separated schema names.
@@ -376,7 +376,7 @@ module ActiveRecord
new_seq = "#{new_name}_#{pk}_seq"
idx = "#{table_name}_pkey"
new_idx = "#{new_name}_pkey"
- execute "ALTER TABLE #{quote_table_name(seq)} RENAME TO #{quote_table_name(new_seq)}"
+ execute "ALTER TABLE #{seq.quoted} RENAME TO #{quote_table_name(new_seq)}"
execute "ALTER INDEX #{quote_table_name(idx)} RENAME TO #{quote_table_name(new_idx)}"
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index 2c43c46a3d..27291bd2ea 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -201,6 +201,10 @@ module ActiveRecord
true
end
+ def supports_json?
+ postgresql_version >= 90200
+ end
+
def index_algorithms
{ concurrently: 'CONCURRENTLY' }
end
diff --git a/activerecord/lib/active_record/enum.rb b/activerecord/lib/active_record/enum.rb
index 91a13cb0cd..4902fcb1a2 100644
--- a/activerecord/lib/active_record/enum.rb
+++ b/activerecord/lib/active_record/enum.rb
@@ -18,10 +18,9 @@ module ActiveRecord
# conversation.archived? # => true
# conversation.status # => "archived"
#
- # # conversation.update! status: 1
+ # # conversation.status = 1
# conversation.status = "archived"
#
- # # conversation.update! status: nil
# conversation.status = nil
# conversation.status.nil? # => true
# conversation.status # => nil
diff --git a/activerecord/lib/active_record/errors.rb b/activerecord/lib/active_record/errors.rb
index d589620f8a..718f04871d 100644
--- a/activerecord/lib/active_record/errors.rb
+++ b/activerecord/lib/active_record/errors.rb
@@ -218,11 +218,12 @@ module ActiveRecord
class UnknownPrimaryKey < ActiveRecordError
attr_reader :model
- def initialize(model)
- super("Unknown primary key for table #{model.table_name} in model #{model}.")
+ def initialize(model, description = nil)
+ message = "Unknown primary key for table #{model.table_name} in model #{model}."
+ message += "\n#{description}" if description
+ super(message)
@model = model
end
-
end
# Raised when a relation cannot be mutated because it's already loaded.
diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb
index b5b91451c7..b4dd8eff5a 100644
--- a/activerecord/lib/active_record/migration.rb
+++ b/activerecord/lib/active_record/migration.rb
@@ -168,7 +168,7 @@ module ActiveRecord
#
# rails generate migration add_fieldname_to_tablename fieldname:string
#
- # This will generate the file <tt>timestamp_add_fieldname_to_tablename</tt>, which will look like this:
+ # This will generate the file <tt>timestamp_add_fieldname_to_tablename.rb</tt>, which will look like this:
# class AddFieldnameToTablename < ActiveRecord::Migration
# def change
# add_column :tablenames, :fieldname, :string
@@ -409,7 +409,10 @@ module ActiveRecord
new.migrate direction
end
- # Disable DDL transactions for this migration.
+ # Disable the transaction wrapping this migration.
+ # You can still create your own transactions even after calling #disable_ddl_transaction!
+ #
+ # For more details read the {"Transactional Migrations" section above}[rdoc-ref:Migration].
def disable_ddl_transaction!
@disable_ddl_transaction = true
end
@@ -456,7 +459,7 @@ module ActiveRecord
# Or equivalently, if +TenderloveMigration+ is defined as in the
# documentation for Migration:
#
- # require_relative '2012121212_tenderlove_migration'
+ # require_relative '20121212123456_tenderlove_migration'
#
# class FixupTLMigration < ActiveRecord::Migration
# def change
@@ -472,13 +475,13 @@ module ActiveRecord
def revert(*migration_classes)
run(*migration_classes.reverse, revert: true) unless migration_classes.empty?
if block_given?
- if @connection.respond_to? :revert
- @connection.revert { yield }
+ if connection.respond_to? :revert
+ connection.revert { yield }
else
- recorder = CommandRecorder.new(@connection)
+ recorder = CommandRecorder.new(connection)
@connection = recorder
suppress_messages do
- @connection.revert { yield }
+ connection.revert { yield }
end
@connection = recorder.delegate
recorder.commands.each do |cmd, args, block|
@@ -489,7 +492,7 @@ module ActiveRecord
end
def reverting?
- @connection.respond_to?(:reverting) && @connection.reverting
+ connection.respond_to?(:reverting) && connection.reverting
end
class ReversibleBlockHelper < Struct.new(:reverting) # :nodoc:
@@ -546,7 +549,7 @@ module ActiveRecord
revert { run(*migration_classes, direction: dir, revert: true) }
else
migration_classes.each do |migration_class|
- migration_class.new.exec_migration(@connection, dir)
+ migration_class.new.exec_migration(connection, dir)
end
end
end
@@ -638,7 +641,7 @@ module ActiveRecord
arg_list = arguments.map(&:inspect) * ', '
say_with_time "#{method}(#{arg_list})" do
- unless @connection.respond_to? :revert
+ unless connection.respond_to? :revert
unless arguments.empty? || [:execute, :enable_extension, :disable_extension].include?(method)
arguments[0] = proper_table_name(arguments.first, table_name_options)
if [:rename_table, :add_foreign_key].include?(method) ||
@@ -811,7 +814,7 @@ module ActiveRecord
new(:up, migrations, target_version).migrate
end
- def down(migrations_paths, target_version = nil, &block)
+ def down(migrations_paths, target_version = nil)
migrations = migrations(migrations_paths)
migrations.select! { |m| yield m } if block_given?
diff --git a/activerecord/lib/active_record/migration/command_recorder.rb b/activerecord/lib/active_record/migration/command_recorder.rb
index dcc2362397..3ab0f28c9b 100644
--- a/activerecord/lib/active_record/migration/command_recorder.rb
+++ b/activerecord/lib/active_record/migration/command_recorder.rb
@@ -5,15 +5,34 @@ module ActiveRecord
# knows how to invert the following commands:
#
# * add_column
+ # * add_foreign_key
# * add_index
+ # * add_reference
# * add_timestamps
- # * create_table
+ # * change_column_default (must supply a :from and :to option)
+ # * change_column_null
# * create_join_table
+ # * create_table
+ # * disable_extension
+ # * drop_join_table
+ # * drop_table (must supply a block)
+ # * enable_extension
+ # * remove_column (must supply a type)
+ # * remove_foreign_key (must supply a second table)
+ # * remove_index
+ # * remove_reference
# * remove_timestamps
# * rename_column
# * rename_index
# * rename_table
class CommandRecorder
+ ReversibleAndIrreversibleMethods = [:create_table, :create_join_table, :rename_table, :add_column, :remove_column,
+ :rename_index, :rename_column, :add_index, :remove_index, :add_timestamps, :remove_timestamps,
+ :change_column_default, :add_reference, :remove_reference, :transaction,
+ :drop_join_table, :drop_table, :execute_block, :enable_extension, :disable_extension,
+ :change_column, :execute, :remove_columns, :change_column_null,
+ :add_foreign_key, :remove_foreign_key
+ ]
include JoinTable
attr_accessor :commands, :delegate, :reverting
@@ -41,7 +60,7 @@ module ActiveRecord
@reverting = !@reverting
end
- # record +command+. +command+ should be a method name and arguments.
+ # Record +command+. +command+ should be a method name and arguments.
# For example:
#
# recorder.record(:method_name, [:arg1, :arg2])
@@ -70,14 +89,7 @@ module ActiveRecord
super || delegate.respond_to?(*args)
end
- [:create_table, :create_join_table, :rename_table, :add_column, :remove_column,
- :rename_index, :rename_column, :add_index, :remove_index, :add_timestamps, :remove_timestamps,
- :add_reference, :remove_reference, :transaction,
- :drop_join_table, :drop_table, :execute_block, :enable_extension,
- :change_column, :execute, :remove_columns, :change_column_null,
- :add_foreign_key, :remove_foreign_key
- # irreversible methods need to be here too
- ].each do |method|
+ ReversibleAndIrreversibleMethods.each do |method|
class_eval <<-EOV, __FILE__, __LINE__ + 1
def #{method}(*args, &block) # def create_table(*args, &block)
record(:"#{method}", args, &block) # record(:create_table, args, &block)
diff --git a/activerecord/lib/active_record/type.rb b/activerecord/lib/active_record/type.rb
index 2c0cda69d0..53f3b53bec 100644
--- a/activerecord/lib/active_record/type.rb
+++ b/activerecord/lib/active_record/type.rb
@@ -20,6 +20,8 @@ require 'active_record/type/adapter_specific_registry'
require 'active_record/type/type_map'
require 'active_record/type/hash_lookup_type_map'
+require 'active_record/type/internal/abstract_json'
+
module ActiveRecord
module Type
@registry = AdapterSpecificRegistry.new
diff --git a/activerecord/lib/active_record/type/internal/abstract_json.rb b/activerecord/lib/active_record/type/internal/abstract_json.rb
new file mode 100644
index 0000000000..963a8245d0
--- /dev/null
+++ b/activerecord/lib/active_record/type/internal/abstract_json.rb
@@ -0,0 +1,33 @@
+module ActiveRecord
+ module Type
+ module Internal # :nodoc:
+ class AbstractJson < Type::Value # :nodoc:
+ include Type::Helpers::Mutable
+
+ def type
+ :json
+ end
+
+ def deserialize(value)
+ if value.is_a?(::String)
+ ::ActiveSupport::JSON.decode(value) rescue nil
+ else
+ value
+ end
+ end
+
+ def serialize(value)
+ if value.is_a?(::Array) || value.is_a?(::Hash)
+ ::ActiveSupport::JSON.encode(value)
+ else
+ value
+ end
+ end
+
+ def accessor
+ ActiveRecord::Store::StringKeyedHashAccessor
+ end
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/validations/uniqueness.rb b/activerecord/lib/active_record/validations/uniqueness.rb
index 32d17a1392..5706bbd903 100644
--- a/activerecord/lib/active_record/validations/uniqueness.rb
+++ b/activerecord/lib/active_record/validations/uniqueness.rb
@@ -18,7 +18,11 @@ module ActiveRecord
relation = build_relation(finder_class, table, attribute, value)
if record.persisted? && finder_class.primary_key.to_s != attribute.to_s
- relation = relation.where.not(finder_class.primary_key => record.id)
+ if finder_class.primary_key
+ relation = relation.where.not(finder_class.primary_key => record.id)
+ else
+ raise UnknownPrimaryKey.new(finder_class, "Can not validate uniqueness for persisted record without primary key.")
+ end
end
relation = scope_relation(record, table, relation)
relation = relation.merge(options[:conditions]) if options[:conditions]
diff --git a/activerecord/test/cases/adapters/mysql2/json_test.rb b/activerecord/test/cases/adapters/mysql2/json_test.rb
new file mode 100644
index 0000000000..c8c933af5e
--- /dev/null
+++ b/activerecord/test/cases/adapters/mysql2/json_test.rb
@@ -0,0 +1,172 @@
+require 'cases/helper'
+require 'support/schema_dumping_helper'
+
+if ActiveRecord::Base.connection.supports_json?
+class Mysql2JSONTest < ActiveRecord::Mysql2TestCase
+ include SchemaDumpingHelper
+ self.use_transactional_tests = false
+
+ class JsonDataType < ActiveRecord::Base
+ self.table_name = 'json_data_type'
+
+ store_accessor :settings, :resolution
+ end
+
+ def setup
+ @connection = ActiveRecord::Base.connection
+ begin
+ @connection.create_table('json_data_type') do |t|
+ t.json 'payload'
+ t.json 'settings'
+ end
+ end
+ end
+
+ def teardown
+ @connection.drop_table :json_data_type, if_exists: true
+ JsonDataType.reset_column_information
+ end
+
+ def test_column
+ column = JsonDataType.columns_hash["payload"]
+ assert_equal :json, column.type
+ assert_equal 'json', column.sql_type
+
+ type = JsonDataType.type_for_attribute("payload")
+ assert_not type.binary?
+ end
+
+ def test_change_table_supports_json
+ @connection.change_table('json_data_type') do |t|
+ t.json 'users'
+ end
+ JsonDataType.reset_column_information
+ column = JsonDataType.columns_hash['users']
+ assert_equal :json, column.type
+ end
+
+ def test_schema_dumping
+ output = dump_table_schema("json_data_type")
+ assert_match(/t\.json\s+"settings"/, output)
+ end
+
+ def test_cast_value_on_write
+ x = JsonDataType.new payload: {"string" => "foo", :symbol => :bar}
+ assert_equal({"string" => "foo", :symbol => :bar}, x.payload_before_type_cast)
+ assert_equal({"string" => "foo", "symbol" => "bar"}, x.payload)
+ x.save
+ assert_equal({"string" => "foo", "symbol" => "bar"}, x.reload.payload)
+ end
+
+ def test_type_cast_json
+ type = JsonDataType.type_for_attribute("payload")
+
+ data = "{\"a_key\":\"a_value\"}"
+ hash = type.deserialize(data)
+ assert_equal({'a_key' => 'a_value'}, hash)
+ assert_equal({'a_key' => 'a_value'}, type.deserialize(data))
+
+ assert_equal({}, type.deserialize("{}"))
+ assert_equal({'key'=>nil}, type.deserialize('{"key": null}'))
+ assert_equal({'c'=>'}','"a"'=>'b "a b'}, type.deserialize(%q({"c":"}", "\"a\"":"b \"a b"})))
+ end
+
+ def test_rewrite
+ @connection.execute "insert into json_data_type (payload) VALUES ('{\"k\":\"v\"}')"
+ x = JsonDataType.first
+ x.payload = { '"a\'' => 'b' }
+ assert x.save!
+ end
+
+ def test_select
+ @connection.execute "insert into json_data_type (payload) VALUES ('{\"k\":\"v\"}')"
+ x = JsonDataType.first
+ assert_equal({'k' => 'v'}, x.payload)
+ end
+
+ def test_select_multikey
+ @connection.execute %q|insert into json_data_type (payload) VALUES ('{"k1":"v1", "k2":"v2", "k3":[1,2,3]}')|
+ x = JsonDataType.first
+ assert_equal({'k1' => 'v1', 'k2' => 'v2', 'k3' => [1,2,3]}, x.payload)
+ end
+
+ def test_null_json
+ @connection.execute %q|insert into json_data_type (payload) VALUES(null)|
+ x = JsonDataType.first
+ assert_equal(nil, x.payload)
+ end
+
+ def test_select_array_json_value
+ @connection.execute %q|insert into json_data_type (payload) VALUES ('["v0",{"k1":"v1"}]')|
+ x = JsonDataType.first
+ assert_equal(['v0', {'k1' => 'v1'}], x.payload)
+ end
+
+ def test_rewrite_array_json_value
+ @connection.execute %q|insert into json_data_type (payload) VALUES ('["v0",{"k1":"v1"}]')|
+ x = JsonDataType.first
+ x.payload = ['v1', {'k2' => 'v2'}, 'v3']
+ assert x.save!
+ end
+
+ def test_with_store_accessors
+ x = JsonDataType.new(resolution: "320×480")
+ assert_equal "320×480", x.resolution
+
+ x.save!
+ x = JsonDataType.first
+ assert_equal "320×480", x.resolution
+
+ x.resolution = "640×1136"
+ x.save!
+
+ x = JsonDataType.first
+ assert_equal "640×1136", x.resolution
+ end
+
+ def test_duplication_with_store_accessors
+ x = JsonDataType.new(resolution: "320×480")
+ assert_equal "320×480", x.resolution
+
+ y = x.dup
+ assert_equal "320×480", y.resolution
+ end
+
+ def test_yaml_round_trip_with_store_accessors
+ x = JsonDataType.new(resolution: "320×480")
+ assert_equal "320×480", x.resolution
+
+ y = YAML.load(YAML.dump(x))
+ assert_equal "320×480", y.resolution
+ end
+
+ def test_changes_in_place
+ json = JsonDataType.new
+ assert_not json.changed?
+
+ json.payload = { 'one' => 'two' }
+ assert json.changed?
+ assert json.payload_changed?
+
+ json.save!
+ assert_not json.changed?
+
+ json.payload['three'] = 'four'
+ assert json.payload_changed?
+
+ json.save!
+ json.reload
+
+ assert_equal({ 'one' => 'two', 'three' => 'four' }, json.payload)
+ assert_not json.changed?
+ end
+
+ def test_assigning_invalid_json
+ json = JsonDataType.new
+
+ json.payload = 'foo'
+
+ assert_nil json.payload
+ end
+end
+end
diff --git a/activerecord/test/cases/adapters/postgresql/schema_authorization_test.rb b/activerecord/test/cases/adapters/postgresql/schema_authorization_test.rb
index fa6584eae5..a0afd922b2 100644
--- a/activerecord/test/cases/adapters/postgresql/schema_authorization_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/schema_authorization_test.rb
@@ -31,7 +31,7 @@ class SchemaAuthorizationTest < ActiveRecord::PostgreSQLTestCase
set_session_auth
@connection.execute "RESET search_path"
USERS.each do |u|
- @connection.execute "DROP SCHEMA #{u} CASCADE"
+ @connection.drop_schema u
@connection.execute "DROP USER #{u}"
end
end
diff --git a/activerecord/test/cases/adapters/postgresql/schema_test.rb b/activerecord/test/cases/adapters/postgresql/schema_test.rb
index 35d5581aa7..ea7e5ac587 100644
--- a/activerecord/test/cases/adapters/postgresql/schema_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/schema_test.rb
@@ -2,7 +2,17 @@ require "cases/helper"
require 'models/default'
require 'support/schema_dumping_helper'
+module PGSchemaHelper
+ def with_schema_search_path(schema_search_path)
+ @connection.schema_search_path = schema_search_path
+ yield if block_given?
+ ensure
+ @connection.schema_search_path = "'$user', public"
+ end
+end
+
class SchemaTest < ActiveRecord::PostgreSQLTestCase
+ include PGSchemaHelper
self.use_transactional_tests = false
SCHEMA_NAME = 'test_schema'
@@ -84,8 +94,8 @@ class SchemaTest < ActiveRecord::PostgreSQLTestCase
end
teardown do
- @connection.execute "DROP SCHEMA #{SCHEMA2_NAME} CASCADE"
- @connection.execute "DROP SCHEMA #{SCHEMA_NAME} CASCADE"
+ @connection.drop_schema SCHEMA2_NAME, if_exists: true
+ @connection.drop_schema SCHEMA_NAME, if_exists: true
end
def test_schema_names
@@ -121,10 +131,17 @@ class SchemaTest < ActiveRecord::PostgreSQLTestCase
assert !@connection.schema_names.include?("test_schema3")
end
+ def test_drop_schema_if_exists
+ @connection.create_schema "some_schema"
+ assert_includes @connection.schema_names, "some_schema"
+ @connection.drop_schema "some_schema", if_exists: true
+ assert_not_includes @connection.schema_names, "some_schema"
+ end
+
def test_habtm_table_name_with_schema
+ ActiveRecord::Base.connection.drop_schema "music", if_exists: true
+ ActiveRecord::Base.connection.create_schema "music"
ActiveRecord::Base.connection.execute <<-SQL
- DROP SCHEMA IF EXISTS music CASCADE;
- CREATE SCHEMA music;
CREATE TABLE music.albums (id serial primary key);
CREATE TABLE music.songs (id serial primary key);
CREATE TABLE music.albums_songs (album_id integer, song_id integer);
@@ -134,12 +151,16 @@ class SchemaTest < ActiveRecord::PostgreSQLTestCase
Album.create
assert_equal song, Song.includes(:albums).references(:albums).first
ensure
- ActiveRecord::Base.connection.execute "DROP SCHEMA music CASCADE;"
+ ActiveRecord::Base.connection.drop_schema "music", if_exists: true
end
- def test_raise_drop_schema_with_nonexisting_schema
+ def test_drop_schema_with_nonexisting_schema
assert_raises(ActiveRecord::StatementInvalid) do
- @connection.drop_schema "test_schema3"
+ @connection.drop_schema "idontexist"
+ end
+
+ assert_nothing_raised do
+ @connection.drop_schema "idontexist", if_exists: true
end
end
@@ -404,13 +425,6 @@ class SchemaTest < ActiveRecord::PostgreSQLTestCase
end
end
- def with_schema_search_path(schema_search_path)
- @connection.schema_search_path = schema_search_path
- yield if block_given?
- ensure
- @connection.schema_search_path = "'$user', public"
- end
-
def do_dump_index_tests_for_schema(this_schema_name, first_index_column_name, second_index_column_name, third_index_column_name, fourth_index_column_name)
with_schema_search_path(this_schema_name) do
indexes = @connection.indexes(TABLE_NAME).sort_by(&:name)
@@ -462,14 +476,14 @@ class SchemaForeignKeyTest < ActiveRecord::PostgreSQLTestCase
ensure
@connection.drop_table "wagons", if_exists: true
@connection.drop_table "my_schema.trains", if_exists: true
- @connection.execute "DROP SCHEMA IF EXISTS my_schema"
+ @connection.drop_schema "my_schema", if_exists: true
end
end
class DefaultsUsingMultipleSchemasAndDomainTest < ActiveRecord::PostgreSQLTestCase
setup do
@connection = ActiveRecord::Base.connection
- @connection.execute "DROP SCHEMA IF EXISTS schema_1 CASCADE"
+ @connection.drop_schema "schema_1", if_exists: true
@connection.execute "CREATE SCHEMA schema_1"
@connection.execute "CREATE DOMAIN schema_1.text AS text"
@connection.execute "CREATE DOMAIN schema_1.varchar AS varchar"
@@ -487,7 +501,7 @@ class DefaultsUsingMultipleSchemasAndDomainTest < ActiveRecord::PostgreSQLTestCa
teardown do
@connection.schema_search_path = @old_search_path
- @connection.execute "DROP SCHEMA IF EXISTS schema_1 CASCADE"
+ @connection.drop_schema "schema_1", if_exists: true
Default.reset_column_information
end
@@ -519,3 +533,40 @@ class DefaultsUsingMultipleSchemasAndDomainTest < ActiveRecord::PostgreSQLTestCa
assert_equal "foo'::bar", Default.new.string_col
end
end
+
+class SchemaWithDotsTest < ActiveRecord::PostgreSQLTestCase
+ include PGSchemaHelper
+ self.use_transactional_tests = false
+
+ setup do
+ @connection = ActiveRecord::Base.connection
+ @connection.create_schema "my.schema"
+ end
+
+ teardown do
+ @connection.drop_schema "my.schema", if_exists: true
+ end
+
+ test "rename_table" do
+ with_schema_search_path('"my.schema"') do
+ @connection.create_table :posts
+ @connection.rename_table :posts, :articles
+ assert_equal ["articles"], @connection.tables
+ end
+ end
+
+ test "Active Record basics" do
+ with_schema_search_path('"my.schema"') do
+ @connection.create_table :articles do |t|
+ t.string :title
+ end
+ article_class = Class.new(ActiveRecord::Base) do
+ self.table_name = '"my.schema".articles'
+ end
+
+ article_class.create!(title: "zOMG, welcome to my blorgh!")
+ welcome_article = article_class.last
+ assert_equal "zOMG, welcome to my blorgh!", welcome_article.title
+ end
+ end
+end
diff --git a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
index 7996e7ad50..77d99bc116 100644
--- a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
@@ -425,13 +425,16 @@ module ActiveRecord
configurations['arunit']['database'])
statement = ::SQLite3::Statement.new(db,
'CREATE TABLE statement_test (number integer not null)')
- statement.stubs(:step).raises(::SQLite3::BusyException, 'busy')
- statement.stubs(:columns).once.returns([])
- statement.expects(:close).once
- ::SQLite3::Statement.stubs(:new).returns(statement)
-
- assert_raises ActiveRecord::StatementInvalid do
- @conn.exec_query 'select * from statement_test'
+ statement.stub(:step, ->{ raise ::SQLite3::BusyException.new('busy') }) do
+ assert_called(statement, :columns, returns: []) do
+ assert_called(statement, :close) do
+ ::SQLite3::Statement.stub(:new, statement) do
+ assert_raises ActiveRecord::StatementInvalid do
+ @conn.exec_query 'select * from statement_test'
+ end
+ end
+ end
+ end
end
end
diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb
index 0d2e2264e3..02b67f901f 100644
--- a/activerecord/test/cases/associations/belongs_to_associations_test.rb
+++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb
@@ -1,6 +1,5 @@
require 'cases/helper'
require 'models/developer'
-require 'models/computer'
require 'models/project'
require 'models/company'
require 'models/topic'
diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb
index 8ed7b4e39f..ddfb856a05 100644
--- a/activerecord/test/cases/associations/eager_test.rb
+++ b/activerecord/test/cases/associations/eager_test.rb
@@ -108,53 +108,57 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
def test_preloading_has_many_in_multiple_queries_with_more_ids_than_database_can_handle
- Comment.connection.expects(:in_clause_length).at_least_once.returns(5)
- posts = Post.all.merge!(:includes=>:comments).to_a
- assert_equal 11, posts.size
+ assert_called(Comment.connection, :in_clause_length, returns: 5) do
+ posts = Post.all.merge!(:includes=>:comments).to_a
+ assert_equal 11, posts.size
+ end
end
def test_preloading_has_many_in_one_queries_when_database_has_no_limit_on_ids_it_can_handle
- Comment.connection.expects(:in_clause_length).at_least_once.returns(nil)
- posts = Post.all.merge!(:includes=>:comments).to_a
- assert_equal 11, posts.size
+ assert_called(Comment.connection, :in_clause_length, returns: nil) do
+ posts = Post.all.merge!(:includes=>:comments).to_a
+ assert_equal 11, posts.size
+ end
end
def test_preloading_habtm_in_multiple_queries_with_more_ids_than_database_can_handle
- Comment.connection.expects(:in_clause_length).at_least_once.returns(5)
- posts = Post.all.merge!(:includes=>:categories).to_a
- assert_equal 11, posts.size
+ assert_called(Comment.connection, :in_clause_length, times: 2, returns: 5) do
+ posts = Post.all.merge!(:includes=>:categories).to_a
+ assert_equal 11, posts.size
+ end
end
def test_preloading_habtm_in_one_queries_when_database_has_no_limit_on_ids_it_can_handle
- Comment.connection.expects(:in_clause_length).at_least_once.returns(nil)
- posts = Post.all.merge!(:includes=>:categories).to_a
- assert_equal 11, posts.size
+ assert_called(Comment.connection, :in_clause_length, times: 2, returns: nil) do
+ posts = Post.all.merge!(:includes=>:categories).to_a
+ assert_equal 11, posts.size
+ end
end
def test_load_associated_records_in_one_query_when_adapter_has_no_limit
- Comment.connection.expects(:in_clause_length).at_least_once.returns(nil)
-
- post = posts(:welcome)
- assert_queries(2) do
- Post.includes(:comments).where(:id => post.id).to_a
+ assert_called(Comment.connection, :in_clause_length, returns: nil) do
+ post = posts(:welcome)
+ assert_queries(2) do
+ Post.includes(:comments).where(:id => post.id).to_a
+ end
end
end
def test_load_associated_records_in_several_queries_when_many_ids_passed
- Comment.connection.expects(:in_clause_length).at_least_once.returns(1)
-
- post1, post2 = posts(:welcome), posts(:thinking)
- assert_queries(3) do
- Post.includes(:comments).where(:id => [post1.id, post2.id]).to_a
+ assert_called(Comment.connection, :in_clause_length, returns: 1) do
+ post1, post2 = posts(:welcome), posts(:thinking)
+ assert_queries(3) do
+ Post.includes(:comments).where(:id => [post1.id, post2.id]).to_a
+ end
end
end
def test_load_associated_records_in_one_query_when_a_few_ids_passed
- Comment.connection.expects(:in_clause_length).at_least_once.returns(3)
-
- post = posts(:welcome)
- assert_queries(2) do
- Post.includes(:comments).where(:id => post.id).to_a
+ assert_called(Comment.connection, :in_clause_length, returns: 3) do
+ post = posts(:welcome)
+ assert_queries(2) do
+ Post.includes(:comments).where(:id => post.id).to_a
+ end
end
end
diff --git a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
index 7718b29125..d160c30375 100644
--- a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
@@ -796,9 +796,10 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
end
def test_association_proxy_transaction_method_starts_transaction_in_association_class
- Post.expects(:transaction)
- Category.first.posts.transaction do
- # nothing
+ assert_called(Post, :transaction) do
+ Category.first.posts.transaction do
+ # nothing
+ end
end
end
diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb
index b22ce42696..cf730e4fe7 100644
--- a/activerecord/test/cases/associations/has_many_through_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb
@@ -744,8 +744,9 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
def test_get_ids_for_has_many_through_with_conditions_should_not_preload
Tagging.create!(:taggable_type => 'Post', :taggable_id => posts(:welcome).id, :tag => tags(:misc))
- ActiveRecord::Associations::Preloader.expects(:new).never
- posts(:welcome).misc_tag_ids
+ assert_not_called(ActiveRecord::Associations::Preloader, :new) do
+ posts(:welcome).misc_tag_ids
+ end
end
def test_get_ids_for_loaded_associations
@@ -765,9 +766,10 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
def test_association_proxy_transaction_method_starts_transaction_in_association_class
- Tag.expects(:transaction)
- Post.first.tags.transaction do
- # nothing
+ assert_called(Tag, :transaction) do
+ Post.first.tags.transaction do
+ # nothing
+ end
end
end
diff --git a/activerecord/test/cases/associations/has_one_associations_test.rb b/activerecord/test/cases/associations/has_one_associations_test.rb
index 5a8afaf4d2..d46e7ad235 100644
--- a/activerecord/test/cases/associations/has_one_associations_test.rb
+++ b/activerecord/test/cases/associations/has_one_associations_test.rb
@@ -107,6 +107,14 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
assert_nil Account.find(old_account_id).firm_id
end
+ def test_nullification_on_destroyed_association
+ developer = Developer.create!(name: "Someone")
+ ship = Ship.create!(name: "Planet Caravan", developer: developer)
+ ship.destroy
+ assert !ship.persisted?
+ assert !developer.persisted?
+ end
+
def test_natural_assignment_to_nil_after_destroy
firm = companies(:rails_core)
old_account_id = firm.account.id
diff --git a/activerecord/test/cases/associations_test.rb b/activerecord/test/cases/associations_test.rb
index 08f790e21b..61e1dd0717 100644
--- a/activerecord/test/cases/associations_test.rb
+++ b/activerecord/test/cases/associations_test.rb
@@ -1,7 +1,6 @@
require "cases/helper"
require 'models/computer'
require 'models/developer'
-require 'models/computer'
require 'models/project'
require 'models/company'
require 'models/categorization'
diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb
index ea2b94cbf4..e2608e3670 100644
--- a/activerecord/test/cases/attribute_methods_test.rb
+++ b/activerecord/test/cases/attribute_methods_test.rb
@@ -1,7 +1,6 @@
require "cases/helper"
require 'models/minimalistic'
require 'models/developer'
-require 'models/computer'
require 'models/auto_id'
require 'models/boolean'
require 'models/computer'
@@ -67,8 +66,9 @@ class AttributeMethodsTest < ActiveRecord::TestCase
def test_caching_nil_primary_key
klass = Class.new(Minimalistic)
- klass.expects(:reset_primary_key).returns(nil).once
- 2.times { klass.primary_key }
+ assert_called(klass, :reset_primary_key, returns: nil) do
+ 2.times { klass.primary_key }
+ end
end
def test_attribute_keys_on_new_instance
diff --git a/activerecord/test/cases/attribute_test.rb b/activerecord/test/cases/attribute_test.rb
index aa419c7a67..0ec368f51d 100644
--- a/activerecord/test/cases/attribute_test.rb
+++ b/activerecord/test/cases/attribute_test.rb
@@ -1,5 +1,4 @@
require 'cases/helper'
-require 'minitest/mock'
module ActiveRecord
class AttributeTest < ActiveRecord::TestCase
diff --git a/activerecord/test/cases/batches_test.rb b/activerecord/test/cases/batches_test.rb
index 48dc8135ab..9cb70ee239 100644
--- a/activerecord/test/cases/batches_test.rb
+++ b/activerecord/test/cases/batches_test.rb
@@ -69,13 +69,15 @@ class EachTest < ActiveRecord::TestCase
end
def test_warn_if_limit_scope_is_set
- ActiveRecord::Base.logger.expects(:warn)
- Post.limit(1).find_each { |post| post }
+ assert_called(ActiveRecord::Base.logger, :warn) do
+ Post.limit(1).find_each { |post| post }
+ end
end
def test_warn_if_order_scope_is_set
- ActiveRecord::Base.logger.expects(:warn)
- Post.order("title").find_each { |post| post }
+ assert_called(ActiveRecord::Base.logger, :warn) do
+ Post.order("title").find_each { |post| post }
+ end
end
def test_logger_not_required
@@ -137,14 +139,15 @@ class EachTest < ActiveRecord::TestCase
def test_find_in_batches_should_not_use_records_after_yielding_them_in_case_original_array_is_modified
not_a_post = "not a post"
- not_a_post.stubs(:id).raises(StandardError, "not_a_post had #id called on it")
-
- assert_nothing_raised do
- Post.find_in_batches(:batch_size => 1) do |batch|
- assert_kind_of Array, batch
- assert_kind_of Post, batch.first
+ def not_a_post.id; end
+ not_a_post.stub(:id, ->{ raise StandardError.new("not_a_post had #id called on it") }) do
+ assert_nothing_raised do
+ Post.find_in_batches(:batch_size => 1) do |batch|
+ assert_kind_of Array, batch
+ assert_kind_of Post, batch.first
- batch.map! { not_a_post }
+ batch.map! { not_a_post }
+ end
end
end
end
diff --git a/activerecord/test/cases/fixtures_test.rb b/activerecord/test/cases/fixtures_test.rb
index 76ea950fb1..64759160dc 100644
--- a/activerecord/test/cases/fixtures_test.rb
+++ b/activerecord/test/cases/fixtures_test.rb
@@ -11,7 +11,6 @@ require 'models/company'
require 'models/computer'
require 'models/course'
require 'models/developer'
-require 'models/computer'
require 'models/joke'
require 'models/matey'
require 'models/parrot'
@@ -259,18 +258,19 @@ class FixturesTest < ActiveRecord::TestCase
def test_fixtures_are_set_up_with_database_env_variable
db_url_tmp = ENV['DATABASE_URL']
ENV['DATABASE_URL'] = "sqlite3::memory:"
- ActiveRecord::Base.stubs(:configurations).returns({})
- test_case = Class.new(ActiveRecord::TestCase) do
- fixtures :accounts
+ ActiveRecord::Base.stub(:configurations, {}) do
+ test_case = Class.new(ActiveRecord::TestCase) do
+ fixtures :accounts
- def test_fixtures
- assert accounts(:signals37)
+ def test_fixtures
+ assert accounts(:signals37)
+ end
end
- end
- result = test_case.new(:test_fixtures).run
+ result = test_case.new(:test_fixtures).run
- assert result.passed?, "Expected #{result.name} to pass:\n#{result}"
+ assert result.passed?, "Expected #{result.name} to pass:\n#{result}"
+ end
ensure
ENV['DATABASE_URL'] = db_url_tmp
end
diff --git a/activerecord/test/cases/helper.rb b/activerecord/test/cases/helper.rb
index d9d0f929db..b61a5126e0 100644
--- a/activerecord/test/cases/helper.rb
+++ b/activerecord/test/cases/helper.rb
@@ -3,6 +3,7 @@ require File.expand_path('../../../../load_paths', __FILE__)
require 'config'
require 'active_support/testing/autorun'
+require 'active_support/testing/method_call_assertions'
require 'stringio'
require 'active_record'
@@ -141,6 +142,7 @@ require "cases/validations_repair_helper"
class ActiveSupport::TestCase
include ActiveRecord::TestFixtures
include ActiveRecord::ValidationsRepairHelper
+ include ActiveSupport::Testing::MethodCallAssertions
self.fixture_path = FIXTURES_ROOT
self.use_instantiated_fixtures = false
diff --git a/activerecord/test/cases/invertible_migration_test.rb b/activerecord/test/cases/invertible_migration_test.rb
index 99230aa3d5..84b0ff8fcb 100644
--- a/activerecord/test/cases/invertible_migration_test.rb
+++ b/activerecord/test/cases/invertible_migration_test.rb
@@ -1,5 +1,8 @@
require "cases/helper"
+class Horse < ActiveRecord::Base
+end
+
module ActiveRecord
class InvertibleMigrationTest < ActiveRecord::TestCase
class SilentMigration < ActiveRecord::Migration
@@ -76,6 +79,32 @@ module ActiveRecord
end
end
+ class ChangeColumnDefault1 < SilentMigration
+ def change
+ create_table("horses") do |t|
+ t.column :name, :string, default: "Sekitoba"
+ end
+ end
+ end
+
+ class ChangeColumnDefault2 < SilentMigration
+ def change
+ change_column_default :horses, :name, from: "Sekitoba", to: "Diomed"
+ end
+ end
+
+ class DisableExtension1 < SilentMigration
+ def change
+ enable_extension "hstore"
+ end
+ end
+
+ class DisableExtension2 < SilentMigration
+ def change
+ disable_extension "hstore"
+ end
+ end
+
class LegacyMigration < ActiveRecord::Migration
def self.up
create_table("horses") do |t|
@@ -223,6 +252,42 @@ module ActiveRecord
assert !revert.connection.table_exists?("horses")
end
+ def test_migrate_revert_change_column_default
+ migration1 = ChangeColumnDefault1.new
+ migration1.migrate(:up)
+ assert_equal "Sekitoba", Horse.new.name
+
+ migration2 = ChangeColumnDefault2.new
+ migration2.migrate(:up)
+ Horse.reset_column_information
+ assert_equal "Diomed", Horse.new.name
+
+ migration2.migrate(:down)
+ Horse.reset_column_information
+ assert_equal "Sekitoba", Horse.new.name
+ end
+
+ if current_adapter?(:PostgreSQLAdapter)
+ def test_migrate_enable_and_disable_extension
+ migration1 = InvertibleMigration.new
+ migration2 = DisableExtension1.new
+ migration3 = DisableExtension2.new
+
+ migration1.migrate(:up)
+ migration2.migrate(:up)
+ assert_equal true, Horse.connection.extension_enabled?('hstore')
+
+ migration3.migrate(:up)
+ assert_equal false, Horse.connection.extension_enabled?('hstore')
+
+ migration3.migrate(:down)
+ assert_equal true, Horse.connection.extension_enabled?('hstore')
+
+ migration2.migrate(:down)
+ assert_equal false, Horse.connection.extension_enabled?('hstore')
+ end
+ end
+
def test_revert_order
block = Proc.new{|t| t.string :name }
recorder = ActiveRecord::Migration::CommandRecorder.new(ActiveRecord::Base.connection)
diff --git a/activerecord/test/cases/migration/change_table_test.rb b/activerecord/test/cases/migration/change_table_test.rb
index 2ffe7a1b0d..2f9c50141f 100644
--- a/activerecord/test/cases/migration/change_table_test.rb
+++ b/activerecord/test/cases/migration/change_table_test.rb
@@ -1,5 +1,4 @@
require "cases/migration/helper"
-require "minitest/mock"
module ActiveRecord
class Migration
diff --git a/activerecord/test/cases/migration/pending_migrations_test.rb b/activerecord/test/cases/migration/pending_migrations_test.rb
index 7afac83bd2..4f5589f32a 100644
--- a/activerecord/test/cases/migration/pending_migrations_test.rb
+++ b/activerecord/test/cases/migration/pending_migrations_test.rb
@@ -1,5 +1,4 @@
require 'cases/helper'
-require "minitest/mock"
module ActiveRecord
class Migration
diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb
index b2f209fe97..128a242495 100644
--- a/activerecord/test/cases/migration_test.rb
+++ b/activerecord/test/cases/migration_test.rb
@@ -115,7 +115,7 @@ class MigrationTest < ActiveRecord::TestCase
end
def test_migration_version
- ActiveRecord::Migrator.run(:up, MIGRATIONS_ROOT + "/version_check", 20131219224947)
+ assert_nothing_raised { ActiveRecord::Migrator.run(:up, MIGRATIONS_ROOT + "/version_check", 20131219224947) }
end
def test_create_table_with_force_true_does_not_drop_nonexisting_table
@@ -132,13 +132,9 @@ class MigrationTest < ActiveRecord::TestCase
Person.connection.drop_table :testings2, if_exists: true
end
- def connection
- ActiveRecord::Base.connection
- end
-
def test_migration_instance_has_connection
migration = Class.new(ActiveRecord::Migration).new
- assert_equal connection, migration.connection
+ assert_equal ActiveRecord::Base.connection, migration.connection
end
def test_method_missing_delegates_to_connection
diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb
index 42e7507631..cdf63957f4 100644
--- a/activerecord/test/cases/persistence_test.rb
+++ b/activerecord/test/cases/persistence_test.rb
@@ -17,6 +17,7 @@ require 'models/minivan'
require 'models/owner'
require 'models/person'
require 'models/pet'
+require 'models/ship'
require 'models/toy'
require 'rexml/document'
diff --git a/activerecord/test/cases/query_cache_test.rb b/activerecord/test/cases/query_cache_test.rb
index 2f0b5df286..d84653e4c9 100644
--- a/activerecord/test/cases/query_cache_test.rb
+++ b/activerecord/test/cases/query_cache_test.rb
@@ -262,61 +262,66 @@ class QueryCacheExpiryTest < ActiveRecord::TestCase
end
def test_find
- Task.connection.expects(:clear_query_cache).times(1)
+ assert_called(Task.connection, :clear_query_cache) do
+ assert !Task.connection.query_cache_enabled
+ Task.cache do
+ assert Task.connection.query_cache_enabled
+ Task.find(1)
- assert !Task.connection.query_cache_enabled
- Task.cache do
- assert Task.connection.query_cache_enabled
- Task.find(1)
+ Task.uncached do
+ assert !Task.connection.query_cache_enabled
+ Task.find(1)
+ end
- Task.uncached do
- assert !Task.connection.query_cache_enabled
- Task.find(1)
+ assert Task.connection.query_cache_enabled
end
-
- assert Task.connection.query_cache_enabled
+ assert !Task.connection.query_cache_enabled
end
- assert !Task.connection.query_cache_enabled
end
def test_update
- Task.connection.expects(:clear_query_cache).times(2)
- Task.cache do
- task = Task.find(1)
- task.starting = Time.now.utc
- task.save!
+ assert_called(Task.connection, :clear_query_cache, times: 2) do
+ Task.cache do
+ task = Task.find(1)
+ task.starting = Time.now.utc
+ task.save!
+ end
end
end
def test_destroy
- Task.connection.expects(:clear_query_cache).times(2)
- Task.cache do
- Task.find(1).destroy
+ assert_called(Task.connection, :clear_query_cache, times: 2) do
+ Task.cache do
+ Task.find(1).destroy
+ end
end
end
def test_insert
- ActiveRecord::Base.connection.expects(:clear_query_cache).times(2)
- Task.cache do
- Task.create!
+ assert_called(ActiveRecord::Base.connection, :clear_query_cache, times: 2) do
+ Task.cache do
+ Task.create!
+ end
end
end
def test_cache_is_expired_by_habtm_update
- ActiveRecord::Base.connection.expects(:clear_query_cache).times(2)
- ActiveRecord::Base.cache do
- c = Category.first
- p = Post.first
- p.categories << c
+ assert_called(ActiveRecord::Base.connection, :clear_query_cache, times: 2) do
+ ActiveRecord::Base.cache do
+ c = Category.first
+ p = Post.first
+ p.categories << c
+ end
end
end
def test_cache_is_expired_by_habtm_delete
- ActiveRecord::Base.connection.expects(:clear_query_cache).times(2)
- ActiveRecord::Base.cache do
- p = Post.find(1)
- assert p.categories.any?
- p.categories.delete_all
+ assert_called(ActiveRecord::Base.connection, :clear_query_cache, times: 2) do
+ ActiveRecord::Base.cache do
+ p = Post.find(1)
+ assert p.categories.any?
+ p.categories.delete_all
+ end
end
end
end
diff --git a/activerecord/test/cases/readonly_test.rb b/activerecord/test/cases/readonly_test.rb
index 1c919f0b57..5f6eb41240 100644
--- a/activerecord/test/cases/readonly_test.rb
+++ b/activerecord/test/cases/readonly_test.rb
@@ -7,6 +7,7 @@ require 'models/computer'
require 'models/project'
require 'models/reader'
require 'models/person'
+require 'models/ship'
class ReadOnlyTest < ActiveRecord::TestCase
fixtures :authors, :posts, :comments, :developers, :projects, :developers_projects, :people, :readers
diff --git a/activerecord/test/cases/reflection_test.rb b/activerecord/test/cases/reflection_test.rb
index 7b47c80331..9c04a41e69 100644
--- a/activerecord/test/cases/reflection_test.rb
+++ b/activerecord/test/cases/reflection_test.rb
@@ -393,12 +393,14 @@ class ReflectionTest < ActiveRecord::TestCase
product = Struct.new(:table_name, :pluralize_table_names).new('products', true)
reflection = ActiveRecord::Reflection.create(:has_many, :categories, nil, {}, product)
- reflection.stubs(:klass).returns(category)
- assert_equal 'categories_products', reflection.join_table
+ reflection.stub(:klass, category) do
+ assert_equal 'categories_products', reflection.join_table
+ end
reflection = ActiveRecord::Reflection.create(:has_many, :products, nil, {}, category)
- reflection.stubs(:klass).returns(product)
- assert_equal 'categories_products', reflection.join_table
+ reflection.stub(:klass, product) do
+ assert_equal 'categories_products', reflection.join_table
+ end
end
def test_join_table_with_common_prefix
@@ -406,12 +408,14 @@ class ReflectionTest < ActiveRecord::TestCase
product = Struct.new(:table_name, :pluralize_table_names).new('catalog_products', true)
reflection = ActiveRecord::Reflection.create(:has_many, :categories, nil, {}, product)
- reflection.stubs(:klass).returns(category)
- assert_equal 'catalog_categories_products', reflection.join_table
+ reflection.stub(:klass, category) do
+ assert_equal 'catalog_categories_products', reflection.join_table
+ end
reflection = ActiveRecord::Reflection.create(:has_many, :products, nil, {}, category)
- reflection.stubs(:klass).returns(product)
- assert_equal 'catalog_categories_products', reflection.join_table
+ reflection.stub(:klass, product) do
+ assert_equal 'catalog_categories_products', reflection.join_table
+ end
end
def test_join_table_with_different_prefix
@@ -419,12 +423,14 @@ class ReflectionTest < ActiveRecord::TestCase
page = Struct.new(:table_name, :pluralize_table_names).new('content_pages', true)
reflection = ActiveRecord::Reflection.create(:has_many, :categories, nil, {}, page)
- reflection.stubs(:klass).returns(category)
- assert_equal 'catalog_categories_content_pages', reflection.join_table
+ reflection.stub(:klass, category) do
+ assert_equal 'catalog_categories_content_pages', reflection.join_table
+ end
reflection = ActiveRecord::Reflection.create(:has_many, :pages, nil, {}, category)
- reflection.stubs(:klass).returns(page)
- assert_equal 'catalog_categories_content_pages', reflection.join_table
+ reflection.stub(:klass, page) do
+ assert_equal 'catalog_categories_content_pages', reflection.join_table
+ end
end
def test_join_table_can_be_overridden
@@ -432,12 +438,14 @@ class ReflectionTest < ActiveRecord::TestCase
product = Struct.new(:table_name, :pluralize_table_names).new('products', true)
reflection = ActiveRecord::Reflection.create(:has_many, :categories, nil, { :join_table => 'product_categories' }, product)
- reflection.stubs(:klass).returns(category)
- assert_equal 'product_categories', reflection.join_table
+ reflection.stub(:klass, category) do
+ assert_equal 'product_categories', reflection.join_table
+ end
reflection = ActiveRecord::Reflection.create(:has_many, :products, nil, { :join_table => 'product_categories' }, category)
- reflection.stubs(:klass).returns(product)
- assert_equal 'product_categories', reflection.join_table
+ reflection.stub(:klass, product) do
+ assert_equal 'product_categories', reflection.join_table
+ end
end
def test_includes_accepts_symbols
diff --git a/activerecord/test/cases/scoping/named_scoping_test.rb b/activerecord/test/cases/scoping/named_scoping_test.rb
index e4cc533517..7a8eaeccb7 100644
--- a/activerecord/test/cases/scoping/named_scoping_test.rb
+++ b/activerecord/test/cases/scoping/named_scoping_test.rb
@@ -188,8 +188,9 @@ class NamedScopingTest < ActiveRecord::TestCase
def test_any_should_call_proxy_found_if_using_a_block
topics = Topic.base
assert_queries(1) do
- topics.expects(:empty?).never
- topics.any? { true }
+ assert_not_called(topics, :empty?) do
+ topics.any? { true }
+ end
end
end
@@ -217,8 +218,9 @@ class NamedScopingTest < ActiveRecord::TestCase
def test_many_should_call_proxy_found_if_using_a_block
topics = Topic.base
assert_queries(1) do
- topics.expects(:size).never
- topics.many? { true }
+ assert_not_called(topics, :size) do
+ topics.many? { true }
+ end
end
end
diff --git a/activerecord/test/cases/validations/i18n_validation_test.rb b/activerecord/test/cases/validations/i18n_validation_test.rb
index 268d7914b5..981239c4d6 100644
--- a/activerecord/test/cases/validations/i18n_validation_test.rb
+++ b/activerecord/test/cases/validations/i18n_validation_test.rb
@@ -53,8 +53,9 @@ class I18nValidationTest < ActiveRecord::TestCase
test "validates_uniqueness_of on generated message #{name}" do
Topic.validates_uniqueness_of :title, validation_options
@topic.title = unique_topic.title
- @topic.errors.expects(:generate_message).with(:title, :taken, generate_message_options.merge(:value => 'unique!'))
- @topic.valid?
+ assert_called_with(@topic.errors, :generate_message, [:title, :taken, generate_message_options.merge(:value => 'unique!')]) do
+ @topic.valid?
+ end
end
end
@@ -63,8 +64,9 @@ class I18nValidationTest < ActiveRecord::TestCase
COMMON_CASES.each do |name, validation_options, generate_message_options|
test "validates_associated on generated message #{name}" do
Topic.validates_associated :replies, validation_options
- replied_topic.errors.expects(:generate_message).with(:replies, :invalid, generate_message_options.merge(:value => replied_topic.replies))
- replied_topic.save
+ assert_called_with(replied_topic.errors, :generate_message, [:replies, :invalid, generate_message_options.merge(:value => replied_topic.replies)]) do
+ replied_topic.save
+ end
end
end
diff --git a/activerecord/test/cases/validations/uniqueness_validation_test.rb b/activerecord/test/cases/validations/uniqueness_validation_test.rb
index ceca2c8366..7502a55391 100644
--- a/activerecord/test/cases/validations/uniqueness_validation_test.rb
+++ b/activerecord/test/cases/validations/uniqueness_validation_test.rb
@@ -4,6 +4,7 @@ require 'models/reply'
require 'models/warehouse_thing'
require 'models/guid'
require 'models/event'
+require 'models/dashboard'
class Wizard < ActiveRecord::Base
self.abstract_class = true
@@ -446,4 +447,26 @@ class UniquenessValidationTest < ActiveRecord::TestCase
key2.key_number = 10
assert_not key2.valid?
end
+
+ def test_validate_uniqueness_without_primary_key
+ klass = Class.new(ActiveRecord::Base) do
+ self.table_name = "dashboards"
+
+ validates_uniqueness_of :dashboard_id
+
+ def self.name; "Dashboard" end
+ end
+
+ abc = klass.create!(dashboard_id: "abc")
+ assert klass.new(dashboard_id: "xyz").valid?
+ assert_not klass.new(dashboard_id: "abc").valid?
+
+ abc.dashboard_id = "def"
+
+ e = assert_raises ActiveRecord::UnknownPrimaryKey do
+ abc.save!
+ end
+ assert_match(/\AUnknown primary key for table dashboards in model/, e.message)
+ assert_match(/Can not validate uniqueness for persisted record without primary key.\z/, e.message)
+ end
end
diff --git a/activerecord/test/models/developer.rb b/activerecord/test/models/developer.rb
index d2a5a7fc49..8ac7a9df6a 100644
--- a/activerecord/test/models/developer.rb
+++ b/activerecord/test/models/developer.rb
@@ -50,6 +50,7 @@ class Developer < ActiveRecord::Base
has_many :firms, :through => :contracts, :source => :firm
has_many :comments, ->(developer) { where(body: "I'm #{developer.name}") }
has_many :ratings, through: :comments
+ has_one :ship, dependent: :nullify
scope :jamises, -> { where(:name => 'Jamis') }
diff --git a/activerecord/test/models/ship.rb b/activerecord/test/models/ship.rb
index 95172e4d3e..e333b964ab 100644
--- a/activerecord/test/models/ship.rb
+++ b/activerecord/test/models/ship.rb
@@ -3,6 +3,7 @@ class Ship < ActiveRecord::Base
belongs_to :pirate
belongs_to :update_only_pirate, :class_name => 'Pirate'
+ belongs_to :developer, dependent: :destroy
has_many :parts, :class_name => 'ShipPart'
has_many :treasures
diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb
index 6f34115534..994ece9244 100644
--- a/activerecord/test/schema/schema.rb
+++ b/activerecord/test/schema/schema.rb
@@ -673,6 +673,7 @@ ActiveRecord::Schema.define do
create_table :ships, force: true do |t|
t.string :name
t.integer :pirate_id
+ t.belongs_to :developer
t.integer :update_only_pirate_id
# Conventionally named column for counter_cache
t.integer :treasures_count, default: 0
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index eaaa3df061..aec8b1efab 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -1,3 +1,13 @@
+* `number_to_currency` and `number_with_delimiter` now accept custom `delimiter_pattern` option
+ to handle placement of delimiter, to support currency formats like INR
+
+ Example:
+
+ number_to_currency(1230000, delimiter_pattern: /(\d+?)(?=(\d\d)+(\d)(?!\d))/, unit: '₹', format: "%u %n")
+ # => '₹ 12,30,000.00'
+
+ *Vipul A M*
+
* Deprecate `:prefix` option of `number_to_human_size` with no replacement.
*Jean Boussier*
diff --git a/activesupport/activesupport.gemspec b/activesupport/activesupport.gemspec
index 6d2ae9be76..67b6663915 100644
--- a/activesupport/activesupport.gemspec
+++ b/activesupport/activesupport.gemspec
@@ -25,6 +25,6 @@ Gem::Specification.new do |s|
s.add_dependency 'tzinfo', '~> 1.1'
s.add_dependency 'minitest', '~> 5.1'
s.add_dependency 'thread_safe','~> 0.3', '>= 0.3.4'
- s.add_dependency 'concurrent-ruby', '~> 0.9.0'
+ s.add_dependency 'concurrent-ruby', '~> 0.9.1'
s.add_dependency 'method_source'
end
diff --git a/activesupport/lib/active_support/array_inquirer.rb b/activesupport/lib/active_support/array_inquirer.rb
index e7188d7adb..f59ddf5403 100644
--- a/activesupport/lib/active_support/array_inquirer.rb
+++ b/activesupport/lib/active_support/array_inquirer.rb
@@ -23,7 +23,7 @@ module ActiveSupport
super
else
candidates.any? do |candidate|
- include?(candidate) || include?(candidate.to_sym)
+ include?(candidate.to_sym) || include?(candidate.to_s)
end
end
end
diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb
index 837974bc85..8253a76383 100644
--- a/activesupport/lib/active_support/cache.rb
+++ b/activesupport/lib/active_support/cache.rb
@@ -26,7 +26,7 @@ module ActiveSupport
end
class << self
- # Creates a new CacheStore object according to the given options.
+ # Creates a new Store object according to the given options.
#
# If no arguments are passed to this method, then a new
# ActiveSupport::Cache::MemoryStore object will be returned.
diff --git a/activesupport/lib/active_support/core_ext/file/atomic.rb b/activesupport/lib/active_support/core_ext/file/atomic.rb
index fad6fa8d9d..ab635f6db8 100644
--- a/activesupport/lib/active_support/core_ext/file/atomic.rb
+++ b/activesupport/lib/active_support/core_ext/file/atomic.rb
@@ -8,43 +8,45 @@ class File
# file.write('hello')
# end
#
- # If your temp directory is not on the same filesystem as the file you're
- # trying to write, you can provide a different temporary directory.
+ # This method needs to create a temporary file. By default it will create it
+ # in the same directory as the destination file. If you don't like this
+ # behaviour you can provide a different directory but it must be on the
+ # same physical filesystem as the the file you're trying to write.
#
# File.atomic_write('/data/something.important', '/data/tmp') do |file|
# file.write('hello')
# end
- def self.atomic_write(file_name, temp_dir = Dir.tmpdir)
+ def self.atomic_write(file_name, temp_dir = dirname(file_name))
require 'tempfile' unless defined?(Tempfile)
- require 'fileutils' unless defined?(FileUtils)
- temp_file = Tempfile.new(basename(file_name), temp_dir)
- temp_file.binmode
- return_val = yield temp_file
- temp_file.close
+ Tempfile.open(".#{basename(file_name)}", temp_dir) do |temp_file|
+ temp_file.binmode
+ return_val = yield temp_file
+ temp_file.close
- if File.exist?(file_name)
- # Get original file permissions
- old_stat = stat(file_name)
- else
- # If not possible, probe which are the default permissions in the
- # destination directory.
- old_stat = probe_stat_in(dirname(file_name))
- end
-
- # Overwrite original file with temp file
- FileUtils.mv(temp_file.path, file_name)
+ old_stat = if exist?(file_name)
+ # Get original file permissions
+ stat(file_name)
+ elsif temp_dir != dirname(file_name)
+ # If not possible, probe which are the default permissions in the
+ # destination directory.
+ probe_stat_in(dirname(file_name))
+ end
- # Set correct permissions on new file
- begin
- chown(old_stat.uid, old_stat.gid, file_name)
- # This operation will affect filesystem ACL's
- chmod(old_stat.mode, file_name)
+ if old_stat
+ # Set correct permissions on new file
+ begin
+ chown(old_stat.uid, old_stat.gid, temp_file.path)
+ # This operation will affect filesystem ACL's
+ chmod(old_stat.mode, temp_file.path)
+ rescue Errno::EPERM, Errno::EACCES
+ # Changing file ownership failed, moving on.
+ end
+ end
- # Make sure we return the result of the yielded block
+ # Overwrite original file with temp file
+ rename(temp_file.path, file_name)
return_val
- rescue Errno::EPERM, Errno::EACCES
- # Changing file ownership failed, moving on.
end
end
diff --git a/activesupport/lib/active_support/core_ext/kernel/reporting.rb b/activesupport/lib/active_support/core_ext/kernel/reporting.rb
index 9189e6d977..8afc258df8 100644
--- a/activesupport/lib/active_support/core_ext/kernel/reporting.rb
+++ b/activesupport/lib/active_support/core_ext/kernel/reporting.rb
@@ -1,5 +1,3 @@
-require 'tempfile'
-
module Kernel
# Sets $VERBOSE to nil for the duration of the block and back to its original
# value afterwards.
diff --git a/activesupport/lib/active_support/core_ext/string/strip.rb b/activesupport/lib/active_support/core_ext/string/strip.rb
index 086c610976..9fdd9d8d2e 100644
--- a/activesupport/lib/active_support/core_ext/string/strip.rb
+++ b/activesupport/lib/active_support/core_ext/string/strip.rb
@@ -17,8 +17,8 @@ class String
#
# the user would see the usage message aligned against the left margin.
#
- # Technically, it looks for the least indented line in the whole string, and removes
- # that amount of leading whitespace.
+ # Technically, it looks for the least indented non-empty line
+ # in the whole string, and removes that amount of leading whitespace.
def strip_heredoc
indent = scan(/^[ \t]*(?=\S)/).min.try(:size) || 0
gsub(/^[ \t]{#{indent}}/, '')
diff --git a/activesupport/lib/active_support/dependencies.rb b/activesupport/lib/active_support/dependencies.rb
index f76ef04f49..8215a3085e 100644
--- a/activesupport/lib/active_support/dependencies.rb
+++ b/activesupport/lib/active_support/dependencies.rb
@@ -421,13 +421,13 @@ module ActiveSupport #:nodoc:
bases.each do |root|
expanded_root = File.expand_path(root)
- next unless %r{\A#{Regexp.escape(expanded_root)}(/|\\)} =~ expanded_path
+ next unless expanded_path.start_with?(expanded_root)
- nesting = expanded_path[(expanded_root.size)..-1]
- nesting = nesting[1..-1] if nesting && nesting[0] == ?/
- next if nesting.blank?
+ root_size = expanded_root.size
+ next if expanded_path[root_size] != ?/.freeze
- paths << nesting.camelize
+ nesting = expanded_path[(root_size + 1)..-1]
+ paths << nesting.camelize unless nesting.blank?
end
paths.uniq!
diff --git a/activesupport/lib/active_support/deprecation/proxy_wrappers.rb b/activesupport/lib/active_support/deprecation/proxy_wrappers.rb
index 9b264cbb79..6572ff78a6 100644
--- a/activesupport/lib/active_support/deprecation/proxy_wrappers.rb
+++ b/activesupport/lib/active_support/deprecation/proxy_wrappers.rb
@@ -20,7 +20,7 @@ module ActiveSupport
private
def method_missing(called, *args, &block)
- warn caller, called, args
+ warn caller_locations, called, args
target.__send__(called, *args, &block)
end
end
diff --git a/activesupport/lib/active_support/deprecation/reporting.rb b/activesupport/lib/active_support/deprecation/reporting.rb
index a7d265d732..bbe25c9260 100644
--- a/activesupport/lib/active_support/deprecation/reporting.rb
+++ b/activesupport/lib/active_support/deprecation/reporting.rb
@@ -14,7 +14,7 @@ module ActiveSupport
def warn(message = nil, callstack = nil)
return if silenced
- callstack ||= caller(2)
+ callstack ||= caller_locations(2)
deprecation_message(callstack, message).tap do |m|
behavior.each { |b| b.call(m, callstack) }
end
@@ -37,7 +37,7 @@ module ActiveSupport
end
def deprecation_warning(deprecated_method_name, message = nil, caller_backtrace = nil)
- caller_backtrace ||= caller(2)
+ caller_backtrace ||= caller_locations(2)
deprecated_method_warning(deprecated_method_name, message).tap do |msg|
warn(msg, caller_backtrace)
end
@@ -79,6 +79,17 @@ module ActiveSupport
end
def extract_callstack(callstack)
+ return _extract_callstack(callstack) if callstack.first.is_a? String
+
+ rails_gem_root = File.expand_path("../../../../..", __FILE__) + "/"
+ offending_line = callstack.find { |frame|
+ !frame.absolute_path.start_with?(rails_gem_root)
+ } || callstack.first
+ [offending_line.path, offending_line.lineno, offending_line.label]
+ end
+
+ def _extract_callstack(callstack)
+ warn "Please pass `caller_locations` to the deprecation API" if $VERBOSE
rails_gem_root = File.expand_path("../../../../..", __FILE__) + "/"
offending_line = callstack.find { |line| !line.start_with?(rails_gem_root) } || callstack.first
if offending_line
diff --git a/activesupport/lib/active_support/log_subscriber/test_helper.rb b/activesupport/lib/active_support/log_subscriber/test_helper.rb
index 70ac4a4d5c..cbc20c103d 100644
--- a/activesupport/lib/active_support/log_subscriber/test_helper.rb
+++ b/activesupport/lib/active_support/log_subscriber/test_helper.rb
@@ -11,6 +11,7 @@ module ActiveSupport
# include ActiveSupport::LogSubscriber::TestHelper
#
# def setup
+ # super
# ActiveRecord::LogSubscriber.attach_to(:active_record)
# end
#
diff --git a/activesupport/lib/active_support/number_helper.rb b/activesupport/lib/active_support/number_helper.rb
index 38a9ce361d..9762d95145 100644
--- a/activesupport/lib/active_support/number_helper.rb
+++ b/activesupport/lib/active_support/number_helper.rb
@@ -135,6 +135,9 @@ module ActiveSupport
# to ",").
# * <tt>:separator</tt> - Sets the separator between the
# fractional and integer digits (defaults to ".").
+ # * <tt>:delimiter_pattern</tt> - Sets a custom regular expression used for
+ # deriving the placement of delimiter. Helpful when using currency formats
+ # like INR.
#
# ==== Examples
#
@@ -147,7 +150,10 @@ module ActiveSupport
# number_to_delimited(12345678.05, locale: :fr) # => 12 345 678,05
# number_to_delimited('112a') # => 112a
# number_to_delimited(98765432.98, delimiter: ' ', separator: ',')
- # # => 98 765 432,98
+ # # => 98 765 432,98
+ # number_to_delimited("123456.78",
+ # delimiter_pattern: /(\d+?)(?=(\d\d)+(\d)(?!\d))/)
+ # # => 1,23,456.78
def number_to_delimited(number, options = {})
NumberToDelimitedConverter.convert(number, options)
end
diff --git a/activesupport/lib/active_support/number_helper/number_to_delimited_converter.rb b/activesupport/lib/active_support/number_helper/number_to_delimited_converter.rb
index d85cc086d7..45ae8f1a93 100644
--- a/activesupport/lib/active_support/number_helper/number_to_delimited_converter.rb
+++ b/activesupport/lib/active_support/number_helper/number_to_delimited_converter.rb
@@ -3,7 +3,7 @@ module ActiveSupport
class NumberToDelimitedConverter < NumberConverter #:nodoc:
self.validate_float = true
- DELIMITED_REGEX = /(\d)(?=(\d\d\d)+(?!\d))/
+ DEFAULT_DELIMITER_REGEX = /(\d)(?=(\d\d\d)+(?!\d))/
def convert
parts.join(options[:separator])
@@ -13,11 +13,16 @@ module ActiveSupport
def parts
left, right = number.to_s.split('.')
- left.gsub!(DELIMITED_REGEX) do |digit_to_delimit|
+ left.gsub!(delimiter_pattern) do |digit_to_delimit|
"#{digit_to_delimit}#{options[:delimiter]}"
end
[left, right].compact
end
+
+ def delimiter_pattern
+ options.fetch(:delimiter_pattern, DEFAULT_DELIMITER_REGEX)
+ end
+
end
end
end
diff --git a/activesupport/lib/active_support/testing/method_call_assertions.rb b/activesupport/lib/active_support/testing/method_call_assertions.rb
index 155d3344d3..fccaa54f40 100644
--- a/activesupport/lib/active_support/testing/method_call_assertions.rb
+++ b/activesupport/lib/active_support/testing/method_call_assertions.rb
@@ -1,11 +1,13 @@
+require 'minitest/mock'
+
module ActiveSupport
module Testing
module MethodCallAssertions # :nodoc:
private
- def assert_called(object, method_name, message = nil, times: 1)
+ def assert_called(object, method_name, message = nil, times: 1, returns: nil)
times_called = 0
- object.stub(method_name, proc { times_called += 1 }) { yield }
+ object.stub(method_name, proc { times_called += 1; returns }) { yield }
error = "Expected #{method_name} to be called #{times} times, " \
"but was called #{times_called} times"
diff --git a/activesupport/lib/active_support/time_with_zone.rb b/activesupport/lib/active_support/time_with_zone.rb
index f8f1b9ac2c..817522677f 100644
--- a/activesupport/lib/active_support/time_with_zone.rb
+++ b/activesupport/lib/active_support/time_with_zone.rb
@@ -246,6 +246,7 @@ module ActiveSupport
utc.future?
end
+ # Returns +true+ if +other+ is equal to current object.
def eql?(other)
other.eql?(utc)
end
@@ -329,6 +330,11 @@ module ActiveSupport
EOV
end
+ # Returns Array of parts of Time in sequence of
+ # [seconds, minutes, hours, day, month, year, weekday, yearday, dst?, zone].
+ #
+ # now = Time.zone.now # => Tue, 18 Aug 2015 02:29:27 UTC +00:00
+ # now.to_a # => [27, 29, 2, 18, 8, 2015, 2, 230, false, "UTC"]
def to_a
[time.sec, time.min, time.hour, time.day, time.mon, time.year, time.wday, time.yday, dst?, zone]
end
@@ -358,11 +364,15 @@ module ActiveSupport
utc.to_r
end
- # Return an instance of Time in the system timezone.
+ # Returns an instance of Time in the system timezone.
def to_time
utc.to_time
end
+ # Returns an instance of DateTime with the timezone's UTC offset
+ #
+ # Time.zone.now.to_datetime # => Tue, 18 Aug 2015 02:32:20 +0000
+ # Time.current.in_time_zone('Hawaii').to_datetime # => Mon, 17 Aug 2015 16:32:20 -1000
def to_datetime
utc.to_datetime.new_offset(Rational(utc_offset, 86_400))
end
diff --git a/activesupport/test/abstract_unit.rb b/activesupport/test/abstract_unit.rb
index 65a8edbabb..c0e23e89f7 100644
--- a/activesupport/test/abstract_unit.rb
+++ b/activesupport/test/abstract_unit.rb
@@ -38,8 +38,6 @@ def jruby_skip(message = '')
skip message if defined?(JRUBY_VERSION)
end
-require 'minitest/mock'
-
class ActiveSupport::TestCase
include ActiveSupport::Testing::MethodCallAssertions
end
diff --git a/activesupport/test/array_inquirer_test.rb b/activesupport/test/array_inquirer_test.rb
index b25e5cca86..263ab3802b 100644
--- a/activesupport/test/array_inquirer_test.rb
+++ b/activesupport/test/array_inquirer_test.rb
@@ -3,7 +3,7 @@ require 'active_support/core_ext/array'
class ArrayInquirerTest < ActiveSupport::TestCase
def setup
- @array_inquirer = ActiveSupport::ArrayInquirer.new([:mobile, :tablet])
+ @array_inquirer = ActiveSupport::ArrayInquirer.new([:mobile, :tablet, 'api'])
end
def test_individual
@@ -18,6 +18,11 @@ class ArrayInquirerTest < ActiveSupport::TestCase
assert_not @array_inquirer.any?(:desktop, :watch)
end
+ def test_any_string_symbol_mismatch
+ assert @array_inquirer.any?('mobile')
+ assert @array_inquirer.any?(:api)
+ end
+
def test_any_with_block
assert @array_inquirer.any? { |v| v == :mobile }
assert_not @array_inquirer.any? { |v| v == :desktop }
@@ -28,7 +33,7 @@ class ArrayInquirerTest < ActiveSupport::TestCase
end
def test_inquiry
- result = [:mobile, :tablet].inquiry
+ result = [:mobile, :tablet, 'api'].inquiry
assert_instance_of ActiveSupport::ArrayInquirer, result
assert_equal @array_inquirer, result
diff --git a/activesupport/test/number_helper_test.rb b/activesupport/test/number_helper_test.rb
index cb5230c4eb..944bce1b41 100644
--- a/activesupport/test/number_helper_test.rb
+++ b/activesupport/test/number_helper_test.rb
@@ -106,6 +106,7 @@ module ActiveSupport
assert_equal("123,456,789.78901", number_helper.number_to_delimited(123456789.78901))
assert_equal("0.78901", number_helper.number_to_delimited(0.78901))
assert_equal("123,456.78", number_helper.number_to_delimited("123456.78"))
+ assert_equal("1,23,456.78", number_helper.number_to_delimited("123456.78", delimiter_pattern: /(\d+?)(?=(\d\d)+(\d)(?!\d))/))
assert_equal("123,456.78", number_helper.number_to_delimited("123456.78".html_safe))
end
end
diff --git a/activesupport/test/testing/method_call_assertions_test.rb b/activesupport/test/testing/method_call_assertions_test.rb
index 2939cf0233..3e5ba7c079 100644
--- a/activesupport/test/testing/method_call_assertions_test.rb
+++ b/activesupport/test/testing/method_call_assertions_test.rb
@@ -33,6 +33,12 @@ class MethodCallAssertionsTest < ActiveSupport::TestCase
end
end
+ def test_assert_called_returns
+ assert_called(@object, :increment, returns: 10) do
+ assert_equal 10, @object.increment
+ end
+ end
+
def test_assert_called_failure
error = assert_raises(Minitest::Assertion) do
assert_called(@object, :increment) do
diff --git a/guides/CHANGELOG.md b/guides/CHANGELOG.md
index fd177b4238..09fb7b1a0e 100644
--- a/guides/CHANGELOG.md
+++ b/guides/CHANGELOG.md
@@ -1,3 +1,7 @@
+* Add code of conduct to contributing guide
+
+ *Jon Moss*
+
* New section in Configuring: Configuring Active Job
*Eliot Sykes*
diff --git a/guides/bug_report_templates/action_controller_master.rb b/guides/bug_report_templates/action_controller_master.rb
index 1a4b736348..31a3f6c650 100644
--- a/guides/bug_report_templates/action_controller_master.rb
+++ b/guides/bug_report_templates/action_controller_master.rb
@@ -9,6 +9,7 @@ gemfile(true) do
source 'https://rubygems.org'
gem 'rails', github: 'rails/rails'
gem 'arel', github: 'rails/arel'
+ gem 'rack', github: 'rack/rack'
end
require 'action_controller/railtie'
diff --git a/guides/bug_report_templates/active_record_master.rb b/guides/bug_report_templates/active_record_master.rb
index 270dbe7df7..6ae50b2460 100644
--- a/guides/bug_report_templates/active_record_master.rb
+++ b/guides/bug_report_templates/active_record_master.rb
@@ -9,6 +9,7 @@ gemfile(true) do
source 'https://rubygems.org'
gem 'rails', github: 'rails/rails'
gem 'arel', github: 'rails/arel'
+ gem 'rack', github: 'rack/rack'
gem 'sqlite3'
end
diff --git a/guides/bug_report_templates/generic_master.rb b/guides/bug_report_templates/generic_master.rb
index b6b4562751..f6dce64280 100644
--- a/guides/bug_report_templates/generic_master.rb
+++ b/guides/bug_report_templates/generic_master.rb
@@ -9,6 +9,7 @@ gemfile(true) do
source 'https://rubygems.org'
gem 'rails', github: 'rails/rails'
gem 'arel', github: 'rails/arel'
+ gem 'rack', github: 'rack/rack'
end
require 'active_support'
diff --git a/guides/source/4_2_release_notes.md b/guides/source/4_2_release_notes.md
index 684bd286bc..8a59007420 100644
--- a/guides/source/4_2_release_notes.md
+++ b/guides/source/4_2_release_notes.md
@@ -227,6 +227,17 @@ restore the old behavior.
If you do this, be sure to configure your firewall properly such that only
trusted machines on your network can access your development server.
+### Changed status option symbols for `render`
+
+Due to a [change in Rack](https://github.com/rack/rack/commit/be28c6a2ac152fe4adfbef71f3db9f4200df89e8), the symbols that the `render` method accepts for the `:status` option have changed:
+
+- 306: `:reserved` has been removed.
+- 413: `:request_entity_too_large` has been renamed to `:payload_too_large`.
+- 414: `:request_uri_too_long` has been renamed to `:uri_too_long`.
+- 416: `:requested_range_not_satisfiable` has been renamed to `:range_not_satisfiable`.
+
+Keep in mind that if calling `render` with an unknown symbol, the response status will default to 500.
+
### HTML Sanitizer
The HTML sanitizer has been replaced with a new, more robust, implementation
diff --git a/guides/source/action_controller_overview.md b/guides/source/action_controller_overview.md
index 09fbdc0d32..b4ca0df0de 100644
--- a/guides/source/action_controller_overview.md
+++ b/guides/source/action_controller_overview.md
@@ -1029,7 +1029,7 @@ There are a couple of things to notice in the above example. We need to make
sure to close the response stream. Forgetting to close the stream will leave
the socket open forever. We also have to set the content type to `text/event-stream`
before we write to the response stream. This is because headers cannot be written
-after the response has been committed (when `response.committed` returns a truthy
+after the response has been committed (when `response.committed?` returns a truthy
value), which occurs when you `write` or `commit` the response stream.
#### Example Usage
@@ -1174,7 +1174,7 @@ end
WARNING: You shouldn't do `rescue_from Exception` or `rescue_from StandardError` unless you have a particular reason as it will cause serious side-effects (e.g. you won't be able to see exception details and tracebacks during development).
-NOTE: Certain exceptions are only rescuable from the `ApplicationController` class, as they are raised before the controller gets initialized and the action gets executed. See Pratik Naik's [article](http://m.onkey.org/2008/7/20/rescue-from-dispatching) on the subject for more information.
+NOTE: Certain exceptions are only rescuable from the `ApplicationController` class, as they are raised before the controller gets initialized and the action gets executed.
Force HTTPS protocol
--------------------
diff --git a/guides/source/action_view_overview.md b/guides/source/action_view_overview.md
index db7eeed19a..00c41a480e 100644
--- a/guides/source/action_view_overview.md
+++ b/guides/source/action_view_overview.md
@@ -1443,12 +1443,12 @@ Sanitizes a block of CSS code.
Strips all link tags from text leaving just the link text.
```ruby
-strip_links("<a href="http://rubyonrails.org">Ruby on Rails</a>")
+strip_links('<a href="http://rubyonrails.org">Ruby on Rails</a>')
# => Ruby on Rails
```
```ruby
-strip_links("emails to <a href="mailto:me@email.com">me@email.com</a>.")
+strip_links('emails to <a href="mailto:me@email.com">me@email.com</a>.')
# => emails to me@email.com.
```
diff --git a/guides/source/active_record_migrations.md b/guides/source/active_record_migrations.md
index 4e5902fb3d..c5ac70143d 100644
--- a/guides/source/active_record_migrations.md
+++ b/guides/source/active_record_migrations.md
@@ -522,20 +522,27 @@ majority of cases, where Active Record knows how to reverse the migration
automatically. Currently, the `change` method supports only these migration
definitions:
-* `add_column`
-* `add_index`
-* `add_reference`
-* `add_timestamps`
-* `add_foreign_key`
-* `create_table`
-* `create_join_table`
-* `drop_table` (must supply a block)
-* `drop_join_table` (must supply a block)
-* `remove_timestamps`
-* `rename_column`
-* `rename_index`
-* `remove_reference`
-* `rename_table`
+* add_column
+* add_foreign_key
+* add_index
+* add_reference
+* add_timestamps
+* change_column_default (must supply a :from and :to option)
+* change_column_null
+* create_join_table
+* create_table
+* disable_extension
+* drop_join_table
+* drop_table (must supply a block)
+* enable_extension
+* remove_column (must supply a type)
+* remove_foreign_key (must supply a second table)
+* remove_index
+* remove_reference
+* remove_timestamps
+* rename_column
+* rename_index
+* rename_table
`change_table` is also reversible, as long as the block does not call `change`,
`change_default` or `remove`.
@@ -652,7 +659,7 @@ can't be done.
You can use Active Record's ability to rollback migrations using the `revert` method:
```ruby
-require_relative '2012121212_example_migration'
+require_relative '20121212123456_example_migration'
class FixupExampleMigration < ActiveRecord::Migration
def change
diff --git a/guides/source/active_record_querying.md b/guides/source/active_record_querying.md
index 4b4d70d3ce..c0a4a0ba39 100644
--- a/guides/source/active_record_querying.md
+++ b/guides/source/active_record_querying.md
@@ -341,8 +341,6 @@ User.find_each(begin_at: 2000, batch_size: 5000) do |user|
end
```
-Another example would be if you wanted multiple workers handling the same processing queue. You could have each worker handle 10000 records by setting the appropriate `:begin_at` option on each worker.
-
**`:end_at`**
Similar to the `:begin_at` option, `:end_at` allows you to configure the last ID of the sequence whenever the highest ID is not the one you need.
@@ -356,6 +354,10 @@ User.find_each(begin_at: 2000, end_at: 10000, batch_size: 5000) do |user|
end
```
+Another example would be if you wanted multiple workers handling the same
+processing queue. You could have each worker handle 10000 records by setting the
+appropriate `:begin_at` and `:end_at` options on each worker.
+
#### `find_in_batches`
The `find_in_batches` method is similar to `find_each`, since both retrieve batches of records. The difference is that `find_in_batches` yields _batches_ to the block as an array of models, instead of individually. The following example will yield to the supplied block an array of up to 1000 invoices at a time, with the final block containing any remaining invoices:
diff --git a/guides/source/active_support_instrumentation.md b/guides/source/active_support_instrumentation.md
index cd44c685ba..e5a560edd0 100644
--- a/guides/source/active_support_instrumentation.md
+++ b/guides/source/active_support_instrumentation.md
@@ -395,6 +395,38 @@ INFO. Cache stores may add their own keys
}
```
+Active Job
+--------
+
+### enqueue_at.active_job
+
+| Key | Value |
+| ------------ | -------------------------------------- |
+| `:adapter` | QueueAdapter object processing the job |
+| `:job` | Job object |
+
+### enqueue.active_job
+
+| Key | Value |
+| ------------ | -------------------------------------- |
+| `:adapter` | QueueAdapter object processing the job |
+| `:job` | Job object |
+
+### perform_start.active_job
+
+| Key | Value |
+| ------------ | -------------------------------------- |
+| `:adapter` | QueueAdapter object processing the job |
+| `:job` | Job object |
+
+### perform.active_job
+
+| Key | Value |
+| ------------ | -------------------------------------- |
+| `:adapter` | QueueAdapter object processing the job |
+| `:job` | Job object |
+
+
Railties
--------
diff --git a/guides/source/api_app.md b/guides/source/api_app.md
index 29ca872254..7f1792181e 100644
--- a/guides/source/api_app.md
+++ b/guides/source/api_app.md
@@ -363,11 +363,8 @@ controller modules by default:
- `ActionController::Renderers::All`: Support for `render :json` and friends.
- `ActionController::ConditionalGet`: Support for `stale?`.
- `ActionController::ForceSSL`: Support for `force_ssl`.
-- `ActionController::RackDelegation`: Support for the `request` and `response`
- methods returning `ActionDispatch::Request` and `ActionDispatch::Response`
- objects.
- `ActionController::DataStreaming`: Support for `send_file` and `send_data`.
-- `AbstractController::Callbacks`: Support for `before_filter` and friends.
+- `AbstractController::Callbacks`: Support for `before_action` and friends.
- `ActionController::Instrumentation`: Support for the instrumentation
hooks defined by Action Controller (see [the instrumentation
guide](active_support_instrumentation.html#action-controller)).
diff --git a/guides/source/api_documentation_guidelines.md b/guides/source/api_documentation_guidelines.md
index 46c9013087..a4feff798d 100644
--- a/guides/source/api_documentation_guidelines.md
+++ b/guides/source/api_documentation_guidelines.md
@@ -84,6 +84,11 @@ English
Please use American English (*color*, *center*, *modularize*, etc). See [a list of American and British English spelling differences here](http://en.wikipedia.org/wiki/American_and_British_English_spelling_differences).
+Comma
+-------
+
+Please use the Oxford comma (*red, white, and blue* style). See [the detail of Oxford comma](http://en.wikipedia.org/wiki/Serial_comma).
+
Example Code
------------
diff --git a/guides/source/configuring.md b/guides/source/configuring.md
index df9704830e..d63317433d 100644
--- a/guides/source/configuring.md
+++ b/guides/source/configuring.md
@@ -267,8 +267,8 @@ All these configuration options are delegated to the `I18n` library.
* `config.active_record.logger` accepts a logger conforming to the interface of Log4r or the default Ruby Logger class, which is then passed on to any new database connections made. You can retrieve this logger by calling `logger` on either an Active Record model class or an Active Record model instance. Set to `nil` to disable logging.
* `config.active_record.primary_key_prefix_type` lets you adjust the naming for primary key columns. By default, Rails assumes that primary key columns are named `id` (and this configuration option doesn't need to be set.) There are two other choices:
-** `:table_name` would make the primary key for the Customer class `customerid`
-** `:table_name_with_underscore` would make the primary key for the Customer class `customer_id`
+ * `:table_name` would make the primary key for the Customer class `customerid`
+ * `:table_name_with_underscore` would make the primary key for the Customer class `customer_id`
* `config.active_record.table_name_prefix` lets you set a global string to be prepended to table names. If you set this to `northwest_`, then the Customer class will look for `northwest_customers` as its table. The default is an empty string.
diff --git a/guides/source/contributing_to_ruby_on_rails.md b/guides/source/contributing_to_ruby_on_rails.md
index 3b944f1274..ba82713266 100644
--- a/guides/source/contributing_to_ruby_on_rails.md
+++ b/guides/source/contributing_to_ruby_on_rails.md
@@ -15,6 +15,9 @@ After reading this guide, you will know:
Ruby on Rails is not "someone else's framework." Over the years, hundreds of people have contributed to Ruby on Rails ranging from a single character to massive architectural changes or significant documentation - all with the goal of making Ruby on Rails better for everyone. Even if you don't feel up to writing code or documentation yet, there are a variety of other ways that you can contribute, from reporting issues to testing patches.
+As mentioned in [Rails
+README](https://github.com/rails/rails/blob/master/README.md), everyone interacting in Rails and its sub-project’s codebases, issue trackers, chat rooms, and mailing lists is expected to follow the Rails [code of conduct](https://github.com/rails/rails/blob/master/CODE_OF_CONDUCT.md).
+
--------------------------------------------------------------------------------
Reporting an Issue
diff --git a/guides/source/debugging_rails_applications.md b/guides/source/debugging_rails_applications.md
index c486009741..a05abb61d6 100644
--- a/guides/source/debugging_rails_applications.md
+++ b/guides/source/debugging_rails_applications.md
@@ -351,6 +351,7 @@ by asking the debugger for help. Type: `help`
help -- prints this help.
help <cmd> -- prints help on command <cmd>.
help <cmd> <subcmd> -- prints help on <cmd>'s subcommand <subcmd>.
+```
To see the previous ten lines you should type `list-` (or `l-`).
@@ -458,12 +459,12 @@ The debugger can list, stop, resume and switch between running threads by using
the `thread` command (or the abbreviated `th`). This command has a handful of
options:
-* `thread` shows the current thread.
-* `thread list` is used to list all threads and their statuses. The plus +
+* `thread`: shows the current thread.
+* `thread list`: is used to list all threads and their statuses. The plus +
character and the number indicates the current thread of execution.
-* `thread stop _n_` stop thread _n_.
-* `thread resume _n_` resumes thread _n_.
-* `thread switch _n_` switches the current thread context to _n_.
+* `thread stop _n_`: stop thread _n_.
+* `thread resume _n_`: resumes thread _n_.
+* `thread switch _n_`: switches the current thread context to _n_.
This command is very helpful when you are debugging concurrent threads and need
to verify that there are no race conditions in your code.
@@ -619,13 +620,16 @@ Processing by ArticlesController#index as HTML
(byebug)
```
-If we use `next`, we want go deep inside method calls. Instead, byebug will go
-to the next line within the same context. In this case, this is the last line of
-the method, so `byebug` will jump to next next line of the previous frame.
+If we use `next`, we won't go deep inside method calls. Instead, `byebug` will
+go to the next line within the same context. In this case, it is the last line
+of the current method, so `byebug` will return to the next line of the caller
+method.
```
(byebug) next
-Next went up a frame because previous frame finished
+
+Next advances to the next line (line 6: `end`), which returns to the next line
+of the caller method:
[4, 13] in /PathTo/project/test_app/app/controllers/articles_controller.rb
4: # GET /articles
@@ -642,8 +646,8 @@ Next went up a frame because previous frame finished
(byebug)
```
-If we use `step` in the same situation, we will literally go to the next Ruby
-instruction to be executed. In this case, Active Support's `week` method.
+If we use `step` in the same situation, `byebug` will literally go to the next
+Ruby instruction to be executed -- in this case, Active Support's `week` method.
```
(byebug) step
@@ -741,12 +745,12 @@ To list all active catchpoints use `catch`.
There are two ways to resume execution of an application that is stopped in the
debugger:
-* `continue` [line-specification] \(or `c`): resume program execution, at the
+* `continue [line-specification]` \(or `c`): resume program execution, at the
address where your script last stopped; any breakpoints set at that address are
bypassed. The optional argument line-specification allows you to specify a line
number to set a one-time breakpoint which is deleted when that breakpoint is
reached.
-* `finish` [frame-number] \(or `fin`): execute until the selected stack frame
+* `finish [frame-number]` \(or `fin`): execute until the selected stack frame
returns. If no frame number is given, the application will run until the
currently selected frame returns. The currently selected frame starts out the
most-recent frame or 0 if no frame positioning (e.g up, down or frame) has been
diff --git a/guides/source/getting_started.md b/guides/source/getting_started.md
index ef66b75ec1..5700e71103 100644
--- a/guides/source/getting_started.md
+++ b/guides/source/getting_started.md
@@ -71,10 +71,9 @@ The Rails philosophy includes two major guiding principles:
Creating a New Rails Project
----------------------------
-
-The best way to use this guide is to follow each step as it happens, no code or
-step needed to make this example application has been left out, so you can
-literally follow along step by step.
+The best way to read this guide is to follow it step by step. All steps are
+essential to run this example application and no additional code or steps are
+needed.
By following along with this guide, you'll create a Rails project called
`blog`, a (very) simple weblog. Before you can start building the application,
diff --git a/guides/source/layouts_and_rendering.md b/guides/source/layouts_and_rendering.md
index 94cd7297e2..b425eb126a 100644
--- a/guides/source/layouts_and_rendering.md
+++ b/guides/source/layouts_and_rendering.md
@@ -360,7 +360,6 @@ Rails understands both numeric status codes and the corresponding symbols shown
| | 303 | :see_other |
| | 304 | :not_modified |
| | 305 | :use_proxy |
-| | 306 | :reserved |
| | 307 | :temporary_redirect |
| | 308 | :permanent_redirect |
| **Client Error** | 400 | :bad_request |
@@ -376,10 +375,10 @@ Rails understands both numeric status codes and the corresponding symbols shown
| | 410 | :gone |
| | 411 | :length_required |
| | 412 | :precondition_failed |
-| | 413 | :request_entity_too_large |
-| | 414 | :request_uri_too_long |
+| | 413 | :payload_too_large |
+| | 414 | :uri_too_long |
| | 415 | :unsupported_media_type |
-| | 416 | :requested_range_not_satisfiable |
+| | 416 | :range_not_satisfiable |
| | 417 | :expectation_failed |
| | 422 | :unprocessable_entity |
| | 423 | :locked |
diff --git a/guides/source/plugins.md b/guides/source/plugins.md
index 4e630a39f3..b94c26a1ae 100644
--- a/guides/source/plugins.md
+++ b/guides/source/plugins.md
@@ -443,4 +443,3 @@ $ bundle exec rake rdoc
* [Developing a RubyGem using Bundler](https://github.com/radar/guides/blob/master/gem-development.md)
* [Using .gemspecs as Intended](http://yehudakatz.com/2010/04/02/using-gemspecs-as-intended/)
* [Gemspec Reference](http://guides.rubygems.org/specification-reference/)
-* [GemPlugins: A Brief Introduction to the Future of Rails Plugins](http://www.intridea.com/blog/2008/6/11/gemplugins-a-brief-introduction-to-the-future-of-rails-plugins)
diff --git a/guides/source/security.md b/guides/source/security.md
index 21cf48c2cf..93c270064a 100644
--- a/guides/source/security.md
+++ b/guides/source/security.md
@@ -760,7 +760,7 @@ s = sanitize(user_input, tags: tags, attributes: %w(href title))
This allows only the given tags and does a good job, even against all kinds of tricks and malformed tags.
-As a second step, _it is good practice to escape all output of the application_, especially when re-displaying user input, which hasn't been input-filtered (as in the search form example earlier on). _Use `escapeHTML()` (or its alias `h()`) method_ to replace the HTML input characters &amp;, &quot;, &lt;, &gt; by their uninterpreted representations in HTML (`&amp;`, `&quot;`, `&lt`;, and `&gt;`). However, it can easily happen that the programmer forgets to use it, so _it is recommended to use the SafeErb gem. SafeErb reminds you to escape strings from external sources.
+As a second step, _it is good practice to escape all output of the application_, especially when re-displaying user input, which hasn't been input-filtered (as in the search form example earlier on). _Use `escapeHTML()` (or its alias `h()`) method_ to replace the HTML input characters &amp;, &quot;, &lt;, and &gt; by their uninterpreted representations in HTML (`&amp;`, `&quot;`, `&lt;`, and `&gt;`). However, it can easily happen that the programmer forgets to use it, so _it is recommended to use the SafeErb gem. SafeErb reminds you to escape strings from external sources.
##### Obfuscation and Encoding Injection
@@ -1014,18 +1014,12 @@ config.action_dispatch.default_headers.clear
Here is a list of common headers:
-* X-Frame-Options
-_'SAMEORIGIN' in Rails by default_ - allow framing on same domain. Set it to 'DENY' to deny framing at all or 'ALLOWALL' if you want to allow framing for all website.
-* X-XSS-Protection
-_'1; mode=block' in Rails by default_ - use XSS Auditor and block page if XSS attack is detected. Set it to '0;' if you want to switch XSS Auditor off(useful if response contents scripts from request parameters)
-* X-Content-Type-Options
-_'nosniff' in Rails by default_ - stops the browser from guessing the MIME type of a file.
-* X-Content-Security-Policy
-[A powerful mechanism for controlling which sites certain content types can be loaded from](http://w3c.github.io/webappsec/specs/content-security-policy/csp-specification.dev.html)
-* Access-Control-Allow-Origin
-Used to control which sites are allowed to bypass same origin policies and send cross-origin requests.
-* Strict-Transport-Security
-[Used to control if the browser is allowed to only access a site over a secure connection](http://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security)
+* **X-Frame-Options:** _'SAMEORIGIN' in Rails by default_ - allow framing on same domain. Set it to 'DENY' to deny framing at all or 'ALLOWALL' if you want to allow framing for all website.
+* **X-XSS-Protection:** _'1; mode=block' in Rails by default_ - use XSS Auditor and block page if XSS attack is detected. Set it to '0;' if you want to switch XSS Auditor off(useful if response contents scripts from request parameters)
+* **X-Content-Type-Options:** _'nosniff' in Rails by default_ - stops the browser from guessing the MIME type of a file.
+* **X-Content-Security-Policy:** [A powerful mechanism for controlling which sites certain content types can be loaded from](http://w3c.github.io/webappsec/specs/content-security-policy/csp-specification.dev.html)
+* **Access-Control-Allow-Origin:** Used to control which sites are allowed to bypass same origin policies and send cross-origin requests.
+* **Strict-Transport-Security:** [Used to control if the browser is allowed to only access a site over a secure connection](http://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security)
Environmental Security
----------------------
diff --git a/guides/source/testing.md b/guides/source/testing.md
index 3a691220cf..aa3497fa13 100644
--- a/guides/source/testing.md
+++ b/guides/source/testing.md
@@ -50,7 +50,7 @@ By default, every Rails application has three environments: development, test, a
Each environment's configuration can be modified similarly. In this case, we can modify our test environment by changing the options found in `config/environments/test.rb`.
-NOTE: Your test are run under RAILS_ENV=test.
+NOTE: Your tests are run under `RAILS_ENV=test`.
### Rails meets Minitest
@@ -336,7 +336,8 @@ All the basic assertions such as `assert_equal` defined in `Minitest::Assertions
Each of these classes include `Minitest::Assertions`, allowing us to use all of the basic assertions in our tests.
-NOTE: For more information on `Minitest`, refer to [Minitest](http://docs.seattlerb.org/minitest)
+NOTE: For more information on `Minitest`, refer to [its
+documentation](http://docs.seattlerb.org/minitest).
### The Rails Test Runner
@@ -355,7 +356,8 @@ Finished tests in 0.009262s, 107.9680 tests/s, 107.9680 assertions/s.
This will run all test methods from the test case.
-You can also run a particular test method from the test case by providing the `-n` or `--name` flag and the `test method name`.
+You can also run a particular test method from the test case by providing the
+`-n` or `--name` flag and the test's method name.
```bash
$ bin/rails test test/models/article_test.rb -n test_the_truth
@@ -469,7 +471,8 @@ user_<%= n %>:
#### Fixtures in Action
-Rails by default automatically loads all fixtures from the `test/fixtures` directory for your models and controllers test. Loading involves three steps:
+Rails automatically loads all fixtures from the `test/fixtures` directory by
+default. Loading involves three steps:
1. Remove any existing data from the table corresponding to the fixture
2. Load the fixture data into the table
@@ -505,7 +508,8 @@ Model Testing
Model tests are used to test the various models of your application.
-For creating Rails model tests, we use the 'test/model' directory for your application. Rails provides a generator to create an model test skeleton for you.
+Rails model tests are stored under the `test/models` directory. Rails provides
+a generator to create a model test skeleton for you.
```bash
$ bin/rails generate test_unit:model article title:string body:text
@@ -563,7 +567,8 @@ We'll start by generating our integration test skeleton:
$ bin/rails generate integration_test blog_flow
```
-It should have created a test file placeholder for us, with the output of the previous command you should see:
+It should have created a test file placeholder for us. With the output of the
+previous command you should see:
```bash
invoke test_unit
@@ -1110,10 +1115,13 @@ require 'test_helper'
class UserMailerTest < ActionMailer::TestCase
test "invite" do
+ # Create the email and store it for further assertions
+ email = UserMailer.create_invite('me@example.com',
+ 'friend@example.com', Time.now)
+
# Send the email, then test that it got queued
assert_emails 1 do
- email = UserMailer.create_invite('me@example.com',
- 'friend@example.com', Time.now).deliver_now
+ email.deliver_now
end
# Test the body of the sent email contains what we expect it to
diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md
index 21adac272d..faf6846e4b 100644
--- a/railties/CHANGELOG.md
+++ b/railties/CHANGELOG.md
@@ -1,3 +1,7 @@
+* `rails server` will now honour the `PORT` environment variable
+
+ *David Cornu*
+
* Plugins generated using `rails plugin new` are now generated with the
version number set to 0.1.0.
diff --git a/railties/Rakefile b/railties/Rakefile
index 4393f45790..cf130a5f14 100644
--- a/railties/Rakefile
+++ b/railties/Rakefile
@@ -26,7 +26,7 @@ end
Rake::TestTask.new('test:regular') do |t|
t.libs << 'test' << "#{File.dirname(__FILE__)}/../activesupport/lib"
t.pattern = 'test/**/*_test.rb'
- t.warning = true
+ t.warning = false
t.verbose = true
t.ruby_opts = ["--dev"] if defined?(JRUBY_VERSION)
end
diff --git a/railties/lib/rails/commands/server.rb b/railties/lib/rails/commands/server.rb
index c8fb58ab05..d3ea441f8e 100644
--- a/railties/lib/rails/commands/server.rb
+++ b/railties/lib/rails/commands/server.rb
@@ -86,7 +86,7 @@ module Rails
def default_options
super.merge({
- Port: 3000,
+ Port: ENV.fetch('PORT', 3000).to_i,
DoNotReverseLookup: true,
environment: (ENV['RAILS_ENV'] || ENV['RACK_ENV'] || "development").dup,
daemonize: false,
diff --git a/railties/lib/rails/generators/app_base.rb b/railties/lib/rails/generators/app_base.rb
index 249fe96772..38074e80e0 100644
--- a/railties/lib/rails/generators/app_base.rb
+++ b/railties/lib/rails/generators/app_base.rb
@@ -209,13 +209,19 @@ module Rails
[
GemfileEntry.path('rails', Rails::Generators::RAILS_DEV_PATH),
GemfileEntry.github('sprockets-rails', 'rails/sprockets-rails'),
- GemfileEntry.github('arel', 'rails/arel')
+ GemfileEntry.github('sprockets', 'rails/sprockets'),
+ GemfileEntry.github('sass-rails', 'rails/sass-rails'),
+ GemfileEntry.github('arel', 'rails/arel'),
+ GemfileEntry.github('rack', 'rack/rack')
]
elsif options.edge?
[
GemfileEntry.github('rails', 'rails/rails'),
GemfileEntry.github('sprockets-rails', 'rails/sprockets-rails'),
- GemfileEntry.github('arel', 'rails/arel')
+ GemfileEntry.github('sprockets', 'rails/sprockets'),
+ GemfileEntry.github('sass-rails', 'rails/sass-rails'),
+ GemfileEntry.github('arel', 'rails/arel'),
+ GemfileEntry.github('rack', 'rack/rack')
]
else
[GemfileEntry.version('rails',
@@ -255,8 +261,6 @@ module Rails
return [] if options[:skip_sprockets]
gems = []
- gems << GemfileEntry.version('sass-rails', '~> 5.0',
- 'Use SCSS for stylesheets')
gems << GemfileEntry.version('uglifier',
'>= 1.3.0',
diff --git a/railties/lib/rails/generators/rails/app/templates/app/assets/manifest.js.tt b/railties/lib/rails/generators/rails/app/templates/app/assets/manifest.js.tt
new file mode 100644
index 0000000000..3325553f57
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/app/assets/manifest.js.tt
@@ -0,0 +1,8 @@
+
+<% unless options.api? -%>
+//= link_tree ./images
+<% end -%>
+<% unless options.skip_javascript -%>
+//= link ./javascripts/application.js
+<% end -%>
+//= link ./stylesheets/application.css
diff --git a/railties/lib/rails/generators/rails/plugin/plugin_generator.rb b/railties/lib/rails/generators/rails/plugin/plugin_generator.rb
index 0e1326ce4f..8fea30189e 100644
--- a/railties/lib/rails/generators/rails/plugin/plugin_generator.rb
+++ b/railties/lib/rails/generators/rails/plugin/plugin_generator.rb
@@ -104,8 +104,9 @@ task default: :test
end
def test_dummy_assets
- template "rails/javascripts.js", "#{dummy_path}/app/assets/javascripts/application.js", force: true
- template "rails/stylesheets.css", "#{dummy_path}/app/assets/stylesheets/application.css", force: true
+ template "rails/javascripts.js", "#{dummy_path}/app/assets/javascripts/application.js", force: true
+ template "rails/stylesheets.css", "#{dummy_path}/app/assets/stylesheets/application.css", force: true
+ template "rails/dummy_manifest.js", "#{dummy_path}/app/assets/manifest.js", force: true
end
def test_dummy_clean
@@ -122,6 +123,10 @@ task default: :test
end
end
+ def assets_manifest
+ template "rails/engine_manifest.js", "app/assets/#{underscored_name}_manifest.js"
+ end
+
def stylesheets
if mountable?
copy_file "rails/stylesheets.css",
@@ -220,6 +225,10 @@ task default: :test
build(:lib)
end
+ def create_assets_manifest_file
+ build(:assets_manifest) unless api?
+ end
+
def create_public_stylesheets_files
build(:stylesheets) unless api?
end
diff --git a/railties/lib/rails/generators/rails/plugin/templates/rails/dummy_manifest.js b/railties/lib/rails/generators/rails/plugin/templates/rails/dummy_manifest.js
new file mode 100644
index 0000000000..ace695a811
--- /dev/null
+++ b/railties/lib/rails/generators/rails/plugin/templates/rails/dummy_manifest.js
@@ -0,0 +1,11 @@
+
+<% unless api? -%>
+//= link_tree ./images
+<% end -%>
+<% unless options.skip_javascript -%>
+//= link ./javascripts/application.js
+<% end -%>
+//= link ./stylesheets/application.css
+<% if mountable? && !api? -%>
+//= link <%= underscored_name %>_manifest.js
+<% end -%>
diff --git a/railties/lib/rails/generators/rails/plugin/templates/rails/engine_manifest.js b/railties/lib/rails/generators/rails/plugin/templates/rails/engine_manifest.js
new file mode 100644
index 0000000000..f8ef26982a
--- /dev/null
+++ b/railties/lib/rails/generators/rails/plugin/templates/rails/engine_manifest.js
@@ -0,0 +1,6 @@
+<% if mountable? -%>
+<% unless options.skip_javascript -%>
+//= link ./javascripts/<%= namespaced_name %>/application.js
+<% end -%>
+//= link ./stylesheets/<%= namespaced_name %>/application.css
+<% end -%>
diff --git a/railties/lib/rails/generators/rails/plugin/templates/test/test_helper.rb b/railties/lib/rails/generators/rails/plugin/templates/test/test_helper.rb
index b1b77629d4..f315144723 100644
--- a/railties/lib/rails/generators/rails/plugin/templates/test/test_helper.rb
+++ b/railties/lib/rails/generators/rails/plugin/templates/test/test_helper.rb
@@ -14,9 +14,6 @@ require "rails/test_help"
# to be shown.
Minitest.backtrace_filter = Minitest::BacktraceFilter.new
-# Load support files
-Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
-
# Load fixtures from the engine
if ActiveSupport::TestCase.respond_to?(:fixture_path=)
ActiveSupport::TestCase.fixture_path = File.expand_path("../fixtures", __FILE__)
diff --git a/railties/lib/rails/source_annotation_extractor.rb b/railties/lib/rails/source_annotation_extractor.rb
index 9b058a1848..8dd87b6cc5 100644
--- a/railties/lib/rails/source_annotation_extractor.rb
+++ b/railties/lib/rails/source_annotation_extractor.rb
@@ -3,7 +3,7 @@
# rake notes
# rake notes:optimize
#
-# and friends. See <tt>rake -T notes</tt> and <tt>railties/lib/tasks/annotations.rake</tt>.
+# and friends. See <tt>rake -T notes</tt> and <tt>railties/lib/rails/tasks/annotations.rake</tt>.
#
# Annotation objects are triplets <tt>:line</tt>, <tt>:tag</tt>, <tt>:text</tt> that
# represent the line where the annotation lives, its tag, and its text. Note
diff --git a/railties/lib/rails/test_unit/minitest_plugin.rb b/railties/lib/rails/test_unit/minitest_plugin.rb
index 5552779ef1..8e5301d1e0 100644
--- a/railties/lib/rails/test_unit/minitest_plugin.rb
+++ b/railties/lib/rails/test_unit/minitest_plugin.rb
@@ -16,7 +16,7 @@ module Minitest
opts.separator ""
opts.separator "Rails options:"
- opts.on("-e", "--environment [ENV]",
+ opts.on("-e", "--environment ENV",
"Run tests in the ENV environment") do |env|
options[:environment] = env.strip
end
diff --git a/railties/test/application/asset_debugging_test.rb b/railties/test/application/asset_debugging_test.rb
index 36ab8109a7..fef65adda2 100644
--- a/railties/test/application/asset_debugging_test.rb
+++ b/railties/test/application/asset_debugging_test.rb
@@ -48,7 +48,7 @@ module ApplicationTests
assert_no_match(/<script src="\/assets\/xmlhr-([0-z]+)\.js"><\/script>/, last_response.body)
end
- test "assets aren't concatenated when compile is true is on and debug_assets params is true" do
+ test "assets are served with sourcemaps when compile is true and debug_assets params is true" do
add_to_env_config "production", "config.assets.compile = true"
ENV["RAILS_ENV"] = "production"
@@ -57,8 +57,7 @@ module ApplicationTests
class ::PostsController < ActionController::Base ; end
get '/posts?debug_assets=true'
- assert_match(/<script src="\/assets\/application(\.self)?-([0-z]+)\.js\?body=1"><\/script>/, last_response.body)
- assert_match(/<script src="\/assets\/xmlhr(\.self)?-([0-z]+)\.js\?body=1"><\/script>/, last_response.body)
+ assert_match(/<script src="\/assets\/application(\.debug)?-([0-z]+)\.js"><\/script>/, last_response.body)
end
end
end
diff --git a/railties/test/application/assets_test.rb b/railties/test/application/assets_test.rb
index ba2c27d8e8..ddcd51f3fc 100644
--- a/railties/test/application/assets_test.rb
+++ b/railties/test/application/assets_test.rb
@@ -72,6 +72,7 @@ module ApplicationTests
end
test "precompile creates the file, gives it the original asset's content and run in production as default" do
+ app_file "app/assets/manifest.js", "//= link_tree ./javascripts"
app_file "app/assets/javascripts/application.js", "alert();"
app_file "app/assets/javascripts/foo/application.js", "alert();"
@@ -87,6 +88,7 @@ module ApplicationTests
end
def test_precompile_does_not_hit_the_database
+ app_file "app/assets/manifest.js", "//= link_tree ./javascripts"
app_file "app/assets/javascripts/application.js", "alert();"
app_file "app/assets/javascripts/foo/application.js", "alert();"
app_file "app/controllers/users_controller.rb", <<-eoruby
@@ -178,8 +180,11 @@ module ApplicationTests
end
test 'precompile use assets defined in app config and reassigned in app env config' do
- add_to_config 'config.assets.precompile = [ "something.js" ]'
- add_to_env_config 'production', 'config.assets.precompile += [ "another.js" ]'
+ add_to_config 'config.assets.precompile = [ "something_manifest.js" ]'
+ add_to_env_config 'production', 'config.assets.precompile += [ "another_manifest.js" ]'
+
+ app_file 'app/assets/something_manifest.js', '//= link ./javascripts/something.js'
+ app_file 'app/assets/another_manifest.js', '//= link ./javascripts/another.js'
app_file 'app/assets/javascripts/something.js.erb', 'alert();'
app_file 'app/assets/javascripts/another.js.erb', 'alert();'
@@ -190,14 +195,14 @@ module ApplicationTests
assert_file_exists("#{app_path}/public/assets/another-*.js")
end
- test "asset pipeline should use a Sprockets::Index when config.assets.digest is true" do
+ test "asset pipeline should use a Sprockets::CachedEnvironment when config.assets.digest is true" do
add_to_config "config.action_controller.perform_caching = false"
add_to_env_config "production", "config.assets.compile = true"
ENV["RAILS_ENV"] = "production"
require "#{app_path}/config/environment"
- assert_equal Sprockets::Index, Rails.application.assets.class
+ assert_equal Sprockets::CachedEnvironment, Rails.application.assets.class
end
test "precompile creates a manifest file with all the assets listed" do
@@ -246,7 +251,7 @@ module ApplicationTests
test "precompile properly refers files referenced with asset_path and runs in the provided RAILS_ENV" do
app_file "app/assets/images/rails.png", "notactuallyapng"
- app_file "app/assets/stylesheets/application.css.erb", "<%= asset_path('rails.png') %>"
+ app_file "app/assets/stylesheets/application.css.erb", "p { background-image: url(<%= asset_path('rails.png') %>) }"
add_to_env_config "test", "config.assets.digest = true"
precompile!('RAILS_ENV=test')
@@ -258,7 +263,7 @@ module ApplicationTests
test "precompile shouldn't use the digests present in manifest.json" do
app_file "app/assets/images/rails.png", "notactuallyapng"
- app_file "app/assets/stylesheets/application.css.erb", "p { url: <%= asset_path('rails.png') %> }"
+ app_file "app/assets/stylesheets/application.css.erb", "p { background-image: url(<%= asset_path('rails.png') %>) }"
ENV["RAILS_ENV"] = "production"
precompile!
@@ -277,7 +282,7 @@ module ApplicationTests
test "precompile appends the md5 hash to files referenced with asset_path and run in production with digest true" do
app_file "app/assets/images/rails.png", "notactuallyapng"
- app_file "app/assets/stylesheets/application.css.erb", "<%= asset_path('rails.png') %>"
+ app_file "app/assets/stylesheets/application.css.erb", "p { background-image: url(<%= asset_path('rails.png') %>) }"
ENV["RAILS_ENV"] = "production"
precompile!
@@ -289,7 +294,8 @@ module ApplicationTests
test "precompile should handle utf8 filenames" do
filename = "レイルズ.png"
app_file "app/assets/images/#{filename}", "not an image really"
- add_to_config "config.assets.precompile = [ /\.png$/, /application.(css|js)$/ ]"
+ app_file "app/assets/manifest.js", "//= link_tree ./images"
+ add_to_config "config.assets.precompile = %w(manifest.js)"
precompile!
@@ -394,7 +400,7 @@ module ApplicationTests
app_file "app/assets/javascripts/xmlhr.js.erb", "<%= Post.name %>"
precompile!
- assert_equal "Post;\n", File.read(Dir["#{app_path}/public/assets/application-*.js"].first)
+ assert_equal "Post\n;\n", File.read(Dir["#{app_path}/public/assets/application-*.js"].first)
end
test "initialization on the assets group should set assets_dir" do
@@ -439,9 +445,9 @@ module ApplicationTests
class ::PostsController < ActionController::Base; end
get '/posts', {}, {'HTTPS'=>'off'}
- assert_match('src="http://example.com/assets/application.self.js', last_response.body)
+ assert_match('src="http://example.com/assets/application.debug.js', last_response.body)
get '/posts', {}, {'HTTPS'=>'on'}
- assert_match('src="https://example.com/assets/application.self.js', last_response.body)
+ assert_match('src="https://example.com/assets/application.debug.js', last_response.body)
end
test "asset urls should be protocol-relative if no request is in scope" do
diff --git a/railties/test/application/initializers/frameworks_test.rb b/railties/test/application/initializers/frameworks_test.rb
index 7bba910b9e..6e3707cc27 100644
--- a/railties/test/application/initializers/frameworks_test.rb
+++ b/railties/test/application/initializers/frameworks_test.rb
@@ -1,5 +1,4 @@
require "isolation/abstract_unit"
-require 'set'
module ApplicationTests
class FrameworksTest < ActiveSupport::TestCase
diff --git a/railties/test/commands/server_test.rb b/railties/test/commands/server_test.rb
index 7a063aeddf..3be4a74f74 100644
--- a/railties/test/commands/server_test.rb
+++ b/railties/test/commands/server_test.rb
@@ -44,6 +44,13 @@ class Rails::ServerTest < ActiveSupport::TestCase
end
end
+ def test_environment_with_port
+ switch_env "PORT", "1234" do
+ server = Rails::Server.new
+ assert_equal 1234, server.options[:Port]
+ end
+ end
+
def test_caching_without_option
args = []
options = Rails::Server::Options.new.parse!(args)
diff --git a/railties/test/generators/actions_test.rb b/railties/test/generators/actions_test.rb
index 2857dae07e..fabba555ef 100644
--- a/railties/test/generators/actions_test.rb
+++ b/railties/test/generators/actions_test.rb
@@ -1,7 +1,6 @@
require 'generators/generators_test_helper'
require 'rails/generators/rails/app/app_generator'
require 'env_helpers'
-require 'minitest/mock'
class ActionsTest < Rails::Generators::TestCase
include GeneratorsTestHelper
@@ -12,13 +11,11 @@ class ActionsTest < Rails::Generators::TestCase
def setup
Rails.application = TestApp::Application
- @mock_generator = Minitest::Mock.new
super
end
def teardown
Rails.application = TestApp::Application.instance
- @mock_generator.verify
end
def test_invoke_other_generator_with_shortcut
@@ -150,16 +147,13 @@ class ActionsTest < Rails::Generators::TestCase
end
def test_git_with_symbol_should_run_command_using_git_scm
- @mock_generator.expect(:call, nil, ['git init'])
- generator.stub(:run, @mock_generator) do
+ assert_called_with(generator, :run, ['git init']) do
action :git, :init
end
end
def test_git_with_hash_should_run_each_command_using_git_scm
- @mock_generator.expect(:call, nil, ["git rm README"])
- @mock_generator.expect(:call, nil, ["git add ."])
- generator.stub(:run, @mock_generator) do
+ assert_called_with(generator, :run, [ ["git rm README"], ["git add ."] ]) do
action :git, rm: 'README', add: '.'
end
end
@@ -185,15 +179,13 @@ class ActionsTest < Rails::Generators::TestCase
end
def test_generate_should_run_script_generate_with_argument_and_options
- @mock_generator.expect(:call, nil, ['bin/rails generate model MyModel', verbose: false])
- generator.stub(:run_ruby_script, @mock_generator) do
+ assert_called_with(generator, :run_ruby_script, ['bin/rails generate model MyModel', verbose: false]) do
action :generate, 'model', 'MyModel'
end
end
def test_rake_should_run_rake_command_with_default_env
- @mock_generator.expect(:call, nil, ["rake log:clear RAILS_ENV=development", verbose: false])
- generator.stub(:run, @mock_generator) do
+ assert_called_with(generator, :run, ["rake log:clear RAILS_ENV=development", verbose: false]) do
with_rails_env nil do
action :rake, 'log:clear'
end
@@ -201,15 +193,13 @@ class ActionsTest < Rails::Generators::TestCase
end
def test_rake_with_env_option_should_run_rake_command_in_env
- @mock_generator.expect(:call, nil, ['rake log:clear RAILS_ENV=production', verbose: false])
- generator.stub(:run, @mock_generator) do
+ assert_called_with(generator, :run, ['rake log:clear RAILS_ENV=production', verbose: false]) do
action :rake, 'log:clear', env: 'production'
end
end
def test_rake_with_rails_env_variable_should_run_rake_command_in_env
- @mock_generator.expect(:call, nil, ['rake log:clear RAILS_ENV=production', verbose: false])
- generator.stub(:run, @mock_generator) do
+ assert_called_with(generator, :run, ['rake log:clear RAILS_ENV=production', verbose: false]) do
with_rails_env "production" do
action :rake, 'log:clear'
end
@@ -217,8 +207,7 @@ class ActionsTest < Rails::Generators::TestCase
end
def test_env_option_should_win_over_rails_env_variable_when_running_rake
- @mock_generator.expect(:call, nil, ['rake log:clear RAILS_ENV=production', verbose: false])
- generator.stub(:run, @mock_generator) do
+ assert_called_with(generator, :run, ['rake log:clear RAILS_ENV=production', verbose: false]) do
with_rails_env "staging" do
action :rake, 'log:clear', env: 'production'
end
@@ -226,8 +215,7 @@ class ActionsTest < Rails::Generators::TestCase
end
def test_rake_with_sudo_option_should_run_rake_command_with_sudo
- @mock_generator.expect(:call, nil, ["sudo rake log:clear RAILS_ENV=development", verbose: false])
- generator.stub(:run, @mock_generator) do
+ assert_called_with(generator, :run, ["sudo rake log:clear RAILS_ENV=development", verbose: false]) do
with_rails_env nil do
action :rake, 'log:clear', sudo: true
end
@@ -235,8 +223,7 @@ class ActionsTest < Rails::Generators::TestCase
end
def test_capify_should_run_the_capify_command
- @mock_generator.expect(:call, nil, ['capify .', verbose: false])
- generator.stub(:run, @mock_generator) do
+ assert_called_with(generator, :run, ['capify .', verbose: false]) do
action :capify!
end
end
@@ -274,8 +261,7 @@ F
def test_readme
run_generator
- 2.times { @mock_generator.expect(:call, destination_root,[]) }
- Rails::Generators::AppGenerator.stub(:source_root, @mock_generator) do
+ assert_called(Rails::Generators::AppGenerator, :source_root, times: 2, returns: destination_root) do
assert_match "application up and running", action(:readme, "README.md")
end
end
@@ -283,8 +269,7 @@ F
def test_readme_with_quiet
generator(default_arguments, quiet: true)
run_generator
- 2.times { @mock_generator.expect(:call, destination_root,[]) }
- Rails::Generators::AppGenerator.stub(:source_root, @mock_generator) do
+ assert_called(Rails::Generators::AppGenerator, :source_root, times: 2, returns: destination_root) do
assert_no_match "application up and running", action(:readme, "README.md")
end
end
diff --git a/railties/test/generators/generators_test_helper.rb b/railties/test/generators/generators_test_helper.rb
index 62ca0ecb4b..b19a5a7144 100644
--- a/railties/test/generators/generators_test_helper.rb
+++ b/railties/test/generators/generators_test_helper.rb
@@ -1,6 +1,7 @@
require 'abstract_unit'
require 'active_support/core_ext/module/remove_method'
require 'active_support/testing/stream'
+require 'active_support/testing/method_call_assertions'
require 'rails/generators'
require 'rails/generators/test_case'
@@ -25,6 +26,7 @@ require 'action_view'
module GeneratorsTestHelper
include ActiveSupport::Testing::Stream
+ include ActiveSupport::Testing::MethodCallAssertions
def self.included(base)
base.class_eval do
diff --git a/railties/test/generators/named_base_test.rb b/railties/test/generators/named_base_test.rb
index 1c32fc1bfd..291f5e06c3 100644
--- a/railties/test/generators/named_base_test.rb
+++ b/railties/test/generators/named_base_test.rb
@@ -1,6 +1,5 @@
require 'generators/generators_test_helper'
require 'rails/generators/rails/scaffold_controller/scaffold_controller_generator'
-require 'minitest/mock'
class NamedBaseTest < Rails::Generators::TestCase
include GeneratorsTestHelper
diff --git a/railties/test/generators_test.rb b/railties/test/generators_test.rb
index 31a575749a..291415858c 100644
--- a/railties/test/generators_test.rb
+++ b/railties/test/generators_test.rb
@@ -1,7 +1,6 @@
require 'generators/generators_test_helper'
require 'rails/generators/rails/model/model_generator'
require 'rails/generators/test_unit/model/model_generator'
-require 'minitest/mock'
class GeneratorsTest < Rails::Generators::TestCase
include GeneratorsTestHelper
@@ -9,18 +8,15 @@ class GeneratorsTest < Rails::Generators::TestCase
def setup
@path = File.expand_path("lib", Rails.root)
$LOAD_PATH.unshift(@path)
- @mock_generator = MiniTest::Mock.new
end
def teardown
$LOAD_PATH.delete(@path)
- @mock_generator.verify
end
def test_simple_invoke
assert File.exist?(File.join(@path, 'generators', 'model_generator.rb'))
- @mock_generator.expect(:call, nil, [["Account"],{}])
- TestUnit::Generators::ModelGenerator.stub(:start, @mock_generator) do
+ assert_called_with(TestUnit::Generators::ModelGenerator, :start, [["Account"], {}]) do
Rails::Generators.invoke("test_unit:model", ["Account"])
end
end
@@ -51,23 +47,20 @@ class GeneratorsTest < Rails::Generators::TestCase
def test_should_give_higher_preference_to_rails_generators
assert File.exist?(File.join(@path, 'generators', 'model_generator.rb'))
- @mock_generator.expect(:call, nil, [["Account"],{}])
- Rails::Generators::ModelGenerator.stub(:start, @mock_generator) do
+ assert_called_with(Rails::Generators::ModelGenerator, :start, [["Account"], {}]) do
warnings = capture(:stderr){ Rails::Generators.invoke :model, ["Account"] }
assert warnings.empty?
end
end
def test_invoke_with_default_values
- @mock_generator.expect(:call, nil, [["Account"],{}])
- Rails::Generators::ModelGenerator.stub(:start, @mock_generator) do
+ assert_called_with(Rails::Generators::ModelGenerator, :start, [["Account"], {}]) do
Rails::Generators.invoke :model, ["Account"]
end
end
def test_invoke_with_config_values
- @mock_generator.expect(:call, nil, [["Account"],{behavior: :skip}])
- Rails::Generators::ModelGenerator.stub(:start, @mock_generator) do
+ assert_called_with(Rails::Generators::ModelGenerator, :start, [["Account"], behavior: :skip]) do
Rails::Generators.invoke :model, ["Account"], behavior: :skip
end
end
@@ -115,8 +108,7 @@ class GeneratorsTest < Rails::Generators::TestCase
def test_invoke_with_nested_namespaces
model_generator = Minitest::Mock.new
model_generator.expect(:start, nil, [["Account"], {}])
- @mock_generator.expect(:call, model_generator, ['namespace', 'my:awesome'])
- Rails::Generators.stub(:find_by_namespace, @mock_generator) do
+ assert_called_with(Rails::Generators, :find_by_namespace, ['namespace', 'my:awesome'], returns: model_generator) do
Rails::Generators.invoke 'my:awesome:namespace', ["Account"]
end
model_generator.verify
@@ -185,8 +177,7 @@ class GeneratorsTest < Rails::Generators::TestCase
def test_fallbacks_for_generators_on_invoke
Rails::Generators.fallbacks[:shoulda] = :test_unit
- @mock_generator.expect(:call, nil, [["Account"],{}])
- TestUnit::Generators::ModelGenerator.stub(:start, @mock_generator) do
+ assert_called_with(TestUnit::Generators::ModelGenerator, :start, [["Account"], {}]) do
Rails::Generators.invoke "shoulda:model", ["Account"]
end
ensure
@@ -196,8 +187,7 @@ class GeneratorsTest < Rails::Generators::TestCase
def test_nested_fallbacks_for_generators
Rails::Generators.fallbacks[:shoulda] = :test_unit
Rails::Generators.fallbacks[:super_shoulda] = :shoulda
- @mock_generator.expect(:call, nil, [["Account"],{}])
- TestUnit::Generators::ModelGenerator.stub(:start, @mock_generator) do
+ assert_called_with(TestUnit::Generators::ModelGenerator, :start, [["Account"], {}]) do
Rails::Generators.invoke "super_shoulda:model", ["Account"]
end
ensure
diff --git a/railties/test/path_generation_test.rb b/railties/test/path_generation_test.rb
index 27e64b97b7..a16adc72a6 100644
--- a/railties/test/path_generation_test.rb
+++ b/railties/test/path_generation_test.rb
@@ -11,26 +11,26 @@ class PathGenerationTest < ActiveSupport::TestCase
super()
end
- class Dispatcher < ActionDispatch::Routing::RouteSet::Dispatcher
- def initialize(defaults, set, block)
- super(defaults)
+ class Request < DelegateClass(ActionDispatch::Request)
+ def initialize(target, url_helpers, block)
+ super(target)
+ @url_helpers = url_helpers
@block = block
- @set = set
end
- def controller_reference(controller_param)
+ def controller_class
+ url_helpers = @url_helpers
block = @block
- set = @set
Class.new(ActionController::Base) {
- include set.url_helpers
+ include url_helpers
define_method(:process) { |name| block.call(self) }
def to_a; [200, {}, []]; end
}
end
end
- def dispatcher defaults
- TestSet::Dispatcher.new defaults, self, @block
+ def make_request(env)
+ Request.new super, self.url_helpers, @block
end
end
diff --git a/tasks/release.rb b/tasks/release.rb
index d8c1390eef..2c7e927679 100644
--- a/tasks/release.rb
+++ b/tasks/release.rb
@@ -98,7 +98,7 @@ namespace :all do
task :push => FRAMEWORKS.map { |f| "#{f}:push" } + ['rails:push']
task :ensure_clean_state do
- unless `git status -s | grep -v RAILS_VERSION`.strip.empty?
+ unless `git status -s | grep -v 'RAILS_VERSION\\|CHANGELOG'`.strip.empty?
abort "[ABORTING] `git status` reports a dirty tree. Make sure all changes are committed"
end