diff options
55 files changed, 715 insertions, 380 deletions
diff --git a/.travis.yml b/.travis.yml index 5b8cfa9dbf..19fc899a8b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,9 +15,6 @@ services: addons: postgresql: "9.4" - apt: - packages: - - postgresql-9.4 bundler_args: --without test --jobs 3 --retry 3 before_install: @@ -42,7 +42,7 @@ gem "json", ">= 2.0.0" gem "rubocop", ">= 0.47", require: false group :doc do - gem "sdoc", "1.0.0.rc1" + gem "sdoc", "> 1.0.0.rc1", "< 2.0" gem "redcarpet", "~> 3.2.3", platforms: :ruby gem "w3c_validators" gem "kindlerb", "~> 1.2.0" diff --git a/Gemfile.lock b/Gemfile.lock index da7e5a8cab..445bab1ca3 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -264,7 +264,7 @@ GEM rainbow (2.2.1) rake (12.0.0) rb-fsevent (0.9.8) - rdoc (5.0.0) + rdoc (5.1.0) redcarpet (3.2.3) redis (3.3.2) redis-namespace (1.5.2) @@ -298,8 +298,8 @@ GEM sprockets (>= 2.8, < 4.0) sprockets-rails (>= 2.0, < 4.0) tilt (>= 1.1, < 3) - sdoc (1.0.0.rc1) - rdoc (= 5.0.0) + sdoc (1.0.0.rc2) + rdoc (~> 5.0) selenium-webdriver (3.0.5) childprocess (~> 0.5) rubyzip (~> 1.0) @@ -417,7 +417,7 @@ DEPENDENCIES resque-scheduler rubocop (>= 0.47) sass-rails - sdoc (= 1.0.0.rc1) + sdoc (> 1.0.0.rc1, < 2.0) sequel sidekiq sneakers diff --git a/actionmailer/lib/action_mailer/preview.rb b/actionmailer/lib/action_mailer/preview.rb index b0152aff03..87ba743f3d 100644 --- a/actionmailer/lib/action_mailer/preview.rb +++ b/actionmailer/lib/action_mailer/preview.rb @@ -52,6 +52,12 @@ module ActionMailer class Preview extend ActiveSupport::DescendantsTracker + attr_reader :params + + def initialize(params = {}) + @params = params + end + class << self # Returns all mailer preview classes. def all @@ -62,8 +68,8 @@ module ActionMailer # Returns the mail object for the given email name. The registered preview # interceptors will be informed so that they can transform the message # as they would if the mail was actually being delivered. - def call(email) - preview = new + def call(email, params = {}) + preview = new(params) message = preview.public_send(email) inform_preview_interceptors(message) message diff --git a/actionmailer/test/base_test.rb b/actionmailer/test/base_test.rb index 61960d411d..ebb97078e6 100644 --- a/actionmailer/test/base_test.rb +++ b/actionmailer/test/base_test.rb @@ -968,3 +968,19 @@ class BasePreviewInterceptorsTest < ActiveSupport::TestCase end end end + +class BasePreviewTest < ActiveSupport::TestCase + class BaseMailerPreview < ActionMailer::Preview + def welcome + BaseMailer.welcome(params) + end + end + + test "has access to params" do + params = { name: "World" } + + assert_called_with(BaseMailer, :welcome, [params]) do + BaseMailerPreview.call(:welcome, params) + end + end +end diff --git a/actionpack/lib/action_dispatch/http/mime_type.rb b/actionpack/lib/action_dispatch/http/mime_type.rb index 0cf010f59e..74a3afab44 100644 --- a/actionpack/lib/action_dispatch/http/mime_type.rb +++ b/actionpack/lib/action_dispatch/http/mime_type.rb @@ -326,11 +326,11 @@ module Mime def ref; end - def respond_to_missing?(method, include_private = false) - method.to_s.ends_with? "?" - end - private + def respond_to_missing?(method, _) + method.to_s.ends_with? "?" + end + def method_missing(method, *args) false if method.to_s.ends_with? "?" end diff --git a/actionpack/lib/action_dispatch/http/parameters.rb b/actionpack/lib/action_dispatch/http/parameters.rb index 79a2ef965b..7c585dbe68 100644 --- a/actionpack/lib/action_dispatch/http/parameters.rb +++ b/actionpack/lib/action_dispatch/http/parameters.rb @@ -85,7 +85,7 @@ module ActionDispatch def set_binary_encoding(params) action = params[:action] - if controller_class.binary_params_for?(action) + if binary_params_for?(action) ActionDispatch::Request::Utils.each_param_value(params) do |param| param.force_encoding ::Encoding::ASCII_8BIT end @@ -93,6 +93,12 @@ module ActionDispatch params end + def binary_params_for?(action) + controller_class.binary_params_for?(action) + rescue NameError + false + end + def parse_formatted_parameters(parsers) return yield if content_length.zero? || content_mime_type.nil? diff --git a/actionpack/lib/action_dispatch/routing/routes_proxy.rb b/actionpack/lib/action_dispatch/routing/routes_proxy.rb index ee847eaeed..c1423f770f 100644 --- a/actionpack/lib/action_dispatch/routing/routes_proxy.rb +++ b/actionpack/lib/action_dispatch/routing/routes_proxy.rb @@ -19,7 +19,8 @@ module ActionDispatch end end - def respond_to_missing?(method, include_private = false) + private + def respond_to_missing?(method, _) super || @helpers.respond_to?(method) end @@ -32,7 +33,7 @@ module ActionDispatch @helpers.#{method}(*args) end RUBY - send(method, *args) + public_send(method, *args) else super end diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb index 2e2db98ad6..2416c58817 100644 --- a/actionpack/lib/action_dispatch/testing/integration.rb +++ b/actionpack/lib/action_dispatch/testing/integration.rb @@ -385,14 +385,15 @@ module ActionDispatch integration_session.default_url_options = options end - def respond_to_missing?(method, include_private = false) - integration_session.respond_to?(method, include_private) || super + private + def respond_to_missing?(method, _) + integration_session.respond_to?(method) || super end # Delegate unhandled messages to the current session instance. - def method_missing(sym, *args, &block) - if integration_session.respond_to?(sym) - integration_session.__send__(sym, *args, &block).tap do + def method_missing(method, *args, &block) + if integration_session.respond_to?(method) + integration_session.public_send(method, *args, &block).tap do copy_session_variables! end else diff --git a/actionview/lib/action_view/helpers/tags/base.rb b/actionview/lib/action_view/helpers/tags/base.rb index 0895533a60..aa420c4b66 100644 --- a/actionview/lib/action_view/helpers/tags/base.rb +++ b/actionview/lib/action_view/helpers/tags/base.rb @@ -149,7 +149,7 @@ module ActionView end value = options.fetch(:selected) { value(object) } - select = content_tag("select", add_options(option_tags, options, value), html_options) + select = content_tag("select", add_options(option_tags, options, value), html_options.except!("skip_default_ids", "allow_method_names_outside_object")) if html_options["multiple"] && options.fetch(:include_hidden, true) tag("input", disabled: html_options["disabled"], name: html_options["name"], type: "hidden", value: "") + select diff --git a/actionview/lib/action_view/helpers/url_helper.rb b/actionview/lib/action_view/helpers/url_helper.rb index 70fc57c35f..a6857101b9 100644 --- a/actionview/lib/action_view/helpers/url_helper.rb +++ b/actionview/lib/action_view/helpers/url_helper.rb @@ -542,7 +542,7 @@ module ActionView return false unless request.get? || request.head? - check_parameters ||= !options.is_a?(String) && options.try(:delete, :check_parameters) + check_parameters ||= options.is_a?(Hash) && options.delete(:check_parameters) url_string = URI.parser.unescape(url_for(options)).force_encoding(Encoding::BINARY) # We ignore any extra parameters in the request_uri if the diff --git a/actionview/test/template/form_helper/form_with_test.rb b/actionview/test/template/form_helper/form_with_test.rb index a09dd468dd..bff0643fb0 100644 --- a/actionview/test/template/form_helper/form_with_test.rb +++ b/actionview/test/template/form_helper/form_with_test.rb @@ -302,6 +302,7 @@ class FormWithActsLikeFormForTest < FormWithTest concat f.text_field(:title) concat f.text_area(:body) concat f.check_box(:secret) + concat f.select(:category, %w( animal economy sports )) concat f.submit("Create post") concat f.button("Create post") concat f.button { @@ -315,6 +316,7 @@ class FormWithActsLikeFormForTest < FormWithTest "<textarea name='post[body]'>\nBack to the hill and over it again!</textarea>" \ "<input name='post[secret]' type='hidden' value='0' />" \ "<input name='post[secret]' checked='checked' type='checkbox' value='1' />" \ + "<select name='post[category]'><option value='animal'>animal</option>\n<option value='economy'>economy</option>\n<option value='sports'>sports</option></select>" \ "<input name='commit' data-disable-with='Create post' type='submit' value='Create post' />" \ "<button name='button' type='submit'>Create post</button>" \ "<button name='button' type='submit'><span>Create post</span></button>" diff --git a/actionview/test/template/url_helper_test.rb b/actionview/test/template/url_helper_test.rb index a6444a1686..6adfa95dd1 100644 --- a/actionview/test/template/url_helper_test.rb +++ b/actionview/test/template/url_helper_test.rb @@ -509,6 +509,12 @@ class UrlHelperTest < ActiveSupport::TestCase assert !current_page?("http://www.example.com/", check_parameters: true) end + def test_current_page_considering_params_when_options_does_not_respond_to_to_hash + @request = request_for_url("/?order=desc&page=1") + + assert !current_page?(:back, check_parameters: false) + end + def test_current_page_with_params_that_match @request = request_for_url("/?order=desc&page=1") @@ -676,13 +682,6 @@ class UrlHelperTest < ActiveSupport::TestCase def request_forgery_protection_token "form_token" end - - private - def sort_query_string_params(uri) - path, qs = uri.split("?") - qs = qs.split("&").sort.join("&") if qs - qs ? "#{path}?#{qs}" : path - end end class UrlHelperControllerTest < ActionController::TestCase @@ -880,6 +879,11 @@ class WorkshopsController < ActionController::Base @workshop = Workshop.new(params[:id]) render inline: "<%= url_for(@workshop) %>\n<%= link_to('Workshop', @workshop) %>" end + + def edit + @workshop = Workshop.new(params[:id]) + render inline: "<%= current_page?(@workshop) %>" + end end class SessionsController < ActionController::Base @@ -944,4 +948,11 @@ class PolymorphicControllerTest < ActionController::TestCase get :edit, params: { workshop_id: 1, id: 1, format: "json" } assert_equal %{/workshops/1/sessions/1.json\n<a href="/workshops/1/sessions/1.json">Session</a>}, @response.body end + + def test_current_page_when_options_does_not_respond_to_to_hash + @controller = WorkshopsController.new + + get :edit, params: { id: 1 } + assert_equal "false", @response.body + end end diff --git a/activemodel/test/cases/validations/format_validation_test.rb b/activemodel/test/cases/validations/format_validation_test.rb index d7e6bf3707..f5b1ad721c 100644 --- a/activemodel/test/cases/validations/format_validation_test.rb +++ b/activemodel/test/cases/validations/format_validation_test.rb @@ -107,7 +107,7 @@ class PresenceValidationTest < ActiveModel::TestCase end def test_validates_format_of_with_lambda - Topic.validates_format_of :content, with: lambda { |topic| topic.title == "digit" ? /\A\d+\Z/ : /\A\S+\Z/ } + Topic.validates_format_of :content, with: lambda { |topic| topic.title == "digit" ? /\A\d+\z/ : /\A\S+\z/ } t = Topic.new t.title = "digit" @@ -119,7 +119,7 @@ class PresenceValidationTest < ActiveModel::TestCase end def test_validates_format_of_without_lambda - Topic.validates_format_of :content, without: lambda { |topic| topic.title == "characters" ? /\A\d+\Z/ : /\A\S+\Z/ } + Topic.validates_format_of :content, without: lambda { |topic| topic.title == "characters" ? /\A\d+\z/ : /\A\S+\z/ } t = Topic.new t.title = "characters" @@ -131,7 +131,7 @@ class PresenceValidationTest < ActiveModel::TestCase end def test_validates_format_of_for_ruby_class - Person.validates_format_of :karma, with: /\A\d+\Z/ + Person.validates_format_of :karma, with: /\A\d+\z/ p = Person.new p.karma = "Pixies" diff --git a/activerecord/lib/active_record/associations/belongs_to_association.rb b/activerecord/lib/active_record/associations/belongs_to_association.rb index a2432e389a..0e61dbfb00 100644 --- a/activerecord/lib/active_record/associations/belongs_to_association.rb +++ b/activerecord/lib/active_record/associations/belongs_to_association.rb @@ -22,7 +22,7 @@ module ActiveRecord end def default(&block) - writer(instance_exec(&block)) if reader.nil? + writer(owner.instance_exec(&block)) if reader.nil? end def reset diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb index 5d6676f0df..74a4d515c2 100644 --- a/activerecord/lib/active_record/associations/collection_proxy.rb +++ b/activerecord/lib/active_record/associations/collection_proxy.rb @@ -1121,6 +1121,19 @@ module ActiveRecord delegate(*delegate_methods, to: :scope) + module DelegateExtending # :nodoc: + private + def method_missing(method, *args, &block) + extending_values = association_scope.extending_values + if extending_values.any? && (extending_values - self.class.included_modules).any? + self.class.include(*extending_values) + public_send(method, *args, &block) + else + super + end + end + end + private def find_nth_with_limit(index, limit) @@ -1141,21 +1154,16 @@ module ActiveRecord @association.find_from_target? end + def association_scope + @association.association_scope + end + def exec_queries load_target end def respond_to_missing?(method, _) - scope.respond_to?(method) || super - end - - def method_missing(method, *args, &block) - if scope.respond_to?(method) && scope.extending_values.any? - extend(*scope.extending_values) - public_send(method, *args, &block) - else - super - end + association_scope.respond_to?(method) || super end end end diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb index 177b253645..61bf5477aa 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb @@ -506,14 +506,16 @@ module ActiveRecord # +conn+: an AbstractAdapter object, which was obtained by earlier by # calling #checkout on this pool. def checkin(conn) - synchronize do - remove_connection_from_thread_cache conn + conn.lock.synchronize do + synchronize do + remove_connection_from_thread_cache conn - conn._run_checkin_callbacks do - conn.expire - end + conn._run_checkin_callbacks do + conn.expire + end - @available.add conn + @available.add conn + end end end diff --git a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb index 45e400b75b..af55cfe2f6 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb @@ -10,8 +10,6 @@ module ActiveRecord # Establishes a connection to the database that's used by all Active Record objects. def mysql2_connection(config) config = config.symbolize_keys - - config[:username] = "root" if config[:username].nil? config[:flags] ||= 0 if config[:flags].kind_of? Array diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/referential_integrity.rb b/activerecord/lib/active_record/connection_adapters/postgresql/referential_integrity.rb index 730e7c7137..44a7338bf5 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/referential_integrity.rb @@ -6,50 +6,8 @@ module ActiveRecord true end - def disable_referential_integrity(&block) # :nodoc: + def disable_referential_integrity # :nodoc: if supports_disable_referential_integrity? - if supports_alter_constraint? - disable_referential_integrity_with_alter_constraint(&block) - else - disable_referential_integrity_with_disable_trigger(&block) - end - else - yield - end - end - - private - - def disable_referential_integrity_with_alter_constraint - tables_constraints = execute(<<-SQL).values - SELECT table_name, constraint_name - FROM information_schema.table_constraints - WHERE constraint_type = 'FOREIGN KEY' - AND is_deferrable = 'NO' - SQL - - execute( - tables_constraints.collect { |table, constraint| - "ALTER TABLE #{quote_table_name(table)} ALTER CONSTRAINT #{constraint} DEFERRABLE" - }.join(";") - ) - - begin - transaction do - execute("SET CONSTRAINTS ALL DEFERRED") - - yield - end - ensure - execute( - tables_constraints.collect { |table, constraint| - "ALTER TABLE #{quote_table_name(table)} ALTER CONSTRAINT #{constraint} NOT DEFERRABLE" - }.join(";") - ) - end - end - - def disable_referential_integrity_with_disable_trigger original_exception = nil begin @@ -81,7 +39,10 @@ Rails needs superuser privileges to disable referential integrity. end rescue ActiveRecord::ActiveRecordError end + else + yield end + end end end end diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 7ae9bd9a5f..033d81916e 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -314,12 +314,6 @@ module ActiveRecord postgresql_version >= 90400 end - def supports_alter_constraint? - # PostgreSQL 9.4 introduces ALTER TABLE ... ALTER CONSTRAINT but it has a bug and fixed in 9.4.2 - # https://www.postgresql.org/docs/9.4/static/release-9-4-2.html - postgresql_version >= 90402 - end - def get_advisory_lock(lock_id) # :nodoc: unless lock_id.is_a?(Integer) && lock_id.bit_length <= 63 raise(ArgumentError, "Postgres requires advisory lock ids to be a signed 64 bit integer") @@ -561,7 +555,7 @@ module ActiveRecord end def has_default_function?(default_value, default) - !default_value && (%r{\w+\(.*\)|\(.*\)::\w+} === default) + !default_value && %r{\w+\(.*\)|\(.*\)::\w+|CURRENT_DATE|CURRENT_TIMESTAMP}.match?(default) end def load_additional_types(type_map, oids = nil) diff --git a/activerecord/lib/active_record/dynamic_matchers.rb b/activerecord/lib/active_record/dynamic_matchers.rb index d9912cfb22..3a9625092e 100644 --- a/activerecord/lib/active_record/dynamic_matchers.rb +++ b/activerecord/lib/active_record/dynamic_matchers.rb @@ -1,15 +1,14 @@ module ActiveRecord module DynamicMatchers #:nodoc: - def respond_to_missing?(name, include_private = false) - if self == Base - super - else - match = Method.match(self, name) - match && match.valid? || super - end - end - private + def respond_to_missing?(name, _) + if self == Base + super + else + match = Method.match(self, name) + match && match.valid? || super + end + end def method_missing(name, *arguments, &block) match = Method.match(self, name) diff --git a/activerecord/lib/active_record/migration/command_recorder.rb b/activerecord/lib/active_record/migration/command_recorder.rb index 03103bba98..f9cf59b283 100644 --- a/activerecord/lib/active_record/migration/command_recorder.rb +++ b/activerecord/lib/active_record/migration/command_recorder.rb @@ -92,10 +92,6 @@ module ActiveRecord send(method, args, &block) end - def respond_to_missing?(*args) # :nodoc: - super || delegate.respond_to?(*args) - end - ReversibleAndIrreversibleMethods.each do |method| class_eval <<-EOV, __FILE__, __LINE__ + 1 def #{method}(*args, &block) # def create_table(*args, &block) @@ -225,10 +221,14 @@ module ActiveRecord [:add_foreign_key, reversed_args] end + def respond_to_missing?(method, _) + super || delegate.respond_to?(method) + end + # Forwards any missing method call to the \target. def method_missing(method, *args, &block) - if @delegate.respond_to?(method) - @delegate.send(method, *args, &block) + if delegate.respond_to?(method) + delegate.public_send(method, *args, &block) else super end diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index f3e2df8786..711099e9e1 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -93,7 +93,7 @@ db_namespace = namespace :db do # desc 'Runs the "up" for a given migration VERSION.' task up: [:environment, :load_config] do - raise "VERSION is required" if ENV["VERSION"] && ENV["VERSION"].empty? + raise "VERSION is required" if !ENV["VERSION"] || ENV["VERSION"].empty? version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil ActiveRecord::Migrator.run(:up, ActiveRecord::Tasks::DatabaseTasks.migrations_paths, version) @@ -102,7 +102,7 @@ db_namespace = namespace :db do # desc 'Runs the "down" for a given migration VERSION.' task down: [:environment, :load_config] do - raise "VERSION is required - To go down one migration, use db:rollback" if ENV["VERSION"] && ENV["VERSION"].empty? + raise "VERSION is required - To go down one migration, use db:rollback" if !ENV["VERSION"] || ENV["VERSION"].empty? version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil ActiveRecord::Migrator.run(:down, ActiveRecord::Tasks::DatabaseTasks.migrations_paths, version) db_namespace["_dump"].invoke diff --git a/activerecord/lib/active_record/relation/delegation.rb b/activerecord/lib/active_record/relation/delegation.rb index 50378f9d99..257ae04ff4 100644 --- a/activerecord/lib/active_record/relation/delegation.rb +++ b/activerecord/lib/active_record/relation/delegation.rb @@ -25,6 +25,8 @@ module ActiveRecord def inherited(child_class) child_class.initialize_relation_delegate_cache + delegate = child_class.relation_delegate_class(ActiveRecord::Associations::CollectionProxy) + delegate.include ActiveRecord::Associations::CollectionProxy::DelegateExtending super end end @@ -109,12 +111,10 @@ module ActiveRecord end end - def respond_to_missing?(method, include_private = false) - super || @klass.respond_to?(method, include_private) || - arel.respond_to?(method, include_private) - end - private + def respond_to_missing?(method, _) + super || @klass.respond_to?(method) || arel.respond_to?(method) + end def method_missing(method, *args, &block) if @klass.respond_to?(method) diff --git a/activerecord/lib/active_record/tasks/database_tasks.rb b/activerecord/lib/active_record/tasks/database_tasks.rb index 46fa8a70a3..45110a79cd 100644 --- a/activerecord/lib/active_record/tasks/database_tasks.rb +++ b/activerecord/lib/active_record/tasks/database_tasks.rb @@ -164,7 +164,7 @@ module ActiveRecord def migrate raise "Empty VERSION provided" if ENV["VERSION"] && ENV["VERSION"].empty? - verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true + verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] != "false" : true version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil scope = ENV["SCOPE"] verbose_was, Migration.verbose = Migration.verbose, verbose diff --git a/activerecord/test/cases/adapters/postgresql/referential_integrity_test.rb b/activerecord/test/cases/adapters/postgresql/referential_integrity_test.rb index c5c540cebc..0ff04bfa27 100644 --- a/activerecord/test/cases/adapters/postgresql/referential_integrity_test.rb +++ b/activerecord/test/cases/adapters/postgresql/referential_integrity_test.rb @@ -1,150 +1,111 @@ require "cases/helper" require "support/connection_helper" -if ActiveRecord::Base.connection.respond_to?(:supports_alter_constraint?) && - ActiveRecord::Base.connection.supports_alter_constraint? - class PostgreSQLReferentialIntegrityWithAlterConstraintTest < ActiveRecord::PostgreSQLTestCase - self.use_transactional_tests = false +class PostgreSQLReferentialIntegrityTest < ActiveRecord::PostgreSQLTestCase + self.use_transactional_tests = false - include ConnectionHelper + include ConnectionHelper - IS_REFERENTIAL_INTEGRITY_SQL = lambda do |sql| - sql.match(/SET CONSTRAINTS ALL DEFERRED/) - end + IS_REFERENTIAL_INTEGRITY_SQL = lambda do |sql| + sql.match(/DISABLE TRIGGER ALL/) || sql.match(/ENABLE TRIGGER ALL/) + end - module ProgrammerMistake - def execute(sql) - if IS_REFERENTIAL_INTEGRITY_SQL.call(sql) - raise ArgumentError, "something is not right." - else - super - end + module MissingSuperuserPrivileges + def execute(sql) + if IS_REFERENTIAL_INTEGRITY_SQL.call(sql) + super "BROKEN;" rescue nil # put transaction in broken state + raise ActiveRecord::StatementInvalid, "PG::InsufficientPrivilege" + else + super end end + end - def setup - @connection = ActiveRecord::Base.connection - end - - def teardown - reset_connection - end - - def test_errors_bubble_up - @connection.extend ProgrammerMistake - - assert_raises ArgumentError do - @connection.disable_referential_integrity {} + module ProgrammerMistake + def execute(sql) + if IS_REFERENTIAL_INTEGRITY_SQL.call(sql) + raise ArgumentError, "something is not right." + else + super end end end -else - class PostgreSQLReferentialIntegrityWithDisableTriggerTest < ActiveRecord::PostgreSQLTestCase - self.use_transactional_tests = false - include ConnectionHelper + def setup + @connection = ActiveRecord::Base.connection + end - IS_REFERENTIAL_INTEGRITY_SQL = lambda do |sql| - sql.match(/DISABLE TRIGGER ALL/) || sql.match(/ENABLE TRIGGER ALL/) + def teardown + reset_connection + if ActiveRecord::Base.connection.is_a?(MissingSuperuserPrivileges) + raise "MissingSuperuserPrivileges patch was not removed" end + end - module MissingSuperuserPrivileges - def execute(sql) - if IS_REFERENTIAL_INTEGRITY_SQL.call(sql) - super "BROKEN;" rescue nil # put transaction in broken state - raise ActiveRecord::StatementInvalid, "PG::InsufficientPrivilege" - else - super - end - end - end + def test_should_reraise_invalid_foreign_key_exception_and_show_warning + @connection.extend MissingSuperuserPrivileges - module ProgrammerMistake - def execute(sql) - if IS_REFERENTIAL_INTEGRITY_SQL.call(sql) - raise ArgumentError, "something is not right." - else - super + warning = capture(:stderr) do + e = assert_raises(ActiveRecord::InvalidForeignKey) do + @connection.disable_referential_integrity do + raise ActiveRecord::InvalidForeignKey, "Should be re-raised" end end + assert_equal "Should be re-raised", e.message end + assert_match (/WARNING: Rails was not able to disable referential integrity/), warning + assert_match (/cause: PG::InsufficientPrivilege/), warning + end - def setup - @connection = ActiveRecord::Base.connection - end - - def teardown - reset_connection - if ActiveRecord::Base.connection.is_a?(MissingSuperuserPrivileges) - raise "MissingSuperuserPrivileges patch was not removed" - end - end - - def test_should_reraise_invalid_foreign_key_exception_and_show_warning - @connection.extend MissingSuperuserPrivileges + def test_does_not_print_warning_if_no_invalid_foreign_key_exception_was_raised + @connection.extend MissingSuperuserPrivileges - warning = capture(:stderr) do - e = assert_raises(ActiveRecord::InvalidForeignKey) do - @connection.disable_referential_integrity do - raise ActiveRecord::InvalidForeignKey, "Should be re-raised" - end + warning = capture(:stderr) do + e = assert_raises(ActiveRecord::StatementInvalid) do + @connection.disable_referential_integrity do + raise ActiveRecord::StatementInvalid, "Should be re-raised" end - assert_equal "Should be re-raised", e.message end - assert_match (/WARNING: Rails was not able to disable referential integrity/), warning - assert_match (/cause: PG::InsufficientPrivilege/), warning + assert_equal "Should be re-raised", e.message end + assert warning.blank?, "expected no warnings but got:\n#{warning}" + end - def test_does_not_print_warning_if_no_invalid_foreign_key_exception_was_raised - @connection.extend MissingSuperuserPrivileges + def test_does_not_break_transactions + @connection.extend MissingSuperuserPrivileges - warning = capture(:stderr) do - e = assert_raises(ActiveRecord::StatementInvalid) do - @connection.disable_referential_integrity do - raise ActiveRecord::StatementInvalid, "Should be re-raised" - end - end - assert_equal "Should be re-raised", e.message + @connection.transaction do + @connection.disable_referential_integrity do + assert_transaction_is_not_broken end - assert warning.blank?, "expected no warnings but got:\n#{warning}" + assert_transaction_is_not_broken end + end - def test_does_not_break_transactions - @connection.extend MissingSuperuserPrivileges + def test_does_not_break_nested_transactions + @connection.extend MissingSuperuserPrivileges - @connection.transaction do + @connection.transaction do + @connection.transaction(requires_new: true) do @connection.disable_referential_integrity do assert_transaction_is_not_broken end - assert_transaction_is_not_broken end + assert_transaction_is_not_broken end + end - def test_does_not_break_nested_transactions - @connection.extend MissingSuperuserPrivileges + def test_only_catch_active_record_errors_others_bubble_up + @connection.extend ProgrammerMistake - @connection.transaction do - @connection.transaction(requires_new: true) do - @connection.disable_referential_integrity do - assert_transaction_is_not_broken - end - end - assert_transaction_is_not_broken - end + assert_raises ArgumentError do + @connection.disable_referential_integrity {} end + end - def test_only_catch_active_record_errors_others_bubble_up - @connection.extend ProgrammerMistake + private - assert_raises ArgumentError do - @connection.disable_referential_integrity {} - end + def assert_transaction_is_not_broken + assert_equal 1, @connection.select_value("SELECT 1") end - - private - - def assert_transaction_is_not_broken - assert_equal 1, @connection.select_value("SELECT 1") - end - end end diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb index 5b08ba1358..c8b26232b6 100644 --- a/activerecord/test/cases/associations/belongs_to_associations_test.rb +++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb @@ -136,6 +136,24 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase assert_equal david, ship.developer end + def test_default_with_lambda + model = Class.new(ActiveRecord::Base) do + self.table_name = "ships" + def self.name; "Temp"; end + belongs_to :developer, default: -> { default_developer } + + def default_developer + Developer.first + end + end + + ship = model.create! + assert_equal developers(:david), ship.developer + + ship = model.create!(developer: developers(:jamis)) + assert_equal developers(:jamis), ship.developer + end + def test_default_scope_on_relations_is_not_cached counter = 0 diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index 25133fc580..6a479a344c 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -2037,12 +2037,6 @@ class HasManyAssociationsTest < ActiveRecord::TestCase assert_equal client_association.new.attributes, client_association.send(:new).attributes end - def test_respond_to_private_class_methods - client_association = companies(:first_firm).clients - assert !client_association.respond_to?(:private_method) - assert client_association.respond_to?(:private_method, true) - end - def test_creating_using_primary_key firm = Firm.all.merge!(order: "id").first client = firm.clients_using_primary_key.create!(name: "test") diff --git a/activerecord/test/cases/associations/left_outer_join_association_test.rb b/activerecord/test/cases/associations/left_outer_join_association_test.rb index 2aca3523c4..6d3757f467 100644 --- a/activerecord/test/cases/associations/left_outer_join_association_test.rb +++ b/activerecord/test/cases/associations/left_outer_join_association_test.rb @@ -7,7 +7,7 @@ require "models/categorization" require "models/person" class LeftOuterJoinAssociationTest < ActiveRecord::TestCase - fixtures :authors, :essays, :posts, :comments, :categorizations, :people, :author_addresses + fixtures :authors, :author_addresses, :essays, :posts, :comments, :categorizations, :people def test_construct_finder_sql_applies_aliases_tables_on_association_conditions result = Author.left_outer_joins(:thinking_posts, :welcome_posts).to_a diff --git a/activerecord/test/cases/batches_test.rb b/activerecord/test/cases/batches_test.rb index f7e21faf0f..1a54c02fac 100644 --- a/activerecord/test/cases/batches_test.rb +++ b/activerecord/test/cases/batches_test.rb @@ -35,12 +35,10 @@ class EachTest < ActiveRecord::TestCase end end - if Enumerator.method_defined? :size - def test_each_should_return_a_sized_enumerator - assert_equal 11, Post.find_each(batch_size: 1).size - assert_equal 5, Post.find_each(batch_size: 2, start: 7).size - assert_equal 11, Post.find_each(batch_size: 10_000).size - end + def test_each_should_return_a_sized_enumerator + assert_equal 11, Post.find_each(batch_size: 1).size + assert_equal 5, Post.find_each(batch_size: 2, start: 7).size + assert_equal 11, Post.find_each(batch_size: 10_000).size end def test_each_enumerator_should_execute_one_query_per_batch @@ -515,14 +513,12 @@ class EachTest < ActiveRecord::TestCase assert_equal 2, person.reload.author_id # incremented only once end - if Enumerator.method_defined? :size - def test_find_in_batches_should_return_a_sized_enumerator - assert_equal 11, Post.find_in_batches(batch_size: 1).size - assert_equal 6, Post.find_in_batches(batch_size: 2).size - assert_equal 4, Post.find_in_batches(batch_size: 2, start: 4).size - assert_equal 4, Post.find_in_batches(batch_size: 3).size - assert_equal 1, Post.find_in_batches(batch_size: 10_000).size - end + def test_find_in_batches_should_return_a_sized_enumerator + assert_equal 11, Post.find_in_batches(batch_size: 1).size + assert_equal 6, Post.find_in_batches(batch_size: 2).size + assert_equal 4, Post.find_in_batches(batch_size: 2, start: 4).size + assert_equal 4, Post.find_in_batches(batch_size: 3).size + assert_equal 1, Post.find_in_batches(batch_size: 10_000).size end [true, false].each do |load| diff --git a/activerecord/test/cases/collection_cache_key_test.rb b/activerecord/test/cases/collection_cache_key_test.rb index 92620761e4..f344c77691 100644 --- a/activerecord/test/cases/collection_cache_key_test.rb +++ b/activerecord/test/cases/collection_cache_key_test.rb @@ -11,16 +11,16 @@ module ActiveRecord fixtures :developers, :projects, :developers_projects, :topics, :comments, :posts test "collection_cache_key on model" do - assert_match(/\Adevelopers\/query-(\h+)-(\d+)-(\d+)\Z/, Developer.collection_cache_key) + assert_match(/\Adevelopers\/query-(\h+)-(\d+)-(\d+)\z/, Developer.collection_cache_key) end test "cache_key for relation" do developers = Developer.where(salary: 100000).order(updated_at: :desc) last_developer_timestamp = developers.first.updated_at - assert_match(/\Adevelopers\/query-(\h+)-(\d+)-(\d+)\Z/, developers.cache_key) + assert_match(/\Adevelopers\/query-(\h+)-(\d+)-(\d+)\z/, developers.cache_key) - /\Adevelopers\/query-(\h+)-(\d+)-(\d+)\Z/ =~ developers.cache_key + /\Adevelopers\/query-(\h+)-(\d+)-(\d+)\z/ =~ developers.cache_key assert_equal Digest::MD5.hexdigest(developers.to_sql), $1 assert_equal developers.count.to_s, $2 @@ -31,9 +31,9 @@ module ActiveRecord developers = Developer.where(salary: 100000).order(updated_at: :desc).limit(5) last_developer_timestamp = developers.first.updated_at - assert_match(/\Adevelopers\/query-(\h+)-(\d+)-(\d+)\Z/, developers.cache_key) + assert_match(/\Adevelopers\/query-(\h+)-(\d+)-(\d+)\z/, developers.cache_key) - /\Adevelopers\/query-(\h+)-(\d+)-(\d+)\Z/ =~ developers.cache_key + /\Adevelopers\/query-(\h+)-(\d+)-(\d+)\z/ =~ developers.cache_key assert_equal Digest::MD5.hexdigest(developers.to_sql), $1 assert_equal developers.count.to_s, $2 @@ -44,9 +44,9 @@ module ActiveRecord developers = Developer.where(salary: 100000).order(updated_at: :desc).limit(5).load last_developer_timestamp = developers.first.updated_at - assert_match(/\Adevelopers\/query-(\h+)-(\d+)-(\d+)\Z/, developers.cache_key) + assert_match(/\Adevelopers\/query-(\h+)-(\d+)-(\d+)\z/, developers.cache_key) - /\Adevelopers\/query-(\h+)-(\d+)-(\d+)\Z/ =~ developers.cache_key + /\Adevelopers\/query-(\h+)-(\d+)-(\d+)\z/ =~ developers.cache_key assert_equal Digest::MD5.hexdigest(developers.to_sql), $1 assert_equal developers.count.to_s, $2 @@ -74,7 +74,7 @@ module ActiveRecord test "cache_key for empty relation" do developers = Developer.where(name: "Non Existent Developer") - assert_match(/\Adevelopers\/query-(\h+)-0\Z/, developers.cache_key) + assert_match(/\Adevelopers\/query-(\h+)-0\z/, developers.cache_key) end test "cache_key with custom timestamp column" do @@ -90,7 +90,7 @@ module ActiveRecord test "collection proxy provides a cache_key" do developers = projects(:active_record).developers - assert_match(/\Adevelopers\/query-(\h+)-(\d+)-(\d+)\Z/, developers.cache_key) + assert_match(/\Adevelopers\/query-(\h+)-(\d+)-(\d+)\z/, developers.cache_key) end test "cache_key for loaded collection with zero size" do @@ -98,18 +98,18 @@ module ActiveRecord posts = Post.includes(:comments) empty_loaded_collection = posts.first.comments - assert_match(/\Acomments\/query-(\h+)-0\Z/, empty_loaded_collection.cache_key) + assert_match(/\Acomments\/query-(\h+)-0\z/, empty_loaded_collection.cache_key) end test "cache_key for queries with offset which return 0 rows" do developers = Developer.offset(20) - assert_match(/\Adevelopers\/query-(\h+)-0\Z/, developers.cache_key) + assert_match(/\Adevelopers\/query-(\h+)-0\z/, developers.cache_key) end test "cache_key with a relation having selected columns" do developers = Developer.select(:salary) - assert_match(/\Adevelopers\/query-(\h+)-(\d+)-(\d+)\Z/, developers.cache_key) + assert_match(/\Adevelopers\/query-(\h+)-(\d+)-(\d+)\z/, developers.cache_key) end end end diff --git a/activerecord/test/cases/defaults_test.rb b/activerecord/test/cases/defaults_test.rb index a6297673c9..996d298689 100644 --- a/activerecord/test/cases/defaults_test.rb +++ b/activerecord/test/cases/defaults_test.rb @@ -87,9 +87,14 @@ if current_adapter?(:PostgreSQLAdapter) test "schema dump includes default expression" do output = dump_table_schema("defaults") - assert_match %r/t\.date\s+"modified_date",\s+default: -> { "\('now'::text\)::date" }/, output + if ActiveRecord::Base.connection.postgresql_version >= 100000 + assert_match %r/t\.date\s+"modified_date",\s+default: -> { "CURRENT_DATE" }/, output + assert_match %r/t\.datetime\s+"modified_time",\s+default: -> { "CURRENT_TIMESTAMP" }/, output + else + assert_match %r/t\.date\s+"modified_date",\s+default: -> { "\('now'::text\)::date" }/, output + assert_match %r/t\.datetime\s+"modified_time",\s+default: -> { "now\(\)" }/, output + end assert_match %r/t\.date\s+"modified_date_function",\s+default: -> { "now\(\)" }/, output - assert_match %r/t\.datetime\s+"modified_time",\s+default: -> { "now\(\)" }/, output assert_match %r/t\.datetime\s+"modified_time_function",\s+default: -> { "now\(\)" }/, output end end diff --git a/activerecord/test/cases/migrator_test.rb b/activerecord/test/cases/migrator_test.rb index aadbc375af..2e4b454a86 100644 --- a/activerecord/test/cases/migrator_test.rb +++ b/activerecord/test/cases/migrator_test.rb @@ -299,6 +299,7 @@ class MigratorTest < ActiveRecord::TestCase def test_migrator_verbosity _, migrations = sensors(3) + ActiveRecord::Migration.verbose = true ActiveRecord::Migrator.new(:up, migrations, 1).migrate assert_not_equal 0, ActiveRecord::Migration.message_count @@ -311,7 +312,6 @@ class MigratorTest < ActiveRecord::TestCase def test_migrator_verbosity_off _, migrations = sensors(3) - ActiveRecord::Migration.message_count = 0 ActiveRecord::Migration.verbose = false ActiveRecord::Migrator.new(:up, migrations, 1).migrate assert_equal 0, ActiveRecord::Migration.message_count diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb index 5b7e2fd008..5895c51714 100644 --- a/activerecord/test/cases/persistence_test.rb +++ b/activerecord/test/cases/persistence_test.rb @@ -49,7 +49,7 @@ class PersistenceTest < ActiveRecord::TestCase assert !test_update_with_order_succeeds.call("id ASC") # test that this wasn't a fluke and using an incorrect order results in an exception else # test that we're failing because the current Arel's engine doesn't support UPDATE ORDER BY queries is using subselects instead - assert_sql(/\AUPDATE .+ \(SELECT .* ORDER BY id DESC\)\Z/i) do + assert_sql(/\AUPDATE .+ \(SELECT .* ORDER BY id DESC\)\z/i) do test_update_with_order_succeeds.call("id DESC") end end diff --git a/activerecord/test/cases/relation/merging_test.rb b/activerecord/test/cases/relation/merging_test.rb index 9e95149ede..64866eaf2d 100644 --- a/activerecord/test/cases/relation/merging_test.rb +++ b/activerecord/test/cases/relation/merging_test.rb @@ -21,7 +21,7 @@ class RelationMergingTest < ActiveRecord::TestCase def test_relation_to_sql post = Post.first sql = post.comments.to_sql - assert_match(/.?post_id.? = #{post.id}\Z/i, sql) + assert_match(/.?post_id.? = #{post.id}\z/i, sql) end def test_relation_merging_with_arel_equalities_keeps_last_equality diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index fcf68b0f2a..7a710f1004 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -594,7 +594,7 @@ class RelationTest < ActiveRecord::TestCase end end - def test_respond_to_delegates_to_relation + def test_respond_to_delegates_to_arel relation = Topic.all fake_arel = Struct.new(:responds) { def respond_to?(method, access = false) @@ -607,10 +607,6 @@ class RelationTest < ActiveRecord::TestCase relation.respond_to?(:matching_attributes) assert_equal [:matching_attributes, false], fake_arel.responds.first - - fake_arel.responds = [] - relation.respond_to?(:matching_attributes, true) - assert_equal [:matching_attributes, true], fake_arel.responds.first end def test_respond_to_dynamic_finders diff --git a/activerecord/test/cases/result_test.rb b/activerecord/test/cases/result_test.rb index 949086fda0..1a0b7c6ca7 100644 --- a/activerecord/test/cases/result_test.rb +++ b/activerecord/test/cases/result_test.rb @@ -45,10 +45,8 @@ module ActiveRecord end end - if Enumerator.method_defined? :size - test "each without block returns a sized enumerator" do - assert_equal 3, result.each.size - end + test "each without block returns a sized enumerator" do + assert_equal 3, result.each.size end test "cast_values returns rows after type casting" do diff --git a/activerecord/test/cases/tasks/database_tasks_test.rb b/activerecord/test/cases/tasks/database_tasks_test.rb index baa41a3a47..c47c97e9d9 100644 --- a/activerecord/test/cases/tasks/database_tasks_test.rb +++ b/activerecord/test/cases/tasks/database_tasks_test.rb @@ -343,8 +343,23 @@ module ActiveRecord ENV["VERBOSE"] = "false" ENV["VERSION"] = "4" - ActiveRecord::Migrator.expects(:migrate).with("custom/path", 4) + ActiveRecord::Migration.expects(:verbose=).with(false) + ActiveRecord::Migration.expects(:verbose=).with(ActiveRecord::Migration.verbose) + ActiveRecord::Tasks::DatabaseTasks.migrate + + ENV.delete("VERBOSE") + ENV.delete("VERSION") + ActiveRecord::Migrator.expects(:migrate).with("custom/path", nil) + ActiveRecord::Migration.expects(:verbose=).with(true) + ActiveRecord::Migration.expects(:verbose=).with(ActiveRecord::Migration.verbose) + ActiveRecord::Tasks::DatabaseTasks.migrate + + ENV["VERBOSE"] = "yes" + ENV["VERSION"] = "unknown" + ActiveRecord::Migrator.expects(:migrate).with("custom/path", 0) + ActiveRecord::Migration.expects(:verbose=).with(true) + ActiveRecord::Migration.expects(:verbose=).with(ActiveRecord::Migration.verbose) ActiveRecord::Tasks::DatabaseTasks.migrate ensure ENV["VERBOSE"], ENV["VERSION"] = verbose, version @@ -353,7 +368,8 @@ module ActiveRecord def test_migrate_raise_error_on_empty_version version = ENV["VERSION"] ENV["VERSION"] = "" - assert_raise(RuntimeError, "Empty VERSION provided") { ActiveRecord::Tasks::DatabaseTasks.migrate } + e = assert_raise(RuntimeError) { ActiveRecord::Tasks::DatabaseTasks.migrate } + assert_equal "Empty VERSION provided", e.message ensure ENV["VERSION"] = version end diff --git a/activerecord/test/models/comment.rb b/activerecord/test/models/comment.rb index a4b81d56e0..76b484e616 100644 --- a/activerecord/test/models/comment.rb +++ b/activerecord/test/models/comment.rb @@ -19,6 +19,11 @@ class Comment < ActiveRecord::Base has_many :children, class_name: "Comment", foreign_key: :parent_id belongs_to :parent, class_name: "Comment", counter_cache: :children_count + # Should not be called if extending modules that having the method exists on an association. + def self.greeting + raise + end + def self.what_are_you "a comment..." end diff --git a/activerecord/test/models/company.rb b/activerecord/test/models/company.rb index 20e37710e7..d269a95e8c 100644 --- a/activerecord/test/models/company.rb +++ b/activerecord/test/models/company.rb @@ -170,14 +170,6 @@ class Client < Company def overwrite_to_raise end - - class << self - private - - def private_method - "darkness" - end - end end class ExclusivelyDependentFirm < Company diff --git a/activesupport/lib/active_support/duration.rb b/activesupport/lib/active_support/duration.rb index 99080e34a1..d4424ed792 100644 --- a/activesupport/lib/active_support/duration.rb +++ b/activesupport/lib/active_support/duration.rb @@ -305,10 +305,6 @@ module ActiveSupport to_i end - def respond_to_missing?(method, include_private = false) #:nodoc: - @value.respond_to?(method, include_private) - end - # Build ISO 8601 Duration string for this duration. # The +precision+ parameter can be used to limit seconds' precision of duration. def iso8601(precision: nil) @@ -335,8 +331,12 @@ module ActiveSupport end end + def respond_to_missing?(method, _) + value.respond_to?(method) + end + def method_missing(method, *args, &block) - value.send(method, *args, &block) + value.public_send(method, *args, &block) end def raise_type_error(other) diff --git a/activesupport/test/core_ext/enumerable_test.rb b/activesupport/test/core_ext/enumerable_test.rb index 4f1ab993b8..0b345ecf0f 100644 --- a/activesupport/test/core_ext/enumerable_test.rb +++ b/activesupport/test/core_ext/enumerable_test.rb @@ -171,10 +171,8 @@ class EnumerableTests < ActiveSupport::TestCase assert_equal({ 5 => Payment.new(5), 15 => Payment.new(15), 10 => Payment.new(10) }, payments.index_by(&:price)) assert_equal Enumerator, payments.index_by.class - if Enumerator.method_defined? :size - assert_nil payments.index_by.size - assert_equal 42, (1..42).index_by.size - end + assert_nil payments.index_by.size + assert_equal 42, (1..42).index_by.size assert_equal({ 5 => Payment.new(5), 15 => Payment.new(15), 10 => Payment.new(10) }, payments.index_by.each(&:price)) end diff --git a/guides/source/5_1_release_notes.md b/guides/source/5_1_release_notes.md index ffa18f1a2b..0f6167f1ee 100644 --- a/guides/source/5_1_release_notes.md +++ b/guides/source/5_1_release_notes.md @@ -40,7 +40,7 @@ Major Features [Pull Request](https://github.com/rails/rails/pull/26836) -Rails 5.1 will allow managing JavaScript dependencies +Rails 5.1 allows managing JavaScript dependencies from NPM via Yarn. This will make it easy to use libraries like React, VueJS or any other library from NPM world. The Yarn support is integrated with the asset pipeline so that all dependencies will work seamlessly with the @@ -70,14 +70,14 @@ offerings. It is no longer required, as the UJS has been rewritten to use plain, vanilla JavaScript. This code now ships inside of Action View as `rails-ujs`. -You can still use the jQuery version if needed, but it is no longer required by default. +You can still use jQuery if needed, but it is no longer required by default. ### System tests [Pull Request](https://github.com/rails/rails/pull/26703) Rails 5.1 has baked-in support for writing Capybara tests, in the form of -System tests. You need no longer worry about configuring Capybara and +System tests. You no longer need to worry about configuring Capybara and database cleaning strategies for such tests. Rails 5.1 provides a wrapper for running tests in Chrome with additional features such as failure screenshots. @@ -86,7 +86,7 @@ screenshots. [Pull Request](https://github.com/rails/rails/pull/28038) -Rails will now allow management of application secrets in a secure way, +Rails now allows management of application secrets in a secure way, building on top of the [sekrets](https://github.com/ahoward/sekrets) gem. Run `bin/rails secrets:setup` to setup a new encrypted secrets file. This will @@ -106,33 +106,24 @@ order to share instance variables, headers and other common setup. ``` ruby class InvitationsMailer < ApplicationMailer - before_action { @inviter, @invitee = params[:inviter], params[:invitee] } before_action { @account = params[:inviter].account } def account_invitation mail subject: "#{@inviter.name} invited you to their Basecamp (#{@account.name})" end - - def project_invitation - @project = params[:project] - @summarizer = ProjectInvitationSummarizer.new(@project.bucket) - - mail subject: "#{@inviter.name.familiar} added you to a project in Basecamp (#{@account.name})" - end end -InvitationsMailer.with(inviter: person_a, invitee: person_b).account_invitation.deliver_later +InvitationsMailer.with(inviter: person_a, invitee: person_b) + .account_invitation.deliver_later ``` ### Direct & resolved routes [Pull Request](https://github.com/rails/rails/pull/23138) -Rails 5.1 has added two new methods, `resolve` and `direct`, to the routing -DSL. - -The `resolve` method allows customizing polymorphic mapping of models. +Rails 5.1 adds two new methods, `resolve` and `direct`, to the routing +DSL. The `resolve` method allows customizing polymorphic mapping of models. ``` ruby resource :basket @@ -181,50 +172,94 @@ Before Rails 5.1, there were two interfaces for handling HTML forms: Rails 5.1 combines both of these interfaces with `form_with`, and can generate form tags based on URLs, scopes or models. -``` erb -# Using just a URL: +Using just a URL: +``` erb <%= form_with url: posts_path do |form| %> <%= form.text_field :title %> <% end %> -# => +<%# Will generate %> + <form action="/posts" method="post" data-remote="true"> <input type="text" name="title"> </form> +``` -# Adding a scope prefixes the input field names: +Adding a scope prefixes the input field names: +``` erb <%= form_with scope: :post, url: posts_path do |form| %> <%= form.text_field :title %> <% end %> -# => + +<%# Will generate %> + <form action="/posts" method="post" data-remote="true"> <input type="text" name="post[title]"> </form> +``` -# Using a model infers both the URL and scope: +Using a model infers both the URL and scope: +``` erb <%= form_with model: Post.new do |form| %> <%= form.text_field :title %> <% end %> -# => + +<%# Will generate %> + <form action="/posts" method="post" data-remote="true"> <input type="text" name="post[title]"> </form> +``` -# An existing model makes an update form and fills out field values: +An existing model makes an update form and fills out field values: +``` erb <%= form_with model: Post.first do |form| %> <%= form.text_field :title %> <% end %> -# => + +<%# Will generate %> + <form action="/posts/1" method="post" data-remote="true"> <input type="hidden" name="_method" value="patch"> <input type="text" name="post[title]" value="<the title of the post>"> </form> ``` +Incompatibilities +----------------- + +The following changes may require immediate action upon upgrade. + +### Transactional tests with multiple connections + +Transactional tests now wrap all Active Record connections in database +transactions. + +When a test spawns additional threads, and those threads obtain database +connections, those connections are now handled specially: + +The threads will share a single connection, which is inside the managed +transaction. This ensures all threads see the database in the same +state, ignoring the outermost transaction. Previously, such additional +connections were unable to see the fixture rows, for example. + +When a thread enters a nested transaction, it will temporarily obtain +exclusive use of the connection, to maintain isolation. + +If your tests currently rely on obtaining a separate, +outside-of-transaction, connection in a spawned thread, you'll need to +switch to more explicit connection management. + +If your tests spawn threads and those threads interact while also using +explicit database transactions, this change may introduce a deadlock. + +The easy way to opt out of this new behavior is to disable transactional +tests on any test cases it affects. + Railties -------- @@ -233,7 +268,7 @@ Please refer to the [Changelog][railties] for detailed changes. ### Removals * Remove deprecated `config.static_cache_control`. - ([commit](https://github.com/rails/rails/commit/c861decd44198f8d7d774ee6a74194d1ac1a5a13) + ([commit](https://github.com/rails/rails/commit/c861decd44198f8d7d774ee6a74194d1ac1a5a13)) * Remove deprecated `config.serve_static_files`. ([commit](https://github.com/rails/rails/commit/0129ca2eeb6d5b2ea8c6e6be38eeb770fe45f1fa)) @@ -267,7 +302,7 @@ Please refer to the [Changelog][railties] for detailed changes. * Add Yarn support in new apps with a yarn binstub and package.json. ([Pull Request](https://github.com/rails/rails/pull/26836)) -* Add Webpack support in new apps via the --webpack option, which will delegate +* Add Webpack support in new apps via the `--webpack` option, which will delegate to the rails/webpacker gem. ([Pull Request](https://github.com/rails/rails/pull/27288)) @@ -278,6 +313,9 @@ Please refer to the [Changelog][railties] for detailed changes. * Add encrypted secrets in `config/secrets.yml.enc`. ([Pull Request](https://github.com/rails/rails/pull/28038)) +* Display railtie class name in `rails initializers`. + ([Pull Request](https://github.com/rails/rails/pull/25257)) + Action Cable ----------- @@ -293,7 +331,7 @@ Please refer to the [Changelog][action-cable] for detailed changes. * Permit same-origin connections by default. ([commit](https://github.com/rails/rails/commit/dae404473409fcab0e07976aec626df670e52282)) -* Add `ActiveSupport::Notifications` hook for broadcasing data. +* Add `ActiveSupport::Notifications` hook for broadcasting data. ([Pull Request](https://github.com/rails/rails/pull/24988)) Action Pack @@ -303,10 +341,37 @@ Please refer to the [Changelog][action-pack] for detailed changes. ### Removals +* Removed support to non-keyword arguments in `#process`, `#get`, `#post`, + `#patch`, `#put`, `#delete`, and `#head` for the `ActionDispatch::IntegrationTest` + and `ActionController::TestCase` classes. + ([Commit](https://github.com/rails/rails/commit/98b8309569a326910a723f521911e54994b112fb), + [Commit](https://github.com/rails/rails/commit/de9542acd56f60d281465a59eac11e15ca8b3323)) + +* Removed deprecated `ActionDispatch::Callbacks.to_prepare` and + `ActionDispatch::Callbacks.to_cleanup`. + ([Commit](https://github.com/rails/rails/commit/3f2b7d60a52ffb2ad2d4fcf889c06b631db1946b)) + +* Removed deprecated methods related to controller filters. + ([Commit](https://github.com/rails/rails/commit/d7be30e8babf5e37a891522869e7b0191b79b757)) + ### Deprecations +* Deprecated `:controller` and `:action` path parameters. + ([Pull Request](https://github.com/rails/rails/pull/23980)) + +* Deprecated `config.action_controller.raise_on_unfiltered_parameters`. + It doesn't have any effect in Rails 5.1. + ([Commit](https://github.com/rails/rails/commit/c6640fb62b10db26004a998d2ece98baede509e5)) + ### Notable changes +* Added the `direct` and `resolve` methods to the routing DSL. + ([Pull Request](https://github.com/rails/rails/pull/23138)) + +* Added a new `ActionDispatch::SystemTestCase` class to write system tests in + your applications. + ([Pull Request](https://github.com/rails/rails/pull/26703)) + Action View ------------- @@ -341,6 +406,9 @@ Please refer to the [Changelog][action-view] for detailed changes. * Add `form_with` to unify `form_tag` and `form_for` usage. ([Pull Request](https://github.com/rails/rails/pull/26976)) +* Add `check_parameters` option to `current_page?`. + ([Pull Request](https://github.com/rails/rails/pull/27549)) + Action Mailer ------------- @@ -378,6 +446,19 @@ Please refer to the [Changelog][active-record] for detailed changes. ### Notable changes +* Change Default Primary Keys to BIGINT. + ([Pull Request](https://github.com/rails/rails/pull/26266)) + +* Virtual/generated column support for MySQL 5.7.5+ and MariaDB 5.2.0+. + ([Commit](https://github.com/rails/rails/commit/65bf1c60053e727835e06392d27a2fb49665484c)) + +* Added support for limits in batch processing. + ([Commit](https://github.com/rails/rails/commit/451437c6f57e66cc7586ec966e530493927098c7)) + +* Transactional tests now wrap all Active Record connections in database + transactions. + ([Pull Request](https://github.com/rails/rails/pull/28726)) + * Skipped comments in the output of `mysqldump` command by default. ([Pull Request](https://github.com/rails/rails/pull/23301)) @@ -389,6 +470,9 @@ Please refer to the [Changelog][active-record] for detailed changes. * Pass `"-v ON_ERROR_STOP=1"` flag with `psql` command to not suppress SQL errors. ([Pull Request](https://github.com/rails/rails/pull/24773)) +* Add `ActiveRecord::Base.connection_pool.stat`. + ([Pull Request](https://github.com/rails/rails/pull/26988)) + Active Model ------------ @@ -440,10 +524,36 @@ Please refer to the [Changelog][active-support] for detailed changes. ### Removals +* Removed the `ActiveSupport::Concurrency::Latch` class. + ([Commit](https://github.com/rails/rails/commit/0d7bd2031b4054fbdeab0a00dd58b1b08fb7fea6)) + +* Removed `halt_callback_chains_on_return_false`. + ([Commit](https://github.com/rails/rails/commit/4e63ce53fc25c3bc15c5ebf54bab54fa847ee02a)) + +* Removed deprecated behavior that halts callbacks when the return is false. + ([Commit](https://github.com/rails/rails/commit/3a25cdca3e0d29ee2040931d0cb6c275d612dffe)) + ### Deprecations +* The top level `HashWithIndifferentAccess` class has been softly deprecated + in favor of the `ActiveSupport::HashWithIndifferentAccess` one. + ([Pull request](https://github.com/rails/rails/pull/28157)) + +* Deprecate passing string to `:if` and `:unless` conditional options on `set_callback` and `skip_callback`. + ([Commit](https://github.com/rails/rails/commit/0952552) + ### Notable changes +* Fixed duration parsing and traveling to make it consistent across DST changes. + ([Commit](https://github.com/rails/rails/commit/8931916f4a1c1d8e70c06063ba63928c5c7eab1e), + [Pull Request](https://github.com/rails/rails/pull/26597)) + +* Updated Unicode to version 9.0.0. + ([Pull Request](https://github.com/rails/rails/pull/27822)) + +* Add Duration#before and #after as aliases for #ago and #since. + ([Pull Request](https://github.com/rails/rails/pull/27721)) + * Added `Module#delegate_missing_to` to delegate method calls not defined for the current object to a proxy object. ([Pull Request](https://github.com/rails/rails/pull/23930)) @@ -452,6 +562,15 @@ Please refer to the [Changelog][active-support] for detailed changes. of the current date & time. ([Pull Request](https://github.com/rails/rails/pull/24930)) +* Introduced the `assert_changes` and `assert_no_changes` methods for tests. + ([Pull Request](https://github.com/rails/rails/pull/25393)) + +* The `travel` and `travel_to` methods now raise on nested calls. + ([Pull Request](https://github.com/rails/rails/pull/24890)) + +* Update `DateTime#change` to support usec and nsec. + ([Pull Request](https://github.com/rails/rails/pull/28242)) + Credits ------- diff --git a/guides/source/working_with_javascript_in_rails.md b/guides/source/working_with_javascript_in_rails.md index 2a6a87c232..9af1c5438c 100644 --- a/guides/source/working_with_javascript_in_rails.md +++ b/guides/source/working_with_javascript_in_rails.md @@ -141,6 +141,8 @@ follow this pattern. Built-in Helpers ---------------------- +### Remote elements + Rails provides a bunch of view helper methods written in Ruby to assist you in generating HTML. Sometimes, you want to add a little Ajax to those elements, and Rails has got your back in those cases. @@ -153,14 +155,18 @@ Unless you have disabled the Asset Pipeline, provides the JavaScript half, and the regular Ruby view helpers add appropriate tags to your DOM. -### form_for +You can read below about the different events that are fired dealing with +remote elements inside your application. + +#### form_with -[`form_for`](http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-form_for) -is a helper that assists with writing forms. `form_for` takes a `:remote` -option. It works like this: +[`form_with`](http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-form_with) +is a helper that assists with writing forms. By default, `form_with` considers +that your form will be using Ajax ; you can opt-out by specifying the `:local` +option. ```erb -<%= form_for(@article, remote: true) do |f| %> +<%= form_with(model: @article) do |f| %> ... <% end %> ``` @@ -168,7 +174,7 @@ option. It works like this: This will generate the following HTML: ```html -<form accept-charset="UTF-8" action="/articles" class="new_article" data-remote="true" id="new_article" method="post"> +<form action="/articles" method="post" data-remote="true"> ... </form> ``` @@ -189,32 +195,9 @@ $(document).ready -> ``` Obviously, you'll want to be a bit more sophisticated than that, but it's a -start. You can see more about the events [in the jquery-ujs wiki](https://github.com/rails/jquery-ujs/wiki/ajax). - -### form_tag - -[`form_tag`](http://api.rubyonrails.org/classes/ActionView/Helpers/FormTagHelper.html#method-i-form_tag) -is very similar to `form_for`. It has a `:remote` option that you can use like -this: - -```erb -<%= form_tag('/articles', remote: true) do %> - ... -<% end %> -``` - -This will generate the following HTML: - -```html -<form accept-charset="UTF-8" action="/articles" data-remote="true" method="post"> - ... -</form> -``` - -Everything else is the same as `form_for`. See its documentation for full -details. +start. -### link_to +#### link_to [`link_to`](http://api.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.html#method-i-link_to) is a helper that assists with generating links. It has a `:remote` option you @@ -230,7 +213,7 @@ which generates <a href="/articles/1" data-remote="true">an article</a> ``` -You can bind to the same Ajax events as `form_for`. Here's an example. Let's +You can bind to the same Ajax events as `form_with`. Here's an example. Let's assume that we have a list of articles that can be deleted with just one click. We would generate some HTML like this: @@ -246,7 +229,7 @@ $ -> alert "The article was deleted." ``` -### button_to +#### button_to [`button_to`](http://api.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.html#method-i-button_to) is a helper that helps you create buttons. It has a `:remote` option that you can call like this: @@ -262,7 +245,136 @@ this generates </form> ``` -Since it's just a `<form>`, all of the information on `form_for` also applies. +Since it's just a `<form>`, all of the information on `form_with` also applies. + +### Customize remote elements + +It is possible to customize the behavior of elements with a `data-remote` +attribute without writing a line of JavaScript. Your can specify extra `data-` +attributes to accomplish this. + +#### `data-method` + +Activating hyperlinks always results in an HTTP GET request. However, if your +application is [RESTful](http://en.wikipedia.org/wiki/Representational_State_Transfer), +some links are in fact actions that change data on the server and must be +performed with non-GET requests. This attribute allows marking up such links +with an explicit method such as "post", "put" or "delete". + +The way it works is that, when the link is activated, it constructs a hidden form +in the document with the "action" attribute corresponding to "href" value of the +link and the method corresponding to `data-method` value, and submits that form. + +NOTE: Because submitting forms with HTTP methods other than GET and POST isn't +widely supported across browsers, all other HTTP methods are actually sent over +POST with the intended method indicated in the `_method` parameter. Rails +automatically detects and compensates for this. + +#### `data-url` and `data-params` + +Certain elements of your page aren't actually referring to any URL but you would +like them to trigger Ajax calls. Specifying the `data-url` attribute along with +the `data-remote` one will trigger an Ajax call to the given URL. You can also +specify extra parameters through the `data-params` attribute. + +This can be useful to trigger an action on check-boxes for instance: + +```html +<input type="checkbox" data-remote="true" + data-url="/update" data-params="id=10" data-method="put"> +``` + +#### `data-type` + +It is also possible to defines the Ajax `dataType` explicitly when performing +requests for `data-remote` elements through the `data-type` attribute. + +### Confirmations + +You can ask for an extra confirmation of the user by adding a `data-confirm` +attribute on links and forms. The user will be presented a JavaScript `confirm()` +dialog containing the attribute's text. If the user chooses to cancel, the action +doesn't take place. + +Adding this attribute on links will trigger the dialog on click and adding it +on forms will trigger it on submit. For example: + +```erb +<%= link_to "Dangerous zone", dangerous_zone_path, + data: { confirm: 'Are you sure?' } %> +``` + +This generates: + +```html +<a href="..." data-confirm="Are you sure?">Dangerous zone</a> +``` + +The attribute is also allowed on form submit buttons. This allows you to customize +the warning message depending on the button which was activated. In this case, +you should **not** have `data-confirm` on the form itself. + +The default confirmation uses a JavaScript confirm dialog, but you can customize +it by listening to the `confirm` event, that is fired just before the confirmation +window appears to the user. To cancel this default confirmation, make the confirm +handler to return `false`. + +### Automatic disabling + +It is also possible to automatically disable an input while the form is submitting +by using the `data-disable-with` attribute. This is to prevent accidental +double-clicks from the user, which could result in duplicate HTTP requests that +the backend may not detect as such. The value of the attribute is text that will +become the new value of the button in its disabled state. + +This also works for links with `data-method` attribute. + +For example: + +```erb +<%= form_with(model: @article.new) do |f| %> + <%= f.submit data: { "disable-with": "Saving..." } %> +<%= end %> +``` + +This generates a form with: + +```html +<input data-disable-with="Saving..." type="submit"> +``` + +Dealing with Ajax events +------------------------ + +Here are the different events that are fired when you deal with elements +that have a `data-remote` attribute: + +NOTE: All handlers bound to these events are always passed the event object as the +first argument. The table below describes the extra parameters passed after the +event argument. For exemple, if the extra parameters are listed as `xhr, settings`, +then to access them, you would define your handler with `function(event, xhr, settings)`. + +| Event name | Extra parameters | Fired | +|---------------------|------------------|-------------------------------------------------------------| +| `ajax:before` | | Before the whole ajax business, aborts if stopped. | +| `ajax:beforeSend` | xhr, options | Before the request is sent, aborts if stopped. | +| `ajax:send` | xhr | When the request is sent. | +| `ajax:success` | xhr, status, err | After completion, if the response was a success. | +| `ajax:error` | xhr, status, err | After completion, if the response was an error. | +| `ajax:complete` | xhr, status | After the request has been completed, no matter the outcome.| +| `ajax:aborted:file` | elements | If there are non-blank file inputs, aborts if stopped. | + +### Stoppable events + +If you stop `ajax:before` or `ajax:beforeSend` by returning false from the +handler method, the Ajax request will never take place. The `ajax:before` event +is also useful for manipulating form data before serialization. The +`ajax:beforeSend` event is also useful for adding custom request headers. + +If you stop the `ajax:aborted:file` event, the default behavior of allowing the +browser to submit the form via normal means (i.e. non-AJAX submission) will be +canceled and the form will not be submitted at all. This is useful for +implementing your own AJAX file upload workaround. Server-Side Concerns -------------------- @@ -297,7 +409,7 @@ The index view (`app/views/users/index.html.erb`) contains: <br> -<%= form_for(@user, remote: true) do |f| %> +<%= form_with(model: @user) do |f| %> <%= f.label :name %><br> <%= f.text_field :name %> <%= f.submit %> diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md index 5463b05b60..e6ef177451 100644 --- a/railties/CHANGELOG.md +++ b/railties/CHANGELOG.md @@ -1,8 +1,12 @@ +* Added a shared section to config/database.yml that will be loaded for all environments. + + *Pierre Schambacher* + * Namespace error pages' CSS selectors to stop the styles from bleeding into other pages - when using Turbolinks. - + when using Turbolinks. + *Jan Krutisch* - + Please check [5-1-stable](https://github.com/rails/rails/blob/5-1-stable/railties/CHANGELOG.md) for previous changes. diff --git a/railties/lib/rails/application/configuration.rb b/railties/lib/rails/application/configuration.rb index 7c49fabba5..27c1572357 100644 --- a/railties/lib/rails/application/configuration.rb +++ b/railties/lib/rails/application/configuration.rb @@ -133,7 +133,14 @@ module Rails config = if yaml && yaml.exist? require "yaml" require "erb" - YAML.load(ERB.new(yaml.read).result) || {} + loaded_yaml = YAML.load(ERB.new(yaml.read).result) || {} + shared = loaded_yaml.delete("shared") + if shared + loaded_yaml.each do |_k, values| + values.reverse_merge!(shared) + end + end + Hash.new(shared).merge(loaded_yaml) elsif ENV["DATABASE_URL"] # Value from ENV['DATABASE_URL'] is set to default database connection # by Active Record. diff --git a/railties/lib/rails/generators/rails/app/templates/bin/yarn b/railties/lib/rails/generators/rails/app/templates/bin/yarn index 4ae896a8d3..44f75c22a4 100644 --- a/railties/lib/rails/generators/rails/app/templates/bin/yarn +++ b/railties/lib/rails/generators/rails/app/templates/bin/yarn @@ -3,7 +3,8 @@ Dir.chdir(VENDOR_PATH) do begin exec "yarnpkg #{ARGV.join(" ")}" rescue Errno::ENOENT - puts "Yarn executable was not detected in the system." - puts "Download Yarn at https://yarnpkg.com/en/docs/install" + $stderr.puts "Yarn executable was not detected in the system." + $stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install" + exit 1 end end diff --git a/railties/lib/rails/mailers_controller.rb b/railties/lib/rails/mailers_controller.rb index 000ce40fbc..7ff55ef5bf 100644 --- a/railties/lib/rails/mailers_controller.rb +++ b/railties/lib/rails/mailers_controller.rb @@ -6,6 +6,8 @@ class Rails::MailersController < Rails::ApplicationController # :nodoc: before_action :require_local!, unless: :show_previews? before_action :find_preview, only: :preview + helper_method :part_query + def index @previews = ActionMailer::Preview.all @page_title = "Mailer Previews" @@ -19,7 +21,7 @@ class Rails::MailersController < Rails::ApplicationController # :nodoc: @email_action = File.basename(params[:path]) if @preview.email_exists?(@email_action) - @email = @preview.call(@email_action) + @email = @preview.call(@email_action, params) if params[:part] part_type = Mime::Type.lookup(params[:part]) @@ -76,4 +78,8 @@ class Rails::MailersController < Rails::ApplicationController # :nodoc: @email end end + + def part_query(mime_type) + request.query_parameters.merge(part: mime_type).to_query + end end diff --git a/railties/lib/rails/railtie.rb b/railties/lib/rails/railtie.rb index 474118c0a9..3476bb0eb5 100644 --- a/railties/lib/rails/railtie.rb +++ b/railties/lib/rails/railtie.rb @@ -162,10 +162,6 @@ module Rails @instance ||= new end - def respond_to_missing?(*args) - instance.respond_to?(*args) || super - end - # Allows you to configure the railtie. This is the same method seen in # Railtie::Configurable, but this module is no longer required for all # subclasses of Railtie so we provide the class method here. @@ -178,6 +174,10 @@ module Rails ActiveSupport::Inflector.underscore(string).tr("/", "_") end + def respond_to_missing?(name, _) + instance.respond_to?(name) || super + end + # If the class method does not have a method, then send the method call # to the Railtie instance. def method_missing(name, *args, &block) diff --git a/railties/lib/rails/templates/rails/mailers/email.html.erb b/railties/lib/rails/templates/rails/mailers/email.html.erb index c63781ed0c..89c1129f90 100644 --- a/railties/lib/rails/templates/rails/mailers/email.html.erb +++ b/railties/lib/rails/templates/rails/mailers/email.html.erb @@ -98,8 +98,8 @@ <% if @email.multipart? %> <dd> <select onchange="formatChanged(this);"> - <option <%= request.format == Mime[:html] ? 'selected' : '' %> value="?part=text%2Fhtml">View as HTML email</option> - <option <%= request.format == Mime[:text] ? 'selected' : '' %> value="?part=text%2Fplain">View as plain-text email</option> + <option <%= request.format == Mime[:html] ? 'selected' : '' %> value="?<%= part_query('text/html') %>">View as HTML email</option> + <option <%= request.format == Mime[:text] ? 'selected' : '' %> value="?<%= part_query('text/plain') %>">View as plain-text email</option> </select> </dd> <% end %> @@ -107,7 +107,7 @@ </header> <% if @part && @part.mime_type %> - <iframe seamless name="messageBody" src="?part=<%= Rack::Utils.escape(@part.mime_type) %>"></iframe> + <iframe seamless name="messageBody" src="?<%= part_query(@part.mime_type) %>"></iframe> <% else %> <p> You are trying to preview an email that does not have any content. diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb index 07c6afed7f..06767167a9 100644 --- a/railties/test/application/configuration_test.rb +++ b/railties/test/application/configuration_test.rb @@ -1407,6 +1407,40 @@ module ApplicationTests assert_match "config/database", err.message end + test "loads database.yml using shared keys" do + app_file "config/database.yml", <<-YAML + shared: + username: bobby + adapter: sqlite3 + + development: + database: 'dev_db' + YAML + + app "development" + + ar_config = Rails.application.config.database_configuration + assert_equal "sqlite3", ar_config["development"]["adapter"] + assert_equal "bobby", ar_config["development"]["username"] + assert_equal "dev_db", ar_config["development"]["database"] + end + + test "loads database.yml using shared keys for undefined environments" do + app_file "config/database.yml", <<-YAML + shared: + username: bobby + adapter: sqlite3 + database: 'dev_db' + YAML + + app "development" + + ar_config = Rails.application.config.database_configuration + assert_equal "sqlite3", ar_config["development"]["adapter"] + assert_equal "bobby", ar_config["development"]["username"] + assert_equal "dev_db", ar_config["development"]["database"] + end + test "config.action_mailer.show_previews defaults to true in development" do app "development" diff --git a/railties/test/application/mailer_previews_test.rb b/railties/test/application/mailer_previews_test.rb index c3a360e5d4..f5c013dab6 100644 --- a/railties/test/application/mailer_previews_test.rb +++ b/railties/test/application/mailer_previews_test.rb @@ -485,6 +485,57 @@ module ApplicationTests assert_match '<li><a href="/my_app/rails/mailers/notifier/foo">foo</a></li>', last_response.body end + test "mailer preview receives query params" do + mailer "notifier", <<-RUBY + class Notifier < ActionMailer::Base + default from: "from@example.com" + + def foo(name) + @name = name + mail to: "to@example.org" + end + end + RUBY + + html_template "notifier/foo", <<-RUBY + <p>Hello, <%= @name %>!</p> + RUBY + + text_template "notifier/foo", <<-RUBY + Hello, <%= @name %>! + RUBY + + mailer_preview "notifier", <<-RUBY + class NotifierPreview < ActionMailer::Preview + def foo + Notifier.foo(params[:name] || "World") + end + end + RUBY + + app("development") + + get "/rails/mailers/notifier/foo.txt" + assert_equal 200, last_response.status + assert_match '<iframe seamless name="messageBody" src="?part=text%2Fplain">', last_response.body + assert_match '<option selected value="?part=text%2Fplain">', last_response.body + assert_match '<option value="?part=text%2Fhtml">', last_response.body + + get "/rails/mailers/notifier/foo?part=text%2Fplain" + assert_equal 200, last_response.status + assert_match %r[Hello, World!], last_response.body + + get "/rails/mailers/notifier/foo.html?name=Ruby" + assert_equal 200, last_response.status + assert_match '<iframe seamless name="messageBody" src="?name=Ruby&part=text%2Fhtml">', last_response.body + assert_match '<option selected value="?name=Ruby&part=text%2Fhtml">', last_response.body + assert_match '<option value="?name=Ruby&part=text%2Fplain">', last_response.body + + get "/rails/mailers/notifier/foo?name=Ruby&part=text%2Fhtml" + assert_equal 200, last_response.status + assert_match %r[<p>Hello, Ruby!</p>], last_response.body + end + test "plain text mailer preview with attachment" do image_file "pixel.png", "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEWzIioca/JlAAAACklEQVQI12NgAAAAAgAB4iG8MwAAAABJRU5ErkJgggo=" diff --git a/railties/test/application/middleware/exceptions_test.rb b/railties/test/application/middleware/exceptions_test.rb index cbb990f13b..fe07ad3cbe 100644 --- a/railties/test/application/middleware/exceptions_test.rb +++ b/railties/test/application/middleware/exceptions_test.rb @@ -100,6 +100,20 @@ module ApplicationTests end end + test "routing to an nonexistent controller when action_dispatch.show_exceptions and consider_all_requests_local are set shows diagnostics" do + app_file "config/routes.rb", <<-RUBY + Rails.application.routes.draw do + resources :articles + end + RUBY + + app.config.action_dispatch.show_exceptions = true + app.config.consider_all_requests_local = true + + get "/articles" + assert_match "<title>Action Controller: Exception caught</title>", last_response.body + end + test "displays diagnostics message when exception raised in template that contains UTF-8" do controller :foo, <<-RUBY class FooController < ActionController::Base diff --git a/railties/test/application/rake/migrations_test.rb b/railties/test/application/rake/migrations_test.rb index 2c9770e147..51dfe2ef98 100644 --- a/railties/test/application/rake/migrations_test.rb +++ b/railties/test/application/rake/migrations_test.rb @@ -48,8 +48,14 @@ module ApplicationTests output = `bin/rails db:migrate:up VERSION= 2>&1` assert_match(/VERSION is required/, output) + output = `bin/rails db:migrate:up 2>&1` + assert_match(/VERSION is required/, output) + output = `bin/rails db:migrate:down VERSION= 2>&1` assert_match(/VERSION is required - To go down one migration, use db:rollback/, output) + + output = `bin/rails db:migrate:down 2>&1` + assert_match(/VERSION is required - To go down one migration, use db:rollback/, output) end end diff --git a/railties/test/isolation/abstract_unit.rb b/railties/test/isolation/abstract_unit.rb index 924503a522..2e742a005e 100644 --- a/railties/test/isolation/abstract_unit.rb +++ b/railties/test/isolation/abstract_unit.rb @@ -119,7 +119,7 @@ module TestHelpers end routes = File.read("#{app_path}/config/routes.rb") - if routes =~ /(\n\s*end\s*)\Z/ + if routes =~ /(\n\s*end\s*)\z/ File.open("#{app_path}/config/routes.rb", "w") do |f| f.puts $` + "\nActiveSupport::Deprecation.silence { match ':controller(/:action(/:id))(.:format)', via: :all }\n" + $1 end @@ -251,7 +251,7 @@ module TestHelpers def add_to_config(str) environment = File.read("#{app_path}/config/application.rb") - if environment =~ /(\n\s*end\s*end\s*)\Z/ + if environment =~ /(\n\s*end\s*end\s*)\z/ File.open("#{app_path}/config/application.rb", "w") do |f| f.puts $` + "\n#{str}\n" + $1 end @@ -260,7 +260,7 @@ module TestHelpers def add_to_env_config(env, str) environment = File.read("#{app_path}/config/environments/#{env}.rb") - if environment =~ /(\n\s*end\s*)\Z/ + if environment =~ /(\n\s*end\s*)\z/ File.open("#{app_path}/config/environments/#{env}.rb", "w") do |f| f.puts $` + "\n#{str}\n" + $1 end |