aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.travis.yml6
-rw-r--r--Gemfile3
-rw-r--r--Gemfile.lock13
-rw-r--r--actioncable/lib/action_cable/connection/web_socket.rb2
-rw-r--r--actionpack/lib/action_controller/metal/params_wrapper.rb2
-rw-r--r--actionpack/lib/action_controller/metal/strong_parameters.rb11
-rw-r--r--actionpack/lib/action_dispatch/http/filter_parameters.rb2
-rw-r--r--actionpack/lib/action_dispatch/http/parameter_filter.rb2
-rw-r--r--actionpack/lib/action_dispatch/system_testing/server.rb13
-rw-r--r--actionpack/lib/action_dispatch/testing/integration.rb3
-rw-r--r--actionpack/test/controller/parameters/mutators_test.rb2
-rw-r--r--actionpack/test/controller/params_wrapper_test.rb8
-rw-r--r--actionpack/test/dispatch/request_test.rb13
-rw-r--r--actionview/CHANGELOG.md6
-rw-r--r--actionview/lib/action_view/helpers/url_helper.rb5
-rw-r--r--actionview/test/template/url_helper_test.rb16
-rw-r--r--activejob/.gitignore1
-rw-r--r--activemodel/lib/active_model/naming.rb2
-rw-r--r--activerecord/CHANGELOG.md30
-rw-r--r--activerecord/lib/active_record/associations/association.rb2
-rw-r--r--activerecord/lib/active_record/associations/collection_association.rb3
-rw-r--r--activerecord/lib/active_record/associations/join_dependency.rb6
-rw-r--r--activerecord/lib/active_record/associations/singular_association.rb7
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb64
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb7
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb17
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql/database_statements.rb19
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql/schema_statements.rb7
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb32
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb6
-rw-r--r--activerecord/lib/active_record/fixtures.rb4
-rw-r--r--activerecord/lib/active_record/locking/optimistic.rb9
-rw-r--r--activerecord/lib/active_record/persistence.rb2
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/array_test.rb6
-rw-r--r--activerecord/test/cases/associations/has_one_associations_test.rb9
-rw-r--r--activerecord/test/cases/connection_pool_test.rb13
-rw-r--r--activerecord/test/cases/fixtures_test.rb32
-rw-r--r--activerecord/test/cases/json_shared_test_cases.rb19
-rw-r--r--activerecord/test/cases/locking_test.rb71
-rw-r--r--activerecord/test/cases/migration/references_foreign_key_test.rb10
-rw-r--r--activerecord/test/cases/migration_test.rb10
-rw-r--r--activerecord/test/cases/nested_attributes_test.rb2
-rw-r--r--activerecord/test/cases/query_cache_test.rb46
-rw-r--r--activerecord/test/cases/relation/merging_test.rb2
-rw-r--r--activerecord/test/cases/relation/or_test.rb25
-rw-r--r--activerecord/test/cases/relation_test.rb23
-rw-r--r--activerecord/test/fixtures/naked/yml/parrots.yml1
-rw-r--r--activerecord/test/models/comment.rb1
-rw-r--r--activerecord/test/schema/schema.rb5
-rw-r--r--activesupport/test/core_ext/duration_test.rb2
-rw-r--r--guides/assets/images/belongs_to.pngbin25803 -> 22147 bytes
-rw-r--r--guides/assets/images/getting_started/article_with_comments.pngbin22560 -> 13884 bytes
-rw-r--r--guides/assets/images/getting_started/challenge.pngbin21690 -> 20347 bytes
-rw-r--r--guides/assets/images/getting_started/confirm_dialog.pngbin18809 -> 17507 bytes
-rw-r--r--guides/assets/images/getting_started/forbidden_attributes_for_new_article.pngbin10783 -> 9851 bytes
-rw-r--r--guides/assets/images/getting_started/form_with_errors.pngbin12447 -> 11665 bytes
-rw-r--r--guides/assets/images/getting_started/index_action_with_edit_link.pngbin10209 -> 9703 bytes
-rw-r--r--guides/assets/images/getting_started/new_article.pngbin3579 -> 3193 bytes
-rw-r--r--guides/assets/images/getting_started/rails_welcome.pngbin1053549 -> 732190 bytes
-rw-r--r--guides/assets/images/getting_started/routing_error_no_controller.pngbin4186 -> 3869 bytes
-rw-r--r--guides/assets/images/getting_started/show_action_for_articles.pngbin2965 -> 2901 bytes
-rw-r--r--guides/assets/images/getting_started/template_is_missing_articles_new.pngbin587962 -> 472167 bytes
-rw-r--r--guides/assets/images/getting_started/unknown_action_create_for_articles.pngbin5327 -> 4808 bytes
-rw-r--r--guides/assets/images/getting_started/unknown_action_new_for_articles.pngbin5481 -> 4933 bytes
-rw-r--r--guides/assets/images/habtm.pngbin49332 -> 47284 bytes
-rw-r--r--guides/assets/images/has_many.pngbin28919 -> 24300 bytes
-rw-r--r--guides/assets/images/has_many_through.pngbin79428 -> 78099 bytes
-rw-r--r--guides/assets/images/has_one.pngbin29072 -> 27547 bytes
-rw-r--r--guides/assets/images/has_one_through.pngbin72434 -> 70130 bytes
-rw-r--r--guides/assets/images/header_backdrop.pngbin224 -> 206 bytes
-rw-r--r--guides/assets/images/i18n/demo_html_safe.pngbin10073 -> 9860 bytes
-rw-r--r--guides/assets/images/i18n/demo_localized_pirate.pngbin11485 -> 11214 bytes
-rw-r--r--guides/assets/images/i18n/demo_translated_en.pngbin9325 -> 9069 bytes
-rw-r--r--guides/assets/images/i18n/demo_translated_pirate.pngbin10202 -> 9974 bytes
-rw-r--r--guides/assets/images/i18n/demo_translation_missing.pngbin10260 -> 9984 bytes
-rw-r--r--guides/assets/images/i18n/demo_untranslated.pngbin9224 -> 8985 bytes
-rw-r--r--guides/assets/images/icons/callouts/14.pngbin246 -> 190 bytes
-rw-r--r--guides/assets/images/icons/example.pngbin2078 -> 2052 bytes
-rw-r--r--guides/assets/images/icons/home.pngbin1163 -> 1134 bytes
-rw-r--r--guides/assets/images/icons/important.pngbin2451 -> 2426 bytes
-rw-r--r--guides/assets/images/icons/next.pngbin1146 -> 1111 bytes
-rw-r--r--guides/assets/images/icons/note.pngbin2155 -> 2096 bytes
-rw-r--r--guides/assets/images/icons/prev.pngbin1126 -> 1093 bytes
-rw-r--r--guides/assets/images/icons/tip.pngbin2248 -> 2170 bytes
-rw-r--r--guides/assets/images/icons/up.pngbin1133 -> 1106 bytes
-rw-r--r--guides/assets/images/polymorphic.pngbin66415 -> 65417 bytes
-rw-r--r--guides/assets/images/rails4_features.pngbin67766 -> 65840 bytes
-rw-r--r--guides/assets/images/session_fixation.pngbin38451 -> 38296 bytes
-rw-r--r--guides/assets/images/tab_yellow.pngbin1441 -> 1395 bytes
-rw-r--r--guides/bug_report_templates/active_record_migrations_gem.rb6
-rw-r--r--guides/bug_report_templates/active_record_migrations_master.rb6
-rw-r--r--guides/source/active_record_querying.md2
-rw-r--r--guides/source/asset_pipeline.md14
-rw-r--r--guides/source/configuring.md2
-rw-r--r--guides/source/development_dependencies_install.md2
-rw-r--r--guides/source/generators.md8
-rw-r--r--guides/source/layouts_and_rendering.md2
-rw-r--r--guides/source/working_with_javascript_in_rails.md6
-rw-r--r--railties/CHANGELOG.md4
-rw-r--r--railties/lib/rails/commands/test/test_command.rb2
-rw-r--r--railties/lib/rails/generators/actions.rb1
-rw-r--r--railties/test/generators/actions_test.rb7
-rw-r--r--tools/README.md2
-rw-r--r--tools/test.rb2
105 files changed, 491 insertions, 179 deletions
diff --git a/.travis.yml b/.travis.yml
index de708c509c..847e11900a 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -98,17 +98,17 @@ matrix:
- "GEM=ar:postgresql POSTGRES=9.2"
addons:
postgresql: "9.2"
- - rvm: jruby-9.1.10.0
+ - rvm: jruby-9.1.12.0
jdk: oraclejdk8
env:
- "GEM=ap"
- - rvm: jruby-9.1.10.0
+ - rvm: jruby-9.1.12.0
jdk: oraclejdk8
env:
- "GEM=am,amo,aj"
allow_failures:
- rvm: ruby-head
- - rvm: jruby-9.1.10.0
+ - rvm: jruby-9.1.12.0
- env: "GEM=ac:integration"
fast_finish: true
diff --git a/Gemfile b/Gemfile
index 2a1b9fa7ef..23b9b34b77 100644
--- a/Gemfile
+++ b/Gemfile
@@ -33,9 +33,6 @@ gem "bcrypt", "~> 3.1.11", require: false
# sprockets.
gem "uglifier", ">= 1.3.0", require: false
-# FIXME: Pending rb-inotify 0.9.9 release
-gem "rb-inotify", github: "guard/rb-inotify", branch: "master", require: false
-
# Explicitly avoid 1.x that doesn't support Ruby 2.4+
gem "json", ">= 2.0.0"
diff --git a/Gemfile.lock b/Gemfile.lock
index 77eaa47cb8..9f636b0307 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -7,14 +7,6 @@ GIT
pg (>= 0.17, < 0.20)
GIT
- remote: https://github.com/guard/rb-inotify.git
- revision: 7e3c714a09ae2b38d2620835e794150d8857cd49
- branch: master
- specs:
- rb-inotify (0.9.9)
- ffi (~> 1.0)
-
-GIT
remote: https://github.com/matthewd/websocket-client-simple.git
revision: e161305f1a466b9398d86df3b1731b03362da91b
branch: close-race
@@ -25,7 +17,7 @@ GIT
GIT
remote: https://github.com/rails/arel.git
- revision: 5db56a513286814991c27000af2c0243cc19d1e2
+ revision: 67a51c62f4e19390cd8eb408596ca48bb0806362
specs:
arel (8.0.0)
@@ -266,6 +258,8 @@ GEM
rake
rake (12.0.0)
rb-fsevent (0.9.8)
+ rb-inotify (0.9.9)
+ ffi (~> 1.0)
rdoc (5.1.0)
redcarpet (3.2.3)
redis (3.3.3)
@@ -413,7 +407,6 @@ DEPENDENCIES
rack-cache (~> 1.2)
rails!
rake (>= 11.1)
- rb-inotify!
redcarpet (~> 3.2.3)
redis
resque
diff --git a/actioncable/lib/action_cable/connection/web_socket.rb b/actioncable/lib/action_cable/connection/web_socket.rb
index 03eb6e2ea8..27ae499f29 100644
--- a/actioncable/lib/action_cable/connection/web_socket.rb
+++ b/actioncable/lib/action_cable/connection/web_socket.rb
@@ -3,7 +3,7 @@ require "websocket/driver"
module ActionCable
module Connection
# Wrap the real socket to minimize the externally-presented API
- class WebSocket
+ class WebSocket # :nodoc:
def initialize(env, event_target, event_loop, protocols: ActionCable::INTERNAL[:protocols])
@websocket = ::WebSocket::Driver.websocket?(env) ? ClientSocket.new(env, event_target, event_loop, protocols) : nil
end
diff --git a/actionpack/lib/action_controller/metal/params_wrapper.rb b/actionpack/lib/action_controller/metal/params_wrapper.rb
index 68881b8402..44151c9f71 100644
--- a/actionpack/lib/action_controller/metal/params_wrapper.rb
+++ b/actionpack/lib/action_controller/metal/params_wrapper.rb
@@ -282,7 +282,7 @@ module ActionController
return false unless request.has_content_type?
ref = request.content_mime_type.ref
- _wrapper_formats.include?(ref) && _wrapper_key && !request.request_parameters[_wrapper_key]
+ _wrapper_formats.include?(ref) && _wrapper_key && !request.request_parameters.key?(_wrapper_key)
end
end
end
diff --git a/actionpack/lib/action_controller/metal/strong_parameters.rb b/actionpack/lib/action_controller/metal/strong_parameters.rb
index cd99e3125f..cd6a0c0b98 100644
--- a/actionpack/lib/action_controller/metal/strong_parameters.rb
+++ b/actionpack/lib/action_controller/metal/strong_parameters.rb
@@ -245,7 +245,7 @@ module ActionController
# oddity: "Heavy stone crab"
# })
# params.to_h
- # # => ActionController::UnfilteredParameters: unable to convert unfiltered parameters to hash
+ # # => ActionController::UnfilteredParameters: unable to convert unpermitted parameters to hash
#
# safe_params = params.permit(:name)
# safe_params.to_h # => {"name"=>"Senjougahara Hitagi"}
@@ -265,7 +265,7 @@ module ActionController
# oddity: "Heavy stone crab"
# })
# params.to_hash
- # # => ActionController::UnfilteredParameters: unable to convert unfiltered parameters to hash
+ # # => ActionController::UnfilteredParameters: unable to convert unpermitted parameters to hash
#
# safe_params = params.permit(:name)
# safe_params.to_hash # => {"name"=>"Senjougahara Hitagi"}
@@ -281,6 +281,10 @@ module ActionController
# nationality: "Danish"
# })
# params.to_query
+ # # => ActionController::UnfilteredParameters: unable to convert unpermitted parameters to hash
+ #
+ # safe_params = params.permit(:name, :nationality)
+ # safe_params.to_query
# # => "name=David&nationality=Danish"
#
# An optional namespace can be passed to enclose key names:
@@ -289,7 +293,8 @@ module ActionController
# name: "David",
# nationality: "Danish"
# })
- # params.to_query("user")
+ # safe_params = params.permit(:name, :nationality)
+ # safe_params.to_query("user")
# # => "user%5Bname%5D=David&user%5Bnationality%5D=Danish"
#
# The string pairs "key=value" that conform the query string
diff --git a/actionpack/lib/action_dispatch/http/filter_parameters.rb b/actionpack/lib/action_dispatch/http/filter_parameters.rb
index e584b84d92..077ab2561f 100644
--- a/actionpack/lib/action_dispatch/http/filter_parameters.rb
+++ b/actionpack/lib/action_dispatch/http/filter_parameters.rb
@@ -74,7 +74,7 @@ module ActionDispatch
PAIR_RE = %r{(#{KV_RE})=(#{KV_RE})}
def filtered_query_string # :doc:
query_string.gsub(PAIR_RE) do |_|
- parameter_filter.filter([[$1, $2]]).first.join("=")
+ parameter_filter.filter($1 => $2).first.join("=")
end
end
end
diff --git a/actionpack/lib/action_dispatch/http/parameter_filter.rb b/actionpack/lib/action_dispatch/http/parameter_filter.rb
index 889f55a52a..1d2b4b902b 100644
--- a/actionpack/lib/action_dispatch/http/parameter_filter.rb
+++ b/actionpack/lib/action_dispatch/http/parameter_filter.rb
@@ -54,7 +54,7 @@ module ActionDispatch
end
def call(original_params, parents = [])
- filtered_params = {}
+ filtered_params = original_params.class.new
original_params.each do |key, value|
parents.push(key) if deep_regexps
diff --git a/actionpack/lib/action_dispatch/system_testing/server.rb b/actionpack/lib/action_dispatch/system_testing/server.rb
index 4a214ef713..89ca6944d9 100644
--- a/actionpack/lib/action_dispatch/system_testing/server.rb
+++ b/actionpack/lib/action_dispatch/system_testing/server.rb
@@ -3,6 +3,12 @@ require "rack/handler/puma"
module ActionDispatch
module SystemTesting
class Server # :nodoc:
+ class << self
+ attr_accessor :silence_puma
+ end
+
+ self.silence_puma = false
+
def run
register
setup
@@ -11,7 +17,12 @@ module ActionDispatch
private
def register
Capybara.register_server :rails_puma do |app, port, host|
- Rack::Handler::Puma.run(app, Port: port, Threads: "0:1")
+ Rack::Handler::Puma.run(
+ app,
+ Port: port,
+ Threads: "0:1",
+ Silent: self.class.silence_puma
+ )
end
end
diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb
index 2416c58817..4973a8f2f2 100644
--- a/actionpack/lib/action_dispatch/testing/integration.rb
+++ b/actionpack/lib/action_dispatch/testing/integration.rb
@@ -338,8 +338,7 @@ module ActionDispatch
@integration_session = nil
end
- %w(get post patch put head delete cookies assigns
- xml_http_request xhr get_via_redirect post_via_redirect).each do |method|
+ %w(get post patch put head delete cookies assigns).each do |method|
define_method(method) do |*args|
# reset the html_document variable, except for cookies/assigns calls
unless method == "cookies" || method == "assigns"
diff --git a/actionpack/test/controller/parameters/mutators_test.rb b/actionpack/test/controller/parameters/mutators_test.rb
index 2c36f488c6..3fe7340782 100644
--- a/actionpack/test/controller/parameters/mutators_test.rb
+++ b/actionpack/test/controller/parameters/mutators_test.rb
@@ -35,7 +35,7 @@ class ParametersMutatorsTest < ActiveSupport::TestCase
end
test "delete returns nil when the key is not present" do
- assert_equal nil, @params[:person].delete(:first_name)
+ assert_nil @params[:person].delete(:first_name)
end
test "delete returns the value of the given block when the key is not present" do
diff --git a/actionpack/test/controller/params_wrapper_test.rb b/actionpack/test/controller/params_wrapper_test.rb
index 2a41d57b26..4cbb28ef60 100644
--- a/actionpack/test/controller/params_wrapper_test.rb
+++ b/actionpack/test/controller/params_wrapper_test.rb
@@ -170,6 +170,14 @@ class ParamsWrapperTest < ActionController::TestCase
end
end
+ def test_no_double_wrap_if_key_exists_and_value_is_nil
+ with_default_wrapper_options do
+ @request.env["CONTENT_TYPE"] = "application/json"
+ post :parse, params: { "user" => nil }
+ assert_parameters("user" => nil)
+ end
+ end
+
def test_nested_params
with_default_wrapper_options do
@request.env["CONTENT_TYPE"] = "application/json"
diff --git a/actionpack/test/dispatch/request_test.rb b/actionpack/test/dispatch/request_test.rb
index 28cbde028d..899b27b962 100644
--- a/actionpack/test/dispatch/request_test.rb
+++ b/actionpack/test/dispatch/request_test.rb
@@ -1098,6 +1098,19 @@ class RequestParameterFilter < BaseRequestTest
end
end
+ test "parameter filter should maintain hash with indifferent access" do
+ test_hashes = [
+ [{ "foo" => "bar" }.with_indifferent_access, ["blah"]],
+ [{ "foo" => "bar" }.with_indifferent_access, []]
+ ]
+
+ test_hashes.each do |before_filter, filter_words|
+ parameter_filter = ActionDispatch::Http::ParameterFilter.new(filter_words)
+ assert_instance_of ActiveSupport::HashWithIndifferentAccess,
+ parameter_filter.filter(before_filter)
+ end
+ end
+
test "filtered_parameters returns params filtered" do
request = stub_request(
"action_dispatch.request.parameters" => {
diff --git a/actionview/CHANGELOG.md b/actionview/CHANGELOG.md
index ceaea40d18..916d29a540 100644
--- a/actionview/CHANGELOG.md
+++ b/actionview/CHANGELOG.md
@@ -1,3 +1,9 @@
+* Fix issues with scopes and engine on `current_page?` method.
+
+ Fixes #29401.
+
+ *Nikita Savrov*
+
* Generate field ids in `collection_check_boxes` and `collection_radio_buttons`.
This makes sure that the labels are linked up with the fields.
diff --git a/actionview/lib/action_view/helpers/url_helper.rb b/actionview/lib/action_view/helpers/url_helper.rb
index a6857101b9..b78c367921 100644
--- a/actionview/lib/action_view/helpers/url_helper.rb
+++ b/actionview/lib/action_view/helpers/url_helper.rb
@@ -552,7 +552,10 @@ module ActionView
request_uri = url_string.index("?") || check_parameters ? request.fullpath : request.path
request_uri = URI.parser.unescape(request_uri).force_encoding(Encoding::BINARY)
- url_string.chomp!("/") if url_string.start_with?("/") && url_string != "/"
+ if url_string.start_with?("/") && url_string != "/"
+ url_string.chomp!("/")
+ request_uri.chomp!("/")
+ end
if %r{^\w+://}.match?(url_string)
url_string == "#{request.protocol}#{request.host_with_port}#{request_uri}"
diff --git a/actionview/test/template/url_helper_test.rb b/actionview/test/template/url_helper_test.rb
index 30dc719ce6..bdedbeba92 100644
--- a/actionview/test/template/url_helper_test.rb
+++ b/actionview/test/template/url_helper_test.rb
@@ -15,6 +15,10 @@ class UrlHelperTest < ActiveSupport::TestCase
get "/other" => "foo#other"
get "/article/:id" => "foo#article", :as => :article
get "/category/:category" => "foo#category"
+
+ scope :engine do
+ get "/" => "foo#bar"
+ end
end
include ActionView::Helpers::UrlHelper
@@ -521,10 +525,10 @@ class UrlHelperTest < ActiveSupport::TestCase
assert current_page?("http://www.example.com/?order=desc&page=1")
end
- def test_current_page_with_not_get_verb
- @request = request_for_url("/events", method: :post)
+ def test_current_page_with_scope_that_match
+ @request = request_for_url("/engine/")
- assert !current_page?("/events")
+ assert current_page?("/engine")
end
def test_current_page_with_escaped_params
@@ -553,6 +557,12 @@ class UrlHelperTest < ActiveSupport::TestCase
assert current_page?("/posts/")
end
+ def test_current_page_with_not_get_verb
+ @request = request_for_url("/events", method: :post)
+
+ assert !current_page?("/events")
+ end
+
def test_link_unless_current
@request = request_for_url("/")
diff --git a/activejob/.gitignore b/activejob/.gitignore
deleted file mode 100644
index b3aaf55871..0000000000
--- a/activejob/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-test/dummy
diff --git a/activemodel/lib/active_model/naming.rb b/activemodel/lib/active_model/naming.rb
index 9853cf38fe..9ac56526a2 100644
--- a/activemodel/lib/active_model/naming.rb
+++ b/activemodel/lib/active_model/naming.rb
@@ -47,7 +47,7 @@ module ActiveModel
# :method: <=>
#
# :call-seq:
- # ==(other)
+ # <=>(other)
#
# Equivalent to <tt>String#<=></tt>.
#
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index f75f1a9108..d4f4041910 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,3 +1,33 @@
+* Merging two relations representing nested joins no longer transforms the joins of
+ the merged relation into LEFT OUTER JOIN. Example to clarify:
+
+ ```
+ Author.joins(:posts).merge(Post.joins(:comments))
+ # Before the change:
+ #=> SELECT ... FROM authors INNER JOIN posts ON ... LEFT OUTER JOIN comments ON...
+
+ # After the change:
+ #=> SELECT ... FROM authors INNER JOIN posts ON ... INNER JOIN comments ON...
+ ```
+
+ TODO: Add to the Rails 5.2 upgrade guide
+
+ *Maxime Handfield Lapointe*
+
+* `ActiveRecord::Persistence#touch` does not work well when optimistic locking enabled and
+ `locking_column`, without default value, is null in the database.
+
+ *bogdanvlviv*
+
+* Fix destroying existing object does not work well when optimistic locking enabled and
+ `locking column` is null in the database.
+
+ *bogdanvlviv*
+
+* Use bulk INSERT to insert fixtures for better performance.
+
+ *Kir Shatrov*
+
* Prevent making bind param if casted value is nil.
*Ryuta Kamizono*
diff --git a/activerecord/lib/active_record/associations/association.rb b/activerecord/lib/active_record/associations/association.rb
index ee2e79889f..5c45187d8f 100644
--- a/activerecord/lib/active_record/associations/association.rb
+++ b/activerecord/lib/active_record/associations/association.rb
@@ -276,7 +276,7 @@ module ActiveRecord
end
# Returns true if statement cache should be skipped on the association reader.
- def skip_statement_cache?
+ def skip_statement_cache?(scope)
reflection.has_scope? ||
scope.eager_loading? ||
klass.scope_attributes? ||
diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb
index edc53e2517..0cb17b47e8 100644
--- a/activerecord/lib/active_record/associations/collection_association.rb
+++ b/activerecord/lib/active_record/associations/collection_association.rb
@@ -300,7 +300,8 @@ module ActiveRecord
private
def find_target
- return scope.to_a if skip_statement_cache?
+ scope = self.scope
+ return scope.to_a if skip_statement_cache?(scope)
conn = klass.connection
sc = reflection.association_scope_cache(conn, owner) do
diff --git a/activerecord/lib/active_record/associations/join_dependency.rb b/activerecord/lib/active_record/associations/join_dependency.rb
index 643226267c..83e5c23cc4 100644
--- a/activerecord/lib/active_record/associations/join_dependency.rb
+++ b/activerecord/lib/active_record/associations/join_dependency.rb
@@ -104,17 +104,17 @@ module ActiveRecord
join_root.drop(1).map!(&:reflection)
end
- def join_constraints(outer_joins, join_type)
+ def join_constraints(joins_to_add, join_type)
joins = join_root.children.flat_map { |child|
make_join_constraints(join_root, child, join_type)
}
- joins.concat outer_joins.flat_map { |oj|
+ joins.concat joins_to_add.flat_map { |oj|
if join_root.match? oj.join_root
walk join_root, oj.join_root
else
oj.join_root.children.flat_map { |child|
- make_outer_joins oj.join_root, child
+ make_join_constraints(oj.join_root, child, join_type)
}
end
}
diff --git a/activerecord/lib/active_record/associations/singular_association.rb b/activerecord/lib/active_record/associations/singular_association.rb
index 91580a28d0..2b426da607 100644
--- a/activerecord/lib/active_record/associations/singular_association.rb
+++ b/activerecord/lib/active_record/associations/singular_association.rb
@@ -36,7 +36,8 @@ module ActiveRecord
end
def find_target
- return scope.take if skip_statement_cache?
+ scope = self.scope
+ return scope.take if skip_statement_cache?(scope)
conn = klass.connection
sc = reflection.association_scope_cache(conn, owner) do
@@ -63,6 +64,10 @@ module ActiveRecord
end
def _create_record(attributes, raise_error = false)
+ unless owner.persisted?
+ raise ActiveRecord::RecordNotSaved, "You cannot call create unless the parent is saved"
+ end
+
record = build_record(attributes)
yield(record) if block_given?
saved = record.save
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 c6811a4802..af5314c1d6 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
@@ -296,6 +296,9 @@ module ActiveRecord
# Inserts the given fixture into the table. Overridden in adapters that require
# something beyond a simple insert (eg. Oracle).
+ # Most of adapters should implement `insert_fixtures` that leverages bulk SQL insert.
+ # We keep this method to provide fallback
+ # for databases like sqlite that do not support bulk inserts.
def insert_fixture(fixture, table_name)
fixture = fixture.stringify_keys
@@ -308,16 +311,52 @@ module ActiveRecord
raise Fixture::FixtureError, %(table "#{table_name}" has no column named #{name.inspect}.)
end
end
- key_list = fixture.keys.map { |name| quote_column_name(name) }
- value_list = binds.map(&:value_for_database).map do |value|
- begin
- quote(value)
- rescue TypeError
- quote(YAML.dump(value))
+
+ table = Arel::Table.new(table_name)
+
+ values = binds.map do |bind|
+ value = with_yaml_fallback(bind.value_for_database)
+ [table[bind.name], value]
+ end
+
+ manager = Arel::InsertManager.new
+ manager.into(table)
+ manager.insert(values)
+ execute manager.to_sql, "Fixture Insert"
+ end
+
+ # Inserts a set of fixtures into the table. Overridden in adapters that require
+ # something beyond a simple insert (eg. Oracle).
+ def insert_fixtures(fixtures, table_name)
+ return if fixtures.empty?
+
+ columns = schema_cache.columns_hash(table_name)
+
+ values = fixtures.map do |fixture|
+ fixture = fixture.stringify_keys
+
+ unknown_columns = fixture.keys - columns.keys
+ if unknown_columns.any?
+ raise Fixture::FixtureError, %(table "#{table_name}" has no columns named #{unknown_columns.map(&:inspect).join(', ')}.)
+ end
+
+ columns.map do |name, column|
+ if fixture.key?(name)
+ type = lookup_cast_type_from_column(column)
+ bind = Relation::QueryAttribute.new(name, fixture[name], type)
+ with_yaml_fallback(bind.value_for_database)
+ else
+ Arel.sql("DEFAULT")
+ end
end
end
- execute "INSERT INTO #{quote_table_name(table_name)} (#{key_list.join(', ')}) VALUES (#{value_list.join(', ')})", "Fixture Insert"
+ table = Arel::Table.new(table_name)
+ manager = Arel::InsertManager.new
+ manager.into(table)
+ columns.each_key { |column| manager.columns << table[column] }
+ manager.values = manager.create_values_list(values)
+ execute manager.to_sql, "Fixtures Insert"
end
def empty_insert_statement_value
@@ -381,6 +420,17 @@ module ActiveRecord
end
[relation, binds]
end
+
+ # Fixture value is quoted by Arel, however scalar values
+ # are not quotable. In this case we want to convert
+ # the column value to YAML.
+ def with_yaml_fallback(value)
+ if value.is_a?(Hash) || value.is_a?(Array)
+ YAML.dump(value)
+ else
+ value
+ end
+ end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb
index a4fecc4a8e..93f4529202 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb
@@ -55,7 +55,7 @@ module ActiveRecord
create_sql << "(#{statements.join(', ')})" if statements.present?
add_table_options!(create_sql, table_options(o))
- create_sql << " AS #{@conn.to_sql(o.as)}" if o.as
+ create_sql << " AS #{to_sql(o.as)}" if o.as
create_sql
end
@@ -114,6 +114,11 @@ module ActiveRecord
sql
end
+ def to_sql(sql)
+ sql = sql.to_sql if sql.respond_to?(:to_sql)
+ sql
+ end
+
def foreign_key_in_create(from_table, to_table, options)
options = foreign_key_options(from_table, to_table, options)
accept ForeignKeyDefinition.new(from_table, to_table, options)
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 648c869915..c42e80ea2c 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -526,8 +526,25 @@ module ActiveRecord
index.using == :btree || super
end
+ def insert_fixtures(*)
+ without_sql_mode("NO_AUTO_VALUE_ON_ZERO") { super }
+ end
+
private
+ def without_sql_mode(mode)
+ result = execute("SELECT @@SESSION.sql_mode")
+ current_mode = result.first[0]
+ return yield unless current_mode.include?(mode)
+
+ sql_mode = "REPLACE(@@sql_mode, '#{mode}', '')"
+ execute("SET @@SESSION.sql_mode = #{sql_mode}")
+ yield
+ ensure
+ sql_mode = "CONCAT(@@sql_mode, ',#{mode}')"
+ execute("SET @@SESSION.sql_mode = #{sql_mode}")
+ end
+
def initialize_type_map(m)
super
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 8c67a7a80b..9f1021456b 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql/database_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql/database_statements.rb
@@ -13,15 +13,6 @@ module ActiveRecord
result
end
- # Returns an array of arrays containing the field values.
- # Order is the same as that returned by +columns+.
- def select_rows(arel, name = nil, binds = []) # :nodoc:
- select_result(arel, name, binds) do |result|
- @connection.next_result while @connection.more_results?
- result.to_a
- end
- end
-
# Executes the SQL statement in the context of this connection.
def execute(sql, name = nil)
# make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
@@ -58,16 +49,6 @@ module ActiveRecord
@connection.last_id
end
- def select_result(arel, name, binds)
- arel, binds = binds_from_relation(arel, binds)
- sql = to_sql(arel, binds)
- if without_prepared_statement?(binds)
- execute_and_free(sql, name) { |result| yield result }
- else
- exec_stmt_and_free(sql, name, binds, cache_stmt: true) { |_, result| yield result }
- end
- end
-
def exec_stmt_and_free(sql, name, binds, cache_stmt: false)
# make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
# made since we established the connection
diff --git a/activerecord/lib/active_record/connection_adapters/mysql/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/mysql/schema_statements.rb
index f9e1e046ea..fc57e41fc9 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql/schema_statements.rb
@@ -45,6 +45,13 @@ module ActiveRecord
indexes
end
+ def remove_column(table_name, column_name, type = nil, options = {})
+ if foreign_key_exists?(table_name, column: column_name)
+ remove_foreign_key(table_name, column: column_name)
+ end
+ super
+ end
+
def internal_string_options_for_primary_key
super.tap do |options|
if CHARSETS_OF_4BYTES_MAXLEN.include?(charset) && (mariadb? || version < "8.0.0")
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb
index 705e6063dc..ac5efbebeb 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb
@@ -7,30 +7,6 @@ module ActiveRecord
PostgreSQL::ExplainPrettyPrinter.new.pp(exec_query(sql, "EXPLAIN", binds))
end
- def select_value(arel, name = nil, binds = []) # :nodoc:
- select_result(arel, name, binds) do |result|
- result.getvalue(0, 0) if result.ntuples > 0 && result.nfields > 0
- end
- end
-
- def select_values(arel, name = nil, binds = []) # :nodoc:
- select_result(arel, name, binds) do |result|
- if result.nfields > 0
- result.column_values(0)
- else
- []
- end
- end
- end
-
- # Executes a SELECT query and returns an array of rows. Each row is an
- # array of field values.
- def select_rows(arel, name = nil, binds = []) # :nodoc:
- select_result(arel, name, binds) do |result|
- result.values
- end
- end
-
# The internal PostgreSQL identifier of the money data type.
MONEY_COLUMN_TYPE_OID = 790 #:nodoc:
# The internal PostgreSQL identifier of the BYTEA data type.
@@ -175,14 +151,6 @@ module ActiveRecord
def suppress_composite_primary_key(pk)
pk unless pk.is_a?(Array)
end
-
- def select_result(arel, name, binds)
- arel, binds = binds_from_relation(arel, binds)
- sql = to_sql(arel, binds)
- execute_and_clear(sql, name, binds) do |result|
- yield result
- end
- end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
index 7233325d5a..ee2faf43b5 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
@@ -349,6 +349,12 @@ module ActiveRecord
end
end
+ def insert_fixtures(rows, table_name)
+ rows.each do |row|
+ insert_fixture(row, table_name)
+ end
+ end
+
private
def table_structure(table_name)
diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb
index a6b66c91e3..e9acb8acae 100644
--- a/activerecord/lib/active_record/fixtures.rb
+++ b/activerecord/lib/active_record/fixtures.rb
@@ -567,9 +567,7 @@ module ActiveRecord
end
table_rows.each do |fixture_set_name, rows|
- rows.each do |row|
- conn.insert_fixture(row, fixture_set_name)
- end
+ conn.insert_fixtures(rows, fixture_set_name)
end
# Cap primary key sequences to max(pk).
diff --git a/activerecord/lib/active_record/locking/optimistic.rb b/activerecord/lib/active_record/locking/optimistic.rb
index 3c7110369b..522da6a571 100644
--- a/activerecord/lib/active_record/locking/optimistic.rb
+++ b/activerecord/lib/active_record/locking/optimistic.rb
@@ -62,8 +62,8 @@ module ActiveRecord
def increment_lock
lock_col = self.class.locking_column
- previous_lock_value = send(lock_col).to_i
- send(lock_col + "=", previous_lock_value + 1)
+ previous_lock_value = send(lock_col)
+ send("#{lock_col}=", previous_lock_value + 1)
end
def _create_record(attribute_names = self.attribute_names, *)
@@ -107,7 +107,8 @@ module ActiveRecord
# If something went wrong, revert the locking_column value.
rescue Exception
- send(lock_col + "=", previous_lock_value.to_i)
+ send("#{lock_col}=", previous_lock_value.to_i)
+
raise
end
end
@@ -127,7 +128,7 @@ module ActiveRecord
if locking_enabled?
locking_column = self.class.locking_column
- relation = relation.where(locking_column => _read_attribute(locking_column))
+ relation = relation.where(locking_column => read_attribute_before_type_cast(locking_column))
end
relation
diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb
index f652c7c3a1..b2dba5516e 100644
--- a/activerecord/lib/active_record/persistence.rb
+++ b/activerecord/lib/active_record/persistence.rb
@@ -526,7 +526,7 @@ module ActiveRecord
if locking_enabled?
locking_column = self.class.locking_column
- scope = scope.where(locking_column => _read_attribute(locking_column))
+ scope = scope.where(locking_column => read_attribute_before_type_cast(locking_column))
changes[locking_column] = increment_lock
end
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index 79e65baae5..6ccdd7adcb 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -1167,7 +1167,7 @@ module ActiveRecord
end
end
- STRUCTURAL_OR_METHODS = Relation::VALUE_METHODS - [:extending, :where, :having]
+ STRUCTURAL_OR_METHODS = Relation::VALUE_METHODS - [:extending, :where, :having, :unscope]
def structurally_incompatible_values_for_or(other)
STRUCTURAL_OR_METHODS.reject do |method|
get_value(method) == other.get_value(method)
diff --git a/activerecord/test/cases/adapters/postgresql/array_test.rb b/activerecord/test/cases/adapters/postgresql/array_test.rb
index c78c6178ff..121c62dadf 100644
--- a/activerecord/test/cases/adapters/postgresql/array_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/array_test.rb
@@ -191,6 +191,12 @@ class PostgresqlArrayTest < ActiveRecord::PostgreSQLTestCase
assert_equal(PgArray.last.tags, tag_values)
end
+ def test_insert_fixtures
+ tag_values = ["val1", "val2", "val3_with_'_multiple_quote_'_chars"]
+ @connection.insert_fixtures([{ "tags" => tag_values }], "pg_arrays")
+ assert_equal(PgArray.last.tags, tag_values)
+ end
+
def test_attribute_for_inspect_for_array_field
record = PgArray.new { |a| a.ratings = (1..10).to_a }
assert_equal("[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]", record.attribute_for_inspect(:ratings))
diff --git a/activerecord/test/cases/associations/has_one_associations_test.rb b/activerecord/test/cases/associations/has_one_associations_test.rb
index 7c11d2e7fc..bf3b8dcd63 100644
--- a/activerecord/test/cases/associations/has_one_associations_test.rb
+++ b/activerecord/test/cases/associations/has_one_associations_test.rb
@@ -307,6 +307,15 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
end
end
+ def test_create_when_parent_is_new_raises
+ firm = Firm.new
+ error = assert_raise(ActiveRecord::RecordNotSaved) do
+ firm.create_account
+ end
+
+ assert_equal "You cannot call create unless the parent is saved", error.message
+ end
+
def test_reload_association
odegy = companies(:odegy)
diff --git a/activerecord/test/cases/connection_pool_test.rb b/activerecord/test/cases/connection_pool_test.rb
index 7e88c9cf7a..00a0187b57 100644
--- a/activerecord/test/cases/connection_pool_test.rb
+++ b/activerecord/test/cases/connection_pool_test.rb
@@ -499,21 +499,8 @@ module ActiveRecord
if failed
second_thread_done.set
- puts
- puts ">>> test_disconnect_and_clear_reloadable_connections_are_able_to_preempt_other_waiting_threads / #{group_action_method}"
- p [first_thread, second_thread]
- p pool.stat
- p pool.connections.map(&:owner)
-
first_thread.join(2)
second_thread.join(2)
-
- puts "---"
- p [first_thread, second_thread]
- p pool.stat
- p pool.connections.map(&:owner)
- puts "<<<"
- puts
end
first_thread.join(10) || raise("first_thread got stuck")
diff --git a/activerecord/test/cases/fixtures_test.rb b/activerecord/test/cases/fixtures_test.rb
index a0a6d3c7ef..b499e60922 100644
--- a/activerecord/test/cases/fixtures_test.rb
+++ b/activerecord/test/cases/fixtures_test.rb
@@ -54,6 +54,31 @@ class FixturesTest < ActiveRecord::TestCase
end
end
+ class InsertQuerySubscriber
+ attr_reader :events
+
+ def initialize
+ @events = []
+ end
+
+ def call(_, _, _, _, values)
+ @events << values[:sql] if values[:sql] =~ /INSERT/
+ end
+ end
+
+ if current_adapter?(:Mysql2Adapter, :PostgreSQLAdapter)
+ def test_bulk_insert
+ begin
+ subscriber = InsertQuerySubscriber.new
+ subscription = ActiveSupport::Notifications.subscribe("sql.active_record", subscriber)
+ create_fixtures("bulbs")
+ assert_equal 1, subscriber.events.size, "It takes one INSERT query to insert two fixtures"
+ ensure
+ ActiveSupport::Notifications.unsubscribe(subscription)
+ end
+ end
+ end
+
def test_broken_yaml_exception
badyaml = Tempfile.new ["foo", ".yml"]
badyaml.write "a: : "
@@ -248,7 +273,12 @@ class FixturesTest < ActiveRecord::TestCase
e = assert_raise(ActiveRecord::Fixture::FixtureError) do
ActiveRecord::FixtureSet.create_fixtures(FIXTURES_ROOT + "/naked/yml", "parrots")
end
- assert_equal(%(table "parrots" has no column named "arrr".), e.message)
+
+ if current_adapter?(:SQLite3Adapter)
+ assert_equal(%(table "parrots" has no column named "arrr".), e.message)
+ else
+ assert_equal(%(table "parrots" has no columns named "arrr", "foobar".), e.message)
+ end
end
def test_yaml_file_with_symbol_columns
diff --git a/activerecord/test/cases/json_shared_test_cases.rb b/activerecord/test/cases/json_shared_test_cases.rb
index 5aa41b9d6d..9a1c1c3f3f 100644
--- a/activerecord/test/cases/json_shared_test_cases.rb
+++ b/activerecord/test/cases/json_shared_test_cases.rb
@@ -169,6 +169,25 @@ module JSONSharedTestCases
assert_not json.changed?
end
+ def test_changes_in_place_ignores_key_order
+ json = klass.new
+ assert_not json.changed?
+
+ json.payload = { "three" => "four", "one" => "two" }
+ json.save!
+ json.reload
+
+ json.payload = { "three" => "four", "one" => "two" }
+ assert_not json.changed?
+
+ json.payload = [{ "three" => "four", "one" => "two" }, { "seven" => "eight", "five" => "six" }]
+ json.save!
+ json.reload
+
+ json.payload = [{ "three" => "four", "one" => "two" }, { "seven" => "eight", "five" => "six" }]
+ assert_not json.changed?
+ end
+
def test_changes_in_place_with_ruby_object
time = Time.now.utc
json = klass.create!(payload: time)
diff --git a/activerecord/test/cases/locking_test.rb b/activerecord/test/cases/locking_test.rb
index 3a3b8e51f9..2fc52393f2 100644
--- a/activerecord/test/cases/locking_test.rb
+++ b/activerecord/test/cases/locking_test.rb
@@ -167,6 +167,12 @@ class OptimisticLockingTest < ActiveRecord::TestCase
assert_equal 0, p1.lock_version
end
+ def test_lock_new_when_explicitly_passing_value
+ p1 = Person.new(first_name: "Douglas Adams", lock_version: 42)
+ p1.save!
+ assert_equal 42, p1.lock_version
+ end
+
def test_touch_existing_lock
p1 = Person.find(1)
assert_equal 0, p1.lock_version
@@ -186,6 +192,19 @@ class OptimisticLockingTest < ActiveRecord::TestCase
end
end
+ def test_explicit_update_lock_column_raise_error
+ person = Person.find(1)
+
+ assert_raises(ActiveRecord::StaleObjectError) do
+ person.first_name = "Douglas Adams"
+ person.lock_version = 42
+
+ assert person.lock_version_changed?
+
+ person.save
+ end
+ end
+
def test_lock_column_name_existing
t1 = LegacyThing.find(1)
t2 = LegacyThing.find(1)
@@ -225,10 +244,33 @@ class OptimisticLockingTest < ActiveRecord::TestCase
assert_equal 0, t1.lock_version_before_type_cast
end
+ def test_touch_existing_lock_without_default_should_work_with_null_in_the_database
+ ActiveRecord::Base.connection.execute("INSERT INTO lock_without_defaults(title) VALUES('title1')")
+ t1 = LockWithoutDefault.last
+
+ assert_equal 0, t1.lock_version
+ assert_nil t1.lock_version_before_type_cast
+
+ t1.touch
+
+ assert_equal 1, t1.lock_version
+ end
+
+ def test_touch_stale_object_with_lock_without_default
+ t1 = LockWithoutDefault.create!(title: "title1")
+ stale_object = LockWithoutDefault.find(t1.id)
+
+ t1.update!(title: "title2")
+
+ assert_raises(ActiveRecord::StaleObjectError) do
+ stale_object.touch
+ end
+ end
+
def test_lock_without_default_should_work_with_null_in_the_database
ActiveRecord::Base.connection.execute("INSERT INTO lock_without_defaults(title) VALUES('title1')")
t1 = LockWithoutDefault.last
- t2 = LockWithoutDefault.last
+ t2 = LockWithoutDefault.find(t1.id)
assert_equal 0, t1.lock_version
assert_nil t1.lock_version_before_type_cast
@@ -285,7 +327,7 @@ class OptimisticLockingTest < ActiveRecord::TestCase
ActiveRecord::Base.connection.execute("INSERT INTO lock_without_defaults_cust(title) VALUES('title1')")
t1 = LockWithCustomColumnWithoutDefault.last
- t2 = LockWithCustomColumnWithoutDefault.last
+ t2 = LockWithCustomColumnWithoutDefault.find(t1.id)
assert_equal 0, t1.custom_lock_version
assert_nil t1.custom_lock_version_before_type_cast
@@ -434,6 +476,31 @@ class OptimisticLockingWithSchemaChangeTest < ActiveRecord::TestCase
PersonalLegacyThing.reset_column_information
end
+ def test_destroy_existing_object_with_locking_column_value_null_in_the_database
+ ActiveRecord::Base.connection.execute("INSERT INTO lock_without_defaults(title) VALUES('title1')")
+ t1 = LockWithoutDefault.last
+
+ assert_equal 0, t1.lock_version
+ assert_nil t1.lock_version_before_type_cast
+
+ t1.destroy
+
+ assert t1.destroyed?
+ end
+
+ def test_destroy_stale_object
+ t1 = LockWithoutDefault.create!(title: "title1")
+ stale_object = LockWithoutDefault.find(t1.id)
+
+ t1.update!(title: "title2")
+
+ assert_raises(ActiveRecord::StaleObjectError) do
+ stale_object.destroy!
+ end
+
+ refute stale_object.destroyed?
+ end
+
private
def add_counter_column_to(model, col = "test_count")
diff --git a/activerecord/test/cases/migration/references_foreign_key_test.rb b/activerecord/test/cases/migration/references_foreign_key_test.rb
index f1ddac1ee2..718b9a0613 100644
--- a/activerecord/test/cases/migration/references_foreign_key_test.rb
+++ b/activerecord/test/cases/migration/references_foreign_key_test.rb
@@ -139,6 +139,16 @@ if ActiveRecord::Base.connection.supports_foreign_keys?
end
end
+ test "removing column removes foreign key" do
+ @connection.create_table :testings do |t|
+ t.references :testing_parent, index: true, foreign_key: true
+ end
+
+ assert_difference "@connection.foreign_keys('testings').size", -1 do
+ @connection.remove_column :testings, :testing_parent_id
+ end
+ end
+
test "foreign key methods respect pluralize_table_names" do
begin
original_pluralize_table_names = ActiveRecord::Base.pluralize_table_names
diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb
index 57f94950f9..3a49a41580 100644
--- a/activerecord/test/cases/migration_test.rb
+++ b/activerecord/test/cases/migration_test.rb
@@ -502,11 +502,10 @@ class MigrationTest < ActiveRecord::TestCase
unless mysql_enforcing_gtid_consistency?
def test_create_table_with_query
- Person.connection.create_table(:person, force: true)
-
- Person.connection.create_table :table_from_query_testings, as: "SELECT id FROM person"
+ Person.connection.create_table :table_from_query_testings, as: "SELECT id FROM people WHERE id = 1"
columns = Person.connection.columns(:table_from_query_testings)
+ assert_equal [1], Person.connection.select_values("SELECT * FROM table_from_query_testings")
assert_equal 1, columns.length
assert_equal "id", columns.first.name
ensure
@@ -514,11 +513,10 @@ class MigrationTest < ActiveRecord::TestCase
end
def test_create_table_with_query_from_relation
- Person.connection.create_table(:person, force: true)
-
- Person.connection.create_table :table_from_query_testings, as: Person.select(:id)
+ Person.connection.create_table :table_from_query_testings, as: Person.select(:id).where(id: 1)
columns = Person.connection.columns(:table_from_query_testings)
+ assert_equal [1], Person.connection.select_values("SELECT * FROM table_from_query_testings")
assert_equal 1, columns.length
assert_equal "id", columns.first.name
ensure
diff --git a/activerecord/test/cases/nested_attributes_test.rb b/activerecord/test/cases/nested_attributes_test.rb
index 5a62cbd3a6..154faa56aa 100644
--- a/activerecord/test/cases/nested_attributes_test.rb
+++ b/activerecord/test/cases/nested_attributes_test.rb
@@ -117,7 +117,7 @@ class TestNestedAttributesInGeneral < ActiveRecord::TestCase
def test_reject_if_with_a_proc_which_returns_true_always_for_has_one
Pirate.accepts_nested_attributes_for :ship, reject_if: proc { |attributes| true }
- pirate = Pirate.new(catchphrase: "Stop wastin' me time")
+ pirate = Pirate.create(catchphrase: "Stop wastin' me time")
ship = pirate.create_ship(name: "s1")
pirate.update(ship_attributes: { name: "s2", id: ship.id })
assert_equal "s1", ship.reload.name
diff --git a/activerecord/test/cases/query_cache_test.rb b/activerecord/test/cases/query_cache_test.rb
index 494663eb04..9b741545d7 100644
--- a/activerecord/test/cases/query_cache_test.rb
+++ b/activerecord/test/cases/query_cache_test.rb
@@ -204,6 +204,52 @@ class QueryCacheTest < ActiveRecord::TestCase
end
end
+ def test_exists_queries_with_cache
+ Post.cache do
+ assert_queries(1) { Post.exists?; Post.exists? }
+ end
+ end
+
+ def test_select_all_with_cache
+ Post.cache do
+ assert_queries(1) do
+ 2.times { Post.connection.select_all(Post.all) }
+ end
+ end
+ end
+
+ def test_select_one_with_cache
+ Post.cache do
+ assert_queries(1) do
+ 2.times { Post.connection.select_one(Post.all) }
+ end
+ end
+ end
+
+ def test_select_value_with_cache
+ Post.cache do
+ assert_queries(1) do
+ 2.times { Post.connection.select_value(Post.all) }
+ end
+ end
+ end
+
+ def test_select_values_with_cache
+ Post.cache do
+ assert_queries(1) do
+ 2.times { Post.connection.select_values(Post.all) }
+ end
+ end
+ end
+
+ def test_select_rows_with_cache
+ Post.cache do
+ assert_queries(1) do
+ 2.times { Post.connection.select_rows(Post.all) }
+ end
+ end
+ end
+
def test_query_cache_dups_results_correctly
Task.cache do
now = Time.now.utc
diff --git a/activerecord/test/cases/relation/merging_test.rb b/activerecord/test/cases/relation/merging_test.rb
index c3b39a9295..3901824aac 100644
--- a/activerecord/test/cases/relation/merging_test.rb
+++ b/activerecord/test/cases/relation/merging_test.rb
@@ -143,7 +143,7 @@ class MergingDifferentRelationsTest < ActiveRecord::TestCase
assert_equal ["Mary", "Mary", "Mary", "David"], posts_by_author_name
end
- test "relation merging (using a proc argument)" do
+ test "relation merging (using a proc argument)" do
dev = Developer.where(name: "Jamis").first
comment_1 = dev.comments.create!(body: "I'm Jamis", post: Post.first)
diff --git a/activerecord/test/cases/relation/or_test.rb b/activerecord/test/cases/relation/or_test.rb
index abb7ca72dd..61b6601580 100644
--- a/activerecord/test/cases/relation/or_test.rb
+++ b/activerecord/test/cases/relation/or_test.rb
@@ -59,6 +59,31 @@ module ActiveRecord
assert_equal "Relation passed to #or must be structurally compatible. Incompatible values: [:order]", error.message
end
+ def test_or_with_unscope_where
+ expected = Post.where("id = 1 or id = 2")
+ partial = Post.where("id = 1 and id != 2")
+ assert_equal expected, partial.or(partial.unscope(:where).where("id = 2")).to_a
+ end
+
+ def test_or_with_unscope_where_column
+ expected = Post.where("id = 1 or id = 2")
+ partial = Post.where(id: 1).where.not(id: 2)
+ assert_equal expected, partial.or(partial.unscope(where: :id).where("id = 2")).to_a
+ end
+
+ def test_or_with_unscope_order
+ expected = Post.where("id = 1 or id = 2")
+ assert_equal expected, Post.order("body asc").where("id = 1").unscope(:order).or(Post.where("id = 2")).to_a
+ end
+
+ def test_or_with_incompatible_unscope
+ error = assert_raises ArgumentError do
+ Post.order("body asc").where("id = 1").or(Post.order("body asc").where("id = 2").unscope(:order)).to_a
+ end
+
+ assert_equal "Relation passed to #or must be structurally compatible. Incompatible values: [:order]", error.message
+ end
+
def test_or_when_grouping
groups = Post.where("id < 10").group("body").select("body, COUNT(*) AS c")
expected = groups.having("COUNT(*) > 1 OR body like 'Such%'").to_a.map { |o| [o.body, o.c] }
diff --git a/activerecord/test/cases/relation_test.rb b/activerecord/test/cases/relation_test.rb
index 5fb32270b7..a403824f1a 100644
--- a/activerecord/test/cases/relation_test.rb
+++ b/activerecord/test/cases/relation_test.rb
@@ -6,7 +6,7 @@ require "models/rating"
module ActiveRecord
class RelationTest < ActiveRecord::TestCase
- fixtures :posts, :comments, :authors, :author_addresses
+ fixtures :posts, :comments, :authors, :author_addresses, :ratings
FakeKlass = Struct.new(:table_name, :name) do
extend ActiveRecord::Delegation::DelegateCache
@@ -224,7 +224,26 @@ module ActiveRecord
def test_relation_merging_with_merged_joins_as_symbols
special_comments_with_ratings = SpecialComment.joins(:ratings)
posts_with_special_comments_with_ratings = Post.group("posts.id").joins(:special_comments).merge(special_comments_with_ratings)
- assert_equal({ 2 => 1, 4 => 3, 5 => 1 }, authors(:david).posts.merge(posts_with_special_comments_with_ratings).count)
+ assert_equal({ 4 => 2 }, authors(:david).posts.merge(posts_with_special_comments_with_ratings).count)
+ end
+
+ def test_relation_merging_with_merged_symbol_joins_keeps_inner_joins
+ queries = capture_sql { Author.joins(:posts).merge(Post.joins(:comments)).to_a }
+
+ nb_inner_join = queries.sum { |sql| sql.scan(/INNER\s+JOIN/i).size }
+ assert_equal 2, nb_inner_join, "Wrong amount of INNER JOIN in query"
+ assert queries.none? { |sql| /LEFT\s+(OUTER)?\s+JOIN/i.match?(sql) }, "Shouldn't have any LEFT JOIN in query"
+ end
+
+ def test_relation_merging_with_merged_symbol_joins_has_correct_size_and_count
+ # Has one entry per comment
+ merged_authors_with_commented_posts_relation = Author.joins(:posts).merge(Post.joins(:comments))
+
+ post_ids_with_author = Post.joins(:author).pluck(:id)
+ manual_comments_on_post_that_have_author = Comment.where(post_id: post_ids_with_author).pluck(:id)
+
+ assert_equal manual_comments_on_post_that_have_author.size, merged_authors_with_commented_posts_relation.count
+ assert_equal manual_comments_on_post_that_have_author.size, merged_authors_with_commented_posts_relation.to_a.size
end
def test_relation_merging_with_joins_as_join_dependency_pick_proper_parent
diff --git a/activerecord/test/fixtures/naked/yml/parrots.yml b/activerecord/test/fixtures/naked/yml/parrots.yml
index 3e10331105..76f66e01ae 100644
--- a/activerecord/test/fixtures/naked/yml/parrots.yml
+++ b/activerecord/test/fixtures/naked/yml/parrots.yml
@@ -1,2 +1,3 @@
george:
arrr: "Curious George"
+ foobar: Foobar
diff --git a/activerecord/test/models/comment.rb b/activerecord/test/models/comment.rb
index d227f6fe86..eecf923046 100644
--- a/activerecord/test/models/comment.rb
+++ b/activerecord/test/models/comment.rb
@@ -9,7 +9,6 @@ class Comment < ActiveRecord::Base
belongs_to :post, counter_cache: true
belongs_to :author, polymorphic: true
belongs_to :resource, polymorphic: true
- belongs_to :developer
has_many :ratings
diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb
index 8863736943..6da0ef26f6 100644
--- a/activerecord/test/schema/schema.rb
+++ b/activerecord/test/schema/schema.rb
@@ -453,11 +453,13 @@ ActiveRecord::Schema.define do
create_table :lock_without_defaults, force: true do |t|
t.column :title, :string
t.column :lock_version, :integer
+ t.timestamps null: true
end
create_table :lock_without_defaults_cust, force: true do |t|
t.column :title, :string
t.column :custom_lock_version, :integer
+ t.timestamps null: true
end
create_table :magazines, force: true do |t|
@@ -814,9 +816,10 @@ ActiveRecord::Schema.define do
t.index :id, unique: true
end
- create_table :subscribers, force: true do |t|
+ create_table :subscribers, id: false, force: true do |t|
t.string :nick, null: false
t.string :name
+ t.integer :id
t.integer :books_count, null: false, default: 0
t.integer :update_count, null: false, default: 0
t.index :nick, unique: true
diff --git a/activesupport/test/core_ext/duration_test.rb b/activesupport/test/core_ext/duration_test.rb
index 3108f24f21..cd1b505c34 100644
--- a/activesupport/test/core_ext/duration_test.rb
+++ b/activesupport/test/core_ext/duration_test.rb
@@ -315,7 +315,7 @@ class DurationTest < ActiveSupport::TestCase
assert_equal(1, scalar <=> 5)
assert_equal(0, scalar <=> 10)
assert_equal(-1, scalar <=> 15)
- assert_equal(nil, scalar <=> "foo")
+ assert_nil(scalar <=> "foo")
end
def test_scalar_plus
diff --git a/guides/assets/images/belongs_to.png b/guides/assets/images/belongs_to.png
index 077d237e4e..1a9926e578 100644
--- a/guides/assets/images/belongs_to.png
+++ b/guides/assets/images/belongs_to.png
Binary files differ
diff --git a/guides/assets/images/getting_started/article_with_comments.png b/guides/assets/images/getting_started/article_with_comments.png
index c489e4c00e..3f16f3b280 100644
--- a/guides/assets/images/getting_started/article_with_comments.png
+++ b/guides/assets/images/getting_started/article_with_comments.png
Binary files differ
diff --git a/guides/assets/images/getting_started/challenge.png b/guides/assets/images/getting_started/challenge.png
index 5b88a842b2..d05ef31bbe 100644
--- a/guides/assets/images/getting_started/challenge.png
+++ b/guides/assets/images/getting_started/challenge.png
Binary files differ
diff --git a/guides/assets/images/getting_started/confirm_dialog.png b/guides/assets/images/getting_started/confirm_dialog.png
index 9755f581a6..ce65734e6c 100644
--- a/guides/assets/images/getting_started/confirm_dialog.png
+++ b/guides/assets/images/getting_started/confirm_dialog.png
Binary files differ
diff --git a/guides/assets/images/getting_started/forbidden_attributes_for_new_article.png b/guides/assets/images/getting_started/forbidden_attributes_for_new_article.png
index 9f32c68472..50b178808e 100644
--- a/guides/assets/images/getting_started/forbidden_attributes_for_new_article.png
+++ b/guides/assets/images/getting_started/forbidden_attributes_for_new_article.png
Binary files differ
diff --git a/guides/assets/images/getting_started/form_with_errors.png b/guides/assets/images/getting_started/form_with_errors.png
index 98bff37d4a..6eefd2885a 100644
--- a/guides/assets/images/getting_started/form_with_errors.png
+++ b/guides/assets/images/getting_started/form_with_errors.png
Binary files differ
diff --git a/guides/assets/images/getting_started/index_action_with_edit_link.png b/guides/assets/images/getting_started/index_action_with_edit_link.png
index 0566a3ffde..a2a087a598 100644
--- a/guides/assets/images/getting_started/index_action_with_edit_link.png
+++ b/guides/assets/images/getting_started/index_action_with_edit_link.png
Binary files differ
diff --git a/guides/assets/images/getting_started/new_article.png b/guides/assets/images/getting_started/new_article.png
index bd3ae4fa67..6edcc161b6 100644
--- a/guides/assets/images/getting_started/new_article.png
+++ b/guides/assets/images/getting_started/new_article.png
Binary files differ
diff --git a/guides/assets/images/getting_started/rails_welcome.png b/guides/assets/images/getting_started/rails_welcome.png
index baccb11322..44f89ec8de 100644
--- a/guides/assets/images/getting_started/rails_welcome.png
+++ b/guides/assets/images/getting_started/rails_welcome.png
Binary files differ
diff --git a/guides/assets/images/getting_started/routing_error_no_controller.png b/guides/assets/images/getting_started/routing_error_no_controller.png
index ed62862291..52150f0426 100644
--- a/guides/assets/images/getting_started/routing_error_no_controller.png
+++ b/guides/assets/images/getting_started/routing_error_no_controller.png
Binary files differ
diff --git a/guides/assets/images/getting_started/show_action_for_articles.png b/guides/assets/images/getting_started/show_action_for_articles.png
index 4dad704f89..68837131f7 100644
--- a/guides/assets/images/getting_started/show_action_for_articles.png
+++ b/guides/assets/images/getting_started/show_action_for_articles.png
Binary files differ
diff --git a/guides/assets/images/getting_started/template_is_missing_articles_new.png b/guides/assets/images/getting_started/template_is_missing_articles_new.png
index f4f054f3c6..a1603f5d28 100644
--- a/guides/assets/images/getting_started/template_is_missing_articles_new.png
+++ b/guides/assets/images/getting_started/template_is_missing_articles_new.png
Binary files differ
diff --git a/guides/assets/images/getting_started/unknown_action_create_for_articles.png b/guides/assets/images/getting_started/unknown_action_create_for_articles.png
index fd20cd53dc..ec4758e085 100644
--- a/guides/assets/images/getting_started/unknown_action_create_for_articles.png
+++ b/guides/assets/images/getting_started/unknown_action_create_for_articles.png
Binary files differ
diff --git a/guides/assets/images/getting_started/unknown_action_new_for_articles.png b/guides/assets/images/getting_started/unknown_action_new_for_articles.png
index e948a51e4a..f7e7464d61 100644
--- a/guides/assets/images/getting_started/unknown_action_new_for_articles.png
+++ b/guides/assets/images/getting_started/unknown_action_new_for_articles.png
Binary files differ
diff --git a/guides/assets/images/habtm.png b/guides/assets/images/habtm.png
index b062bc73fe..41013b743d 100644
--- a/guides/assets/images/habtm.png
+++ b/guides/assets/images/habtm.png
Binary files differ
diff --git a/guides/assets/images/has_many.png b/guides/assets/images/has_many.png
index 79da2613d7..0d67bea38b 100644
--- a/guides/assets/images/has_many.png
+++ b/guides/assets/images/has_many.png
Binary files differ
diff --git a/guides/assets/images/has_many_through.png b/guides/assets/images/has_many_through.png
index 858c898dc1..b4da60e1fb 100644
--- a/guides/assets/images/has_many_through.png
+++ b/guides/assets/images/has_many_through.png
Binary files differ
diff --git a/guides/assets/images/has_one.png b/guides/assets/images/has_one.png
index 93faa05b07..c70763856a 100644
--- a/guides/assets/images/has_one.png
+++ b/guides/assets/images/has_one.png
Binary files differ
diff --git a/guides/assets/images/has_one_through.png b/guides/assets/images/has_one_through.png
index 07dac1a27d..888a02b775 100644
--- a/guides/assets/images/has_one_through.png
+++ b/guides/assets/images/has_one_through.png
Binary files differ
diff --git a/guides/assets/images/header_backdrop.png b/guides/assets/images/header_backdrop.png
index 72b030478f..81f4d91774 100644
--- a/guides/assets/images/header_backdrop.png
+++ b/guides/assets/images/header_backdrop.png
Binary files differ
diff --git a/guides/assets/images/i18n/demo_html_safe.png b/guides/assets/images/i18n/demo_html_safe.png
index 9afa8ebec1..be75d4830e 100644
--- a/guides/assets/images/i18n/demo_html_safe.png
+++ b/guides/assets/images/i18n/demo_html_safe.png
Binary files differ
diff --git a/guides/assets/images/i18n/demo_localized_pirate.png b/guides/assets/images/i18n/demo_localized_pirate.png
index bf8d0b558c..528cc27900 100644
--- a/guides/assets/images/i18n/demo_localized_pirate.png
+++ b/guides/assets/images/i18n/demo_localized_pirate.png
Binary files differ
diff --git a/guides/assets/images/i18n/demo_translated_en.png b/guides/assets/images/i18n/demo_translated_en.png
index e887bfa306..bbb5e93c3a 100644
--- a/guides/assets/images/i18n/demo_translated_en.png
+++ b/guides/assets/images/i18n/demo_translated_en.png
Binary files differ
diff --git a/guides/assets/images/i18n/demo_translated_pirate.png b/guides/assets/images/i18n/demo_translated_pirate.png
index aa5618a865..305fa93a14 100644
--- a/guides/assets/images/i18n/demo_translated_pirate.png
+++ b/guides/assets/images/i18n/demo_translated_pirate.png
Binary files differ
diff --git a/guides/assets/images/i18n/demo_translation_missing.png b/guides/assets/images/i18n/demo_translation_missing.png
index 867aa7c42d..e9833ba307 100644
--- a/guides/assets/images/i18n/demo_translation_missing.png
+++ b/guides/assets/images/i18n/demo_translation_missing.png
Binary files differ
diff --git a/guides/assets/images/i18n/demo_untranslated.png b/guides/assets/images/i18n/demo_untranslated.png
index 2ea6404822..2653abc491 100644
--- a/guides/assets/images/i18n/demo_untranslated.png
+++ b/guides/assets/images/i18n/demo_untranslated.png
Binary files differ
diff --git a/guides/assets/images/icons/callouts/14.png b/guides/assets/images/icons/callouts/14.png
index 4274e6580a..dbde9ca749 100644
--- a/guides/assets/images/icons/callouts/14.png
+++ b/guides/assets/images/icons/callouts/14.png
Binary files differ
diff --git a/guides/assets/images/icons/example.png b/guides/assets/images/icons/example.png
index de23c0aa87..a0e855befa 100644
--- a/guides/assets/images/icons/example.png
+++ b/guides/assets/images/icons/example.png
Binary files differ
diff --git a/guides/assets/images/icons/home.png b/guides/assets/images/icons/home.png
index 24149d6e78..e70e164522 100644
--- a/guides/assets/images/icons/home.png
+++ b/guides/assets/images/icons/home.png
Binary files differ
diff --git a/guides/assets/images/icons/important.png b/guides/assets/images/icons/important.png
index dafcf0f59e..bab53bf3aa 100644
--- a/guides/assets/images/icons/important.png
+++ b/guides/assets/images/icons/important.png
Binary files differ
diff --git a/guides/assets/images/icons/next.png b/guides/assets/images/icons/next.png
index 355b329f5a..a158832725 100644
--- a/guides/assets/images/icons/next.png
+++ b/guides/assets/images/icons/next.png
Binary files differ
diff --git a/guides/assets/images/icons/note.png b/guides/assets/images/icons/note.png
index 08d35a6f5c..62eec7845f 100644
--- a/guides/assets/images/icons/note.png
+++ b/guides/assets/images/icons/note.png
Binary files differ
diff --git a/guides/assets/images/icons/prev.png b/guides/assets/images/icons/prev.png
index ea564c865e..8a96960422 100644
--- a/guides/assets/images/icons/prev.png
+++ b/guides/assets/images/icons/prev.png
Binary files differ
diff --git a/guides/assets/images/icons/tip.png b/guides/assets/images/icons/tip.png
index d834e6d1bb..a5316d318f 100644
--- a/guides/assets/images/icons/tip.png
+++ b/guides/assets/images/icons/tip.png
Binary files differ
diff --git a/guides/assets/images/icons/up.png b/guides/assets/images/icons/up.png
index 379f0045af..6cac818170 100644
--- a/guides/assets/images/icons/up.png
+++ b/guides/assets/images/icons/up.png
Binary files differ
diff --git a/guides/assets/images/polymorphic.png b/guides/assets/images/polymorphic.png
index a3cbc4502a..e0a7f6d64a 100644
--- a/guides/assets/images/polymorphic.png
+++ b/guides/assets/images/polymorphic.png
Binary files differ
diff --git a/guides/assets/images/rails4_features.png b/guides/assets/images/rails4_features.png
index b3bd5ef69e..ac73f05cf7 100644
--- a/guides/assets/images/rails4_features.png
+++ b/guides/assets/images/rails4_features.png
Binary files differ
diff --git a/guides/assets/images/session_fixation.png b/guides/assets/images/session_fixation.png
index ac3ab01614..e009484f09 100644
--- a/guides/assets/images/session_fixation.png
+++ b/guides/assets/images/session_fixation.png
Binary files differ
diff --git a/guides/assets/images/tab_yellow.png b/guides/assets/images/tab_yellow.png
index 3ab1c56c4d..053c807d28 100644
--- a/guides/assets/images/tab_yellow.png
+++ b/guides/assets/images/tab_yellow.png
Binary files differ
diff --git a/guides/bug_report_templates/active_record_migrations_gem.rb b/guides/bug_report_templates/active_record_migrations_gem.rb
index 0c398e334a..00ba3c1cd6 100644
--- a/guides/bug_report_templates/active_record_migrations_gem.rb
+++ b/guides/bug_report_templates/active_record_migrations_gem.rb
@@ -48,16 +48,14 @@ end
class BugTest < Minitest::Test
def test_migration_up
- migrator = ActiveRecord::Migrator.new(:up, [ChangeAmountToAddScale])
- migrator.run
+ ChangeAmountToAddScale.migrate(:up)
Payment.reset_column_information
assert_equal "decimal(10,2)", Payment.columns.last.sql_type
end
def test_migration_down
- migrator = ActiveRecord::Migrator.new(:down, [ChangeAmountToAddScale])
- migrator.run
+ ChangeAmountToAddScale.migrate(:down)
Payment.reset_column_information
assert_equal "decimal(10,0)", Payment.columns.last.sql_type
diff --git a/guides/bug_report_templates/active_record_migrations_master.rb b/guides/bug_report_templates/active_record_migrations_master.rb
index 84a4b71909..52c9028b0f 100644
--- a/guides/bug_report_templates/active_record_migrations_master.rb
+++ b/guides/bug_report_templates/active_record_migrations_master.rb
@@ -48,16 +48,14 @@ end
class BugTest < Minitest::Test
def test_migration_up
- migrator = ActiveRecord::Migrator.new(:up, [ChangeAmountToAddScale])
- migrator.run
+ ChangeAmountToAddScale.migrate(:up)
Payment.reset_column_information
assert_equal "decimal(10,2)", Payment.columns.last.sql_type
end
def test_migration_down
- migrator = ActiveRecord::Migrator.new(:down, [ChangeAmountToAddScale])
- migrator.run
+ ChangeAmountToAddScale.migrate(:down)
Payment.reset_column_information
assert_equal "decimal(10,0)", Payment.columns.last.sql_type
diff --git a/guides/source/active_record_querying.md b/guides/source/active_record_querying.md
index 3676462788..215142223d 100644
--- a/guides/source/active_record_querying.md
+++ b/guides/source/active_record_querying.md
@@ -513,8 +513,6 @@ Article.where(author: author)
Author.joins(:articles).where(articles: { author: author })
```
-NOTE: The values cannot be symbols. For example, you cannot do `Client.where(status: :active)`.
-
#### Range Conditions
```ruby
diff --git a/guides/source/asset_pipeline.md b/guides/source/asset_pipeline.md
index 5d774566dd..a02eebf263 100644
--- a/guides/source/asset_pipeline.md
+++ b/guides/source/asset_pipeline.md
@@ -572,20 +572,6 @@ would generate this HTML:
The `body` param is required by Sprockets.
-### Runtime Error Checking
-
-By default the asset pipeline will check for potential errors in development mode during
-runtime. To disable this behavior you can set:
-
-```ruby
-config.assets.raise_runtime_errors = false
-```
-
-When this option is true, the asset pipeline will check if all the assets loaded
-in your application are included in the `config.assets.precompile` list.
-If `config.assets.digest` is also true, the asset pipeline will require that
-all requests for assets include digests.
-
### Raise an Error When an Asset is Not Found
If you are using sprockets-rails >= 3.2.0 you can configure what happens
diff --git a/guides/source/configuring.md b/guides/source/configuring.md
index 1234e1f192..21b3ca0efa 100644
--- a/guides/source/configuring.md
+++ b/guides/source/configuring.md
@@ -157,8 +157,6 @@ defaults to `:debug` for all environments. The available log levels are: `:debug
* `config.assets.enabled` a flag that controls whether the asset
pipeline is enabled. It is set to `true` by default.
-* `config.assets.raise_runtime_errors` Set this flag to `true` to enable additional runtime error checking. Recommended in `config/environments/development.rb` to minimize unexpected behavior when deploying to `production`.
-
* `config.assets.css_compressor` defines the CSS compressor to use. It is set by default by `sass-rails`. The unique alternative value at the moment is `:yui`, which uses the `yui-compressor` gem.
* `config.assets.js_compressor` defines the JavaScript compressor to use. Possible values are `:closure`, `:uglifier` and `:yui` which require the use of the `closure-compiler`, `uglifier` or `yui-compressor` gems respectively.
diff --git a/guides/source/development_dependencies_install.md b/guides/source/development_dependencies_install.md
index 7ec038eb4d..c57efd6362 100644
--- a/guides/source/development_dependencies_install.md
+++ b/guides/source/development_dependencies_install.md
@@ -62,7 +62,7 @@ $ sudo apt-get install sqlite3 libsqlite3-dev
If you are on Fedora or CentOS, you're done with
```bash
-$ sudo yum install sqlite3 sqlite3-devel
+$ sudo yum install libsqlite3x libsqlite3x-devel
```
If you are on Arch Linux, you will need to run:
diff --git a/guides/source/generators.md b/guides/source/generators.md
index d4ed2355d4..be1be75e7a 100644
--- a/guides/source/generators.md
+++ b/guides/source/generators.md
@@ -689,14 +689,6 @@ Available options are:
* `:env` - Specifies the environment in which to run this rake task.
* `:sudo` - Whether or not to run this task using `sudo`. Defaults to `false`.
-### `capify!`
-
-Runs the `capify` command from Capistrano at the root of the application which generates Capistrano configuration.
-
-```ruby
-capify!
-```
-
### `route`
Adds text to the `config/routes.rb` file:
diff --git a/guides/source/layouts_and_rendering.md b/guides/source/layouts_and_rendering.md
index caa3d21d23..c96cf61761 100644
--- a/guides/source/layouts_and_rendering.md
+++ b/guides/source/layouts_and_rendering.md
@@ -1171,7 +1171,7 @@ To pass a local variable to a partial in only specific cases use the `local_assi
This way it is possible to use the partial without the need to declare all local variables.
-Every partial also has a local variable with the same name as the partial (minus the underscore). You can pass an object in to this local variable via the `:object` option:
+Every partial also has a local variable with the same name as the partial (minus the leading underscore). You can pass an object in to this local variable via the `:object` option:
```erb
<%= render partial: "customer", object: @new_customer %>
diff --git a/guides/source/working_with_javascript_in_rails.md b/guides/source/working_with_javascript_in_rails.md
index 35e6aea4cf..ed27752a06 100644
--- a/guides/source/working_with_javascript_in_rails.md
+++ b/guides/source/working_with_javascript_in_rails.md
@@ -382,9 +382,9 @@ Rails 5.1 introduced rails-ujs and dropped jQuery as a dependency.
As a result the Unobtrusive JavaScript (UJS) driver has been rewritten to operate without jQuery.
These introductions cause small changes to `custom events` fired during the request:
-NOTE: Signature of calls to UJS’s event handlers have changed.
-Unlike the version with jqeury, all custom events return only one parameter: `event`.
-In this parameter, there is an additional attribute `details` which contains an array of extra parameters.
+NOTE: Signature of calls to UJS’s event handlers has changed.
+Unlike the version with jQuery, all custom events return only one parameter: `event`.
+In this parameter, there is an additional attribute `detail` which contains an array of extra parameters.
| Event name | Extra parameters (event.detail) | Fired |
|---------------------|---------------------------------|-------------------------------------------------------------|
diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md
index d93c532c7e..6cb8cbcd73 100644
--- a/railties/CHANGELOG.md
+++ b/railties/CHANGELOG.md
@@ -1,3 +1,7 @@
+* Deprecate `capify!` method in generators and templates.
+
+ *Yuji Yaginuma*
+
* Allow irb options to be passed from `rails console` command.
Fixes #28988.
diff --git a/railties/lib/rails/commands/test/test_command.rb b/railties/lib/rails/commands/test/test_command.rb
index 65e16900ba..dce85cf12d 100644
--- a/railties/lib/rails/commands/test/test_command.rb
+++ b/railties/lib/rails/commands/test/test_command.rb
@@ -11,7 +11,7 @@ module Rails
end
def perform(*)
- $LOAD_PATH << Rails::Command.root.join("test")
+ $LOAD_PATH << Rails::Command.root.join("test").to_s
Minitest.run_via = :rails
diff --git a/railties/lib/rails/generators/actions.rb b/railties/lib/rails/generators/actions.rb
index 0bd0615b7e..5cf0985050 100644
--- a/railties/lib/rails/generators/actions.rb
+++ b/railties/lib/rails/generators/actions.rb
@@ -227,6 +227,7 @@ module Rails
#
# capify!
def capify!
+ ActiveSupport::Deprecation.warn("`capify!` is deprecated and will be removed in the next version of Rails.")
log :capify, ""
in_root { run("#{extify(:capify)} .", verbose: false) }
end
diff --git a/railties/test/generators/actions_test.rb b/railties/test/generators/actions_test.rb
index 360e8e97d7..03b29be907 100644
--- a/railties/test/generators/actions_test.rb
+++ b/railties/test/generators/actions_test.rb
@@ -278,9 +278,12 @@ class ActionsTest < Rails::Generators::TestCase
end
def test_capify_should_run_the_capify_command
- assert_called_with(generator, :run, ["capify .", verbose: false]) do
- action :capify!
+ content = capture(:stderr) do
+ assert_called_with(generator, :run, ["capify .", verbose: false]) do
+ action :capify!
+ end
end
+ assert_match(/DEPRECATION WARNING: `capify!` is deprecated/, content)
end
def test_route_should_add_data_to_the_routes_block_in_config_routes
diff --git a/tools/README.md b/tools/README.md
index b2e7e4b0ae..f133b27128 100644
--- a/tools/README.md
+++ b/tools/README.md
@@ -6,3 +6,5 @@ They aren't used by Rails apps directly.
* `console` drops you in irb and loads local Rails repos
* `profile` profiles `Kernel#require` to help reduce startup time
* `line_statistics` provides CodeTools module and LineStatistics class to count lines
+ * `test` is loaded by every major component of Rails to simplify testing, for example:
+ `cd ./actioncable; bin/test ./path/to/actioncable_test_with_line_number.rb:5`
diff --git a/tools/test.rb b/tools/test.rb
index 1a1eca8f95..52e9c19198 100644
--- a/tools/test.rb
+++ b/tools/test.rb
@@ -8,7 +8,7 @@ require "rails/test_unit/line_filtering"
require "active_support/test_case"
class << Rails
- # Necessary to get rerun-snippts working.
+ # Necessary to get rerun-snippets working.
def root
@root ||= Pathname.new(COMPONENT_ROOT)
end