aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--actioncable/lib/action_cable/connection/base.rb2
-rw-r--r--actioncable/lib/action_cable/server/base.rb6
-rw-r--r--actioncable/lib/rails/generators/channel/channel_generator.rb3
-rw-r--r--actioncable/lib/rails/generators/channel/templates/assets/channel.js18
-rw-r--r--actionpack/lib/action_dispatch/routing.rb4
-rw-r--r--actionview/CHANGELOG.md17
-rw-r--r--actionview/lib/action_view/helpers/form_tag_helper.rb4
-rw-r--r--actionview/lib/action_view/lookup_context.rb12
-rw-r--r--actionview/test/actionpack/abstract/layouts_test.rb156
-rw-r--r--actionview/test/template/form_tag_helper_test.rb10
-rw-r--r--activerecord/CHANGELOG.md10
-rw-r--r--activerecord/lib/active_record/associations/collection_association.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb8
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb14
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb3
-rw-r--r--activerecord/lib/active_record/errors.rb10
-rw-r--r--activerecord/lib/active_record/internal_metadata.rb2
-rw-r--r--activerecord/lib/active_record/log_subscriber.rb6
-rw-r--r--activerecord/lib/active_record/schema_dumper.rb4
-rw-r--r--activerecord/test/cases/adapters/mysql2/transaction_test.rb62
-rw-r--r--activerecord/test/cases/adapters/postgresql/transaction_test.rb72
-rw-r--r--activerecord/test/cases/log_subscriber_test.rb6
-rw-r--r--activerecord/test/cases/migration_test.rb17
-rw-r--r--activerecord/test/cases/schema_dumper_test.rb4
-rw-r--r--activesupport/lib/active_support/xml_mini/rexml.rb4
-rw-r--r--activesupport/test/xml_mini/rexml_engine_test.rb26
-rw-r--r--guides/source/5_0_release_notes.md3
-rw-r--r--guides/source/active_record_querying.md2
-rw-r--r--guides/source/testing.md2
-rw-r--r--railties/CHANGELOG.md5
-rw-r--r--railties/lib/rails/application.rb11
-rw-r--r--railties/lib/rails/generators/named_base.rb4
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/secrets.yml8
-rw-r--r--railties/lib/rails/generators/rails/controller/controller_generator.rb3
-rw-r--r--railties/test/application/configuration_test.rb25
-rw-r--r--railties/test/generators/channel_generator_test.rb24
36 files changed, 501 insertions, 70 deletions
diff --git a/actioncable/lib/action_cable/connection/base.rb b/actioncable/lib/action_cable/connection/base.rb
index cc4e0f8c8b..75c1299e36 100644
--- a/actioncable/lib/action_cable/connection/base.rb
+++ b/actioncable/lib/action_cable/connection/base.rb
@@ -2,7 +2,7 @@ require 'action_dispatch'
module ActionCable
module Connection
- # For every WebSocket the Action Cable server accepts, a Connection object will be instantiated. This instance becomes the parent
+ # For every WebSocket connection the Action Cable server accepts, a Connection object will be instantiated. This instance becomes the parent
# of all of the channel subscriptions that are created from there on. Incoming messages are then routed to these channel subscriptions
# based on an identifier sent by the Action Cable consumer. The Connection itself does not deal with any specific application logic beyond
# authentication and authorization.
diff --git a/actioncable/lib/action_cable/server/base.rb b/actioncable/lib/action_cable/server/base.rb
index b1a0e11631..717a60fe4f 100644
--- a/actioncable/lib/action_cable/server/base.rb
+++ b/actioncable/lib/action_cable/server/base.rb
@@ -54,15 +54,15 @@ module ActionCable
# The worker pool is where we run connection callbacks and channel actions. We do as little as possible on the server's main thread.
# The worker pool is an executor service that's backed by a pool of threads working from a task queue. The thread pool size maxes out
- # at 4 worker threads by default. Tune the size yourself with config.action_cable.worker_pool_size.
+ # at 4 worker threads by default. Tune the size yourself with `config.action_cable.worker_pool_size`.
#
# Using Active Record, Redis, etc within your channel actions means you'll get a separate connection from each thread in the worker pool.
# Plan your deployment accordingly: 5 servers each running 5 Puma workers each running an 8-thread worker pool means at least 200 database
# connections.
#
# Also, ensure that your database connection pool size is as least as large as your worker pool size. Otherwise, workers may oversubscribe
- # the db connection pool and block while they wait for other workers to release their connections. Use a smaller worker pool or a larger
- # db connection pool instead.
+ # the database connection pool and block while they wait for other workers to release their connections. Use a smaller worker pool or a larger
+ # database connection pool instead.
def worker_pool
@worker_pool || @mutex.synchronize { @worker_pool ||= ActionCable::Server::Worker.new(max_size: config.worker_pool_size) }
end
diff --git a/actioncable/lib/rails/generators/channel/channel_generator.rb b/actioncable/lib/rails/generators/channel/channel_generator.rb
index 05fd21a954..47232252e3 100644
--- a/actioncable/lib/rails/generators/channel/channel_generator.rb
+++ b/actioncable/lib/rails/generators/channel/channel_generator.rb
@@ -16,7 +16,8 @@ module Rails
if self.behavior == :invoke
template "assets/cable.js", "app/assets/javascripts/cable.js"
end
- template "assets/channel.coffee", File.join('app/assets/javascripts/channels', class_path, "#{file_name}.coffee")
+
+ js_template "assets/channel", File.join('app/assets/javascripts/channels', class_path, "#{file_name}")
end
generate_application_cable_files
diff --git a/actioncable/lib/rails/generators/channel/templates/assets/channel.js b/actioncable/lib/rails/generators/channel/templates/assets/channel.js
new file mode 100644
index 0000000000..ab0e68b11a
--- /dev/null
+++ b/actioncable/lib/rails/generators/channel/templates/assets/channel.js
@@ -0,0 +1,18 @@
+App.<%= class_name.underscore %> = App.cable.subscriptions.create("<%= class_name %>Channel", {
+ connected: function() {
+ // Called when the subscription is ready for use on the server
+ },
+
+ disconnected: function() {
+ // Called when the subscription has been terminated by the server
+ },
+
+ received: function(data) {
+ // Called when there's incoming data on the websocket for this channel
+ }<%= actions.any? ? ",\n" : '' %>
+<% actions.each do |action| -%>
+ <%=action %>: function() {
+ return this.perform('<%= action %>');
+ }<%= action == actions[-1] ? '' : ",\n" %>
+<% end -%>
+});
diff --git a/actionpack/lib/action_dispatch/routing.rb b/actionpack/lib/action_dispatch/routing.rb
index 67f441dfec..dd6ac9db9c 100644
--- a/actionpack/lib/action_dispatch/routing.rb
+++ b/actionpack/lib/action_dispatch/routing.rb
@@ -118,11 +118,11 @@ module ActionDispatch
# controller :blog do
# get 'blog/show' => :list
# get 'blog/delete' => :delete
- # get 'blog/edit/:id' => :edit
+ # get 'blog/edit' => :edit
# end
#
# # provides named routes for show, delete, and edit
- # link_to @article.title, show_path(id: @article.id)
+ # link_to @article.title, blog_show_path(id: @article.id)
#
# == Pretty URLs
#
diff --git a/actionview/CHANGELOG.md b/actionview/CHANGELOG.md
index 1d7ec77e70..9d669c7cd8 100644
--- a/actionview/CHANGELOG.md
+++ b/actionview/CHANGELOG.md
@@ -1,2 +1,19 @@
+* `select_tag`'s `include_blank` option for generation for blank option tag, now adds an empty space label,
+ when the value as well as content for option tag are empty, so that we confirm with html specification.
+ Ref: https://www.w3.org/TR/html5/forms.html#the-option-element.
+
+ Generation of option before:
+
+ ```html
+ <option value=""></option>
+ ```
+
+ Generation of option after:
+
+ ```html
+ <option value="" label=" "></option>
+ ```
+
+ *Vipul A M*
Please check [5-0-stable](https://github.com/rails/rails/blob/5-0-stable/actionview/CHANGELOG.md) for previous changes.
diff --git a/actionview/lib/action_view/helpers/form_tag_helper.rb b/actionview/lib/action_view/helpers/form_tag_helper.rb
index cfff0bef5d..82f2fd30c7 100644
--- a/actionview/lib/action_view/helpers/form_tag_helper.rb
+++ b/actionview/lib/action_view/helpers/form_tag_helper.rb
@@ -134,13 +134,15 @@ module ActionView
if options.include?(:include_blank)
include_blank = options.delete(:include_blank)
+ options_for_blank_options_tag = { value: '' }
if include_blank == true
include_blank = ''
+ options_for_blank_options_tag[:label] = ' '
end
if include_blank
- option_tags = content_tag("option".freeze, include_blank, value: '').safe_concat(option_tags)
+ option_tags = content_tag("option".freeze, include_blank, options_for_blank_options_tag).safe_concat(option_tags)
end
end
diff --git a/actionview/lib/action_view/lookup_context.rb b/actionview/lib/action_view/lookup_context.rb
index 626c4b8f5e..9db1460ee7 100644
--- a/actionview/lib/action_view/lookup_context.rb
+++ b/actionview/lib/action_view/lookup_context.rb
@@ -63,7 +63,7 @@ module ActionView
details = details.dup
details[:formats] &= Template::Types.symbols
end
- @details_keys[details] ||= new
+ @details_keys[details] ||= Concurrent::Map.new
end
def self.clear
@@ -71,13 +71,7 @@ module ActionView
end
def self.digest_caches
- @details_keys.values.map(&:digest_cache)
- end
-
- attr_reader :digest_cache
-
- def initialize
- @digest_cache = Concurrent::Map.new
+ @details_keys.values
end
end
@@ -236,7 +230,7 @@ module ActionView
end
def digest_cache
- details_key.digest_cache
+ details_key
end
def initialize_details(target, details)
diff --git a/actionview/test/actionpack/abstract/layouts_test.rb b/actionview/test/actionpack/abstract/layouts_test.rb
index 72765dc7f5..78f6e78c61 100644
--- a/actionview/test/actionpack/abstract/layouts_test.rb
+++ b/actionview/test/actionpack/abstract/layouts_test.rb
@@ -49,6 +49,10 @@ module AbstractControllerTests
render :template => ActionView::Template::Text.new("Hello string!")
end
+ def action_has_layout_false
+ render template: ActionView::Template::Text.new("Hello string!")
+ end
+
def overwrite_default
render :template => ActionView::Template::Text.new("Hello string!"), :layout => :default
end
@@ -92,7 +96,7 @@ module AbstractControllerTests
end
end
- class WithProcReturningNil < Base
+ class WithProcReturningNil < WithString
layout proc { nil }
def index
@@ -100,6 +104,14 @@ module AbstractControllerTests
end
end
+ class WithProcReturningFalse < WithString
+ layout proc { false }
+
+ def index
+ render template: ActionView::Template::Text.new("Hello false!")
+ end
+ end
+
class WithZeroArityProc < Base
layout proc { "overwrite" }
@@ -199,6 +211,14 @@ module AbstractControllerTests
end
end
+ class WithOnlyConditionalFlipped < WithOnlyConditional
+ layout "hello_override", only: :index
+ end
+
+ class WithOnlyConditionalFlippedAndInheriting < WithOnlyConditional
+ layout nil, only: :index
+ end
+
class WithExceptConditional < WithStringImpliedChild
layout "overwrite", :except => :show
@@ -211,6 +231,45 @@ module AbstractControllerTests
end
end
+ class AbstractWithString < Base
+ layout "hello"
+ abstract!
+ end
+
+ class AbstractWithStringChild < AbstractWithString
+ def index
+ render template: ActionView::Template::Text.new("Hello abstract child!")
+ end
+ end
+
+ class AbstractWithStringChildDefaultsToInherited < AbstractWithString
+ layout nil
+
+ def index
+ render template: ActionView::Template::Text.new("Hello abstract child!")
+ end
+ end
+
+ class WithConditionalOverride < WithString
+ layout "overwrite", only: :overwritten
+
+ def non_overwritten
+ render template: ActionView::Template::Text.new("Hello non overwritten!")
+ end
+
+ def overwritten
+ render template: ActionView::Template::Text.new("Hello overwritten!")
+ end
+ end
+
+ class WithConditionalOverrideFlipped < WithConditionalOverride
+ layout "hello_override", only: :non_overwritten
+ end
+
+ class WithConditionalOverrideFlippedAndInheriting < WithConditionalOverride
+ layout nil, only: :non_overwritten
+ end
+
class TestBase < ActiveSupport::TestCase
test "when no layout is specified, and no default is available, render without a layout" do
controller = Blank.new
@@ -299,10 +358,16 @@ module AbstractControllerTests
assert_equal "Overwrite Hello proc!", controller.response_body
end
- test "when layout is specified as a proc and the proc returns nil, don't use a layout" do
+ test "when layout is specified as a proc and the proc returns nil, use inherited layout" do
controller = WithProcReturningNil.new
controller.process(:index)
- assert_equal "Hello nil!", controller.response_body
+ assert_equal "With String Hello nil!", controller.response_body
+ end
+
+ test "when layout is specified as a proc and the proc returns false, use no layout instead of inherited layout" do
+ controller = WithProcReturningFalse.new
+ controller.process(:index)
+ assert_equal "Hello false!", controller.response_body
end
test "when layout is specified as a proc without parameters it works just the same" do
@@ -363,12 +428,24 @@ module AbstractControllerTests
end
test "when a grandchild has nil layout specified, the child has an implied layout, and the " \
- "parent has specified a layout, use the child controller layout" do
+ "parent has specified a layout, use the grand child controller layout" do
controller = WithGrandChildOfImplied.new
controller.process(:index)
assert_equal "With Grand Child Hello string!", controller.response_body
end
+ test "a child inherits layout from abstract controller" do
+ controller = AbstractWithStringChild.new
+ controller.process(:index)
+ assert_equal "With String Hello abstract child!", controller.response_body
+ end
+
+ test "a child inherits layout from abstract controller2" do
+ controller = AbstractWithStringChildDefaultsToInherited.new
+ controller.process(:index)
+ assert_equal "With String Hello abstract child!", controller.response_body
+ end
+
test "raises an exception when specifying layout true" do
assert_raises ArgumentError do
Object.class_eval do
@@ -391,6 +468,30 @@ module AbstractControllerTests
assert_equal "With Implied Hello index!", controller.response_body
end
+ test "when specify an :only option which match current action name and is opposite from parent controller" do
+ controller = WithOnlyConditionalFlipped.new
+ controller.process(:show)
+ assert_equal "With Implied Hello show!", controller.response_body
+ end
+
+ test "when specify an :only option which does not match current action name and is opposite from parent controller" do
+ controller = WithOnlyConditionalFlipped.new
+ controller.process(:index)
+ assert_equal "With Override Hello index!", controller.response_body
+ end
+
+ test "when specify to inherit and an :only option which match current action name and is opposite from parent controller" do
+ controller = WithOnlyConditionalFlippedAndInheriting.new
+ controller.process(:show)
+ assert_equal "With Implied Hello show!", controller.response_body
+ end
+
+ test "when specify to inherit and an :only option which does not match current action name and is opposite from parent controller" do
+ controller = WithOnlyConditionalFlippedAndInheriting.new
+ controller.process(:index)
+ assert_equal "Overwrite Hello index!", controller.response_body
+ end
+
test "when specify an :except option which match current action name" do
controller = WithExceptConditional.new
controller.process(:show)
@@ -403,6 +504,42 @@ module AbstractControllerTests
assert_equal "Overwrite Hello index!", controller.response_body
end
+ test "when specify overwrite as an :only option which match current action name" do
+ controller = WithConditionalOverride.new
+ controller.process(:overwritten)
+ assert_equal "Overwrite Hello overwritten!", controller.response_body
+ end
+
+ test "when specify overwrite as an :only option which does not match current action name" do
+ controller = WithConditionalOverride.new
+ controller.process(:non_overwritten)
+ assert_equal "Hello non overwritten!", controller.response_body
+ end
+
+ test "when specify overwrite as an :only option which match current action name and is opposite from parent controller" do
+ controller = WithConditionalOverrideFlipped.new
+ controller.process(:overwritten)
+ assert_equal "Hello overwritten!", controller.response_body
+ end
+
+ test "when specify overwrite as an :only option which does not match current action name and is opposite from parent controller" do
+ controller = WithConditionalOverrideFlipped.new
+ controller.process(:non_overwritten)
+ assert_equal "With Override Hello non overwritten!", controller.response_body
+ end
+
+ test "when specify to inherit and overwrite as an :only option which match current action name and is opposite from parent controller" do
+ controller = WithConditionalOverrideFlippedAndInheriting.new
+ controller.process(:overwritten)
+ assert_equal "Hello overwritten!", controller.response_body
+ end
+
+ test "when specify to inherit and overwrite as an :only option which does not match current action name and is opposite from parent controller" do
+ controller = WithConditionalOverrideFlippedAndInheriting.new
+ controller.process(:non_overwritten)
+ assert_equal "Overwrite Hello non overwritten!", controller.response_body
+ end
+
test "layout for anonymous controller" do
klass = Class.new(WithString) do
def index
@@ -414,6 +551,17 @@ module AbstractControllerTests
controller.process(:index)
assert_equal "With String index", controller.response_body
end
+
+ test "when layout is disabled with #action_has_layout? returning false, render no layout" do
+ controller = WithString.new
+ controller.instance_eval do
+ def action_has_layout?
+ false
+ end
+ end
+ controller.process(:action_has_layout_false)
+ assert_equal "Hello string!", controller.response_body
+ end
end
end
end
diff --git a/actionview/test/template/form_tag_helper_test.rb b/actionview/test/template/form_tag_helper_test.rb
index 7b93c8dc29..5b0b708618 100644
--- a/actionview/test/template/form_tag_helper_test.rb
+++ b/actionview/test/template/form_tag_helper_test.rb
@@ -239,8 +239,8 @@ class FormTagHelperTest < ActionView::TestCase
end
def test_select_tag_with_include_blank
- actual = select_tag "places", raw("<option>Home</option><option>Work</option><option>Pub</option>"), :include_blank => true
- expected = %(<select id="places" name="places"><option value=""></option><option>Home</option><option>Work</option><option>Pub</option></select>)
+ actual = select_tag "places", raw("<option>Home</option><option>Work</option><option>Pub</option>"), include_blank: true
+ expected = %(<select id="places" name="places"><option value="" label=" "></option><option>Home</option><option>Work</option><option>Pub</option></select>)
assert_dom_equal expected, actual
end
@@ -269,14 +269,14 @@ class FormTagHelperTest < ActionView::TestCase
end
def test_select_tag_with_prompt_and_include_blank
- actual = select_tag "places", raw("<option>Home</option><option>Work</option><option>Pub</option>"), :prompt => "string", :include_blank => true
- expected = %(<select name="places" id="places"><option value="">string</option><option value=""></option><option>Home</option><option>Work</option><option>Pub</option></select>)
+ actual = select_tag "places", raw("<option>Home</option><option>Work</option><option>Pub</option>"), prompt: "string", include_blank: true
+ expected = %(<select name="places" id="places"><option value="">string</option><option value="" label=" "></option><option>Home</option><option>Work</option><option>Pub</option></select>)
assert_dom_equal expected, actual
end
def test_select_tag_with_nil_option_tags_and_include_blank
actual = select_tag "places", nil, :include_blank => true
- expected = %(<select id="places" name="places"><option value=""></option></select>)
+ expected = %(<select id="places" name="places"><option value="" label=" "></option></select>)
assert_dom_equal expected, actual
end
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index c677178253..929f241b1b 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,6 +1,16 @@
+* Fix logging edge case where if an attribute was of the binary type and
+ was provided as a Hash.
+
+ *Jon Moss*
+
* Handle JSON deserialization correctly if the column default from database
adapter returns `''` instead of `nil`.
*Johannes Opper*
+* Introduce ActiveRecord::TransactionSerializationError for catching
+ transaction serialization failures or deadlocks.
+
+ *Erol Fornoles*
+
Please check [5-0-stable](https://github.com/rails/rails/blob/5-0-stable/activerecord/CHANGELOG.md) for previous changes.
diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb
index 6d06ce5c6c..0eaa0a4f36 100644
--- a/activerecord/lib/active_record/associations/collection_association.rb
+++ b/activerecord/lib/active_record/associations/collection_association.rb
@@ -10,9 +10,9 @@ module ActiveRecord
# HasManyAssociation => has_many
# HasManyThroughAssociation + ThroughAssociation => has_many :through
#
- # CollectionAssociation class provides common methods to the collections
+ # The CollectionAssociation class provides common methods to the collections
# defined by +has_and_belongs_to_many+, +has_many+ or +has_many+ with
- # +:through association+ option.
+ # the +:through association+ option.
#
# You need to be careful with assumptions regarding the target: The proxy
# does not fetch records from the database until it needs them, but new
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
index f3abd01290..f437dafec2 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
@@ -896,15 +896,9 @@ module ActiveRecord
end
end
- # Retrieving the connection pool happens a lot so we cache it in @class_to_pool.
+ # Retrieving the connection pool happens a lot, so we cache it in @owner_to_pool.
# This makes retrieving the connection pool O(1) once the process is warm.
# When a connection is established or removed, we invalidate the cache.
- #
- # Ideally we would use #fetch here, as class_to_pool[klass] may sometimes be nil.
- # However, benchmarking (https://gist.github.com/jonleighton/3552829) showed that
- # #fetch is significantly slower than #[]. So in the nil case, no caching will
- # take place, but that's ok since the nil case is not the common one that we wish
- # to optimise for.
def retrieve_connection_pool(spec_name)
owner_to_pool.fetch(spec_name) do
# Check if a connection was previously established in an ancestor process,
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 0f565277e3..44b4b547f3 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -727,14 +727,22 @@ module ActiveRecord
column_names.map {|name| quote_column_name(name) + option_strings[name]}
end
+ # See https://dev.mysql.com/doc/refman/5.7/en/error-messages-server.html
+ ER_DUP_ENTRY = 1062
+ ER_NO_REFERENCED_ROW_2 = 1452
+ ER_DATA_TOO_LONG = 1406
+ ER_LOCK_DEADLOCK = 1213
+
def translate_exception(exception, message)
case error_number(exception)
- when 1062
+ when ER_DUP_ENTRY
RecordNotUnique.new(message)
- when 1452
+ when ER_NO_REFERENCED_ROW_2
InvalidForeignKey.new(message)
- when 1406
+ when ER_DATA_TOO_LONG
ValueTooLong.new(message)
+ when ER_LOCK_DEADLOCK
+ TransactionSerializationError.new(message)
else
super
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index bab80a8890..ddfc560747 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -406,6 +406,7 @@ module ActiveRecord
VALUE_LIMIT_VIOLATION = "22001"
FOREIGN_KEY_VIOLATION = "23503"
UNIQUE_VIOLATION = "23505"
+ SERIALIZATION_FAILURE = "40001"
def translate_exception(exception, message)
return exception unless exception.respond_to?(:result)
@@ -417,6 +418,8 @@ module ActiveRecord
InvalidForeignKey.new(message)
when VALUE_LIMIT_VIOLATION
ValueTooLong.new(message)
+ when SERIALIZATION_FAILURE
+ TransactionSerializationError.new(message)
else
super
end
diff --git a/activerecord/lib/active_record/errors.rb b/activerecord/lib/active_record/errors.rb
index b8b8684cff..38e4fbec8b 100644
--- a/activerecord/lib/active_record/errors.rb
+++ b/activerecord/lib/active_record/errors.rb
@@ -285,6 +285,16 @@ module ActiveRecord
class TransactionIsolationError < ActiveRecordError
end
+ # TransactionSerializationError will be raised when a transaction is rolled
+ # back by the database due to a serialization failure or a deadlock.
+ #
+ # See the following:
+ #
+ # * http://www.postgresql.org/docs/current/static/transaction-iso.html
+ # * https://dev.mysql.com/doc/refman/5.7/en/error-messages-server.html#error_er_lock_deadlock
+ class TransactionSerializationError < ActiveRecordError
+ end
+
# IrreversibleOrderError is raised when a relation's order is too complex for
# +reverse_order+ to automatically reverse.
class IrreversibleOrderError < ActiveRecordError
diff --git a/activerecord/lib/active_record/internal_metadata.rb b/activerecord/lib/active_record/internal_metadata.rb
index 81db96bffd..17a5dc1d1b 100644
--- a/activerecord/lib/active_record/internal_metadata.rb
+++ b/activerecord/lib/active_record/internal_metadata.rb
@@ -19,7 +19,7 @@ module ActiveRecord
end
def []=(key, value)
- first_or_initialize(key: key).update_attributes!(value: value)
+ find_or_initialize_by(key: key).update_attributes!(value: value)
end
def [](key)
diff --git a/activerecord/lib/active_record/log_subscriber.rb b/activerecord/lib/active_record/log_subscriber.rb
index efa2a4df02..8e32af1c49 100644
--- a/activerecord/lib/active_record/log_subscriber.rb
+++ b/activerecord/lib/active_record/log_subscriber.rb
@@ -22,7 +22,11 @@ module ActiveRecord
def render_bind(attribute)
value = if attribute.type.binary? && attribute.value
- "<#{attribute.value.bytesize} bytes of binary data>"
+ if attribute.value.is_a?(Hash)
+ "<#{attribute.value_for_database.to_s.bytesize} bytes of binary data>"
+ else
+ "<#{attribute.value.bytesize} bytes of binary data>"
+ end
else
attribute.value_for_database
end
diff --git a/activerecord/lib/active_record/schema_dumper.rb b/activerecord/lib/active_record/schema_dumper.rb
index 301718b874..d769376d1a 100644
--- a/activerecord/lib/active_record/schema_dumper.rb
+++ b/activerecord/lib/active_record/schema_dumper.rb
@@ -50,10 +50,6 @@ module ActiveRecord
def header(stream)
define_params = @version ? "version: #{@version}" : ""
- if stream.respond_to?(:external_encoding) && stream.external_encoding
- stream.puts "# encoding: #{stream.external_encoding.name}"
- end
-
stream.puts <<HEADER
# This file is auto-generated from the current state of the database. Instead
# of editing this file, please use the migrations feature of Active Record to
diff --git a/activerecord/test/cases/adapters/mysql2/transaction_test.rb b/activerecord/test/cases/adapters/mysql2/transaction_test.rb
new file mode 100644
index 0000000000..0e37c70e5c
--- /dev/null
+++ b/activerecord/test/cases/adapters/mysql2/transaction_test.rb
@@ -0,0 +1,62 @@
+require "cases/helper"
+require 'support/connection_helper'
+
+module ActiveRecord
+ class Mysql2TransactionTest < ActiveRecord::Mysql2TestCase
+ self.use_transactional_tests = false
+
+ class Sample < ActiveRecord::Base
+ self.table_name = 'samples'
+ end
+
+ setup do
+ @connection = ActiveRecord::Base.connection
+ @connection.clear_cache!
+
+ @connection.transaction do
+ @connection.drop_table 'samples', if_exists: true
+ @connection.create_table('samples') do |t|
+ t.integer 'value'
+ end
+ end
+
+ Sample.reset_column_information
+ end
+
+ teardown do
+ @connection.drop_table 'samples', if_exists: true
+ end
+
+ test "raises error when a serialization failure occurs" do
+ assert_raises(ActiveRecord::TransactionSerializationError) do
+ thread = Thread.new do
+ Sample.transaction isolation: :serializable do
+ Sample.delete_all
+
+ 10.times do |i|
+ sleep 0.1
+
+ Sample.create value: i
+ end
+ end
+ end
+
+ sleep 0.1
+
+ Sample.transaction isolation: :serializable do
+ Sample.delete_all
+
+ 10.times do |i|
+ sleep 0.1
+
+ Sample.create value: i
+ end
+
+ sleep 1
+ end
+
+ thread.join
+ end
+ end
+ end
+end
diff --git a/activerecord/test/cases/adapters/postgresql/transaction_test.rb b/activerecord/test/cases/adapters/postgresql/transaction_test.rb
new file mode 100644
index 0000000000..e76705a802
--- /dev/null
+++ b/activerecord/test/cases/adapters/postgresql/transaction_test.rb
@@ -0,0 +1,72 @@
+require "cases/helper"
+require 'support/connection_helper'
+
+module ActiveRecord
+ class PostgresqlTransactionTest < ActiveRecord::PostgreSQLTestCase
+ self.use_transactional_tests = false
+
+ class Sample < ActiveRecord::Base
+ self.table_name = 'samples'
+ end
+
+ setup do
+ @connection = ActiveRecord::Base.connection
+
+ @connection.transaction do
+ @connection.drop_table 'samples', if_exists: true
+ @connection.create_table('samples') do |t|
+ t.integer 'value'
+ end
+ end
+
+ Sample.reset_column_information
+ end
+
+ teardown do
+ @connection.drop_table 'samples', if_exists: true
+ end
+
+ test "raises error when a serialization failure occurs" do
+ with_warning_suppression do
+ assert_raises(ActiveRecord::TransactionSerializationError) do
+ thread = Thread.new do
+ Sample.transaction isolation: :serializable do
+ Sample.delete_all
+
+ 10.times do |i|
+ sleep 0.1
+
+ Sample.create value: i
+ end
+ end
+ end
+
+ sleep 0.1
+
+ Sample.transaction isolation: :serializable do
+ Sample.delete_all
+
+ 10.times do |i|
+ sleep 0.1
+
+ Sample.create value: i
+ end
+
+ sleep 1
+ end
+
+ thread.join
+ end
+ end
+ end
+
+ protected
+
+ def with_warning_suppression
+ log_level = @connection.client_min_messages
+ @connection.client_min_messages = 'error'
+ yield
+ @connection.client_min_messages = log_level
+ end
+ end
+end
diff --git a/activerecord/test/cases/log_subscriber_test.rb b/activerecord/test/cases/log_subscriber_test.rb
index 707a2d1da1..c97960a412 100644
--- a/activerecord/test/cases/log_subscriber_test.rb
+++ b/activerecord/test/cases/log_subscriber_test.rb
@@ -215,5 +215,11 @@ class LogSubscriberTest < ActiveRecord::TestCase
wait
assert_match(/<16 bytes of binary data>/, @logger.logged(:debug).join)
end
+
+ def test_binary_data_hash
+ Binary.create(data: { a: 1 })
+ wait
+ assert_match(/<7 bytes of binary data>/, @logger.logged(:debug).join)
+ end
end
end
diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb
index 6ad028d31b..36b6662820 100644
--- a/activerecord/test/cases/migration_test.rb
+++ b/activerecord/test/cases/migration_test.rb
@@ -428,6 +428,23 @@ class MigrationTest < ActiveRecord::TestCase
ENV["RACK_ENV"] = original_rack_env
end
+ def test_internal_metadata_stores_environment_when_other_data_exists
+ ActiveRecord::InternalMetadata.delete_all
+ ActiveRecord::InternalMetadata[:foo] = 'bar'
+
+ current_env = ActiveRecord::ConnectionHandling::DEFAULT_ENV.call
+ migrations_path = MIGRATIONS_ROOT + "/valid"
+ old_path = ActiveRecord::Migrator.migrations_paths
+ ActiveRecord::Migrator.migrations_paths = migrations_path
+
+ current_env = ActiveRecord::ConnectionHandling::DEFAULT_ENV.call
+ ActiveRecord::Migrator.up(migrations_path)
+ assert_equal current_env, ActiveRecord::InternalMetadata[:environment]
+ assert_equal 'bar', ActiveRecord::InternalMetadata[:foo]
+ ensure
+ ActiveRecord::Migrator.migrations_paths = old_path
+ end
+
def test_rename_internal_metadata_table
original_internal_metadata_table_name = ActiveRecord::Base.internal_metadata_table_name
diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb
index f1927f561e..9dc1f5e2c2 100644
--- a/activerecord/test/cases/schema_dumper_test.rb
+++ b/activerecord/test/cases/schema_dumper_test.rb
@@ -47,10 +47,6 @@ class SchemaDumperTest < ActiveRecord::TestCase
end
end
- def test_magic_comment
- assert_match "# encoding: #{Encoding.default_external.name}", standard_dump
- end
-
def test_schema_dump
output = standard_dump
assert_match %r{create_table "accounts"}, output
diff --git a/activesupport/lib/active_support/xml_mini/rexml.rb b/activesupport/lib/active_support/xml_mini/rexml.rb
index 924ed72345..95af5af2c0 100644
--- a/activesupport/lib/active_support/xml_mini/rexml.rb
+++ b/activesupport/lib/active_support/xml_mini/rexml.rb
@@ -20,11 +20,9 @@ module ActiveSupport
data = StringIO.new(data || '')
end
- char = data.getc
- if char.nil?
+ if data.eof?
{}
else
- data.ungetc(char)
silence_warnings { require 'rexml/document' } unless defined?(REXML::Document)
doc = REXML::Document.new(data)
diff --git a/activesupport/test/xml_mini/rexml_engine_test.rb b/activesupport/test/xml_mini/rexml_engine_test.rb
index f0067ca656..6e9ce7ac11 100644
--- a/activesupport/test/xml_mini/rexml_engine_test.rb
+++ b/activesupport/test/xml_mini/rexml_engine_test.rb
@@ -22,14 +22,24 @@ class REXMLEngineTest < ActiveSupport::TestCase
morning
</root>
eoxml
- assert_equal_rexml(io)
+ hash = ActiveSupport::XmlMini.parse(io)
+ assert hash.has_key?('root')
+ assert hash['root'].has_key?('products')
+ assert_match "good", hash['root']['__content__']
+ products = hash['root']['products']
+ assert products.has_key?("__content__")
+ assert_match 'hello everyone', products['__content__']
+ end
+
+ def test_parse_from_empty_string
+ ActiveSupport::XmlMini.backend = 'REXML'
+ assert_equal({}, ActiveSupport::XmlMini.parse(""))
+ end
+
+ def test_parse_from_frozen_string
+ ActiveSupport::XmlMini.backend = 'REXML'
+ xml_string = "<root></root>".freeze
+ assert_equal({"root" => {}}, ActiveSupport::XmlMini.parse(xml_string))
end
- private
- def assert_equal_rexml(xml)
- parsed_xml = ActiveSupport::XmlMini.parse(xml)
- xml.rewind if xml.respond_to?(:rewind)
- hash = ActiveSupport::XmlMini.with_backend('REXML') { ActiveSupport::XmlMini.parse(xml) }
- assert_equal(hash, parsed_xml)
- end
end
diff --git a/guides/source/5_0_release_notes.md b/guides/source/5_0_release_notes.md
index 2583b32925..07cdf84c9c 100644
--- a/guides/source/5_0_release_notes.md
+++ b/guides/source/5_0_release_notes.md
@@ -297,6 +297,9 @@ Please refer to the [Changelog][action-pack] for detailed changes.
* `ActionDispatch::IntegrationTest` and `ActionController::TestCase` deprecate positional arguments in favor of
keyword arguments. ([Pull Request](https://github.com/rails/rails/pull/18323))
+* Deprecated `:controller` and `:action` path parameters.
+ ([Pull Request](https://github.com/rails/rails/pull/23980))
+
### Notable changes
* Added `ActionController::Renderer` to render arbitrary templates
diff --git a/guides/source/active_record_querying.md b/guides/source/active_record_querying.md
index 9a13e3bda7..928ab43b3b 100644
--- a/guides/source/active_record_querying.md
+++ b/guides/source/active_record_querying.md
@@ -1121,7 +1121,7 @@ If you want to select a set of records whether or not they have associated
records you can use the `left_outer_joins` method.
```ruby
-Author.left_outer_joins(:posts).uniq.select('authors.*, COUNT(posts.*) AS posts_count').group('authors.id')
+Author.left_outer_joins(:posts).distinct.select('authors.*, COUNT(posts.*) AS posts_count').group('authors.id')
```
Which produces:
diff --git a/guides/source/testing.md b/guides/source/testing.md
index 34c831c802..050bdda9e3 100644
--- a/guides/source/testing.md
+++ b/guides/source/testing.md
@@ -1289,5 +1289,5 @@ end
assert_equal Date.new(2004, 10, 24), user.activation_date # The change was visible only inside the `travel_to` block.
```
-Please see [`ActiveSupport::TimeHelpers` API Documentation](http://api.rubyonrails.org/classes/ActiveSupport/Testing/TimeHelpers.html)
+Please see [`ActiveSupport::Testing::TimeHelpers` API Documentation](http://api.rubyonrails.org/classes/ActiveSupport/Testing/TimeHelpers.html)
for in-depth information about the available time helpers.
diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md
index 41fbf9044c..555cb27921 100644
--- a/railties/CHANGELOG.md
+++ b/railties/CHANGELOG.md
@@ -1,2 +1,7 @@
+## Rails 5.1.0.alpha ##
+
+* Added a shared section to `config/secrets.yml` that will be loaded for all environments.
+
+ *DHH*
Please check [5-0-stable](https://github.com/rails/rails/blob/5-0-stable/railties/CHANGELOG.md) for previous changes.
diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb
index ed106c9918..c383de3e06 100644
--- a/railties/lib/rails/application.rb
+++ b/railties/lib/rails/application.rb
@@ -385,11 +385,16 @@ module Rails
def secrets
@secrets ||= begin
secrets = ActiveSupport::OrderedOptions.new
- yaml = config.paths["config/secrets"].first
+ yaml = config.paths["config/secrets"].first
+
if File.exist?(yaml)
require "erb"
- all_secrets = YAML.load(ERB.new(IO.read(yaml)).result) || {}
- env_secrets = all_secrets[Rails.env]
+
+ all_secrets = YAML.load(ERB.new(IO.read(yaml)).result) || {}
+ shared_secrets = all_secrets['shared']
+ env_secrets = all_secrets[Rails.env]
+
+ secrets.merge!(shared_secrets.symbolize_keys) if shared_secrets
secrets.merge!(env_secrets.symbolize_keys) if env_secrets
end
diff --git a/railties/lib/rails/generators/named_base.rb b/railties/lib/rails/generators/named_base.rb
index efbf51ddfb..ee076eb711 100644
--- a/railties/lib/rails/generators/named_base.rb
+++ b/railties/lib/rails/generators/named_base.rb
@@ -26,6 +26,10 @@ module Rails
super
end
end
+
+ def js_template(source, destination)
+ template(source + '.js', destination + '.js')
+ end
end
protected
diff --git a/railties/lib/rails/generators/rails/app/templates/config/secrets.yml b/railties/lib/rails/generators/rails/app/templates/config/secrets.yml
index cdea2fd060..8e995a5df1 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/secrets.yml
+++ b/railties/lib/rails/generators/rails/app/templates/config/secrets.yml
@@ -10,6 +10,13 @@
# Make sure the secrets in this file are kept private
# if you're sharing your code publicly.
+# Shared secrets are available across all environments.
+
+shared:
+ api_key: 123
+
+# Environmental secrets are only available for that specific environment.
+
development:
secret_key_base: <%= app_secret %>
@@ -18,5 +25,6 @@ test:
# Do not keep production secrets in the repository,
# instead read values from the environment.
+
production:
secret_key_base: <%%= ENV["SECRET_KEY_BASE"] %>
diff --git a/railties/lib/rails/generators/rails/controller/controller_generator.rb b/railties/lib/rails/generators/rails/controller/controller_generator.rb
index 0a4c509a31..6c583e5811 100644
--- a/railties/lib/rails/generators/rails/controller/controller_generator.rb
+++ b/railties/lib/rails/generators/rails/controller/controller_generator.rb
@@ -19,8 +19,7 @@ module Rails
end
end
- hook_for :template_engine, :test_framework
- hook_for :helper, :assets, hide: true
+ hook_for :template_engine, :test_framework, :helper, :assets
private
diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb
index 9f3a9cd232..7ec25aeca1 100644
--- a/railties/test/application/configuration_test.rb
+++ b/railties/test/application/configuration_test.rb
@@ -555,6 +555,31 @@ module ApplicationTests
assert_equal 'myamazonsecretaccesskey', app.secrets.aws_secret_access_key
end
+ test "shared secrets saved in config/secrets.yml are loaded in app secrets" do
+ app_file 'config/secrets.yml', <<-YAML
+ shared:
+ api_key: 3b7cd727
+ YAML
+
+ app 'development'
+
+ assert_equal '3b7cd727', app.secrets.api_key
+ end
+
+ test "shared secrets will yield to environment specific secrets" do
+ app_file 'config/secrets.yml', <<-YAML
+ shared:
+ api_key: 3b7cd727
+
+ development:
+ api_key: abc12345
+ YAML
+
+ app 'development'
+
+ assert_equal 'abc12345', app.secrets.api_key
+ end
+
test "blank config/secrets.yml does not crash the loading process" do
app_file 'config/secrets.yml', <<-YAML
YAML
diff --git a/railties/test/generators/channel_generator_test.rb b/railties/test/generators/channel_generator_test.rb
index d58b54ac24..e3edde681f 100644
--- a/railties/test/generators/channel_generator_test.rb
+++ b/railties/test/generators/channel_generator_test.rb
@@ -24,8 +24,24 @@ class ChannelGeneratorTest < Rails::Generators::TestCase
assert_match(/class ChatChannel < ApplicationCable::Channel/, channel)
end
- assert_file "app/assets/javascripts/channels/chat.coffee" do |channel|
- assert_match(/App.cable.subscriptions.create "ChatChannel"/, channel)
+ assert_file "app/assets/javascripts/channels/chat.js" do |channel|
+ assert_match(/App.chat = App.cable.subscriptions.create\("ChatChannel/, channel)
+ end
+ end
+
+ def test_channel_with_multiple_actions_is_created
+ run_generator ['chat', 'speak', 'mute']
+
+ assert_file "app/channels/chat_channel.rb" do |channel|
+ assert_match(/class ChatChannel < ApplicationCable::Channel/, channel)
+ assert_match(/def speak/, channel)
+ assert_match(/def mute/, channel)
+ end
+
+ assert_file "app/assets/javascripts/channels/chat.js" do |channel|
+ assert_match(/App.chat = App.cable.subscriptions.create\("ChatChannel/, channel)
+ assert_match(/,\n\n speak/, channel)
+ assert_match(/,\n\n mute: function\(\) \{\n return this\.perform\('mute'\);\n \}\n\}\);/, channel)
end
end
@@ -36,7 +52,7 @@ class ChannelGeneratorTest < Rails::Generators::TestCase
assert_match(/class ChatChannel < ApplicationCable::Channel/, channel)
end
- assert_no_file "app/assets/javascripts/channels/chat.coffee"
+ assert_no_file "app/assets/javascripts/channels/chat.js"
end
def test_cable_js_is_created_if_not_present_already
@@ -52,7 +68,7 @@ class ChannelGeneratorTest < Rails::Generators::TestCase
run_generator ['chat'], behavior: :revoke
assert_no_file "app/channels/chat_channel.rb"
- assert_no_file "app/assets/javascripts/channels/chat.coffee"
+ assert_no_file "app/assets/javascripts/channels/chat.js"
assert_file "app/channels/application_cable/channel.rb"
assert_file "app/channels/application_cable/connection.rb"