aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.codeclimate.yml2
-rw-r--r--.github/issue_template.md2
-rw-r--r--.github/pull_request_template.md2
-rw-r--r--.rubocop.yml22
-rw-r--r--Gemfile2
-rw-r--r--Gemfile.lock23
-rw-r--r--actioncable/lib/rails/generators/channel/channel_generator.rb2
-rw-r--r--actioncable/test/connection/base_test.rb2
-rw-r--r--actionmailer/lib/rails/generators/mailer/mailer_generator.rb2
-rw-r--r--actionmailer/test/caching_test.rb8
-rw-r--r--actionpack/CHANGELOG.md19
-rw-r--r--actionpack/Rakefile2
-rw-r--r--actionpack/lib/action_controller/metal/exceptions.rb3
-rw-r--r--actionpack/lib/action_controller/metal/implicit_render.rb14
-rw-r--r--actionpack/lib/action_controller/metal/strong_parameters.rb2
-rw-r--r--actionpack/lib/action_controller/test_case.rb9
-rw-r--r--actionpack/lib/action_dispatch/http/filter_parameters.rb4
-rw-r--r--actionpack/lib/action_dispatch/middleware/debug_exceptions.rb24
-rw-r--r--actionpack/lib/action_dispatch/middleware/exception_wrapper.rb12
-rw-r--r--actionpack/lib/action_dispatch/middleware/static.rb2
-rw-r--r--actionpack/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb19
-rw-r--r--actionpack/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.text.erb3
-rw-r--r--actionpack/lib/action_dispatch/routing/route_set.rb2
-rw-r--r--actionpack/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb2
-rw-r--r--actionpack/test/abstract/callbacks_test.rb4
-rw-r--r--actionpack/test/controller/action_pack_assertions_test.rb6
-rw-r--r--actionpack/test/controller/caching_test.rb8
-rw-r--r--actionpack/test/controller/filters_test.rb2
-rw-r--r--actionpack/test/controller/flash_hash_test.rb4
-rw-r--r--actionpack/test/controller/http_digest_authentication_test.rb2
-rw-r--r--actionpack/test/controller/integration_test.rb2
-rw-r--r--actionpack/test/controller/mime/respond_to_test.rb4
-rw-r--r--actionpack/test/controller/parameters/parameters_permit_test.rb7
-rw-r--r--actionpack/test/controller/render_test.rb2
-rw-r--r--actionpack/test/controller/test_case_test.rb8
-rw-r--r--actionpack/test/dispatch/cookies_test.rb4
-rw-r--r--actionpack/test/dispatch/debug_exceptions_test.rb24
-rw-r--r--actionpack/test/dispatch/executor_test.rb6
-rw-r--r--actionpack/test/dispatch/mime_type_test.rb4
-rw-r--r--actionpack/test/dispatch/reloader_test.rb4
-rw-r--r--actionpack/test/dispatch/response_test.rb2
-rw-r--r--actionpack/test/dispatch/routing_test.rb2
-rw-r--r--actionview/CHANGELOG.md7
-rw-r--r--actionview/Rakefile2
-rw-r--r--actionview/app/assets/javascripts/README.md2
-rw-r--r--actionview/app/assets/javascripts/rails-ujs/utils/ajax.coffee2
-rw-r--r--actionview/lib/action_view/helpers/asset_tag_helper.rb4
-rw-r--r--actionview/test/template/capture_helper_test.rb26
-rw-r--r--actionview/test/template/lookup_context_test.rb2
-rw-r--r--actionview/test/template/test_case_test.rb2
-rw-r--r--actionview/test/template/url_helper_test.rb12
-rw-r--r--actionview/test/ujs/public/test/call-remote-callbacks.js12
-rw-r--r--actionview/test/ujs/public/test/call-remote.js8
-rw-r--r--actionview/test/ujs/public/test/data-confirm.js16
-rw-r--r--actionview/test/ujs/public/test/data-disable-with.js27
-rw-r--r--actionview/test/ujs/public/test/data-disable.js25
-rw-r--r--actionview/test/ujs/public/test/data-remote.js23
-rw-r--r--actionview/test/ujs/public/test/override.js4
-rw-r--r--actionview/test/ujs/public/test/settings.js12
-rw-r--r--activejob/lib/rails/generators/job/job_generator.rb4
-rw-r--r--activemodel/test/cases/attributes_dirty_test.rb2
-rw-r--r--activemodel/test/cases/dirty_test.rb2
-rw-r--r--activemodel/test/cases/errors_test.rb8
-rw-r--r--activemodel/test/cases/secure_password_test.rb2
-rw-r--r--activerecord/CHANGELOG.md12
-rw-r--r--activerecord/lib/active_record/associations/collection_association.rb4
-rw-r--r--activerecord/lib/active_record/associations/has_one_through_association.rb6
-rw-r--r--activerecord/lib/active_record/associations/preloader.rb25
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql/database_statements.rb5
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb9
-rw-r--r--activerecord/lib/active_record/log_subscriber.rb2
-rw-r--r--activerecord/lib/active_record/persistence.rb2
-rw-r--r--activerecord/lib/active_record/relation.rb39
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb4
-rw-r--r--activerecord/lib/active_record/timestamp.rb20
-rw-r--r--activerecord/test/cases/adapter_test.rb2
-rw-r--r--activerecord/test/cases/adapters/mysql2/active_schema_test.rb4
-rw-r--r--activerecord/test/cases/ar_schema_test.rb12
-rw-r--r--activerecord/test/cases/associations/eager_test.rb29
-rw-r--r--activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb2
-rw-r--r--activerecord/test/cases/associations/has_many_associations_test.rb14
-rw-r--r--activerecord/test/cases/associations/has_many_through_associations_test.rb2
-rw-r--r--activerecord/test/cases/associations/has_one_associations_test.rb2
-rw-r--r--activerecord/test/cases/associations/has_one_through_associations_test.rb18
-rw-r--r--activerecord/test/cases/associations/join_model_test.rb2
-rw-r--r--activerecord/test/cases/attribute_methods_test.rb12
-rw-r--r--activerecord/test/cases/autosave_association_test.rb38
-rw-r--r--activerecord/test/cases/base_test.rb10
-rw-r--r--activerecord/test/cases/callbacks_test.rb26
-rw-r--r--activerecord/test/cases/connection_adapters/connection_handler_test.rb4
-rw-r--r--activerecord/test/cases/dirty_test.rb22
-rw-r--r--activerecord/test/cases/dup_test.rb2
-rw-r--r--activerecord/test/cases/finder_test.rb4
-rw-r--r--activerecord/test/cases/fixtures_test.rb12
-rw-r--r--activerecord/test/cases/invertible_migration_test.rb18
-rw-r--r--activerecord/test/cases/migration/change_schema_test.rb6
-rw-r--r--activerecord/test/cases/migration/create_join_table_test.rb12
-rw-r--r--activerecord/test/cases/migration/index_test.rb8
-rw-r--r--activerecord/test/cases/migration_test.rb10
-rw-r--r--activerecord/test/cases/nested_attributes_test.rb4
-rw-r--r--activerecord/test/cases/persistence_test.rb2
-rw-r--r--activerecord/test/cases/query_cache_test.rb6
-rw-r--r--activerecord/test/cases/readonly_test.rb12
-rw-r--r--activerecord/test/cases/reaper_test.rb6
-rw-r--r--activerecord/test/cases/reflection_test.rb2
-rw-r--r--activerecord/test/cases/relation/merging_test.rb4
-rw-r--r--activerecord/test/cases/relations_test.rb63
-rw-r--r--activerecord/test/cases/scoping/default_scoping_test.rb2
-rw-r--r--activerecord/test/cases/scoping/named_scoping_test.rb2
-rw-r--r--activerecord/test/cases/scoping/relation_scoping_test.rb2
-rw-r--r--activerecord/test/cases/serialization_test.rb4
-rw-r--r--activerecord/test/cases/transactions_test.rb8
-rw-r--r--activerecord/test/cases/validations/length_validation_test.rb8
-rw-r--r--activerecord/test/cases/validations_test.rb4
-rw-r--r--activerecord/test/fixtures/sponsors.yml3
-rw-r--r--activerecord/test/models/member_detail.rb1
-rw-r--r--activerecord/test/schema/schema.rb3
-rw-r--r--activestorage/CHANGELOG.md19
-rw-r--r--activestorage/README.md4
-rw-r--r--activestorage/app/models/active_storage/blob/representable.rb10
-rw-r--r--activestorage/app/models/active_storage/preview.rb2
-rw-r--r--activestorage/app/models/active_storage/variant.rb73
-rw-r--r--activestorage/app/models/active_storage/variation.rb78
-rw-r--r--activestorage/lib/active_storage.rb1
-rw-r--r--activestorage/lib/active_storage/downloading.rb1
-rw-r--r--activestorage/lib/active_storage/engine.rb11
-rw-r--r--activestorage/lib/active_storage/service/s3_service.rb2
-rw-r--r--activestorage/test/models/variant_test.rb45
-rw-r--r--activestorage/test/test_helper.rb2
-rw-r--r--activesupport/CHANGELOG.md30
-rw-r--r--activesupport/lib/active_support/deprecation/reporting.rb2
-rw-r--r--activesupport/lib/active_support/i18n.rb1
-rw-r--r--activesupport/lib/active_support/inflector/methods.rb15
-rw-r--r--activesupport/lib/active_support/locale/en.rb28
-rw-r--r--activesupport/lib/active_support/testing/stream.rb2
-rw-r--r--activesupport/test/cache/behaviors/cache_delete_matched_behavior.rb4
-rw-r--r--activesupport/test/cache/behaviors/cache_store_behavior.rb4
-rw-r--r--activesupport/test/cache/behaviors/cache_store_version_behavior.rb2
-rw-r--r--activesupport/test/cache/behaviors/failure_safety_behavior.rb2
-rw-r--r--activesupport/test/cache/cache_store_namespace_test.rb4
-rw-r--r--activesupport/test/cache/stores/memory_store_test.rb2
-rw-r--r--activesupport/test/cache/stores/redis_cache_store_test.rb8
-rw-r--r--activesupport/test/callbacks_test.rb4
-rw-r--r--activesupport/test/class_cache_test.rb2
-rw-r--r--activesupport/test/core_ext/duration_test.rb20
-rw-r--r--activesupport/test/core_ext/file_test.rb6
-rw-r--r--activesupport/test/core_ext/integer_ext_test.rb6
-rw-r--r--activesupport/test/core_ext/module/attr_internal_test.rb12
-rw-r--r--activesupport/test/core_ext/module/concerning_test.rb2
-rw-r--r--activesupport/test/core_ext/name_error_test.rb2
-rw-r--r--activesupport/test/core_ext/object/acts_like_test.rb10
-rw-r--r--activesupport/test/core_ext/object/deep_dup_test.rb2
-rw-r--r--activesupport/test/core_ext/object/inclusion_test.rb14
-rw-r--r--activesupport/test/core_ext/range_ext_test.rb6
-rw-r--r--activesupport/test/core_ext/string_ext_test.rb4
-rw-r--r--activesupport/test/dependencies_test.rb20
-rw-r--r--activesupport/test/deprecation/proxy_wrappers_test.rb6
-rw-r--r--activesupport/test/descendants_tracker_without_autoloading_test.rb2
-rw-r--r--activesupport/test/file_update_checker_shared_tests.rb10
-rw-r--r--activesupport/test/hash_with_indifferent_access_test.rb4
-rw-r--r--activesupport/test/message_verifier_test.rb14
-rw-r--r--activesupport/test/multibyte_chars_test.rb4
-rw-r--r--activesupport/test/notifications_test.rb6
-rw-r--r--activesupport/test/ordered_options_test.rb4
-rw-r--r--activesupport/test/reloader_test.rb10
-rw-r--r--activesupport/test/security_utils_test.rb2
-rw-r--r--activesupport/test/time_zone_test.rb2
-rw-r--r--ci/custom_cops/lib/custom_cops.rb1
-rw-r--r--ci/custom_cops/lib/custom_cops/assert_not.rb40
-rw-r--r--ci/custom_cops/test/custom_cops/assert_not_test.rb42
-rw-r--r--ci/custom_cops/test/custom_cops/refute_not_test.rb11
-rw-r--r--ci/custom_cops/test/support/cop_helper.rb12
-rw-r--r--guides/assets/javascripts/guides.js91
-rw-r--r--guides/assets/javascripts/jquery.min.js4
-rw-r--r--guides/assets/javascripts/responsive-tables.js83
-rw-r--r--guides/assets/stylesheets/main.css16
-rw-r--r--guides/assets/stylesheets/print.css2
-rw-r--r--guides/source/action_controller_overview.md4
-rw-r--r--guides/source/active_storage_overview.md23
-rw-r--r--guides/source/association_basics.md2
-rw-r--r--guides/source/configuring.md2
-rw-r--r--guides/source/layout.html.erb11
-rw-r--r--guides/source/testing.md33
-rw-r--r--guides/source/working_with_javascript_in_rails.md10
-rw-r--r--railties/RDOC_MAIN.rdoc22
-rw-r--r--railties/lib/rails/generators/app_base.rb2
-rw-r--r--railties/lib/rails/generators/erb/mailer/mailer_generator.rb2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/Gemfile.tt2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/ruby-version.tt2
-rw-r--r--railties/lib/rails/generators/rails/controller/controller_generator.rb12
-rw-r--r--railties/lib/rails/generators/test_unit/job/job_generator.rb5
-rw-r--r--railties/lib/rails/generators/test_unit/mailer/mailer_generator.rb2
-rw-r--r--railties/lib/rails/source_annotation_extractor.rb12
-rw-r--r--railties/test/app_loader_test.rb6
-rw-r--r--railties/test/application/assets_test.rb6
-rw-r--r--railties/test/application/configuration_test.rb4
-rw-r--r--railties/test/application/initializers/frameworks_test.rb2
-rw-r--r--railties/test/application/middleware/sendfile_test.rb2
-rw-r--r--railties/test/application/middleware/session_test.rb4
-rw-r--r--railties/test/application/rake/dbs_test.rb2
-rw-r--r--railties/test/application/rake/multi_dbs_test.rb4
-rw-r--r--railties/test/application/rake_test.rb2
-rw-r--r--railties/test/commands/dbconsole_test.rb24
-rw-r--r--railties/test/generators/app_generator_test.rb21
-rw-r--r--railties/test/generators/channel_generator_test.rb10
-rw-r--r--railties/test/generators/controller_generator_test.rb22
-rw-r--r--railties/test/generators/job_generator_test.rb10
-rw-r--r--railties/test/rails_info_test.rb2
-rw-r--r--railties/test/railties/engine_test.rb4
-rw-r--r--railties/test/railties/railtie_test.rb14
212 files changed, 1364 insertions, 773 deletions
diff --git a/.codeclimate.yml b/.codeclimate.yml
index e4568d9d8b..7d4ec1c54f 100644
--- a/.codeclimate.yml
+++ b/.codeclimate.yml
@@ -23,7 +23,7 @@ checks:
engines:
rubocop:
enabled: true
- channel: rubocop-0-52
+ channel: rubocop-0-54
ratings:
paths:
diff --git a/.github/issue_template.md b/.github/issue_template.md
index 2d071d4a71..2ff6a271db 100644
--- a/.github/issue_template.md
+++ b/.github/issue_template.md
@@ -1,7 +1,7 @@
### Steps to reproduce
(Guidelines for creating a bug report are [available
-here](http://guides.rubyonrails.org/contributing_to_ruby_on_rails.html#creating-a-bug-report))
+here](http://edgeguides.rubyonrails.org/contributing_to_ruby_on_rails.html#creating-a-bug-report))
### Expected behavior
Tell us what should happen
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
index 214d63740c..a36687ec99 100644
--- a/.github/pull_request_template.md
+++ b/.github/pull_request_template.md
@@ -16,6 +16,6 @@ CHANGELOG files by reviewers, please add the CHANGELOG entry at the top of the f
Finally, if your pull request affects documentation or any non-code
changes, guidelines for those changes are [available
-here](http://guides.rubyonrails.org/contributing_to_ruby_on_rails.html#contributing-to-the-rails-documentation)
+here](http://edgeguides.rubyonrails.org/contributing_to_ruby_on_rails.html#contributing-to-the-rails-documentation)
Thanks for contributing to Rails!
diff --git a/.rubocop.yml b/.rubocop.yml
index eb410376fe..954ab3b1cb 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -9,11 +9,17 @@ AllCops:
- '**/templates/**/*'
- '**/vendor/**/*'
- 'actionpack/lib/action_dispatch/journey/parser.rb'
+ - 'railties/test/fixtures/tmp/**/*'
# Prefer assert_not_x over refute_x
CustomCops/RefuteNot:
Include:
- - '**/*_test.rb'
+ - '**/test/**/*'
+
+# Prefer assert_not over assert !
+CustomCops/AssertNot:
+ Include:
+ - '**/test/**/*'
# Prefer &&/|| over and/or.
Style/AndOr:
@@ -36,6 +42,13 @@ Layout/CommentIndentation:
Layout/ElseAlignment:
Enabled: true
+# Align `end` with the matching keyword or starting expression except for
+# assignments, where it should be aligned with the LHS.
+Layout/EndAlignment:
+ Enabled: true
+ EnforcedStyleAlignWith: variable
+ AutoCorrect: true
+
Layout/EmptyLineAfterMagicComment:
Enabled: true
@@ -145,13 +158,6 @@ Layout/TrailingWhitespace:
Style/UnneededPercentQ:
Enabled: true
-# Align `end` with the matching keyword or starting expression except for
-# assignments, where it should be aligned with the LHS.
-Lint/EndAlignment:
- Enabled: true
- EnforcedStyleAlignWith: variable
- AutoCorrect: true
-
# Use my_method(my_arg) not my_method( my_arg ) or my_method my_arg.
Lint/RequireParentheses:
Enabled: true
diff --git a/Gemfile b/Gemfile
index 8b7075051f..fb1d0b4c56 100644
--- a/Gemfile
+++ b/Gemfile
@@ -88,7 +88,7 @@ group :storage do
gem "google-cloud-storage", "~> 1.8", require: false
gem "azure-storage", require: false
- gem "mini_magick"
+ gem "image_processing", "~> 1.2"
end
group :ujs do
diff --git a/Gemfile.lock b/Gemfile.lock
index 23b668ac72..e70616a82a 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -229,10 +229,10 @@ GEM
faye-websocket (0.10.7)
eventmachine (>= 0.12.0)
websocket-driver (>= 0.5.1)
- ffi (1.9.18)
- ffi (1.9.18-java)
- ffi (1.9.18-x64-mingw32)
- ffi (1.9.18-x86-mingw32)
+ ffi (1.9.23)
+ ffi (1.9.23-java)
+ ffi (1.9.23-x64-mingw32)
+ ffi (1.9.23-x86-mingw32)
globalid (0.4.1)
activesupport (>= 4.2.0)
google-api-client (0.17.3)
@@ -265,6 +265,9 @@ GEM
httpclient (2.8.3)
i18n (1.0.0)
concurrent-ruby (~> 1.0)
+ image_processing (1.2.0)
+ mini_magick (~> 4.0)
+ ruby-vips (>= 2.0.10, < 3)
io-like (0.3.0)
jdbc-mysql (5.1.44)
jdbc-postgres (9.4.1206)
@@ -333,7 +336,7 @@ GEM
mini_portile2 (~> 2.3.0)
os (0.9.6)
parallel (1.12.1)
- parser (2.5.0.2)
+ parser (2.5.1.0)
ast (~> 2.4.0)
path_expander (1.0.2)
pg (1.0.0)
@@ -385,14 +388,16 @@ GEM
resque (~> 1.26)
rufus-scheduler (~> 3.2)
retriable (3.1.1)
- rubocop (0.52.1)
+ rubocop (0.54.0)
parallel (~> 1.10)
- parser (>= 2.4.0.2, < 3.0)
+ parser (>= 2.5)
powerpack (~> 0.1)
rainbow (>= 2.2.2, < 4.0)
ruby-progressbar (~> 1.7)
unicode-display_width (~> 1.0, >= 1.0.1)
ruby-progressbar (1.9.0)
+ ruby-vips (2.0.10)
+ ffi (~> 1.9)
ruby_dep (1.5.0)
rubyzip (1.2.1)
rufus-scheduler (3.4.2)
@@ -470,7 +475,7 @@ GEM
uber (0.1.0)
uglifier (3.2.0)
execjs (>= 0.3.0, < 3)
- unicode-display_width (1.3.0)
+ unicode-display_width (1.3.2)
useragent (0.16.8)
vegas (0.1.11)
rack (>= 1.0.0)
@@ -515,11 +520,11 @@ DEPENDENCIES
delayed_job_active_record
google-cloud-storage (~> 1.8)
hiredis
+ image_processing (~> 1.2)
json (>= 2.0.0)
kindlerb (~> 1.2.0)
libxml-ruby
listen (>= 3.0.5, < 3.2)
- mini_magick
minitest-bisect
mocha
mysql2 (>= 0.4.10)
diff --git a/actioncable/lib/rails/generators/channel/channel_generator.rb b/actioncable/lib/rails/generators/channel/channel_generator.rb
index c3528370c6..427eef1f55 100644
--- a/actioncable/lib/rails/generators/channel/channel_generator.rb
+++ b/actioncable/lib/rails/generators/channel/channel_generator.rb
@@ -27,7 +27,7 @@ module Rails
private
def file_name
- @_file_name ||= super.gsub(/_channel/i, "")
+ @_file_name ||= super.sub(/_channel\z/i, "")
end
# FIXME: Change these files to symlinks once RubyGems 2.5.0 is required.
diff --git a/actioncable/test/connection/base_test.rb b/actioncable/test/connection/base_test.rb
index c69d4d0b36..ac10b3d4d2 100644
--- a/actioncable/test/connection/base_test.rb
+++ b/actioncable/test/connection/base_test.rb
@@ -83,7 +83,7 @@ class ActionCable::Connection::BaseTest < ActionCable::TestCase
connection.subscriptions.expects(:unsubscribe_from_all)
connection.send :handle_close
- assert ! connection.connected
+ assert_not connection.connected
assert_equal [], @server.connections
end
end
diff --git a/actionmailer/lib/rails/generators/mailer/mailer_generator.rb b/actionmailer/lib/rails/generators/mailer/mailer_generator.rb
index 97eac30db1..c37a74c762 100644
--- a/actionmailer/lib/rails/generators/mailer/mailer_generator.rb
+++ b/actionmailer/lib/rails/generators/mailer/mailer_generator.rb
@@ -23,7 +23,7 @@ module Rails
private
def file_name # :doc:
- @_file_name ||= super.gsub(/_mailer/i, "")
+ @_file_name ||= super.sub(/_mailer\z/i, "")
end
def application_mailer_file_name
diff --git a/actionmailer/test/caching_test.rb b/actionmailer/test/caching_test.rb
index 574840c30e..22f310f39f 100644
--- a/actionmailer/test/caching_test.rb
+++ b/actionmailer/test/caching_test.rb
@@ -40,14 +40,14 @@ class FragmentCachingTest < BaseCachingTest
def test_fragment_exist_with_caching_enabled
@store.write("views/name", "value")
assert @mailer.fragment_exist?("name")
- assert !@mailer.fragment_exist?("other_name")
+ assert_not @mailer.fragment_exist?("other_name")
end
def test_fragment_exist_with_caching_disabled
@mailer.perform_caching = false
@store.write("views/name", "value")
- assert !@mailer.fragment_exist?("name")
- assert !@mailer.fragment_exist?("other_name")
+ assert_not @mailer.fragment_exist?("name")
+ assert_not @mailer.fragment_exist?("other_name")
end
def test_write_fragment_with_caching_enabled
@@ -90,7 +90,7 @@ class FragmentCachingTest < BaseCachingTest
buffer = "generated till now -> ".html_safe
buffer << view_context.send(:fragment_for, "expensive") { fragment_computed = true }
- assert !fragment_computed
+ assert_not fragment_computed
assert_equal "generated till now -> fragment content", buffer
end
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md
index a370c3fc99..a13d9e1078 100644
--- a/actionpack/CHANGELOG.md
+++ b/actionpack/CHANGELOG.md
@@ -1,3 +1,22 @@
+* Introduce a new error page to when the implict render page is accessed in the browser.
+
+ Now instead of showing an error page that with exception and backtraces we now show only
+ one informative page.
+
+ *Vinicius Stock*
+
+* Introduce ActionDispatch::DebugExceptions.register_interceptor
+
+ Exception aware plugin authors can use the newly introduced
+ `.register_interceptor` method to get the processed exception, instead of
+ monkey patching DebugExceptions.
+
+ ActionDispatch::DebugExceptions.register_interceptor do |request, exception|
+ HypoteticalPlugin.capture_exception(request, exception)
+ end
+
+ *Genadi Samokovarov*
+
* Output only one Content-Security-Policy nonce header value per request.
Fixes #32597.
diff --git a/actionpack/Rakefile b/actionpack/Rakefile
index 4dd7c59ce8..e99eb1723a 100644
--- a/actionpack/Rakefile
+++ b/actionpack/Rakefile
@@ -28,7 +28,7 @@ namespace :test do
end
task :lines do
- load File.expand_path("..", __dir__) + "/tools/line_statistics"
+ load File.expand_path("../tools/line_statistics", __dir__)
files = FileList["lib/**/*.rb"]
CodeTools::LineStatistics.new(files).print_loc
end
diff --git a/actionpack/lib/action_controller/metal/exceptions.rb b/actionpack/lib/action_controller/metal/exceptions.rb
index 58db4a1458..ce9eb209fe 100644
--- a/actionpack/lib/action_controller/metal/exceptions.rb
+++ b/actionpack/lib/action_controller/metal/exceptions.rb
@@ -50,4 +50,7 @@ module ActionController
class UnknownFormat < ActionControllerError #:nodoc:
end
+
+ class MissingExactTemplate < UnknownFormat #:nodoc:
+ end
end
diff --git a/actionpack/lib/action_controller/metal/implicit_render.rb b/actionpack/lib/action_controller/metal/implicit_render.rb
index ac0c127cdc..d3bb58f48b 100644
--- a/actionpack/lib/action_controller/metal/implicit_render.rb
+++ b/actionpack/lib/action_controller/metal/implicit_render.rb
@@ -41,18 +41,8 @@ module ActionController
raise ActionController::UnknownFormat, message
elsif interactive_browser_request?
- message = "#{self.class.name}\##{action_name} is missing a template " \
- "for this request format and variant.\n\n" \
- "request.formats: #{request.formats.map(&:to_s).inspect}\n" \
- "request.variant: #{request.variant.inspect}\n\n" \
- "NOTE! For XHR/Ajax or API requests, this action would normally " \
- "respond with 204 No Content: an empty white screen. Since you're " \
- "loading it in a web browser, we assume that you expected to " \
- "actually render a template, not nothing, so we're showing an " \
- "error to be extra-clear. If you expect 204 No Content, carry on. " \
- "That's what you'll get from an XHR or API request. Give it a shot."
-
- raise ActionController::UnknownFormat, message
+ message = "#{self.class.name}\##{action_name} is missing a template for request formats: #{request.formats.map(&:to_s).join(',')}"
+ raise ActionController::MissingExactTemplate, message
else
logger.info "No template found for #{self.class.name}\##{action_name}, rendering head :no_content" if logger
super
diff --git a/actionpack/lib/action_controller/metal/strong_parameters.rb b/actionpack/lib/action_controller/metal/strong_parameters.rb
index 4789252550..5a06bf86e3 100644
--- a/actionpack/lib/action_controller/metal/strong_parameters.rb
+++ b/actionpack/lib/action_controller/metal/strong_parameters.rb
@@ -374,7 +374,7 @@ module ActionController
# Person.new(params) # => #<Person id: nil, name: "Francesco">
def permit!
each_pair do |key, value|
- Array.wrap(value).each do |v|
+ Array.wrap(value).flatten.each do |v|
v.permit! if v.respond_to? :permit!
end
end
diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb
index 798d142755..8f2a7e2b5f 100644
--- a/actionpack/lib/action_controller/test_case.rb
+++ b/actionpack/lib/action_controller/test_case.rb
@@ -460,10 +460,6 @@ module ActionController
def process(action, method: "GET", params: {}, session: nil, body: nil, flash: {}, format: nil, xhr: false, as: nil)
check_required_ivars
- if body
- @request.set_header "RAW_POST_DATA", body
- end
-
http_method = method.to_s.upcase
@html_document = nil
@@ -478,6 +474,10 @@ module ActionController
@response.request = @request
@controller.recycle!
+ if body
+ @request.set_header "RAW_POST_DATA", body
+ end
+
@request.set_header "REQUEST_METHOD", http_method
if as
@@ -604,6 +604,7 @@ module ActionController
env.delete "action_dispatch.request.query_parameters"
env.delete "action_dispatch.request.request_parameters"
env["rack.input"] = StringIO.new
+ env.delete "RAW_POST_DATA"
env
end
diff --git a/actionpack/lib/action_dispatch/http/filter_parameters.rb b/actionpack/lib/action_dispatch/http/filter_parameters.rb
index ec86b8bc47..ec012ad02d 100644
--- a/actionpack/lib/action_dispatch/http/filter_parameters.rb
+++ b/actionpack/lib/action_dispatch/http/filter_parameters.rb
@@ -9,8 +9,8 @@ module ActionDispatch
# sub-hashes of the params hash to filter. Filtering only certain sub-keys
# from a hash is possible by using the dot notation: 'credit_card.number'.
# If a block is given, each key and value of the params hash and all
- # sub-hashes is passed to it, where the value or the key can be replaced using
- # String#replace or similar method.
+ # sub-hashes are passed to it, where the value or the key can be replaced using
+ # String#replace or similar methods.
#
# env["action_dispatch.parameter_filter"] = [:password]
# => replaces the value to all keys matching /password/i with "[FILTERED]"
diff --git a/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb b/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb
index 511306eb0e..33edad8bd9 100644
--- a/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb
+++ b/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb
@@ -50,10 +50,18 @@ module ActionDispatch
end
end
- def initialize(app, routes_app = nil, response_format = :default)
+ cattr_reader :interceptors, instance_accessor: false, default: []
+
+ def self.register_interceptor(object = nil, &block)
+ interceptor = object || block
+ interceptors << interceptor
+ end
+
+ def initialize(app, routes_app = nil, response_format = :default, interceptors = self.class.interceptors)
@app = app
@routes_app = routes_app
@response_format = response_format
+ @interceptors = interceptors
end
def call(env)
@@ -67,12 +75,26 @@ module ActionDispatch
response
rescue Exception => exception
+ invoke_interceptors(request, exception)
raise exception unless request.show_exceptions?
render_exception(request, exception)
end
private
+ def invoke_interceptors(request, exception)
+ backtrace_cleaner = request.get_header("action_dispatch.backtrace_cleaner")
+ wrapper = ExceptionWrapper.new(backtrace_cleaner, exception)
+
+ @interceptors.each do |interceptor|
+ begin
+ interceptor.call(request, exception)
+ rescue Exception
+ log_error(request, wrapper)
+ end
+ end
+ end
+
def render_exception(request, exception)
backtrace_cleaner = request.get_header("action_dispatch.backtrace_cleaner")
wrapper = ExceptionWrapper.new(backtrace_cleaner, exception)
diff --git a/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb b/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb
index d1b4508378..f05c69137b 100644
--- a/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb
+++ b/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb
@@ -12,6 +12,7 @@ module ActionDispatch
"ActionController::UnknownHttpMethod" => :method_not_allowed,
"ActionController::NotImplemented" => :not_implemented,
"ActionController::UnknownFormat" => :not_acceptable,
+ "ActionController::MissingExactTemplate" => :not_acceptable,
"ActionController::InvalidAuthenticityToken" => :unprocessable_entity,
"ActionController::InvalidCrossOriginRequest" => :unprocessable_entity,
"ActionDispatch::Http::Parameters::ParseError" => :bad_request,
@@ -22,11 +23,12 @@ module ActionDispatch
)
cattr_accessor :rescue_templates, default: Hash.new("diagnostics").merge!(
- "ActionView::MissingTemplate" => "missing_template",
- "ActionController::RoutingError" => "routing_error",
- "AbstractController::ActionNotFound" => "unknown_action",
- "ActiveRecord::StatementInvalid" => "invalid_statement",
- "ActionView::Template::Error" => "template_error"
+ "ActionView::MissingTemplate" => "missing_template",
+ "ActionController::RoutingError" => "routing_error",
+ "AbstractController::ActionNotFound" => "unknown_action",
+ "ActiveRecord::StatementInvalid" => "invalid_statement",
+ "ActionView::Template::Error" => "template_error",
+ "ActionController::MissingExactTemplate" => "missing_exact_template",
)
attr_reader :backtrace_cleaner, :exception, :line_number, :file
diff --git a/actionpack/lib/action_dispatch/middleware/static.rb b/actionpack/lib/action_dispatch/middleware/static.rb
index acd999444a..8130bfe2e7 100644
--- a/actionpack/lib/action_dispatch/middleware/static.rb
+++ b/actionpack/lib/action_dispatch/middleware/static.rb
@@ -69,7 +69,7 @@ module ActionDispatch
headers["Vary"] = "Accept-Encoding" if gzip_path
- return [status, headers, body]
+ [status, headers, body]
ensure
request.path_info = path
end
diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb
new file mode 100644
index 0000000000..76ab1691b5
--- /dev/null
+++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb
@@ -0,0 +1,19 @@
+<header>
+ <h1>No template for interactive request</h1>
+</header>
+
+<div id="container">
+ <h2><%= h @exception.message %></h2>
+
+ <p class="summary">
+ <strong>NOTE!</strong><br>
+ Unless told otherwise, Rails expects an action to render a template with the same name,<br>
+ contained in a folder named after its controller.
+
+ If this controller is an API responding with 204 (No Content), <br>
+ which does not require a template,
+ then this error will occur when trying to access it via browser,<br>
+ since we expect an HTML template
+ to be rendered for such requests. If that's the case, carry on.
+ </p>
+</div>
diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.text.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.text.erb
new file mode 100644
index 0000000000..fcdbe6069d
--- /dev/null
+++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.text.erb
@@ -0,0 +1,3 @@
+Missing exact template
+
+<%= @exception.message %>
diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb
index a29a5a04ef..1134279a7f 100644
--- a/actionpack/lib/action_dispatch/routing/route_set.rb
+++ b/actionpack/lib/action_dispatch/routing/route_set.rb
@@ -35,7 +35,7 @@ module ActionDispatch
if @raise_on_name_error
raise
else
- return [404, { "X-Cascade" => "pass" }, []]
+ [404, { "X-Cascade" => "pass" }, []]
end
end
diff --git a/actionpack/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb b/actionpack/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb
index 2c8cee3a9b..d2685e0452 100644
--- a/actionpack/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb
+++ b/actionpack/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb
@@ -80,7 +80,7 @@ module ActionDispatch
end
def inline_base64(path)
- Base64.encode64(path).gsub("\n", "")
+ Base64.strict_encode64(path)
end
def failed?
diff --git a/actionpack/test/abstract/callbacks_test.rb b/actionpack/test/abstract/callbacks_test.rb
index fdc09bd951..4512ea27b3 100644
--- a/actionpack/test/abstract/callbacks_test.rb
+++ b/actionpack/test/abstract/callbacks_test.rb
@@ -154,7 +154,7 @@ module AbstractController
test "when :except is specified, an after action is not triggered on that action" do
@controller.process(:index)
- assert !@controller.instance_variable_defined?("@authenticated")
+ assert_not @controller.instance_variable_defined?("@authenticated")
end
end
@@ -198,7 +198,7 @@ module AbstractController
test "when :except is specified with an array, an after action is not triggered on that action" do
@controller.process(:index)
- assert !@controller.instance_variable_defined?("@authenticated")
+ assert_not @controller.instance_variable_defined?("@authenticated")
end
end
diff --git a/actionpack/test/controller/action_pack_assertions_test.rb b/actionpack/test/controller/action_pack_assertions_test.rb
index 504c77b8ef..763df3a776 100644
--- a/actionpack/test/controller/action_pack_assertions_test.rb
+++ b/actionpack/test/controller/action_pack_assertions_test.rb
@@ -290,13 +290,13 @@ class ActionPackAssertionsControllerTest < ActionController::TestCase
def test_template_objects_exist
process :assign_this
- assert !@controller.instance_variable_defined?(:"@hi")
+ assert_not @controller.instance_variable_defined?(:"@hi")
assert @controller.instance_variable_get(:"@howdy")
end
def test_template_objects_missing
process :nothing
- assert !@controller.instance_variable_defined?(:@howdy)
+ assert_not @controller.instance_variable_defined?(:@howdy)
end
def test_empty_flash
@@ -366,7 +366,7 @@ class ActionPackAssertionsControllerTest < ActionController::TestCase
process :redirect_external
assert_predicate @response, :redirect?
assert_match(/rubyonrails/, @response.redirect_url)
- assert !/perloffrails/.match(@response.redirect_url)
+ assert_no_match(/perloffrails/, @response.redirect_url)
end
def test_redirection
diff --git a/actionpack/test/controller/caching_test.rb b/actionpack/test/controller/caching_test.rb
index a8f726f181..6fe036dd15 100644
--- a/actionpack/test/controller/caching_test.rb
+++ b/actionpack/test/controller/caching_test.rb
@@ -94,14 +94,14 @@ class FragmentCachingTest < ActionController::TestCase
def test_fragment_exist_with_caching_enabled
@store.write("views/name", "value")
assert @controller.fragment_exist?("name")
- assert !@controller.fragment_exist?("other_name")
+ assert_not @controller.fragment_exist?("other_name")
end
def test_fragment_exist_with_caching_disabled
@controller.perform_caching = false
@store.write("views/name", "value")
- assert !@controller.fragment_exist?("name")
- assert !@controller.fragment_exist?("other_name")
+ assert_not @controller.fragment_exist?("name")
+ assert_not @controller.fragment_exist?("other_name")
end
def test_write_fragment_with_caching_enabled
@@ -144,7 +144,7 @@ class FragmentCachingTest < ActionController::TestCase
buffer = "generated till now -> ".html_safe
buffer << view_context.send(:fragment_for, "expensive") { fragment_computed = true }
- assert !fragment_computed
+ assert_not fragment_computed
assert_equal "generated till now -> fragment content", buffer
end
diff --git a/actionpack/test/controller/filters_test.rb b/actionpack/test/controller/filters_test.rb
index 2b16a555bb..425a6e25cc 100644
--- a/actionpack/test/controller/filters_test.rb
+++ b/actionpack/test/controller/filters_test.rb
@@ -787,7 +787,7 @@ class FilterTest < ActionController::TestCase
assert_equal %w( ensure_login find_user ), @controller.instance_variable_get(:@ran_filter)
test_process(ConditionalSkippingController, "login")
- assert !@controller.instance_variable_defined?("@ran_after_action")
+ assert_not @controller.instance_variable_defined?("@ran_after_action")
test_process(ConditionalSkippingController, "change_password")
assert_equal %w( clean_up ), @controller.instance_variable_get("@ran_after_action")
end
diff --git a/actionpack/test/controller/flash_hash_test.rb b/actionpack/test/controller/flash_hash_test.rb
index 6c3ac26de1..e3ec5bb7fc 100644
--- a/actionpack/test/controller/flash_hash_test.rb
+++ b/actionpack/test/controller/flash_hash_test.rb
@@ -44,7 +44,7 @@ module ActionDispatch
@hash["foo"] = "bar"
@hash.delete "foo"
- assert !@hash.key?("foo")
+ assert_not @hash.key?("foo")
assert_nil @hash["foo"]
end
@@ -53,7 +53,7 @@ module ActionDispatch
assert_equal({ "foo" => "bar" }, @hash.to_hash)
@hash.to_hash["zomg"] = "aaron"
- assert !@hash.key?("zomg")
+ assert_not @hash.key?("zomg")
assert_equal({ "foo" => "bar" }, @hash.to_hash)
end
diff --git a/actionpack/test/controller/http_digest_authentication_test.rb b/actionpack/test/controller/http_digest_authentication_test.rb
index 560157dc61..3f211cd60d 100644
--- a/actionpack/test/controller/http_digest_authentication_test.rb
+++ b/actionpack/test/controller/http_digest_authentication_test.rb
@@ -202,7 +202,7 @@ class HttpDigestAuthenticationTest < ActionController::TestCase
test "validate_digest_response should fail with nil returning password_procedure" do
@request.env["HTTP_AUTHORIZATION"] = encode_credentials(username: nil, password: nil)
- assert !ActionController::HttpAuthentication::Digest.validate_digest_response(@request, "SuperSecret") { nil }
+ assert_not ActionController::HttpAuthentication::Digest.validate_digest_response(@request, "SuperSecret") { nil }
end
test "authentication request with request-uri ending in '/'" do
diff --git a/actionpack/test/controller/integration_test.rb b/actionpack/test/controller/integration_test.rb
index 8a49d7822e..9cdf04b886 100644
--- a/actionpack/test/controller/integration_test.rb
+++ b/actionpack/test/controller/integration_test.rb
@@ -135,7 +135,7 @@ class IntegrationTestTest < ActiveSupport::TestCase
session1 = @test.open_session { |sess| }
session2 = @test.open_session # implicit session
- assert !session1.equal?(session2)
+ assert_not session1.equal?(session2)
end
# RSpec mixes Matchers (which has a #method_missing) into
diff --git a/actionpack/test/controller/mime/respond_to_test.rb b/actionpack/test/controller/mime/respond_to_test.rb
index f9ffd5f54c..771eccb29b 100644
--- a/actionpack/test/controller/mime/respond_to_test.rb
+++ b/actionpack/test/controller/mime/respond_to_test.rb
@@ -658,13 +658,13 @@ class RespondToControllerTest < ActionController::TestCase
end
def test_variant_without_implicit_rendering_from_browser
- assert_raises(ActionController::UnknownFormat) do
+ assert_raises(ActionController::MissingExactTemplate) do
get :variant_without_implicit_template_rendering, params: { v: :does_not_matter }
end
end
def test_variant_variant_not_set_and_without_implicit_rendering_from_browser
- assert_raises(ActionController::UnknownFormat) do
+ assert_raises(ActionController::MissingExactTemplate) do
get :variant_without_implicit_template_rendering
end
end
diff --git a/actionpack/test/controller/parameters/parameters_permit_test.rb b/actionpack/test/controller/parameters/parameters_permit_test.rb
index e60003dc64..34b9ac0ab8 100644
--- a/actionpack/test/controller/parameters/parameters_permit_test.rb
+++ b/actionpack/test/controller/parameters/parameters_permit_test.rb
@@ -136,7 +136,7 @@ class ParametersPermitTest < ActiveSupport::TestCase
test "key: it is not assigned if not present in params" do
params = ActionController::Parameters.new(name: "Joe")
permitted = params.permit(:id)
- assert !permitted.has_key?(:id)
+ assert_not permitted.has_key?(:id)
end
test "key to empty array: empty arrays pass" do
@@ -353,12 +353,15 @@ class ParametersPermitTest < ActiveSupport::TestCase
assert_equal "Jonas", @params[:person][:family][:brother]
end
- test "permit is recursive" do
+ test "permit! is recursive" do
+ @params[:nested_array] = [[{ x: 2, y: 3 }, { x: 21, y: 42 }]]
@params.permit!
assert_predicate @params, :permitted?
assert_predicate @params[:person], :permitted?
assert_predicate @params[:person][:name], :permitted?
assert_predicate @params[:person][:addresses][0], :permitted?
+ assert_predicate @params[:nested_array][0][0], :permitted?
+ assert_predicate @params[:nested_array][0][1], :permitted?
end
test "permitted takes a default value when Parameters.permit_all_parameters is set" do
diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb
index fc21543049..24c5761e41 100644
--- a/actionpack/test/controller/render_test.rb
+++ b/actionpack/test/controller/render_test.rb
@@ -650,7 +650,7 @@ class ImplicitRenderTest < ActionController::TestCase
tests ImplicitRenderTestController
def test_implicit_no_content_response_as_browser
- assert_raises(ActionController::UnknownFormat) do
+ assert_raises(ActionController::MissingExactTemplate) do
get :empty_action
end
end
diff --git a/actionpack/test/controller/test_case_test.rb b/actionpack/test/controller/test_case_test.rb
index d1122abba6..e66c409786 100644
--- a/actionpack/test/controller/test_case_test.rb
+++ b/actionpack/test/controller/test_case_test.rb
@@ -681,6 +681,14 @@ XML
assert_equal "baz", @request.filtered_parameters[:foo]
end
+ def test_raw_post_reset_between_post_requests
+ post :no_op, params: { foo: "bar" }
+ assert_equal "foo=bar", @request.raw_post
+
+ post :no_op, params: { foo: "baz" }
+ assert_equal "foo=baz", @request.raw_post
+ end
+
def test_path_is_kept_after_the_request
get :test_params, params: { id: "foo" }
assert_equal "/test_case_test/test/test_params/foo", @request.path
diff --git a/actionpack/test/dispatch/cookies_test.rb b/actionpack/test/dispatch/cookies_test.rb
index 94cff10fe4..aba778fad6 100644
--- a/actionpack/test/dispatch/cookies_test.rb
+++ b/actionpack/test/dispatch/cookies_test.rb
@@ -65,8 +65,8 @@ class CookieJarTest < ActiveSupport::TestCase
end
def test_key_methods
- assert !request.cookie_jar.key?(:foo)
- assert !request.cookie_jar.has_key?("foo")
+ assert_not request.cookie_jar.key?(:foo)
+ assert_not request.cookie_jar.has_key?("foo")
request.cookie_jar[:foo] = :bar
assert request.cookie_jar.key?(:foo)
diff --git a/actionpack/test/dispatch/debug_exceptions_test.rb b/actionpack/test/dispatch/debug_exceptions_test.rb
index 60acba0616..045567ff83 100644
--- a/actionpack/test/dispatch/debug_exceptions_test.rb
+++ b/actionpack/test/dispatch/debug_exceptions_test.rb
@@ -3,6 +3,8 @@
require "abstract_unit"
class DebugExceptionsTest < ActionDispatch::IntegrationTest
+ InterceptedErrorInstance = StandardError.new
+
class Boomer
attr_accessor :closed
@@ -36,6 +38,8 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest
raise RuntimeError
when %r{/method_not_allowed}
raise ActionController::MethodNotAllowed
+ when %r{/intercepted_error}
+ raise InterceptedErrorInstance
when %r{/unknown_http_method}
raise ActionController::UnknownHttpMethod
when %r{/not_implemented}
@@ -76,9 +80,13 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest
end
end
+ Interceptor = proc { |request, exception| request.set_header("int", exception) }
+ BadInterceptor = proc { |request, exception| raise "bad" }
RoutesApp = Struct.new(:routes).new(SharedTestRoutes)
ProductionApp = ActionDispatch::DebugExceptions.new(Boomer.new(false), RoutesApp)
DevelopmentApp = ActionDispatch::DebugExceptions.new(Boomer.new(true), RoutesApp)
+ InterceptedApp = ActionDispatch::DebugExceptions.new(Boomer.new(true), RoutesApp, :default, [Interceptor])
+ BadInterceptedApp = ActionDispatch::DebugExceptions.new(Boomer.new(true), RoutesApp, :default, [BadInterceptor])
test "skip diagnosis if not showing detailed exceptions" do
@app = ProductionApp
@@ -499,4 +507,20 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest
end
end
end
+
+ test "invoke interceptors before rendering" do
+ @app = InterceptedApp
+ get "/intercepted_error", headers: { "action_dispatch.show_exceptions" => true }
+
+ assert_equal InterceptedErrorInstance, request.get_header("int")
+ end
+
+ test "bad interceptors doesn't debug exceptions" do
+ @app = BadInterceptedApp
+
+ get "/puke", headers: { "action_dispatch.show_exceptions" => true }
+
+ assert_response 500
+ assert_match(/puke/, body)
+ end
end
diff --git a/actionpack/test/dispatch/executor_test.rb b/actionpack/test/dispatch/executor_test.rb
index 8eb6450385..5b8be39b6d 100644
--- a/actionpack/test/dispatch/executor_test.rb
+++ b/actionpack/test/dispatch/executor_test.rb
@@ -81,7 +81,7 @@ class ExecutorTest < ActiveSupport::TestCase
running = false
body.close
- assert !running
+ assert_not running
end
def test_complete_callbacks_are_called_on_close
@@ -89,7 +89,7 @@ class ExecutorTest < ActiveSupport::TestCase
executor.to_complete { completed = true }
body = call_and_return_body
- assert !completed
+ assert_not completed
body.close
assert completed
@@ -116,7 +116,7 @@ class ExecutorTest < ActiveSupport::TestCase
call_and_return_body.close
assert result
- assert !defined?(@in_shared_context) # it's not in the test itself
+ assert_not defined?(@in_shared_context) # it's not in the test itself
end
private
diff --git a/actionpack/test/dispatch/mime_type_test.rb b/actionpack/test/dispatch/mime_type_test.rb
index 6167ea46df..fa264417e1 100644
--- a/actionpack/test/dispatch/mime_type_test.rb
+++ b/actionpack/test/dispatch/mime_type_test.rb
@@ -180,8 +180,8 @@ class MimeTypeTest < ActiveSupport::TestCase
assert Mime[:js] =~ "text/javascript"
assert Mime[:js] =~ "application/javascript"
assert Mime[:js] !~ "text/html"
- assert !(Mime[:js] !~ "text/javascript")
- assert !(Mime[:js] !~ "application/javascript")
+ assert_not (Mime[:js] !~ "text/javascript")
+ assert_not (Mime[:js] !~ "application/javascript")
assert Mime[:html] =~ "application/xhtml+xml"
end
end
diff --git a/actionpack/test/dispatch/reloader_test.rb b/actionpack/test/dispatch/reloader_test.rb
index e529229fae..edc4cd62a3 100644
--- a/actionpack/test/dispatch/reloader_test.rb
+++ b/actionpack/test/dispatch/reloader_test.rb
@@ -115,7 +115,7 @@ class ReloaderTest < ActiveSupport::TestCase
reloader.to_complete { completed = true }
body = call_and_return_body
- assert !completed
+ assert_not completed
body.close
assert completed
@@ -129,7 +129,7 @@ class ReloaderTest < ActiveSupport::TestCase
prepared = false
body.close
- assert !prepared
+ assert_not prepared
end
def test_complete_callbacks_are_called_on_exceptions
diff --git a/actionpack/test/dispatch/response_test.rb b/actionpack/test/dispatch/response_test.rb
index 4c8d528507..6d87314e97 100644
--- a/actionpack/test/dispatch/response_test.rb
+++ b/actionpack/test/dispatch/response_test.rb
@@ -191,7 +191,7 @@ class ResponseTest < ActiveSupport::TestCase
test "does not include Status header" do
@response.status = "200 OK"
_, headers, _ = @response.to_a
- assert !headers.has_key?("Status")
+ assert_not headers.has_key?("Status")
end
test "response code" do
diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb
index fe314e26b1..dd6adcbfd1 100644
--- a/actionpack/test/dispatch/routing_test.rb
+++ b/actionpack/test/dispatch/routing_test.rb
@@ -3166,7 +3166,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
end
end
- assert !respond_to?(:routes_no_collision_path)
+ assert_not respond_to?(:routes_no_collision_path)
end
def test_controller_name_with_leading_slash_raise_error
diff --git a/actionview/CHANGELOG.md b/actionview/CHANGELOG.md
index d833f9cd98..2c1ca12043 100644
--- a/actionview/CHANGELOG.md
+++ b/actionview/CHANGELOG.md
@@ -1,3 +1,10 @@
+* Fix JavaScript views rendering does not work with Firefox when using
+ Content Security Policy.
+
+ Fixes #32577.
+
+ *Yuji Yaginuma*
+
* Add the `nonce: true` option for `javascript_include_tag` helper to
support automatic nonce generation for Content Security Policy.
Works the same way as `javascript_tag nonce: true` does.
diff --git a/actionview/Rakefile b/actionview/Rakefile
index 9542b626d1..bdfd96c141 100644
--- a/actionview/Rakefile
+++ b/actionview/Rakefile
@@ -131,7 +131,7 @@ namespace :assets do
end
task :lines do
- load File.join(File.expand_path("..", __dir__), "/tools/line_statistics")
+ load File.expand_path("../tools/line_statistics", __dir__)
files = FileList["lib/**/*.rb"]
CodeTools::LineStatistics.new(files).print_loc
end
diff --git a/actionview/app/assets/javascripts/README.md b/actionview/app/assets/javascripts/README.md
index 185dddc7e5..b74fa1afad 100644
--- a/actionview/app/assets/javascripts/README.md
+++ b/actionview/app/assets/javascripts/README.md
@@ -23,6 +23,8 @@ Note that the `data` attributes this library adds are a feature of HTML5. If you
yarn add rails-ujs
+Ensure that `.yarnclean` does not include `assets` if you use [yarn autoclean](https://yarnpkg.com/lang/en/docs/cli/autoclean/).
+
## Usage
### Asset pipeline
diff --git a/actionview/app/assets/javascripts/rails-ujs/utils/ajax.coffee b/actionview/app/assets/javascripts/rails-ujs/utils/ajax.coffee
index cf31c796df..019bda635a 100644
--- a/actionview/app/assets/javascripts/rails-ujs/utils/ajax.coffee
+++ b/actionview/app/assets/javascripts/rails-ujs/utils/ajax.coffee
@@ -66,7 +66,7 @@ processResponse = (response, type) ->
try response = JSON.parse(response)
else if type.match(/\b(?:java|ecma)script\b/)
script = document.createElement('script')
- script.nonce = cspNonce()
+ script.setAttribute('nonce', cspNonce())
script.text = response
document.head.appendChild(script).parentNode.removeChild(script)
else if type.match(/\bxml\b/)
diff --git a/actionview/lib/action_view/helpers/asset_tag_helper.rb b/actionview/lib/action_view/helpers/asset_tag_helper.rb
index 257080d902..14bd8ffa84 100644
--- a/actionview/lib/action_view/helpers/asset_tag_helper.rb
+++ b/actionview/lib/action_view/helpers/asset_tag_helper.rb
@@ -333,9 +333,9 @@ module ActionView
#
# image_tag(user.avatar)
# # => <img src="/rails/active_storage/blobs/.../tiger.jpg" />
- # image_tag(user.avatar.variant(resize: "100x100"))
+ # image_tag(user.avatar.variant(resize_to_fit: [100, 100]))
# # => <img src="/rails/active_storage/variants/.../tiger.jpg" />
- # image_tag(user.avatar.variant(resize: "100x100"), size: '100')
+ # image_tag(user.avatar.variant(resize_to_fit: [100, 100]), size: '100')
# # => <img width="100" height="100" src="/rails/active_storage/variants/.../tiger.jpg" />
def image_tag(source, options = {})
options = options.symbolize_keys
diff --git a/actionview/test/template/capture_helper_test.rb b/actionview/test/template/capture_helper_test.rb
index 31c280a91c..131e49327e 100644
--- a/actionview/test/template/capture_helper_test.rb
+++ b/actionview/test/template/capture_helper_test.rb
@@ -49,21 +49,21 @@ class CaptureHelperTest < ActionView::TestCase
end
def test_content_for_with_multiple_calls
- assert ! content_for?(:title)
+ assert_not content_for?(:title)
content_for :title, "foo"
content_for :title, "bar"
assert_equal "foobar", content_for(:title)
end
def test_content_for_with_multiple_calls_and_flush
- assert ! content_for?(:title)
+ assert_not content_for?(:title)
content_for :title, "foo"
content_for :title, "bar", flush: true
assert_equal "bar", content_for(:title)
end
def test_content_for_with_block
- assert ! content_for?(:title)
+ assert_not content_for?(:title)
content_for :title do
output_buffer << "foo"
output_buffer << "bar"
@@ -73,7 +73,7 @@ class CaptureHelperTest < ActionView::TestCase
end
def test_content_for_with_block_and_multiple_calls_with_flush
- assert ! content_for?(:title)
+ assert_not content_for?(:title)
content_for :title do
"foo"
end
@@ -84,7 +84,7 @@ class CaptureHelperTest < ActionView::TestCase
end
def test_content_for_with_block_and_multiple_calls_with_flush_nil_content
- assert ! content_for?(:title)
+ assert_not content_for?(:title)
content_for :title do
"foo"
end
@@ -95,7 +95,7 @@ class CaptureHelperTest < ActionView::TestCase
end
def test_content_for_with_block_and_multiple_calls_without_flush
- assert ! content_for?(:title)
+ assert_not content_for?(:title)
content_for :title do
"foo"
end
@@ -106,7 +106,7 @@ class CaptureHelperTest < ActionView::TestCase
end
def test_content_for_with_whitespace_block
- assert ! content_for?(:title)
+ assert_not content_for?(:title)
content_for :title, "foo"
content_for :title do
output_buffer << " \n "
@@ -117,7 +117,7 @@ class CaptureHelperTest < ActionView::TestCase
end
def test_content_for_with_whitespace_block_and_flush
- assert ! content_for?(:title)
+ assert_not content_for?(:title)
content_for :title, "foo"
content_for :title, flush: true do
output_buffer << " \n "
@@ -128,7 +128,7 @@ class CaptureHelperTest < ActionView::TestCase
end
def test_content_for_returns_nil_when_writing
- assert ! content_for?(:title)
+ assert_not content_for?(:title)
assert_nil content_for(:title, "foo")
assert_nil content_for(:title) { output_buffer << "bar"; nil }
assert_nil content_for(:title) { output_buffer << " \n "; nil }
@@ -144,14 +144,14 @@ class CaptureHelperTest < ActionView::TestCase
end
def test_content_for_question_mark
- assert ! content_for?(:title)
+ assert_not content_for?(:title)
content_for :title, "title"
assert content_for?(:title)
- assert ! content_for?(:something_else)
+ assert_not content_for?(:something_else)
end
def test_content_for_should_be_html_safe_after_flush_empty
- assert ! content_for?(:title)
+ assert_not content_for?(:title)
content_for :title do
content_tag(:p, "title")
end
@@ -164,7 +164,7 @@ class CaptureHelperTest < ActionView::TestCase
end
def test_provide
- assert !content_for?(:title)
+ assert_not content_for?(:title)
provide :title, "hi"
assert content_for?(:title)
assert_equal "hi", content_for(:title)
diff --git a/actionview/test/template/lookup_context_test.rb b/actionview/test/template/lookup_context_test.rb
index beee76f711..38469cbe3d 100644
--- a/actionview/test/template/lookup_context_test.rb
+++ b/actionview/test/template/lookup_context_test.rb
@@ -195,7 +195,7 @@ class LookupContextTest < ActiveSupport::TestCase
assert @lookup_context.cache
template = @lookup_context.disable_cache do
- assert !@lookup_context.cache
+ assert_not @lookup_context.cache
@lookup_context.find("foo", %w(test), true)
end
assert @lookup_context.cache
diff --git a/actionview/test/template/test_case_test.rb b/actionview/test/template/test_case_test.rb
index 05e5f21ce4..d98fd4f9a2 100644
--- a/actionview/test/template/test_case_test.rb
+++ b/actionview/test/template/test_case_test.rb
@@ -192,7 +192,7 @@ module ActionView
helper HelperThatInvokesProtectAgainstForgery
test "protect_from_forgery? in any helpers returns false" do
- assert !view.help_me
+ assert_not view.help_me
end
end
diff --git a/actionview/test/template/url_helper_test.rb b/actionview/test/template/url_helper_test.rb
index 8bccda481b..08cb5dfea7 100644
--- a/actionview/test/template/url_helper_test.rb
+++ b/actionview/test/template/url_helper_test.rb
@@ -508,16 +508,16 @@ class UrlHelperTest < ActiveSupport::TestCase
def test_current_page_considering_params
@request = request_for_url("/?order=desc&page=1")
- assert !current_page?(url_hash, check_parameters: true)
- assert !current_page?(url_hash.merge(check_parameters: true))
- assert !current_page?(ActionController::Parameters.new(url_hash.merge(check_parameters: true)).permit!)
- assert !current_page?("http://www.example.com/", check_parameters: true)
+ assert_not current_page?(url_hash, check_parameters: true)
+ assert_not current_page?(url_hash.merge(check_parameters: true))
+ assert_not current_page?(ActionController::Parameters.new(url_hash.merge(check_parameters: true)).permit!)
+ assert_not current_page?("http://www.example.com/", check_parameters: true)
end
def test_current_page_considering_params_when_options_does_not_respond_to_to_hash
@request = request_for_url("/?order=desc&page=1")
- assert !current_page?(:back, check_parameters: false)
+ assert_not current_page?(:back, check_parameters: false)
end
def test_current_page_with_params_that_match
@@ -562,7 +562,7 @@ class UrlHelperTest < ActiveSupport::TestCase
def test_current_page_with_not_get_verb
@request = request_for_url("/events", method: :post)
- assert !current_page?("/events")
+ assert_not current_page?("/events")
end
def test_link_unless_current
diff --git a/actionview/test/ujs/public/test/call-remote-callbacks.js b/actionview/test/ujs/public/test/call-remote-callbacks.js
index 48763f6301..9c0c8cfb4b 100644
--- a/actionview/test/ujs/public/test/call-remote-callbacks.js
+++ b/actionview/test/ujs/public/test/call-remote-callbacks.js
@@ -75,9 +75,9 @@ asyncTest('setting data("with-credentials",true) with "ajax:before" uses new set
asyncTest('stopping the "ajax:beforeSend" event aborts the request', 1, function() {
submit(function(form) {
- form.bindNative('ajax:beforeSend', function() {
+ form.bindNative('ajax:beforeSend', function(e) {
ok(true, 'aborting request in ajax:beforeSend')
- return false
+ e.preventDefault()
})
form.unbind('ajax:send').bindNative('ajax:send', function() {
ok(false, 'ajax:send should not run')
@@ -148,8 +148,8 @@ function skipIt() {
.bind('iframe:loading', function() {
ok(false, 'form should not get submitted')
})
- .bindNative('ajax:aborted:file', function() {
- return false
+ .bindNative('ajax:aborted:file', function(e) {
+ e.preventDefault()
})
.triggerNative('submit')
@@ -162,9 +162,9 @@ function skipIt() {
}
asyncTest('"ajax:beforeSend" can be observed and stopped with event delegation', 1, function() {
- $(document).delegate('form[data-remote]', 'ajax:beforeSend', function() {
+ $(document).delegate('form[data-remote]', 'ajax:beforeSend', function(e) {
ok(true, 'ajax:beforeSend observed with event delegation')
- return false
+ e.preventDefault()
})
submit(function(form) {
diff --git a/actionview/test/ujs/public/test/call-remote.js b/actionview/test/ujs/public/test/call-remote.js
index e5277a2a03..778dc1b09a 100644
--- a/actionview/test/ujs/public/test/call-remote.js
+++ b/actionview/test/ujs/public/test/call-remote.js
@@ -210,7 +210,7 @@ asyncTest('allow empty form "action"', 1, function() {
buildForm({ action: '' })
$('#qunit-fixture').find('form')
- .bindNative('ajax:beforeSend', function(e, xhr, settings) {
+ .bindNative('ajax:beforeSend', function(evt, xhr, settings) {
// Get current location (the same way jQuery does)
try {
currentLocation = location.href
@@ -229,7 +229,7 @@ asyncTest('allow empty form "action"', 1, function() {
// Prevent the request from actually getting sent to the current page and
// causing an error.
- return false
+ evt.preventDefault()
})
.triggerNative('submit')
@@ -257,7 +257,7 @@ asyncTest('intelligently guesses crossDomain behavior when target URL has a diff
equal(settings.crossDomain, true, 'crossDomain should be set to true')
// prevent request from actually getting sent off-domain
- return false
+ evt.preventDefault()
})
.triggerNative('submit')
@@ -276,7 +276,7 @@ asyncTest('intelligently guesses crossDomain behavior when target URL consists o
equal(settings.crossDomain, false, 'crossDomain should be set to false')
// prevent request from actually getting sent off-domain
- return false
+ evt.preventDefault()
})
.triggerNative('submit')
diff --git a/actionview/test/ujs/public/test/data-confirm.js b/actionview/test/ujs/public/test/data-confirm.js
index 74f373148f..1bd57b69ad 100644
--- a/actionview/test/ujs/public/test/data-confirm.js
+++ b/actionview/test/ujs/public/test/data-confirm.js
@@ -173,9 +173,9 @@ asyncTest('binding to confirm event of a link and returning false', 1, function(
}
$('a[data-confirm]')
- .bindNative('confirm', function() {
+ .bindNative('confirm', function(e) {
App.assertCallbackInvoked('confirm')
- return false
+ e.preventDefault()
})
.bindNative('confirm:complete', function() {
App.assertCallbackNotInvoked('confirm:complete')
@@ -194,9 +194,9 @@ asyncTest('binding to confirm event of a button and returning false', 1, functio
}
$('button[data-confirm]')
- .bindNative('confirm', function() {
+ .bindNative('confirm', function(e) {
App.assertCallbackInvoked('confirm')
- return false
+ e.preventDefault()
})
.bindNative('confirm:complete', function() {
App.assertCallbackNotInvoked('confirm:complete')
@@ -216,9 +216,9 @@ asyncTest('binding to confirm:complete event of a link and returning false', 2,
}
$('a[data-confirm]')
- .bindNative('confirm:complete', function() {
+ .bindNative('confirm:complete', function(e) {
App.assertCallbackInvoked('confirm:complete')
- return false
+ e.preventDefault()
})
.bindNative('ajax:beforeSend', function() {
App.assertCallbackNotInvoked('ajax:beforeSend')
@@ -238,9 +238,9 @@ asyncTest('binding to confirm:complete event of a button and returning false', 2
}
$('button[data-confirm]')
- .bindNative('confirm:complete', function() {
+ .bindNative('confirm:complete', function(e) {
App.assertCallbackInvoked('confirm:complete')
- return false
+ e.preventDefault()
})
.bindNative('ajax:beforeSend', function() {
App.assertCallbackNotInvoked('ajax:beforeSend')
diff --git a/actionview/test/ujs/public/test/data-disable-with.js b/actionview/test/ujs/public/test/data-disable-with.js
index b29cbbc867..645ad494c3 100644
--- a/actionview/test/ujs/public/test/data-disable-with.js
+++ b/actionview/test/ujs/public/test/data-disable-with.js
@@ -132,7 +132,8 @@ test('form input[type=submit][data-disable-with] re-enables when `pageshow` even
})
asyncTest('form[data-remote] input[type=submit][data-disable-with] is replaced in ajax callback', 2, function() {
- var form = $('form:not([data-remote])').attr('data-remote', 'true'), origFormContents = form.html()
+ var form = $('#qunit-fixture form:not([data-remote])').attr('data-remote', 'true'),
+ origFormContents = form.html()
form.bindNative('ajax:success', function() {
form.html(origFormContents)
@@ -146,7 +147,8 @@ asyncTest('form[data-remote] input[type=submit][data-disable-with] is replaced i
})
asyncTest('form[data-remote] input[data-disable-with] is replaced with disabled field in ajax callback', 2, function() {
- var form = $('form:not([data-remote])').attr('data-remote', 'true'), input = form.find('input[type=submit]'),
+ var form = $('#qunit-fixture form:not([data-remote])').attr('data-remote', 'true'),
+ input = form.find('input[type=submit]'),
newDisabledInput = input.clone().attr('disabled', 'disabled')
form.bindNative('ajax:success', function() {
@@ -238,9 +240,9 @@ asyncTest('a[data-remote][data-disable-with] re-enables when `ajax:before` event
App.checkEnabledState(link, 'Click me')
link
- .bindNative('ajax:before', function() {
+ .bindNative('ajax:before', function(e) {
App.checkDisabledState(link, 'clicking...')
- return false
+ e.preventDefault()
})
.triggerNative('click')
@@ -256,9 +258,9 @@ asyncTest('a[data-remote][data-disable-with] re-enables when `ajax:beforeSend` e
App.checkEnabledState(link, 'Click me')
link
- .bindNative('ajax:beforeSend', function() {
+ .bindNative('ajax:beforeSend', function(e) {
App.checkDisabledState(link, 'clicking...')
- return false
+ e.preventDefault()
})
.triggerNative('click')
@@ -293,8 +295,9 @@ asyncTest('form[data-remote] input|button|textarea[data-disable-with] does not d
submit = $('<input type="submit" data-disable-with="submitting ..." name="submit2" value="Submit" />').appendTo(form)
form
- .bindNative('ajax:beforeSend', function() {
- return false
+ .bindNative('ajax:beforeSend', function(e) {
+ e.preventDefault()
+ e.stopPropagation()
})
.triggerNative('submit')
@@ -343,9 +346,9 @@ asyncTest('button[data-remote][data-disable-with] re-enables when `ajax:before`
App.checkEnabledState(button, 'Click me')
button
- .bindNative('ajax:before', function() {
+ .bindNative('ajax:before', function(e) {
App.checkDisabledState(button, 'clicking...')
- return false
+ e.preventDefault()
})
.triggerNative('click')
@@ -361,9 +364,9 @@ asyncTest('button[data-remote][data-disable-with] re-enables when `ajax:beforeSe
App.checkEnabledState(button, 'Click me')
button
- .bindNative('ajax:beforeSend', function() {
+ .bindNative('ajax:beforeSend', function(e) {
App.checkDisabledState(button, 'clicking...')
- return false
+ e.preventDefault()
})
.triggerNative('click')
diff --git a/actionview/test/ujs/public/test/data-disable.js b/actionview/test/ujs/public/test/data-disable.js
index ccc38cf9ae..e9919764b6 100644
--- a/actionview/test/ujs/public/test/data-disable.js
+++ b/actionview/test/ujs/public/test/data-disable.js
@@ -91,7 +91,7 @@ asyncTest('form input[type=submit][data-disable] disables', 6, function() {
})
asyncTest('form[data-remote] input[type=submit][data-disable] is replaced in ajax callback', 2, function() {
- var form = $('form:not([data-remote])').attr('data-remote', 'true'), origFormContents = form.html()
+ var form = $('#qunit-fixture form:not([data-remote])').attr('data-remote', 'true'), origFormContents = form.html()
form.bindNative('ajax:success', function() {
form.html(origFormContents)
@@ -105,7 +105,7 @@ asyncTest('form[data-remote] input[type=submit][data-disable] is replaced in aja
})
asyncTest('form[data-remote] input[data-disable] is replaced with disabled field in ajax callback', 2, function() {
- var form = $('form:not([data-remote])').attr('data-remote', 'true'), input = form.find('input[type=submit]'),
+ var form = $('#qunit-fixture form:not([data-remote])').attr('data-remote', 'true'), input = form.find('input[type=submit]'),
newDisabledInput = input.clone().attr('disabled', 'disabled')
form.bindNative('ajax:success', function() {
@@ -168,9 +168,9 @@ asyncTest('a[data-remote][data-disable] re-enables when `ajax:before` event is c
App.checkEnabledState(link, 'Click me')
link
- .bindNative('ajax:before', function() {
+ .bindNative('ajax:before', function(e) {
App.checkDisabledState(link, 'Click me')
- return false
+ e.preventDefault()
})
.triggerNative('click')
@@ -186,9 +186,9 @@ asyncTest('a[data-remote][data-disable] re-enables when `ajax:beforeSend` event
App.checkEnabledState(link, 'Click me')
link
- .bindNative('ajax:beforeSend', function() {
+ .bindNative('ajax:beforeSend', function(e) {
App.checkDisabledState(link, 'Click me')
- return false
+ e.preventDefault()
})
.triggerNative('click')
@@ -223,8 +223,9 @@ asyncTest('form[data-remote] input|button|textarea[data-disable] does not disabl
submit = $('<input type="submit" data-disable="submitting ..." name="submit2" value="Submit" />').appendTo(form)
form
- .bindNative('ajax:beforeSend', function() {
- return false
+ .bindNative('ajax:beforeSend', function(e) {
+ e.preventDefault()
+ e.stopPropagation()
})
.triggerNative('submit')
@@ -273,9 +274,9 @@ asyncTest('button[data-remote][data-disable] re-enables when `ajax:before` event
App.checkEnabledState(button, 'Click me')
button
- .bindNative('ajax:before', function() {
+ .bindNative('ajax:before', function(e) {
App.checkDisabledState(button, 'Click me')
- return false
+ e.preventDefault()
})
.triggerNative('click')
@@ -291,9 +292,9 @@ asyncTest('button[data-remote][data-disable] re-enables when `ajax:beforeSend` e
App.checkEnabledState(button, 'Click me')
button
- .bindNative('ajax:beforeSend', function() {
+ .bindNative('ajax:beforeSend', function(e) {
App.checkDisabledState(button, 'Click me')
- return false
+ e.preventDefault()
})
.triggerNative('click')
diff --git a/actionview/test/ujs/public/test/data-remote.js b/actionview/test/ujs/public/test/data-remote.js
index cbbd4e6c92..3503c2cff3 100644
--- a/actionview/test/ujs/public/test/data-remote.js
+++ b/actionview/test/ujs/public/test/data-remote.js
@@ -272,9 +272,10 @@ asyncTest('returning false in form\'s submit bindings in non-submit-bubbling bro
form
.append($('<input type="submit" />'))
- .bindNative('submit', function() {
+ .bindNative('submit', function(e) {
ok(true, 'binding handler is called')
- return false
+ e.preventDefault()
+ e.stopPropagation()
})
.bindNative('ajax:beforeSend', function() {
ok(false, 'form should not be submitted')
@@ -296,8 +297,8 @@ asyncTest('clicking on a link with falsy "data-remote" attribute does not fire a
.bindNative('ajax:beforeSend', function() {
ok(false, 'ajax should not be triggered')
})
- .bindNative('click', function() {
- return false
+ .bindNative('click', function(e) {
+ e.preventDefault()
})
.triggerNative('click')
@@ -314,8 +315,8 @@ asyncTest('ctrl-clicking on a link with falsy "data-remote" attribute does not f
.bindNative('ajax:beforeSend', function() {
ok(false, 'ajax should not be triggered')
})
- .bindNative('click', function() {
- return false
+ .bindNative('click', function(e) {
+ e.preventDefault()
})
.triggerNative('click', { metaKey: true })
@@ -333,8 +334,8 @@ asyncTest('clicking on a button with falsy "data-remote" attribute', 0, function
.bindNative('ajax:beforeSend', function() {
ok(false, 'ajax should not be triggered')
})
- .bindNative('click', function() {
- return false
+ .bindNative('click', function(e) {
+ e.preventDefault()
})
.triggerNative('click')
@@ -347,8 +348,8 @@ asyncTest('submitting a form with falsy "data-remote" attribute', 0, function()
.bindNative('ajax:beforeSend', function() {
ok(false, 'ajax should not be triggered')
})
- .bindNative('submit', function() {
- return false
+ .bindNative('submit', function(e) {
+ e.preventDefault()
})
.triggerNative('submit')
@@ -429,7 +430,7 @@ asyncTest('changing a select option without "data-url" attribute still fires aja
ajaxLocation = settings.url.replace(settings.data, '').replace(/&$/, '').replace(/\?$/, '')
equal(ajaxLocation, currentLocation, 'URL should be current page by default')
- return false
+ e.preventDefault()
})
.val('optionValue2')
.triggerNative('change')
diff --git a/actionview/test/ujs/public/test/override.js b/actionview/test/ujs/public/test/override.js
index 299c7018cc..d73276ee4f 100644
--- a/actionview/test/ujs/public/test/override.js
+++ b/actionview/test/ujs/public/test/override.js
@@ -25,7 +25,7 @@ asyncTest('the getter for an element\'s href is overridable', 1, function() {
$('#qunit-fixture a')
.bindNative('ajax:beforeSend', function(e, xhr, options) {
equal('/data/href', options.url)
- return false
+ e.preventDefault()
})
.triggerNative('click')
start()
@@ -35,7 +35,7 @@ asyncTest('the getter for an element\'s href works normally if not overridden',
$('#qunit-fixture a')
.bindNative('ajax:beforeSend', function(e, xhr, options) {
equal(location.protocol + '//' + location.host + '/real/href', options.url)
- return false
+ e.preventDefault()
})
.triggerNative('click')
start()
diff --git a/actionview/test/ujs/public/test/settings.js b/actionview/test/ujs/public/test/settings.js
index 299c71bb00..b1ce3b8c64 100644
--- a/actionview/test/ujs/public/test/settings.js
+++ b/actionview/test/ujs/public/test/settings.js
@@ -103,14 +103,16 @@ $.fn.extend({
bindNative: function(event, handler) {
if (!handler) return this
- this.bind(event, function(e) {
+ var el = this[0]
+ el.addEventListener(event, function(e) {
var args = []
- if (e.originalEvent.detail) {
- args = e.originalEvent.detail.slice()
+ if (e.detail) {
+ args = e.detail.slice()
}
args.unshift(e)
- return handler.apply(this, args)
- })
+ return handler.apply(el, args)
+ }, false)
+
return this
}
})
diff --git a/activejob/lib/rails/generators/job/job_generator.rb b/activejob/lib/rails/generators/job/job_generator.rb
index 69b4fe7d26..03346a7f12 100644
--- a/activejob/lib/rails/generators/job/job_generator.rb
+++ b/activejob/lib/rails/generators/job/job_generator.rb
@@ -28,6 +28,10 @@ module Rails # :nodoc:
end
private
+ def file_name
+ @_file_name ||= super.sub(/_job\z/i, "")
+ end
+
def application_job_file_name
@application_job_file_name ||= if mountable_engine?
"app/jobs/#{namespaced_path}/application_job.rb"
diff --git a/activemodel/test/cases/attributes_dirty_test.rb b/activemodel/test/cases/attributes_dirty_test.rb
index c991176389..f9693a23cd 100644
--- a/activemodel/test/cases/attributes_dirty_test.rb
+++ b/activemodel/test/cases/attributes_dirty_test.rb
@@ -39,7 +39,7 @@ class AttributesDirtyTest < ActiveModel::TestCase
end
test "changes to attribute values" do
- assert !@model.changes["name"]
+ assert_not @model.changes["name"]
@model.name = "John"
assert_equal [nil, "John"], @model.changes["name"]
end
diff --git a/activemodel/test/cases/dirty_test.rb b/activemodel/test/cases/dirty_test.rb
index f769eb0da1..b120e68027 100644
--- a/activemodel/test/cases/dirty_test.rb
+++ b/activemodel/test/cases/dirty_test.rb
@@ -78,7 +78,7 @@ class DirtyTest < ActiveModel::TestCase
end
test "changes to attribute values" do
- assert !@model.changes["name"]
+ assert_not @model.changes["name"]
@model.name = "John"
assert_equal [nil, "John"], @model.changes["name"]
end
diff --git a/activemodel/test/cases/errors_test.rb b/activemodel/test/cases/errors_test.rb
index cb6a8c43d5..6ff3be1308 100644
--- a/activemodel/test/cases/errors_test.rb
+++ b/activemodel/test/cases/errors_test.rb
@@ -207,26 +207,26 @@ class ErrorsTest < ActiveModel::TestCase
test "added? returns false when no errors are present" do
person = Person.new
- assert !person.errors.added?(:name)
+ assert_not person.errors.added?(:name)
end
test "added? returns false when checking a nonexisting error and other errors are present for the given attribute" do
person = Person.new
person.errors.add(:name, "is invalid")
- assert !person.errors.added?(:name, "cannot be blank")
+ assert_not person.errors.added?(:name, "cannot be blank")
end
test "added? returns false when checking for an error, but not providing message arguments" do
person = Person.new
person.errors.add(:name, "cannot be blank")
- assert !person.errors.added?(:name)
+ assert_not person.errors.added?(:name)
end
test "added? returns false when checking for an error by symbol and a different error with same message is present" do
I18n.backend.store_translations("en", errors: { attributes: { name: { wrong: "is wrong", used: "is wrong" } } })
person = Person.new
person.errors.add(:name, :wrong)
- assert !person.errors.added?(:name, :used)
+ assert_not person.errors.added?(:name, :used)
end
test "size calculates the number of error messages" do
diff --git a/activemodel/test/cases/secure_password_test.rb b/activemodel/test/cases/secure_password_test.rb
index d19e81a119..912cb6d5ab 100644
--- a/activemodel/test/cases/secure_password_test.rb
+++ b/activemodel/test/cases/secure_password_test.rb
@@ -187,7 +187,7 @@ class SecurePasswordTest < ActiveModel::TestCase
test "authenticate" do
@user.password = "secret"
- assert !@user.authenticate("wrong")
+ assert_not @user.authenticate("wrong")
assert @user.authenticate("secret")
end
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index d8bf7df63b..0d8fa48235 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,3 +1,15 @@
+* Add support to preload associations of polymorphic associations when not all the records have the requested associations.
+
+ *Dana Sherson*
+
+* Add `touch_all` method to `ActiveRecord::Relation`.
+
+ Example:
+
+ Person.where(name: "David").touch_all(time: Time.new(2020, 5, 16, 0, 0, 0))
+
+ *fatkodima*, *duggiefresh*
+
* Add `ActiveRecord::Base.base_class?` predicate.
*Bogdan Gusiev*
diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb
index 443ccaaa72..671c4c56df 100644
--- a/activerecord/lib/active_record/associations/collection_association.rb
+++ b/activerecord/lib/active_record/associations/collection_association.rb
@@ -214,7 +214,7 @@ module ActiveRecord
target.size
elsif !association_scope.group_values.empty?
load_target.size
- elsif !association_scope.distinct_value && target.is_a?(Array)
+ elsif !association_scope.distinct_value && !target.empty?
unsaved_records = target.select(&:new_record?)
unsaved_records.size + count_records
else
@@ -234,7 +234,7 @@ module ActiveRecord
if loaded?
size.zero?
else
- @target.blank? && !scope.exists?
+ target.empty? && !scope.exists?
end
end
diff --git a/activerecord/lib/active_record/associations/has_one_through_association.rb b/activerecord/lib/active_record/associations/has_one_through_association.rb
index 491282adf7..019bf0729f 100644
--- a/activerecord/lib/active_record/associations/has_one_through_association.rb
+++ b/activerecord/lib/active_record/associations/has_one_through_association.rb
@@ -28,7 +28,11 @@ module ActiveRecord
end
if through_record
- through_record.update(attributes)
+ if through_record.new_record?
+ through_record.assign_attributes(attributes)
+ else
+ through_record.update(attributes)
+ end
elsif owner.new_record? || !save
through_proxy.build(attributes)
else
diff --git a/activerecord/lib/active_record/associations/preloader.rb b/activerecord/lib/active_record/associations/preloader.rb
index 1ea0aeac3a..5c2ac5b374 100644
--- a/activerecord/lib/active_record/associations/preloader.rb
+++ b/activerecord/lib/active_record/associations/preloader.rb
@@ -98,26 +98,30 @@ module ActiveRecord
private
# Loads all the given data into +records+ for the +association+.
- def preloaders_on(association, records, scope)
+ def preloaders_on(association, records, scope, polymorphic_parent = false)
case association
when Hash
- preloaders_for_hash(association, records, scope)
+ preloaders_for_hash(association, records, scope, polymorphic_parent)
when Symbol
- preloaders_for_one(association, records, scope)
+ preloaders_for_one(association, records, scope, polymorphic_parent)
when String
- preloaders_for_one(association.to_sym, records, scope)
+ preloaders_for_one(association.to_sym, records, scope, polymorphic_parent)
else
raise ArgumentError, "#{association.inspect} was not recognized for preload"
end
end
- def preloaders_for_hash(association, records, scope)
+ def preloaders_for_hash(association, records, scope, polymorphic_parent)
association.flat_map { |parent, child|
- loaders = preloaders_for_one parent, records, scope
+ loaders = preloaders_for_one parent, records, scope, polymorphic_parent
recs = loaders.flat_map(&:preloaded_records).uniq
+
+ reflection = records.first.class._reflect_on_association(parent)
+ polymorphic_parent = reflection && reflection.options[:polymorphic]
+
loaders.concat Array.wrap(child).flat_map { |assoc|
- preloaders_on assoc, recs, scope
+ preloaders_on assoc, recs, scope, polymorphic_parent
}
loaders
}
@@ -135,8 +139,8 @@ module ActiveRecord
# Additionally, polymorphic belongs_to associations can have multiple associated
# classes, depending on the polymorphic_type field. So we group by the classes as
# well.
- def preloaders_for_one(association, records, scope)
- grouped_records(association, records).flat_map do |reflection, klasses|
+ def preloaders_for_one(association, records, scope, polymorphic_parent)
+ grouped_records(association, records, polymorphic_parent).flat_map do |reflection, klasses|
klasses.map do |rhs_klass, rs|
loader = preloader_for(reflection, rs).new(rhs_klass, rs, reflection, scope)
loader.run self
@@ -145,10 +149,11 @@ module ActiveRecord
end
end
- def grouped_records(association, records)
+ def grouped_records(association, records, polymorphic_parent)
h = {}
records.each do |record|
next unless record
+ next if polymorphic_parent && !record.class._reflect_on_association(association)
assoc = record.association(association)
next unless assoc.klass
klasses = h[assoc.reflection] ||= {}
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
index 08f3e15a4b..41553cfa83 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
@@ -385,7 +385,7 @@ module ActiveRecord
end
end
- def empty_insert_statement_value
+ def empty_insert_statement_value(primary_key = nil)
"DEFAULT VALUES"
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 fbedddd7f9..07acb5425e 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -44,7 +44,7 @@ module ActiveRecord
class StatementPool < ConnectionAdapters::StatementPool # :nodoc:
private def dealloc(stmt)
- stmt[:stmt].close
+ stmt.close
end
end
@@ -223,7 +223,7 @@ module ActiveRecord
end
end
- def empty_insert_statement_value
+ def empty_insert_statement_value(primary_key = nil)
"VALUES ()"
end
diff --git a/activerecord/lib/active_record/connection_adapters/mysql/database_statements.rb b/activerecord/lib/active_record/connection_adapters/mysql/database_statements.rb
index 458c9bfd70..4106ce01be 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql/database_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql/database_statements.rb
@@ -71,10 +71,7 @@ module ActiveRecord
log(sql, name, binds, type_casted_binds) do
if cache_stmt
- cache = @statements[sql] ||= {
- stmt: @connection.prepare(sql)
- }
- stmt = cache[:stmt]
+ stmt = @statements[sql] ||= @connection.prepare(sql)
else
stmt = @connection.prepare(sql)
end
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
index a958600446..800e731f06 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
@@ -94,7 +94,7 @@ module ActiveRecord
class StatementPool < ConnectionAdapters::StatementPool # :nodoc:
private
def dealloc(stmt)
- stmt[:stmt].close unless stmt[:stmt].closed?
+ stmt.close unless stmt.closed?
end
end
@@ -224,11 +224,8 @@ module ActiveRecord
stmt.close
end
else
- cache = @statements[sql] ||= {
- stmt: @connection.prepare(sql)
- }
- stmt = cache[:stmt]
- cols = cache[:cols] ||= stmt.columns
+ stmt = @statements[sql] ||= @connection.prepare(sql)
+ cols = stmt.columns
stmt.reset!
stmt.bind_params(type_casted_binds)
records = stmt.to_a
diff --git a/activerecord/lib/active_record/log_subscriber.rb b/activerecord/lib/active_record/log_subscriber.rb
index 9234029c22..013c3765b2 100644
--- a/activerecord/lib/active_record/log_subscriber.rb
+++ b/activerecord/lib/active_record/log_subscriber.rb
@@ -125,7 +125,7 @@ module ActiveRecord
]
end
- RAILS_GEM_ROOT = File.expand_path("../../../..", __FILE__) + "/"
+ RAILS_GEM_ROOT = File.expand_path("../../..", __dir__) + "/"
def ignored_callstack(path)
path.start_with?(RAILS_GEM_ROOT) ||
diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb
index 7721e6b691..c2393c1fc8 100644
--- a/activerecord/lib/active_record/persistence.rb
+++ b/activerecord/lib/active_record/persistence.rb
@@ -178,7 +178,7 @@ module ActiveRecord
end
if values.empty?
- im = arel_table.compile_insert(connection.empty_insert_statement_value)
+ im = arel_table.compile_insert(connection.empty_insert_statement_value(primary_key))
im.into arel_table
else
im = arel_table.compile_insert(_substitute_values(values))
diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb
index 3c66d2f8be..c055b97061 100644
--- a/activerecord/lib/active_record/relation.rb
+++ b/activerecord/lib/active_record/relation.rb
@@ -369,6 +369,43 @@ module ActiveRecord
@klass.connection.update stmt, "#{@klass} Update All"
end
+ # Touches all records in the current relation without instantiating records first with the updated_at/on attributes
+ # set to the current time or the time specified.
+ # This method can be passed attribute names and an optional time argument.
+ # If attribute names are passed, they are updated along with updated_at/on attributes.
+ # If no time argument is passed, the current time is used as default.
+ #
+ # === Examples
+ #
+ # # Touch all records
+ # Person.all.touch_all
+ # # => "UPDATE \"people\" SET \"updated_at\" = '2018-01-04 22:55:23.132670'"
+ #
+ # # Touch multiple records with a custom attribute
+ # Person.all.touch_all(:created_at)
+ # # => "UPDATE \"people\" SET \"updated_at\" = '2018-01-04 22:55:23.132670', \"created_at\" = '2018-01-04 22:55:23.132670'"
+ #
+ # # Touch multiple records with a specified time
+ # Person.all.touch_all(time: Time.new(2020, 5, 16, 0, 0, 0))
+ # # => "UPDATE \"people\" SET \"updated_at\" = '2020-05-16 00:00:00'"
+ #
+ # # Touch records with scope
+ # Person.where(name: 'David').touch_all
+ # # => "UPDATE \"people\" SET \"updated_at\" = '2018-01-04 22:55:23.132670' WHERE \"people\".\"name\" = 'David'"
+ def touch_all(*names, time: nil)
+ attributes = Array(names) + klass.timestamp_attributes_for_update_in_model
+ time ||= klass.current_time_from_proper_timezone
+ updates = {}
+ attributes.each { |column| updates[column] = time }
+
+ if klass.locking_enabled?
+ quoted_locking_column = connection.quote_column_name(klass.locking_column)
+ updates = sanitize_sql_for_assignment(updates) + ", #{quoted_locking_column} = COALESCE(#{quoted_locking_column}, 0) + 1"
+ end
+
+ update_all(updates)
+ end
+
# Destroys the records by instantiating each
# record and calling its {#destroy}[rdoc-ref:Persistence#destroy] method.
# Each object's callbacks are executed (including <tt>:dependent</tt> association options).
@@ -557,7 +594,7 @@ module ActiveRecord
ActiveRecord::Associations::AliasTracker.create(connection, table.name, joins)
end
- def preload_associations(records)
+ def preload_associations(records) # :nodoc:
preload = preload_values
preload += includes_values unless eager_loading?
preloader = nil
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index 4e60863e52..a180b0f0d3 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -894,8 +894,8 @@ module ActiveRecord
self
end
- def skip_query_cache! # :nodoc:
- self.skip_query_cache_value = true
+ def skip_query_cache!(value = true) # :nodoc:
+ self.skip_query_cache_value = value
self
end
diff --git a/activerecord/lib/active_record/timestamp.rb b/activerecord/lib/active_record/timestamp.rb
index 54aa7aca2c..e47f06bf3a 100644
--- a/activerecord/lib/active_record/timestamp.rb
+++ b/activerecord/lib/active_record/timestamp.rb
@@ -53,15 +53,19 @@ module ActiveRecord
end
module ClassMethods # :nodoc:
+ def timestamp_attributes_for_update_in_model
+ timestamp_attributes_for_update.select { |c| column_names.include?(c) }
+ end
+
+ def current_time_from_proper_timezone
+ default_timezone == :utc ? Time.now.utc : Time.now
+ end
+
private
def timestamp_attributes_for_create_in_model
timestamp_attributes_for_create.select { |c| column_names.include?(c) }
end
- def timestamp_attributes_for_update_in_model
- timestamp_attributes_for_update.select { |c| column_names.include?(c) }
- end
-
def all_timestamp_attributes_in_model
timestamp_attributes_for_create_in_model + timestamp_attributes_for_update_in_model
end
@@ -73,10 +77,6 @@ module ActiveRecord
def timestamp_attributes_for_update
["updated_at", "updated_on"]
end
-
- def current_time_from_proper_timezone
- default_timezone == :utc ? Time.now.utc : Time.now
- end
end
private
@@ -116,7 +116,7 @@ module ActiveRecord
end
def timestamp_attributes_for_update_in_model
- self.class.send(:timestamp_attributes_for_update_in_model)
+ self.class.timestamp_attributes_for_update_in_model
end
def all_timestamp_attributes_in_model
@@ -124,7 +124,7 @@ module ActiveRecord
end
def current_time_from_proper_timezone
- self.class.send(:current_time_from_proper_timezone)
+ self.class.current_time_from_proper_timezone
end
def max_updated_column_timestamp(timestamp_names = timestamp_attributes_for_update_in_model)
diff --git a/activerecord/test/cases/adapter_test.rb b/activerecord/test/cases/adapter_test.rb
index 4c41a68407..02c7e47583 100644
--- a/activerecord/test/cases/adapter_test.rb
+++ b/activerecord/test/cases/adapter_test.rb
@@ -84,7 +84,7 @@ module ActiveRecord
indexes = @connection.indexes("accounts")
assert_equal "accounts", indexes.first.table
assert_equal idx_name, indexes.first.name
- assert !indexes.first.unique
+ assert_not indexes.first.unique
assert_equal ["firm_id"], indexes.first.columns
ensure
@connection.remove_index(:accounts, name: idx_name) rescue nil
diff --git a/activerecord/test/cases/adapters/mysql2/active_schema_test.rb b/activerecord/test/cases/adapters/mysql2/active_schema_test.rb
index 9ae2c42368..976c5dde58 100644
--- a/activerecord/test/cases/adapters/mysql2/active_schema_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/active_schema_test.rb
@@ -148,8 +148,8 @@ class Mysql2ActiveSchemaTest < ActiveRecord::Mysql2TestCase
t.timestamps null: true
end
ActiveRecord::Base.connection.remove_timestamps :delete_me, null: true
- assert !column_present?("delete_me", "updated_at", "datetime")
- assert !column_present?("delete_me", "created_at", "datetime")
+ assert_not column_present?("delete_me", "updated_at", "datetime")
+ assert_not column_present?("delete_me", "created_at", "datetime")
ensure
ActiveRecord::Base.connection.drop_table :delete_me rescue nil
end
diff --git a/activerecord/test/cases/ar_schema_test.rb b/activerecord/test/cases/ar_schema_test.rb
index 140d7cbcae..f05dcac7dd 100644
--- a/activerecord/test/cases/ar_schema_test.rb
+++ b/activerecord/test/cases/ar_schema_test.rb
@@ -116,8 +116,8 @@ class ActiveRecordSchemaTest < ActiveRecord::TestCase
end
end
- assert !@connection.columns(:has_timestamps).find { |c| c.name == "created_at" }.null
- assert !@connection.columns(:has_timestamps).find { |c| c.name == "updated_at" }.null
+ assert_not @connection.columns(:has_timestamps).find { |c| c.name == "created_at" }.null
+ assert_not @connection.columns(:has_timestamps).find { |c| c.name == "updated_at" }.null
end
def test_timestamps_without_null_set_null_to_false_on_change_table
@@ -129,8 +129,8 @@ class ActiveRecordSchemaTest < ActiveRecord::TestCase
end
end
- assert !@connection.columns(:has_timestamps).find { |c| c.name == "created_at" }.null
- assert !@connection.columns(:has_timestamps).find { |c| c.name == "updated_at" }.null
+ assert_not @connection.columns(:has_timestamps).find { |c| c.name == "created_at" }.null
+ assert_not @connection.columns(:has_timestamps).find { |c| c.name == "updated_at" }.null
end
def test_timestamps_without_null_set_null_to_false_on_add_timestamps
@@ -139,7 +139,7 @@ class ActiveRecordSchemaTest < ActiveRecord::TestCase
add_timestamps :has_timestamps, default: Time.now
end
- assert !@connection.columns(:has_timestamps).find { |c| c.name == "created_at" }.null
- assert !@connection.columns(:has_timestamps).find { |c| c.name == "updated_at" }.null
+ assert_not @connection.columns(:has_timestamps).find { |c| c.name == "created_at" }.null
+ assert_not @connection.columns(:has_timestamps).find { |c| c.name == "updated_at" }.null
end
end
diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb
index 6e0cf30092..f46be8734b 100644
--- a/activerecord/test/cases/associations/eager_test.rb
+++ b/activerecord/test/cases/associations/eager_test.rb
@@ -1515,6 +1515,35 @@ class EagerAssociationTest < ActiveRecord::TestCase
Author.preload(:readonly_comments).first!
end
+ test "preloading through a polymorphic association doesn't require the association to exist" do
+ sponsors = []
+ assert_queries 5 do
+ sponsors = Sponsor.where(sponsorable_id: 1).preload(sponsorable: [:post, :membership]).to_a
+ end
+ # check the preload worked
+ assert_queries 0 do
+ sponsors.map(&:sponsorable).map { |s| s.respond_to?(:posts) ? s.post.author : s.membership }
+ end
+ end
+
+ test "preloading a regular association through a polymorphic association doesn't require the association to exist on all types" do
+ sponsors = []
+ assert_queries 6 do
+ sponsors = Sponsor.where(sponsorable_id: 1).preload(sponsorable: [{ post: :first_comment }, :membership]).to_a
+ end
+ # check the preload worked
+ assert_queries 0 do
+ sponsors.map(&:sponsorable).map { |s| s.respond_to?(:posts) ? s.post.author : s.membership }
+ end
+ end
+
+ test "preloading a regular association with a typo through a polymorphic association still raises" do
+ # this test contains an intentional typo of first -> fist
+ assert_raises(ActiveRecord::AssociationNotFoundError) do
+ Sponsor.where(sponsorable_id: 1).preload(sponsorable: [{ post: :fist_comment }, :membership]).to_a
+ end
+ end
+
private
def find_all_ordered(klass, include = nil)
klass.order("#{klass.table_name}.#{klass.primary_key}").includes(include).to_a
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 5f771fe85f..5d9735d98a 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
@@ -569,7 +569,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
developer = Developer.create name: "Bryan", salary: 50_000
assert_not_predicate project.developers, :loaded?
- assert ! project.developers.include?(developer)
+ assert_not project.developers.include?(developer)
end
def test_find_with_merged_options
diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb
index a64432fae7..33fe5ccabc 100644
--- a/activerecord/test/cases/associations/has_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_associations_test.rb
@@ -983,6 +983,16 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_not_empty company.contracts
end
+ def test_collection_size_with_dirty_target
+ post = posts(:thinking)
+ assert_equal [], post.reader_ids
+ assert_equal 0, post.readers.size
+ post.readers.reset
+ post.readers.build
+ assert_equal [], post.reader_ids
+ assert_equal 1, post.readers.size
+ end
+
def test_collection_size_twice_for_regressions
post = posts(:thinking)
assert_equal 0, post.readers.size
@@ -2009,7 +2019,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_calling_none_on_loaded_association_should_not_use_query
firm = companies(:first_firm)
firm.clients.load # force load
- assert_no_queries { assert ! firm.clients.none? }
+ assert_no_queries { assert_not firm.clients.none? }
end
def test_calling_none_should_defer_to_collection_if_using_a_block
@@ -2044,7 +2054,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_calling_one_on_loaded_association_should_not_use_query
firm = companies(:first_firm)
firm.clients.load # force load
- assert_no_queries { assert ! firm.clients.one? }
+ assert_no_queries { assert_not firm.clients.one? }
end
def test_calling_one_should_defer_to_collection_if_using_a_block
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 3b3d4037b9..0facc286da 100644
--- a/activerecord/test/cases/associations/has_many_through_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb
@@ -866,7 +866,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
author = authors(:mary)
category = author.named_categories.create(name: "Primary")
author.named_categories.delete(category)
- assert !Categorization.exists?(author_id: author.id, named_category_name: category.name)
+ assert_not Categorization.exists?(author_id: author.id, named_category_name: category.name)
assert_empty author.named_categories.reload
end
diff --git a/activerecord/test/cases/associations/has_one_associations_test.rb b/activerecord/test/cases/associations/has_one_associations_test.rb
index b90edf9b13..d7e898a1c0 100644
--- a/activerecord/test/cases/associations/has_one_associations_test.rb
+++ b/activerecord/test/cases/associations/has_one_associations_test.rb
@@ -452,7 +452,7 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
assert_equal new_ship, pirate.ship
assert_predicate new_ship, :new_record?
assert_nil orig_ship.pirate_id
- assert !orig_ship.changed? # check it was saved
+ assert_not orig_ship.changed? # check it was saved
end
def test_creation_failure_with_dependent_option
diff --git a/activerecord/test/cases/associations/has_one_through_associations_test.rb b/activerecord/test/cases/associations/has_one_through_associations_test.rb
index 9964f084ac..0309663943 100644
--- a/activerecord/test/cases/associations/has_one_through_associations_test.rb
+++ b/activerecord/test/cases/associations/has_one_through_associations_test.rb
@@ -64,6 +64,24 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
assert_equal clubs(:moustache_club), new_member.club
end
+ def test_building_multiple_associations_builds_through_record
+ member_type = MemberType.create!
+ member = Member.create!
+ member_detail_with_one_association = MemberDetail.new(member_type: member_type)
+ assert_predicate member_detail_with_one_association.member, :new_record?
+ member_detail_with_two_associations = MemberDetail.new(member_type: member_type, admittable: member)
+ assert_predicate member_detail_with_two_associations.member, :new_record?
+ end
+
+ def test_creating_multiple_associations_creates_through_record
+ member_type = MemberType.create!
+ member = Member.create!
+ member_detail_with_one_association = MemberDetail.create!(member_type: member_type)
+ assert_not_predicate member_detail_with_one_association.member, :new_record?
+ member_detail_with_two_associations = MemberDetail.create!(member_type: member_type, admittable: member)
+ assert_not_predicate member_detail_with_two_associations.member, :new_record?
+ end
+
def test_creating_association_sets_both_parent_ids_for_new
member = Member.new(name: "Sean Griffin")
club = Club.new(name: "Da Club")
diff --git a/activerecord/test/cases/associations/join_model_test.rb b/activerecord/test/cases/associations/join_model_test.rb
index 57ebc1e3e0..9d1c73c33b 100644
--- a/activerecord/test/cases/associations/join_model_test.rb
+++ b/activerecord/test/cases/associations/join_model_test.rb
@@ -732,7 +732,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
category = Category.create!(name: "Not Associated")
assert_not_predicate david.categories, :loaded?
- assert ! david.categories.include?(category)
+ assert_not david.categories.include?(category)
end
def test_has_many_through_goes_through_all_sti_classes
diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb
index dc6638d45d..1a37ad963f 100644
--- a/activerecord/test/cases/attribute_methods_test.rb
+++ b/activerecord/test/cases/attribute_methods_test.rb
@@ -63,8 +63,8 @@ class AttributeMethodsTest < ActiveRecord::TestCase
t.author_name = ""
assert t.attribute_present?("title")
assert t.attribute_present?("written_on")
- assert !t.attribute_present?("content")
- assert !t.attribute_present?("author_name")
+ assert_not t.attribute_present?("content")
+ assert_not t.attribute_present?("author_name")
end
test "attribute_present with booleans" do
@@ -77,7 +77,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert b2.attribute_present?(:value)
b3 = Boolean.new
- assert !b3.attribute_present?(:value)
+ assert_not b3.attribute_present?(:value)
b4 = Boolean.new
b4.value = false
@@ -827,7 +827,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
self.table_name = "computers"
end
- assert !klass.instance_method_already_implemented?(:system)
+ assert_not klass.instance_method_already_implemented?(:system)
computer = klass.new
assert_nil computer.system
end
@@ -841,8 +841,8 @@ class AttributeMethodsTest < ActiveRecord::TestCase
self.table_name = "computers"
end
- assert !klass.instance_method_already_implemented?(:system)
- assert !subklass.instance_method_already_implemented?(:system)
+ assert_not klass.instance_method_already_implemented?(:system)
+ assert_not subklass.instance_method_already_implemented?(:system)
computer = subklass.new
assert_nil computer.system
end
diff --git a/activerecord/test/cases/autosave_association_test.rb b/activerecord/test/cases/autosave_association_test.rb
index b8243d148a..7915599f72 100644
--- a/activerecord/test/cases/autosave_association_test.rb
+++ b/activerecord/test/cases/autosave_association_test.rb
@@ -116,7 +116,7 @@ class TestDefaultAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCas
assert_not_predicate firm.account, :valid?
assert_not_predicate firm, :valid?
- assert !firm.save
+ assert_not firm.save
assert_equal ["is invalid"], firm.errors["account"]
end
@@ -237,7 +237,7 @@ class TestDefaultAutosaveAssociationOnABelongsToAssociation < ActiveRecord::Test
log.developer = Developer.new
assert_not_predicate log.developer, :valid?
assert_not_predicate log, :valid?
- assert !log.save
+ assert_not log.save
assert_equal ["is invalid"], log.errors["developer"]
end
@@ -499,10 +499,10 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCa
def test_invalid_adding
firm = Firm.find(1)
- assert !(firm.clients_of_firm << c = Client.new)
+ assert_not (firm.clients_of_firm << c = Client.new)
assert_not_predicate c, :persisted?
assert_not_predicate firm, :valid?
- assert !firm.save
+ assert_not firm.save
assert_not_predicate c, :persisted?
end
@@ -512,7 +512,7 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCa
assert_not_predicate c, :persisted?
assert_not_predicate c, :valid?
assert_not_predicate new_firm, :valid?
- assert !new_firm.save
+ assert_not new_firm.save
assert_not_predicate c, :persisted?
assert_not_predicate new_firm, :persisted?
end
@@ -550,7 +550,7 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCa
assert_not_predicate new_client, :persisted?
assert_not_predicate new_client, :valid?
assert_equal new_client, companies(:first_firm).clients_of_firm.last
- assert !companies(:first_firm).save
+ assert_not companies(:first_firm).save
assert_not_predicate new_client, :persisted?
assert_equal 2, companies(:first_firm).clients_of_firm.reload.size
end
@@ -795,7 +795,7 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
@ship.pirate.catchphrase = "Changed Catchphrase"
@ship.name_will_change!
- assert_raise(RuntimeError) { assert !@pirate.save }
+ assert_raise(RuntimeError) { assert_not @pirate.save }
assert_not_nil @pirate.reload.ship
end
@@ -855,7 +855,7 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
@ship.pirate.catchphrase = "Changed Catchphrase"
- assert_raise(RuntimeError) { assert !@ship.save }
+ assert_raise(RuntimeError) { assert_not @ship.save }
assert_not_nil @ship.reload.pirate
end
@@ -871,7 +871,7 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
def test_should_destroy_has_many_as_part_of_the_save_transaction_if_they_were_marked_for_destruction
2.times { |i| @pirate.birds.create!(name: "birds_#{i}") }
- assert !@pirate.birds.any?(&:marked_for_destruction?)
+ assert_not @pirate.birds.any?(&:marked_for_destruction?)
@pirate.birds.each(&:mark_for_destruction)
klass = @pirate.birds.first.class
@@ -937,7 +937,7 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
end
end
- assert_raise(RuntimeError) { assert !@pirate.save }
+ assert_raise(RuntimeError) { assert_not @pirate.save }
assert_equal before, @pirate.reload.birds
end
@@ -1003,7 +1003,7 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
def test_should_destroy_habtm_as_part_of_the_save_transaction_if_they_were_marked_for_destruction
2.times { |i| @pirate.parrots.create!(name: "parrots_#{i}") }
- assert !@pirate.parrots.any?(&:marked_for_destruction?)
+ assert_not @pirate.parrots.any?(&:marked_for_destruction?)
@pirate.parrots.each(&:mark_for_destruction)
assert_no_difference "Parrot.count" do
@@ -1065,7 +1065,7 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
end
end
- assert_raise(RuntimeError) { assert !@pirate.save }
+ assert_raise(RuntimeError) { assert_not @pirate.save }
assert_equal before, @pirate.reload.parrots
end
@@ -1213,7 +1213,7 @@ class TestAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCase
assert_no_difference "Pirate.count" do
assert_no_difference "Ship.count" do
- assert !pirate.save
+ assert_not pirate.save
end
end
end
@@ -1232,7 +1232,7 @@ class TestAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCase
end
end
- assert_raise(RuntimeError) { assert !@pirate.save }
+ assert_raise(RuntimeError) { assert_not @pirate.save }
assert_equal before, [@pirate.reload.catchphrase, @pirate.ship.name]
end
@@ -1337,7 +1337,7 @@ class TestAutosaveAssociationOnABelongsToAssociation < ActiveRecord::TestCase
assert_no_difference "Ship.count" do
assert_no_difference "Pirate.count" do
- assert !ship.save
+ assert_not ship.save
end
end
end
@@ -1356,7 +1356,7 @@ class TestAutosaveAssociationOnABelongsToAssociation < ActiveRecord::TestCase
end
end
- assert_raise(RuntimeError) { assert !@ship.save }
+ assert_raise(RuntimeError) { assert_not @ship.save }
assert_equal before, [@ship.pirate.reload.catchphrase, @ship.reload.name]
end
@@ -1480,7 +1480,7 @@ module AutosaveAssociationOnACollectionAssociationTests
@child_1.name = "Changed"
@child_1.cancel_save_from_callback = true
- assert !@pirate.save
+ assert_not @pirate.save
assert_equal "Don' botharrr talkin' like one, savvy?", @pirate.reload.catchphrase
assert_equal "Posideons Killer", @child_1.reload.name
@@ -1490,7 +1490,7 @@ module AutosaveAssociationOnACollectionAssociationTests
assert_no_difference "Pirate.count" do
assert_no_difference "#{new_child.class.name}.count" do
- assert !new_pirate.save
+ assert_not new_pirate.save
end
end
end
@@ -1510,7 +1510,7 @@ module AutosaveAssociationOnACollectionAssociationTests
end
end
- assert_raise(RuntimeError) { assert !@pirate.save }
+ assert_raise(RuntimeError) { assert_not @pirate.save }
assert_equal before, [@pirate.reload.catchphrase, *@pirate.send(@association_name).map(&:name)]
end
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index d5e1c2feb7..fd008ca8e3 100644
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -307,7 +307,7 @@ class BasicsTest < ActiveRecord::TestCase
assert_equal "Dude", cbs[0].name
assert_equal "Bob", cbs[1].name
assert cbs[0].frickinawesome
- assert !cbs[1].frickinawesome
+ assert_not cbs[1].frickinawesome
end
def test_load
@@ -856,11 +856,11 @@ class BasicsTest < ActiveRecord::TestCase
def test_clone_of_new_object_marks_as_dirty_only_changed_attributes
developer = Developer.new name: "Bjorn"
assert developer.name_changed? # obviously
- assert !developer.salary_changed? # attribute has non-nil default value, so treated as not changed
+ assert_not developer.salary_changed? # attribute has non-nil default value, so treated as not changed
cloned_developer = developer.clone
assert_predicate cloned_developer, :name_changed?
- assert !cloned_developer.salary_changed? # ... and cloned instance should behave same
+ assert_not cloned_developer.salary_changed? # ... and cloned instance should behave same
end
def test_dup_of_saved_object_marks_attributes_as_dirty
@@ -875,12 +875,12 @@ class BasicsTest < ActiveRecord::TestCase
def test_dup_of_saved_object_marks_as_dirty_only_changed_attributes
developer = Developer.create! name: "Bjorn"
- assert !developer.name_changed? # both attributes of saved object should be treated as not changed
+ assert_not developer.name_changed? # both attributes of saved object should be treated as not changed
assert_not_predicate developer, :salary_changed?
cloned_developer = developer.dup
assert cloned_developer.name_changed? # ... but on cloned object should be
- assert !cloned_developer.salary_changed? # ... BUT salary has non-nil default which should be treated as not changed on cloned instance
+ assert_not cloned_developer.salary_changed? # ... BUT salary has non-nil default which should be treated as not changed on cloned instance
end
def test_bignum
diff --git a/activerecord/test/cases/callbacks_test.rb b/activerecord/test/cases/callbacks_test.rb
index 3b283a3aa6..b9ba51c730 100644
--- a/activerecord/test/cases/callbacks_test.rb
+++ b/activerecord/test/cases/callbacks_test.rb
@@ -385,9 +385,9 @@ class CallbacksTest < ActiveRecord::TestCase
end
def assert_save_callbacks_not_called(someone)
- assert !someone.after_save_called
- assert !someone.after_create_called
- assert !someone.after_update_called
+ assert_not someone.after_save_called
+ assert_not someone.after_create_called
+ assert_not someone.after_update_called
end
private :assert_save_callbacks_not_called
@@ -395,27 +395,27 @@ class CallbacksTest < ActiveRecord::TestCase
someone = CallbackHaltedDeveloper.new
someone.cancel_before_create = true
assert_predicate someone, :valid?
- assert !someone.save
+ assert_not someone.save
assert_save_callbacks_not_called(someone)
end
def test_before_save_throwing_abort
david = DeveloperWithCanceledCallbacks.find(1)
assert_predicate david, :valid?
- assert !david.save
+ assert_not david.save
exc = assert_raise(ActiveRecord::RecordNotSaved) { david.save! }
assert_equal david, exc.record
david = DeveloperWithCanceledCallbacks.find(1)
david.salary = 10_000_000
assert_not_predicate david, :valid?
- assert !david.save
+ assert_not david.save
assert_raise(ActiveRecord::RecordInvalid) { david.save! }
someone = CallbackHaltedDeveloper.find(1)
someone.cancel_before_save = true
assert_predicate someone, :valid?
- assert !someone.save
+ assert_not someone.save
assert_save_callbacks_not_called(someone)
end
@@ -423,22 +423,22 @@ class CallbacksTest < ActiveRecord::TestCase
someone = CallbackHaltedDeveloper.find(1)
someone.cancel_before_update = true
assert_predicate someone, :valid?
- assert !someone.save
+ assert_not someone.save
assert_save_callbacks_not_called(someone)
end
def test_before_destroy_throwing_abort
david = DeveloperWithCanceledCallbacks.find(1)
- assert !david.destroy
+ assert_not david.destroy
exc = assert_raise(ActiveRecord::RecordNotDestroyed) { david.destroy! }
assert_equal david, exc.record
assert_not_nil ImmutableDeveloper.find_by_id(1)
someone = CallbackHaltedDeveloper.find(1)
someone.cancel_before_destroy = true
- assert !someone.destroy
+ assert_not someone.destroy
assert_raise(ActiveRecord::RecordNotDestroyed) { someone.destroy! }
- assert !someone.after_destroy_called
+ assert_not someone.after_destroy_called
end
def test_callback_throwing_abort
@@ -467,12 +467,12 @@ class CallbacksTest < ActiveRecord::TestCase
def test_inheritance_of_callbacks
parent = ParentDeveloper.new
- assert !parent.after_save_called
+ assert_not parent.after_save_called
parent.save
assert parent.after_save_called
child = ChildDeveloper.new
- assert !child.after_save_called
+ assert_not child.after_save_called
child.save
assert child.after_save_called
end
diff --git a/activerecord/test/cases/connection_adapters/connection_handler_test.rb b/activerecord/test/cases/connection_adapters/connection_handler_test.rb
index 57b3a5844e..b8e623f17b 100644
--- a/activerecord/test/cases/connection_adapters/connection_handler_test.rb
+++ b/activerecord/test/cases/connection_adapters/connection_handler_test.rb
@@ -89,7 +89,7 @@ module ActiveRecord
ActiveRecord::Base.establish_connection
- assert_equal "db/primary.sqlite3", ActiveRecord::Base.connection.pool.spec.config[:database]
+ assert_match "db/primary.sqlite3", ActiveRecord::Base.connection.pool.spec.config[:database]
ensure
ActiveRecord::Base.configurations = @prev_configs
ENV["RAILS_ENV"] = previous_env
@@ -112,7 +112,7 @@ module ActiveRecord
ActiveRecord::Base.establish_connection
- assert_equal "db/primary.sqlite3", ActiveRecord::Base.connection.pool.spec.config[:database]
+ assert_match "db/primary.sqlite3", ActiveRecord::Base.connection.pool.spec.config[:database]
ensure
ActiveRecord::Base.configurations = @prev_configs
ENV["RAILS_ENV"] = previous_env
diff --git a/activerecord/test/cases/dirty_test.rb b/activerecord/test/cases/dirty_test.rb
index 5c7f70b7a0..83cc2aa319 100644
--- a/activerecord/test/cases/dirty_test.rb
+++ b/activerecord/test/cases/dirty_test.rb
@@ -366,7 +366,7 @@ class DirtyTest < ActiveRecord::TestCase
def test_changed_attributes_should_be_preserved_if_save_failure
pirate = Pirate.new
pirate.parrot_id = 1
- assert !pirate.save
+ assert_not pirate.save
check_pirate_after_save_failure(pirate)
pirate = Pirate.new
@@ -496,7 +496,7 @@ class DirtyTest < ActiveRecord::TestCase
assert_not_nil pirate.previous_changes["updated_on"][1]
assert_nil pirate.previous_changes["created_on"][0]
assert_not_nil pirate.previous_changes["created_on"][1]
- assert !pirate.previous_changes.key?("parrot_id")
+ assert_not pirate.previous_changes.key?("parrot_id")
# original values should be in previous_changes
pirate = Pirate.new
@@ -510,7 +510,7 @@ class DirtyTest < ActiveRecord::TestCase
assert_equal [nil, pirate.id], pirate.previous_changes["id"]
assert_includes pirate.previous_changes, "updated_on"
assert_includes pirate.previous_changes, "created_on"
- assert !pirate.previous_changes.key?("parrot_id")
+ assert_not pirate.previous_changes.key?("parrot_id")
pirate.catchphrase = "Yar!!"
pirate.reload
@@ -527,8 +527,8 @@ class DirtyTest < ActiveRecord::TestCase
assert_equal ["arrr", "Me Maties!"], pirate.previous_changes["catchphrase"]
assert_not_nil pirate.previous_changes["updated_on"][0]
assert_not_nil pirate.previous_changes["updated_on"][1]
- assert !pirate.previous_changes.key?("parrot_id")
- assert !pirate.previous_changes.key?("created_on")
+ assert_not pirate.previous_changes.key?("parrot_id")
+ assert_not pirate.previous_changes.key?("created_on")
pirate = Pirate.find_by_catchphrase("Me Maties!")
@@ -541,8 +541,8 @@ class DirtyTest < ActiveRecord::TestCase
assert_equal ["Me Maties!", "Thar She Blows!"], pirate.previous_changes["catchphrase"]
assert_not_nil pirate.previous_changes["updated_on"][0]
assert_not_nil pirate.previous_changes["updated_on"][1]
- assert !pirate.previous_changes.key?("parrot_id")
- assert !pirate.previous_changes.key?("created_on")
+ assert_not pirate.previous_changes.key?("parrot_id")
+ assert_not pirate.previous_changes.key?("created_on")
travel(1.second)
@@ -553,8 +553,8 @@ class DirtyTest < ActiveRecord::TestCase
assert_equal ["Thar She Blows!", "Ahoy!"], pirate.previous_changes["catchphrase"]
assert_not_nil pirate.previous_changes["updated_on"][0]
assert_not_nil pirate.previous_changes["updated_on"][1]
- assert !pirate.previous_changes.key?("parrot_id")
- assert !pirate.previous_changes.key?("created_on")
+ assert_not pirate.previous_changes.key?("parrot_id")
+ assert_not pirate.previous_changes.key?("created_on")
travel(1.second)
@@ -565,8 +565,8 @@ class DirtyTest < ActiveRecord::TestCase
assert_equal ["Ahoy!", "Ninjas suck!"], pirate.previous_changes["catchphrase"]
assert_not_nil pirate.previous_changes["updated_on"][0]
assert_not_nil pirate.previous_changes["updated_on"][1]
- assert !pirate.previous_changes.key?("parrot_id")
- assert !pirate.previous_changes.key?("created_on")
+ assert_not pirate.previous_changes.key?("parrot_id")
+ assert_not pirate.previous_changes.key?("created_on")
ensure
travel_back
end
diff --git a/activerecord/test/cases/dup_test.rb b/activerecord/test/cases/dup_test.rb
index 9e33c3110c..387a0b1fdd 100644
--- a/activerecord/test/cases/dup_test.rb
+++ b/activerecord/test/cases/dup_test.rb
@@ -171,7 +171,7 @@ module ActiveRecord
end
end
- assert !movie.persisted?
+ assert_not movie.persisted?
end
end
end
diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb
index d85910d9c6..04150f4d57 100644
--- a/activerecord/test/cases/finder_test.rb
+++ b/activerecord/test/cases/finder_test.rb
@@ -727,8 +727,8 @@ class FinderTest < ActiveRecord::TestCase
assert_raise(ActiveModel::MissingAttributeError) { topic.title? }
assert_nil topic.read_attribute("title")
assert_equal "David", topic.author_name
- assert !topic.attribute_present?("title")
- assert !topic.attribute_present?(:title)
+ assert_not topic.attribute_present?("title")
+ assert_not topic.attribute_present?(:title)
assert topic.attribute_present?("author_name")
assert_respond_to topic, "author_name"
end
diff --git a/activerecord/test/cases/fixtures_test.rb b/activerecord/test/cases/fixtures_test.rb
index 184b750161..a4fa3c285b 100644
--- a/activerecord/test/cases/fixtures_test.rb
+++ b/activerecord/test/cases/fixtures_test.rb
@@ -592,10 +592,10 @@ class FixturesWithoutInstantiationTest < ActiveRecord::TestCase
fixtures :topics, :developers, :accounts
def test_without_complete_instantiation
- assert !defined?(@first)
- assert !defined?(@topics)
- assert !defined?(@developers)
- assert !defined?(@accounts)
+ assert_not defined?(@first)
+ assert_not defined?(@topics)
+ assert_not defined?(@developers)
+ assert_not defined?(@accounts)
end
def test_fixtures_from_root_yml_without_instantiation
@@ -1082,13 +1082,13 @@ class FoxyFixturesTest < ActiveRecord::TestCase
def test_supports_inline_habtm
assert(parrots(:george).treasures.include?(treasures(:diamond)))
assert(parrots(:george).treasures.include?(treasures(:sapphire)))
- assert(!parrots(:george).treasures.include?(treasures(:ruby)))
+ assert_not(parrots(:george).treasures.include?(treasures(:ruby)))
end
def test_supports_inline_habtm_with_specified_id
assert(parrots(:polly).treasures.include?(treasures(:ruby)))
assert(parrots(:polly).treasures.include?(treasures(:sapphire)))
- assert(!parrots(:polly).treasures.include?(treasures(:diamond)))
+ assert_not(parrots(:polly).treasures.include?(treasures(:diamond)))
end
def test_supports_yaml_arrays
diff --git a/activerecord/test/cases/invertible_migration_test.rb b/activerecord/test/cases/invertible_migration_test.rb
index ebe0b0aa87..9c85543b9b 100644
--- a/activerecord/test/cases/invertible_migration_test.rb
+++ b/activerecord/test/cases/invertible_migration_test.rb
@@ -215,7 +215,7 @@ module ActiveRecord
migration = InvertibleMigration.new
migration.migrate :up
migration.migrate :down
- assert !migration.connection.table_exists?("horses")
+ assert_not migration.connection.table_exists?("horses")
end
def test_migrate_revert
@@ -223,11 +223,11 @@ module ActiveRecord
revert = InvertibleRevertMigration.new
migration.migrate :up
revert.migrate :up
- assert !migration.connection.table_exists?("horses")
+ assert_not migration.connection.table_exists?("horses")
revert.migrate :down
assert migration.connection.table_exists?("horses")
migration.migrate :down
- assert !migration.connection.table_exists?("horses")
+ assert_not migration.connection.table_exists?("horses")
end
def test_migrate_revert_by_part
@@ -241,12 +241,12 @@ module ActiveRecord
}
migration.migrate :up
assert_equal [:both, :up], received
- assert !migration.connection.table_exists?("horses")
+ assert_not migration.connection.table_exists?("horses")
assert migration.connection.table_exists?("new_horses")
migration.migrate :down
assert_equal [:both, :up, :both, :down], received
assert migration.connection.table_exists?("horses")
- assert !migration.connection.table_exists?("new_horses")
+ assert_not migration.connection.table_exists?("new_horses")
end
def test_migrate_revert_whole_migration
@@ -255,11 +255,11 @@ module ActiveRecord
revert = RevertWholeMigration.new(klass)
migration.migrate :up
revert.migrate :up
- assert !migration.connection.table_exists?("horses")
+ assert_not migration.connection.table_exists?("horses")
revert.migrate :down
assert migration.connection.table_exists?("horses")
migration.migrate :down
- assert !migration.connection.table_exists?("horses")
+ assert_not migration.connection.table_exists?("horses")
end
end
@@ -268,7 +268,7 @@ module ActiveRecord
revert.migrate :down
assert revert.connection.table_exists?("horses")
revert.migrate :up
- assert !revert.connection.table_exists?("horses")
+ assert_not revert.connection.table_exists?("horses")
end
def test_migrate_revert_change_column_default
@@ -402,7 +402,7 @@ module ActiveRecord
UpOnlyMigration.new.migrate(:down) # should be no error
connection = ActiveRecord::Base.connection
- assert !connection.column_exists?(:horses, :oldie)
+ assert_not connection.column_exists?(:horses, :oldie)
Horse.reset_column_information
end
end
diff --git a/activerecord/test/cases/migration/change_schema_test.rb b/activerecord/test/cases/migration/change_schema_test.rb
index 1494027182..f4d16cb093 100644
--- a/activerecord/test/cases/migration/change_schema_test.rb
+++ b/activerecord/test/cases/migration/change_schema_test.rb
@@ -205,8 +205,8 @@ module ActiveRecord
created_at_column = created_columns.detect { |c| c.name == "created_at" }
updated_at_column = created_columns.detect { |c| c.name == "updated_at" }
- assert !created_at_column.null
- assert !updated_at_column.null
+ assert_not created_at_column.null
+ assert_not updated_at_column.null
end
def test_create_table_with_timestamps_should_create_datetime_columns_with_options
@@ -408,7 +408,7 @@ module ActiveRecord
end
connection.change_table :testings do |t|
assert t.column_exists?(:foo)
- assert !(t.column_exists?(:bar))
+ assert_not (t.column_exists?(:bar))
end
end
diff --git a/activerecord/test/cases/migration/create_join_table_test.rb b/activerecord/test/cases/migration/create_join_table_test.rb
index 83fb4f9385..a1e5fb1115 100644
--- a/activerecord/test/cases/migration/create_join_table_test.rb
+++ b/activerecord/test/cases/migration/create_join_table_test.rb
@@ -95,42 +95,42 @@ module ActiveRecord
connection.create_join_table :artists, :musics
connection.drop_join_table :artists, :musics
- assert !connection.table_exists?("artists_musics")
+ assert_not connection.table_exists?("artists_musics")
end
def test_drop_join_table_with_strings
connection.create_join_table :artists, :musics
connection.drop_join_table "artists", "musics"
- assert !connection.table_exists?("artists_musics")
+ assert_not connection.table_exists?("artists_musics")
end
def test_drop_join_table_with_the_proper_order
connection.create_join_table :videos, :musics
connection.drop_join_table :videos, :musics
- assert !connection.table_exists?("musics_videos")
+ assert_not connection.table_exists?("musics_videos")
end
def test_drop_join_table_with_the_table_name
connection.create_join_table :artists, :musics, table_name: :catalog
connection.drop_join_table :artists, :musics, table_name: :catalog
- assert !connection.table_exists?("catalog")
+ assert_not connection.table_exists?("catalog")
end
def test_drop_join_table_with_the_table_name_as_string
connection.create_join_table :artists, :musics, table_name: "catalog"
connection.drop_join_table :artists, :musics, table_name: "catalog"
- assert !connection.table_exists?("catalog")
+ assert_not connection.table_exists?("catalog")
end
def test_drop_join_table_with_column_options
connection.create_join_table :artists, :musics, column_options: { null: true }
connection.drop_join_table :artists, :musics, column_options: { null: true }
- assert !connection.table_exists?("artists_musics")
+ assert_not connection.table_exists?("artists_musics")
end
def test_create_and_drop_join_table_with_common_prefix
diff --git a/activerecord/test/cases/migration/index_test.rb b/activerecord/test/cases/migration/index_test.rb
index b25c6d84bc..f9b2dc0c73 100644
--- a/activerecord/test/cases/migration/index_test.rb
+++ b/activerecord/test/cases/migration/index_test.rb
@@ -99,7 +99,7 @@ module ActiveRecord
connection.add_index :testings, :foo
assert connection.index_exists?(:testings, :foo)
- assert !connection.index_exists?(:testings, :bar)
+ assert_not connection.index_exists?(:testings, :bar)
end
def test_index_exists_on_multiple_columns
@@ -131,7 +131,7 @@ module ActiveRecord
assert connection.index_exists?(:testings, :foo)
assert connection.index_exists?(:testings, :foo, name: "custom_index_name")
- assert !connection.index_exists?(:testings, :foo, name: "other_index_name")
+ assert_not connection.index_exists?(:testings, :foo, name: "other_index_name")
end
def test_remove_named_index
@@ -139,7 +139,7 @@ module ActiveRecord
assert connection.index_exists?(:testings, :foo)
connection.remove_index :testings, :foo
- assert !connection.index_exists?(:testings, :foo)
+ assert_not connection.index_exists?(:testings, :foo)
end
def test_add_index_attribute_length_limit
@@ -203,7 +203,7 @@ module ActiveRecord
assert connection.index_exists?("testings", "last_name")
connection.remove_index("testings", "last_name")
- assert !connection.index_exists?("testings", "last_name")
+ assert_not connection.index_exists?("testings", "last_name")
end
end
diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb
index e3fd7d1a7b..1fa9a3c34a 100644
--- a/activerecord/test/cases/migration_test.rb
+++ b/activerecord/test/cases/migration_test.rb
@@ -548,7 +548,7 @@ class MigrationTest < ActiveRecord::TestCase
end
assert Person.connection.column_exists?(:something, :foo)
assert_nothing_raised { Person.connection.remove_column :something, :foo, :bar }
- assert !Person.connection.column_exists?(:something, :foo)
+ assert_not Person.connection.column_exists?(:something, :foo)
assert Person.connection.column_exists?(:something, :name)
assert Person.connection.column_exists?(:something, :number)
ensure
@@ -822,7 +822,7 @@ if ActiveRecord::Base.connection.supports_bulk_alter?
end
end
- [:qualification, :experience].each { |c| assert ! column(c) }
+ [:qualification, :experience].each { |c| assert_not column(c) }
assert column(:qualification_experience)
end
@@ -852,7 +852,7 @@ if ActiveRecord::Base.connection.supports_bulk_alter?
name_age_index = index(:index_delete_me_on_name_and_age)
assert_equal ["name", "age"].sort, name_age_index.columns.sort
- assert ! name_age_index.unique
+ assert_not name_age_index.unique
assert index(:awesome_username_index).unique
end
@@ -880,7 +880,7 @@ if ActiveRecord::Base.connection.supports_bulk_alter?
end
end
- assert ! index(:index_delete_me_on_name)
+ assert_not index(:index_delete_me_on_name)
new_name_index = index(:new_name_index)
assert new_name_index.unique
@@ -892,7 +892,7 @@ if ActiveRecord::Base.connection.supports_bulk_alter?
t.date :birthdate
end
- assert ! column(:name).default
+ assert_not column(:name).default
assert_equal :date, column(:birthdate).type
classname = ActiveRecord::Base.connection.class.name[/[^:]*$/]
diff --git a/activerecord/test/cases/nested_attributes_test.rb b/activerecord/test/cases/nested_attributes_test.rb
index 1ed3a61bbb..32af90caef 100644
--- a/activerecord/test/cases/nested_attributes_test.rb
+++ b/activerecord/test/cases/nested_attributes_test.rb
@@ -83,7 +83,7 @@ class TestNestedAttributesInGeneral < ActiveRecord::TestCase
def test_a_model_should_respond_to_underscore_destroy_and_return_if_it_is_marked_for_destruction
ship = Ship.create!(name: "Nights Dirty Lightning")
- assert !ship._destroy
+ assert_not ship._destroy
ship.mark_for_destruction
assert ship._destroy
end
@@ -835,7 +835,7 @@ module NestedAttributesOnACollectionAssociationTests
man = Man.create(name: "John")
interest = man.interests.create(topic: "bar", zine_id: 0)
assert interest.save
- assert !man.update(interests_attributes: { id: interest.id, zine_id: "foo" })
+ assert_not man.update(interests_attributes: { id: interest.id, zine_id: "foo" })
end
end
diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb
index e4a65a48ca..e0c5725944 100644
--- a/activerecord/test/cases/persistence_test.rb
+++ b/activerecord/test/cases/persistence_test.rb
@@ -48,7 +48,7 @@ class PersistenceTest < ActiveRecord::TestCase
end
if test_update_with_order_succeeds.call("id DESC")
- assert !test_update_with_order_succeeds.call("id ASC") # test that this wasn't a fluke and using an incorrect order results in an exception
+ assert_not test_update_with_order_succeeds.call("id ASC") # test that this wasn't a fluke and using an incorrect order results in an exception
else
# test that we're failing because the current Arel's engine doesn't support UPDATE ORDER BY queries is using subselects instead
assert_sql(/\AUPDATE .+ \(SELECT .* ORDER BY id DESC\)\z/i) do
diff --git a/activerecord/test/cases/query_cache_test.rb b/activerecord/test/cases/query_cache_test.rb
index 07be046fb7..393f363e37 100644
--- a/activerecord/test/cases/query_cache_test.rb
+++ b/activerecord/test/cases/query_cache_test.rb
@@ -518,19 +518,19 @@ class QueryCacheExpiryTest < ActiveRecord::TestCase
def test_find
assert_called(Task.connection, :clear_query_cache) do
- assert !Task.connection.query_cache_enabled
+ assert_not 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
+ assert_not Task.connection.query_cache_enabled
Task.find(1)
end
assert Task.connection.query_cache_enabled
end
- assert !Task.connection.query_cache_enabled
+ assert_not Task.connection.query_cache_enabled
end
end
diff --git a/activerecord/test/cases/readonly_test.rb b/activerecord/test/cases/readonly_test.rb
index 383e43ed55..059fa76132 100644
--- a/activerecord/test/cases/readonly_test.rb
+++ b/activerecord/test/cases/readonly_test.rb
@@ -23,7 +23,7 @@ class ReadOnlyTest < ActiveRecord::TestCase
assert_nothing_raised do
dev.name = "Luscious forbidden fruit."
- assert !dev.save
+ assert_not dev.save
dev.name = "Forbidden."
end
@@ -38,8 +38,8 @@ class ReadOnlyTest < ActiveRecord::TestCase
end
def test_find_with_readonly_option
- Developer.all.each { |d| assert !d.readonly? }
- Developer.readonly(false).each { |d| assert !d.readonly? }
+ Developer.all.each { |d| assert_not d.readonly? }
+ Developer.readonly(false).each { |d| assert_not d.readonly? }
Developer.readonly(true).each { |d| assert d.readonly? }
Developer.readonly.each { |d| assert d.readonly? }
end
@@ -55,14 +55,14 @@ class ReadOnlyTest < ActiveRecord::TestCase
def test_has_many_find_readonly
post = Post.find(1)
assert_not_empty post.comments
- assert !post.comments.any?(&:readonly?)
- assert !post.comments.to_a.any?(&:readonly?)
+ assert_not post.comments.any?(&:readonly?)
+ assert_not post.comments.to_a.any?(&:readonly?)
assert post.comments.readonly(true).all?(&:readonly?)
end
def test_has_many_with_through_is_not_implicitly_marked_readonly
assert people = Post.find(1).people
- assert !people.any?(&:readonly?)
+ assert_not people.any?(&:readonly?)
end
def test_has_many_with_through_is_not_implicitly_marked_readonly_while_finding_by_id
diff --git a/activerecord/test/cases/reaper_test.rb b/activerecord/test/cases/reaper_test.rb
index 61cb0f130d..b034fe3e3b 100644
--- a/activerecord/test/cases/reaper_test.rb
+++ b/activerecord/test/cases/reaper_test.rb
@@ -36,15 +36,15 @@ module ActiveRecord
# A reaper with nil time should never reap connections
def test_nil_time
fp = FakePool.new
- assert !fp.reaped
+ assert_not fp.reaped
reaper = ConnectionPool::Reaper.new(fp, nil)
reaper.run
- assert !fp.reaped
+ assert_not fp.reaped
end
def test_some_time
fp = FakePool.new
- assert !fp.reaped
+ assert_not fp.reaped
reaper = ConnectionPool::Reaper.new(fp, 0.0001)
reaper.run
diff --git a/activerecord/test/cases/reflection_test.rb b/activerecord/test/cases/reflection_test.rb
index ed19192ad9..abadafbad4 100644
--- a/activerecord/test/cases/reflection_test.rb
+++ b/activerecord/test/cases/reflection_test.rb
@@ -75,7 +75,7 @@ class ReflectionTest < ActiveRecord::TestCase
def test_column_null_not_null
subscriber = Subscriber.first
assert subscriber.column_for_attribute("name").null
- assert !subscriber.column_for_attribute("nick").null
+ assert_not subscriber.column_for_attribute("nick").null
end
def test_human_name_for_column
diff --git a/activerecord/test/cases/relation/merging_test.rb b/activerecord/test/cases/relation/merging_test.rb
index 074ce9454f..f53ef1fe35 100644
--- a/activerecord/test/cases/relation/merging_test.rb
+++ b/activerecord/test/cases/relation/merging_test.rb
@@ -78,6 +78,10 @@ class RelationMergingTest < ActiveRecord::TestCase
assert_equal 1, comments.count
end
+ def test_relation_merging_with_skip_query_cache
+ assert_equal Post.all.merge(Post.all.skip_query_cache!).skip_query_cache_value, true
+ end
+
def test_relation_merging_with_association
assert_queries(2) do # one for loading post, and another one merged query
post = Post.where(body: "Such a lovely day").first
diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb
index cf65789b97..952d2dd5d9 100644
--- a/activerecord/test/cases/relations_test.rb
+++ b/activerecord/test/cases/relations_test.rb
@@ -9,6 +9,7 @@ require "models/comment"
require "models/author"
require "models/entrant"
require "models/developer"
+require "models/person"
require "models/computer"
require "models/reply"
require "models/company"
@@ -25,7 +26,7 @@ require "models/edge"
require "models/subscriber"
class RelationTest < ActiveRecord::TestCase
- fixtures :authors, :author_addresses, :topics, :entrants, :developers, :companies, :developers_projects, :accounts, :categories, :categorizations, :categories_posts, :posts, :comments, :tags, :taggings, :cars, :minivans
+ fixtures :authors, :author_addresses, :topics, :entrants, :developers, :people, :companies, :developers_projects, :accounts, :categories, :categorizations, :categories_posts, :posts, :comments, :tags, :taggings, :cars, :minivans
class TopicWithCallbacks < ActiveRecord::Base
self.table_name = :topics
@@ -511,7 +512,7 @@ class RelationTest < ActiveRecord::TestCase
end
def test_find_with_readonly_option
- Developer.all.each { |d| assert !d.readonly? }
+ Developer.all.each { |d| assert_not d.readonly? }
Developer.all.readonly.each { |d| assert d.readonly? }
end
@@ -1097,7 +1098,7 @@ class RelationTest < ActiveRecord::TestCase
assert_not_predicate posts.where(id: nil), :any?
assert posts.any? { |p| p.id > 0 }
- assert ! posts.any? { |p| p.id <= 0 }
+ assert_not posts.any? { |p| p.id <= 0 }
end
assert_predicate posts, :loaded?
@@ -1109,7 +1110,7 @@ class RelationTest < ActiveRecord::TestCase
assert_queries(2) do
assert posts.many? # Uses COUNT()
assert posts.many? { |p| p.id > 0 }
- assert ! posts.many? { |p| p.id < 2 }
+ assert_not posts.many? { |p| p.id < 2 }
end
assert_predicate posts, :loaded?
@@ -1125,14 +1126,14 @@ class RelationTest < ActiveRecord::TestCase
def test_none?
posts = Post.all
assert_queries(1) do
- assert ! posts.none? # Uses COUNT()
+ assert_not posts.none? # Uses COUNT()
end
assert_not_predicate posts, :loaded?
assert_queries(1) do
assert posts.none? { |p| p.id < 0 }
- assert ! posts.none? { |p| p.id == 1 }
+ assert_not posts.none? { |p| p.id == 1 }
end
assert_predicate posts, :loaded?
@@ -1141,13 +1142,13 @@ class RelationTest < ActiveRecord::TestCase
def test_one
posts = Post.all
assert_queries(1) do
- assert ! posts.one? # Uses COUNT()
+ assert_not posts.one? # Uses COUNT()
end
assert_not_predicate posts, :loaded?
assert_queries(1) do
- assert ! posts.one? { |p| p.id < 3 }
+ assert_not posts.one? { |p| p.id < 3 }
assert posts.one? { |p| p.id == 1 }
end
@@ -1525,6 +1526,50 @@ class RelationTest < ActiveRecord::TestCase
assert_equal posts(:welcome), comments(:greetings).post
end
+ def test_touch_all_updates_records_timestamps
+ david = developers(:david)
+ david_previously_updated_at = david.updated_at
+ jamis = developers(:jamis)
+ jamis_previously_updated_at = jamis.updated_at
+ Developer.where(name: "David").touch_all
+
+ assert_not_equal david_previously_updated_at, david.reload.updated_at
+ assert_equal jamis_previously_updated_at, jamis.reload.updated_at
+ end
+
+ def test_touch_all_with_custom_timestamp
+ developer = developers(:david)
+ previously_created_at = developer.created_at
+ previously_updated_at = developer.updated_at
+ Developer.where(name: "David").touch_all(:created_at)
+ developer = developer.reload
+
+ assert_not_equal previously_created_at, developer.created_at
+ assert_not_equal previously_updated_at, developer.updated_at
+ end
+
+ def test_touch_all_with_given_time
+ developer = developers(:david)
+ previously_created_at = developer.created_at
+ previously_updated_at = developer.updated_at
+ new_time = Time.utc(2015, 2, 16, 4, 54, 0)
+ Developer.where(name: "David").touch_all(:created_at, time: new_time)
+ developer = developer.reload
+
+ assert_not_equal previously_created_at, developer.created_at
+ assert_not_equal previously_updated_at, developer.updated_at
+ assert_equal new_time, developer.created_at
+ assert_equal new_time, developer.updated_at
+ end
+
+ def test_touch_all_updates_locking_column
+ person = people(:david)
+
+ assert_difference -> { person.reload.lock_version }, +1 do
+ Person.where(first_name: "David").touch_all
+ end
+ end
+
def test_update_on_relation
topic1 = TopicWithCallbacks.create! title: "arel", author_name: nil
topic2 = TopicWithCallbacks.create! title: "activerecord", author_name: nil
@@ -1696,7 +1741,7 @@ class RelationTest < ActiveRecord::TestCase
# checking if there are topics is used before you actually display them,
# thus it shouldn't invoke an extra count query.
assert_no_queries { assert topics.present? }
- assert_no_queries { assert !topics.blank? }
+ assert_no_queries { assert_not topics.blank? }
# shows count of topics and loops after loading the query should not trigger extra queries either.
assert_no_queries { topics.size }
diff --git a/activerecord/test/cases/scoping/default_scoping_test.rb b/activerecord/test/cases/scoping/default_scoping_test.rb
index 0804de1fb3..e3a34aa50d 100644
--- a/activerecord/test/cases/scoping/default_scoping_test.rb
+++ b/activerecord/test/cases/scoping/default_scoping_test.rb
@@ -193,7 +193,7 @@ class DefaultScopingTest < ActiveRecord::TestCase
def test_order_to_unscope_reordering
scope = DeveloperOrderedBySalary.order("salary DESC, name ASC").reverse_order.unscope(:order)
- assert !/order/i.match?(scope.to_sql)
+ assert_no_match(/order/i, scope.to_sql)
end
def test_unscope_reverse_order
diff --git a/activerecord/test/cases/scoping/named_scoping_test.rb b/activerecord/test/cases/scoping/named_scoping_test.rb
index 03f8a4f7c9..6cb252edaf 100644
--- a/activerecord/test/cases/scoping/named_scoping_test.rb
+++ b/activerecord/test/cases/scoping/named_scoping_test.rb
@@ -86,7 +86,7 @@ class NamedScopingTest < ActiveRecord::TestCase
def test_scopes_are_composable
assert_equal((approved = Topic.all.merge!(where: { approved: true }).to_a), Topic.approved)
assert_equal((replied = Topic.all.merge!(where: "replies_count > 0").to_a), Topic.replied)
- assert !(approved == replied)
+ assert_not (approved == replied)
assert_not_empty (approved & replied)
assert_equal approved & replied, Topic.approved.replied
diff --git a/activerecord/test/cases/scoping/relation_scoping_test.rb b/activerecord/test/cases/scoping/relation_scoping_test.rb
index 5c86bc892d..f18f1ed981 100644
--- a/activerecord/test/cases/scoping/relation_scoping_test.rb
+++ b/activerecord/test/cases/scoping/relation_scoping_test.rb
@@ -105,7 +105,7 @@ class RelationScopingTest < ActiveRecord::TestCase
Developer.select("id, name").scoping do
developer = Developer.where("name = 'David'").first
assert_equal "David", developer.name
- assert !developer.has_attribute?(:salary)
+ assert_not developer.has_attribute?(:salary)
end
end
diff --git a/activerecord/test/cases/serialization_test.rb b/activerecord/test/cases/serialization_test.rb
index 2d829ad4ba..932780bfef 100644
--- a/activerecord/test/cases/serialization_test.rb
+++ b/activerecord/test/cases/serialization_test.rb
@@ -67,8 +67,8 @@ class SerializationTest < ActiveRecord::TestCase
klazz.include_root_in_json = false
assert ActiveRecord::Base.include_root_in_json
- assert !klazz.include_root_in_json
- assert !klazz.new.include_root_in_json
+ assert_not klazz.include_root_in_json
+ assert_not klazz.new.include_root_in_json
ensure
ActiveRecord::Base.include_root_in_json = original_root_in_json
end
diff --git a/activerecord/test/cases/transactions_test.rb b/activerecord/test/cases/transactions_test.rb
index 1c144a781d..3fd38b4b60 100644
--- a/activerecord/test/cases/transactions_test.rb
+++ b/activerecord/test/cases/transactions_test.rb
@@ -159,7 +159,7 @@ class TransactionTest < ActiveRecord::TestCase
def @first.before_save_for_transaction
raise ActiveRecord::Rollback
end
- assert !@first.approved
+ assert_not @first.approved
Topic.transaction do
@first.approved = true
@@ -194,7 +194,7 @@ class TransactionTest < ActiveRecord::TestCase
posts_count = author.posts.size
assert posts_count > 0
status = author.update(name: nil, post_ids: [])
- assert !status
+ assert_not status
assert_equal posts_count, author.posts.reload.size
end
@@ -212,7 +212,7 @@ class TransactionTest < ActiveRecord::TestCase
add_cancelling_before_destroy_with_db_side_effect_to_topic @first
nbooks_before_destroy = Book.count
status = @first.destroy
- assert !status
+ assert_not status
@first.reload
assert_equal nbooks_before_destroy, Book.count
end
@@ -224,7 +224,7 @@ class TransactionTest < ActiveRecord::TestCase
original_author_name = @first.author_name
@first.author_name += "_this_should_not_end_up_in_the_db"
status = @first.save
- assert !status
+ assert_not status
assert_equal original_author_name, @first.reload.author_name
assert_equal nbooks_before_save, Book.count
end
diff --git a/activerecord/test/cases/validations/length_validation_test.rb b/activerecord/test/cases/validations/length_validation_test.rb
index 62cd89041a..1fbcdc271b 100644
--- a/activerecord/test/cases/validations/length_validation_test.rb
+++ b/activerecord/test/cases/validations/length_validation_test.rb
@@ -17,7 +17,7 @@ class LengthValidationTest < ActiveRecord::TestCase
def test_validates_size_of_association
assert_nothing_raised { @owner.validates_size_of :pets, minimum: 1 }
o = @owner.new("name" => "nopets")
- assert !o.save
+ assert_not o.save
assert_predicate o.errors[:pets], :any?
o.pets.build("name" => "apet")
assert_predicate o, :valid?
@@ -26,21 +26,21 @@ class LengthValidationTest < ActiveRecord::TestCase
def test_validates_size_of_association_using_within
assert_nothing_raised { @owner.validates_size_of :pets, within: 1..2 }
o = @owner.new("name" => "nopets")
- assert !o.save
+ assert_not o.save
assert_predicate o.errors[:pets], :any?
o.pets.build("name" => "apet")
assert_predicate o, :valid?
2.times { o.pets.build("name" => "apet") }
- assert !o.save
+ assert_not o.save
assert_predicate o.errors[:pets], :any?
end
def test_validates_size_of_association_utf8
@owner.validates_size_of :pets, minimum: 1
o = @owner.new("name" => "あいうえおかきくけこ")
- assert !o.save
+ assert_not o.save
assert_predicate o.errors[:pets], :any?
o.pets.build("name" => "あいうえおかきくけこ")
assert_predicate o, :valid?
diff --git a/activerecord/test/cases/validations_test.rb b/activerecord/test/cases/validations_test.rb
index 6c83bbd15c..a33877f43a 100644
--- a/activerecord/test/cases/validations_test.rb
+++ b/activerecord/test/cases/validations_test.rb
@@ -39,7 +39,7 @@ class ValidationsTest < ActiveRecord::TestCase
def test_valid_using_special_context
r = WrongReply.new(title: "Valid title")
- assert !r.valid?(:special_case)
+ assert_not r.valid?(:special_case)
assert_equal "Invalid", r.errors[:author_name].join
r.author_name = "secret"
@@ -125,7 +125,7 @@ class ValidationsTest < ActiveRecord::TestCase
def test_save_without_validation
reply = WrongReply.new
- assert !reply.save
+ assert_not reply.save
assert reply.save(validate: false)
end
diff --git a/activerecord/test/fixtures/sponsors.yml b/activerecord/test/fixtures/sponsors.yml
index 2da541c539..02ddb8dd38 100644
--- a/activerecord/test/fixtures/sponsors.yml
+++ b/activerecord/test/fixtures/sponsors.yml
@@ -10,3 +10,6 @@ crazy_club_sponsor_for_groucho:
sponsor_club: crazy_club
sponsorable_id: 3
sponsorable_type: Member
+sponsor_for_author_david:
+ sponsorable_id: 1
+ sponsorable_type: Author
diff --git a/activerecord/test/models/member_detail.rb b/activerecord/test/models/member_detail.rb
index 87f7aab9a2..e121a849d0 100644
--- a/activerecord/test/models/member_detail.rb
+++ b/activerecord/test/models/member_detail.rb
@@ -5,6 +5,7 @@ class MemberDetail < ActiveRecord::Base
belongs_to :organization
has_one :member_type, through: :member
has_one :membership, through: :member
+ has_one :admittable, through: :member, source_type: "Member"
has_many :organization_member_details, through: :organization, source: :member_details
end
diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb
index 350113eaab..92ad25ef76 100644
--- a/activerecord/test/schema/schema.rb
+++ b/activerecord/test/schema/schema.rb
@@ -486,7 +486,8 @@ ActiveRecord::Schema.define do
create_table :members, force: true do |t|
t.string :name
- t.integer :member_type_id
+ t.references :member_type, index: false
+ t.references :admittable, polymorphic: true, index: false
end
create_table :member_details, force: true do |t|
diff --git a/activestorage/CHANGELOG.md b/activestorage/CHANGELOG.md
index d794afb0e6..60d7d19540 100644
--- a/activestorage/CHANGELOG.md
+++ b/activestorage/CHANGELOG.md
@@ -1,3 +1,22 @@
+* Use the [ImageProcessing](https://github.com/janko-m/image_processing) gem
+ for Active Storage variants, and deprecate the MiniMagick backend.
+
+ This means that variants are now automatically oriented if the original
+ image was rotated. Also, in addition to the existing ImageMagick
+ operations, variants can now use `:resize_to_fit`, `:resize_to_fill`, and
+ other ImageProcessing macros. These are now recommended over raw `:resize`,
+ as they also sharpen the thumbnail after resizing.
+
+ The ImageProcessing gem also comes with a backend implemented on
+ [libvips](http://jcupitt.github.io/libvips/), an alternative to
+ ImageMagick which has significantly better performance than
+ ImageMagick in most cases, both in terms of speed and memory usage. In
+ Active Storage it's now possible to switch to the libvips backend by
+ changing `Rails.application.config.active_storage.variant_processor` to
+ `:vips`.
+
+ *Janko Marohnić*
+
* Rails 6 requires Ruby 2.4.1 or newer.
*Jeremy Daer*
diff --git a/activestorage/README.md b/activestorage/README.md
index 85ab70dac6..b677721d95 100644
--- a/activestorage/README.md
+++ b/activestorage/README.md
@@ -4,7 +4,7 @@ Active Storage makes it simple to upload and reference files in cloud services l
Files can be uploaded from the server to the cloud or directly from the client to the cloud.
-Image files can furthermore be transformed using on-demand variants for quality, aspect ratio, size, or any other [MiniMagick](https://github.com/minimagick/minimagick) supported transformation.
+Image files can furthermore be transformed using on-demand variants for quality, aspect ratio, size, or any other [MiniMagick](https://github.com/minimagick/minimagick) or [Vips](http://www.rubydoc.info/gems/ruby-vips/Vips/Image) supported transformation.
## Compared to other storage solutions
@@ -99,7 +99,7 @@ Variation of image attachment:
```erb
<%# Hitting the variant URL will lazy transform the original blob and then redirect to its new service location %>
-<%= image_tag user.avatar.variant(resize: "100x100") %>
+<%= image_tag user.avatar.variant(resize_to_fit: [100, 100]) %>
```
## Direct uploads
diff --git a/activestorage/app/models/active_storage/blob/representable.rb b/activestorage/app/models/active_storage/blob/representable.rb
index fea62e62de..03d5511481 100644
--- a/activestorage/app/models/active_storage/blob/representable.rb
+++ b/activestorage/app/models/active_storage/blob/representable.rb
@@ -10,7 +10,7 @@ module ActiveStorage::Blob::Representable
# Returns an ActiveStorage::Variant instance with the set of +transformations+ provided. This is only relevant for image
# files, and it allows any image to be transformed for size, colors, and the like. Example:
#
- # avatar.variant(resize: "100x100").processed.service_url
+ # avatar.variant(resize_to_fit: [100, 100]).processed.service_url
#
# This will create and process a variant of the avatar blob that's constrained to a height and width of 100px.
# Then it'll upload said variant to the service according to a derivative key of the blob and the transformations.
@@ -18,7 +18,7 @@ module ActiveStorage::Blob::Representable
# Frequently, though, you don't actually want to transform the variant right away. But rather simply refer to a
# specific variant that can be created by a controller on-demand. Like so:
#
- # <%= image_tag Current.user.avatar.variant(resize: "100x100") %>
+ # <%= image_tag Current.user.avatar.variant(resize_to_fit: [100, 100]) %>
#
# This will create a URL for that specific blob with that specific variant, which the ActiveStorage::RepresentationsController
# can then produce on-demand.
@@ -43,13 +43,13 @@ module ActiveStorage::Blob::Representable
# from a non-image blob. Active Storage comes with built-in previewers for videos and PDF documents. The video previewer
# extracts the first frame from a video and the PDF previewer extracts the first page from a PDF document.
#
- # blob.preview(resize: "100x100").processed.service_url
+ # blob.preview(resize_to_fit: [100, 100]).processed.service_url
#
# Avoid processing previews synchronously in views. Instead, link to a controller action that processes them on demand.
# Active Storage provides one, but you may want to create your own (for example, if you need authentication). Here’s
# how to use the built-in version:
#
- # <%= image_tag video.preview(resize: "100x100") %>
+ # <%= image_tag video.preview(resize_to_fit: [100, 100]) %>
#
# This method raises ActiveStorage::UnpreviewableError if no previewer accepts the receiving blob. To determine
# whether a blob is accepted by any previewer, call ActiveStorage::Blob#previewable?.
@@ -69,7 +69,7 @@ module ActiveStorage::Blob::Representable
# Returns an ActiveStorage::Preview for a previewable blob or an ActiveStorage::Variant for a variable image blob.
#
- # blob.representation(resize: "100x100").processed.service_url
+ # blob.representation(resize_to_fit: [100, 100]).processed.service_url
#
# Raises ActiveStorage::UnrepresentableError if the receiving blob is neither variable nor previewable. Call
# ActiveStorage::Blob#representable? to determine whether a blob is representable.
diff --git a/activestorage/app/models/active_storage/preview.rb b/activestorage/app/models/active_storage/preview.rb
index 2b87897183..de58763399 100644
--- a/activestorage/app/models/active_storage/preview.rb
+++ b/activestorage/app/models/active_storage/preview.rb
@@ -38,7 +38,7 @@ class ActiveStorage::Preview
# Processes the preview if it has not been processed yet. Returns the receiving Preview instance for convenience:
#
- # blob.preview(resize: "100x100").processed.service_url
+ # blob.preview(resize_to_fit: [100, 100]).processed.service_url
#
# Processing a preview generates an image from its blob and attaches the preview image to the blob. Because the preview
# image is stored with the blob, it is only generated once.
diff --git a/activestorage/app/models/active_storage/variant.rb b/activestorage/app/models/active_storage/variant.rb
index d84208419c..b782489a92 100644
--- a/activestorage/app/models/active_storage/variant.rb
+++ b/activestorage/app/models/active_storage/variant.rb
@@ -6,19 +6,28 @@ require "active_storage/downloading"
# These variants are used to create thumbnails, fixed-size avatars, or any other derivative image from the
# original.
#
-# Variants rely on {MiniMagick}[https://github.com/minimagick/minimagick] for the actual transformations
-# of the file, so you must add <tt>gem "mini_magick"</tt> to your Gemfile if you wish to use variants.
+# Variants rely on {ImageProcessing}[https://github.com/janko-m/image_processing] gem for the actual transformations
+# of the file, so you must add <tt>gem "image_processing"</tt> to your Gemfile if you wish to use variants. By
+# default, images will be processed with {ImageMagick}[http://imagemagick.org] using the
+# {MiniMagick}[https://github.com/minimagick/minimagick] gem, but you can also switch to the
+# {libvips}[http://jcupitt.github.io/libvips/] processor operated by the {ruby-vips}[https://github.com/jcupitt/ruby-vips]
+# gem).
#
-# Note that to create a variant it's necessary to download the entire blob file from the service and load it
-# into memory. The larger the image, the more memory is used. Because of this process, you also want to be
-# considerate about when the variant is actually processed. You shouldn't be processing variants inline in a
-# template, for example. Delay the processing to an on-demand controller, like the one provided in
+# Rails.application.config.active_storage.variant_processor
+# # => :mini_magick
+#
+# Rails.application.config.active_storage.variant_processor = :vips
+# # => :vips
+#
+# Note that to create a variant it's necessary to download the entire blob file from the service. Because of this process,
+# you also want to be considerate about when the variant is actually processed. You shouldn't be processing variants inline
+# in a template, for example. Delay the processing to an on-demand controller, like the one provided in
# ActiveStorage::RepresentationsController.
#
# To refer to such a delayed on-demand variant, simply link to the variant through the resolved route provided
# by Active Storage like so:
#
-# <%= image_tag Current.user.avatar.variant(resize: "100x100") %>
+# <%= image_tag Current.user.avatar.variant(resize_to_fit: [100, 100]) %>
#
# This will create a URL for that specific blob with that specific variant, which the ActiveStorage::RepresentationsController
# can then produce on-demand.
@@ -27,15 +36,22 @@ require "active_storage/downloading"
# has already been processed and uploaded to the service, and, if so, just return that. Otherwise it will perform
# the transformations, upload the variant to the service, and return itself again. Example:
#
-# avatar.variant(resize: "100x100").processed.service_url
+# avatar.variant(resize_to_fit: [100, 100]).processed.service_url
#
# This will create and process a variant of the avatar blob that's constrained to a height and width of 100.
# Then it'll upload said variant to the service according to a derivative key of the blob and the transformations.
#
-# A list of all possible transformations is available at https://www.imagemagick.org/script/mogrify.php. You can
-# combine as many as you like freely:
+# You can combine any number of ImageMagick/libvips operations into a variant, as well as any macros provided by the
+# ImageProcessing gem (such as +resize_to_fit+):
+#
+# avatar.variant(resize_to_fit: [800, 800], monochrome: true, flip: "-90")
+#
+# Visit the following links for a list of available ImageProcessing commands and ImageMagick/libvips operations:
#
-# avatar.variant(resize: "100x100", monochrome: true, flip: "-90")
+# * {ImageProcessing::MiniMagick}[https://github.com/janko-m/image_processing/blob/master/doc/minimagick.md#methods]
+# * {ImageMagick reference}[https://www.imagemagick.org/script/mogrify.php]
+# * {ImageProcessing::Vips}[https://github.com/janko-m/image_processing/blob/master/doc/vips.md#methods]
+# * {ruby-vips reference}[http://www.rubydoc.info/gems/ruby-vips/Vips/Image]
class ActiveStorage::Variant
include ActiveStorage::Downloading
@@ -82,10 +98,10 @@ class ActiveStorage::Variant
end
def process
- open_image do |image|
- transform image
- format image
- upload image
+ download_blob_to_tempfile do |image|
+ transform image do |output|
+ upload output
+ end
end
end
@@ -102,31 +118,18 @@ class ActiveStorage::Variant
blob.content_type.presence_in(WEB_IMAGE_CONTENT_TYPES) || "image/png"
end
-
- def open_image(&block)
- image = download_image
+ def transform(image)
+ format = "png" unless WEB_IMAGE_CONTENT_TYPES.include?(blob.content_type)
+ result = variation.transform(image, format: format)
begin
- yield image
+ yield result
ensure
- image.destroy!
+ result.close!
end
end
- def download_image
- require "mini_magick"
- MiniMagick::Image.create(blob.filename.extension_with_delimiter) { |file| download_blob_to(file) }
- end
-
- def transform(image)
- variation.transform(image)
- end
-
- def format(image)
- image.format("PNG") unless WEB_IMAGE_CONTENT_TYPES.include?(blob.content_type)
- end
-
- def upload(image)
- File.open(image.path, "r") { |file| service.upload(key, file) }
+ def upload(file)
+ service.upload(key, file)
end
end
diff --git a/activestorage/app/models/active_storage/variation.rb b/activestorage/app/models/active_storage/variation.rb
index 12e7f9f0b5..42f00beb82 100644
--- a/activestorage/app/models/active_storage/variation.rb
+++ b/activestorage/app/models/active_storage/variation.rb
@@ -6,17 +6,9 @@
# In case you do need to use this directly, it's instantiated using a hash of transformations where
# the key is the command and the value is the arguments. Example:
#
-# ActiveStorage::Variation.new(resize: "100x100", monochrome: true, trim: true, rotate: "-90")
+# ActiveStorage::Variation.new(resize_to_fit: [100, 100], monochrome: true, trim: true, rotate: "-90")
#
-# You can also combine multiple transformations in one step, e.g. for center-weighted cropping:
-#
-# ActiveStorage::Variation.new(combine_options: {
-# resize: "100x100^",
-# gravity: "center",
-# crop: "100x100+0+0",
-# })
-#
-# A list of all possible transformations is available at https://www.imagemagick.org/script/mogrify.php.
+# The options map directly to {ImageProcessing}[https://github.com/janko-m/image_processing] commands.
class ActiveStorage::Variation
attr_reader :transformations
@@ -51,10 +43,49 @@ class ActiveStorage::Variation
@transformations = transformations
end
- # Accepts an open MiniMagick image instance, like what's returned by <tt>MiniMagick::Image.read(io)</tt>,
- # and performs the +transformations+ against it. The transformed image instance is then returned.
- def transform(image)
+ # Accepts a File object, performs the +transformations+ against it, and
+ # saves the transformed image into a temporary file. If +format+ is specified
+ # it will be the format of the result image, otherwise the result image
+ # retains the source format.
+ def transform(file, format: nil)
ActiveSupport::Notifications.instrument("transform.active_storage") do
+ if processor
+ image_processing_transform(file, format)
+ else
+ mini_magick_transform(file, format)
+ end
+ end
+ end
+
+ # Returns a signed key for all the +transformations+ that this variation was instantiated with.
+ def key
+ self.class.encode(transformations)
+ end
+
+ private
+ # Applies image transformations using the ImageProcessing gem.
+ def image_processing_transform(file, format)
+ operations = transformations.inject([]) do |list, (name, argument)|
+ if name.to_s == "combine_options"
+ ActiveSupport::Deprecation.warn("The ImageProcessing ActiveStorage variant backend doesn't need :combine_options, as it already generates a single MiniMagick command. In Rails 6.1 :combine_options will not be supported anymore.")
+ list.concat argument.to_a
+ else
+ list << [name, argument]
+ end
+ end
+
+ processor
+ .source(file)
+ .loader(page: 0)
+ .convert(format)
+ .apply(operations)
+ .call
+ end
+
+ # Applies image transformations using the MiniMagick gem.
+ def mini_magick_transform(file, format)
+ image = MiniMagick::Image.new(file.path, file)
+
transformations.each do |name, argument_or_subtransformations|
image.mogrify do |command|
if name.to_s == "combine_options"
@@ -66,15 +97,24 @@ class ActiveStorage::Variation
end
end
end
+
+ image.format(format) if format
+
+ image.tempfile.tap(&:open)
end
- end
- # Returns a signed key for all the +transformations+ that this variation was instantiated with.
- def key
- self.class.encode(transformations)
- end
+ # Returns the ImageProcessing processor class specified by `ActiveStorage.variant_processor`.
+ def processor
+ begin
+ require "image_processing"
+ rescue LoadError
+ ActiveSupport::Deprecation.warn("Using mini_magick gem directly is deprecated and will be removed in Rails 6.1. Please add `gem 'image_processing', '~> 1.2'` to your Gemfile.")
+ return nil
+ end
+
+ ImageProcessing.const_get(ActiveStorage.variant_processor.to_s.camelize) if ActiveStorage.variant_processor
+ end
- private
def pass_transform_argument(command, method, argument)
if eligible_argument?(argument)
command.public_send(method, argument)
diff --git a/activestorage/lib/active_storage.rb b/activestorage/lib/active_storage.rb
index e1bd974853..e1deee1d82 100644
--- a/activestorage/lib/active_storage.rb
+++ b/activestorage/lib/active_storage.rb
@@ -45,6 +45,7 @@ module ActiveStorage
mattr_accessor :queue
mattr_accessor :previewers, default: []
mattr_accessor :analyzers, default: []
+ mattr_accessor :variant_processor, default: :mini_magick
mattr_accessor :paths, default: {}
mattr_accessor :variable_content_types, default: []
mattr_accessor :content_types_to_serve_as_binary, default: []
diff --git a/activestorage/lib/active_storage/downloading.rb b/activestorage/lib/active_storage/downloading.rb
index f2a1fffdcb..7c3d20ade0 100644
--- a/activestorage/lib/active_storage/downloading.rb
+++ b/activestorage/lib/active_storage/downloading.rb
@@ -27,6 +27,7 @@ module ActiveStorage
def download_blob_to(file) #:doc:
file.binmode
blob.download { |chunk| file.write(chunk) }
+ file.flush
file.rewind
end
diff --git a/activestorage/lib/active_storage/engine.rb b/activestorage/lib/active_storage/engine.rb
index 1385e2aa84..99588cdd4b 100644
--- a/activestorage/lib/active_storage/engine.rb
+++ b/activestorage/lib/active_storage/engine.rb
@@ -43,11 +43,12 @@ module ActiveStorage
initializer "active_storage.configs" do
config.after_initialize do |app|
- ActiveStorage.logger = app.config.active_storage.logger || Rails.logger
- ActiveStorage.queue = app.config.active_storage.queue
- ActiveStorage.previewers = app.config.active_storage.previewers || []
- ActiveStorage.analyzers = app.config.active_storage.analyzers || []
- ActiveStorage.paths = app.config.active_storage.paths || {}
+ ActiveStorage.logger = app.config.active_storage.logger || Rails.logger
+ ActiveStorage.queue = app.config.active_storage.queue
+ ActiveStorage.variant_processor = app.config.active_storage.variant_processor || :mini_magick
+ ActiveStorage.previewers = app.config.active_storage.previewers || []
+ ActiveStorage.analyzers = app.config.active_storage.analyzers || []
+ ActiveStorage.paths = app.config.active_storage.paths || {}
ActiveStorage.variable_content_types = app.config.active_storage.variable_content_types || []
ActiveStorage.content_types_to_serve_as_binary = app.config.active_storage.content_types_to_serve_as_binary || []
diff --git a/activestorage/lib/active_storage/service/s3_service.rb b/activestorage/lib/active_storage/service/s3_service.rb
index 5e489f4be1..0286e7ff21 100644
--- a/activestorage/lib/active_storage/service/s3_service.rb
+++ b/activestorage/lib/active_storage/service/s3_service.rb
@@ -33,7 +33,7 @@ module ActiveStorage
end
else
instrument :download, key: key do
- object_for(key).get.body.read.force_encoding(Encoding::BINARY)
+ object_for(key).get.body.string.force_encoding(Encoding::BINARY)
end
end
end
diff --git a/activestorage/test/models/variant_test.rb b/activestorage/test/models/variant_test.rb
index 0f3ada25c0..e74bbc9ab4 100644
--- a/activestorage/test/models/variant_test.rb
+++ b/activestorage/test/models/variant_test.rb
@@ -25,13 +25,30 @@ class ActiveStorage::VariantTest < ActiveSupport::TestCase
assert_match(/Gray/, image.colorspace)
end
- test "center-weighted crop of JPEG blob" do
+ test "center-weighted crop of JPEG blob using :combine_options" do
+ begin
+ ActiveStorage.variant_processor = nil
+ blob = create_file_blob(filename: "racecar.jpg")
+ variant = ActiveSupport::Deprecation.silence do
+ blob.variant(combine_options: {
+ gravity: "center",
+ resize: "100x100^",
+ crop: "100x100+0+0",
+ }).processed
+ end
+ assert_match(/racecar\.jpg/, variant.service_url)
+
+ image = read_image(variant)
+ assert_equal 100, image.width
+ assert_equal 100, image.height
+ ensure
+ ActiveStorage.variant_processor = :mini_magick
+ end
+ end
+
+ test "center-weighted crop of JPEG blob using :resize_to_fill" do
blob = create_file_blob(filename: "racecar.jpg")
- variant = blob.variant(combine_options: {
- gravity: "center",
- resize: "100x100^",
- crop: "100x100+0+0",
- }).processed
+ variant = blob.variant(resize_to_fill: [100, 100]).processed
assert_match(/racecar\.jpg/, variant.service_url)
image = read_image(variant)
@@ -80,4 +97,20 @@ class ActiveStorage::VariantTest < ActiveSupport::TestCase
variant = blob.variant(font: "a" * 10_000).processed
assert_operator variant.service_url.length, :<, 525
end
+
+ test "works for vips processor" do
+ begin
+ ActiveStorage.variant_processor = :vips
+ blob = create_file_blob(filename: "racecar.jpg")
+ variant = blob.variant(thumbnail_image: 100).processed
+
+ image = read_image(variant)
+ assert_equal 100, image.width
+ assert_equal 67, image.height
+ rescue LoadError
+ # libvips not installed
+ ensure
+ ActiveStorage.variant_processor = :mini_magick
+ end
+ end
end
diff --git a/activestorage/test/test_helper.rb b/activestorage/test/test_helper.rb
index 028874f374..4787eccd09 100644
--- a/activestorage/test/test_helper.rb
+++ b/activestorage/test/test_helper.rb
@@ -7,7 +7,7 @@ require "bundler/setup"
require "active_support"
require "active_support/test_case"
require "active_support/testing/autorun"
-require "mini_magick"
+require "image_processing/mini_magick"
begin
require "byebug"
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index 247eec68ac..62c0f612a8 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -1,5 +1,5 @@
* Fix bug where `ActiveSupport::Timezone.all` would fail when tzinfo data for
- any timezone defined in `ActiveSupport::MAPPING` is missing.
+ any timezone defined in `ActiveSupport::TimeZone::MAPPING` is missing.
*Dominik Sander*
@@ -28,6 +28,34 @@
*Nick Holden*
+* `ActiveSupport::Inflector#ordinal` and `ActiveSupport::Inflector#ordinalize` now support
+ translations through I18n.
+
+ # locale/fr.rb
+
+ {
+ fr: {
+ number: {
+ nth: {
+ ordinals: lambda do |_key, number:, **_options|
+ if number.to_i.abs == 1
+ 'er'
+ else
+ 'e'
+ end
+ end,
+
+ ordinalized: lambda do |_key, number:, **_options|
+ "#{number}#{ActiveSupport::Inflector.ordinal(number)}"
+ end
+ }
+ }
+ }
+ }
+
+
+ *Christian Blais*
+
* Add `:private` option to ActiveSupport's `Module#delegate`
in order to delegate methods as private:
diff --git a/activesupport/lib/active_support/deprecation/reporting.rb b/activesupport/lib/active_support/deprecation/reporting.rb
index 2c004f4c9e..7075b5b869 100644
--- a/activesupport/lib/active_support/deprecation/reporting.rb
+++ b/activesupport/lib/active_support/deprecation/reporting.rb
@@ -104,7 +104,7 @@ module ActiveSupport
end
end
- RAILS_GEM_ROOT = File.expand_path("../../../..", __dir__)
+ RAILS_GEM_ROOT = File.expand_path("../../../..", __dir__) + "/"
def ignored_callstack(path)
path.start_with?(RAILS_GEM_ROOT) || path.start_with?(RbConfig::CONFIG["rubylibdir"])
diff --git a/activesupport/lib/active_support/i18n.rb b/activesupport/lib/active_support/i18n.rb
index d60b3eff30..39dab1cc71 100644
--- a/activesupport/lib/active_support/i18n.rb
+++ b/activesupport/lib/active_support/i18n.rb
@@ -13,3 +13,4 @@ require "active_support/lazy_load_hooks"
ActiveSupport.run_load_hooks(:i18n)
I18n.load_path << File.expand_path("locale/en.yml", __dir__)
+I18n.load_path << File.expand_path("locale/en.rb", __dir__)
diff --git a/activesupport/lib/active_support/inflector/methods.rb b/activesupport/lib/active_support/inflector/methods.rb
index 7e782e2a93..339b93b8da 100644
--- a/activesupport/lib/active_support/inflector/methods.rb
+++ b/activesupport/lib/active_support/inflector/methods.rb
@@ -341,18 +341,7 @@ module ActiveSupport
# ordinal(-11) # => "th"
# ordinal(-1021) # => "st"
def ordinal(number)
- abs_number = number.to_i.abs
-
- if (11..13).include?(abs_number % 100)
- "th"
- else
- case abs_number % 10
- when 1; "st"
- when 2; "nd"
- when 3; "rd"
- else "th"
- end
- end
+ I18n.translate("number.nth.ordinals", number: number)
end
# Turns a number into an ordinal string used to denote the position in an
@@ -365,7 +354,7 @@ module ActiveSupport
# ordinalize(-11) # => "-11th"
# ordinalize(-1021) # => "-1021st"
def ordinalize(number)
- "#{number}#{ordinal(number)}"
+ I18n.translate("number.nth.ordinalized", number: number)
end
private
diff --git a/activesupport/lib/active_support/locale/en.rb b/activesupport/lib/active_support/locale/en.rb
new file mode 100644
index 0000000000..26c2280c95
--- /dev/null
+++ b/activesupport/lib/active_support/locale/en.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+{
+ en: {
+ number: {
+ nth: {
+ ordinals: lambda do |_key, number:, **_options|
+ abs_number = number.to_i.abs
+
+ if (11..13).cover?(abs_number % 100)
+ "th"
+ else
+ case abs_number % 10
+ when 1 then "st"
+ when 2 then "nd"
+ when 3 then "rd"
+ else "th"
+ end
+ end
+ end,
+
+ ordinalized: lambda do |_key, number:, **_options|
+ "#{number}#{ActiveSupport::Inflector.ordinal(number)}"
+ end
+ }
+ }
+ }
+}
diff --git a/activesupport/lib/active_support/testing/stream.rb b/activesupport/lib/active_support/testing/stream.rb
index d070a1793d..127cfe1e12 100644
--- a/activesupport/lib/active_support/testing/stream.rb
+++ b/activesupport/lib/active_support/testing/stream.rb
@@ -33,7 +33,7 @@ module ActiveSupport
yield
stream_io.rewind
- return captured_stream.read
+ captured_stream.read
ensure
captured_stream.close
captured_stream.unlink
diff --git a/activesupport/test/cache/behaviors/cache_delete_matched_behavior.rb b/activesupport/test/cache/behaviors/cache_delete_matched_behavior.rb
index 6f59ce48d2..ed8eba8fc2 100644
--- a/activesupport/test/cache/behaviors/cache_delete_matched_behavior.rb
+++ b/activesupport/test/cache/behaviors/cache_delete_matched_behavior.rb
@@ -7,9 +7,9 @@ module CacheDeleteMatchedBehavior
@cache.write("foo/bar", "baz")
@cache.write("fu/baz", "bar")
@cache.delete_matched(/oo/)
- assert !@cache.exist?("foo")
+ assert_not @cache.exist?("foo")
assert @cache.exist?("fu")
- assert !@cache.exist?("foo/bar")
+ assert_not @cache.exist?("foo/bar")
assert @cache.exist?("fu/baz")
end
end
diff --git a/activesupport/test/cache/behaviors/cache_store_behavior.rb b/activesupport/test/cache/behaviors/cache_store_behavior.rb
index f6763d195a..f9153ffe2a 100644
--- a/activesupport/test/cache/behaviors/cache_store_behavior.rb
+++ b/activesupport/test/cache/behaviors/cache_store_behavior.rb
@@ -33,7 +33,7 @@ module CacheStoreBehavior
cache_miss = false
assert_equal 3, @cache.fetch("foo") { |key| cache_miss = true; key.length }
- assert !cache_miss
+ assert_not cache_miss
end
def test_fetch_with_forced_cache_miss
@@ -308,7 +308,7 @@ module CacheStoreBehavior
@cache.write("foo", "bar")
assert @cache.exist?("foo")
assert @cache.delete("foo")
- assert !@cache.exist?("foo")
+ assert_not @cache.exist?("foo")
end
def test_original_store_objects_should_not_be_immutable
diff --git a/activesupport/test/cache/behaviors/cache_store_version_behavior.rb b/activesupport/test/cache/behaviors/cache_store_version_behavior.rb
index 62c0bb4f6f..805f061839 100644
--- a/activesupport/test/cache/behaviors/cache_store_version_behavior.rb
+++ b/activesupport/test/cache/behaviors/cache_store_version_behavior.rb
@@ -30,7 +30,7 @@ module CacheStoreVersionBehavior
def test_exist_with_wrong_version_should_be_false
@cache.write("foo", "bar", version: 1)
- assert !@cache.exist?("foo", version: 2)
+ assert_not @cache.exist?("foo", version: 2)
end
def test_reading_and_writing_with_model_supporting_cache_version
diff --git a/activesupport/test/cache/behaviors/failure_safety_behavior.rb b/activesupport/test/cache/behaviors/failure_safety_behavior.rb
index 53bda4f942..43b67d81db 100644
--- a/activesupport/test/cache/behaviors/failure_safety_behavior.rb
+++ b/activesupport/test/cache/behaviors/failure_safety_behavior.rb
@@ -63,7 +63,7 @@ module FailureSafetyBehavior
@cache.write("foo", "bar")
emulating_unavailability do |cache|
- assert !cache.exist?("foo")
+ assert_not cache.exist?("foo")
end
end
diff --git a/activesupport/test/cache/cache_store_namespace_test.rb b/activesupport/test/cache/cache_store_namespace_test.rb
index b52a61c500..dfdb3262f2 100644
--- a/activesupport/test/cache/cache_store_namespace_test.rb
+++ b/activesupport/test/cache/cache_store_namespace_test.rb
@@ -25,7 +25,7 @@ class CacheStoreNamespaceTest < ActiveSupport::TestCase
cache.write("foo", "bar")
cache.write("fu", "baz")
cache.delete_matched(/^fo/)
- assert !cache.exist?("foo")
+ assert_not cache.exist?("foo")
assert cache.exist?("fu")
end
@@ -34,7 +34,7 @@ class CacheStoreNamespaceTest < ActiveSupport::TestCase
cache.write("foo", "bar")
cache.write("fu", "baz")
cache.delete_matched(/OO/i)
- assert !cache.exist?("foo")
+ assert_not cache.exist?("foo")
assert cache.exist?("fu")
end
end
diff --git a/activesupport/test/cache/stores/memory_store_test.rb b/activesupport/test/cache/stores/memory_store_test.rb
index 8fe8384fb0..340fb517cb 100644
--- a/activesupport/test/cache/stores/memory_store_test.rb
+++ b/activesupport/test/cache/stores/memory_store_test.rb
@@ -104,7 +104,7 @@ class MemoryStorePruningTest < ActiveSupport::TestCase
assert @cache.exist?(4)
assert @cache.exist?(3)
assert @cache.exist?(2)
- assert !@cache.exist?(1)
+ assert_not @cache.exist?(1)
end
def test_write_with_unless_exist
diff --git a/activesupport/test/cache/stores/redis_cache_store_test.rb b/activesupport/test/cache/stores/redis_cache_store_test.rb
index 3cae2bb11f..24c4c5c481 100644
--- a/activesupport/test/cache/stores/redis_cache_store_test.rb
+++ b/activesupport/test/cache/stores/redis_cache_store_test.rb
@@ -211,7 +211,7 @@ module ActiveSupport::Cache::RedisCacheStoreTests
@cache.write("foo", "bar")
@cache.write("fu", "baz")
@cache.delete_matched("foo*")
- assert !@cache.exist?("foo")
+ assert_not @cache.exist?("foo")
assert @cache.exist?("fu")
end
@@ -227,15 +227,15 @@ module ActiveSupport::Cache::RedisCacheStoreTests
@cache.write("foo", "bar")
@cache.write("fu", "baz")
@cache.clear
- assert !@cache.exist?("foo")
- assert !@cache.exist?("fu")
+ assert_not @cache.exist?("foo")
+ assert_not @cache.exist?("fu")
end
test "only clear namespace cache key" do
@cache.write("foo", "bar")
@cache.redis.set("fu", "baz")
@cache.clear
- assert !@cache.exist?("foo")
+ assert_not @cache.exist?("foo")
assert @cache.redis.exists("fu")
end
end
diff --git a/activesupport/test/callbacks_test.rb b/activesupport/test/callbacks_test.rb
index 6c7643ed72..5c9a3b29e7 100644
--- a/activesupport/test/callbacks_test.rb
+++ b/activesupport/test/callbacks_test.rb
@@ -829,7 +829,7 @@ module CallbacksTest
def test_block_never_called_if_terminated
obj = CallbackTerminator.new
obj.save
- assert !obj.saved
+ assert_not obj.saved
end
end
@@ -857,7 +857,7 @@ module CallbacksTest
def test_block_never_called_if_abort_is_thrown
obj = CallbackDefaultTerminator.new
obj.save
- assert !obj.saved
+ assert_not obj.saved
end
end
diff --git a/activesupport/test/class_cache_test.rb b/activesupport/test/class_cache_test.rb
index 8cfcaedafd..1ef1939b4b 100644
--- a/activesupport/test/class_cache_test.rb
+++ b/activesupport/test/class_cache_test.rb
@@ -68,7 +68,7 @@ module ActiveSupport
def test_new_rejects_strings
@cache.store ClassCacheTest.name
- assert !@cache.key?(ClassCacheTest.name)
+ assert_not @cache.key?(ClassCacheTest.name)
end
def test_store_returns_self
diff --git a/activesupport/test/core_ext/duration_test.rb b/activesupport/test/core_ext/duration_test.rb
index 8f6befe809..240ae3bde0 100644
--- a/activesupport/test/core_ext/duration_test.rb
+++ b/activesupport/test/core_ext/duration_test.rb
@@ -16,30 +16,30 @@ class DurationTest < ActiveSupport::TestCase
assert_kind_of ActiveSupport::Duration, d
assert_kind_of Numeric, d
assert_kind_of Integer, d
- assert !d.is_a?(Hash)
+ assert_not d.is_a?(Hash)
k = Class.new
class << k; undef_method :== end
- assert !d.is_a?(k)
+ assert_not d.is_a?(k)
end
def test_instance_of
assert 1.minute.instance_of?(Integer)
assert 2.days.instance_of?(ActiveSupport::Duration)
- assert !3.second.instance_of?(Numeric)
+ assert_not 3.second.instance_of?(Numeric)
end
def test_threequals
assert ActiveSupport::Duration === 1.day
- assert !(ActiveSupport::Duration === 1.day.to_i)
- assert !(ActiveSupport::Duration === "foo")
+ assert_not (ActiveSupport::Duration === 1.day.to_i)
+ assert_not (ActiveSupport::Duration === "foo")
end
def test_equals
assert 1.day == 1.day
assert 1.day == 1.day.to_i
assert 1.day.to_i == 1.day
- assert !(1.day == "foo")
+ assert_not (1.day == "foo")
end
def test_to_s
@@ -53,11 +53,11 @@ class DurationTest < ActiveSupport::TestCase
assert 1.minute.eql?(1.minute)
assert 1.minute.eql?(60.seconds)
assert 2.days.eql?(48.hours)
- assert !1.second.eql?(1)
- assert !1.eql?(1.second)
+ assert_not 1.second.eql?(1)
+ assert_not 1.eql?(1.second)
assert 1.minute.eql?(180.seconds - 2.minutes)
- assert !1.minute.eql?(60)
- assert !1.minute.eql?("foo")
+ assert_not 1.minute.eql?(60)
+ assert_not 1.minute.eql?("foo")
end
def test_inspect
diff --git a/activesupport/test/core_ext/file_test.rb b/activesupport/test/core_ext/file_test.rb
index 23e3c277cc..9c97700e5d 100644
--- a/activesupport/test/core_ext/file_test.rb
+++ b/activesupport/test/core_ext/file_test.rb
@@ -8,7 +8,7 @@ class AtomicWriteTest < ActiveSupport::TestCase
contents = "Atomic Text"
File.atomic_write(file_name, Dir.pwd) do |file|
file.write(contents)
- assert !File.exist?(file_name)
+ assert_not File.exist?(file_name)
end
assert File.exist?(file_name)
assert_equal contents, File.read(file_name)
@@ -22,7 +22,7 @@ class AtomicWriteTest < ActiveSupport::TestCase
raise "something bad"
end
rescue
- assert !File.exist?(file_name)
+ assert_not File.exist?(file_name)
end
def test_atomic_write_preserves_file_permissions
@@ -50,7 +50,7 @@ class AtomicWriteTest < ActiveSupport::TestCase
contents = "Atomic Text"
File.atomic_write(file_name, Dir.pwd) do |file|
file.write(contents)
- assert !File.exist?(file_name)
+ assert_not File.exist?(file_name)
end
assert File.exist?(file_name)
assert_equal File.probe_stat_in(Dir.pwd).mode, file_mode
diff --git a/activesupport/test/core_ext/integer_ext_test.rb b/activesupport/test/core_ext/integer_ext_test.rb
index 14169b084d..5691dc5341 100644
--- a/activesupport/test/core_ext/integer_ext_test.rb
+++ b/activesupport/test/core_ext/integer_ext_test.rb
@@ -8,14 +8,14 @@ class IntegerExtTest < ActiveSupport::TestCase
def test_multiple_of
[ -7, 0, 7, 14 ].each { |i| assert i.multiple_of?(7) }
- [ -7, 7, 14 ].each { |i| assert ! i.multiple_of?(6) }
+ [ -7, 7, 14 ].each { |i| assert_not i.multiple_of?(6) }
# test the 0 edge case
assert 0.multiple_of?(0)
- assert !5.multiple_of?(0)
+ assert_not 5.multiple_of?(0)
# test with a prime
- [2, 3, 5, 7].each { |i| assert !PRIME.multiple_of?(i) }
+ [2, 3, 5, 7].each { |i| assert_not PRIME.multiple_of?(i) }
end
def test_ordinalize
diff --git a/activesupport/test/core_ext/module/attr_internal_test.rb b/activesupport/test/core_ext/module/attr_internal_test.rb
index c2a28eced4..9a65f75497 100644
--- a/activesupport/test/core_ext/module/attr_internal_test.rb
+++ b/activesupport/test/core_ext/module/attr_internal_test.rb
@@ -12,7 +12,7 @@ class AttrInternalTest < ActiveSupport::TestCase
def test_reader
assert_nothing_raised { @target.attr_internal_reader :foo }
- assert !@instance.instance_variable_defined?("@_foo")
+ assert_not @instance.instance_variable_defined?("@_foo")
assert_raise(NoMethodError) { @instance.foo = 1 }
@instance.instance_variable_set("@_foo", 1)
@@ -22,7 +22,7 @@ class AttrInternalTest < ActiveSupport::TestCase
def test_writer
assert_nothing_raised { @target.attr_internal_writer :foo }
- assert !@instance.instance_variable_defined?("@_foo")
+ assert_not @instance.instance_variable_defined?("@_foo")
assert_nothing_raised { assert_equal 1, @instance.foo = 1 }
assert_equal 1, @instance.instance_variable_get("@_foo")
@@ -32,7 +32,7 @@ class AttrInternalTest < ActiveSupport::TestCase
def test_accessor
assert_nothing_raised { @target.attr_internal :foo }
- assert !@instance.instance_variable_defined?("@_foo")
+ assert_not @instance.instance_variable_defined?("@_foo")
assert_nothing_raised { assert_equal 1, @instance.foo = 1 }
assert_equal 1, @instance.instance_variable_get("@_foo")
@@ -44,10 +44,10 @@ class AttrInternalTest < ActiveSupport::TestCase
assert_nothing_raised { Module.attr_internal_naming_format = "@abc%sdef" }
@target.attr_internal :foo
- assert !@instance.instance_variable_defined?("@_foo")
- assert !@instance.instance_variable_defined?("@abcfoodef")
+ assert_not @instance.instance_variable_defined?("@_foo")
+ assert_not @instance.instance_variable_defined?("@abcfoodef")
assert_nothing_raised { @instance.foo = 1 }
- assert !@instance.instance_variable_defined?("@_foo")
+ assert_not @instance.instance_variable_defined?("@_foo")
assert @instance.instance_variable_defined?("@abcfoodef")
ensure
Module.attr_internal_naming_format = "@_%s"
diff --git a/activesupport/test/core_ext/module/concerning_test.rb b/activesupport/test/core_ext/module/concerning_test.rb
index 969434766f..374114c11b 100644
--- a/activesupport/test/core_ext/module/concerning_test.rb
+++ b/activesupport/test/core_ext/module/concerning_test.rb
@@ -21,7 +21,7 @@ class ModuleConcernTest < ActiveSupport::TestCase
# Declares a concern but doesn't include it
assert klass.const_defined?(:Baz, false)
- assert !ModuleConcernTest.const_defined?(:Baz)
+ assert_not ModuleConcernTest.const_defined?(:Baz)
assert_kind_of ActiveSupport::Concern, klass::Baz
assert_not_includes klass.ancestors, klass::Baz, klass.ancestors.inspect
diff --git a/activesupport/test/core_ext/name_error_test.rb b/activesupport/test/core_ext/name_error_test.rb
index d1dace3713..5c6c12ffc7 100644
--- a/activesupport/test/core_ext/name_error_test.rb
+++ b/activesupport/test/core_ext/name_error_test.rb
@@ -17,7 +17,7 @@ class NameErrorTest < ActiveSupport::TestCase
exc = assert_raise NameError do
some_method_that_does_not_exist
end
- assert !exc.missing_name?(:Foo)
+ assert_not exc.missing_name?(:Foo)
assert_nil exc.missing_name
end
end
diff --git a/activesupport/test/core_ext/object/acts_like_test.rb b/activesupport/test/core_ext/object/acts_like_test.rb
index 9f7b81f7fc..31241caf0a 100644
--- a/activesupport/test/core_ext/object/acts_like_test.rb
+++ b/activesupport/test/core_ext/object/acts_like_test.rb
@@ -17,19 +17,19 @@ class ObjectTests < ActiveSupport::TestCase
dt = DateTime.new
duck = DuckTime.new
- assert !object.acts_like?(:time)
- assert !object.acts_like?(:date)
+ assert_not object.acts_like?(:time)
+ assert_not object.acts_like?(:date)
assert time.acts_like?(:time)
- assert !time.acts_like?(:date)
+ assert_not time.acts_like?(:date)
- assert !date.acts_like?(:time)
+ assert_not date.acts_like?(:time)
assert date.acts_like?(:date)
assert dt.acts_like?(:time)
assert dt.acts_like?(:date)
assert duck.acts_like?(:time)
- assert !duck.acts_like?(:date)
+ assert_not duck.acts_like?(:date)
end
end
diff --git a/activesupport/test/core_ext/object/deep_dup_test.rb b/activesupport/test/core_ext/object/deep_dup_test.rb
index 2486592441..1fb26ebac7 100644
--- a/activesupport/test/core_ext/object/deep_dup_test.rb
+++ b/activesupport/test/core_ext/object/deep_dup_test.rb
@@ -47,7 +47,7 @@ class DeepDupTest < ActiveSupport::TestCase
object = Object.new
dup = object.deep_dup
dup.instance_variable_set(:@a, 1)
- assert !object.instance_variable_defined?(:@a)
+ assert_not object.instance_variable_defined?(:@a)
assert dup.instance_variable_defined?(:@a)
end
diff --git a/activesupport/test/core_ext/object/inclusion_test.rb b/activesupport/test/core_ext/object/inclusion_test.rb
index 52c21f2e8e..8cbb4f848f 100644
--- a/activesupport/test/core_ext/object/inclusion_test.rb
+++ b/activesupport/test/core_ext/object/inclusion_test.rb
@@ -6,30 +6,30 @@ require "active_support/core_ext/object/inclusion"
class InTest < ActiveSupport::TestCase
def test_in_array
assert 1.in?([1, 2])
- assert !3.in?([1, 2])
+ assert_not 3.in?([1, 2])
end
def test_in_hash
h = { "a" => 100, "b" => 200 }
assert "a".in?(h)
- assert !"z".in?(h)
+ assert_not "z".in?(h)
end
def test_in_string
assert "lo".in?("hello")
- assert !"ol".in?("hello")
+ assert_not "ol".in?("hello")
assert ?h.in?("hello")
end
def test_in_range
assert 25.in?(1..50)
- assert !75.in?(1..50)
+ assert_not 75.in?(1..50)
end
def test_in_set
s = Set.new([1, 2])
assert 1.in?(s)
- assert !3.in?(s)
+ assert_not 3.in?(s)
end
module A
@@ -45,8 +45,8 @@ class InTest < ActiveSupport::TestCase
def test_in_module
assert A.in?(B)
assert A.in?(C)
- assert !A.in?(A)
- assert !A.in?(D)
+ assert_not A.in?(A)
+ assert_not A.in?(D)
end
def test_no_method_catching
diff --git a/activesupport/test/core_ext/range_ext_test.rb b/activesupport/test/core_ext/range_ext_test.rb
index 903c173e59..7c7a78f461 100644
--- a/activesupport/test/core_ext/range_ext_test.rb
+++ b/activesupport/test/core_ext/range_ext_test.rb
@@ -37,7 +37,7 @@ class RangeTest < ActiveSupport::TestCase
end
def test_overlaps_last_exclusive
- assert !(1...5).overlaps?(5..10)
+ assert_not (1...5).overlaps?(5..10)
end
def test_overlaps_first_inclusive
@@ -45,7 +45,7 @@ class RangeTest < ActiveSupport::TestCase
end
def test_overlaps_first_exclusive
- assert !(5..10).overlaps?(1...5)
+ assert_not (5..10).overlaps?(1...5)
end
def test_should_include_identical_inclusive
@@ -102,7 +102,7 @@ class RangeTest < ActiveSupport::TestCase
def test_no_overlaps_on_time
time_range_1 = Time.utc(2005, 12, 10, 15, 30)..Time.utc(2005, 12, 10, 17, 30)
time_range_2 = Time.utc(2005, 12, 10, 17, 31)..Time.utc(2005, 12, 10, 18, 00)
- assert !time_range_1.overlaps?(time_range_2)
+ assert_not time_range_1.overlaps?(time_range_2)
end
def test_each_on_time_with_zone
diff --git a/activesupport/test/core_ext/string_ext_test.rb b/activesupport/test/core_ext/string_ext_test.rb
index ccaa5dc786..b8de16cc5e 100644
--- a/activesupport/test/core_ext/string_ext_test.rb
+++ b/activesupport/test/core_ext/string_ext_test.rb
@@ -237,11 +237,11 @@ class StringInflectionsTest < ActiveSupport::TestCase
s = "hello"
assert s.starts_with?("h")
assert s.starts_with?("hel")
- assert !s.starts_with?("el")
+ assert_not s.starts_with?("el")
assert s.ends_with?("o")
assert s.ends_with?("lo")
- assert !s.ends_with?("el")
+ assert_not s.ends_with?("el")
end
def test_string_squish
diff --git a/activesupport/test/dependencies_test.rb b/activesupport/test/dependencies_test.rb
index 2ca21f215e..a4fbab7b55 100644
--- a/activesupport/test/dependencies_test.rb
+++ b/activesupport/test/dependencies_test.rb
@@ -535,9 +535,9 @@ class DependenciesTest < ActiveSupport::TestCase
def test_qualified_const_defined_should_not_call_const_missing
ModuleWithMissing.missing_count = 0
- assert ! ActiveSupport::Dependencies.qualified_const_defined?("ModuleWithMissing::A")
+ assert_not ActiveSupport::Dependencies.qualified_const_defined?("ModuleWithMissing::A")
assert_equal 0, ModuleWithMissing.missing_count
- assert ! ActiveSupport::Dependencies.qualified_const_defined?("ModuleWithMissing::A::B")
+ assert_not ActiveSupport::Dependencies.qualified_const_defined?("ModuleWithMissing::A::B")
assert_equal 0, ModuleWithMissing.missing_count
end
@@ -547,13 +547,13 @@ class DependenciesTest < ActiveSupport::TestCase
def test_autoloaded?
with_autoloading_fixtures do
- assert ! ActiveSupport::Dependencies.autoloaded?("ModuleFolder")
- assert ! ActiveSupport::Dependencies.autoloaded?("ModuleFolder::NestedClass")
+ assert_not ActiveSupport::Dependencies.autoloaded?("ModuleFolder")
+ assert_not ActiveSupport::Dependencies.autoloaded?("ModuleFolder::NestedClass")
assert ActiveSupport::Dependencies.autoloaded?(ModuleFolder)
assert ActiveSupport::Dependencies.autoloaded?("ModuleFolder")
- assert ! ActiveSupport::Dependencies.autoloaded?("ModuleFolder::NestedClass")
+ assert_not ActiveSupport::Dependencies.autoloaded?("ModuleFolder::NestedClass")
assert ActiveSupport::Dependencies.autoloaded?(ModuleFolder::NestedClass)
@@ -564,11 +564,11 @@ class DependenciesTest < ActiveSupport::TestCase
assert ActiveSupport::Dependencies.autoloaded?(:ModuleFolder)
# Anonymous modules aren't autoloaded.
- assert !ActiveSupport::Dependencies.autoloaded?(Module.new)
+ assert_not ActiveSupport::Dependencies.autoloaded?(Module.new)
nil_name = Module.new
def nil_name.name() nil end
- assert !ActiveSupport::Dependencies.autoloaded?(nil_name)
+ assert_not ActiveSupport::Dependencies.autoloaded?(nil_name)
end
ensure
remove_constants(:ModuleFolder)
@@ -778,7 +778,7 @@ class DependenciesTest < ActiveSupport::TestCase
M.unloadable
ActiveSupport::Dependencies.clear
- assert ! defined?(M)
+ assert_not defined?(M)
Object.const_set :M, Module.new
ActiveSupport::Dependencies.clear
@@ -809,7 +809,7 @@ class DependenciesTest < ActiveSupport::TestCase
assert_called(C, :before_remove_const, times: 1) do
assert_respond_to C, :before_remove_const
ActiveSupport::Dependencies.clear
- assert !defined?(C)
+ assert_not defined?(C)
end
ensure
remove_constants(:C)
@@ -1023,7 +1023,7 @@ class DependenciesTest < ActiveSupport::TestCase
assert !defined?(::RaisesNameError), "::RaisesNameError is defined but it should have failed!"
end
- assert !defined?(::RaisesNameError)
+ assert_not defined?(::RaisesNameError)
2.times do
assert_raise(NameError) { ::RaisesNameError }
assert !defined?(::RaisesNameError), "::RaisesNameError is defined but it should have failed!"
diff --git a/activesupport/test/deprecation/proxy_wrappers_test.rb b/activesupport/test/deprecation/proxy_wrappers_test.rb
index 2f866775f6..9e26052fb4 100644
--- a/activesupport/test/deprecation/proxy_wrappers_test.rb
+++ b/activesupport/test/deprecation/proxy_wrappers_test.rb
@@ -9,16 +9,16 @@ class ProxyWrappersTest < ActiveSupport::TestCase
def test_deprecated_object_proxy_doesnt_wrap_falsy_objects
proxy = ActiveSupport::Deprecation::DeprecatedObjectProxy.new(nil, "message")
- assert !proxy
+ assert_not proxy
end
def test_deprecated_instance_variable_proxy_doesnt_wrap_falsy_objects
proxy = ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy.new(nil, :waffles)
- assert !proxy
+ assert_not proxy
end
def test_deprecated_constant_proxy_doesnt_wrap_falsy_objects
proxy = ActiveSupport::Deprecation::DeprecatedConstantProxy.new(Waffles, NewWaffles)
- assert !proxy
+ assert_not proxy
end
end
diff --git a/activesupport/test/descendants_tracker_without_autoloading_test.rb b/activesupport/test/descendants_tracker_without_autoloading_test.rb
index f5c6a3045d..c65f69cba3 100644
--- a/activesupport/test/descendants_tracker_without_autoloading_test.rb
+++ b/activesupport/test/descendants_tracker_without_autoloading_test.rb
@@ -13,7 +13,7 @@ class DescendantsTrackerWithoutAutoloadingTest < ActiveSupport::TestCase
parent_instance = Parent.new
parent_instance.singleton_class.descendants
ActiveSupport::DescendantsTracker.clear
- assert !ActiveSupport::DescendantsTracker.class_variable_get(:@@direct_descendants).key?(parent_instance.singleton_class)
+ assert_not ActiveSupport::DescendantsTracker.class_variable_get(:@@direct_descendants).key?(parent_instance.singleton_class)
end
end
end
diff --git a/activesupport/test/file_update_checker_shared_tests.rb b/activesupport/test/file_update_checker_shared_tests.rb
index daf7f9d139..72683816b3 100644
--- a/activesupport/test/file_update_checker_shared_tests.rb
+++ b/activesupport/test/file_update_checker_shared_tests.rb
@@ -30,7 +30,7 @@ module FileUpdateCheckerSharedTests
checker = new_checker { i += 1 }
- assert !checker.execute_if_updated
+ assert_not checker.execute_if_updated
assert_equal 0, i
end
@@ -41,7 +41,7 @@ module FileUpdateCheckerSharedTests
checker = new_checker(tmpfiles) { i += 1 }
- assert !checker.execute_if_updated
+ assert_not checker.execute_if_updated
assert_equal 0, i
end
@@ -212,7 +212,7 @@ module FileUpdateCheckerSharedTests
touch(tmpfile("foo.rb"))
wait
- assert !checker.execute_if_updated
+ assert_not checker.execute_if_updated
assert_equal 0, i
end
@@ -238,7 +238,7 @@ module FileUpdateCheckerSharedTests
mkdir(subdir)
wait
- assert !checker.execute_if_updated
+ assert_not checker.execute_if_updated
assert_equal 0, i
touch(File.join(subdir, "nested.rb"))
@@ -259,7 +259,7 @@ module FileUpdateCheckerSharedTests
touch(tmpfile("new.txt"))
wait
- assert !checker.execute_if_updated
+ assert_not checker.execute_if_updated
assert_equal 0, i
# subdir does not look for Ruby files, but its parent tmpdir does.
diff --git a/activesupport/test/hash_with_indifferent_access_test.rb b/activesupport/test/hash_with_indifferent_access_test.rb
index b06250baf8..a20c428bf8 100644
--- a/activesupport/test/hash_with_indifferent_access_test.rb
+++ b/activesupport/test/hash_with_indifferent_access_test.rb
@@ -280,7 +280,7 @@ class HashWithIndifferentAccessTest < ActiveSupport::TestCase
replaced = hash.replace(b: 12)
assert hash.key?("b")
- assert !hash.key?(:a)
+ assert_not hash.key?(:a)
assert_equal 12, hash[:b]
assert_same hash, replaced
end
@@ -292,7 +292,7 @@ class HashWithIndifferentAccessTest < ActiveSupport::TestCase
replaced = hash.replace(HashByConversion.new(b: 12))
assert hash.key?("b")
- assert !hash.key?(:a)
+ assert_not hash.key?(:a)
assert_equal 12, hash[:b]
assert_same hash, replaced
end
diff --git a/activesupport/test/message_verifier_test.rb b/activesupport/test/message_verifier_test.rb
index 05d5c1cbc3..0fa53695e0 100644
--- a/activesupport/test/message_verifier_test.rb
+++ b/activesupport/test/message_verifier_test.rb
@@ -25,12 +25,12 @@ class MessageVerifierTest < ActiveSupport::TestCase
def test_valid_message
data, hash = @verifier.generate(@data).split("--")
- assert !@verifier.valid_message?(nil)
- assert !@verifier.valid_message?("")
- assert !@verifier.valid_message?("\xff") # invalid encoding
- assert !@verifier.valid_message?("#{data.reverse}--#{hash}")
- assert !@verifier.valid_message?("#{data}--#{hash.reverse}")
- assert !@verifier.valid_message?("purejunk")
+ assert_not @verifier.valid_message?(nil)
+ assert_not @verifier.valid_message?("")
+ assert_not @verifier.valid_message?("\xff") # invalid encoding
+ assert_not @verifier.valid_message?("#{data.reverse}--#{hash}")
+ assert_not @verifier.valid_message?("#{data}--#{hash.reverse}")
+ assert_not @verifier.valid_message?("purejunk")
end
def test_simple_round_tripping
@@ -40,7 +40,7 @@ class MessageVerifierTest < ActiveSupport::TestCase
end
def test_verified_returns_false_on_invalid_message
- assert !@verifier.verified("purejunk")
+ assert_not @verifier.verified("purejunk")
end
def test_verify_exception_on_invalid_message
diff --git a/activesupport/test/multibyte_chars_test.rb b/activesupport/test/multibyte_chars_test.rb
index 560b86b1a3..061446c782 100644
--- a/activesupport/test/multibyte_chars_test.rb
+++ b/activesupport/test/multibyte_chars_test.rb
@@ -75,7 +75,7 @@ class MultibyteCharsTest < ActiveSupport::TestCase
def test_consumes_utf8_strings
assert @proxy_class.consumes?(UNICODE_STRING)
assert @proxy_class.consumes?(ASCII_STRING)
- assert !@proxy_class.consumes?(BYTE_STRING)
+ assert_not @proxy_class.consumes?(BYTE_STRING)
end
def test_concatenation_should_return_a_proxy_class_instance
@@ -148,7 +148,7 @@ class MultibyteCharsUTF8BehaviourTest < ActiveSupport::TestCase
def test_identity
assert_equal @chars, @chars
assert @chars.eql?(@chars)
- assert !@chars.eql?(UNICODE_STRING)
+ assert_not @chars.eql?(UNICODE_STRING)
end
def test_string_methods_are_chainable
diff --git a/activesupport/test/notifications_test.rb b/activesupport/test/notifications_test.rb
index 28a02aafc8..d035f993f7 100644
--- a/activesupport/test/notifications_test.rb
+++ b/activesupport/test/notifications_test.rb
@@ -270,9 +270,9 @@ module Notifications
parent.children << child
assert parent.parent_of?(child)
- assert !child.parent_of?(parent)
- assert !parent.parent_of?(not_child)
- assert !not_child.parent_of?(parent)
+ assert_not child.parent_of?(parent)
+ assert_not parent.parent_of?(not_child)
+ assert_not not_child.parent_of?(parent)
end
private
diff --git a/activesupport/test/ordered_options_test.rb b/activesupport/test/ordered_options_test.rb
index cba5b8a8de..90394fee0a 100644
--- a/activesupport/test/ordered_options_test.rb
+++ b/activesupport/test/ordered_options_test.rb
@@ -15,7 +15,7 @@ class OrderedOptionsTest < ActiveSupport::TestCase
a[:allow_concurrency] = false
assert_equal 1, a.size
- assert !a[:allow_concurrency]
+ assert_not a[:allow_concurrency]
a["else_where"] = 56
assert_equal 2, a.size
@@ -47,7 +47,7 @@ class OrderedOptionsTest < ActiveSupport::TestCase
a.allow_concurrency = false
assert_equal 1, a.size
- assert !a.allow_concurrency
+ assert_not a.allow_concurrency
a.else_where = 56
assert_equal 2, a.size
diff --git a/activesupport/test/reloader_test.rb b/activesupport/test/reloader_test.rb
index 3e4229eaf7..976917c1a1 100644
--- a/activesupport/test/reloader_test.rb
+++ b/activesupport/test/reloader_test.rb
@@ -8,18 +8,18 @@ class ReloaderTest < ActiveSupport::TestCase
reloader.to_prepare { prepared = true }
reloader.to_complete { completed = true }
- assert !prepared
- assert !completed
+ assert_not prepared
+ assert_not completed
reloader.prepare!
assert prepared
- assert !completed
+ assert_not completed
prepared = false
reloader.wrap do
assert prepared
prepared = false
end
- assert !prepared
+ assert_not prepared
end
def test_prepend_prepare_callback
@@ -42,7 +42,7 @@ class ReloaderTest < ActiveSupport::TestCase
invoked = false
r.to_run { invoked = true }
r.wrap {}
- assert !invoked
+ assert_not invoked
end
def test_full_reload_sequence
diff --git a/activesupport/test/security_utils_test.rb b/activesupport/test/security_utils_test.rb
index 0a607594a2..fff9cc2a8d 100644
--- a/activesupport/test/security_utils_test.rb
+++ b/activesupport/test/security_utils_test.rb
@@ -11,7 +11,7 @@ class SecurityUtilsTest < ActiveSupport::TestCase
def test_fixed_length_secure_compare_should_perform_string_comparison
assert ActiveSupport::SecurityUtils.fixed_length_secure_compare("a", "a")
- assert !ActiveSupport::SecurityUtils.fixed_length_secure_compare("a", "b")
+ assert_not ActiveSupport::SecurityUtils.fixed_length_secure_compare("a", "b")
end
def test_fixed_length_secure_compare_raise_on_length_mismatch
diff --git a/activesupport/test/time_zone_test.rb b/activesupport/test/time_zone_test.rb
index 120afa61f2..b59f3e9405 100644
--- a/activesupport/test/time_zone_test.rb
+++ b/activesupport/test/time_zone_test.rb
@@ -725,7 +725,7 @@ class TimeZoneTest < ActiveSupport::TestCase
assert_not_includes all_zones, galapagos
end
- def test_all_not_raises_exception_with_mizzing_tzinfo_data
+ def test_all_doesnt_raise_exception_with_missing_tzinfo_data
mappings = {
"Puerto Rico" => "America/Unknown",
"Pittsburgh" => "America/New_York"
diff --git a/ci/custom_cops/lib/custom_cops.rb b/ci/custom_cops/lib/custom_cops.rb
index d5d17f8856..157b8247e4 100644
--- a/ci/custom_cops/lib/custom_cops.rb
+++ b/ci/custom_cops/lib/custom_cops.rb
@@ -1,3 +1,4 @@
# frozen_string_literal: true
require_relative "custom_cops/refute_not"
+require_relative "custom_cops/assert_not"
diff --git a/ci/custom_cops/lib/custom_cops/assert_not.rb b/ci/custom_cops/lib/custom_cops/assert_not.rb
new file mode 100644
index 0000000000..e722448e21
--- /dev/null
+++ b/ci/custom_cops/lib/custom_cops/assert_not.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+module CustomCops
+ # Enforces the use of `assert_not` over `assert !`.
+ #
+ # @example
+ # # bad
+ # assert !x
+ # assert ! x
+ #
+ # # good
+ # assert_not x
+ #
+ class AssertNot < RuboCop::Cop::Cop
+ MSG = "Prefer `assert_not` over `assert !`"
+
+ def_node_matcher :offensive?, "(send nil? :assert (send ... :!))"
+
+ def on_send(node)
+ add_offense(node) if offensive?(node)
+ end
+
+ def autocorrect(node)
+ expression = node.loc.expression
+
+ ->(corrector) do
+ corrector.replace(
+ expression,
+ corrected_source(expression.source)
+ )
+ end
+ end
+
+ private
+
+ def corrected_source(source)
+ source.gsub(/^assert(\(| ) *! */, "assert_not\\1")
+ end
+ end
+end
diff --git a/ci/custom_cops/test/custom_cops/assert_not_test.rb b/ci/custom_cops/test/custom_cops/assert_not_test.rb
new file mode 100644
index 0000000000..abb151aeb4
--- /dev/null
+++ b/ci/custom_cops/test/custom_cops/assert_not_test.rb
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+
+require "support/cop_helper"
+require_relative "../../lib/custom_cops/assert_not"
+
+class AssertNotTest < ActiveSupport::TestCase
+ include CopHelper
+
+ setup do
+ @cop = CustomCops::AssertNot.new
+ end
+
+ test "rejects 'assert !'" do
+ inspect_source @cop, "assert !x"
+ assert_offense @cop, "^^^^^^^^^ Prefer `assert_not` over `assert !`"
+ end
+
+ test "rejects 'assert !' with a complex value" do
+ inspect_source @cop, "assert !a.b(c)"
+ assert_offense @cop, "^^^^^^^^^^^^^^ Prefer `assert_not` over `assert !`"
+ end
+
+ test "autocorrects `assert !`" do
+ corrected = autocorrect_source(@cop, "assert !false")
+ assert_equal "assert_not false", corrected
+ end
+
+ test "autocorrects `assert !` with extra spaces" do
+ corrected = autocorrect_source(@cop, "assert ! false")
+ assert_equal "assert_not false", corrected
+ end
+
+ test "autocorrects `assert !` with parentheses" do
+ corrected = autocorrect_source(@cop, "assert(!false)")
+ assert_equal "assert_not(false)", corrected
+ end
+
+ test "accepts `assert_not`" do
+ inspect_source @cop, "assert_not x"
+ assert_empty @cop.offenses
+ end
+end
diff --git a/ci/custom_cops/test/custom_cops/refute_not_test.rb b/ci/custom_cops/test/custom_cops/refute_not_test.rb
index 5dbd8bf32a..f0f6eaeda0 100644
--- a/ci/custom_cops/test/custom_cops/refute_not_test.rb
+++ b/ci/custom_cops/test/custom_cops/refute_not_test.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "support/cop_helper"
-require "./lib/custom_cops/refute_not"
+require_relative "../../lib/custom_cops/refute_not"
class RefuteNotTest < ActiveSupport::TestCase
include CopHelper
@@ -59,15 +59,6 @@ class RefuteNotTest < ActiveSupport::TestCase
private
- def assert_offense(cop, expected_message)
- assert_not_empty cop.offenses
-
- offense = cop.offenses.first
- carets = "^" * offense.column_length
-
- assert_equal expected_message, "#{carets} #{offense.message}"
- end
-
def offense_message(refute_method, assert_method)
carets = "^" * refute_method.to_s.length
"#{carets} Prefer `#{assert_method}` over `#{refute_method}`"
diff --git a/ci/custom_cops/test/support/cop_helper.rb b/ci/custom_cops/test/support/cop_helper.rb
index d259154df5..c2c6b969dd 100644
--- a/ci/custom_cops/test/support/cop_helper.rb
+++ b/ci/custom_cops/test/support/cop_helper.rb
@@ -16,6 +16,18 @@ module CopHelper
rewrite(cop, processed_source)
end
+ def assert_offense(cop, expected_message)
+ assert_not_empty(
+ cop.offenses,
+ "Expected offense with message \"#{expected_message}\", but got no offense"
+ )
+
+ offense = cop.offenses.first
+ carets = "^" * offense.column_length
+
+ assert_equal expected_message, "#{carets} #{offense.message}"
+ end
+
private
TARGET_RUBY_VERSION = 2.4
diff --git a/guides/assets/javascripts/guides.js b/guides/assets/javascripts/guides.js
index e4d25dfb21..e39ac239cd 100644
--- a/guides/assets/javascripts/guides.js
+++ b/guides/assets/javascripts/guides.js
@@ -1,53 +1,54 @@
-$.fn.selectGuide = function(guide) {
- $("select", this).val(guide);
-};
-
-var guidesIndex = {
- bind: function() {
- var currentGuidePath = window.location.pathname;
- var currentGuide = currentGuidePath.substring(currentGuidePath.lastIndexOf("/")+1);
- $(".guides-index-small").
- on("change", "select", guidesIndex.navigate).
- selectGuide(currentGuide);
- $(document).on("click", ".more-info-button", function(e){
- e.stopPropagation();
- if ($(".more-info-links").is(":visible")) {
- $(".more-info-links").addClass("s-hidden").unwrap();
- } else {
- $(".more-info-links").wrap("<div class='more-info-container'></div>").removeClass("s-hidden");
- }
- });
- $("#guidesMenu").on("click", function(e) {
- $("#guides").toggle();
- return false;
+(function() {
+ "use strict";
+
+ this.syntaxhighlighterConfig = { autoLinks: false };
+
+ this.wrap = function(elem, wrapper) {
+ elem.parentNode.insertBefore(wrapper, elem);
+ wrapper.appendChild(elem);
+ }
+
+ this.unwrap = function(elem) {
+ var wrapper = elem.parentNode;
+ wrapper.parentNode.replaceChild(elem, wrapper);
+ }
+
+ this.createElement = function(tagName, className) {
+ var elem = document.createElement(tagName);
+ elem.classList.add(className);
+ return elem;
+ }
+
+ document.addEventListener("DOMContentLoaded", function() {
+ var guidesMenu = document.getElementById("guidesMenu");
+ var guides = document.getElementById("guides");
+
+ guidesMenu.addEventListener("click", function(e) {
+ e.preventDefault();
+ guides.classList.toggle("visible");
});
- $(document).on("click", function(e){
- e.stopPropagation();
- var $button = $(".more-info-button");
- var element;
- // Cross browser find the element that had the event
- if (e.target) element = e.target;
- else if (e.srcElement) element = e.srcElement;
+ var guidesIndexItem = document.querySelector("select.guides-index-item");
+ var currentGuidePath = window.location.pathname;
+ guidesIndexItem.value = currentGuidePath.substring(currentGuidePath.lastIndexOf("/") + 1);
- // Defeat the older Safari bug:
- // http://www.quirksmode.org/js/events_properties.html
- if (element.nodeType === 3) element = element.parentNode;
+ guidesIndexItem.addEventListener("change", function(e) {
+ window.location = e.target.value;
+ });
- var $element = $(element);
+ var moreInfoButton = document.querySelector(".more-info-button");
+ var moreInfoLinks = document.querySelector(".more-info-links");
- var $container = $element.parents(".more-info-container");
+ moreInfoButton.addEventListener("click", function(e) {
+ e.preventDefault();
- // We've captured a click outside the popup
- if($container.length === 0){
- $container = $button.next(".more-info-container");
- $container.find(".more-info-links").addClass("s-hidden").unwrap();
+ if (moreInfoLinks.classList.contains("s-hidden")) {
+ wrap(moreInfoLinks, createElement("div", "more-info-container"));
+ moreInfoLinks.classList.remove("s-hidden");
+ } else {
+ moreInfoLinks.classList.add("s-hidden");
+ unwrap(moreInfoLinks);
}
});
- },
- navigate: function(e){
- var $list = $(e.target);
- var url = $list.val();
- window.location = url;
- }
-};
+ });
+}).call(this);
diff --git a/guides/assets/javascripts/jquery.min.js b/guides/assets/javascripts/jquery.min.js
deleted file mode 100644
index 93adea19fd..0000000000
--- a/guides/assets/javascripts/jquery.min.js
+++ /dev/null
@@ -1,4 +0,0 @@
-/*! jQuery v1.7.2 jquery.com | jquery.org/license */
-(function(a,b){function cy(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cu(a){if(!cj[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){ck||(ck=c.createElement("iframe"),ck.frameBorder=ck.width=ck.height=0),b.appendChild(ck);if(!cl||!ck.createElement)cl=(ck.contentWindow||ck.contentDocument).document,cl.write((f.support.boxModel?"<!doctype html>":"")+"<html><body>"),cl.close();d=cl.createElement(a),cl.body.appendChild(d),e=f.css(d,"display"),b.removeChild(ck)}cj[a]=e}return cj[a]}function ct(a,b){var c={};f.each(cp.concat.apply([],cp.slice(0,b)),function(){c[this]=a});return c}function cs(){cq=b}function cr(){setTimeout(cs,0);return cq=f.now()}function ci(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ch(){try{return new a.XMLHttpRequest}catch(b){}}function cb(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g<i;g++){if(g===1)for(h in a.converters)typeof h=="string"&&(e[h.toLowerCase()]=a.converters[h]);l=k,k=d[g];if(k==="*")k=l;else if(l!=="*"&&l!==k){m=l+" "+k,n=e[m]||e["* "+k];if(!n){p=b;for(o in e){j=o.split(" ");if(j[0]===l||j[0]==="*"){p=e[j[1]+" "+k];if(p){o=e[o],o===!0?n=p:p===!0&&(n=o);break}}}}!n&&!p&&f.error("No conversion from "+m.replace(" "," to ")),n!==!0&&(c=n?n(c):p(o(c)))}}return c}function ca(a,c,d){var e=a.contents,f=a.dataTypes,g=a.responseFields,h,i,j,k;for(i in g)i in d&&(c[g[i]]=d[i]);while(f[0]==="*")f.shift(),h===b&&(h=a.mimeType||c.getResponseHeader("content-type"));if(h)for(i in e)if(e[i]&&e[i].test(h)){f.unshift(i);break}if(f[0]in d)j=f[0];else{for(i in d){if(!f[0]||a.converters[i+" "+f[0]]){j=i;break}k||(k=i)}j=j||k}if(j){j!==f[0]&&f.unshift(j);return d[j]}}function b_(a,b,c,d){if(f.isArray(b))f.each(b,function(b,e){c||bD.test(a)?d(a,e):b_(a+"["+(typeof e=="object"?b:"")+"]",e,c,d)});else if(!c&&f.type(b)==="object")for(var e in b)b_(a+"["+e+"]",b[e],c,d);else d(a,b)}function b$(a,c){var d,e,g=f.ajaxSettings.flatOptions||{};for(d in c)c[d]!==b&&((g[d]?a:e||(e={}))[d]=c[d]);e&&f.extend(!0,a,e)}function bZ(a,c,d,e,f,g){f=f||c.dataTypes[0],g=g||{},g[f]=!0;var h=a[f],i=0,j=h?h.length:0,k=a===bS,l;for(;i<j&&(k||!l);i++)l=h[i](c,d,e),typeof l=="string"&&(!k||g[l]?l=b:(c.dataTypes.unshift(l),l=bZ(a,c,d,e,l,g)));(k||!l)&&!g["*"]&&(l=bZ(a,c,d,e,"*",g));return l}function bY(a){return function(b,c){typeof b!="string"&&(c=b,b="*");if(f.isFunction(c)){var d=b.toLowerCase().split(bO),e=0,g=d.length,h,i,j;for(;e<g;e++)h=d[e],j=/^\+/.test(h),j&&(h=h.substr(1)||"*"),i=a[h]=a[h]||[],i[j?"unshift":"push"](c)}}}function bB(a,b,c){var d=b==="width"?a.offsetWidth:a.offsetHeight,e=b==="width"?1:0,g=4;if(d>0){if(c!=="border")for(;e<g;e+=2)c||(d-=parseFloat(f.css(a,"padding"+bx[e]))||0),c==="margin"?d+=parseFloat(f.css(a,c+bx[e]))||0:d-=parseFloat(f.css(a,"border"+bx[e]+"Width"))||0;return d+"px"}d=by(a,b);if(d<0||d==null)d=a.style[b];if(bt.test(d))return d;d=parseFloat(d)||0;if(c)for(;e<g;e+=2)d+=parseFloat(f.css(a,"padding"+bx[e]))||0,c!=="padding"&&(d+=parseFloat(f.css(a,"border"+bx[e]+"Width"))||0),c==="margin"&&(d+=parseFloat(f.css(a,c+bx[e]))||0);return d+"px"}function bo(a){var b=c.createElement("div");bh.appendChild(b),b.innerHTML=a.outerHTML;return b.firstChild}function bn(a){var b=(a.nodeName||"").toLowerCase();b==="input"?bm(a):b!=="script"&&typeof a.getElementsByTagName!="undefined"&&f.grep(a.getElementsByTagName("input"),bm)}function bm(a){if(a.type==="checkbox"||a.type==="radio")a.defaultChecked=a.checked}function bl(a){return typeof a.getElementsByTagName!="undefined"?a.getElementsByTagName("*"):typeof a.querySelectorAll!="undefined"?a.querySelectorAll("*"):[]}function bk(a,b){var c;b.nodeType===1&&(b.clearAttributes&&b.clearAttributes(),b.mergeAttributes&&b.mergeAttributes(a),c=b.nodeName.toLowerCase(),c==="object"?b.outerHTML=a.outerHTML:c!=="input"||a.type!=="checkbox"&&a.type!=="radio"?c==="option"?b.selected=a.defaultSelected:c==="input"||c==="textarea"?b.defaultValue=a.defaultValue:c==="script"&&b.text!==a.text&&(b.text=a.text):(a.checked&&(b.defaultChecked=b.checked=a.checked),b.value!==a.value&&(b.value=a.value)),b.removeAttribute(f.expando),b.removeAttribute("_submit_attached"),b.removeAttribute("_change_attached"))}function bj(a,b){if(b.nodeType===1&&!!f.hasData(a)){var c,d,e,g=f._data(a),h=f._data(b,g),i=g.events;if(i){delete h.handle,h.events={};for(c in i)for(d=0,e=i[c].length;d<e;d++)f.event.add(b,c,i[c][d])}h.data&&(h.data=f.extend({},h.data))}}function bi(a,b){return f.nodeName(a,"table")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function U(a){var b=V.split("|"),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}function T(a,b,c){b=b||0;if(f.isFunction(b))return f.grep(a,function(a,d){var e=!!b.call(a,d,a);return e===c});if(b.nodeType)return f.grep(a,function(a,d){return a===b===c});if(typeof b=="string"){var d=f.grep(a,function(a){return a.nodeType===1});if(O.test(b))return f.filter(b,d,!c);b=f.filter(b,d)}return f.grep(a,function(a,d){return f.inArray(a,b)>=0===c})}function S(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function K(){return!0}function J(){return!1}function n(a,b,c){var d=b+"defer",e=b+"queue",g=b+"mark",h=f._data(a,d);h&&(c==="queue"||!f._data(a,e))&&(c==="mark"||!f._data(a,g))&&setTimeout(function(){!f._data(a,e)&&!f._data(a,g)&&(f.removeData(a,d,!0),h.fire())},0)}function m(a){for(var b in a){if(b==="data"&&f.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function l(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(k,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNumeric(d)?+d:j.test(d)?f.parseJSON(d):d}catch(g){}f.data(a,c,d)}else d=b}return d}function h(a){var b=g[a]={},c,d;a=a.split(/\s+/);for(c=0,d=a.length;c<d;c++)b[a[c]]=!0;return b}var c=a.document,d=a.navigator,e=a.location,f=function(){function J(){if(!e.isReady){try{c.documentElement.doScroll("left")}catch(a){setTimeout(J,1);return}e.ready()}}var e=function(a,b){return new e.fn.init(a,b,h)},f=a.jQuery,g=a.$,h,i=/^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,n=/^[\],:{}\s]*$/,o=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,p=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,q=/(?:^|:|,)(?:\s*\[)+/g,r=/(webkit)[ \/]([\w.]+)/,s=/(opera)(?:.*version)?[ \/]([\w.]+)/,t=/(msie) ([\w.]+)/,u=/(mozilla)(?:.*? rv:([\w.]+))?/,v=/-([a-z]|[0-9])/ig,w=/^-ms-/,x=function(a,b){return(b+"").toUpperCase()},y=d.userAgent,z,A,B,C=Object.prototype.toString,D=Object.prototype.hasOwnProperty,E=Array.prototype.push,F=Array.prototype.slice,G=String.prototype.trim,H=Array.prototype.indexOf,I={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=m.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.7.2",length:0,size:function(){return this.length},toArray:function(){return F.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?E.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),A.add(a);return this},eq:function(a){a=+a;return a===-1?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(F.apply(this,arguments),"slice",F.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:E,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j<k;j++)if((a=arguments[j])!=null)for(c in a){d=i[c],f=a[c];if(i===f)continue;l&&f&&(e.isPlainObject(f)||(g=e.isArray(f)))?(g?(g=!1,h=d&&e.isArray(d)?d:[]):h=d&&e.isPlainObject(d)?d:{},i[c]=e.extend(l,h,f)):f!==b&&(i[c]=f)}return i},e.extend({noConflict:function(b){a.$===e&&(a.$=g),b&&a.jQuery===e&&(a.jQuery=f);return e},isReady:!1,readyWait:1,holdReady:function(a){a?e.readyWait++:e.ready(!0)},ready:function(a){if(a===!0&&!--e.readyWait||a!==!0&&!e.isReady){if(!c.body)return setTimeout(e.ready,1);e.isReady=!0;if(a!==!0&&--e.readyWait>0)return;A.fireWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").off("ready")}},bindReady:function(){if(!A){A=e.Callbacks("once memory");if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",B,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",B),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&J()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a!=null&&a==a.window},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):I[C.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;try{if(a.constructor&&!D.call(a,"constructor")&&!D.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||D.call(a,d)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw new Error(a)},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(n.test(b.replace(o,"@").replace(p,"]").replace(q,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(c){if(typeof c!="string"||!c)return null;var d,f;try{a.DOMParser?(f=new DOMParser,d=f.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(g){d=b}(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&e.error("Invalid XML: "+c);return d},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(w,"ms-").replace(v,x)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g<h;)if(c.apply(a[g++],d)===!1)break}else if(i){for(f in a)if(c.call(a[f],f,a[f])===!1)break}else for(;g<h;)if(c.call(a[g],g,a[g++])===!1)break;return a},trim:G?function(a){return a==null?"":G.call(a)}:function(a){return a==null?"":(a+"").replace(k,"").replace(l,"")},makeArray:function(a,b){var c=b||[];if(a!=null){var d=e.type(a);a.length==null||d==="string"||d==="function"||d==="regexp"||e.isWindow(a)?E.call(c,a):e.merge(c,a)}return c},inArray:function(a,b,c){var d;if(b){if(H)return H.call(b,a,c);d=b.length,c=c?c<0?Math.max(0,d+c):c:0;for(;c<d;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,c){var d=a.length,e=0;if(typeof c.length=="number")for(var f=c.length;e<f;e++)a[d++]=c[e];else while(c[e]!==b)a[d++]=c[e++];a.length=d;return a},grep:function(a,b,c){var d=[],e;c=!!c;for(var f=0,g=a.length;f<g;f++)e=!!b(a[f],f),c!==e&&d.push(a[f]);return d},map:function(a,c,d){var f,g,h=[],i=0,j=a.length,k=a instanceof e||j!==b&&typeof j=="number"&&(j>0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i<j;i++)f=c(a[i],i,d),f!=null&&(h[h.length]=f);else for(g in a)f=c(a[g],g,d),f!=null&&(h[h.length]=f);return h.concat.apply([],h)},guid:1,proxy:function(a,c){if(typeof c=="string"){var d=a[c];c=a,a=d}if(!e.isFunction(a))return b;var f=F.call(arguments,2),g=function(){return a.apply(c,f.concat(F.call(arguments)))};g.guid=a.guid=a.guid||g.guid||e.guid++;return g},access:function(a,c,d,f,g,h,i){var j,k=d==null,l=0,m=a.length;if(d&&typeof d=="object"){for(l in d)e.access(a,c,l,d[l],1,h,f);g=1}else if(f!==b){j=i===b&&e.isFunction(f),k&&(j?(j=c,c=function(a,b,c){return j.call(e(a),c)}):(c.call(a,f),c=null));if(c)for(;l<m;l++)c(a[l],d,j?f.call(a[l],l,c(a[l],d)):f,i);g=1}return g?a:k?c.call(a):m?c(a[0],d):h},now:function(){return(new Date).getTime()},uaMatch:function(a){a=a.toLowerCase();var b=r.exec(a)||s.exec(a)||t.exec(a)||a.indexOf("compatible")<0&&u.exec(a)||[];return{browser:b[1]||"",version:b[2]||"0"}},sub:function(){function a(b,c){return new a.fn.init(b,c)}e.extend(!0,a,this),a.superclass=this,a.fn=a.prototype=this(),a.fn.constructor=a,a.sub=this.sub,a.fn.init=function(d,f){f&&f instanceof e&&!(f instanceof a)&&(f=a(f));return e.fn.init.call(this,d,f,b)},a.fn.init.prototype=a.fn;var b=a(c);return a},browser:{}}),e.each("Boolean Number String Function Array Date RegExp Object".split(" "),function(a,b){I["[object "+b+"]"]=b.toLowerCase()}),z=e.uaMatch(y),z.browser&&(e.browser[z.browser]=!0,e.browser.version=z.version),e.browser.webkit&&(e.browser.safari=!0),j.test(" ")&&(k=/^[\s\xA0]+/,l=/[\s\xA0]+$/),h=e(c),c.addEventListener?B=function(){c.removeEventListener("DOMContentLoaded",B,!1),e.ready()}:c.attachEvent&&(B=function(){c.readyState==="complete"&&(c.detachEvent("onreadystatechange",B),e.ready())});return e}(),g={};f.Callbacks=function(a){a=a?g[a]||h(a):{};var c=[],d=[],e,i,j,k,l,m,n=function(b){var d,e,g,h,i;for(d=0,e=b.length;d<e;d++)g=b[d],h=f.type(g),h==="array"?n(g):h==="function"&&(!a.unique||!p.has(g))&&c.push(g)},o=function(b,f){f=f||[],e=!a.memory||[b,f],i=!0,j=!0,m=k||0,k=0,l=c.length;for(;c&&m<l;m++)if(c[m].apply(b,f)===!1&&a.stopOnFalse){e=!0;break}j=!1,c&&(a.once?e===!0?p.disable():c=[]:d&&d.length&&(e=d.shift(),p.fireWith(e[0],e[1])))},p={add:function(){if(c){var a=c.length;n(arguments),j?l=c.length:e&&e!==!0&&(k=a,o(e[0],e[1]))}return this},remove:function(){if(c){var b=arguments,d=0,e=b.length;for(;d<e;d++)for(var f=0;f<c.length;f++)if(b[d]===c[f]){j&&f<=l&&(l--,f<=m&&m--),c.splice(f--,1);if(a.unique)break}}return this},has:function(a){if(c){var b=0,d=c.length;for(;b<d;b++)if(a===c[b])return!0}return!1},empty:function(){c=[];return this},disable:function(){c=d=e=b;return this},disabled:function(){return!c},lock:function(){d=b,(!e||e===!0)&&p.disable();return this},locked:function(){return!d},fireWith:function(b,c){d&&(j?a.once||d.push([b,c]):(!a.once||!e)&&o(b,c));return this},fire:function(){p.fireWith(this,arguments);return this},fired:function(){return!!i}};return p};var i=[].slice;f.extend({Deferred:function(a){var b=f.Callbacks("once memory"),c=f.Callbacks("once memory"),d=f.Callbacks("memory"),e="pending",g={resolve:b,reject:c,notify:d},h={done:b.add,fail:c.add,progress:d.add,state:function(){return e},isResolved:b.fired,isRejected:c.fired,then:function(a,b,c){i.done(a).fail(b).progress(c);return this},always:function(){i.done.apply(i,arguments).fail.apply(i,arguments);return this},pipe:function(a,b,c){return f.Deferred(function(d){f.each({done:[a,"resolve"],fail:[b,"reject"],progress:[c,"notify"]},function(a,b){var c=b[0],e=b[1],g;f.isFunction(c)?i[a](function(){g=c.apply(this,arguments),g&&f.isFunction(g.promise)?g.promise().then(d.resolve,d.reject,d.notify):d[e+"With"](this===i?d:this,[g])}):i[a](d[e])})}).promise()},promise:function(a){if(a==null)a=h;else for(var b in h)a[b]=h[b];return a}},i=h.promise({}),j;for(j in g)i[j]=g[j].fire,i[j+"With"]=g[j].fireWith;i.done(function(){e="resolved"},c.disable,d.lock).fail(function(){e="rejected"},b.disable,d.lock),a&&a.call(i,i);return i},when:function(a){function m(a){return function(b){e[a]=arguments.length>1?i.call(arguments,0):b,j.notifyWith(k,e)}}function l(a){return function(c){b[a]=arguments.length>1?i.call(arguments,0):c,--g||j.resolveWith(j,b)}}var b=i.call(arguments,0),c=0,d=b.length,e=Array(d),g=d,h=d,j=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred(),k=j.promise();if(d>1){for(;c<d;c++)b[c]&&b[c].promise&&f.isFunction(b[c].promise)?b[c].promise().then(l(c),j.reject,m(c)):--g;g||j.resolveWith(j,b)}else j!==a&&j.resolveWith(j,d?[a]:[]);return k}}),f.support=function(){var b,d,e,g,h,i,j,k,l,m,n,o,p=c.createElement("div"),q=c.documentElement;p.setAttribute("className","t"),p.innerHTML=" <link/><table></table><a href='/a' style='top:1px;float:left;opacity:.55;'>a</a><input type='checkbox'/>",d=p.getElementsByTagName("*"),e=p.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=p.getElementsByTagName("input")[0],b={leadingWhitespace:p.firstChild.nodeType===3,tbody:!p.getElementsByTagName("tbody").length,htmlSerialize:!!p.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:p.className!=="t",enctype:!!c.createElement("form").enctype,html5Clone:c.createElement("nav").cloneNode(!0).outerHTML!=="<:nav></:nav>",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,pixelMargin:!0},f.boxModel=b.boxModel=c.compatMode==="CSS1Compat",i.checked=!0,b.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,b.optDisabled=!h.disabled;try{delete p.test}catch(r){b.deleteExpando=!1}!p.addEventListener&&p.attachEvent&&p.fireEvent&&(p.attachEvent("onclick",function(){b.noCloneEvent=!1}),p.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),b.radioValue=i.value==="t",i.setAttribute("checked","checked"),i.setAttribute("name","t"),p.appendChild(i),j=c.createDocumentFragment(),j.appendChild(p.lastChild),b.checkClone=j.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=i.checked,j.removeChild(i),j.appendChild(p);if(p.attachEvent)for(n in{submit:1,change:1,focusin:1})m="on"+n,o=m in p,o||(p.setAttribute(m,"return;"),o=typeof p[m]=="function"),b[n+"Bubbles"]=o;j.removeChild(p),j=g=h=p=i=null,f(function(){var d,e,g,h,i,j,l,m,n,q,r,s,t,u=c.getElementsByTagName("body")[0];!u||(m=1,t="padding:0;margin:0;border:",r="position:absolute;top:0;left:0;width:1px;height:1px;",s=t+"0;visibility:hidden;",n="style='"+r+t+"5px solid #000;",q="<div "+n+"display:block;'><div style='"+t+"0;display:block;overflow:hidden;'></div></div>"+"<table "+n+"' cellpadding='0' cellspacing='0'>"+"<tr><td></td></tr></table>",d=c.createElement("div"),d.style.cssText=s+"width:0;height:0;position:static;top:0;margin-top:"+m+"px",u.insertBefore(d,u.firstChild),p=c.createElement("div"),d.appendChild(p),p.innerHTML="<table><tr><td style='"+t+"0;display:none'></td><td>t</td></tr></table>",k=p.getElementsByTagName("td"),o=k[0].offsetHeight===0,k[0].style.display="",k[1].style.display="none",b.reliableHiddenOffsets=o&&k[0].offsetHeight===0,a.getComputedStyle&&(p.innerHTML="",l=c.createElement("div"),l.style.width="0",l.style.marginRight="0",p.style.width="2px",p.appendChild(l),b.reliableMarginRight=(parseInt((a.getComputedStyle(l,null)||{marginRight:0}).marginRight,10)||0)===0),typeof p.style.zoom!="undefined"&&(p.innerHTML="",p.style.width=p.style.padding="1px",p.style.border=0,p.style.overflow="hidden",p.style.display="inline",p.style.zoom=1,b.inlineBlockNeedsLayout=p.offsetWidth===3,p.style.display="block",p.style.overflow="visible",p.innerHTML="<div style='width:5px;'></div>",b.shrinkWrapBlocks=p.offsetWidth!==3),p.style.cssText=r+s,p.innerHTML=q,e=p.firstChild,g=e.firstChild,i=e.nextSibling.firstChild.firstChild,j={doesNotAddBorder:g.offsetTop!==5,doesAddBorderForTableAndCells:i.offsetTop===5},g.style.position="fixed",g.style.top="20px",j.fixedPosition=g.offsetTop===20||g.offsetTop===15,g.style.position=g.style.top="",e.style.overflow="hidden",e.style.position="relative",j.subtractsBorderForOverflowNotVisible=g.offsetTop===-5,j.doesNotIncludeMarginInBodyOffset=u.offsetTop!==m,a.getComputedStyle&&(p.style.marginTop="1%",b.pixelMargin=(a.getComputedStyle(p,null)||{marginTop:0}).marginTop!=="1%"),typeof d.style.zoom!="undefined"&&(d.style.zoom=1),u.removeChild(d),l=p=d=null,f.extend(b,j))});return b}();var j=/^(?:\{.*\}|\[.*\])$/,k=/([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!m(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g,h,i,j=f.expando,k=typeof c=="string",l=a.nodeType,m=l?f.cache:a,n=l?a[j]:a[j]&&j,o=c==="events";if((!n||!m[n]||!o&&!e&&!m[n].data)&&k&&d===b)return;n||(l?a[j]=n=++f.uuid:n=j),m[n]||(m[n]={},l||(m[n].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?m[n]=f.extend(m[n],c):m[n].data=f.extend(m[n].data,c);g=h=m[n],e||(h.data||(h.data={}),h=h.data),d!==b&&(h[f.camelCase(c)]=d);if(o&&!h[c])return g.events;k?(i=h[c],i==null&&(i=h[f.camelCase(c)])):i=h;return i}},removeData:function(a,b,c){if(!!f.acceptData(a)){var d,e,g,h=f.expando,i=a.nodeType,j=i?f.cache:a,k=i?a[h]:h;if(!j[k])return;if(b){d=c?j[k]:j[k].data;if(d){f.isArray(b)||(b in d?b=[b]:(b=f.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,g=b.length;e<g;e++)delete d[b[e]];if(!(c?m:f.isEmptyObject)(d))return}}if(!c){delete j[k].data;if(!m(j[k]))return}f.support.deleteExpando||!j.setInterval?delete j[k]:j[k]=null,i&&(f.support.deleteExpando?delete a[h]:a.removeAttribute?a.removeAttribute(h):a[h]=null)}},_data:function(a,b,c){return f.data(a,b,c,!0)},acceptData:function(a){if(a.nodeName){var b=f.noData[a.nodeName.toLowerCase()];if(b)return b!==!0&&a.getAttribute("classid")===b}return!0}}),f.fn.extend({data:function(a,c){var d,e,g,h,i,j=this[0],k=0,m=null;if(a===b){if(this.length){m=f.data(j);if(j.nodeType===1&&!f._data(j,"parsedAttrs")){g=j.attributes;for(i=g.length;k<i;k++)h=g[k].name,h.indexOf("data-")===0&&(h=f.camelCase(h.substring(5)),l(j,h,m[h]));f._data(j,"parsedAttrs",!0)}}return m}if(typeof a=="object")return this.each(function(){f.data(this,a)});d=a.split(".",2),d[1]=d[1]?"."+d[1]:"",e=d[1]+"!";return f.access(this,function(c){if(c===b){m=this.triggerHandler("getData"+e,[d[0]]),m===b&&j&&(m=f.data(j,a),m=l(j,a,m));return m===b&&d[1]?this.data(d[0]):m}d[1]=c,this.each(function(){var b=f(this);b.triggerHandler("setData"+e,d),f.data(this,a,c),b.triggerHandler("changeData"+e,d)})},null,c,arguments.length>1,null,!1)},removeData:function(a){return this.each(function(){f.removeData(this,a)})}}),f.extend({_mark:function(a,b){a&&(b=(b||"fx")+"mark",f._data(a,b,(f._data(a,b)||0)+1))},_unmark:function(a,b,c){a!==!0&&(c=b,b=a,a=!1);if(b){c=c||"fx";var d=c+"mark",e=a?0:(f._data(b,d)||1)-1;e?f._data(b,d,e):(f.removeData(b,d,!0),n(b,c,"mark"))}},queue:function(a,b,c){var d;if(a){b=(b||"fx")+"queue",d=f._data(a,b),c&&(!d||f.isArray(c)?d=f._data(a,b,f.makeArray(c)):d.push(c));return d||[]}},dequeue:function(a,b){b=b||"fx";var c=f.queue(a,b),d=c.shift(),e={};d==="inprogress"&&(d=c.shift()),d&&(b==="fx"&&c.unshift("inprogress"),f._data(a,b+".run",e),d.call(a,function(){f.dequeue(a,b)},e)),c.length||(f.removeData(a,b+"queue "+b+".run",!0),n(a,b,"queue"))}}),f.fn.extend({queue:function(a,c){var d=2;typeof a!="string"&&(c=a,a="fx",d--);if(arguments.length<d)return f.queue(this[0],a);return c===b?this:this.each(function(){var b=f.queue(this,a,c);a==="fx"&&b[0]!=="inprogress"&&f.dequeue(this,a)})},dequeue:function(a){return this.each(function(){f.dequeue(this,a)})},delay:function(a,b){a=f.fx?f.fx.speeds[a]||a:a,b=b||"fx";return this.queue(b,function(b,c){var d=setTimeout(b,a);c.stop=function(){clearTimeout(d)}})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,c){function m(){--h||d.resolveWith(e,[e])}typeof a!="string"&&(c=a,a=b),a=a||"fx";var d=f.Deferred(),e=this,g=e.length,h=1,i=a+"defer",j=a+"queue",k=a+"mark",l;while(g--)if(l=f.data(e[g],i,b,!0)||(f.data(e[g],j,b,!0)||f.data(e[g],k,b,!0))&&f.data(e[g],i,f.Callbacks("once memory"),!0))h++,l.add(m);m();return d.promise(c)}});var o=/[\n\t\r]/g,p=/\s+/,q=/\r/g,r=/^(?:button|input)$/i,s=/^(?:button|input|object|select|textarea)$/i,t=/^a(?:rea)?$/i,u=/^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,v=f.support.getSetAttribute,w,x,y;f.fn.extend({attr:function(a,b){return f.access(this,f.attr,a,b,arguments.length>1)},removeAttr:function(a){return this.each(function(){f.removeAttr(this,a)})},prop:function(a,b){return f.access(this,f.prop,a,b,arguments.length>1)},removeProp:function(a){a=f.propFix[a]||a;return this.each(function(){try{this[a]=b,delete this[a]}catch(c){}})},addClass:function(a){var b,c,d,e,g,h,i;if(f.isFunction(a))return this.each(function(b){f(this).addClass(a.call(this,b,this.className))});if(a&&typeof a=="string"){b=a.split(p);for(c=0,d=this.length;c<d;c++){e=this[c];if(e.nodeType===1)if(!e.className&&b.length===1)e.className=a;else{g=" "+e.className+" ";for(h=0,i=b.length;h<i;h++)~g.indexOf(" "+b[h]+" ")||(g+=b[h]+" ");e.className=f.trim(g)}}}return this},removeClass:function(a){var c,d,e,g,h,i,j;if(f.isFunction(a))return this.each(function(b){f(this).removeClass(a.call(this,b,this.className))});if(a&&typeof a=="string"||a===b){c=(a||"").split(p);for(d=0,e=this.length;d<e;d++){g=this[d];if(g.nodeType===1&&g.className)if(a){h=(" "+g.className+" ").replace(o," ");for(i=0,j=c.length;i<j;i++)h=h.replace(" "+c[i]+" "," ");g.className=f.trim(h)}else g.className=""}}return this},toggleClass:function(a,b){var c=typeof a,d=typeof b=="boolean";if(f.isFunction(a))return this.each(function(c){f(this).toggleClass(a.call(this,c,this.className,b),b)});return this.each(function(){if(c==="string"){var e,g=0,h=f(this),i=b,j=a.split(p);while(e=j[g++])i=d?i:!h.hasClass(e),h[i?"addClass":"removeClass"](e)}else if(c==="undefined"||c==="boolean")this.className&&f._data(this,"__className__",this.className),this.className=this.className||a===!1?"":f._data(this,"__className__")||""})},hasClass:function(a){var b=" "+a+" ",c=0,d=this.length;for(;c<d;c++)if(this[c].nodeType===1&&(" "+this[c].className+" ").replace(o," ").indexOf(b)>-1)return!0;return!1},val:function(a){var c,d,e,g=this[0];{if(!!arguments.length){e=f.isFunction(a);return this.each(function(d){var g=f(this),h;if(this.nodeType===1){e?h=a.call(this,d,g.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.type]||f.valHooks[this.nodeName.toLowerCase()];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}if(g){c=f.valHooks[g.type]||f.valHooks[g.nodeName.toLowerCase()];if(c&&"get"in c&&(d=c.get(g,"value"))!==b)return d;d=g.value;return typeof d=="string"?d.replace(q,""):d==null?"":d}}}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,g=a.selectedIndex,h=[],i=a.options,j=a.type==="select-one";if(g<0)return null;c=j?g:0,d=j?g+1:i.length;for(;c<d;c++){e=i[c];if(e.selected&&(f.support.optDisabled?!e.disabled:e.getAttribute("disabled")===null)&&(!e.parentNode.disabled||!f.nodeName(e.parentNode,"optgroup"))){b=f(e).val();if(j)return b;h.push(b)}}if(j&&!h.length&&i.length)return f(i[g]).val();return h},set:function(a,b){var c=f.makeArray(b);f(a).find("option").each(function(){this.selected=f.inArray(f(this).val(),c)>=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attr:function(a,c,d,e){var g,h,i,j=a.nodeType;if(!!a&&j!==3&&j!==8&&j!==2){if(e&&c in f.attrFn)return f(a)[c](d);if(typeof a.getAttribute=="undefined")return f.prop(a,c,d);i=j!==1||!f.isXMLDoc(a),i&&(c=c.toLowerCase(),h=f.attrHooks[c]||(u.test(c)?x:w));if(d!==b){if(d===null){f.removeAttr(a,c);return}if(h&&"set"in h&&i&&(g=h.set(a,d,c))!==b)return g;a.setAttribute(c,""+d);return d}if(h&&"get"in h&&i&&(g=h.get(a,c))!==null)return g;g=a.getAttribute(c);return g===null?b:g}},removeAttr:function(a,b){var c,d,e,g,h,i=0;if(b&&a.nodeType===1){d=b.toLowerCase().split(p),g=d.length;for(;i<g;i++)e=d[i],e&&(c=f.propFix[e]||e,h=u.test(e),h||f.attr(a,e,""),a.removeAttribute(v?e:c),h&&c in a&&(a[c]=!1))}},attrHooks:{type:{set:function(a,b){if(r.test(a.nodeName)&&a.parentNode)f.error("type property can't be changed");else if(!f.support.radioValue&&b==="radio"&&f.nodeName(a,"input")){var c=a.value;a.setAttribute("type",b),c&&(a.value=c);return b}}},value:{get:function(a,b){if(w&&f.nodeName(a,"button"))return w.get(a,b);return b in a?a.value:null},set:function(a,b,c){if(w&&f.nodeName(a,"button"))return w.set(a,b,c);a.value=b}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(a,c,d){var e,g,h,i=a.nodeType;if(!!a&&i!==3&&i!==8&&i!==2){h=i!==1||!f.isXMLDoc(a),h&&(c=f.propFix[c]||c,g=f.propHooks[c]);return d!==b?g&&"set"in g&&(e=g.set(a,d,c))!==b?e:a[c]=d:g&&"get"in g&&(e=g.get(a,c))!==null?e:a[c]}},propHooks:{tabIndex:{get:function(a){var c=a.getAttributeNode("tabindex");return c&&c.specified?parseInt(c.value,10):s.test(a.nodeName)||t.test(a.nodeName)&&a.href?0:b}}}}),f.attrHooks.tabindex=f.propHooks.tabIndex,x={get:function(a,c){var d,e=f.prop(a,c);return e===!0||typeof e!="boolean"&&(d=a.getAttributeNode(c))&&d.nodeValue!==!1?c.toLowerCase():b},set:function(a,b,c){var d;b===!1?f.removeAttr(a,c):(d=f.propFix[c]||c,d in a&&(a[d]=!0),a.setAttribute(c,c.toLowerCase()));return c}},v||(y={name:!0,id:!0,coords:!0},w=f.valHooks.button={get:function(a,c){var d;d=a.getAttributeNode(c);return d&&(y[c]?d.nodeValue!=="":d.specified)?d.nodeValue:b},set:function(a,b,d){var e=a.getAttributeNode(d);e||(e=c.createAttribute(d),a.setAttributeNode(e));return e.nodeValue=b+""}},f.attrHooks.tabindex.set=w.set,f.each(["width","height"],function(a,b){f.attrHooks[b]=f.extend(f.attrHooks[b],{set:function(a,c){if(c===""){a.setAttribute(b,"auto");return c}}})}),f.attrHooks.contenteditable={get:w.get,set:function(a,b,c){b===""&&(b="false"),w.set(a,b,c)}}),f.support.hrefNormalized||f.each(["href","src","width","height"],function(a,c){f.attrHooks[c]=f.extend(f.attrHooks[c],{get:function(a){var d=a.getAttribute(c,2);return d===null?b:d}})}),f.support.style||(f.attrHooks.style={get:function(a){return a.style.cssText.toLowerCase()||b},set:function(a,b){return a.style.cssText=""+b}}),f.support.optSelected||(f.propHooks.selected=f.extend(f.propHooks.selected,{get:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex);return null}})),f.support.enctype||(f.propFix.enctype="encoding"),f.support.checkOn||f.each(["radio","checkbox"],function(){f.valHooks[this]={get:function(a){return a.getAttribute("value")===null?"on":a.value}}}),f.each(["radio","checkbox"],function(){f.valHooks[this]=f.extend(f.valHooks[this],{set:function(a,b){if(f.isArray(b))return a.checked=f.inArray(f(a).val(),b)>=0}})});var z=/^(?:textarea|input|select)$/i,A=/^([^\.]*)?(?:\.(.+))?$/,B=/(?:^|\s)hover(\.\S+)?\b/,C=/^key/,D=/^(?:mouse|contextmenu)|click/,E=/^(?:focusinfocus|focusoutblur)$/,F=/^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,G=function(
-a){var b=F.exec(a);b&&(b[1]=(b[1]||"").toLowerCase(),b[3]=b[3]&&new RegExp("(?:^|\\s)"+b[3]+"(?:\\s|$)"));return b},H=function(a,b){var c=a.attributes||{};return(!b[1]||a.nodeName.toLowerCase()===b[1])&&(!b[2]||(c.id||{}).value===b[2])&&(!b[3]||b[3].test((c["class"]||{}).value))},I=function(a){return f.event.special.hover?a:a.replace(B,"mouseenter$1 mouseleave$1")};f.event={add:function(a,c,d,e,g){var h,i,j,k,l,m,n,o,p,q,r,s;if(!(a.nodeType===3||a.nodeType===8||!c||!d||!(h=f._data(a)))){d.handler&&(p=d,d=p.handler,g=p.selector),d.guid||(d.guid=f.guid++),j=h.events,j||(h.events=j={}),i=h.handle,i||(h.handle=i=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.dispatch.apply(i.elem,arguments):b},i.elem=a),c=f.trim(I(c)).split(" ");for(k=0;k<c.length;k++){l=A.exec(c[k])||[],m=l[1],n=(l[2]||"").split(".").sort(),s=f.event.special[m]||{},m=(g?s.delegateType:s.bindType)||m,s=f.event.special[m]||{},o=f.extend({type:m,origType:l[1],data:e,handler:d,guid:d.guid,selector:g,quick:g&&G(g),namespace:n.join(".")},p),r=j[m];if(!r){r=j[m]=[],r.delegateCount=0;if(!s.setup||s.setup.call(a,e,n,i)===!1)a.addEventListener?a.addEventListener(m,i,!1):a.attachEvent&&a.attachEvent("on"+m,i)}s.add&&(s.add.call(a,o),o.handler.guid||(o.handler.guid=d.guid)),g?r.splice(r.delegateCount++,0,o):r.push(o),f.event.global[m]=!0}a=null}},global:{},remove:function(a,b,c,d,e){var g=f.hasData(a)&&f._data(a),h,i,j,k,l,m,n,o,p,q,r,s;if(!!g&&!!(o=g.events)){b=f.trim(I(b||"")).split(" ");for(h=0;h<b.length;h++){i=A.exec(b[h])||[],j=k=i[1],l=i[2];if(!j){for(j in o)f.event.remove(a,j+b[h],c,d,!0);continue}p=f.event.special[j]||{},j=(d?p.delegateType:p.bindType)||j,r=o[j]||[],m=r.length,l=l?new RegExp("(^|\\.)"+l.split(".").sort().join("\\.(?:.*\\.)?")+"(\\.|$)"):null;for(n=0;n<r.length;n++)s=r[n],(e||k===s.origType)&&(!c||c.guid===s.guid)&&(!l||l.test(s.namespace))&&(!d||d===s.selector||d==="**"&&s.selector)&&(r.splice(n--,1),s.selector&&r.delegateCount--,p.remove&&p.remove.call(a,s));r.length===0&&m!==r.length&&((!p.teardown||p.teardown.call(a,l)===!1)&&f.removeEvent(a,j,g.handle),delete o[j])}f.isEmptyObject(o)&&(q=g.handle,q&&(q.elem=null),f.removeData(a,["events","handle"],!0))}},customEvent:{getData:!0,setData:!0,changeData:!0},trigger:function(c,d,e,g){if(!e||e.nodeType!==3&&e.nodeType!==8){var h=c.type||c,i=[],j,k,l,m,n,o,p,q,r,s;if(E.test(h+f.event.triggered))return;h.indexOf("!")>=0&&(h=h.slice(0,-1),k=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i.shift(),i.sort());if((!e||f.event.customEvent[h])&&!f.event.global[h])return;c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.isTrigger=!0,c.exclusive=k,c.namespace=i.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)"):null,o=h.indexOf(":")<0?"on"+h:"";if(!e){j=f.cache;for(l in j)j[l].events&&j[l].events[h]&&f.event.trigger(c,d,j[l].handle.elem,!0);return}c.result=b,c.target||(c.target=e),d=d!=null?f.makeArray(d):[],d.unshift(c),p=f.event.special[h]||{};if(p.trigger&&p.trigger.apply(e,d)===!1)return;r=[[e,p.bindType||h]];if(!g&&!p.noBubble&&!f.isWindow(e)){s=p.delegateType||h,m=E.test(s+h)?e:e.parentNode,n=null;for(;m;m=m.parentNode)r.push([m,s]),n=m;n&&n===e.ownerDocument&&r.push([n.defaultView||n.parentWindow||a,s])}for(l=0;l<r.length&&!c.isPropagationStopped();l++)m=r[l][0],c.type=r[l][1],q=(f._data(m,"events")||{})[c.type]&&f._data(m,"handle"),q&&q.apply(m,d),q=o&&m[o],q&&f.acceptData(m)&&q.apply(m,d)===!1&&c.preventDefault();c.type=h,!g&&!c.isDefaultPrevented()&&(!p._default||p._default.apply(e.ownerDocument,d)===!1)&&(h!=="click"||!f.nodeName(e,"a"))&&f.acceptData(e)&&o&&e[h]&&(h!=="focus"&&h!=="blur"||c.target.offsetWidth!==0)&&!f.isWindow(e)&&(n=e[o],n&&(e[o]=null),f.event.triggered=h,e[h](),f.event.triggered=b,n&&(e[o]=n));return c.result}},dispatch:function(c){c=f.event.fix(c||a.event);var d=(f._data(this,"events")||{})[c.type]||[],e=d.delegateCount,g=[].slice.call(arguments,0),h=!c.exclusive&&!c.namespace,i=f.event.special[c.type]||{},j=[],k,l,m,n,o,p,q,r,s,t,u;g[0]=c,c.delegateTarget=this;if(!i.preDispatch||i.preDispatch.call(this,c)!==!1){if(e&&(!c.button||c.type!=="click")){n=f(this),n.context=this.ownerDocument||this;for(m=c.target;m!=this;m=m.parentNode||this)if(m.disabled!==!0){p={},r=[],n[0]=m;for(k=0;k<e;k++)s=d[k],t=s.selector,p[t]===b&&(p[t]=s.quick?H(m,s.quick):n.is(t)),p[t]&&r.push(s);r.length&&j.push({elem:m,matches:r})}}d.length>e&&j.push({elem:this,matches:d.slice(e)});for(k=0;k<j.length&&!c.isPropagationStopped();k++){q=j[k],c.currentTarget=q.elem;for(l=0;l<q.matches.length&&!c.isImmediatePropagationStopped();l++){s=q.matches[l];if(h||!c.namespace&&!s.namespace||c.namespace_re&&c.namespace_re.test(s.namespace))c.data=s.data,c.handleObj=s,o=((f.event.special[s.origType]||{}).handle||s.handler).apply(q.elem,g),o!==b&&(c.result=o,o===!1&&(c.preventDefault(),c.stopPropagation()))}}i.postDispatch&&i.postDispatch.call(this,c);return c.result}},props:"attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(a,b){a.which==null&&(a.which=b.charCode!=null?b.charCode:b.keyCode);return a}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(a,d){var e,f,g,h=d.button,i=d.fromElement;a.pageX==null&&d.clientX!=null&&(e=a.target.ownerDocument||c,f=e.documentElement,g=e.body,a.pageX=d.clientX+(f&&f.scrollLeft||g&&g.scrollLeft||0)-(f&&f.clientLeft||g&&g.clientLeft||0),a.pageY=d.clientY+(f&&f.scrollTop||g&&g.scrollTop||0)-(f&&f.clientTop||g&&g.clientTop||0)),!a.relatedTarget&&i&&(a.relatedTarget=i===a.target?d.toElement:i),!a.which&&h!==b&&(a.which=h&1?1:h&2?3:h&4?2:0);return a}},fix:function(a){if(a[f.expando])return a;var d,e,g=a,h=f.event.fixHooks[a.type]||{},i=h.props?this.props.concat(h.props):this.props;a=f.Event(g);for(d=i.length;d;)e=i[--d],a[e]=g[e];a.target||(a.target=g.srcElement||c),a.target.nodeType===3&&(a.target=a.target.parentNode),a.metaKey===b&&(a.metaKey=a.ctrlKey);return h.filter?h.filter(a,g):a},special:{ready:{setup:f.bindReady},load:{noBubble:!0},focus:{delegateType:"focusin"},blur:{delegateType:"focusout"},beforeunload:{setup:function(a,b,c){f.isWindow(this)&&(this.onbeforeunload=c)},teardown:function(a,b){this.onbeforeunload===b&&(this.onbeforeunload=null)}}},simulate:function(a,b,c,d){var e=f.extend(new f.Event,c,{type:a,isSimulated:!0,originalEvent:{}});d?f.event.trigger(e,null,b):f.event.dispatch.call(b,e),e.isDefaultPrevented()&&c.preventDefault()}},f.event.handle=f.event.dispatch,f.removeEvent=c.removeEventListener?function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c,!1)}:function(a,b,c){a.detachEvent&&a.detachEvent("on"+b,c)},f.Event=function(a,b){if(!(this instanceof f.Event))return new f.Event(a,b);a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||a.returnValue===!1||a.getPreventDefault&&a.getPreventDefault()?K:J):this.type=a,b&&f.extend(this,b),this.timeStamp=a&&a.timeStamp||f.now(),this[f.expando]=!0},f.Event.prototype={preventDefault:function(){this.isDefaultPrevented=K;var a=this.originalEvent;!a||(a.preventDefault?a.preventDefault():a.returnValue=!1)},stopPropagation:function(){this.isPropagationStopped=K;var a=this.originalEvent;!a||(a.stopPropagation&&a.stopPropagation(),a.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=K,this.stopPropagation()},isDefaultPrevented:J,isPropagationStopped:J,isImmediatePropagationStopped:J},f.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){f.event.special[a]={delegateType:b,bindType:b,handle:function(a){var c=this,d=a.relatedTarget,e=a.handleObj,g=e.selector,h;if(!d||d!==c&&!f.contains(c,d))a.type=e.origType,h=e.handler.apply(this,arguments),a.type=b;return h}}}),f.support.submitBubbles||(f.event.special.submit={setup:function(){if(f.nodeName(this,"form"))return!1;f.event.add(this,"click._submit keypress._submit",function(a){var c=a.target,d=f.nodeName(c,"input")||f.nodeName(c,"button")?c.form:b;d&&!d._submit_attached&&(f.event.add(d,"submit._submit",function(a){a._submit_bubble=!0}),d._submit_attached=!0)})},postDispatch:function(a){a._submit_bubble&&(delete a._submit_bubble,this.parentNode&&!a.isTrigger&&f.event.simulate("submit",this.parentNode,a,!0))},teardown:function(){if(f.nodeName(this,"form"))return!1;f.event.remove(this,"._submit")}}),f.support.changeBubbles||(f.event.special.change={setup:function(){if(z.test(this.nodeName)){if(this.type==="checkbox"||this.type==="radio")f.event.add(this,"propertychange._change",function(a){a.originalEvent.propertyName==="checked"&&(this._just_changed=!0)}),f.event.add(this,"click._change",function(a){this._just_changed&&!a.isTrigger&&(this._just_changed=!1,f.event.simulate("change",this,a,!0))});return!1}f.event.add(this,"beforeactivate._change",function(a){var b=a.target;z.test(b.nodeName)&&!b._change_attached&&(f.event.add(b,"change._change",function(a){this.parentNode&&!a.isSimulated&&!a.isTrigger&&f.event.simulate("change",this.parentNode,a,!0)}),b._change_attached=!0)})},handle:function(a){var b=a.target;if(this!==b||a.isSimulated||a.isTrigger||b.type!=="radio"&&b.type!=="checkbox")return a.handleObj.handler.apply(this,arguments)},teardown:function(){f.event.remove(this,"._change");return z.test(this.nodeName)}}),f.support.focusinBubbles||f.each({focus:"focusin",blur:"focusout"},function(a,b){var d=0,e=function(a){f.event.simulate(b,a.target,f.event.fix(a),!0)};f.event.special[b]={setup:function(){d++===0&&c.addEventListener(a,e,!0)},teardown:function(){--d===0&&c.removeEventListener(a,e,!0)}}}),f.fn.extend({on:function(a,c,d,e,g){var h,i;if(typeof a=="object"){typeof c!="string"&&(d=d||c,c=b);for(i in a)this.on(i,c,d,a[i],g);return this}d==null&&e==null?(e=c,d=c=b):e==null&&(typeof c=="string"?(e=d,d=b):(e=d,d=c,c=b));if(e===!1)e=J;else if(!e)return this;g===1&&(h=e,e=function(a){f().off(a);return h.apply(this,arguments)},e.guid=h.guid||(h.guid=f.guid++));return this.each(function(){f.event.add(this,a,e,d,c)})},one:function(a,b,c,d){return this.on(a,b,c,d,1)},off:function(a,c,d){if(a&&a.preventDefault&&a.handleObj){var e=a.handleObj;f(a.delegateTarget).off(e.namespace?e.origType+"."+e.namespace:e.origType,e.selector,e.handler);return this}if(typeof a=="object"){for(var g in a)this.off(g,c,a[g]);return this}if(c===!1||typeof c=="function")d=c,c=b;d===!1&&(d=J);return this.each(function(){f.event.remove(this,a,d,c)})},bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},live:function(a,b,c){f(this.context).on(a,this.selector,b,c);return this},die:function(a,b){f(this.context).off(a,this.selector||"**",b);return this},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return arguments.length==1?this.off(a,"**"):this.off(b,a,c)},trigger:function(a,b){return this.each(function(){f.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0])return f.event.trigger(a,b,this[0],!0)},toggle:function(a){var b=arguments,c=a.guid||f.guid++,d=0,e=function(c){var e=(f._data(this,"lastToggle"+a.guid)||0)%d;f._data(this,"lastToggle"+a.guid,e+1),c.preventDefault();return b[e].apply(this,arguments)||!1};e.guid=c;while(d<b.length)b[d++].guid=c;return this.click(e)},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}}),f.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(a,b){f.fn[b]=function(a,c){c==null&&(c=a,a=null);return arguments.length>0?this.on(b,null,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0),C.test(b)&&(f.event.fixHooks[b]=f.event.keyHooks),D.test(b)&&(f.event.fixHooks[b]=f.event.mouseHooks)}),function(){function x(a,b,c,e,f,g){for(var h=0,i=e.length;h<i;h++){var j=e[h];if(j){var k=!1;j=j[a];while(j){if(j[d]===c){k=e[j.sizset];break}if(j.nodeType===1){g||(j[d]=c,j.sizset=h);if(typeof b!="string"){if(j===b){k=!0;break}}else if(m.filter(b,[j]).length>0){k=j;break}}j=j[a]}e[h]=k}}}function w(a,b,c,e,f,g){for(var h=0,i=e.length;h<i;h++){var j=e[h];if(j){var k=!1;j=j[a];while(j){if(j[d]===c){k=e[j.sizset];break}j.nodeType===1&&!g&&(j[d]=c,j.sizset=h);if(j.nodeName.toLowerCase()===b){k=j;break}j=j[a]}e[h]=k}}}var a=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d="sizcache"+(Math.random()+"").replace(".",""),e=0,g=Object.prototype.toString,h=!1,i=!0,j=/\\/g,k=/\r\n/g,l=/\W/;[0,0].sort(function(){i=!1;return 0});var m=function(b,d,e,f){e=e||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return e;var i,j,k,l,n,q,r,t,u=!0,v=m.isXML(d),w=[],x=b;do{a.exec(""),i=a.exec(x);if(i){x=i[3],w.push(i[1]);if(i[2]){l=i[3];break}}}while(i);if(w.length>1&&p.exec(b))if(w.length===2&&o.relative[w[0]])j=y(w[0]+w[1],d,f);else{j=o.relative[w[0]]?[d]:m(w.shift(),d);while(w.length)b=w.shift(),o.relative[b]&&(b+=w.shift()),j=y(b,j,f)}else{!f&&w.length>1&&d.nodeType===9&&!v&&o.match.ID.test(w[0])&&!o.match.ID.test(w[w.length-1])&&(n=m.find(w.shift(),d,v),d=n.expr?m.filter(n.expr,n.set)[0]:n.set[0]);if(d){n=f?{expr:w.pop(),set:s(f)}:m.find(w.pop(),w.length===1&&(w[0]==="~"||w[0]==="+")&&d.parentNode?d.parentNode:d,v),j=n.expr?m.filter(n.expr,n.set):n.set,w.length>0?k=s(j):u=!1;while(w.length)q=w.pop(),r=q,o.relative[q]?r=w.pop():q="",r==null&&(r=d),o.relative[q](k,r,v)}else k=w=[]}k||(k=j),k||m.error(q||b);if(g.call(k)==="[object Array]")if(!u)e.push.apply(e,k);else if(d&&d.nodeType===1)for(t=0;k[t]!=null;t++)k[t]&&(k[t]===!0||k[t].nodeType===1&&m.contains(d,k[t]))&&e.push(j[t]);else for(t=0;k[t]!=null;t++)k[t]&&k[t].nodeType===1&&e.push(j[t]);else s(k,e);l&&(m(l,h,e,f),m.uniqueSort(e));return e};m.uniqueSort=function(a){if(u){h=i,a.sort(u);if(h)for(var b=1;b<a.length;b++)a[b]===a[b-1]&&a.splice(b--,1)}return a},m.matches=function(a,b){return m(a,null,null,b)},m.matchesSelector=function(a,b){return m(b,null,null,[a]).length>0},m.find=function(a,b,c){var d,e,f,g,h,i;if(!a)return[];for(e=0,f=o.order.length;e<f;e++){h=o.order[e];if(g=o.leftMatch[h].exec(a)){i=g[1],g.splice(1,1);if(i.substr(i.length-1)!=="\\"){g[1]=(g[1]||"").replace(j,""),d=o.find[h](g,b,c);if(d!=null){a=a.replace(o.match[h],"");break}}}}d||(d=typeof b.getElementsByTagName!="undefined"?b.getElementsByTagName("*"):[]);return{set:d,expr:a}},m.filter=function(a,c,d,e){var f,g,h,i,j,k,l,n,p,q=a,r=[],s=c,t=c&&c[0]&&m.isXML(c[0]);while(a&&c.length){for(h in o.filter)if((f=o.leftMatch[h].exec(a))!=null&&f[2]){k=o.filter[h],l=f[1],g=!1,f.splice(1,1);if(l.substr(l.length-1)==="\\")continue;s===r&&(r=[]);if(o.preFilter[h]){f=o.preFilter[h](f,s,d,r,e,t);if(!f)g=i=!0;else if(f===!0)continue}if(f)for(n=0;(j=s[n])!=null;n++)j&&(i=k(j,f,n,s),p=e^i,d&&i!=null?p?g=!0:s[n]=!1:p&&(r.push(j),g=!0));if(i!==b){d||(s=r),a=a.replace(o.match[h],"");if(!g)return[];break}}if(a===q)if(g==null)m.error(a);else break;q=a}return s},m.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)};var n=m.getText=function(a){var b,c,d=a.nodeType,e="";if(d){if(d===1||d===9||d===11){if(typeof a.textContent=="string")return a.textContent;if(typeof a.innerText=="string")return a.innerText.replace(k,"");for(a=a.firstChild;a;a=a.nextSibling)e+=n(a)}else if(d===3||d===4)return a.nodeValue}else for(b=0;c=a[b];b++)c.nodeType!==8&&(e+=n(c));return e},o=m.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(a){return a.getAttribute("href")},type:function(a){return a.getAttribute("type")}},relative:{"+":function(a,b){var c=typeof b=="string",d=c&&!l.test(b),e=c&&!d;d&&(b=b.toLowerCase());for(var f=0,g=a.length,h;f<g;f++)if(h=a[f]){while((h=h.previousSibling)&&h.nodeType!==1);a[f]=e||h&&h.nodeName.toLowerCase()===b?h||!1:h===b}e&&m.filter(b,a,!0)},">":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!l.test(b)){b=b.toLowerCase();for(;e<f;e++){c=a[e];if(c){var g=c.parentNode;a[e]=g.nodeName.toLowerCase()===b?g:!1}}}else{for(;e<f;e++)c=a[e],c&&(a[e]=d?c.parentNode:c.parentNode===b);d&&m.filter(b,a,!0)}},"":function(a,b,c){var d,f=e++,g=x;typeof b=="string"&&!l.test(b)&&(b=b.toLowerCase(),d=b,g=w),g("parentNode",b,f,a,d,c)},"~":function(a,b,c){var d,f=e++,g=x;typeof b=="string"&&!l.test(b)&&(b=b.toLowerCase(),d=b,g=w),g("previousSibling",b,f,a,d,c)}},find:{ID:function(a,b,c){if(typeof b.getElementById!="undefined"&&!c){var d=b.getElementById(a[1]);return d&&d.parentNode?[d]:[]}},NAME:function(a,b){if(typeof b.getElementsByName!="undefined"){var c=[],d=b.getElementsByName(a[1]);for(var e=0,f=d.length;e<f;e++)d[e].getAttribute("name")===a[1]&&c.push(d[e]);return c.length===0?null:c}},TAG:function(a,b){if(typeof b.getElementsByTagName!="undefined")return b.getElementsByTagName(a[1])}},preFilter:{CLASS:function(a,b,c,d,e,f){a=" "+a[1].replace(j,"")+" ";if(f)return a;for(var g=0,h;(h=b[g])!=null;g++)h&&(e^(h.className&&(" "+h.className+" ").replace(/[\t\n\r]/g," ").indexOf(a)>=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(j,"")},TAG:function(a,b){return a[1].replace(j,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||m.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&m.error(a[0]);a[0]=e++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(j,"");!f&&o.attrMap[g]&&(a[1]=o.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(j,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=m(b[3],null,null,c);else{var g=m.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(o.match.POS.test(b[0])||o.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!m(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return b<c[3]-0},gt:function(a,b,c){return b>c[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=o.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||n([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h<i;h++)if(g[h]===a)return!1;return!0}m.error(e)},CHILD:function(a,b){var c,e,f,g,h,i,j,k=b[1],l=a;switch(k){case"only":case"first":while(l=l.previousSibling)if(l.nodeType===1)return!1;if(k==="first")return!0;l=a;case"last":while(l=l.nextSibling)if(l.nodeType===1)return!1;return!0;case"nth":c=b[2],e=b[3];if(c===1&&e===0)return!0;f=b[0],g=a.parentNode;if(g&&(g[d]!==f||!a.nodeIndex)){i=0;for(l=g.firstChild;l;l=l.nextSibling)l.nodeType===1&&(l.nodeIndex=++i);g[d]=f}j=a.nodeIndex-e;return c===0?j===0:j%c===0&&j/c>=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||!!a.nodeName&&a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=m.attr?m.attr(a,c):o.attrHandle[c]?o.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":!f&&m.attr?d!=null:f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=o.setFilters[e];if(f)return f(a,c,b,d)}}},p=o.match.POS,q=function(a,b){return"\\"+(b-0+1)};for(var r in o.match)o.match[r]=new RegExp(o.match[r].source+/(?![^\[]*\])(?![^\(]*\))/.source),o.leftMatch[r]=new RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[r].source.replace(/\\(\d+)/g,q));o.match.globalPOS=p;var s=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(t){s=function(a,b){var c=0,d=b||[];if(g.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var e=a.length;c<e;c++)d.push(a[c]);else for(;a[c];c++)d.push(a[c]);return d}}var u,v;c.documentElement.compareDocumentPosition?u=function(a,b){if(a===b){h=!0;return 0}if(!a.compareDocumentPosition||!b.compareDocumentPosition)return a.compareDocumentPosition?-1:1;return a.compareDocumentPosition(b)&4?-1:1}:(u=function(a,b){if(a===b){h=!0;return 0}if(a.sourceIndex&&b.sourceIndex)return a.sourceIndex-b.sourceIndex;var c,d,e=[],f=[],g=a.parentNode,i=b.parentNode,j=g;if(g===i)return v(a,b);if(!g)return-1;if(!i)return 1;while(j)e.unshift(j),j=j.parentNode;j=i;while(j)f.unshift(j),j=j.parentNode;c=e.length,d=f.length;for(var k=0;k<c&&k<d;k++)if(e[k]!==f[k])return v(e[k],f[k]);return k===c?v(a,f[k],-1):v(e[k],b,1)},v=function(a,b,c){if(a===b)return c;var d=a.nextSibling;while(d){if(d===b)return-1;d=d.nextSibling}return 1}),function(){var a=c.createElement("div"),d="script"+(new Date).getTime(),e=c.documentElement;a.innerHTML="<a name='"+d+"'/>",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(o.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},o.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(o.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="<a href='#'></a>",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(o.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=m,b=c.createElement("div"),d="__sizzle__";b.innerHTML="<p class='TEST'></p>";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){m=function(b,e,f,g){e=e||c;if(!g&&!m.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return s(e.getElementsByTagName(b),f);if(h[2]&&o.find.CLASS&&e.getElementsByClassName)return s(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return s([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return s([],f);if(i.id===h[3])return s([i],f)}try{return s(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var k=e,l=e.getAttribute("id"),n=l||d,p=e.parentNode,q=/^\s*[+~]/.test(b);l?n=n.replace(/'/g,"\\$&"):e.setAttribute("id",n),q&&p&&(e=e.parentNode);try{if(!q||p)return s(e.querySelectorAll("[id='"+n+"'] "+b),f)}catch(r){}finally{l||k.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)m[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}m.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!m.isXML(a))try{if(e||!o.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return m(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="<div class='test e'></div><div class='test'></div>";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;o.order.splice(1,0,"CLASS"),o.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?m.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?m.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:m.contains=function(){return!1},m.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var y=function(a,b,c){var d,e=[],f="",g=b.nodeType?[b]:b;while(d=o.match.PSEUDO.exec(a))f+=d[0],a=a.replace(o.match.PSEUDO,"");a=o.relative[a]?a+"*":a;for(var h=0,i=g.length;h<i;h++)m(a,g[h],e,c);return m.filter(f,e)};m.attr=f.attr,m.selectors.attrMap={},f.find=m,f.expr=m.selectors,f.expr[":"]=f.expr.filters,f.unique=m.uniqueSort,f.text=m.getText,f.isXMLDoc=m.isXML,f.contains=m.contains}();var L=/Until$/,M=/^(?:parents|prevUntil|prevAll)/,N=/,/,O=/^.[^:#\[\.,]*$/,P=Array.prototype.slice,Q=f.expr.match.globalPOS,R={children:!0,contents:!0,next:!0,prev:!0};f.fn.extend({find:function(a){var b=this,c,d;if(typeof a!="string")return f(a).filter(function(){for(c=0,d=b.length;c<d;c++)if(f.contains(b[c],this))return!0});var e=this.pushStack("","find",a),g,h,i;for(c=0,d=this.length;c<d;c++){g=e.length,f.find(a,this[c],e);if(c>0)for(h=g;h<e.length;h++)for(i=0;i<g;i++)if(e[i]===e[h]){e.splice(h--,1);break}}return e},has:function(a){var b=f(a);return this.filter(function(){for(var a=0,c=b.length;a<c;a++)if(f.contains(this,b[a]))return!0})},not:function(a){return this.pushStack(T(this,a,!1),"not",a)},filter:function(a){return this.pushStack(T(this,a,!0),"filter",a)},is:function(a){return!!a&&(typeof a=="string"?Q.test(a)?f(a,this.context).index(this[0])>=0:f.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h=1;while(g&&g.ownerDocument&&g!==b){for(d=0;d<a.length;d++)f(g).is(a[d])&&c.push({selector:a[d],elem:g,level:h});g=g.parentNode,h++}return c}var i=Q.test(a)||typeof a!="string"?f(a,b||this.context):0;for(d=0,e=this.length;d<e;d++){g=this[d];while(g){if(i?i.index(g)>-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a)return this[0]&&this[0].parentNode?this.prevAll().length:-1;if(typeof a=="string")return f.inArray(this[0],f(a));return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(S(c[0])||S(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c);L.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!R[a]?f.unique(e):e,(this.length>1||N.test(d))&&M.test(a)&&(e=e.reverse());return this.pushStack(e,a,P.call(arguments).join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var V="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",W=/ jQuery\d+="(?:\d+|null)"/g,X=/^\s+/,Y=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Z=/<([\w:]+)/,$=/<tbody/i,_=/<|&#?\w+;/,ba=/<(?:script|style)/i,bb=/<(?:script|object|embed|option|style)/i,bc=new RegExp("<(?:"+V+")[\\s/>]","i"),bd=/checked\s*(?:[^=]|=\s*.checked.)/i,be=/\/(java|ecma)script/i,bf=/^\s*<!(?:\[CDATA\[|\-\-)/,bg={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]},bh=U(c);bg.optgroup=bg.option,bg.tbody=bg.tfoot=bg.colgroup=bg.caption=bg.thead,bg.th=bg.td,f.support.htmlSerialize||(bg._default=[1,"div<div>","</div>"]),f.fn.extend({text:function(a){return f.access(this,function(a){return a===b?f.text(this):this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a))},null,a,arguments.length)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=f.isFunction(a);return this.each(function(c){f(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f
-.clean(arguments);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f.clean(arguments));return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){return f.access(this,function(a){var c=this[0]||{},d=0,e=this.length;if(a===b)return c.nodeType===1?c.innerHTML.replace(W,""):null;if(typeof a=="string"&&!ba.test(a)&&(f.support.leadingWhitespace||!X.test(a))&&!bg[(Z.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Y,"<$1></$2>");try{for(;d<e;d++)c=this[d]||{},c.nodeType===1&&(f.cleanData(c.getElementsByTagName("*")),c.innerHTML=a);c=0}catch(g){}}c&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(a){if(this[0]&&this[0].parentNode){if(f.isFunction(a))return this.each(function(b){var c=f(this),d=c.html();c.replaceWith(a.call(this,b,d))});typeof a!="string"&&(a=f(a).detach());return this.each(function(){var b=this.nextSibling,c=this.parentNode;f(this).remove(),b?f(b).before(a):f(c).append(a)})}return this.length?this.pushStack(f(f.isFunction(a)?a():a),"replaceWith",a):this},detach:function(a){return this.remove(a,!0)},domManip:function(a,c,d){var e,g,h,i,j=a[0],k=[];if(!f.support.checkClone&&arguments.length===3&&typeof j=="string"&&bd.test(j))return this.each(function(){f(this).domManip(a,c,d,!0)});if(f.isFunction(j))return this.each(function(e){var g=f(this);a[0]=j.call(this,e,c?g.html():b),g.domManip(a,c,d)});if(this[0]){i=j&&j.parentNode,f.support.parentNode&&i&&i.nodeType===11&&i.childNodes.length===this.length?e={fragment:i}:e=f.buildFragment(a,this,k),h=e.fragment,h.childNodes.length===1?g=h=h.firstChild:g=h.firstChild;if(g){c=c&&f.nodeName(g,"tr");for(var l=0,m=this.length,n=m-1;l<m;l++)d.call(c?bi(this[l],g):this[l],e.cacheable||m>1&&l<n?f.clone(h,!0,!0):h)}k.length&&f.each(k,function(a,b){b.src?f.ajax({type:"GET",global:!1,url:b.src,async:!1,dataType:"script"}):f.globalEval((b.text||b.textContent||b.innerHTML||"").replace(bf,"/*$0*/")),b.parentNode&&b.parentNode.removeChild(b)})}return this}}),f.buildFragment=function(a,b,d){var e,g,h,i,j=a[0];b&&b[0]&&(i=b[0].ownerDocument||b[0]),i.createDocumentFragment||(i=c),a.length===1&&typeof j=="string"&&j.length<512&&i===c&&j.charAt(0)==="<"&&!bb.test(j)&&(f.support.checkClone||!bd.test(j))&&(f.support.html5Clone||!bc.test(j))&&(g=!0,h=f.fragments[j],h&&h!==1&&(e=h)),e||(e=i.createDocumentFragment(),f.clean(a,i,e,d)),g&&(f.fragments[j]=h?e:1);return{fragment:e,cacheable:g}},f.fragments={},f.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){f.fn[a]=function(c){var d=[],e=f(c),g=this.length===1&&this[0].parentNode;if(g&&g.nodeType===11&&g.childNodes.length===1&&e.length===1){e[b](this[0]);return this}for(var h=0,i=e.length;h<i;h++){var j=(h>0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d,e,g,h=f.support.html5Clone||f.isXMLDoc(a)||!bc.test("<"+a.nodeName+">")?a.cloneNode(!0):bo(a);if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bk(a,h),d=bl(a),e=bl(h);for(g=0;d[g];++g)e[g]&&bk(d[g],e[g])}if(b){bj(a,h);if(c){d=bl(a),e=bl(h);for(g=0;d[g];++g)bj(d[g],e[g])}}d=e=null;return h},clean:function(a,b,d,e){var g,h,i,j=[];b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);for(var k=0,l;(l=a[k])!=null;k++){typeof l=="number"&&(l+="");if(!l)continue;if(typeof l=="string")if(!_.test(l))l=b.createTextNode(l);else{l=l.replace(Y,"<$1></$2>");var m=(Z.exec(l)||["",""])[1].toLowerCase(),n=bg[m]||bg._default,o=n[0],p=b.createElement("div"),q=bh.childNodes,r;b===c?bh.appendChild(p):U(b).appendChild(p),p.innerHTML=n[1]+l+n[2];while(o--)p=p.lastChild;if(!f.support.tbody){var s=$.test(l),t=m==="table"&&!s?p.firstChild&&p.firstChild.childNodes:n[1]==="<table>"&&!s?p.childNodes:[];for(i=t.length-1;i>=0;--i)f.nodeName(t[i],"tbody")&&!t[i].childNodes.length&&t[i].parentNode.removeChild(t[i])}!f.support.leadingWhitespace&&X.test(l)&&p.insertBefore(b.createTextNode(X.exec(l)[0]),p.firstChild),l=p.childNodes,p&&(p.parentNode.removeChild(p),q.length>0&&(r=q[q.length-1],r&&r.parentNode&&r.parentNode.removeChild(r)))}var u;if(!f.support.appendChecked)if(l[0]&&typeof (u=l.length)=="number")for(i=0;i<u;i++)bn(l[i]);else bn(l);l.nodeType?j.push(l):j=f.merge(j,l)}if(d){g=function(a){return!a.type||be.test(a.type)};for(k=0;j[k];k++){h=j[k];if(e&&f.nodeName(h,"script")&&(!h.type||be.test(h.type)))e.push(h.parentNode?h.parentNode.removeChild(h):h);else{if(h.nodeType===1){var v=f.grep(h.getElementsByTagName("script"),g);j.splice.apply(j,[k+1,0].concat(v))}d.appendChild(h)}}}return j},cleanData:function(a){var b,c,d=f.cache,e=f.event.special,g=f.support.deleteExpando;for(var h=0,i;(i=a[h])!=null;h++){if(i.nodeName&&f.noData[i.nodeName.toLowerCase()])continue;c=i[f.expando];if(c){b=d[c];if(b&&b.events){for(var j in b.events)e[j]?f.event.remove(i,j):f.removeEvent(i,j,b.handle);b.handle&&(b.handle.elem=null)}g?delete i[f.expando]:i.removeAttribute&&i.removeAttribute(f.expando),delete d[c]}}}});var bp=/alpha\([^)]*\)/i,bq=/opacity=([^)]*)/,br=/([A-Z]|^ms)/g,bs=/^[\-+]?(?:\d*\.)?\d+$/i,bt=/^-?(?:\d*\.)?\d+(?!px)[^\d\s]+$/i,bu=/^([\-+])=([\-+.\de]+)/,bv=/^margin/,bw={position:"absolute",visibility:"hidden",display:"block"},bx=["Top","Right","Bottom","Left"],by,bz,bA;f.fn.css=function(a,c){return f.access(this,function(a,c,d){return d!==b?f.style(a,c,d):f.css(a,c)},a,c,arguments.length>1)},f.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=by(a,"opacity");return c===""?"1":c}return a.style.opacity}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":f.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!!a&&a.nodeType!==3&&a.nodeType!==8&&!!a.style){var g,h,i=f.camelCase(c),j=a.style,k=f.cssHooks[i];c=f.cssProps[i]||i;if(d===b){if(k&&"get"in k&&(g=k.get(a,!1,e))!==b)return g;return j[c]}h=typeof d,h==="string"&&(g=bu.exec(d))&&(d=+(g[1]+1)*+g[2]+parseFloat(f.css(a,c)),h="number");if(d==null||h==="number"&&isNaN(d))return;h==="number"&&!f.cssNumber[i]&&(d+="px");if(!k||!("set"in k)||(d=k.set(a,d))!==b)try{j[c]=d}catch(l){}}},css:function(a,c,d){var e,g;c=f.camelCase(c),g=f.cssHooks[c],c=f.cssProps[c]||c,c==="cssFloat"&&(c="float");if(g&&"get"in g&&(e=g.get(a,!0,d))!==b)return e;if(by)return by(a,c)},swap:function(a,b,c){var d={},e,f;for(f in b)d[f]=a.style[f],a.style[f]=b[f];e=c.call(a);for(f in b)a.style[f]=d[f];return e}}),f.curCSS=f.css,c.defaultView&&c.defaultView.getComputedStyle&&(bz=function(a,b){var c,d,e,g,h=a.style;b=b.replace(br,"-$1").toLowerCase(),(d=a.ownerDocument.defaultView)&&(e=d.getComputedStyle(a,null))&&(c=e.getPropertyValue(b),c===""&&!f.contains(a.ownerDocument.documentElement,a)&&(c=f.style(a,b))),!f.support.pixelMargin&&e&&bv.test(b)&&bt.test(c)&&(g=h.width,h.width=c,c=e.width,h.width=g);return c}),c.documentElement.currentStyle&&(bA=function(a,b){var c,d,e,f=a.currentStyle&&a.currentStyle[b],g=a.style;f==null&&g&&(e=g[b])&&(f=e),bt.test(f)&&(c=g.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),g.left=b==="fontSize"?"1em":f,f=g.pixelLeft+"px",g.left=c,d&&(a.runtimeStyle.left=d));return f===""?"auto":f}),by=bz||bA,f.each(["height","width"],function(a,b){f.cssHooks[b]={get:function(a,c,d){if(c)return a.offsetWidth!==0?bB(a,b,d):f.swap(a,bw,function(){return bB(a,b,d)})},set:function(a,b){return bs.test(b)?b+"px":b}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return bq.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=f.isNumeric(b)?"alpha(opacity="+b*100+")":"",g=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&f.trim(g.replace(bp,""))===""){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bp.test(g)?g.replace(bp,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){return f.swap(a,{display:"inline-block"},function(){return b?by(a,"margin-right"):a.style.marginRight})}})}),f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style&&a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)}),f.each({margin:"",padding:"",border:"Width"},function(a,b){f.cssHooks[a+b]={expand:function(c){var d,e=typeof c=="string"?c.split(" "):[c],f={};for(d=0;d<4;d++)f[a+bx[d]+b]=e[d]||e[d-2]||e[0];return f}}});var bC=/%20/g,bD=/\[\]$/,bE=/\r?\n/g,bF=/#.*$/,bG=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bH=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bI=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,bJ=/^(?:GET|HEAD)$/,bK=/^\/\//,bL=/\?/,bM=/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,bN=/^(?:select|textarea)/i,bO=/\s+/,bP=/([?&])_=[^&]*/,bQ=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bR=f.fn.load,bS={},bT={},bU,bV,bW=["*/"]+["*"];try{bU=e.href}catch(bX){bU=c.createElement("a"),bU.href="",bU=bU.href}bV=bQ.exec(bU.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bR)return bR.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("<div>").append(c.replace(bM,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bN.test(this.nodeName)||bH.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bE,"\r\n")}}):{name:b.name,value:c.replace(bE,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.on(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?b$(a,f.ajaxSettings):(b=a,a=f.ajaxSettings),b$(a,b);return a},ajaxSettings:{url:bU,isLocal:bI.test(bV[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded; charset=UTF-8",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":bW},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:bY(bS),ajaxTransport:bY(bT),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a>0?4:0;var o,r,u,w=c,x=l?ca(d,v,l):b,y,z;if(a>=200&&a<300||a===304){if(d.ifModified){if(y=v.getResponseHeader("Last-Modified"))f.lastModified[k]=y;if(z=v.getResponseHeader("Etag"))f.etag[k]=z}if(a===304)w="notmodified",o=!0;else try{r=cb(d,x),w="success",o=!0}catch(A){w="parsererror",u=A}}else{u=w;if(!w||a)w="error",a<0&&(a=0)}v.status=a,v.statusText=""+(c||w),o?h.resolveWith(e,[r,w,v]):h.rejectWith(e,[v,w,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.fireWith(e,[v,w]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f.Callbacks("once memory"),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bG.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.add,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bF,"").replace(bK,bV[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bO),d.crossDomain==null&&(r=bQ.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bV[1]&&r[2]==bV[2]&&(r[3]||(r[1]==="http:"?80:443))==(bV[3]||(bV[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),bZ(bS,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bJ.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bL.test(d.url)?"&":"?")+d.data,delete d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bP,"$1_="+x);d.url=y+(y===d.url?(bL.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", "+bW+"; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=bZ(bT,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){if(s<2)w(-1,z);else throw z}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)b_(g,a[g],c,e);return d.join("&").replace(bC,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var cc=f.now(),cd=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+cc++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=typeof b.data=="string"&&/^application\/x\-www\-form\-urlencoded/.test(b.contentType);if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(cd.test(b.url)||e&&cd.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(cd,l),b.url===j&&(e&&(k=k.replace(cd,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var ce=a.ActiveXObject?function(){for(var a in cg)cg[a](0,1)}:!1,cf=0,cg;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ch()||ci()}:ch,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,ce&&delete cg[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n);try{m.text=h.responseText}catch(a){}try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++cf,ce&&(cg||(cg={},f(a).unload(ce)),cg[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var cj={},ck,cl,cm=/^(?:toggle|show|hide)$/,cn=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,co,cp=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],cq;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(ct("show",3),a,b,c);for(var g=0,h=this.length;g<h;g++)d=this[g],d.style&&(e=d.style.display,!f._data(d,"olddisplay")&&e==="none"&&(e=d.style.display=""),(e===""&&f.css(d,"display")==="none"||!f.contains(d.ownerDocument.documentElement,d))&&f._data(d,"olddisplay",cu(d.nodeName)));for(g=0;g<h;g++){d=this[g];if(d.style){e=d.style.display;if(e===""||e==="none")d.style.display=f._data(d,"olddisplay")||""}}return this},hide:function(a,b,c){if(a||a===0)return this.animate(ct("hide",3),a,b,c);var d,e,g=0,h=this.length;for(;g<h;g++)d=this[g],d.style&&(e=f.css(d,"display"),e!=="none"&&!f._data(d,"olddisplay")&&f._data(d,"olddisplay",e));for(g=0;g<h;g++)this[g].style&&(this[g].style.display="none");return this},_toggle:f.fn.toggle,toggle:function(a,b,c){var d=typeof a=="boolean";f.isFunction(a)&&f.isFunction(b)?this._toggle.apply(this,arguments):a==null||d?this.each(function(){var b=d?a:f(this).is(":hidden");f(this)[b?"show":"hide"]()}):this.animate(ct("toggle",3),a,b,c);return this},fadeTo:function(a,b,c,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){function g(){e.queue===!1&&f._mark(this);var b=f.extend({},e),c=this.nodeType===1,d=c&&f(this).is(":hidden"),g,h,i,j,k,l,m,n,o,p,q;b.animatedProperties={};for(i in a){g=f.camelCase(i),i!==g&&(a[g]=a[i],delete a[i]);if((k=f.cssHooks[g])&&"expand"in k){l=k.expand(a[g]),delete a[g];for(i in l)i in a||(a[i]=l[i])}}for(g in a){h=a[g],f.isArray(h)?(b.animatedProperties[g]=h[1],h=a[g]=h[0]):b.animatedProperties[g]=b.specialEasing&&b.specialEasing[g]||b.easing||"swing";if(h==="hide"&&d||h==="show"&&!d)return b.complete.call(this);c&&(g==="height"||g==="width")&&(b.overflow=[this.style.overflow,this.style.overflowX,this.style.overflowY],f.css(this,"display")==="inline"&&f.css(this,"float")==="none"&&(!f.support.inlineBlockNeedsLayout||cu(this.nodeName)==="inline"?this.style.display="inline-block":this.style.zoom=1))}b.overflow!=null&&(this.style.overflow="hidden");for(i in a)j=new f.fx(this,b,i),h=a[i],cm.test(h)?(q=f._data(this,"toggle"+i)||(h==="toggle"?d?"show":"hide":0),q?(f._data(this,"toggle"+i,q==="show"?"hide":"show"),j[q]()):j[h]()):(m=cn.exec(h),n=j.cur(),m?(o=parseFloat(m[2]),p=m[3]||(f.cssNumber[i]?"":"px"),p!=="px"&&(f.style(this,i,(o||1)+p),n=(o||1)/j.cur()*n,f.style(this,i,n+p)),m[1]&&(o=(m[1]==="-="?-1:1)*o+n),j.custom(n,o,p)):j.custom(n,h,""));return!0}var e=f.speed(b,c,d);if(f.isEmptyObject(a))return this.each(e.complete,[!1]);a=f.extend({},a);return e.queue===!1?this.each(g):this.queue(e.queue,g)},stop:function(a,c,d){typeof a!="string"&&(d=c,c=a,a=b),c&&a!==!1&&this.queue(a||"fx",[]);return this.each(function(){function h(a,b,c){var e=b[c];f.removeData(a,c,!0),e.stop(d)}var b,c=!1,e=f.timers,g=f._data(this);d||f._unmark(!0,this);if(a==null)for(b in g)g[b]&&g[b].stop&&b.indexOf(".run")===b.length-4&&h(this,g,b);else g[b=a+".run"]&&g[b].stop&&h(this,g,b);for(b=e.length;b--;)e[b].elem===this&&(a==null||e[b].queue===a)&&(d?e[b](!0):e[b].saveState(),c=!0,e.splice(b,1));(!d||!c)&&f.dequeue(this,a)})}}),f.each({slideDown:ct("show",1),slideUp:ct("hide",1),slideToggle:ct("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){f.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),f.extend({speed:function(a,b,c){var d=a&&typeof a=="object"?f.extend({},a):{complete:c||!c&&b||f.isFunction(a)&&a,duration:a,easing:c&&b||b&&!f.isFunction(b)&&b};d.duration=f.fx.off?0:typeof d.duration=="number"?d.duration:d.duration in f.fx.speeds?f.fx.speeds[d.duration]:f.fx.speeds._default;if(d.queue==null||d.queue===!0)d.queue="fx";d.old=d.complete,d.complete=function(a){f.isFunction(d.old)&&d.old.call(this),d.queue?f.dequeue(this,d.queue):a!==!1&&f._unmark(this)};return d},easing:{linear:function(a){return a},swing:function(a){return-Math.cos(a*Math.PI)/2+.5}},timers:[],fx:function(a,b,c){this.options=b,this.elem=a,this.prop=c,b.orig=b.orig||{}}}),f.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this),(f.fx.step[this.prop]||f.fx.step._default)(this)},cur:function(){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];var a,b=f.css(this.elem,this.prop);return isNaN(a=parseFloat(b))?!b||b==="auto"?0:b:a},custom:function(a,c,d){function h(a){return e.step(a)}var e=this,g=f.fx;this.startTime=cq||cr(),this.end=c,this.now=this.start=a,this.pos=this.state=0,this.unit=d||this.unit||(f.cssNumber[this.prop]?"":"px"),h.queue=this.options.queue,h.elem=this.elem,h.saveState=function(){f._data(e.elem,"fxshow"+e.prop)===b&&(e.options.hide?f._data(e.elem,"fxshow"+e.prop,e.start):e.options.show&&f._data(e.elem,"fxshow"+e.prop,e.end))},h()&&f.timers.push(h)&&!co&&(co=setInterval(g.tick,g.interval))},show:function(){var a=f._data(this.elem,"fxshow"+this.prop);this.options.orig[this.prop]=a||f.style(this.elem,this.prop),this.options.show=!0,a!==b?this.custom(this.cur(),a):this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur()),f(this.elem).show()},hide:function(){this.options.orig[this.prop]=f._data(this.elem,"fxshow"+this.prop)||f.style(this.elem,this.prop),this.options.hide=!0,this.custom(this.cur(),0)},step:function(a){var b,c,d,e=cq||cr(),g=!0,h=this.elem,i=this.options;if(a||e>=i.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),i.animatedProperties[this.prop]=!0;for(b in i.animatedProperties)i.animatedProperties[b]!==!0&&(g=!1);if(g){i.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){h.style["overflow"+b]=i.overflow[a]}),i.hide&&f(h).hide();if(i.hide||i.show)for(b in i.animatedProperties)f.style(h,b,i.orig[b]),f.removeData(h,"fxshow"+b,!0),f.removeData(h,"toggle"+b,!0);d=i.complete,d&&(i.complete=!1,d.call(h))}return!1}i.duration==Infinity?this.now=e:(c=e-this.startTime,this.state=c/i.duration,this.pos=f.easing[i.animatedProperties[this.prop]](this.state,c,0,1,i.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){var a,b=f.timers,c=0;for(;c<b.length;c++)a=b[c],!a()&&b[c]===a&&b.splice(c--,1);b.length||f.fx.stop()},interval:13,stop:function(){clearInterval(co),co=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){f.style(a.elem,"opacity",a.now)},_default:function(a){a.elem.style&&a.elem.style[a.prop]!=null?a.elem.style[a.prop]=a.now+a.unit:a.elem[a.prop]=a.now}}}),f.each(cp.concat.apply([],cp),function(a,b){b.indexOf("margin")&&(f.fx.step[b]=function(a){f.style(a.elem,b,Math.max(0,a.now)+a.unit)})}),f.expr&&f.expr.filters&&(f.expr.filters.animated=function(a){return f.grep(f.timers,function(b){return a===b.elem}).length});var cv,cw=/^t(?:able|d|h)$/i,cx=/^(?:body|html)$/i;"getBoundingClientRect"in c.documentElement?cv=function(a,b,c,d){try{d=a.getBoundingClientRect()}catch(e){}if(!d||!f.contains(c,a))return d?{top:d.top,left:d.left}:{top:0,left:0};var g=b.body,h=cy(b),i=c.clientTop||g.clientTop||0,j=c.clientLeft||g.clientLeft||0,k=h.pageYOffset||f.support.boxModel&&c.scrollTop||g.scrollTop,l=h.pageXOffset||f.support.boxModel&&c.scrollLeft||g.scrollLeft,m=d.top+k-i,n=d.left+l-j;return{top:m,left:n}}:cv=function(a,b,c){var d,e=a.offsetParent,g=a,h=b.body,i=b.defaultView,j=i?i.getComputedStyle(a,null):a.currentStyle,k=a.offsetTop,l=a.offsetLeft;while((a=a.parentNode)&&a!==h&&a!==c){if(f.support.fixedPosition&&j.position==="fixed")break;d=i?i.getComputedStyle(a,null):a.currentStyle,k-=a.scrollTop,l-=a.scrollLeft,a===e&&(k+=a.offsetTop,l+=a.offsetLeft,f.support.doesNotAddBorder&&(!f.support.doesAddBorderForTableAndCells||!cw.test(a.nodeName))&&(k+=parseFloat(d.borderTopWidth)||0,l+=parseFloat(d.borderLeftWidth)||0),g=e,e=a.offsetParent),f.support.subtractsBorderForOverflowNotVisible&&d.overflow!=="visible"&&(k+=parseFloat(d.borderTopWidth)||0,l+=parseFloat(d.borderLeftWidth)||0),j=d}if(j.position==="relative"||j.position==="static")k+=h.offsetTop,l+=h.offsetLeft;f.support.fixedPosition&&j.position==="fixed"&&(k+=Math.max(c.scrollTop,h.scrollTop),l+=Math.max(c.scrollLeft,h.scrollLeft));return{top:k,left:l}},f.fn.offset=function(a){if(arguments.length)return a===b?this:this.each(function(b){f.offset.setOffset(this,a,b)});var c=this[0],d=c&&c.ownerDocument;if(!d)return null;if(c===d.body)return f.offset.bodyOffset(c);return cv(c,d,d.documentElement)},f.offset={bodyOffset:function(a){var b=a.offsetTop,c=a.offsetLeft;f.support.doesNotIncludeMarginInBodyOffset&&(b+=parseFloat(f.css(a,"marginTop"))||0,c+=parseFloat(f.css(a,"marginLeft"))||0);return{top:b,left:c}},setOffset:function(a,b,c){var d=f.css(a,"position");d==="static"&&(a.style.position="relative");var e=f(a),g=e.offset(),h=f.css(a,"top"),i=f.css(a,"left"),j=(d==="absolute"||d==="fixed")&&f.inArray("auto",[h,i])>-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=cx.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cx.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,c){var d=/Y/.test(c);f.fn[a]=function(e){return f.access(this,function(a,e,g){var h=cy(a);if(g===b)return h?c in h?h[c]:f.support.boxModel&&h.document.documentElement[e]||h.document.body[e]:a[e];h?h.scrollTo(d?f(h).scrollLeft():g,d?g:f(h).scrollTop()):a[e]=g},a,e,arguments.length,null)}}),f.each({Height:"height",Width:"width"},function(a,c){var d="client"+a,e="scroll"+a,g="offset"+a;f.fn["inner"+a]=function(){var a=this[0];return a?a.style?parseFloat(f.css(a,c,"padding")):this[c]():null},f.fn["outer"+a]=function(a){var b=this[0];return b?b.style?parseFloat(f.css(b,c,a?"margin":"border")):this[c]():null},f.fn[c]=function(a){return f.access(this,function(a,c,h){var i,j,k,l;if(f.isWindow(a)){i=a.document,j=i.documentElement[d];return f.support.boxModel&&j||i.body&&i.body[d]||j}if(a.nodeType===9){i=a.documentElement;if(i[d]>=i[e])return i[d];return Math.max(a.body[e],i[e],a.body[g],i[g])}if(h===b){k=f.css(a,c),l=parseFloat(k);return f.isNumeric(l)?l:k}f(a).css(c,h)},c,a,arguments.length,null)}}),a.jQuery=a.$=f,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return f})})(window); \ No newline at end of file
diff --git a/guides/assets/javascripts/responsive-tables.js b/guides/assets/javascripts/responsive-tables.js
index 8554a1343b..24906dddeb 100644
--- a/guides/assets/javascripts/responsive-tables.js
+++ b/guides/assets/javascripts/responsive-tables.js
@@ -1,43 +1,50 @@
-$(document).ready(function() {
+(function() {
+ "use strict";
+
var switched = false;
- $("table").not(".syntaxhighlighter").addClass("responsive");
+
+ // For old browsers
+ var each = function(node, callback) {
+ var array = Array.prototype.slice.call(node);
+ for(var i = 0; i < array.length; i++) callback(array[i]);
+ }
+
+ each(document.querySelectorAll(":not(.syntaxhighlighter)>table"), function(element) {
+ element.classList.add("responsive");
+ });
+
var updateTables = function() {
- if (($(window).width() < 767) && !switched ){
+ if (document.documentElement.clientWidth < 767 && !switched) {
switched = true;
- $("table.responsive").each(function(i, element) {
- splitTable($(element));
- });
- return true;
- }
- else if (switched && ($(window).width() > 767)) {
+ each(document.querySelectorAll("table.responsive"), splitTable);
+ } else {
switched = false;
- $("table.responsive").each(function(i, element) {
- unsplitTable($(element));
- });
+ each(document.querySelectorAll(".table-wrapper table.responsive"), unsplitTable);
}
- };
-
- $(window).load(updateTables);
- $(window).bind("resize", updateTables);
-
-
- function splitTable(original)
- {
- original.wrap("<div class='table-wrapper' />");
-
- var copy = original.clone();
- copy.find("td:not(:first-child), th:not(:first-child)").css("display", "none");
- copy.removeClass("responsive");
-
- original.closest(".table-wrapper").append(copy);
- copy.wrap("<div class='pinned' />");
- original.wrap("<div class='scrollable' />");
- }
-
- function unsplitTable(original) {
- original.closest(".table-wrapper").find(".pinned").remove();
- original.unwrap();
- original.unwrap();
- }
-
-});
+ }
+
+ document.addEventListener("DOMContentLoaded", updateTables);
+ window.addEventListener("resize", updateTables);
+
+ var splitTable = function(original) {
+ wrap(original, createElement("div", "table-wrapper"));
+
+ var copy = original.cloneNode(true);
+ each(copy.querySelectorAll("td:not(:first-child), th:not(:first-child)"), function(element) {
+ element.style.display = "none";
+ });
+ copy.classList.remove("responsive");
+
+ original.parentNode.append(copy);
+ wrap(copy, createElement("div", "pinned"))
+ wrap(original, createElement("div", "scrollable"));
+ }
+
+ var unsplitTable = function(original) {
+ each(document.querySelectorAll(".table-wrapper .pinned"), function(element) {
+ element.parentNode.removeChild(element);
+ });
+ unwrap(original.parentNode);
+ unwrap(original);
+ }
+}).call(this);
diff --git a/guides/assets/stylesheets/main.css b/guides/assets/stylesheets/main.css
index b27776745a..00d4bcb21e 100644
--- a/guides/assets/stylesheets/main.css
+++ b/guides/assets/stylesheets/main.css
@@ -70,7 +70,7 @@ table {
}
table th, table td {
- padding: 0.25em 1em;
+ padding: 9px 10px;
border: 1px solid #CCC;
border-collapse: collapse;
}
@@ -79,7 +79,6 @@ table th {
border-bottom: 2px solid #CCC;
background: #EEE;
font-weight: bold;
- padding: 0.5em 1em;
}
img {
@@ -265,8 +264,6 @@ body {
}
}
-#extraCol {display: none;}
-
#footer {
padding: 2em 0;
background: #222 url(../images/footer_tile.gif) repeat-x;
@@ -410,6 +407,10 @@ a, a:link, a:visited {
padding-top: 2em;
}
+#guides.visible {
+ display: block !important;
+}
+
#guides dt, #guides dd {
font-weight: normal;
font-size: 0.722em;
@@ -555,8 +556,6 @@ h6 {
font-size: 1.2857em;
padding: 0.125em 0 0.25em 0;
margin-bottom: 0;
- /*background: url(../images/book_icon.gif) no-repeat left top;
- padding: 0.125em 0 0.25em 28px;*/
}
@media screen and (max-width: 480px) {
@@ -665,10 +664,8 @@ div.code_container {
visibility: hidden;
}
-.clearfix {display: inline-block;}
* html .clearfix {height: 1%;}
.clearfix {display: block;}
-.clear { clear:both; }
/* Same bottom margin for special boxes than for regular paragraphs, this way
intermediate whitespace looks uniform. */
@@ -696,9 +693,6 @@ div.important p, div.caution p, div.warning p, div.note p, div.info p {
/* Foundation v2.1.4 http://foundation.zurb.com */
/* Artfully masterminded by ZURB */
-table th { font-weight: bold; }
-table td, table th { padding: 9px 10px; text-align: left; }
-
/* Mobile */
@media only screen and (max-width: 767px) {
table.responsive { margin-bottom: 0; }
diff --git a/guides/assets/stylesheets/print.css b/guides/assets/stylesheets/print.css
index bdc8ec948d..6280422469 100644
--- a/guides/assets/stylesheets/print.css
+++ b/guides/assets/stylesheets/print.css
@@ -4,7 +4,7 @@
/* Modified January 31, 2009
--------------------------------------- */
-body, .wrapper, .note, .info, code, #topNav, .L, .R, #frame, #container, #header, #navigation, #footer, #feature, #mainCol, #subCol, #extraCol, .content {position: static; text-align: left; text-indent: 0; background: White; color: Black; border-color: Black; width: auto; height: auto; display: block; float: none; min-height: 0; margin: 0; padding: 0;}
+body, .wrapper, .note, .info, code, #topNav, .L, .R, #frame, #container, #header, #navigation, #footer, #feature, #mainCol, #subCol, .content {position: static; text-align: left; text-indent: 0; background: White; color: Black; border-color: Black; width: auto; height: auto; display: block; float: none; min-height: 0; margin: 0; padding: 0;}
body {
background: #FFF;
diff --git a/guides/source/action_controller_overview.md b/guides/source/action_controller_overview.md
index e0e85588a0..4e3319dec4 100644
--- a/guides/source/action_controller_overview.md
+++ b/guides/source/action_controller_overview.md
@@ -457,7 +457,9 @@ class LoginsController < ApplicationController
# "Delete" a login, aka "log the user out"
def destroy
# Remove the user id from the session
- @_current_user = session[:current_user_id] = nil
+ session.delete(:current_user_id)
+ # Clear the memoized current user
+ @_current_user = nil
redirect_to root_url
end
end
diff --git a/guides/source/active_storage_overview.md b/guides/source/active_storage_overview.md
index d67f65e88a..4a915b1083 100644
--- a/guides/source/active_storage_overview.md
+++ b/guides/source/active_storage_overview.md
@@ -337,14 +337,15 @@ rails_blob_path(user.avatar, disposition: "attachment")
Transforming Images
-------------------
-To create variation of the image, call `variant` on the Blob.
-You can pass any [MiniMagick](https://github.com/minimagick/minimagick)
-supported transformation to the method.
+To create variation of the image, call `variant` on the Blob. You can pass
+any transformation to the method supported by the processor. The default
+processor is [MiniMagick](https://github.com/minimagick/minimagick), but you
+can also use [Vips](http://www.rubydoc.info/gems/ruby-vips/Vips/Image).
-To enable variants, add `mini_magick` to your `Gemfile`:
+To enable variants, add the `image_processing` gem to your `Gemfile`:
```ruby
-gem 'mini_magick'
+gem 'image_processing', '~> 1.2'
```
When the browser hits the variant URL, Active Storage will lazy transform the
@@ -352,7 +353,15 @@ original blob into the format you specified and redirect to its new service
location.
```erb
-<%= image_tag user.avatar.variant(resize: "100x100") %>
+<%= image_tag user.avatar.variant(resize_to_fit: [100, 100]) %>
+```
+
+To switch to the Vips processor, you would add the following to
+`config/application.rb`:
+
+```ruby
+# Use Vips for processing variants.
+config.active_storage.variant_processor = :vips
```
Previewing Files
@@ -366,7 +375,7 @@ the box, Active Storage supports previewing videos and PDF documents.
<ul>
<% @message.files.each do |file| %>
<li>
- <%= image_tag file.preview(resize: "100x100>") %>
+ <%= image_tag file.preview(resize_to_limit: [100, 100]) %>
</li>
<% end %>
</ul>
diff --git a/guides/source/association_basics.md b/guides/source/association_basics.md
index 860a1e1cba..6fbd52edbd 100644
--- a/guides/source/association_basics.md
+++ b/guides/source/association_basics.md
@@ -505,7 +505,7 @@ class Employee < ApplicationRecord
has_many :subordinates, class_name: "Employee",
foreign_key: "manager_id"
- belongs_to :manager, class_name: "Employee"
+ belongs_to :manager, class_name: "Employee", optional: true
end
```
diff --git a/guides/source/configuring.md b/guides/source/configuring.md
index 7d5ca4b8a7..c98e9b719c 100644
--- a/guides/source/configuring.md
+++ b/guides/source/configuring.md
@@ -778,6 +778,8 @@ normal Rails server.
`config.active_storage` provides the following configuration options:
+* `config.active_storage.variant_processor` accepts a symbol `:mini_magick` or `:vips`, specifying whether variant transformations will be performed with MiniMagick or ruby-vips. The default is `:mini_magick`.
+
* `config.active_storage.analyzers` accepts an array of classes indicating the analyzers available for Active Storage blobs. The default is `[ActiveStorage::Analyzer::ImageAnalyzer, ActiveStorage::Analyzer::VideoAnalyzer]`. The former can extract width and height of an image blob; the latter can extract width, height, duration, angle, and aspect ratio of a video blob.
* `config.active_storage.previewers` accepts an array of classes indicating the image previewers available in Active Storage blobs. The default is `[ActiveStorage::Previewer::PDFPreviewer, ActiveStorage::Previewer::VideoPreviewer]`. The former can generate a thumbnail from the first page of a PDF blob; the latter from the relevant frame of a video blob.
diff --git a/guides/source/layout.html.erb b/guides/source/layout.html.erb
index 6e3aa9207e..d2024b726d 100644
--- a/guides/source/layout.html.erb
+++ b/guides/source/layout.html.erb
@@ -123,15 +123,8 @@
</div>
</div>
- <script type="text/javascript" src="javascripts/jquery.min.js"></script>
- <script type="text/javascript" src="javascripts/responsive-tables.js"></script>
- <script type="text/javascript" src="javascripts/guides.js"></script>
<script type="text/javascript" src="javascripts/syntaxhighlighter.js"></script>
- <script type="text/javascript">
- syntaxhighlighterConfig = {
- autoLinks: false,
- };
- $(guidesIndex.bind);
- </script>
+ <script type="text/javascript" src="javascripts/guides.js"></script>
+ <script type="text/javascript" src="javascripts/responsive-tables.js"></script>
</body>
</html>
diff --git a/guides/source/testing.md b/guides/source/testing.md
index 47a3b26447..9fd29672c7 100644
--- a/guides/source/testing.md
+++ b/guides/source/testing.md
@@ -33,8 +33,8 @@ Rails creates a `test` directory for you as soon as you create a Rails project u
```bash
$ ls -F test
-controllers/ helpers/ mailers/ system/ test_helper.rb
-fixtures/ integration/ models/ application_system_test_case.rb
+application_system_test_case.rb fixtures/ integration/ models/ test_helper.rb
+controllers/ helpers/ mailers/ system/
```
The `helpers`, `mailers`, and `models` directories are meant to hold tests for view helpers, mailers, and models, respectively. The `controllers` directory is meant to hold tests for controllers, routes, and views. The `integration` directory is meant to hold tests for interactions between controllers.
@@ -433,16 +433,8 @@ at the end of test run and so on. Check the documentation of the test runner as
```bash
$ bin/rails test -h
-minitest options:
- -h, --help Display this help.
- -s, --seed SEED Sets random seed. Also via env. Eg: SEED=n rake
- -v, --verbose Verbose. Show progress processing files.
- -n, --name PATTERN Filter run on /regexp/ or string.
- --exclude PATTERN Exclude /regexp/ or string from run.
-
-Known extensions: rails, pride
-
Usage: bin/rails test [options] [files or directories]
+
You can run a single test by appending a line number to a filename:
bin/rails test test/models/user_test.rb:27
@@ -453,13 +445,22 @@ You can run multiple files and directories at the same time:
By default test failures and errors are reported inline during a run.
-Rails options:
+minitest options:
+ -h, --help Display this help.
+ --no-plugins Bypass minitest plugin auto-loading (or set $MT_NO_PLUGINS).
+ -s, --seed SEED Sets random seed. Also via env. Eg: SEED=n rake
+ -v, --verbose Verbose. Show progress processing files.
+ -n, --name PATTERN Filter run on /regexp/ or string.
+ --exclude PATTERN Exclude /regexp/ or string from run.
+
+Known extensions: rails, pride
-w, --warnings Run with Ruby warnings enabled
- -e, --environment Run tests in the ENV environment
+ -e, --environment ENV Run tests in the ENV environment
-b, --backtrace Show the complete backtrace
-d, --defer-output Output test failures and errors after the test run
-f, --fail-fast Abort test run on first failure or error
-c, --[no-]color Enable color in the output
+ -p, --pride Pride. Show your testing pride!
```
Parallel Testing
@@ -1484,7 +1485,7 @@ located under the `test/helpers` directory.
Given we have the following helper:
```ruby
-module UserHelper
+module UsersHelper
def link_to_user(user)
link_to "#{user.first_name} #{user.last_name}", user
end
@@ -1494,7 +1495,7 @@ end
We can test the output of this method like this:
```ruby
-class UserHelperTest < ActionView::TestCase
+class UsersHelperTest < ActionView::TestCase
test "should return the user's full name" do
user = users(:david)
@@ -1600,7 +1601,7 @@ Functional testing for mailers involves more than just checking that the email b
```ruby
require 'test_helper'
-class UserControllerTest < ActionDispatch::IntegrationTest
+class UsersControllerTest < ActionDispatch::IntegrationTest
test "invite friend" do
assert_difference 'ActionMailer::Base.deliveries.size', +1 do
post invite_friend_url, params: { email: 'friend@example.com' }
diff --git a/guides/source/working_with_javascript_in_rails.md b/guides/source/working_with_javascript_in_rails.md
index b9ea4ad47a..a922bdc16b 100644
--- a/guides/source/working_with_javascript_in_rails.md
+++ b/guides/source/working_with_javascript_in_rails.md
@@ -382,10 +382,9 @@ have been bundled into `event.detail`. For information about the previously used
`jquery-ujs` in Rails 5 and earlier, read the [`jquery-ujs` wiki](https://github.com/rails/jquery-ujs/wiki/ajax).
### Stoppable events
-
-If you stop `ajax:before` or `ajax:beforeSend` by returning false from the
-handler method, the Ajax request will never take place. The `ajax:before` event
-can manipulate form data before serialization and the
+You can stop execution of the Ajax request by running `event.preventDefault()`
+from the handlers methods `ajax:before` or `ajax:beforeSend`.
+The `ajax:before` event can manipulate form data before serialization and the
`ajax:beforeSend` event is useful for adding custom request headers.
If you stop the `ajax:aborted:file` event, the default behavior of allowing the
@@ -393,6 +392,9 @@ browser to submit the form via normal means (i.e. non-Ajax submission) will be
canceled and the form will not be submitted at all. This is useful for
implementing your own Ajax file upload workaround.
+Note, you should use `return false` to prevent event for `jquery-ujs` and
+`e.preventDefault()` for `rails-ujs`
+
Server-Side Concerns
--------------------
diff --git a/railties/RDOC_MAIN.rdoc b/railties/RDOC_MAIN.rdoc
index 096551cac0..a4a4b6b235 100644
--- a/railties/RDOC_MAIN.rdoc
+++ b/railties/RDOC_MAIN.rdoc
@@ -15,11 +15,11 @@ application into three layers: Model, View, and Controller, each with a specific
The <em><b>Model layer</b></em> represents the domain model (such as Account, Product,
Person, Post, etc.) and encapsulates the business logic specific to
your application. In \Rails, database-backed model classes are derived from
-<tt>ActiveRecord::Base</tt>. {Active Record}[link:../activerecord/README.rdoc] allows you to present the data from
+<tt>ActiveRecord::Base</tt>. {Active Record}[link:files/activerecord/README_rdoc.html] allows you to present the data from
database rows as objects and embellish these data objects with business logic
methods. Although most \Rails models are backed by a database, models can also be ordinary
Ruby classes, or Ruby classes that implement a set of interfaces as provided by
-the {Active Model}[link:../activemodel/README.rdoc] module.
+the {Active Model}[link:files/activemodel/README_rdoc.html] module.
== Controller layer
@@ -29,7 +29,7 @@ can also generate XML, JSON, PDFs, mobile-specific views, and more. Controllers
manipulate models, and render view templates in order to generate the appropriate HTTP response.
In \Rails, incoming requests are routed by Action Dispatch to an appropriate controller, and
controller classes are derived from <tt>ActionController::Base</tt>. Action Dispatch and Action Controller
-are bundled together in {Action Pack}[link:../actionpack/README.rdoc].
+are bundled together in {Action Pack}[link:files/actionpack/README_rdoc.html].
== View layer
@@ -37,19 +37,19 @@ The <em><b>View layer</b></em> is composed of "templates" that are responsible f
appropriate representations of your application's resources. Templates can
come in a variety of formats, but most view templates are \HTML with embedded
Ruby code (ERB files). Views are typically rendered to generate a controller response,
-or to generate the body of an email. In \Rails, View generation is handled by {Action View}[link:../actionview/README.rdoc].
+or to generate the body of an email. In \Rails, View generation is handled by {Action View}[link:files/actionview/README_rdoc.html].
== Frameworks and libraries
-{Active Record}[link:../activerecord/README.rdoc], {Active Model}[link:../activemodel/README.rdoc],
-{Action Pack}[link:../actionpack/README.rdoc], and {Action View}[link:../actionview/README.rdoc] can each be used independently outside \Rails.
-In addition to that, \Rails also comes with {Action Mailer}[link:../actionmailer/README.rdoc], a library
-to generate and send emails; {Active Job}[link:../activejob/README.md], a
+{Active Record}[link:files/activerecord/README_rdoc.html], {Active Model}[link:files/activemodel/README_rdoc.html],
+{Action Pack}[link:files/actionpack/README_rdoc.html], and {Action View}[link:files/actionview/README_rdoc.html] can each be used independently outside \Rails.
+In addition to that, \Rails also comes with {Action Mailer}[link:files/actionmailer/README_rdoc.html], a library
+to generate and send emails; {Active Job}[link:files/activejob/README_md.html], a
framework for declaring jobs and making them run on a variety of queueing
-backends; {Action Cable}[link:../actioncable/README.md], a framework to
-integrate WebSockets with a \Rails application; {Active Storage}[link:../activestorage/README.md],
+backends; {Action Cable}[link:files/actioncable/README_md.html], a framework to
+integrate WebSockets with a \Rails application; {Active Storage}[link:files/activestorage/README_md.html],
a library to attach cloud and local files to \Rails applications;
-and {Active Support}[link:../activesupport/README.rdoc], a collection
+and {Active Support}[link:files/activesupport/README_rdoc.html], a collection
of utility classes and standard library extensions that are useful for \Rails,
and may also be used independently outside \Rails.
diff --git a/railties/lib/rails/generators/app_base.rb b/railties/lib/rails/generators/app_base.rb
index 8c5d872573..f51542f3ec 100644
--- a/railties/lib/rails/generators/app_base.rb
+++ b/railties/lib/rails/generators/app_base.rb
@@ -440,7 +440,7 @@ module Rails
end
def depend_on_bootsnap?
- !options[:skip_bootsnap] && !options[:dev]
+ !options[:skip_bootsnap] && !options[:dev] && !defined?(JRUBY_VERSION)
end
def os_supports_listen_out_of_the_box?
diff --git a/railties/lib/rails/generators/erb/mailer/mailer_generator.rb b/railties/lib/rails/generators/erb/mailer/mailer_generator.rb
index e2ea66415f..997602cb8c 100644
--- a/railties/lib/rails/generators/erb/mailer/mailer_generator.rb
+++ b/railties/lib/rails/generators/erb/mailer/mailer_generator.rb
@@ -35,7 +35,7 @@ module Erb # :nodoc:
end
def file_name
- @_file_name ||= super.gsub(/_mailer/i, "")
+ @_file_name ||= super.sub(/_mailer\z/i, "")
end
end
end
diff --git a/railties/lib/rails/generators/rails/app/templates/Gemfile.tt b/railties/lib/rails/generators/rails/app/templates/Gemfile.tt
index 5e7455cdc7..1567333023 100644
--- a/railties/lib/rails/generators/rails/app/templates/Gemfile.tt
+++ b/railties/lib/rails/generators/rails/app/templates/Gemfile.tt
@@ -23,7 +23,7 @@ ruby <%= "'#{RUBY_VERSION}'" -%>
<% unless skip_active_storage? -%>
# Use ActiveStorage variant
-# gem 'mini_magick', '~> 4.8'
+# gem 'image_processing', '~> 1.2'
<% end -%>
# Use Capistrano for deployment
diff --git a/railties/lib/rails/generators/rails/app/templates/ruby-version.tt b/railties/lib/rails/generators/rails/app/templates/ruby-version.tt
index c444f33b0f..19f0d7f202 100644
--- a/railties/lib/rails/generators/rails/app/templates/ruby-version.tt
+++ b/railties/lib/rails/generators/rails/app/templates/ruby-version.tt
@@ -1 +1 @@
-<%= RUBY_VERSION -%>
+<%= "#{RUBY_ENGINE}-#{RUBY_ENGINE_VERSION}" -%>
diff --git a/railties/lib/rails/generators/rails/controller/controller_generator.rb b/railties/lib/rails/generators/rails/controller/controller_generator.rb
index 6e2495d45f..eb75e7e661 100644
--- a/railties/lib/rails/generators/rails/controller/controller_generator.rb
+++ b/railties/lib/rails/generators/rails/controller/controller_generator.rb
@@ -20,10 +20,20 @@ module Rails
route generate_routing_code
end
- hook_for :template_engine, :test_framework, :helper, :assets
+ hook_for :template_engine, :test_framework, :helper, :assets do |generator|
+ invoke generator, [ remove_possible_suffix(name), actions ]
+ end
private
+ def file_name
+ @_file_name ||= remove_possible_suffix(super)
+ end
+
+ def remove_possible_suffix(name)
+ name.sub(/_?controller$/i, "")
+ end
+
# This method creates nested route entry for namespaced resources.
# For eg. rails g controller foo/bar/baz index show
# Will generate -
diff --git a/railties/lib/rails/generators/test_unit/job/job_generator.rb b/railties/lib/rails/generators/test_unit/job/job_generator.rb
index a99ce914c0..1dae3cb6a5 100644
--- a/railties/lib/rails/generators/test_unit/job/job_generator.rb
+++ b/railties/lib/rails/generators/test_unit/job/job_generator.rb
@@ -10,6 +10,11 @@ module TestUnit # :nodoc:
def create_test_file
template "unit_test.rb", File.join("test/jobs", class_path, "#{file_name}_job_test.rb")
end
+
+ private
+ def file_name
+ @_file_name ||= super.sub(/_job\z/i, "")
+ end
end
end
end
diff --git a/railties/lib/rails/generators/test_unit/mailer/mailer_generator.rb b/railties/lib/rails/generators/test_unit/mailer/mailer_generator.rb
index 610d47a729..ab8331f31c 100644
--- a/railties/lib/rails/generators/test_unit/mailer/mailer_generator.rb
+++ b/railties/lib/rails/generators/test_unit/mailer/mailer_generator.rb
@@ -21,7 +21,7 @@ module TestUnit # :nodoc:
private
def file_name
- @_file_name ||= super.gsub(/_mailer/i, "")
+ @_file_name ||= super.sub(/_mailer\z/i, "")
end
end
end
diff --git a/railties/lib/rails/source_annotation_extractor.rb b/railties/lib/rails/source_annotation_extractor.rb
index 73bee5bcbf..7257aaeaae 100644
--- a/railties/lib/rails/source_annotation_extractor.rb
+++ b/railties/lib/rails/source_annotation_extractor.rb
@@ -29,7 +29,7 @@ module Rails
end
# Registers additional directories to be included
- # SourceAnnotationExtractor::Annotation.register_directories("spec", "another")
+ # Rails::SourceAnnotationExtractor::Annotation.register_directories("spec", "another")
def self.register_directories(*dirs)
directories.push(*dirs)
end
@@ -39,7 +39,7 @@ module Rails
end
# Registers new Annotations File Extensions
- # SourceAnnotationExtractor::Annotation.register_extensions("css", "scss", "sass", "less", "js") { |tag| /\/\/\s*(#{tag}):?\s*(.*)$/ }
+ # Rails::SourceAnnotationExtractor::Annotation.register_extensions("css", "scss", "sass", "less", "js") { |tag| /\/\/\s*(#{tag}):?\s*(.*)$/ }
def self.register_extensions(*exts, &block)
extensions[/\.(#{exts.join("|")})$/] = block
end
@@ -69,7 +69,7 @@ module Rails
#
# Directories may also be explicitly set using the <tt>:dirs</tt> key in +options+.
#
- # SourceAnnotationExtractor.enumerate 'TODO|FIXME', dirs: %w(app lib), tag: true
+ # Rails::SourceAnnotationExtractor.enumerate 'TODO|FIXME', dirs: %w(app lib), tag: true
#
# If +options+ has a <tt>:tag</tt> flag, it will be passed to each annotation's +to_s+.
#
@@ -95,9 +95,9 @@ module Rails
end
# Returns a hash that maps filenames under +dir+ (recursively) to arrays
- # with their annotations. Only files with annotations are included. Files
- # with extension +.builder+, +.rb+, +.rake+, +.yml+, +.yaml+, +.ruby+,
- # +.css+, +.js+ and +.erb+ are taken into account.
+ # with their annotations. Files with extensions registered in
+ # <tt>Rails::SourceAnnotationExtractor::Annotation.extensions</tt> are
+ # taken into account. Only files with annotations are included.
def find_in(dir)
results = {}
diff --git a/railties/test/app_loader_test.rb b/railties/test/app_loader_test.rb
index bb556f1968..c7a6bdee1b 100644
--- a/railties/test/app_loader_test.rb
+++ b/railties/test/app_loader_test.rb
@@ -40,13 +40,13 @@ class AppLoaderTest < ActiveSupport::TestCase
test "is not in a Rails application if #{exe} is not found in the current or parent directories" do
def loader.find_executables; end
- assert !loader.exec_app
+ assert_not loader.exec_app
end
test "is not in a Rails application if #{exe} exists but is a folder" do
FileUtils.mkdir_p(exe)
- assert !loader.exec_app
+ assert_not loader.exec_app
end
["APP_PATH", "ENGINE_PATH"].each do |keyword|
@@ -61,7 +61,7 @@ class AppLoaderTest < ActiveSupport::TestCase
test "is not in a Rails application if #{exe} exists but doesn't contain #{keyword}" do
write exe
- assert !loader.exec_app
+ assert_not loader.exec_app
end
test "is in a Rails application if parent directory has #{exe} containing #{keyword} and chdirs to the root directory" do
diff --git a/railties/test/application/assets_test.rb b/railties/test/application/assets_test.rb
index 0d3262d6f6..9ef123c5b6 100644
--- a/railties/test/application/assets_test.rb
+++ b/railties/test/application/assets_test.rb
@@ -76,7 +76,7 @@ module ApplicationTests
# Load app env
app "production"
- assert !defined?(Uglifier)
+ assert_not defined?(Uglifier)
get "/assets/demo.js"
assert_match "alert()", last_response.body
assert defined?(Uglifier)
@@ -270,10 +270,10 @@ module ApplicationTests
app "production"
# Checking if Uglifier is defined we can know if Sprockets was reached or not
- assert !defined?(Uglifier)
+ assert_not defined?(Uglifier)
get "/assets/#{asset_path}"
assert_match "alert()", last_response.body
- assert !defined?(Uglifier)
+ assert_not defined?(Uglifier)
end
test "precompile properly refers files referenced with asset_path" do
diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb
index 420abe8673..84606d3b90 100644
--- a/railties/test/application/configuration_test.rb
+++ b/railties/test/application/configuration_test.rb
@@ -361,7 +361,7 @@ module ApplicationTests
end
RUBY
- assert !$prepared
+ assert_not $prepared
app "development"
@@ -1417,7 +1417,7 @@ module ApplicationTests
assert_equal "XML", last_response.body
end
- test "Rails.application#env_config exists and include some existing parameters" do
+ test "Rails.application#env_config exists and includes some existing parameters" do
make_basic_app
assert_equal app.env_config["action_dispatch.parameter_filter"], app.config.filter_parameters
diff --git a/railties/test/application/initializers/frameworks_test.rb b/railties/test/application/initializers/frameworks_test.rb
index e631318f82..1530ea82d6 100644
--- a/railties/test/application/initializers/frameworks_test.rb
+++ b/railties/test/application/initializers/frameworks_test.rb
@@ -226,7 +226,7 @@ module ApplicationTests
rails %w(generate model post title:string)
rails %w(db:migrate db:schema:cache:dump db:rollback)
require "#{app_path}/config/environment"
- assert !ActiveRecord::Base.connection.schema_cache.data_sources("posts")
+ assert_not ActiveRecord::Base.connection.schema_cache.data_sources("posts")
end
test "active record establish_connection uses Rails.env if DATABASE_URL is not set" do
diff --git a/railties/test/application/middleware/sendfile_test.rb b/railties/test/application/middleware/sendfile_test.rb
index 9def3a0ce7..818ad61c64 100644
--- a/railties/test/application/middleware/sendfile_test.rb
+++ b/railties/test/application/middleware/sendfile_test.rb
@@ -29,7 +29,7 @@ module ApplicationTests
simple_controller
get "/"
- assert !last_response.headers["X-Sendfile"]
+ assert_not last_response.headers["X-Sendfile"]
assert_equal File.read(__FILE__), last_response.body
end
diff --git a/railties/test/application/middleware/session_test.rb b/railties/test/application/middleware/session_test.rb
index a17988235a..9182a63ab7 100644
--- a/railties/test/application/middleware/session_test.rb
+++ b/railties/test/application/middleware/session_test.rb
@@ -31,7 +31,7 @@ module ApplicationTests
add_to_config "config.force_ssl = true"
add_to_config "config.ssl_options = { secure_cookies: false }"
require "#{app_path}/config/environment"
- assert !app.config.session_options[:secure]
+ assert_not app.config.session_options[:secure]
end
test "session is not loaded if it's not used" do
@@ -51,7 +51,7 @@ module ApplicationTests
get "/"
assert last_request.env["HTTP_COOKIE"]
- assert !last_response.headers["Set-Cookie"]
+ assert_not last_response.headers["Set-Cookie"]
end
test "session is empty and isn't saved on unverified request when using :null_session protect method" do
diff --git a/railties/test/application/rake/dbs_test.rb b/railties/test/application/rake/dbs_test.rb
index 5b4c42c189..0594236b1f 100644
--- a/railties/test/application/rake/dbs_test.rb
+++ b/railties/test/application/rake/dbs_test.rb
@@ -34,7 +34,7 @@ module ApplicationTests
assert_equal expected_database, ActiveRecord::Base.connection_config[:database] if environment_loaded
output = rails("db:drop")
assert_match(/Dropped database/, output)
- assert !File.exist?(expected_database)
+ assert_not File.exist?(expected_database)
end
end
diff --git a/railties/test/application/rake/multi_dbs_test.rb b/railties/test/application/rake/multi_dbs_test.rb
index da5ae8376a..07d96fcb56 100644
--- a/railties/test/application/rake/multi_dbs_test.rb
+++ b/railties/test/application/rake/multi_dbs_test.rb
@@ -26,7 +26,7 @@ module ApplicationTests
output = rails("db:drop")
assert_match(/Dropped database/, output)
assert_match_namespace(namespace, output)
- assert !File.exist?(expected_database)
+ assert_not File.exist?(expected_database)
end
end
@@ -40,7 +40,7 @@ module ApplicationTests
output = rails("db:drop:#{namespace}")
assert_match(/Dropped database/, output)
assert_match_namespace(namespace, output)
- assert !File.exist?(expected_database)
+ assert_not File.exist?(expected_database)
end
end
diff --git a/railties/test/application/rake_test.rb b/railties/test/application/rake_test.rb
index d94caf5fe0..1522a2bbc5 100644
--- a/railties/test/application/rake_test.rb
+++ b/railties/test/application/rake_test.rb
@@ -229,7 +229,7 @@ module ApplicationTests
def test_rake_clear_schema_cache
rails "db:schema:cache:dump", "db:schema:cache:clear"
- assert !File.exist?(File.join(app_path, "db", "schema_cache.yml"))
+ assert_not File.exist?(File.join(app_path, "db", "schema_cache.yml"))
end
def test_copy_templates
diff --git a/railties/test/commands/dbconsole_test.rb b/railties/test/commands/dbconsole_test.rb
index 6ad96b28c7..0aea21051a 100644
--- a/railties/test/commands/dbconsole_test.rb
+++ b/railties/test/commands/dbconsole_test.rb
@@ -123,31 +123,31 @@ class Rails::DBConsoleTest < ActiveSupport::TestCase
def test_mysql
start(adapter: "mysql2", database: "db")
- assert !aborted
+ assert_not aborted
assert_equal [%w[mysql mysql5], "db"], dbconsole.find_cmd_and_exec_args
end
def test_mysql_full
start(adapter: "mysql2", database: "db", host: "locahost", port: 1234, socket: "socket", username: "user", password: "qwerty", encoding: "UTF-8")
- assert !aborted
+ assert_not aborted
assert_equal [%w[mysql mysql5], "--host=locahost", "--port=1234", "--socket=socket", "--user=user", "--default-character-set=UTF-8", "-p", "db"], dbconsole.find_cmd_and_exec_args
end
def test_mysql_include_password
start({ adapter: "mysql2", database: "db", username: "user", password: "qwerty" }, ["-p"])
- assert !aborted
+ assert_not aborted
assert_equal [%w[mysql mysql5], "--user=user", "--password=qwerty", "db"], dbconsole.find_cmd_and_exec_args
end
def test_postgresql
start(adapter: "postgresql", database: "db")
- assert !aborted
+ assert_not aborted
assert_equal ["psql", "db"], dbconsole.find_cmd_and_exec_args
end
def test_postgresql_full
start(adapter: "postgresql", database: "db", username: "user", password: "q1w2e3", host: "host", port: 5432)
- assert !aborted
+ assert_not aborted
assert_equal ["psql", "db"], dbconsole.find_cmd_and_exec_args
assert_equal "user", ENV["PGUSER"]
assert_equal "host", ENV["PGHOST"]
@@ -157,7 +157,7 @@ class Rails::DBConsoleTest < ActiveSupport::TestCase
def test_postgresql_include_password
start({ adapter: "postgresql", database: "db", username: "user", password: "q1w2e3" }, ["-p"])
- assert !aborted
+ assert_not aborted
assert_equal ["psql", "db"], dbconsole.find_cmd_and_exec_args
assert_equal "user", ENV["PGUSER"]
assert_equal "q1w2e3", ENV["PGPASSWORD"]
@@ -165,13 +165,13 @@ class Rails::DBConsoleTest < ActiveSupport::TestCase
def test_sqlite3
start(adapter: "sqlite3", database: "db.sqlite3")
- assert !aborted
+ assert_not aborted
assert_equal ["sqlite3", Rails.root.join("db.sqlite3").to_s], dbconsole.find_cmd_and_exec_args
end
def test_sqlite3_mode
start({ adapter: "sqlite3", database: "db.sqlite3" }, ["--mode", "html"])
- assert !aborted
+ assert_not aborted
assert_equal ["sqlite3", "-html", Rails.root.join("db.sqlite3").to_s], dbconsole.find_cmd_and_exec_args
end
@@ -182,27 +182,27 @@ class Rails::DBConsoleTest < ActiveSupport::TestCase
def test_sqlite3_db_absolute_path
start(adapter: "sqlite3", database: "/tmp/db.sqlite3")
- assert !aborted
+ assert_not aborted
assert_equal ["sqlite3", "/tmp/db.sqlite3"], dbconsole.find_cmd_and_exec_args
end
def test_sqlite3_db_without_defined_rails_root
Rails.stub(:respond_to?, false) do
start(adapter: "sqlite3", database: "config/db.sqlite3")
- assert !aborted
+ assert_not aborted
assert_equal ["sqlite3", Rails.root.join("../config/db.sqlite3").to_s], dbconsole.find_cmd_and_exec_args
end
end
def test_oracle
start(adapter: "oracle", database: "db", username: "user", password: "secret")
- assert !aborted
+ assert_not aborted
assert_equal ["sqlplus", "user@db"], dbconsole.find_cmd_and_exec_args
end
def test_oracle_include_password
start({ adapter: "oracle", database: "db", username: "user", password: "secret" }, ["-p"])
- assert !aborted
+ assert_not aborted
assert_equal ["sqlplus", "user/secret@db"], dbconsole.find_cmd_and_exec_args
end
diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb
index c3809a912b..9b80cfa406 100644
--- a/railties/test/generators/app_generator_test.rb
+++ b/railties/test/generators/app_generator_test.rb
@@ -312,7 +312,7 @@ class AppGeneratorTest < Rails::Generators::TestCase
def test_active_storage_mini_magick_gem
run_generator
- assert_file "Gemfile", /^# gem 'mini_magick'/
+ assert_file "Gemfile", /^# gem 'image_processing'/
end
def test_mini_magick_gem_when_skip_active_storage_is_given
@@ -320,7 +320,7 @@ class AppGeneratorTest < Rails::Generators::TestCase
run_generator [app_root, "--skip-active-storage"]
assert_file "#{app_root}/Gemfile" do |content|
- assert_no_match(/gem 'mini_magick'/, content)
+ assert_no_match(/gem 'image_processing'/, content)
end
end
@@ -816,9 +816,18 @@ class AppGeneratorTest < Rails::Generators::TestCase
def test_bootsnap
run_generator
- assert_gem "bootsnap"
- assert_file "config/boot.rb" do |content|
- assert_match(/require 'bootsnap\/setup'/, content)
+ unless defined?(JRUBY_VERSION)
+ assert_gem "bootsnap"
+ assert_file "config/boot.rb" do |content|
+ assert_match(/require 'bootsnap\/setup'/, content)
+ end
+ else
+ assert_file "Gemfile" do |content|
+ assert_no_match(/bootsnap/, content)
+ end
+ assert_file "config/boot.rb" do |content|
+ assert_no_match(/require 'bootsnap\/setup'/, content)
+ end
end
end
@@ -851,7 +860,7 @@ class AppGeneratorTest < Rails::Generators::TestCase
assert_match(/ruby '#{RUBY_VERSION}'/, content)
end
assert_file ".ruby-version" do |content|
- assert_match(/#{RUBY_VERSION}/, content)
+ assert_match(/#{RUBY_ENGINE}-#{RUBY_ENGINE_VERSION}/, content)
end
end
diff --git a/railties/test/generators/channel_generator_test.rb b/railties/test/generators/channel_generator_test.rb
index e238197eba..e543cc11b8 100644
--- a/railties/test/generators/channel_generator_test.rb
+++ b/railties/test/generators/channel_generator_test.rb
@@ -76,4 +76,14 @@ class ChannelGeneratorTest < Rails::Generators::TestCase
assert_file "app/channels/application_cable/connection.rb"
assert_file "app/assets/javascripts/cable.js"
end
+
+ def test_channel_suffix_is_not_duplicated
+ run_generator ["chat_channel"]
+
+ assert_no_file "app/channels/chat_channel_channel.rb"
+ assert_file "app/channels/chat_channel.rb"
+
+ assert_no_file "app/assets/javascripts/channels/chat_channel.js"
+ assert_file "app/assets/javascripts/channels/chat.js"
+ end
end
diff --git a/railties/test/generators/controller_generator_test.rb b/railties/test/generators/controller_generator_test.rb
index 91e4a86775..021004c9b8 100644
--- a/railties/test/generators/controller_generator_test.rb
+++ b/railties/test/generators/controller_generator_test.rb
@@ -116,4 +116,26 @@ class ControllerGeneratorTest < Rails::Generators::TestCase
assert_no_match(/namespace :admin/, routes)
end
end
+
+ def test_controller_suffix_is_not_duplicated
+ run_generator ["account_controller"]
+
+ assert_no_file "app/controllers/account_controller_controller.rb"
+ assert_file "app/controllers/account_controller.rb"
+
+ assert_no_file "app/views/account_controller/"
+ assert_file "app/views/account/"
+
+ assert_no_file "test/controllers/account_controller_controller_test.rb"
+ assert_file "test/controllers/account_controller_test.rb"
+
+ assert_no_file "app/helpers/account_controller_helper.rb"
+ assert_file "app/helpers/account_helper.rb"
+
+ assert_no_file "app/assets/javascripts/account_controller.js"
+ assert_file "app/assets/javascripts/account.js"
+
+ assert_no_file "app/assets/stylesheets/account_controller.css"
+ assert_file "app/assets/stylesheets/account.css"
+ end
end
diff --git a/railties/test/generators/job_generator_test.rb b/railties/test/generators/job_generator_test.rb
index 13276fac65..234ba6dad7 100644
--- a/railties/test/generators/job_generator_test.rb
+++ b/railties/test/generators/job_generator_test.rb
@@ -35,4 +35,14 @@ class JobGeneratorTest < Rails::Generators::TestCase
assert_match(/class ApplicationJob < ActiveJob::Base/, job)
end
end
+
+ def test_job_suffix_is_not_duplicated
+ run_generator ["notifier_job"]
+
+ assert_no_file "app/jobs/notifier_job_job.rb"
+ assert_file "app/jobs/notifier_job.rb"
+
+ assert_no_file "test/jobs/notifier_job_job_test.rb"
+ assert_file "test/jobs/notifier_job_test.rb"
+ end
end
diff --git a/railties/test/rails_info_test.rb b/railties/test/rails_info_test.rb
index 43b60b9144..50522c1be6 100644
--- a/railties/test/rails_info_test.rb
+++ b/railties/test/rails_info_test.rb
@@ -9,7 +9,7 @@ class InfoTest < ActiveSupport::TestCase
property("Bogus") { raise }
end
end
- assert !property_defined?("Bogus")
+ assert_not property_defined?("Bogus")
end
def test_property_with_string
diff --git a/railties/test/railties/engine_test.rb b/railties/test/railties/engine_test.rb
index a59c63f343..9a3ddc8d5e 100644
--- a/railties/test/railties/engine_test.rb
+++ b/railties/test/railties/engine_test.rb
@@ -226,7 +226,7 @@ module RailtiesTest
require "rdoc/task"
require "rake/testtask"
Rails.application.load_tasks
- assert !Rake::Task.task_defined?("bukkits:install:migrations")
+ assert_not Rake::Task.task_defined?("bukkits:install:migrations")
end
test "puts its lib directory on load path" do
@@ -745,7 +745,7 @@ YAML
assert_equal "bukkits", Bukkits::Engine.engine_name
assert_equal Bukkits.railtie_namespace, Bukkits::Engine
assert ::Bukkits::MyMailer.method_defined?(:foo_url)
- assert !::Bukkits::MyMailer.method_defined?(:bar_url)
+ assert_not ::Bukkits::MyMailer.method_defined?(:bar_url)
get("/bukkits/from_app")
assert_equal "false", last_response.body
diff --git a/railties/test/railties/railtie_test.rb b/railties/test/railties/railtie_test.rb
index 359ab0fdae..7c3d1e3759 100644
--- a/railties/test/railties/railtie_test.rb
+++ b/railties/test/railties/railtie_test.rb
@@ -65,7 +65,7 @@ module RailtiesTest
test "railtie can add to_prepare callbacks" do
$to_prepare = false
class Foo < Rails::Railtie ; config.to_prepare { $to_prepare = true } ; end
- assert !$to_prepare
+ assert_not $to_prepare
require "#{app_path}/config/environment"
require "rack/test"
extend Rack::Test::Methods
@@ -91,7 +91,7 @@ module RailtiesTest
test "railtie can add after_initialize callbacks" do
$after_initialize = false
class Foo < Rails::Railtie ; config.after_initialize { $after_initialize = true } ; end
- assert !$after_initialize
+ assert_not $after_initialize
require "#{app_path}/config/environment"
assert $after_initialize
end
@@ -107,7 +107,7 @@ module RailtiesTest
require "#{app_path}/config/environment"
- assert !$ran_block
+ assert_not $ran_block
require "rake"
require "rake/testtask"
require "rdoc/task"
@@ -151,7 +151,7 @@ module RailtiesTest
require "#{app_path}/config/environment"
- assert !$ran_block
+ assert_not $ran_block
Rails.application.load_generators
assert $ran_block
end
@@ -167,7 +167,7 @@ module RailtiesTest
require "#{app_path}/config/environment"
- assert !$ran_block
+ assert_not $ran_block
Rails.application.load_console
assert $ran_block
end
@@ -183,7 +183,7 @@ module RailtiesTest
require "#{app_path}/config/environment"
- assert !$ran_block
+ assert_not $ran_block
Rails.application.load_runner
assert $ran_block
end
@@ -197,7 +197,7 @@ module RailtiesTest
end
end
- assert !$ran_block
+ assert_not $ran_block
require "#{app_path}/config/environment"
assert $ran_block
end