aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Gemfile.lock2
-rw-r--r--actioncable/lib/action_cable/channel/base.rb2
-rw-r--r--actioncable/lib/action_cable/connection/client_socket.rb7
-rw-r--r--actioncable/lib/action_cable/connection/subscriptions.rb2
-rw-r--r--actioncable/lib/action_cable/engine.rb6
-rw-r--r--actioncable/lib/action_cable/server/configuration.rb15
-rw-r--r--actioncable/test/client/echo_channel.rb4
-rw-r--r--actioncable/test/client_test.rb58
-rw-r--r--actioncable/test/subscription_adapter/common.rb22
-rw-r--r--actioncable/test/worker_test.rb4
-rw-r--r--actionmailer/CHANGELOG.md6
-rw-r--r--actionmailer/lib/action_mailer/railtie.rb2
-rw-r--r--actionmailer/lib/action_mailer/test_case.rb17
-rw-r--r--actionpack/CHANGELOG.md4
-rw-r--r--actionpack/lib/action_controller/metal.rb5
-rw-r--r--actionpack/lib/action_controller/metal/strong_parameters.rb18
-rw-r--r--actionpack/lib/action_dispatch/http/mime_types.rb3
-rw-r--r--actionpack/lib/action_dispatch/journey/backwards.rb5
-rw-r--r--actionpack/lib/action_dispatch/journey/router.rb3
-rw-r--r--actionpack/lib/action_dispatch/middleware/cookies.rb7
-rw-r--r--actionpack/lib/action_dispatch/middleware/debug_exceptions.rb15
-rw-r--r--actionpack/lib/action_dispatch/middleware/session/cookie_store.rb2
-rw-r--r--actionpack/lib/action_dispatch/routing/route_set.rb2
-rw-r--r--actionpack/lib/action_dispatch/routing/url_for.rb2
-rw-r--r--actionpack/test/controller/parameters/accessors_test.rb9
-rw-r--r--actionpack/test/controller/parameters/always_permitted_parameters_test.rb6
-rw-r--r--actionpack/test/controller/parameters/parameters_permit_test.rb21
-rw-r--r--actionpack/test/controller/redirect_test.rb2
-rw-r--r--actionpack/test/controller/render_test.rb2
-rw-r--r--actionpack/test/controller/test_case_test.rb10
-rw-r--r--actionpack/test/dispatch/mime_type_test.rb2
-rw-r--r--actionpack/test/dispatch/request/json_params_parsing_test.rb11
-rw-r--r--actionview/lib/action_view/digestor.rb16
-rw-r--r--actionview/lib/action_view/helpers/tag_helper.rb1
-rw-r--r--actionview/lib/action_view/lookup_context.rb16
-rw-r--r--actionview/lib/action_view/template/error.rb4
-rw-r--r--actionview/test/fixtures/digestor/messages/peek.html.erb2
-rw-r--r--actionview/test/template/digestor_test.rb15
-rw-r--r--actionview/test/template/render_test.rb6
-rw-r--r--actionview/test/template/tag_helper_test.rb6
-rw-r--r--activerecord/CHANGELOG.md31
-rw-r--r--activerecord/lib/active_record/attribute_assignment.rb8
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_adapter.rb3
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb9
-rw-r--r--activerecord/lib/active_record/relation/finder_methods.rb13
-rw-r--r--activerecord/lib/active_record/relation/predicate_builder.rb7
-rw-r--r--activerecord/lib/active_record/relation/predicate_builder/association_query_handler.rb10
-rw-r--r--activerecord/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb57
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb4
-rw-r--r--activerecord/test/cases/batches_test.rb4
-rw-r--r--activerecord/test/cases/migration/references_foreign_key_test.rb16
-rw-r--r--activerecord/test/cases/nested_attributes_test.rb14
-rw-r--r--activerecord/test/cases/relation/or_test.rb6
-rw-r--r--activerecord/test/cases/relation/where_test.rb14
-rw-r--r--activerecord/test/cases/store_test.rb1
-rw-r--r--activerecord/test/fixtures/price_estimates.yml11
-rw-r--r--activerecord/test/models/car.rb2
-rw-r--r--activesupport/CHANGELOG.md8
-rw-r--r--activesupport/lib/active_support/core_ext/date_and_time/calculations.rb5
-rw-r--r--activesupport/lib/active_support/dependencies.rb6
-rw-r--r--activesupport/lib/active_support/message_verifier.rb6
-rw-r--r--activesupport/lib/active_support/test_case.rb7
-rw-r--r--activesupport/test/core_ext/date_and_time_behavior.rb10
-rw-r--r--activesupport/test/dependencies_test.rb13
-rw-r--r--guides/bug_report_templates/action_controller_master.rb5
-rw-r--r--guides/bug_report_templates/active_record_master.rb5
-rw-r--r--guides/bug_report_templates/generic_master.rb5
-rw-r--r--guides/source/5_0_release_notes.md3
-rw-r--r--guides/source/active_record_querying.md2
-rw-r--r--guides/source/active_record_validations.md2
-rw-r--r--guides/source/active_support_core_extensions.md6
-rw-r--r--guides/source/api_app.md37
-rw-r--r--guides/source/engines.md4
-rw-r--r--guides/source/i18n.md2
-rw-r--r--guides/source/testing.md6
-rw-r--r--railties/CHANGELOG.md8
-rw-r--r--railties/lib/rails/engine/configuration.rb1
-rw-r--r--railties/lib/rails/generators.rb6
-rw-r--r--railties/lib/rails/generators/app_base.rb7
-rw-r--r--railties/lib/rails/generators/rails/app/app_generator.rb4
-rw-r--r--railties/lib/rails/generators/rails/app/templates/Gemfile4
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt3
-rw-r--r--railties/lib/rails/generators/rails/plugin/plugin_generator.rb5
-rw-r--r--railties/lib/rails/railtie.rb11
-rw-r--r--railties/lib/rails/test_unit/minitest_plugin.rb2
-rw-r--r--railties/lib/rails/test_unit/reporter.rb14
-rw-r--r--railties/test/application/configuration_test.rb4
-rw-r--r--railties/test/application/generators_test.rb10
-rw-r--r--railties/test/application/integration_test_case_test.rb46
-rw-r--r--railties/test/application/per_request_digest_cache_test.rb5
-rw-r--r--railties/test/application/test_runner_test.rb2
-rw-r--r--railties/test/generators/api_app_generator_test.rb4
-rw-r--r--railties/test/generators/app_generator_test.rb33
-rw-r--r--railties/test/generators/plugin_generator_test.rb8
-rw-r--r--railties/test/generators/plugin_test_runner_test.rb2
-rw-r--r--railties/test/generators/test_runner_in_engine_test.rb2
-rw-r--r--railties/test/isolation/abstract_unit.rb5
-rw-r--r--railties/test/test_unit/reporter_test.rb21
-rw-r--r--tools/README.md1
101 files changed, 600 insertions, 298 deletions
diff --git a/Gemfile.lock b/Gemfile.lock
index 0d2ce7b97f..d12aa0772f 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -118,7 +118,7 @@ GEM
coffee-script-source (1.10.0)
concurrent-ruby (1.0.0)
connection_pool (2.2.0)
- dalli (2.7.5)
+ dalli (2.7.6)
dante (0.2.0)
delayed_job (4.1.1)
activesupport (>= 3.0, < 5.0)
diff --git a/actioncable/lib/action_cable/channel/base.rb b/actioncable/lib/action_cable/channel/base.rb
index 874ebe2e71..86c8f12f6e 100644
--- a/actioncable/lib/action_cable/channel/base.rb
+++ b/actioncable/lib/action_cable/channel/base.rb
@@ -191,7 +191,7 @@ module ActionCable
# Transmit a hash of data to the subscriber. The hash will automatically be wrapped in a JSON envelope with
# the proper channel identifier marked as the recipient.
def transmit(data, via: nil)
- logger.info "#{self.class.name} transmitting #{data.inspect}".tap { |m| m << " (via #{via})" if via }
+ logger.info "#{self.class.name} transmitting #{data.inspect.truncate(300)}".tap { |m| m << " (via #{via})" if via }
connection.transmit ActiveSupport::JSON.encode(identifier: @identifier, message: data)
end
diff --git a/actioncable/lib/action_cable/connection/client_socket.rb b/actioncable/lib/action_cable/connection/client_socket.rb
index ef937d7c16..95e1ac4c16 100644
--- a/actioncable/lib/action_cable/connection/client_socket.rb
+++ b/actioncable/lib/action_cable/connection/client_socket.rb
@@ -132,11 +132,8 @@ module ActionCable
@ready_state = CLOSING
@close_params = [reason, code]
- if @stream
- @stream.shutdown
- else
- finalize_close
- end
+ @stream.shutdown if @stream
+ finalize_close
end
def finalize_close
diff --git a/actioncable/lib/action_cable/connection/subscriptions.rb b/actioncable/lib/action_cable/connection/subscriptions.rb
index d7f95e6a62..24934e12f2 100644
--- a/actioncable/lib/action_cable/connection/subscriptions.rb
+++ b/actioncable/lib/action_cable/connection/subscriptions.rb
@@ -54,7 +54,7 @@ module ActionCable
end
def unsubscribe_from_all
- subscriptions.each { |id, channel| channel.unsubscribe_from_channel }
+ subscriptions.each { |id, channel| remove_subscription(channel) }
end
protected
diff --git a/actioncable/lib/action_cable/engine.rb b/actioncable/lib/action_cable/engine.rb
index f5e233e091..f5f1cb59e0 100644
--- a/actioncable/lib/action_cable/engine.rb
+++ b/actioncable/lib/action_cable/engine.rb
@@ -31,6 +31,12 @@ module ActionCable
self.cable = Rails.application.config_for(config_path).with_indifferent_access
end
+ if 'ApplicationCable::Connection'.safe_constantize
+ self.connection_class = ApplicationCable::Connection
+ end
+
+ self.channel_paths = Rails.application.paths['app/channels'].existent
+
options.each { |k,v| send("#{k}=", v) }
end
end
diff --git a/actioncable/lib/action_cable/server/configuration.rb b/actioncable/lib/action_cable/server/configuration.rb
index 9a248933c4..58bb8ff65a 100644
--- a/actioncable/lib/action_cable/server/configuration.rb
+++ b/actioncable/lib/action_cable/server/configuration.rb
@@ -5,27 +5,20 @@ module ActionCable
class Configuration
attr_accessor :logger, :log_tags
attr_accessor :connection_class, :worker_pool_size
- attr_accessor :channel_load_paths
attr_accessor :disable_request_forgery_protection, :allowed_request_origins
attr_accessor :cable, :url
+ attr_accessor :channel_paths # :nodoc:
+
def initialize
@log_tags = []
- @connection_class = ApplicationCable::Connection
- @worker_pool_size = 100
-
- @channel_load_paths = [Rails.root.join('app/channels')]
+ @connection_class = ActionCable::Connection::Base
+ @worker_pool_size = 100
@disable_request_forgery_protection = false
end
- def channel_paths
- @channel_paths ||= channel_load_paths.flat_map do |path|
- Dir["#{path}/**/*_channel.rb"]
- end
- end
-
def channel_class_names
@channel_class_names ||= channel_paths.collect do |channel_path|
Pathname.new(channel_path).basename.to_s.split('.').first.camelize
diff --git a/actioncable/test/client/echo_channel.rb b/actioncable/test/client/echo_channel.rb
index 63e35f194a..5a7bac25c5 100644
--- a/actioncable/test/client/echo_channel.rb
+++ b/actioncable/test/client/echo_channel.rb
@@ -3,6 +3,10 @@ class EchoChannel < ActionCable::Channel::Base
stream_from "global"
end
+ def unsubscribed
+ 'Goodbye from EchoChannel!'
+ end
+
def ding(data)
transmit(dong: data['message'])
end
diff --git a/actioncable/test/client_test.rb b/actioncable/test/client_test.rb
index 4ade9832e0..1b07689127 100644
--- a/actioncable/test/client_test.rb
+++ b/actioncable/test/client_test.rb
@@ -12,24 +12,15 @@ class ClientTest < ActionCable::TestCase
WAIT_WHEN_NOT_EXPECTING_EVENT = 0.2
def setup
- # TODO: ActionCable requires a *lot* of setup at the moment...
- ::Object.const_set(:ApplicationCable, Module.new)
- ::ApplicationCable.const_set(:Connection, Class.new(ActionCable::Connection::Base))
-
- ::Object.const_set(:Rails, Module.new)
- ::Rails.singleton_class.send(:define_method, :root) { Pathname.new(__dir__) }
-
ActionCable.instance_variable_set(:@server, nil)
server = ActionCable.server
- server.config = ActionCable::Server::Configuration.new
- inner_logger = Logger.new(StringIO.new).tap { |l| l.level = Logger::UNKNOWN }
- server.config.logger = ActionCable::Connection::TaggedLoggerProxy.new(inner_logger, tags: [])
+ server.config.logger = Logger.new(StringIO.new).tap { |l| l.level = Logger::UNKNOWN }
server.config.cable = { adapter: 'async' }.with_indifferent_access
# and now the "real" setup for our test:
server.config.disable_request_forgery_protection = true
- server.config.channel_load_paths = [File.expand_path('client', __dir__)]
+ server.config.channel_paths = [ File.expand_path('client/echo_channel.rb', __dir__) ]
Thread.new { EventMachine.run } unless EventMachine.reactor_running?
Thread.pass until EventMachine.reactor_running?
@@ -40,15 +31,6 @@ class ClientTest < ActionCable::TestCase
def teardown
$VERBOSE = @previous_verbose
-
- begin
- ::Object.send(:remove_const, :ApplicationCable)
- rescue NameError
- end
- begin
- ::Object.send(:remove_const, :Rails)
- rescue NameError
- end
end
def with_puma_server(rack_app = ActionCable.server, port = 3099)
@@ -72,7 +54,7 @@ class ClientTest < ActionCable::TestCase
@ws = Faye::WebSocket::Client.new("ws://127.0.0.1:#{port}/")
@messages = Queue.new
@closed = Concurrent::Event.new
- @has_messages = Concurrent::Event.new
+ @has_messages = Concurrent::Semaphore.new(0)
@pings = 0
open = Concurrent::Event.new
@@ -97,7 +79,7 @@ class ClientTest < ActionCable::TestCase
@pings += 1
else
@messages << hash
- @has_messages.set
+ @has_messages.release
end
end
@@ -110,8 +92,7 @@ class ClientTest < ActionCable::TestCase
end
def read_message
- @has_messages.wait(WAIT_WHEN_EXPECTING_EVENT) if @messages.empty?
- @has_messages.reset if @messages.size < 2
+ @has_messages.try_acquire(1, WAIT_WHEN_EXPECTING_EVENT)
msg = @messages.pop(true)
raise msg if msg.is_a?(Exception)
@@ -122,9 +103,11 @@ class ClientTest < ActionCable::TestCase
def read_messages(expected_size = 0)
list = []
loop do
- @has_messages.wait(list.size < expected_size ? WAIT_WHEN_EXPECTING_EVENT : WAIT_WHEN_NOT_EXPECTING_EVENT)
- if @has_messages.set?
- list << read_message
+ if @has_messages.try_acquire(1, list.size < expected_size ? WAIT_WHEN_EXPECTING_EVENT : WAIT_WHEN_NOT_EXPECTING_EVENT)
+ msg = @messages.pop(true)
+ raise msg if msg.is_a?(Exception)
+
+ list << msg
else
break
end
@@ -216,4 +199,25 @@ class ClientTest < ActionCable::TestCase
c.close # disappear before read
end
end
+
+ def test_unsubscribe_client
+ with_puma_server do |port|
+ app = ActionCable.server
+ identifier = JSON.dump(channel: 'EchoChannel')
+
+ c = faye_client(port)
+ c.send_message command: 'subscribe', identifier: identifier
+ assert_equal({"identifier"=>"{\"channel\":\"EchoChannel\"}", "type"=>"confirm_subscription"}, c.read_message)
+ assert_equal(1, app.connections.count)
+ assert(app.remote_connections.where(identifier: identifier))
+
+ channel = app.connections.first.subscriptions.send(:subscriptions).first[1]
+ channel.expects(:unsubscribed)
+ c.close
+ sleep 0.1 # Data takes a moment to process
+
+ # All data is removed: No more connection or subscription information!
+ assert_equal(0, app.connections.count)
+ end
+ end
end
diff --git a/actioncable/test/subscription_adapter/common.rb b/actioncable/test/subscription_adapter/common.rb
index 361858784e..b31c2aa36c 100644
--- a/actioncable/test/subscription_adapter/common.rb
+++ b/actioncable/test/subscription_adapter/common.rb
@@ -9,20 +9,7 @@ module CommonSubscriptionAdapterTest
WAIT_WHEN_NOT_EXPECTING_EVENT = 0.2
def setup
- # TODO: ActionCable requires a *lot* of setup at the moment...
- ::Object.const_set(:ApplicationCable, Module.new)
- ::ApplicationCable.const_set(:Connection, Class.new(ActionCable::Connection::Base))
-
- ::Object.const_set(:Rails, Module.new)
- ::Rails.singleton_class.send(:define_method, :root) { Pathname.new(__dir__) }
-
server = ActionCable::Server::Base.new
- server.config = ActionCable::Server::Configuration.new
- inner_logger = Logger.new(StringIO.new).tap { |l| l.level = Logger::UNKNOWN }
- server.config.logger = ActionCable::Connection::TaggedLoggerProxy.new(inner_logger, tags: [])
-
-
- # and now the "real" setup for our test:
server.config.cable = cable_config.with_indifferent_access
adapter_klass = server.config.pubsub_adapter
@@ -34,15 +21,6 @@ module CommonSubscriptionAdapterTest
def teardown
@tx_adapter.shutdown if @tx_adapter && @tx_adapter != @rx_adapter
@rx_adapter.shutdown if @rx_adapter
-
- begin
- ::Object.send(:remove_const, :ApplicationCable)
- rescue NameError
- end
- begin
- ::Object.send(:remove_const, :Rails)
- rescue NameError
- end
end
diff --git a/actioncable/test/worker_test.rb b/actioncable/test/worker_test.rb
index 4a699cde27..654f49821e 100644
--- a/actioncable/test/worker_test.rb
+++ b/actioncable/test/worker_test.rb
@@ -17,7 +17,9 @@ class WorkerTest < ActiveSupport::TestCase
end
def logger
- ActionCable.server.logger
+ # Impersonating a connection requires a TaggedLoggerProxy'ied logger.
+ inner_logger = Logger.new(StringIO.new).tap { |l| l.level = Logger::UNKNOWN }
+ ActionCable::Connection::TaggedLoggerProxy.new(inner_logger, tags: [])
end
end
diff --git a/actionmailer/CHANGELOG.md b/actionmailer/CHANGELOG.md
index 22e9bd12a1..1531f2b471 100644
--- a/actionmailer/CHANGELOG.md
+++ b/actionmailer/CHANGELOG.md
@@ -1,3 +1,9 @@
+* Reset `ActionMailer::Base.deliveries` after every test in
+ `ActionDispatch::IntegrationTest`.
+
+ *Yves Senn*
+
+
## Rails 5.0.0.beta2 (February 01, 2016) ##
* No changes.
diff --git a/actionmailer/lib/action_mailer/railtie.rb b/actionmailer/lib/action_mailer/railtie.rb
index fa707021c7..22390c4953 100644
--- a/actionmailer/lib/action_mailer/railtie.rb
+++ b/actionmailer/lib/action_mailer/railtie.rb
@@ -51,6 +51,8 @@ module ActionMailer
get '/rails/mailers/*path' => "rails/mailers#preview"
end
end
+
+ ActionDispatch::IntegrationTest.send :include, ActionMailer::TestCase::ClearTestDeliveries
end
end
diff --git a/actionmailer/lib/action_mailer/test_case.rb b/actionmailer/lib/action_mailer/test_case.rb
index 0aa15e31ba..d83719e57d 100644
--- a/actionmailer/lib/action_mailer/test_case.rb
+++ b/actionmailer/lib/action_mailer/test_case.rb
@@ -11,6 +11,21 @@ module ActionMailer
end
class TestCase < ActiveSupport::TestCase
+ module ClearTestDeliveries
+ extend ActiveSupport::Concern
+
+ included do
+ teardown :clear_test_deliviers
+ end
+
+ private
+ def clear_test_deliviers
+ if ActionMailer::Base.delivery_method == :test
+ ActionMailer::Base.deliveries.clear
+ end
+ end
+ end
+
module Behavior
extend ActiveSupport::Concern
@@ -66,7 +81,6 @@ module ActionMailer
def restore_test_deliveries # :nodoc:
restore_delivery_method
ActionMailer::Base.perform_deliveries = @old_perform_deliveries
- ActionMailer::Base.deliveries.clear
end
def set_delivery_method(method) # :nodoc:
@@ -100,5 +114,6 @@ module ActionMailer
end
include Behavior
+ include ClearTestDeliveries
end
end
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md
index 931313612c..d473ab427d 100644
--- a/actionpack/CHANGELOG.md
+++ b/actionpack/CHANGELOG.md
@@ -1,3 +1,7 @@
+* Add application/gzip as a default mime type.
+
+ *Mehmet Emin İNAÇ*
+
* Add request encoding and response parsing to integration tests.
What previously was:
diff --git a/actionpack/lib/action_controller/metal.rb b/actionpack/lib/action_controller/metal.rb
index 1641d01c30..f6e67b02d7 100644
--- a/actionpack/lib/action_controller/metal.rb
+++ b/actionpack/lib/action_controller/metal.rb
@@ -175,10 +175,7 @@ module ActionController
body = [body] unless body.nil? || body.respond_to?(:each)
response.reset_body!
return unless body
- body.each { |part|
- next if part.empty?
- response.write part
- }
+ response.body = body
super
end
diff --git a/actionpack/lib/action_controller/metal/strong_parameters.rb b/actionpack/lib/action_controller/metal/strong_parameters.rb
index d3382ef296..e17189f9f9 100644
--- a/actionpack/lib/action_controller/metal/strong_parameters.rb
+++ b/actionpack/lib/action_controller/metal/strong_parameters.rb
@@ -109,7 +109,7 @@ module ActionController
cattr_accessor :permit_all_parameters, instance_accessor: false
cattr_accessor :action_on_unpermitted_parameters, instance_accessor: false
- delegate :keys, :key?, :has_key?, :values, :has_value?, :value?, :empty?, :include?, :inspect,
+ delegate :keys, :key?, :has_key?, :values, :has_value?, :value?, :empty?, :include?,
:as_json, to: :@parameters
# By default, never raise an UnpermittedParameters exception if these
@@ -122,16 +122,6 @@ module ActionController
cattr_accessor :always_permitted_parameters
self.always_permitted_parameters = %w( controller action )
- def self.const_missing(const_name)
- return super unless const_name == :NEVER_UNPERMITTED_PARAMS
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
- `ActionController::Parameters::NEVER_UNPERMITTED_PARAMS` has been deprecated.
- Use `ActionController::Parameters.always_permitted_parameters` instead.
- MSG
-
- always_permitted_parameters
- end
-
# Returns a new instance of <tt>ActionController::Parameters</tt>.
# Also, sets the +permitted+ attribute to the default value of
# <tt>ActionController::Parameters.permit_all_parameters</tt>.
@@ -584,6 +574,10 @@ module ActionController
dup
end
+ def inspect
+ "<#{self.class} #{@parameters}>"
+ end
+
def method_missing(method_sym, *args, &block)
if @parameters.respond_to?(method_sym)
message = <<-DEPRECATE.squish
@@ -608,7 +602,7 @@ module ActionController
end
def fields_for_style?
- @parameters.all? { |k, v| k =~ /\A-?\d+\z/ && v.is_a?(Hash) }
+ @parameters.all? { |k, v| k =~ /\A-?\d+\z/ && (v.is_a?(Hash) || v.is_a?(Parameters)) }
end
private
diff --git a/actionpack/lib/action_dispatch/http/mime_types.rb b/actionpack/lib/action_dispatch/http/mime_types.rb
index 8356d1a238..66cea88256 100644
--- a/actionpack/lib/action_dispatch/http/mime_types.rb
+++ b/actionpack/lib/action_dispatch/http/mime_types.rb
@@ -28,7 +28,8 @@ Mime::Type.register "application/x-www-form-urlencoded", :url_encoded_form
# http://www.ietf.org/rfc/rfc4627.txt
# http://www.json.org/JSONRequest.html
-Mime::Type.register "application/json", :json, %w( text/x-json application/jsonrequest application/vnd.api+json )
+Mime::Type.register "application/json", :json, %w( text/x-json application/jsonrequest )
Mime::Type.register "application/pdf", :pdf, [], %w(pdf)
Mime::Type.register "application/zip", :zip, [], %w(zip)
+Mime::Type.register "application/gzip", :gzip, %w(application/x-gzip), %w(gz)
diff --git a/actionpack/lib/action_dispatch/journey/backwards.rb b/actionpack/lib/action_dispatch/journey/backwards.rb
deleted file mode 100644
index 3bd20fdf81..0000000000
--- a/actionpack/lib/action_dispatch/journey/backwards.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-module Rack # :nodoc:
- Mount = ActionDispatch::Journey::Router
- Mount::RouteSet = ActionDispatch::Journey::Router
- Mount::RegexpWithNamedGroups = ActionDispatch::Journey::Path::Pattern
-end
diff --git a/actionpack/lib/action_dispatch/journey/router.rb b/actionpack/lib/action_dispatch/journey/router.rb
index f649588520..06cdce1724 100644
--- a/actionpack/lib/action_dispatch/journey/router.rb
+++ b/actionpack/lib/action_dispatch/journey/router.rb
@@ -16,9 +16,6 @@ module ActionDispatch
class RoutingError < ::StandardError # :nodoc:
end
- # :nodoc:
- VERSION = '2.0.0'
-
attr_accessor :routes
def initialize(routes)
diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb
index 3477aa8b29..f2f3150b56 100644
--- a/actionpack/lib/action_dispatch/middleware/cookies.rb
+++ b/actionpack/lib/action_dispatch/middleware/cookies.rb
@@ -2,6 +2,7 @@ require 'active_support/core_ext/hash/keys'
require 'active_support/key_generator'
require 'active_support/message_verifier'
require 'active_support/json'
+require 'rack/utils'
module ActionDispatch
class Request
@@ -337,7 +338,7 @@ module ActionDispatch
end
def to_header
- @cookies.map { |k,v| "#{k}=#{v}" }.join ';'
+ @cookies.map { |k,v| "#{escape(k)}=#{escape(v)}" }.join '; '
end
def handle_options(options) #:nodoc:
@@ -419,6 +420,10 @@ module ActionDispatch
private
+ def escape(string)
+ ::Rack::Utils.escape(string)
+ end
+
def make_set_cookie_header(header)
header = @set_cookies.inject(header) { |m, (k, v)|
if write_cookie?(v)
diff --git a/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb b/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb
index b55c937e0c..51a471fb23 100644
--- a/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb
+++ b/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb
@@ -156,15 +156,20 @@ module ActionDispatch
trace = wrapper.framework_trace if trace.empty?
ActiveSupport::Deprecation.silence do
- message = "\n#{exception.class} (#{exception.message}):\n"
- message << exception.annoted_source_code.to_s if exception.respond_to?(:annoted_source_code)
- message << " " << trace.join("\n ")
- logger.fatal("#{message}\n\n")
+ logger.fatal " "
+ logger.fatal "#{exception.class} (#{exception.message}):"
+ log_array logger, exception.annoted_source_code if exception.respond_to?(:annoted_source_code)
+ logger.fatal " "
+ log_array logger, trace
end
end
+ def log_array(logger, array)
+ array.map { |line| logger.fatal line }
+ end
+
def logger(request)
- request.logger || stderr_logger
+ request.logger || ActionView::Base.logger || stderr_logger
end
def stderr_logger
diff --git a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb
index 429a98f236..dec9c60ef2 100644
--- a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb
+++ b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb
@@ -23,7 +23,7 @@ module ActionDispatch
# goes a step further than signed cookies in that encrypted cookies cannot
# be altered or read by users. This is the default starting in Rails 4.
#
- # If you have both secret_token and secret_key base set, your cookies will
+ # If you have both secret_token and secret_key_base set, your cookies will
# be encrypted, and signed cookies generated by Rails 3 will be
# transparently read and encrypted to provide a smooth upgrade path.
#
diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb
index 846b5fa1fc..310e98f584 100644
--- a/actionpack/lib/action_dispatch/routing/route_set.rb
+++ b/actionpack/lib/action_dispatch/routing/route_set.rb
@@ -289,7 +289,7 @@ module ActionDispatch
if last.permitted?
args.pop.to_h
else
- raise ArgumentError, "Generating an URL from non sanitized request parameters is insecure!"
+ raise ArgumentError, "Generating a URL from non sanitized request parameters is insecure!"
end
end
helper.call self, args, options
diff --git a/actionpack/lib/action_dispatch/routing/url_for.rb b/actionpack/lib/action_dispatch/routing/url_for.rb
index f91679593e..28be189f93 100644
--- a/actionpack/lib/action_dispatch/routing/url_for.rb
+++ b/actionpack/lib/action_dispatch/routing/url_for.rb
@@ -173,7 +173,7 @@ module ActionDispatch
route_name)
when ActionController::Parameters
unless options.permitted?
- raise ArgumentError.new("Generating an URL from non sanitized request parameters is insecure!")
+ raise ArgumentError.new("Generating a URL from non sanitized request parameters is insecure!")
end
route_name = options.delete :use_route
_routes.url_for(options.to_h.symbolize_keys.
diff --git a/actionpack/test/controller/parameters/accessors_test.rb b/actionpack/test/controller/parameters/accessors_test.rb
index bd43ff7697..08b3d81bf0 100644
--- a/actionpack/test/controller/parameters/accessors_test.rb
+++ b/actionpack/test/controller/parameters/accessors_test.rb
@@ -134,4 +134,13 @@ class ParametersAccessorsTest < ActiveSupport::TestCase
params1 = ActionController::Parameters.new(hash1)
assert(params1 == hash1)
end
+
+ test "inspect shows both class name and parameters" do
+ assert_equal(
+ '<ActionController::Parameters {"person"=>{"age"=>"32", '\
+ '"name"=>{"first"=>"David", "last"=>"Heinemeier Hansson"}, ' \
+ '"addresses"=>[{"city"=>"Chicago", "state"=>"Illinois"}]}}>',
+ @params.inspect
+ )
+ end
end
diff --git a/actionpack/test/controller/parameters/always_permitted_parameters_test.rb b/actionpack/test/controller/parameters/always_permitted_parameters_test.rb
index efaf8a96c3..c5bfb10b53 100644
--- a/actionpack/test/controller/parameters/always_permitted_parameters_test.rb
+++ b/actionpack/test/controller/parameters/always_permitted_parameters_test.rb
@@ -12,12 +12,6 @@ class AlwaysPermittedParametersTest < ActiveSupport::TestCase
ActionController::Parameters.always_permitted_parameters = %w( controller action )
end
- test "shows deprecations warning on NEVER_UNPERMITTED_PARAMS" do
- assert_deprecated do
- ActionController::Parameters::NEVER_UNPERMITTED_PARAMS
- end
- end
-
test "returns super on missing constant other than NEVER_UNPERMITTED_PARAMS" do
ActionController::Parameters.superclass.stub :const_missing, "super" do
assert_equal "super", ActionController::Parameters::NON_EXISTING_CONSTANT
diff --git a/actionpack/test/controller/parameters/parameters_permit_test.rb b/actionpack/test/controller/parameters/parameters_permit_test.rb
index 3299f2d9d0..96048e2868 100644
--- a/actionpack/test/controller/parameters/parameters_permit_test.rb
+++ b/actionpack/test/controller/parameters/parameters_permit_test.rb
@@ -27,6 +27,27 @@ class ParametersPermitTest < ActiveSupport::TestCase
end
end
+ def walk_permitted params
+ params.each do |k,v|
+ case v
+ when ActionController::Parameters
+ walk_permitted v
+ when Array
+ v.each { |x| walk_permitted v }
+ end
+ end
+ end
+
+ test 'iteration should not impact permit' do
+ hash = {"foo"=>{"bar"=>{"0"=>{"baz"=>"hello", "zot"=>"1"}}}}
+ params = ActionController::Parameters.new(hash)
+
+ walk_permitted params
+
+ sanitized = params[:foo].permit(bar: [:baz])
+ assert_equal({"0"=>{"baz"=>"hello"}}, sanitized[:bar].to_unsafe_h)
+ end
+
test 'if nothing is permitted, the hash becomes empty' do
params = ActionController::Parameters.new(id: '1234')
permitted = params.permit
diff --git a/actionpack/test/controller/redirect_test.rb b/actionpack/test/controller/redirect_test.rb
index 0b184eace9..3ea03be74a 100644
--- a/actionpack/test/controller/redirect_test.rb
+++ b/actionpack/test/controller/redirect_test.rb
@@ -310,7 +310,7 @@ class RedirectTest < ActionController::TestCase
error = assert_raise(ArgumentError) do
get :redirect_to_params
end
- assert_equal "Generating an URL from non sanitized request parameters is insecure!", error.message
+ assert_equal "Generating a URL from non sanitized request parameters is insecure!", error.message
end
def test_redirect_to_with_block
diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb
index c814d4ea54..60c6518c62 100644
--- a/actionpack/test/controller/render_test.rb
+++ b/actionpack/test/controller/render_test.rb
@@ -509,7 +509,7 @@ class EtagRenderTest < ActionController::TestCase
begin
File.write path, 'foo'
- ActionView::Digestor.cache.clear
+ ActionView::LookupContext::DetailsKey.clear
request.if_none_match = etag
get :with_template
diff --git a/actionpack/test/controller/test_case_test.rb b/actionpack/test/controller/test_case_test.rb
index b9caddcdb7..0c1393548e 100644
--- a/actionpack/test/controller/test_case_test.rb
+++ b/actionpack/test/controller/test_case_test.rb
@@ -137,6 +137,10 @@ XML
head :created, location: 'created resource'
end
+ def render_cookie
+ render plain: cookies["foo"]
+ end
+
def delete_cookie
cookies.delete("foo")
render plain: 'ok'
@@ -829,6 +833,12 @@ XML
assert_equal 'bar', cookies['foo']
end
+ def test_cookies_should_be_escaped_properly
+ cookies['foo'] = '+'
+ get :render_cookie
+ assert_equal '+', @response.body
+ end
+
def test_should_detect_if_cookie_is_deleted
cookies['foo'] = 'bar'
get :delete_cookie
diff --git a/actionpack/test/dispatch/mime_type_test.rb b/actionpack/test/dispatch/mime_type_test.rb
index 149e37bf3d..672b272590 100644
--- a/actionpack/test/dispatch/mime_type_test.rb
+++ b/actionpack/test/dispatch/mime_type_test.rb
@@ -49,7 +49,7 @@ class MimeTypeTest < ActiveSupport::TestCase
test "parse application with trailing star" do
accept = "application/*"
- expect = [Mime[:html], Mime[:js], Mime[:xml], Mime[:rss], Mime[:atom], Mime[:yaml], Mime[:url_encoded_form], Mime[:json], Mime[:pdf], Mime[:zip]]
+ expect = [Mime[:html], Mime[:js], Mime[:xml], Mime[:rss], Mime[:atom], Mime[:yaml], Mime[:url_encoded_form], Mime[:json], Mime[:pdf], Mime[:zip], Mime[:gzip]]
parsed = Mime::Type.parse(accept)
assert_equal expect, parsed
end
diff --git a/actionpack/test/dispatch/request/json_params_parsing_test.rb b/actionpack/test/dispatch/request/json_params_parsing_test.rb
index a3992ad008..64801bff39 100644
--- a/actionpack/test/dispatch/request/json_params_parsing_test.rb
+++ b/actionpack/test/dispatch/request/json_params_parsing_test.rb
@@ -37,9 +37,9 @@ class JsonParamsParsingTest < ActionDispatch::IntegrationTest
)
end
- test "parses json params for application/vnd.api+json" do
+ test "does not parse unregistered media types such as application/vnd.api+json" do
assert_parses(
- {"person" => {"name" => "David"}},
+ {},
"{\"person\": {\"name\": \"David\"}}", { 'CONTENT_TYPE' => 'application/vnd.api+json' }
)
end
@@ -143,13 +143,6 @@ class RootLessJSONParamsParsingTest < ActionDispatch::IntegrationTest
)
end
- test "parses json params for application/vnd.api+json" do
- assert_parses(
- {"user" => {"username" => "sikachu"}, "username" => "sikachu"},
- "{\"username\": \"sikachu\"}", { 'CONTENT_TYPE' => 'application/vnd.api+json' }
- )
- end
-
test "parses json with non-object JSON content" do
assert_parses(
{"user" => {"_json" => "string content" }, "_json" => "string content" },
diff --git a/actionview/lib/action_view/digestor.rb b/actionview/lib/action_view/digestor.rb
index 657026fa14..f3c5d6c8df 100644
--- a/actionview/lib/action_view/digestor.rb
+++ b/actionview/lib/action_view/digestor.rb
@@ -4,13 +4,11 @@ require 'monitor'
module ActionView
class Digestor
- cattr_reader(:cache)
- @@cache = Concurrent::Map.new
@@digest_monitor = Monitor.new
class PerRequestDigestCacheExpiry < Struct.new(:app) # :nodoc:
def call(env)
- ActionView::Digestor.cache.clear
+ ActionView::LookupContext::DetailsKey.clear
app.call(env)
end
end
@@ -25,12 +23,12 @@ module ActionView
def digest(name:, finder:, **options)
options.assert_valid_keys(:dependencies, :partial)
- cache_key = ([ name, finder.details_key.hash ].compact + Array.wrap(options[:dependencies])).join('.')
+ cache_key = ([ name ].compact + Array.wrap(options[:dependencies])).join('.')
# this is a correctly done double-checked locking idiom
# (Concurrent::Map's lookups have volatile semantics)
- @@cache[cache_key] || @@digest_monitor.synchronize do
- @@cache.fetch(cache_key) do # re-check under lock
+ finder.digest_cache[cache_key] || @@digest_monitor.synchronize do
+ finder.digest_cache.fetch(cache_key) do # re-check under lock
compute_and_store_digest(cache_key, name, finder, options)
end
end
@@ -42,16 +40,16 @@ module ActionView
# Prevent re-entry or else recursive templates will blow the stack.
# There is no need to worry about other threads seeing the +false+ value,
# as they will then have to wait for this thread to let go of the @@digest_monitor lock.
- pre_stored = @@cache.put_if_absent(cache_key, false).nil? # put_if_absent returns nil on insertion
+ pre_stored = finder.digest_cache.put_if_absent(cache_key, false).nil? # put_if_absent returns nil on insertion
PartialDigestor
else
Digestor
end
- @@cache[cache_key] = stored_digest = klass.new(name, finder, options).digest
+ finder.digest_cache[cache_key] = stored_digest = klass.new(name, finder, options).digest
ensure
# something went wrong or ActionView::Resolver.caching? is false, make sure not to corrupt the @@cache
- @@cache.delete_pair(cache_key, false) if pre_stored && !stored_digest
+ finder.digest_cache.delete_pair(cache_key, false) if pre_stored && !stored_digest
end
end
diff --git a/actionview/lib/action_view/helpers/tag_helper.rb b/actionview/lib/action_view/helpers/tag_helper.rb
index 2562504896..42e7358a1d 100644
--- a/actionview/lib/action_view/helpers/tag_helper.rb
+++ b/actionview/lib/action_view/helpers/tag_helper.rb
@@ -154,6 +154,7 @@ module ActionView
options.each_pair do |key, value|
if TAG_PREFIXES.include?(key) && value.is_a?(Hash)
value.each_pair do |k, v|
+ next if v.nil?
output << sep
output << prefix_tag_option(key, k, v, escape)
end
diff --git a/actionview/lib/action_view/lookup_context.rb b/actionview/lib/action_view/lookup_context.rb
index 126f289f55..86afedaa2d 100644
--- a/actionview/lib/action_view/lookup_context.rb
+++ b/actionview/lib/action_view/lookup_context.rb
@@ -55,9 +55,7 @@ module ActionView
class DetailsKey #:nodoc:
alias :eql? :equal?
- alias :object_hash :hash
- attr_reader :hash
@details_keys = Concurrent::Map.new
def self.get(details)
@@ -72,8 +70,16 @@ module ActionView
@details_keys.clear
end
+ def self.empty?; @details_keys.empty?; end
+
+ def self.digest_caches
+ @details_keys.values.map(&:digest_cache)
+ end
+
+ attr_reader :digest_cache
+
def initialize
- @hash = object_hash
+ @digest_cache = Concurrent::Map.new
end
end
@@ -200,6 +206,10 @@ module ActionView
self.view_paths = view_paths
end
+ def digest_cache
+ details_key.digest_cache
+ end
+
def initialize_details(target, details)
registered_details.each do |k|
target[k] = details[k] || Accessors::DEFAULT_PROCS[k].call
diff --git a/actionview/lib/action_view/template/error.rb b/actionview/lib/action_view/template/error.rb
index ccee785d3e..3f38c3d2b9 100644
--- a/actionview/lib/action_view/template/error.rb
+++ b/actionview/lib/action_view/template/error.rb
@@ -135,13 +135,13 @@ module ActionView
end
def formatted_code_for(source_code, line_counter, indent, output)
- start_value = (output == :html) ? {} : ""
+ start_value = (output == :html) ? {} : []
source_code.inject(start_value) do |result, line|
line_counter += 1
if output == :html
result.update(line_counter.to_s => "%#{indent}s %s\n" % ["", line])
else
- result << "%#{indent}s: %s\n" % [line_counter, line]
+ result << "%#{indent}s: %s" % [line_counter, line]
end
end
end
diff --git a/actionview/test/fixtures/digestor/messages/peek.html.erb b/actionview/test/fixtures/digestor/messages/peek.html.erb
new file mode 100644
index 0000000000..84885ab0bc
--- /dev/null
+++ b/actionview/test/fixtures/digestor/messages/peek.html.erb
@@ -0,0 +1,2 @@
+<%# Template Dependency: messages/message %>
+<%= render "comments/comments" %>
diff --git a/actionview/test/template/digestor_test.rb b/actionview/test/template/digestor_test.rb
index bfab97cf1e..9ff5f7126b 100644
--- a/actionview/test/template/digestor_test.rb
+++ b/actionview/test/template/digestor_test.rb
@@ -33,7 +33,6 @@ class TemplateDigestorTest < ActionView::TestCase
def teardown
Dir.chdir @cwd
FileUtils.rm_r @tmp_dir
- ActionView::Digestor.cache.clear
end
def test_top_level_change_reflected
@@ -130,6 +129,16 @@ class TemplateDigestorTest < ActionView::TestCase
end
end
+ def test_getting_of_singly_nested_dependencies
+ singly_nested_dependencies = ["messages/header", "messages/form", "messages/message", "events/event", "comments/comment"]
+ assert_equal singly_nested_dependencies, nested_dependencies('messages/edit')
+ end
+
+ def test_getting_of_doubly_nested_dependencies
+ doubly_nested = [{"comments/comments"=>["comments/comment"]}, "messages/message"]
+ assert_equal doubly_nested, nested_dependencies('messages/peek')
+ end
+
def test_nested_template_directory
assert_digest_difference("messages/show") do
change_template("messages/actions/_move")
@@ -291,12 +300,12 @@ class TemplateDigestorTest < ActionView::TestCase
def assert_digest_difference(template_name, options = {})
previous_digest = digest(template_name, options)
- ActionView::Digestor.cache.clear
+ finder.digest_cache.clear
yield
assert_not_equal previous_digest, digest(template_name, options), "digest didn't change"
- ActionView::Digestor.cache.clear
+ finder.digest_cache.clear
end
def digest(template_name, options = {})
diff --git a/actionview/test/template/render_test.rb b/actionview/test/template/render_test.rb
index 333e0cca11..bf811abdd0 100644
--- a/actionview/test/template/render_test.rb
+++ b/actionview/test/template/render_test.rb
@@ -226,13 +226,13 @@ module RenderTestCases
assert_match %r!method.*doesnt_exist!, e.message
assert_equal "", e.sub_template_message
assert_equal "1", e.line_number
- assert_equal "1: <%= doesnt_exist %>", e.annoted_source_code.strip
+ assert_equal "1: <%= doesnt_exist %>", e.annoted_source_code[0].strip
assert_equal File.expand_path("#{FIXTURE_LOAD_PATH}/test/_raise.html.erb"), e.file_name
end
def test_render_error_indentation
e = assert_raises(ActionView::Template::Error) { @view.render(:partial => "test/raise_indentation") }
- error_lines = e.annoted_source_code.split("\n")
+ error_lines = e.annoted_source_code
assert_match %r!error\shere!, e.message
assert_equal "11", e.line_number
assert_equal " 9: <p>Ninth paragraph</p>", error_lines.second
@@ -252,7 +252,7 @@ module RenderTestCases
assert_match %r!method.*doesnt_exist!, e.message
assert_equal "", e.sub_template_message
assert_equal "1", e.line_number
- assert_equal "1: <%= doesnt_exist %>", e.annoted_source_code.strip
+ assert_equal "1: <%= doesnt_exist %>", e.annoted_source_code[0].strip
assert_equal File.expand_path("#{FIXTURE_LOAD_PATH}/test/_raise.html.erb"), e.file_name
end
diff --git a/actionview/test/template/tag_helper_test.rb b/actionview/test/template/tag_helper_test.rb
index 6f7a78ccef..f3956a31f6 100644
--- a/actionview/test/template/tag_helper_test.rb
+++ b/actionview/test/template/tag_helper_test.rb
@@ -173,4 +173,10 @@ class TagHelperTest < ActionView::TestCase
tag('a', { aria => { a_float: 3.14, a_big_decimal: BigDecimal.new("-123.456"), a_number: 1, string: 'hello', symbol: :foo, array: [1, 2, 3], hash: { key: 'value'}, string_with_quotes: 'double"quote"party"' } })
}
end
+
+ def test_link_to_data_nil_equal
+ div_type1 = content_tag(:div, 'test', { 'data-tooltip' => nil })
+ div_type2 = content_tag(:div, 'test', { data: {tooltip: nil} })
+ assert_dom_equal div_type1, div_type2
+ end
end
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index f13a80195a..641e9195c6 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,5 +1,30 @@
-* Rework `ActiveRecord::Relation#last`
-
+* Fixed `where` for polymorphic associations when passed an array containing different types.
+
+ Fixes #17011.
+
+ Example:
+
+ PriceEstimate.where(estimate_of: [Treasure.find(1), Car.find(2)])
+ # => SELECT "price_estimates".* FROM "price_estimates"
+ WHERE (("price_estimates"."estimate_of_type" = 'Treasure' AND "price_estimates"."estimate_of_id" = 1)
+ OR ("price_estimates"."estimate_of_type" = 'Car' AND "price_estimates"."estimate_of_id" = 2))
+
+ *Philippe Huibonhoa*
+
+* Fix a bug where using `t.foreign_key` twice with the same `to_table` within
+ the same table definition would only create one foreign key.
+
+ *George Millo*
+
+* Fix a regression on has many association, where calling a child from parent in child's callback
+ results in same child records getting added repeatedly to target.
+
+ Fixes #13387.
+
+ *Bogdan Gusiev*, *Jon Hinson*
+
+* Rework `ActiveRecord::Relation#last`.
+
1. Never perform additional SQL on loaded relation
2. Use SQL reverse order instead of loading relation if relation doesn't have limit
3. Deprecated relation loading when SQL order can not be automatically reversed
@@ -21,7 +46,7 @@
* Allow `joins` to be unscoped.
- Closes #13775.
+ Fixes #13775.
*Takashi Kokubun*
diff --git a/activerecord/lib/active_record/attribute_assignment.rb b/activerecord/lib/active_record/attribute_assignment.rb
index a6d81c82b4..4c22be8235 100644
--- a/activerecord/lib/active_record/attribute_assignment.rb
+++ b/activerecord/lib/active_record/attribute_assignment.rb
@@ -29,14 +29,6 @@ module ActiveRecord
assign_multiparameter_attributes(multi_parameter_attributes) unless multi_parameter_attributes.empty?
end
- # Tries to assign given value to given attribute.
- # In case of an error, re-raises with the ActiveRecord constant.
- def _assign_attribute(k, v) # :nodoc:
- super
- rescue ActiveModel::UnknownAttributeError
- raise UnknownAttributeError.new(self, k)
- end
-
# Assign any deferred nested attributes after the base attributes have been set.
def assign_nested_parameter_attributes(pairs)
pairs.each { |k, v| _assign_attribute(k, v) }
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
index cb10ca9929..4f97c7c065 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
@@ -212,7 +212,7 @@ module ActiveRecord
def initialize(name, temporary, options, as = nil)
@columns_hash = {}
@indexes = {}
- @foreign_keys = {}
+ @foreign_keys = []
@primary_keys = nil
@temporary = temporary
@options = options
@@ -330,7 +330,7 @@ module ActiveRecord
end
def foreign_key(table_name, options = {}) # :nodoc:
- foreign_keys[table_name] = options
+ foreign_keys.push([table_name, options])
end
# Appends <tt>:datetime</tt> columns <tt>:created_at</tt> and
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
index d9b42d4283..5ef434734a 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
@@ -1,5 +1,4 @@
require 'active_record/type'
-require 'active_support/core_ext/benchmark'
require 'active_record/connection_adapters/determine_if_preparable_visitor'
require 'active_record/connection_adapters/schema_cache'
require 'active_record/connection_adapters/sql_type_metadata'
@@ -398,7 +397,7 @@ module ActiveRecord
if can_perform_case_insensitive_comparison_for?(column)
table[attribute].lower.eq(table.lower(Arel::Nodes::BindParam.new))
else
- case_sensitive_comparison(table, attribute, column, value)
+ table[attribute].eq(Arel::Nodes::BindParam.new)
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
index 8751b6da4b..b12bac2737 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -615,13 +615,10 @@ module ActiveRecord
end
end
- def case_insensitive_comparison(table, attribute, column, value)
- if column.case_sensitive?
- super
- else
- table[attribute].eq(Arel::Nodes::BindParam.new)
- end
+ def can_perform_case_insensitive_comparison_for?(column)
+ column.case_sensitive?
end
+ private :can_perform_case_insensitive_comparison_for?
# In MySQL 5.7.5 and up, ONLY_FULL_GROUP_BY affects handling of queries that use
# DISTINCT and ORDER BY. It requires the ORDER BY columns in the select list for
diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb
index 8b114c6bdf..16563515f6 100644
--- a/activerecord/lib/active_record/relation/finder_methods.rb
+++ b/activerecord/lib/active_record/relation/finder_methods.rb
@@ -561,19 +561,6 @@ module ActiveRecord
relation.limit(limit).to_a
end
- def find_last
- if loaded?
- @records.last
- else
- @last ||=
- if limit_value
- to_a.last
- else
- reverse_order.limit(1).to_a.first
- end
- end
- end
-
private
def find_nth_with_limit_and_offset(index, limit, offset:) # :nodoc:
diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb
index 0f88791d92..953495a8b6 100644
--- a/activerecord/lib/active_record/relation/predicate_builder.rb
+++ b/activerecord/lib/active_record/relation/predicate_builder.rb
@@ -5,6 +5,7 @@ module ActiveRecord
require 'active_record/relation/predicate_builder/base_handler'
require 'active_record/relation/predicate_builder/basic_object_handler'
require 'active_record/relation/predicate_builder/class_handler'
+ require 'active_record/relation/predicate_builder/polymorphic_array_handler'
require 'active_record/relation/predicate_builder/range_handler'
require 'active_record/relation/predicate_builder/relation_handler'
@@ -22,6 +23,7 @@ module ActiveRecord
register_handler(Relation, RelationHandler.new)
register_handler(Array, ArrayHandler.new(self))
register_handler(AssociationQueryValue, AssociationQueryHandler.new(self))
+ register_handler(PolymorphicArrayValue, PolymorphicArrayHandler.new(self))
end
def build_from_hash(attributes)
@@ -40,10 +42,7 @@ module ActiveRecord
#
# For polymorphic relationships, find the foreign key and type:
# PriceEstimate.where(estimate_of: treasure)
- if table.associated_with?(column)
- value = AssociationQueryValue.new(table.associated_table(column), value)
- end
-
+ value = AssociationQueryHandler.value_for(table, column, value) if table.associated_with?(column)
build(table.arel_attribute(column), value)
end
diff --git a/activerecord/lib/active_record/relation/predicate_builder/association_query_handler.rb b/activerecord/lib/active_record/relation/predicate_builder/association_query_handler.rb
index e81be63cd3..d7fd878265 100644
--- a/activerecord/lib/active_record/relation/predicate_builder/association_query_handler.rb
+++ b/activerecord/lib/active_record/relation/predicate_builder/association_query_handler.rb
@@ -1,6 +1,16 @@
module ActiveRecord
class PredicateBuilder
class AssociationQueryHandler # :nodoc:
+ def self.value_for(table, column, value)
+ klass = if table.associated_table(column).polymorphic_association? && ::Array === value && value.first.is_a?(Base)
+ PolymorphicArrayValue
+ else
+ AssociationQueryValue
+ end
+
+ klass.new(table.associated_table(column), value)
+ end
+
def initialize(predicate_builder)
@predicate_builder = predicate_builder
end
diff --git a/activerecord/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb b/activerecord/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb
new file mode 100644
index 0000000000..b6c6240343
--- /dev/null
+++ b/activerecord/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb
@@ -0,0 +1,57 @@
+module ActiveRecord
+ class PredicateBuilder
+ class PolymorphicArrayHandler # :nodoc:
+ def initialize(predicate_builder)
+ @predicate_builder = predicate_builder
+ end
+
+ def call(attribute, value)
+ table = value.associated_table
+ queries = value.type_to_ids_mapping.map do |type, ids|
+ { table.association_foreign_type.to_s => type, table.association_foreign_key.to_s => ids }
+ end
+
+ predicates = queries.map { |query| predicate_builder.build_from_hash(query) }
+
+ if predicates.size > 1
+ type_and_ids_predicates = predicates.map { |type_predicate, id_predicate| Arel::Nodes::Grouping.new(type_predicate.and(id_predicate)) }
+ type_and_ids_predicates.inject(&:or)
+ else
+ predicates.first
+ end
+ end
+
+ protected
+
+ attr_reader :predicate_builder
+ end
+
+ class PolymorphicArrayValue # :nodoc:
+ attr_reader :associated_table, :values
+
+ def initialize(associated_table, values)
+ @associated_table = associated_table
+ @values = values
+ end
+
+ def type_to_ids_mapping
+ default_hash = Hash.new { |hsh, key| hsh[key] = [] }
+ values.each_with_object(default_hash) { |value, hash| hash[base_class(value).name] << convert_to_id(value) }
+ end
+
+ private
+
+ def primary_key(value)
+ associated_table.association_primary_key(base_class(value))
+ end
+
+ def base_class(value)
+ value.class.base_class
+ end
+
+ def convert_to_id(value)
+ value._read_attribute(primary_key(value))
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index 91d486e902..4533f3263f 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -655,6 +655,10 @@ module ActiveRecord
# # SELECT `posts`.* FROM `posts` WHERE (('id = 1' OR 'author_id = 3'))
#
def or(other)
+ unless other.is_a? Relation
+ raise ArgumentError, "You have passed #{other.class.name} object to #or. Pass an ActiveRecord::Relation object instead."
+ end
+
spawn.or!(other)
end
diff --git a/activerecord/test/cases/batches_test.rb b/activerecord/test/cases/batches_test.rb
index 3602ee7ba2..84aac3e721 100644
--- a/activerecord/test/cases/batches_test.rb
+++ b/activerecord/test/cases/batches_test.rb
@@ -108,7 +108,7 @@ class EachTest < ActiveRecord::TestCase
end
end
- def test_find_in_batches_should_finish_the_end_option
+ def test_find_in_batches_should_end_at_the_finish_option
assert_queries(6) do
Post.find_in_batches(batch_size: 1, finish: 5) do |batch|
assert_kind_of Array, batch
@@ -316,7 +316,7 @@ class EachTest < ActiveRecord::TestCase
end
end
- def test_in_batches_should_finish_the_end_option
+ def test_in_batches_should_end_at_the_finish_option
post = Post.order('id DESC').where('id <= ?', 5).first
assert_queries(7) do
relation = Post.in_batches(of: 1, finish: 5, load: true).reverse_each.first
diff --git a/activerecord/test/cases/migration/references_foreign_key_test.rb b/activerecord/test/cases/migration/references_foreign_key_test.rb
index b01415afb2..85435f4dbc 100644
--- a/activerecord/test/cases/migration/references_foreign_key_test.rb
+++ b/activerecord/test/cases/migration/references_foreign_key_test.rb
@@ -144,6 +144,22 @@ module ActiveRecord
@connection.drop_table "testing", if_exists: true
end
end
+
+ test "multiple foreign keys can be added to the same table" do
+ @connection.create_table :testings do |t|
+ t.integer :col_1
+ t.integer :col_2
+
+ t.foreign_key :testing_parents, column: :col_1
+ t.foreign_key :testing_parents, column: :col_2
+ end
+
+ fks = @connection.foreign_keys("testings")
+
+ fk_definitions = fks.map {|fk| [fk.from_table, fk.to_table, fk.column] }
+ assert_equal([["testings", "testing_parents", "col_1"],
+ ["testings", "testing_parents", "col_2"]], fk_definitions)
+ end
end
end
end
diff --git a/activerecord/test/cases/nested_attributes_test.rb b/activerecord/test/cases/nested_attributes_test.rb
index 6fbc6196cc..38a4072114 100644
--- a/activerecord/test/cases/nested_attributes_test.rb
+++ b/activerecord/test/cases/nested_attributes_test.rb
@@ -61,6 +61,13 @@ class TestNestedAttributesInGeneral < ActiveRecord::TestCase
assert_equal "No association found for name `honesty'. Has it been defined yet?", exception.message
end
+ def test_should_raise_an_UnknownAttributeError_for_non_existing_nested_attributes
+ exception = assert_raise ActiveModel::UnknownAttributeError do
+ Pirate.new(:ship_attributes => { :sail => true })
+ end
+ assert_equal "unknown attribute 'sail' for Ship.", exception.message
+ end
+
def test_should_disable_allow_destroy_by_default
Pirate.accepts_nested_attributes_for :ship
@@ -582,6 +589,13 @@ module NestedAttributesOnACollectionAssociationTests
assert_respond_to @pirate, association_setter
end
+ def test_should_raise_an_UnknownAttributeError_for_non_existing_nested_attributes_for_has_many
+ exception = assert_raise ActiveModel::UnknownAttributeError do
+ @pirate.parrots_attributes = [{ peg_leg: true }]
+ end
+ assert_equal "unknown attribute 'peg_leg' for Parrot.", exception.message
+ end
+
def test_should_save_only_one_association_on_create
pirate = Pirate.create!({
:catchphrase => 'Arr',
diff --git a/activerecord/test/cases/relation/or_test.rb b/activerecord/test/cases/relation/or_test.rb
index 28a0862f91..ce8c5ca489 100644
--- a/activerecord/test/cases/relation/or_test.rb
+++ b/activerecord/test/cases/relation/or_test.rb
@@ -82,5 +82,11 @@ module ActiveRecord
assert_equal p.loaded?, true
assert_equal expected, p.or(Post.where('id = 2')).to_a
end
+
+ def test_or_with_non_relation_object_raises_error
+ assert_raises ArgumentError do
+ Post.where(id: [1, 2, 3]).or(title: 'Rails')
+ end
+ end
end
end
diff --git a/activerecord/test/cases/relation/where_test.rb b/activerecord/test/cases/relation/where_test.rb
index bc6378b90e..56a2b5b8c6 100644
--- a/activerecord/test/cases/relation/where_test.rb
+++ b/activerecord/test/cases/relation/where_test.rb
@@ -2,6 +2,7 @@ require "cases/helper"
require "models/author"
require "models/binary"
require "models/cake_designer"
+require "models/car"
require "models/chef"
require "models/comment"
require "models/edge"
@@ -14,7 +15,7 @@ require "models/vertex"
module ActiveRecord
class WhereTest < ActiveRecord::TestCase
- fixtures :posts, :edges, :authors, :binaries, :essays
+ fixtures :posts, :edges, :authors, :binaries, :essays, :cars, :treasures, :price_estimates
def test_where_copies_bind_params
author = authors(:david)
@@ -114,6 +115,17 @@ module ActiveRecord
assert_equal expected.to_sql, actual.to_sql
end
+ def test_polymorphic_array_where_multiple_types
+ treasure_1 = treasures(:diamond)
+ treasure_2 = treasures(:sapphire)
+ car = cars(:honda)
+
+ expected = [price_estimates(:diamond), price_estimates(:sapphire_1), price_estimates(:sapphire_2), price_estimates(:honda)].sort
+ actual = PriceEstimate.where(estimate_of: [treasure_1, treasure_2, car]).to_a.sort
+
+ assert_equal expected, actual
+ end
+
def test_polymorphic_nested_relation_where
expected = PriceEstimate.where(estimate_of_type: 'Treasure', estimate_of_id: Treasure.where(id: [1,2]))
actual = PriceEstimate.where(estimate_of: Treasure.where(id: [1,2]))
diff --git a/activerecord/test/cases/store_test.rb b/activerecord/test/cases/store_test.rb
index ab63f5825c..bce86875e1 100644
--- a/activerecord/test/cases/store_test.rb
+++ b/activerecord/test/cases/store_test.rb
@@ -177,6 +177,7 @@ class StoreTest < ActiveRecord::TestCase
assert_equal [:color], first_model.stored_attributes[:data]
assert_equal [:color, :width, :height], second_model.stored_attributes[:data]
assert_equal [:color, :area, :volume], third_model.stored_attributes[:data]
+ assert_equal [:color], first_model.stored_attributes[:data]
end
test "YAML coder initializes the store when a Nil value is given" do
diff --git a/activerecord/test/fixtures/price_estimates.yml b/activerecord/test/fixtures/price_estimates.yml
index 1149ab17a2..406d65a142 100644
--- a/activerecord/test/fixtures/price_estimates.yml
+++ b/activerecord/test/fixtures/price_estimates.yml
@@ -1,7 +1,16 @@
-saphire_1:
+sapphire_1:
price: 10
estimate_of: sapphire (Treasure)
sapphire_2:
price: 20
estimate_of: sapphire (Treasure)
+
+diamond:
+ price: 30
+ estimate_of: diamond (Treasure)
+
+honda:
+ price: 40
+ estimate_of_type: Car
+ estimate_of_id: 1
diff --git a/activerecord/test/models/car.rb b/activerecord/test/models/car.rb
index 778c22b1f6..0f37e9a289 100644
--- a/activerecord/test/models/car.rb
+++ b/activerecord/test/models/car.rb
@@ -12,6 +12,8 @@ class Car < ActiveRecord::Base
has_many :engines, :dependent => :destroy, inverse_of: :my_car
has_many :wheels, :as => :wheelable, :dependent => :destroy
+ has_many :price_estimates, :as => :estimate_of
+
scope :incl_tyres, -> { includes(:tyres) }
scope :incl_engines, -> { includes(:engines) }
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index 3d4cc8fae6..f4c324803c 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -1,8 +1,16 @@
+* Add `#on_weekday?` method to `Date`, `Time`, and `DateTime`.
+
+ `#on_weekday?` returns `true` if the receiving date/time does not fall on a Saturday
+ or Sunday.
+
+ *Vipul A M*
+
* Add `Array#second_to_last` and `Array#third_to_last` methods.
*Brian Christian*
* Fix regression in `Hash#dig` for HashWithIndifferentAccess.
+
*Jon Moss*
## Rails 5.0.0.beta2 (February 01, 2016) ##
diff --git a/activesupport/lib/active_support/core_ext/date_and_time/calculations.rb b/activesupport/lib/active_support/core_ext/date_and_time/calculations.rb
index e079af594d..2de0d19a7e 100644
--- a/activesupport/lib/active_support/core_ext/date_and_time/calculations.rb
+++ b/activesupport/lib/active_support/core_ext/date_and_time/calculations.rb
@@ -51,6 +51,11 @@ module DateAndTime
WEEKEND_DAYS.include?(wday)
end
+ # Returns true if the date/time does not fall on a Saturday or Sunday.
+ def on_weekday?
+ !WEEKEND_DAYS.include?(wday)
+ end
+
# Returns a new date/time the specified number of days ago.
def days_ago(days)
advance(:days => -days)
diff --git a/activesupport/lib/active_support/dependencies.rb b/activesupport/lib/active_support/dependencies.rb
index af18ff746f..fd9fbff96a 100644
--- a/activesupport/lib/active_support/dependencies.rb
+++ b/activesupport/lib/active_support/dependencies.rb
@@ -145,9 +145,9 @@ module ActiveSupport #:nodoc:
# Get a list of the constants that were added
new_constants = mod.local_constants - original_constants
- # self[namespace] returns an Array of the constants that are being evaluated
+ # @stack[namespace] returns an Array of the constants that are being evaluated
# for that namespace. For instance, if parent.rb requires child.rb, the first
- # element of self[Object] will be an Array of the constants that were present
+ # element of @stack[Object] will be an Array of the constants that were present
# before parent.rb was required. The second element will be an Array of the
# constants that were present before child.rb was required.
@stack[namespace].each do |namespace_constants|
@@ -262,7 +262,7 @@ module ActiveSupport #:nodoc:
end
def load_dependency(file)
- if Dependencies.load? && ActiveSupport::Dependencies.constant_watch_stack.watching?
+ if Dependencies.load? && Dependencies.constant_watch_stack.watching?
Dependencies.new_constants_in(Object) { yield }
else
yield
diff --git a/activesupport/lib/active_support/message_verifier.rb b/activesupport/lib/active_support/message_verifier.rb
index 854029bf83..4c3deffe6e 100644
--- a/activesupport/lib/active_support/message_verifier.rb
+++ b/activesupport/lib/active_support/message_verifier.rb
@@ -24,6 +24,12 @@ module ActiveSupport
# hash upon initialization:
#
# @verifier = ActiveSupport::MessageVerifier.new('s3Krit', serializer: YAML)
+ #
+ # +MessageVerifier+ creates HMAC signatures using SHA1 hash algorithm by default.
+ # If you want to use a different hash algorithm, you can change it by providing
+ # `:digest` key as an option while initializing the verifier:
+ #
+ # @verifier = ActiveSupport::MessageVerifier.new('s3Krit', digest: 'SHA256')
class MessageVerifier
class InvalidSignature < StandardError; end
diff --git a/activesupport/lib/active_support/test_case.rb b/activesupport/lib/active_support/test_case.rb
index d9a668c0ea..4dc84e4a59 100644
--- a/activesupport/lib/active_support/test_case.rb
+++ b/activesupport/lib/active_support/test_case.rb
@@ -66,10 +66,13 @@ module ActiveSupport
alias :assert_not_respond_to :refute_respond_to
alias :assert_not_same :refute_same
- # Reveals the intention that the block should not raise any exception.
+
+ # Assertion that the block should not raise an exception.
+ #
+ # Passes if evaluated code in the yielded block raises no exception.
#
# assert_nothing_raised do
- # ...
+ # perform_service(param: 'no_exception')
# end
def assert_nothing_raised(*args)
yield
diff --git a/activesupport/test/core_ext/date_and_time_behavior.rb b/activesupport/test/core_ext/date_and_time_behavior.rb
index 784547bdf8..54df87def8 100644
--- a/activesupport/test/core_ext/date_and_time_behavior.rb
+++ b/activesupport/test/core_ext/date_and_time_behavior.rb
@@ -301,6 +301,16 @@ module DateAndTimeBehavior
assert_not date_time_init(2015,1,5,15,15,10).on_weekend?
end
+ def test_on_weekday_on_sunday
+ assert_not date_time_init(2015,1,4,0,0,0).on_weekday?
+ assert_not date_time_init(2015,1,4,15,15,10).on_weekday?
+ end
+
+ def test_on_weekday_on_monday
+ assert date_time_init(2015,1,5,0,0,0).on_weekday?
+ assert date_time_init(2015,1,5,15,15,10).on_weekday?
+ end
+
def with_bw_default(bw = :monday)
old_bw = Date.beginning_of_week
Date.beginning_of_week = bw
diff --git a/activesupport/test/dependencies_test.rb b/activesupport/test/dependencies_test.rb
index 757e600646..b7a5747f1b 100644
--- a/activesupport/test/dependencies_test.rb
+++ b/activesupport/test/dependencies_test.rb
@@ -76,6 +76,7 @@ class DependenciesTest < ActiveSupport::TestCase
def test_dependency_which_raises_exception_isnt_added_to_loaded_set
with_loading do
filename = 'dependencies/raises_exception'
+ expanded = File.expand_path(filename)
$raises_exception_load_count = 0
5.times do |count|
@@ -86,8 +87,8 @@ class DependenciesTest < ActiveSupport::TestCase
assert_equal 'Loading me failed, so do not add to loaded or history.', e.message
assert_equal count + 1, $raises_exception_load_count
- assert_not ActiveSupport::Dependencies.loaded.include?(filename)
- assert_not ActiveSupport::Dependencies.history.include?(filename)
+ assert_not ActiveSupport::Dependencies.loaded.include?(expanded)
+ assert_not ActiveSupport::Dependencies.history.include?(expanded)
end
end
end
@@ -1047,12 +1048,4 @@ class DependenciesTest < ActiveSupport::TestCase
ensure
ActiveSupport::Dependencies.hook!
end
-
- def test_unhook
- ActiveSupport::Dependencies.unhook!
- assert !Module.new.respond_to?(:const_missing_without_dependencies)
- assert !Module.new.respond_to?(:load_without_new_constant_marking)
- ensure
- ActiveSupport::Dependencies.hook!
- end
end
diff --git a/guides/bug_report_templates/action_controller_master.rb b/guides/bug_report_templates/action_controller_master.rb
index 3f24aa3b4d..8322707495 100644
--- a/guides/bug_report_templates/action_controller_master.rb
+++ b/guides/bug_report_templates/action_controller_master.rb
@@ -8,11 +8,6 @@ end
gemfile(true) do
source 'https://rubygems.org'
gem 'rails', github: 'rails/rails'
- gem 'arel', github: 'rails/arel'
- gem 'rack', github: 'rack/rack'
- gem 'sprockets', github: 'rails/sprockets'
- gem 'sprockets-rails', github: 'rails/sprockets-rails'
- gem 'sass-rails', github: 'rails/sass-rails'
end
require 'action_controller/railtie'
diff --git a/guides/bug_report_templates/active_record_master.rb b/guides/bug_report_templates/active_record_master.rb
index 5b742a9093..a86edd9121 100644
--- a/guides/bug_report_templates/active_record_master.rb
+++ b/guides/bug_report_templates/active_record_master.rb
@@ -8,11 +8,6 @@ end
gemfile(true) do
source 'https://rubygems.org'
gem 'rails', github: 'rails/rails'
- gem 'arel', github: 'rails/arel'
- gem 'rack', github: 'rack/rack'
- gem 'sprockets', github: 'rails/sprockets'
- gem 'sprockets-rails', github: 'rails/sprockets-rails'
- gem 'sass-rails', github: 'rails/sass-rails'
gem 'sqlite3'
end
diff --git a/guides/bug_report_templates/generic_master.rb b/guides/bug_report_templates/generic_master.rb
index fcc90fa503..70cf931f34 100644
--- a/guides/bug_report_templates/generic_master.rb
+++ b/guides/bug_report_templates/generic_master.rb
@@ -8,11 +8,6 @@ end
gemfile(true) do
source 'https://rubygems.org'
gem 'rails', github: 'rails/rails'
- gem 'arel', github: 'rails/arel'
- gem 'rack', github: 'rack/rack'
- gem 'sprockets', github: 'rails/sprockets'
- gem 'sprockets-rails', github: 'rails/sprockets-rails'
- gem 'sass-rails', github: 'rails/sass-rails'
end
require 'active_support'
diff --git a/guides/source/5_0_release_notes.md b/guides/source/5_0_release_notes.md
index 4e8252f85b..d380ba8fce 100644
--- a/guides/source/5_0_release_notes.md
+++ b/guides/source/5_0_release_notes.md
@@ -705,7 +705,7 @@ Please refer to the [Changelog][active-support] for detailed changes.
* Changed the default test order from `:sorted` to `:random`.
([commit](https://github.com/rails/rails/commit/5f777e4b5ee2e3e8e6fd0e2a208ec2a4d25a960d))
-* Added `#on_weekend?`, `#next_weekday`, `#prev_weekday` methods to `Date`,
+* Added `#on_weekend?`, `#on_weekday?`, `#next_weekday`, `#prev_weekday` methods to `Date`,
`Time`, and `DateTime`.
([Pull Request](https://github.com/rails/rails/pull/18335))
@@ -768,6 +768,7 @@ framework it is. Kudos to all of them.
[action-pack]: https://github.com/rails/rails/blob/5-0-stable/actionpack/CHANGELOG.md
[action-view]: https://github.com/rails/rails/blob/5-0-stable/actionview/CHANGELOG.md
[action-mailer]: https://github.com/rails/rails/blob/5-0-stable/actionmailer/CHANGELOG.md
+[action-cable]: https://github.com/rails/rails/blob/5-0-stable/actioncable/CHANGELOG.md
[active-record]: https://github.com/rails/rails/blob/5-0-stable/activerecord/CHANGELOG.md
[active-model]: https://github.com/rails/rails/blob/5-0-stable/activemodel/CHANGELOG.md
[active-support]: https://github.com/rails/rails/blob/5-0-stable/activesupport/CHANGELOG.md
diff --git a/guides/source/active_record_querying.md b/guides/source/active_record_querying.md
index 1235c04c50..af15d4870c 100644
--- a/guides/source/active_record_querying.md
+++ b/guides/source/active_record_querying.md
@@ -11,7 +11,7 @@ After reading this guide, you will know:
* How to specify the order, retrieved attributes, grouping, and other properties of the found records.
* How to use eager loading to reduce the number of database queries needed for data retrieval.
* How to use dynamic finder methods.
-* How to use method chaining to use multiple ActiveRecord methods together.
+* How to use method chaining to use multiple Active Record methods together.
* How to check for the existence of particular records.
* How to perform various calculations on Active Record models.
* How to run EXPLAIN on relations.
diff --git a/guides/source/active_record_validations.md b/guides/source/active_record_validations.md
index dd7adf09a2..10bd201145 100644
--- a/guides/source/active_record_validations.md
+++ b/guides/source/active_record_validations.md
@@ -149,7 +149,7 @@ false` as an argument. This technique should be used with caution.
### `valid?` and `invalid?`
-Before saving an ActiveRecord object, Rails runs your validations.
+Before saving an Active Record object, Rails runs your validations.
If these validations produce any errors, Rails does not save the object.
You can also run these validations on your own. `valid?` triggers your validations
diff --git a/guides/source/active_support_core_extensions.md b/guides/source/active_support_core_extensions.md
index 10122629b2..e66b9a4301 100644
--- a/guides/source/active_support_core_extensions.md
+++ b/guides/source/active_support_core_extensions.md
@@ -3078,7 +3078,7 @@ INFO: The following calculation methods have edge cases in October 1582, since d
#### `Date.current`
-Active Support defines `Date.current` to be today in the current time zone. That's like `Date.today`, except that it honors the user time zone, if defined. It also defines `Date.yesterday` and `Date.tomorrow`, and the instance predicates `past?`, `today?`, and `future?`, all of them relative to `Date.current`.
+Active Support defines `Date.current` to be today in the current time zone. That's like `Date.today`, except that it honors the user time zone, if defined. It also defines `Date.yesterday` and `Date.tomorrow`, and the instance predicates `past?`, `today?`, `future?`, `on_weekday?` and `on_weekend?`, all of them relative to `Date.current`.
When making Date comparisons using methods which honor the user time zone, make sure to use `Date.current` and not `Date.today`. There are cases where the user time zone might be in the future compared to the system time zone, which `Date.today` uses by default. This means `Date.today` may equal `Date.yesterday`.
@@ -3467,6 +3467,8 @@ years_ago
years_since
prev_year (last_year)
next_year
+on_weekday?
+on_weekend?
```
The following methods are reimplemented so you do **not** need to load `active_support/core_ext/date/calculations.rb` for these ones:
@@ -3653,6 +3655,8 @@ years_ago
years_since
prev_year (last_year)
next_year
+on_weekday?
+on_weekend?
```
They are analogous. Please refer to their documentation above and take into account the following differences:
diff --git a/guides/source/api_app.md b/guides/source/api_app.md
index 563214896a..0598b9c7fa 100644
--- a/guides/source/api_app.md
+++ b/guides/source/api_app.md
@@ -13,8 +13,8 @@ In this guide you will learn:
--------------------------------------------------------------------------------
-What is an API app?
--------------------
+What is an API Application?
+---------------------------
Traditionally, when people said that they used Rails as an "API", they meant
providing a programmatically accessible API alongside their web application.
@@ -28,15 +28,14 @@ applications.
For example, Twitter uses its [public API](https://dev.twitter.com) in its web
application, which is built as a static site that consumes JSON resources.
-Instead of using Rails to generate dynamic HTML that will communicate with the
-server through forms and links, many developers are treating their web application
-as just another client, delivered as static HTML, CSS and JavaScript consuming
-a simple JSON API.
+Instead of using Rails to generate HTML that communicates with the server
+through forms and links, many developers are treating their web application as
+just an API client delivered as HTML with JavaScript that consumes a JSON API.
This guide covers building a Rails application that serves JSON resources to an
-API client **or** a client-side framework.
+API client, including client-side frameworks.
-Why use Rails for JSON APIs?
+Why Use Rails for JSON APIs?
----------------------------
The first question a lot of people have when thinking about building a JSON API
@@ -75,7 +74,7 @@ Handled at the middleware layer:
URL-encoded String? No problem. Rails will decode the JSON for you and make
it available in `params`. Want to use nested URL-encoded parameters? That
works too.
-- Conditional GETs: Rails handles conditional `GET`, (`ETag` and `Last-Modified`),
+- Conditional GETs: Rails handles conditional `GET` (`ETag` and `Last-Modified`)
processing request headers and returning the correct response headers and status
code. All you need to do is use the
[`stale?`](http://api.rubyonrails.org/classes/ActionController/ConditionalGet.html#method-i-stale-3F)
@@ -104,21 +103,21 @@ Handled at the Action Pack layer:
add the response headers, but why?
- Caching: Rails provides page, action and fragment caching. Fragment caching
is especially helpful when building up a nested JSON object.
-- Basic, Digest and Token Authentication: Rails comes with out-of-the-box support
+- Basic, Digest, and Token Authentication: Rails comes with out-of-the-box support
for three kinds of HTTP authentication.
-- Instrumentation: Rails has an instrumentation API that will trigger registered
+- Instrumentation: Rails has an instrumentation API that triggers registered
handlers for a variety of events, such as action processing, sending a file or
data, redirection, and database queries. The payload of each event comes with
relevant information (for the action processing event, the payload includes
the controller, action, parameters, request format, request method and the
request's full path).
-- Generators: This may be passé for advanced Rails users, but it can be nice to
- generate a resource and get your model, controller, test stubs, and routes
- created for you in a single command.
+- Generators: It is often handy to generate a resource and get your model,
+ controller, test stubs, and routes created for you in a single command for
+ further tweaking. Same for migrations and others.
- Plugins: Many third-party libraries come with support for Rails that reduce
or eliminate the cost of setting up and gluing together the library and the
web framework. This includes things like overriding default generators, adding
- rake tasks, and honoring Rails choices (like the logger and cache back-end).
+ Rake tasks, and honoring Rails choices (like the logger and cache back-end).
Of course, the Rails boot process also glues together all registered components.
For example, the Rails boot process is what uses your `config/database.yml` file
@@ -167,14 +166,6 @@ class definition:
config.api_only = true
```
-Optionally, in `config/environments/development.rb` add the following line
-to render error responses using the API format (JSON by default) when it
-is a local request:
-
-```ruby
-config.debug_exception_response_format = :api
-```
-
Finally, inside `app/controllers/application_controller.rb`, instead of:
```ruby
diff --git a/guides/source/engines.md b/guides/source/engines.md
index db50ad278f..c5fc2f73b4 100644
--- a/guides/source/engines.md
+++ b/guides/source/engines.md
@@ -402,8 +402,8 @@ module Blorgh
end
```
-NOTE: The `ApplicationController` class being inherited from here is the
-`Blorgh::ApplicationController`, not an application's `ApplicationController`.
+NOTE: The `ArticlesController` class inherits from
+`Blorgh::ApplicationController`, not the application's `ApplicationController`.
The helper inside `app/helpers/blorgh/articles_helper.rb` is also namespaced:
diff --git a/guides/source/i18n.md b/guides/source/i18n.md
index 5bbd4048b9..56b0c6c812 100644
--- a/guides/source/i18n.md
+++ b/guides/source/i18n.md
@@ -25,7 +25,7 @@ After reading this guide, you will know:
* How I18n works in Ruby on Rails
* How to correctly use I18n into a RESTful application in various ways
-* How to use I18n to translate ActiveRecord errors or ActionMailer E-mail subjects
+* How to use I18n to translate Active Record errors or Action Mailer E-mail subjects
* Some other tools to go further with the translation process of your application
--------------------------------------------------------------------------------
diff --git a/guides/source/testing.md b/guides/source/testing.md
index 13f4446751..7a9a30f7ac 100644
--- a/guides/source/testing.md
+++ b/guides/source/testing.md
@@ -1191,9 +1191,9 @@ testing) but instead it will be appended to an array
(`ActionMailer::Base.deliveries`).
NOTE: The `ActionMailer::Base.deliveries` array is only reset automatically in
-`ActionMailer::TestCase` tests. If you want to have a clean slate outside Action
-Mailer tests, you can reset it manually with:
-`ActionMailer::Base.deliveries.clear`
+`ActionMailer::TestCase` and `ActionDispatch::IntegrationTest` tests.
+If you want to have a clean slate outside these test cases, you can reset it
+manually with: `ActionMailer::Base.deliveries.clear`
### Functional Testing
diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md
index 2506baac16..a3be5356a1 100644
--- a/railties/CHANGELOG.md
+++ b/railties/CHANGELOG.md
@@ -1,3 +1,11 @@
+* Change fail fast of `bin/rails test` interrupts run on error.
+
+ *Yuji Yaginuma*
+
+* The application generator supports `--skip-listen` to opt-out of features
+ that depend on the listen gem. As of this writing they are the evented file
+ system monitor and the async plugin for spring.
+
* The Gemfiles of new applications include spring-watcher-listen on Linux and
Mac OS X (unless --skip-spring).
diff --git a/railties/lib/rails/engine/configuration.rb b/railties/lib/rails/engine/configuration.rb
index 8cadbc3ddd..294d07446f 100644
--- a/railties/lib/rails/engine/configuration.rb
+++ b/railties/lib/rails/engine/configuration.rb
@@ -39,6 +39,7 @@ module Rails
paths.add "app", eager_load: true, glob: "{*,*/concerns}"
paths.add "app/assets", glob: "*"
paths.add "app/controllers", eager_load: true
+ paths.add "app/channels", eager_load: true, glob: "**/*_channel.rb"
paths.add "app/helpers", eager_load: true
paths.add "app/models", eager_load: true
paths.add "app/mailers", eager_load: true
diff --git a/railties/lib/rails/generators.rb b/railties/lib/rails/generators.rb
index e3d79521e7..330bd7ec5d 100644
--- a/railties/lib/rails/generators.rb
+++ b/railties/lib/rails/generators.rb
@@ -105,7 +105,7 @@ module Rails
# Configure generators for API only applications. It basically hides
# everything that is usually browser related, such as assets and session
- # migration generators, and completely disable views, helpers and assets
+ # migration generators, and completely disable helpers and assets
# so generators such as scaffold won't create them.
def self.api_only!
hide_namespaces "assets", "helper", "css", "js"
@@ -116,6 +116,10 @@ module Rails
helper: false,
template_engine: nil
)
+
+ if ARGV.first == 'mailer'
+ options[:rails].merge!(template_engine: :erb)
+ end
end
# Remove the color from output.
diff --git a/railties/lib/rails/generators/app_base.rb b/railties/lib/rails/generators/app_base.rb
index 8f8c2ec9e1..9adfcc6ee7 100644
--- a/railties/lib/rails/generators/app_base.rb
+++ b/railties/lib/rails/generators/app_base.rb
@@ -63,6 +63,9 @@ module Rails
class_option :skip_spring, type: :boolean, default: false,
desc: "Don't install Spring application preloader"
+ class_option :skip_listen, type: :boolean, default: false,
+ desc: "Don't generate configuration that depends on the listen gem"
+
class_option :skip_javascript, type: :boolean, aliases: '-J', default: false,
desc: 'Skip JavaScript files'
@@ -390,6 +393,10 @@ module Rails
!options[:skip_spring] && !options.dev? && Process.respond_to?(:fork) && !RUBY_PLATFORM.include?("cygwin")
end
+ def depend_on_listen?
+ !options[:skip_listen] && os_supports_listen_out_of_the_box?
+ end
+
def os_supports_listen_out_of_the_box?
RbConfig::CONFIG['host_os'] =~ /darwin|linux/
end
diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb
index 248ad20019..885f0c20f6 100644
--- a/railties/lib/rails/generators/rails/app/app_generator.rb
+++ b/railties/lib/rails/generators/rails/app/app_generator.rb
@@ -276,9 +276,9 @@ module Rails
end
end
- def delete_app_views_if_api_option
+ def delete_application_layout_file_if_api_option
if options[:api]
- remove_dir 'app/views'
+ remove_file 'app/views/layouts/application.html.erb'
end
end
diff --git a/railties/lib/rails/generators/rails/app/templates/Gemfile b/railties/lib/rails/generators/rails/app/templates/Gemfile
index c3fad31f23..f3bc9d9734 100644
--- a/railties/lib/rails/generators/rails/app/templates/Gemfile
+++ b/railties/lib/rails/generators/rails/app/templates/Gemfile
@@ -38,13 +38,13 @@ group :development do
gem 'web-console', '~> 3.0'
<%- end -%>
<%- end -%>
-<% if os_supports_listen_out_of_the_box? -%>
+<% if depend_on_listen? -%>
gem 'listen', '~> 3.0.5'
<% end -%>
<% if spring_install? -%>
# Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
gem 'spring'
-<% if os_supports_listen_out_of_the_box? -%>
+<% if depend_on_listen? -%>
gem 'spring-watcher-listen', '~> 2.0.0'
<% end -%>
<% end -%>
diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt
index 3451ade158..d4e2b1c756 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt
@@ -58,5 +58,5 @@ Rails.application.configure do
# Use an evented file watcher to asynchronously detect changes in source code,
# routes, locales, etc. This feature depends on the listen gem.
- <%= '# ' unless os_supports_listen_out_of_the_box? %>config.file_watcher = ActiveSupport::EventedFileUpdateChecker
+ <%= '# ' unless depend_on_listen? %>config.file_watcher = ActiveSupport::EventedFileUpdateChecker
end
diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt
index 8133917591..e8c8b00669 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt
@@ -35,9 +35,6 @@ Rails.application.configure do
config.action_mailer.delivery_method = :test
<%- end -%>
- # Randomize the order test cases are executed.
- config.active_support.test_order = :random
-
# Print deprecation notices to the stderr.
config.active_support.deprecation = :stderr
diff --git a/railties/lib/rails/generators/rails/plugin/plugin_generator.rb b/railties/lib/rails/generators/rails/plugin/plugin_generator.rb
index b5e836b584..56efd35a95 100644
--- a/railties/lib/rails/generators/rails/plugin/plugin_generator.rb
+++ b/railties/lib/rails/generators/rails/plugin/plugin_generator.rb
@@ -90,6 +90,7 @@ task default: :test
opts[:force] = force
opts[:skip_bundle] = true
opts[:api] = options.api?
+ opts[:skip_listen] = true
invoke Rails::Generators::AppGenerator,
[ File.expand_path(dummy_path, destination_root) ], opts
@@ -287,10 +288,6 @@ task default: :test
protected
- def app_templates_dir
- "../../app/templates"
- end
-
def create_dummy_app(path = nil)
dummy_path(path) if path
diff --git a/railties/lib/rails/railtie.rb b/railties/lib/rails/railtie.rb
index 8c24d1d56d..99dd571a00 100644
--- a/railties/lib/rails/railtie.rb
+++ b/railties/lib/rails/railtie.rb
@@ -183,7 +183,7 @@ module Rails
end
protected
- def generate_railtie_name(string)
+ def generate_railtie_name(string) #:nodoc:
ActiveSupport::Inflector.underscore(string).tr("/", "_")
end
@@ -200,21 +200,24 @@ module Rails
delegate :railtie_name, to: :class
- def initialize
+ def initialize #:nodoc:
if self.class.abstract_railtie?
raise "#{self.class.name} is abstract, you cannot instantiate it directly."
end
end
- def configure(&block)
+ def configure(&block) #:nodoc:
instance_eval(&block)
end
+ # This is used to create the <tt>config</tt> object on Railties, an instance of
+ # Railtie::Configuration, that is used by Railties and Application to store
+ # related configuration.
def config
@config ||= Railtie::Configuration.new
end
- def railtie_namespace
+ def railtie_namespace #:nodoc:
@railtie_namespace ||= self.class.parents.detect { |n| n.respond_to?(:railtie_namespace) }
end
diff --git a/railties/lib/rails/test_unit/minitest_plugin.rb b/railties/lib/rails/test_unit/minitest_plugin.rb
index 29a3d991b8..03c705ffef 100644
--- a/railties/lib/rails/test_unit/minitest_plugin.rb
+++ b/railties/lib/rails/test_unit/minitest_plugin.rb
@@ -42,7 +42,7 @@ module Minitest
end
opts.on("-f", "--fail-fast",
- "Abort test run on first failure") do
+ "Abort test run on first failure or error") do
options[:fail_fast] = true
end
diff --git a/railties/lib/rails/test_unit/reporter.rb b/railties/lib/rails/test_unit/reporter.rb
index 73b8d7d27b..e81bd9df7e 100644
--- a/railties/lib/rails/test_unit/reporter.rb
+++ b/railties/lib/rails/test_unit/reporter.rb
@@ -24,7 +24,7 @@ module Rails
io.puts
end
- if fail_fast? && result.failure && !result.error? && !result.skipped?
+ if fail_fast? && result.failure && !result.skipped?
raise Interrupt
end
end
@@ -68,19 +68,13 @@ module Rails
def format_failures(result)
result.failures.map do |failure|
- "#{failure.result_label}:\n#{result.class}##{result.name}:\n#{failure.message}\n"
+ "#{failure.result_label}:\n#{result.location}:\n#{failure.message}\n"
end
end
def format_rerun_snippet(result)
- # Try to extract path to assertion from backtrace.
- if result.location =~ /\[(.*)\]\z/
- assertion_path = $1
- else
- assertion_path = result.method(result.name).source_location.join(':')
- end
-
- "#{self.executable} #{relative_path_for(assertion_path)}"
+ location, line = result.method(result.name).source_location
+ "#{self.executable} #{relative_path_for(location)}:#{line}"
end
def app_root
diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb
index 7bcfc86d03..383f485db5 100644
--- a/railties/test/application/configuration_test.rb
+++ b/railties/test/application/configuration_test.rb
@@ -988,7 +988,7 @@ module ApplicationTests
app 'development'
post "/posts.json", '{ "title": "foo", "name": "bar" }', "CONTENT_TYPE" => "application/json"
- assert_equal '{"title"=>"foo"}', last_response.body
+ assert_equal '<ActionController::Parameters {"title"=>"foo"}>', last_response.body
end
test "config.action_controller.permit_all_parameters = true" do
@@ -1444,7 +1444,7 @@ module ApplicationTests
assert Rails.configuration.api_only
end
- test "debug_exception_response_format is :api by default if only_api is enabled" do
+ test "debug_exception_response_format is :api by default if api_only is enabled" do
add_to_config <<-RUBY
config.api_only = true
RUBY
diff --git a/railties/test/application/generators_test.rb b/railties/test/application/generators_test.rb
index 84cc6e120b..644af0e737 100644
--- a/railties/test/application/generators_test.rb
+++ b/railties/test/application/generators_test.rb
@@ -160,5 +160,15 @@ module ApplicationTests
assert Rails::Generators.options[:rails][:helper]
assert_equal :my_template, Rails::Generators.options[:rails][:template_engine]
end
+
+ test "api only generator generate mailer views" do
+ add_to_config <<-RUBY
+ config.api_only = true
+ RUBY
+
+ FileUtils.cd(rails_root){ `bin/rails generate mailer notifier foo` }
+ assert File.exist?(File.join(rails_root, "app/views/notifier_mailer/foo.text.erb"))
+ assert File.exist?(File.join(rails_root, "app/views/notifier_mailer/foo.html.erb"))
+ end
end
end
diff --git a/railties/test/application/integration_test_case_test.rb b/railties/test/application/integration_test_case_test.rb
new file mode 100644
index 0000000000..40a79fc636
--- /dev/null
+++ b/railties/test/application/integration_test_case_test.rb
@@ -0,0 +1,46 @@
+require 'isolation/abstract_unit'
+
+module ApplicationTests
+ class IntegrationTestCaseTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ setup do
+ build_app
+ boot_rails
+ end
+
+ teardown do
+ teardown_app
+ end
+
+ test "resets Action Mailer test deliveries" do
+ script('generate mailer BaseMailer welcome')
+
+ app_file 'test/integration/mailer_integration_test.rb', <<-RUBY
+ require 'test_helper'
+
+ class MailerIntegrationTest < ActionDispatch::IntegrationTest
+ setup do
+ @old_delivery_method = ActionMailer::Base.delivery_method
+ ActionMailer::Base.delivery_method = :test
+ end
+
+ teardown do
+ ActionMailer::Base.delivery_method = @old_delivery_method
+ end
+
+ 2.times do |i|
+ define_method "test_resets_deliveries_\#{i}" do
+ BaseMailer.welcome.deliver_now
+ assert_equal 1, ActionMailer::Base.deliveries.count
+ end
+ end
+ end
+ RUBY
+
+ output = Dir.chdir(app_path) { `bin/rails test 2>&1` }
+ assert_equal 0, $?.to_i, output
+ assert_match /0 failures, 0 errors/, output
+ end
+ end
+end
diff --git a/railties/test/application/per_request_digest_cache_test.rb b/railties/test/application/per_request_digest_cache_test.rb
index 3198e12662..210646c7c0 100644
--- a/railties/test/application/per_request_digest_cache_test.rb
+++ b/railties/test/application/per_request_digest_cache_test.rb
@@ -50,12 +50,13 @@ class PerRequestDigestCacheTest < ActiveSupport::TestCase
get '/customers'
assert_equal 200, last_response.status
- assert_equal [ '8ba099b7749542fe765ff34a6824d548' ], ActionView::Digestor.cache.values
+ values = ActionView::LookupContext::DetailsKey.digest_caches.first.values
+ assert_equal [ '8ba099b7749542fe765ff34a6824d548' ], values
assert_equal %w(david dingus), last_response.body.split.map(&:strip)
end
test "template digests are cleared before a request" do
- assert_called(ActionView::Digestor.cache, :clear) do
+ assert_called(ActionView::LookupContext::DetailsKey, :clear) do
get '/customers'
assert_equal 200, last_response.status
end
diff --git a/railties/test/application/test_runner_test.rb b/railties/test/application/test_runner_test.rb
index 7ecadb60ca..b0f348f3f1 100644
--- a/railties/test/application/test_runner_test.rb
+++ b/railties/test/application/test_runner_test.rb
@@ -467,7 +467,7 @@ module ApplicationTests
create_test_file :models, 'post', pass: false
output = run_test_command('test/models/post_test.rb')
- expect = %r{Running:\n\nPostTest\nF\n\nFailure:\nPostTest#test_truth:\nwups!\n\nbin/rails test test/models/post_test.rb:6\n\n\n\n}
+ expect = %r{Running:\n\nPostTest\nF\n\nFailure:\nPostTest#test_truth \[[^\]]+test/models/post_test.rb:6\]:\nwups!\n\nbin/rails test test/models/post_test.rb:4\n\n\n\n}
assert_match expect, output
end
diff --git a/railties/test/generators/api_app_generator_test.rb b/railties/test/generators/api_app_generator_test.rb
index 1ea5661006..8e1cd0891a 100644
--- a/railties/test/generators/api_app_generator_test.rb
+++ b/railties/test/generators/api_app_generator_test.rb
@@ -73,6 +73,8 @@ class ApiAppGeneratorTest < Rails::Generators::TestCase
app/controllers
app/mailers
app/models
+ app/views/layouts/mailer.html.erb
+ app/views/layouts/mailer.text.erb
config/environments
config/initializers
config/locales
@@ -94,7 +96,7 @@ class ApiAppGeneratorTest < Rails::Generators::TestCase
def skipped_files
%w(app/assets
app/helpers
- app/views
+ app/views/layouts/application.html.erb
config/initializers/assets.rb
config/initializers/cookies_serializer.rb
config/initializers/session_store.rb
diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb
index be05e779ea..921a5b36b5 100644
--- a/railties/test/generators/app_generator_test.rb
+++ b/railties/test/generators/app_generator_test.rb
@@ -479,18 +479,20 @@ class AppGeneratorTest < Rails::Generators::TestCase
end
end
- def test_inclusion_of_listen_related_gems
+ def test_inclusion_of_listen_related_configuration_by_default
run_generator
if RbConfig::CONFIG['host_os'] =~ /darwin|linux/
- assert_gem 'listen'
- assert_gem 'spring-watcher-listen'
+ assert_listen_related_configuration
else
- assert_file 'Gemfile' do |content|
- assert_no_match(/listen/, content)
- end
+ assert_no_listen_related_configuration
end
end
+ def test_non_inclusion_of_listen_related_configuration_if_skip_listen
+ run_generator [destination_root, '--skip-listen']
+ assert_no_listen_related_configuration
+ end
+
def test_evented_file_update_checker_config
run_generator
assert_file 'config/environments/development.rb' do |content|
@@ -759,4 +761,23 @@ class AppGeneratorTest < Rails::Generators::TestCase
assert_file "Gemfile", /^\s*gem\s+["']#{gem}["']$*/
end
end
+
+ def assert_listen_related_configuration
+ assert_gem 'listen'
+ assert_gem 'spring-watcher-listen'
+
+ assert_file 'config/environments/development.rb' do |content|
+ assert_match(/^\s*config.file_watcher = ActiveSupport::EventedFileUpdateChecker/, content)
+ end
+ end
+
+ def assert_no_listen_related_configuration
+ assert_file 'Gemfile' do |content|
+ assert_no_match(/listen/, content)
+ end
+
+ assert_file 'config/environments/development.rb' do |content|
+ assert_match(/^\s*# config.file_watcher = ActiveSupport::EventedFileUpdateChecker/, content)
+ end
+ end
end
diff --git a/railties/test/generators/plugin_generator_test.rb b/railties/test/generators/plugin_generator_test.rb
index 4111a30664..d7d27e6b2e 100644
--- a/railties/test/generators/plugin_generator_test.rb
+++ b/railties/test/generators/plugin_generator_test.rb
@@ -444,6 +444,14 @@ class PluginGeneratorTest < Rails::Generators::TestCase
end
end
+ def test_dummy_appplication_skip_listen_by_default
+ run_generator
+
+ assert_file 'test/dummy/config/environments/development.rb' do |contents|
+ assert_match(/^\s*# config.file_watcher = ActiveSupport::EventedFileUpdateChecker/, contents)
+ end
+ end
+
def test_ensure_that_gitignore_can_be_generated_from_a_template_for_dummy_path
FileUtils.cd(Rails.root)
run_generator([destination_root, "--dummy_path", "spec/dummy", "--skip-test"])
diff --git a/railties/test/generators/plugin_test_runner_test.rb b/railties/test/generators/plugin_test_runner_test.rb
index f492cd49ef..ef6359fece 100644
--- a/railties/test/generators/plugin_test_runner_test.rb
+++ b/railties/test/generators/plugin_test_runner_test.rb
@@ -63,7 +63,7 @@ class PluginTestRunnerTest < ActiveSupport::TestCase
create_test_file 'post', pass: false
output = run_test_command('test/post_test.rb')
- expect = %r{Running:\n\nPostTest\nF\n\nFailure:\nPostTest#test_truth:\nwups!\n\nbin/test (/private)?#{plugin_path}/test/post_test.rb:6}
+ expect = %r{Running:\n\nPostTest\nF\n\nFailure:\nPostTest#test_truth \[[^\]]+test/post_test.rb:6\]:\nwups!\n\nbin/test (/private)?#{plugin_path}/test/post_test.rb:4}
assert_match expect, output
end
diff --git a/railties/test/generators/test_runner_in_engine_test.rb b/railties/test/generators/test_runner_in_engine_test.rb
index 69906b962b..d37e261fbb 100644
--- a/railties/test/generators/test_runner_in_engine_test.rb
+++ b/railties/test/generators/test_runner_in_engine_test.rb
@@ -17,7 +17,7 @@ class TestRunnerInEngineTest < ActiveSupport::TestCase
create_test_file 'post', pass: false
output = run_test_command('test/post_test.rb')
- expect = %r{Running:\n\nPostTest\nF\n\nFailure:\nPostTest#test_truth:\nwups!\n\nbin/rails test test/post_test.rb:6}
+ expect = %r{Running:\n\nPostTest\nF\n\nFailure:\nPostTest#test_truth \[[^\]]+test/post_test.rb:6\]:\nwups!\n\nbin/rails test test/post_test.rb:4}
assert_match expect, output
end
diff --git a/railties/test/isolation/abstract_unit.rb b/railties/test/isolation/abstract_unit.rb
index e7a261fa1f..66a8cf54af 100644
--- a/railties/test/isolation/abstract_unit.rb
+++ b/railties/test/isolation/abstract_unit.rb
@@ -154,8 +154,6 @@ module TestHelpers
config.action_controller.allow_forgery_protection = false
config.log_level = :info
RUBY
-
- remove_from_env_config('development', 'config.file_watcher.*')
end
def teardown_app
@@ -328,7 +326,6 @@ class ActiveSupport::TestCase
include ActiveSupport::Testing::Stream
self.test_order = :sorted
-
end
# Create a scope and build a fixture rails app
@@ -342,7 +339,7 @@ Module.new do
environment = File.expand_path('../../../../load_paths', __FILE__)
require_environment = "-r #{environment}"
- `#{Gem.ruby} #{require_environment} #{RAILS_FRAMEWORK_ROOT}/railties/exe/rails new #{app_template_path} --skip-gemfile --no-rc`
+ `#{Gem.ruby} #{require_environment} #{RAILS_FRAMEWORK_ROOT}/railties/exe/rails new #{app_template_path} --skip-gemfile --skip-listen --no-rc`
File.open("#{app_template_path}/config/boot.rb", 'w') do |f|
f.puts "require '#{environment}'"
f.puts "require 'rails/all'"
diff --git a/railties/test/test_unit/reporter_test.rb b/railties/test/test_unit/reporter_test.rb
index 2d08d4ec30..0d64b48550 100644
--- a/railties/test/test_unit/reporter_test.rb
+++ b/railties/test/test_unit/reporter_test.rb
@@ -62,7 +62,7 @@ class TestUnitReporterTest < ActiveSupport::TestCase
@reporter.record(failed_test)
@reporter.report
- expect = %r{\AF\n\nFailure:\nTestUnitReporterTest::ExampleTest#woot:\nboo\n\nbin/rails test test/test_unit/reporter_test.rb:\d+\n\n\z}
+ expect = %r{\AF\n\nFailure:\nTestUnitReporterTest::ExampleTest#woot \[[^\]]+\]:\nboo\n\nbin/rails test test/test_unit/reporter_test.rb:\d+\n\n\z}
assert_match expect, @output.string
end
@@ -79,7 +79,7 @@ class TestUnitReporterTest < ActiveSupport::TestCase
verbose.record(skipped_test)
verbose.report
- expect = %r{\ATestUnitReporterTest::ExampleTest#woot = 10\.00 s = S\n\n\nSkipped:\nTestUnitReporterTest::ExampleTest#woot:\nskipchurches, misstemples\n\nbin/rails test test/test_unit/reporter_test.rb:\d+\n\n\z}
+ expect = %r{\ATestUnitReporterTest::ExampleTest#woot = 10\.00 s = S\n\n\nSkipped:\nTestUnitReporterTest::ExampleTest#woot \[[^\]]+\]:\nskipchurches, misstemples\n\nbin/rails test test/test_unit/reporter_test.rb:\d+\n\n\z}
assert_match expect, @output.string
end
@@ -104,11 +104,22 @@ class TestUnitReporterTest < ActiveSupport::TestCase
end
end
- test "fail fast does not interrupt run errors or skips" do
+ test "fail fast interrupts run on error" do
fail_fast = Rails::TestUnitReporter.new @output, fail_fast: true
+ interrupt_raised = false
- fail_fast.record(errored_test)
- assert_no_match 'Failed tests:', @output.string
+ # Minitest passes through Interrupt, catch it manually.
+ begin
+ fail_fast.record(errored_test)
+ rescue Interrupt
+ interrupt_raised = true
+ ensure
+ assert interrupt_raised, 'Expected Interrupt to be raised.'
+ end
+ end
+
+ test "fail fast does not interrupt run skips" do
+ fail_fast = Rails::TestUnitReporter.new @output, fail_fast: true
fail_fast.record(skipped_test)
assert_no_match 'Failed tests:', @output.string
diff --git a/tools/README.md b/tools/README.md
index 25ab798bd5..1f3d6c59d9 100644
--- a/tools/README.md
+++ b/tools/README.md
@@ -5,3 +5,4 @@ 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 \ No newline at end of file