diff options
44 files changed, 588 insertions, 471 deletions
diff --git a/actionmailer/lib/action_mailer/message_delivery.rb b/actionmailer/lib/action_mailer/message_delivery.rb index 2377aeb9a5..a57980c322 100644 --- a/actionmailer/lib/action_mailer/message_delivery.rb +++ b/actionmailer/lib/action_mailer/message_delivery.rb @@ -29,7 +29,7 @@ module ActionMailer @mail_message ||= processed_mailer.message end - # Unused except for delegator internals (dup, marshaling). + # Unused except for delegator internals (dup, marshalling). def __setobj__(mail_message) #:nodoc: @mail_message = mail_message end diff --git a/actionpack/lib/action_controller/railties/helpers.rb b/actionpack/lib/action_controller/railties/helpers.rb index fa746fa9e8..75938108d6 100644 --- a/actionpack/lib/action_controller/railties/helpers.rb +++ b/actionpack/lib/action_controller/railties/helpers.rb @@ -7,7 +7,7 @@ module ActionController super return unless klass.respond_to?(:helpers_path=) - if namespace = klass.parents.detect { |m| m.respond_to?(:railtie_helpers_paths) } + if namespace = klass.module_parents.detect { |m| m.respond_to?(:railtie_helpers_paths) } paths = namespace.railtie_helpers_paths else paths = ActionController::Helpers.helpers_path diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index 3b7611acc0..2c811b5e55 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -444,7 +444,7 @@ module ActionDispatch end def include_helpers_now(klass, include_path_helpers) - namespace = klass.parents.detect { |m| m.respond_to?(:railtie_include_helpers) } + namespace = klass.module_parents.detect { |m| m.respond_to?(:railtie_include_helpers) } if namespace && namespace.railtie_namespace.routes != self namespace.railtie_include_helpers(klass, include_path_helpers) diff --git a/actionview/lib/action_view/template.rb b/actionview/lib/action_view/template.rb index 69a64948cc..070d82cf17 100644 --- a/actionview/lib/action_view/template.rb +++ b/actionview/lib/action_view/template.rb @@ -235,10 +235,10 @@ module ActionView end end - - # Exceptions are marshaled when using the parallel test runner with DRb, so we need - # to ensure that references to the template object can be marshaled as well. This means forgoing - # the marshalling of the compiler mutex and instantiating that again on unmarshaling. + + # Exceptions are marshalled when using the parallel test runner with DRb, so we need + # to ensure that references to the template object can be marshalled as well. This means forgoing + # the marshalling of the compiler mutex and instantiating that again on unmarshalling. def marshal_dump # :nodoc: [ @source, @identifier, @handler, @compiled, @original_encoding, @locals, @virtual_path, @updated_at, @formats, @variants ] end diff --git a/activemodel/lib/active_model/naming.rb b/activemodel/lib/active_model/naming.rb index 5f3d674c4e..bf23fa3c05 100644 --- a/activemodel/lib/active_model/naming.rb +++ b/activemodel/lib/active_model/naming.rb @@ -252,7 +252,7 @@ module ActiveModel # Person.model_name.plural # => "people" def model_name @_model_name ||= begin - namespace = parents.detect do |n| + namespace = module_parents.detect do |n| n.respond_to?(:use_relative_model_naming?) && n.use_relative_model_naming? end ActiveModel::Name.new(self, namespace) diff --git a/activemodel/test/cases/attribute_set_test.rb b/activemodel/test/cases/attribute_set_test.rb index b868dba743..62feb9074e 100644 --- a/activemodel/test/cases/attribute_set_test.rb +++ b/activemodel/test/cases/attribute_set_test.rb @@ -217,7 +217,7 @@ module ActiveModel assert_equal({ foo: "1" }, attributes.to_hash) end - test "marshaling dump/load legacy materialized attribute hash" do + test "marshalling dump/load legacy materialized attribute hash" do builder = AttributeSet::Builder.new(foo: Type::String.new) attributes = builder.build_from_database(foo: "1") diff --git a/activerecord/lib/active_record/associations/builder/collection_association.rb b/activerecord/lib/active_record/associations/builder/collection_association.rb index 35a72c3850..ff57c40121 100644 --- a/activerecord/lib/active_record/associations/builder/collection_association.rb +++ b/activerecord/lib/active_record/associations/builder/collection_association.rb @@ -24,7 +24,7 @@ module ActiveRecord::Associations::Builder # :nodoc: if block_given? extension_module_name = "#{model.name.demodulize}#{name.to_s.camelize}AssociationExtension" extension = Module.new(&Proc.new) - model.parent.const_set(extension_module_name, extension) + model.module_parent.const_set(extension_module_name, extension) end end diff --git a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb index feacdf6931..0059f0b773 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb @@ -410,16 +410,6 @@ module ActiveRecord end end - # The default strategy for an UPDATE with joins is to use a subquery. This doesn't work - # on MySQL (even when aliasing the tables), but MySQL allows using JOIN directly in - # an UPDATE statement, so in the MySQL adapters we redefine this to do that. - def join_to_update(update, select, key) # :nodoc: - subselect = subquery_for(key, select) - - update.where key.in(subselect) - end - alias join_to_delete join_to_update - private def default_insert_value(column) Arel.sql("DEFAULT") @@ -460,13 +450,6 @@ module ActiveRecord total_sql.join(";\n") end - # Returns a subquery for the given key using the join information. - def subquery_for(key, select) - subselect = select.clone - subselect.projections = [key] - subselect - end - # Returns an ActiveRecord::Result instance. def select(sql, name = nil, binds = []) exec_query(sql, name, binds, prepare: false) 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 09242a0f14..d40f38fb77 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb @@ -223,18 +223,6 @@ module ActiveRecord execute "ROLLBACK" end - # In the simple case, MySQL allows us to place JOINs directly into the UPDATE - # query. However, this does not allow for LIMIT, OFFSET and ORDER. To support - # these, we must use a subquery. - def join_to_update(update, select, key) # :nodoc: - if select.limit || select.offset || select.orders.any? - super - else - update.table select.source - update.wheres = select.constraints - end - end - def empty_insert_statement_value(primary_key = nil) "VALUES ()" end @@ -733,20 +721,6 @@ module ActiveRecord [remove_column_for_alter(table_name, :updated_at), remove_column_for_alter(table_name, :created_at)] end - # MySQL is too stupid to create a temporary table for use subquery, so we have - # to give it some prompting in the form of a subsubquery. Ugh! - def subquery_for(key, select) - subselect = select.clone - subselect.projections = [key] - - # Materialize subquery by adding distinct - # to work with MySQL 5.7.6 which sets optimizer_switch='derived_merge=on' - subselect.distinct unless select.limit || select.offset || select.orders.any? - - key_name = quote_column_name(key.name) - Arel::SelectManager.new(subselect.as("__active_record_temp")).project(Arel.sql(key_name)) - end - def supports_rename_index? mariadb? ? false : version >= "5.7.6" end diff --git a/activerecord/lib/active_record/fixture_set/render_context.rb b/activerecord/lib/active_record/fixture_set/render_context.rb new file mode 100644 index 0000000000..c90b5343dc --- /dev/null +++ b/activerecord/lib/active_record/fixture_set/render_context.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +# NOTE: This class has to be defined in compact style in +# order for rendering context subclassing to work correctly. +class ActiveRecord::FixtureSet::RenderContext # :nodoc: + def self.create_subclass + Class.new(ActiveRecord::FixtureSet.context_class) do + def get_binding + binding() + end + + def binary(path) + %(!!binary "#{Base64.strict_encode64(File.read(path))}") + end + end + end +end diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb index 0d1fdcfb28..e697c30bb6 100644 --- a/activerecord/lib/active_record/fixtures.rb +++ b/activerecord/lib/active_record/fixtures.rb @@ -7,6 +7,8 @@ require "set" require "active_support/dependencies" require "active_support/core_ext/digest/uuid" require "active_record/fixture_set/file" +require "active_record/fixture_set/render_context" +require "active_record/test_fixtures" require "active_record/errors" module ActiveRecord @@ -851,217 +853,3 @@ module ActiveRecord end end end - -module ActiveRecord - module TestFixtures - extend ActiveSupport::Concern - - def before_setup # :nodoc: - setup_fixtures - super - end - - def after_teardown # :nodoc: - super - teardown_fixtures - end - - included do - class_attribute :fixture_path, instance_writer: false - class_attribute :fixture_table_names, default: [] - class_attribute :fixture_class_names, default: {} - class_attribute :use_transactional_tests, default: true - class_attribute :use_instantiated_fixtures, default: false # true, false, or :no_instances - class_attribute :pre_loaded_fixtures, default: false - class_attribute :config, default: ActiveRecord::Base - class_attribute :lock_threads, default: true - end - - module ClassMethods - # Sets the model class for a fixture when the class name cannot be inferred from the fixture name. - # - # Examples: - # - # set_fixture_class some_fixture: SomeModel, - # 'namespaced/fixture' => Another::Model - # - # The keys must be the fixture names, that coincide with the short paths to the fixture files. - def set_fixture_class(class_names = {}) - self.fixture_class_names = fixture_class_names.merge(class_names.stringify_keys) - end - - def fixtures(*fixture_set_names) - if fixture_set_names.first == :all - raise StandardError, "No fixture path found. Please set `#{self}.fixture_path`." if fixture_path.blank? - fixture_set_names = Dir["#{fixture_path}/{**,*}/*.{yml}"].uniq - fixture_set_names.map! { |f| f[(fixture_path.to_s.size + 1)..-5] } - else - fixture_set_names = fixture_set_names.flatten.map(&:to_s) - end - - self.fixture_table_names |= fixture_set_names - setup_fixture_accessors(fixture_set_names) - end - - def setup_fixture_accessors(fixture_set_names = nil) - fixture_set_names = Array(fixture_set_names || fixture_table_names) - methods = Module.new do - fixture_set_names.each do |fs_name| - fs_name = fs_name.to_s - accessor_name = fs_name.tr("/", "_").to_sym - - define_method(accessor_name) do |*fixture_names| - force_reload = fixture_names.pop if fixture_names.last == true || fixture_names.last == :reload - return_single_record = fixture_names.size == 1 - fixture_names = @loaded_fixtures[fs_name].fixtures.keys if fixture_names.empty? - - @fixture_cache[fs_name] ||= {} - - instances = fixture_names.map do |f_name| - f_name = f_name.to_s if f_name.is_a?(Symbol) - @fixture_cache[fs_name].delete(f_name) if force_reload - - if @loaded_fixtures[fs_name][f_name] - @fixture_cache[fs_name][f_name] ||= @loaded_fixtures[fs_name][f_name].find - else - raise StandardError, "No fixture named '#{f_name}' found for fixture set '#{fs_name}'" - end - end - - return_single_record ? instances.first : instances - end - private accessor_name - end - end - include methods - end - - def uses_transaction(*methods) - @uses_transaction = [] unless defined?(@uses_transaction) - @uses_transaction.concat methods.map(&:to_s) - end - - def uses_transaction?(method) - @uses_transaction = [] unless defined?(@uses_transaction) - @uses_transaction.include?(method.to_s) - end - end - - def run_in_transaction? - use_transactional_tests && - !self.class.uses_transaction?(method_name) - end - - def setup_fixtures(config = ActiveRecord::Base) - if pre_loaded_fixtures && !use_transactional_tests - raise RuntimeError, "pre_loaded_fixtures requires use_transactional_tests" - end - - @fixture_cache = {} - @fixture_connections = [] - @@already_loaded_fixtures ||= {} - @connection_subscriber = nil - - # Load fixtures once and begin transaction. - if run_in_transaction? - if @@already_loaded_fixtures[self.class] - @loaded_fixtures = @@already_loaded_fixtures[self.class] - else - @loaded_fixtures = load_fixtures(config) - @@already_loaded_fixtures[self.class] = @loaded_fixtures - end - - # Begin transactions for connections already established - @fixture_connections = enlist_fixture_connections - @fixture_connections.each do |connection| - connection.begin_transaction joinable: false - connection.pool.lock_thread = true if lock_threads - end - - # When connections are established in the future, begin a transaction too - @connection_subscriber = ActiveSupport::Notifications.subscribe("!connection.active_record") do |_, _, _, _, payload| - spec_name = payload[:spec_name] if payload.key?(:spec_name) - - if spec_name - begin - connection = ActiveRecord::Base.connection_handler.retrieve_connection(spec_name) - rescue ConnectionNotEstablished - connection = nil - end - - if connection && !@fixture_connections.include?(connection) - connection.begin_transaction joinable: false - connection.pool.lock_thread = true if lock_threads - @fixture_connections << connection - end - end - end - - # Load fixtures for every test. - else - ActiveRecord::FixtureSet.reset_cache - @@already_loaded_fixtures[self.class] = nil - @loaded_fixtures = load_fixtures(config) - end - - # Instantiate fixtures for every test if requested. - instantiate_fixtures if use_instantiated_fixtures - end - - def teardown_fixtures - # Rollback changes if a transaction is active. - if run_in_transaction? - ActiveSupport::Notifications.unsubscribe(@connection_subscriber) if @connection_subscriber - @fixture_connections.each do |connection| - connection.rollback_transaction if connection.transaction_open? - connection.pool.lock_thread = false - end - @fixture_connections.clear - else - ActiveRecord::FixtureSet.reset_cache - end - - ActiveRecord::Base.clear_active_connections! - end - - def enlist_fixture_connections - ActiveRecord::Base.connection_handler.connection_pool_list.map(&:connection) - end - - private - def load_fixtures(config) - fixtures = ActiveRecord::FixtureSet.create_fixtures(fixture_path, fixture_table_names, fixture_class_names, config) - Hash[fixtures.map { |f| [f.name, f] }] - end - - def instantiate_fixtures - if pre_loaded_fixtures - raise RuntimeError, "Load fixtures before instantiating them." if ActiveRecord::FixtureSet.all_loaded_fixtures.empty? - ActiveRecord::FixtureSet.instantiate_all_loaded_fixtures(self, load_instances?) - else - raise RuntimeError, "Load fixtures before instantiating them." if @loaded_fixtures.nil? - @loaded_fixtures.each_value do |fixture_set| - ActiveRecord::FixtureSet.instantiate_fixtures(self, fixture_set, load_instances?) - end - end - end - - def load_instances? - use_instantiated_fixtures != :no_instances - end - end -end - -class ActiveRecord::FixtureSet::RenderContext # :nodoc: - def self.create_subclass - Class.new ActiveRecord::FixtureSet.context_class do - def get_binding - binding() - end - - def binary(path) - %(!!binary "#{Base64.strict_encode64(File.read(path))}") - end - end - end -end diff --git a/activerecord/lib/active_record/model_schema.rb b/activerecord/lib/active_record/model_schema.rb index 9b985e049b..5a4a1fb969 100644 --- a/activerecord/lib/active_record/model_schema.rb +++ b/activerecord/lib/active_record/model_schema.rb @@ -218,11 +218,11 @@ module ActiveRecord end def full_table_name_prefix #:nodoc: - (parents.detect { |p| p.respond_to?(:table_name_prefix) } || self).table_name_prefix + (module_parents.detect { |p| p.respond_to?(:table_name_prefix) } || self).table_name_prefix end def full_table_name_suffix #:nodoc: - (parents.detect { |p| p.respond_to?(:table_name_suffix) } || self).table_name_suffix + (module_parents.detect { |p| p.respond_to?(:table_name_suffix) } || self).table_name_suffix end # The array of names of environments where destructive actions should be prohibited. By default, @@ -503,9 +503,9 @@ module ActiveRecord def compute_table_name if base_class? # Nested classes are prefixed with singular parent table name. - if parent < Base && !parent.abstract_class? - contained = parent.table_name - contained = contained.singularize if parent.pluralize_table_names + if module_parent < Base && !module_parent.abstract_class? + contained = module_parent.table_name + contained = contained.singularize if module_parent.pluralize_table_names contained += "_" end diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb index 81ad9ef3a2..538659d6bd 100644 --- a/activerecord/lib/active_record/railtie.rb +++ b/activerecord/lib/active_record/railtie.rb @@ -168,21 +168,7 @@ end_error initializer "active_record.initialize_database" do ActiveSupport.on_load(:active_record) do self.configurations = Rails.application.config.database_configuration - - begin - establish_connection - rescue ActiveRecord::NoDatabaseError - warn <<-end_warning -Oops - You have a database configured, but it doesn't exist yet! - -Here's how to get started: - - 1. Configure your database in config/database.yml. - 2. Run `rails db:create` to create the database. - 3. Run `rails db:setup` to load your database schema. -end_warning - raise - end + establish_connection end end diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index d8cff30b88..d5b6082d13 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -348,7 +348,12 @@ module ActiveRecord end stmt = Arel::UpdateManager.new - stmt.table(table) + stmt.table(arel.join_sources.empty? ? table : arel.source) + stmt.key = arel_attribute(primary_key) + stmt.take(arel.limit) + stmt.offset(arel.offset) + stmt.order(*arel.orders) + stmt.wheres = arel.constraints if updates.is_a?(Hash) stmt.set _substitute_values(updates) @@ -356,16 +361,6 @@ module ActiveRecord stmt.set Arel.sql(klass.sanitize_sql_for_assignment(updates, table.name)) end - if has_join_values? - @klass.connection.join_to_update(stmt, arel, arel_attribute(primary_key)) - else - stmt.key = arel_attribute(primary_key) - stmt.take(arel.limit) - stmt.offset(arel.offset) - stmt.order(*arel.orders) - stmt.wheres = arel.constraints - end - @klass.connection.update stmt, "#{@klass} Update All" end @@ -483,17 +478,12 @@ module ActiveRecord end stmt = Arel::DeleteManager.new - stmt.from(table) - - if has_join_values? - @klass.connection.join_to_delete(stmt, arel, arel_attribute(primary_key)) - else - stmt.key = arel_attribute(primary_key) - stmt.take(arel.limit) - stmt.offset(arel.offset) - stmt.order(*arel.orders) - stmt.wheres = arel.constraints - end + stmt.from(arel.join_sources.empty? ? table : arel.source) + stmt.key = arel_attribute(primary_key) + stmt.take(arel.limit) + stmt.offset(arel.offset) + stmt.order(*arel.orders) + stmt.wheres = arel.constraints affected = @klass.connection.delete(stmt, "#{@klass} Destroy") @@ -648,10 +638,6 @@ module ActiveRecord end end - def has_join_values? - joins_values.any? || left_outer_joins_values.any? - end - def exec_queries(&block) skip_query_cache_if_necessary do @records = diff --git a/activerecord/lib/active_record/test_fixtures.rb b/activerecord/lib/active_record/test_fixtures.rb new file mode 100644 index 0000000000..7b7b3f7112 --- /dev/null +++ b/activerecord/lib/active_record/test_fixtures.rb @@ -0,0 +1,201 @@ +# frozen_string_literal: true + +module ActiveRecord + module TestFixtures + extend ActiveSupport::Concern + + def before_setup # :nodoc: + setup_fixtures + super + end + + def after_teardown # :nodoc: + super + teardown_fixtures + end + + included do + class_attribute :fixture_path, instance_writer: false + class_attribute :fixture_table_names, default: [] + class_attribute :fixture_class_names, default: {} + class_attribute :use_transactional_tests, default: true + class_attribute :use_instantiated_fixtures, default: false # true, false, or :no_instances + class_attribute :pre_loaded_fixtures, default: false + class_attribute :config, default: ActiveRecord::Base + class_attribute :lock_threads, default: true + end + + module ClassMethods + # Sets the model class for a fixture when the class name cannot be inferred from the fixture name. + # + # Examples: + # + # set_fixture_class some_fixture: SomeModel, + # 'namespaced/fixture' => Another::Model + # + # The keys must be the fixture names, that coincide with the short paths to the fixture files. + def set_fixture_class(class_names = {}) + self.fixture_class_names = fixture_class_names.merge(class_names.stringify_keys) + end + + def fixtures(*fixture_set_names) + if fixture_set_names.first == :all + raise StandardError, "No fixture path found. Please set `#{self}.fixture_path`." if fixture_path.blank? + fixture_set_names = Dir["#{fixture_path}/{**,*}/*.{yml}"].uniq + fixture_set_names.map! { |f| f[(fixture_path.to_s.size + 1)..-5] } + else + fixture_set_names = fixture_set_names.flatten.map(&:to_s) + end + + self.fixture_table_names |= fixture_set_names + setup_fixture_accessors(fixture_set_names) + end + + def setup_fixture_accessors(fixture_set_names = nil) + fixture_set_names = Array(fixture_set_names || fixture_table_names) + methods = Module.new do + fixture_set_names.each do |fs_name| + fs_name = fs_name.to_s + accessor_name = fs_name.tr("/", "_").to_sym + + define_method(accessor_name) do |*fixture_names| + force_reload = fixture_names.pop if fixture_names.last == true || fixture_names.last == :reload + return_single_record = fixture_names.size == 1 + fixture_names = @loaded_fixtures[fs_name].fixtures.keys if fixture_names.empty? + + @fixture_cache[fs_name] ||= {} + + instances = fixture_names.map do |f_name| + f_name = f_name.to_s if f_name.is_a?(Symbol) + @fixture_cache[fs_name].delete(f_name) if force_reload + + if @loaded_fixtures[fs_name][f_name] + @fixture_cache[fs_name][f_name] ||= @loaded_fixtures[fs_name][f_name].find + else + raise StandardError, "No fixture named '#{f_name}' found for fixture set '#{fs_name}'" + end + end + + return_single_record ? instances.first : instances + end + private accessor_name + end + end + include methods + end + + def uses_transaction(*methods) + @uses_transaction = [] unless defined?(@uses_transaction) + @uses_transaction.concat methods.map(&:to_s) + end + + def uses_transaction?(method) + @uses_transaction = [] unless defined?(@uses_transaction) + @uses_transaction.include?(method.to_s) + end + end + + def run_in_transaction? + use_transactional_tests && + !self.class.uses_transaction?(method_name) + end + + def setup_fixtures(config = ActiveRecord::Base) + if pre_loaded_fixtures && !use_transactional_tests + raise RuntimeError, "pre_loaded_fixtures requires use_transactional_tests" + end + + @fixture_cache = {} + @fixture_connections = [] + @@already_loaded_fixtures ||= {} + @connection_subscriber = nil + + # Load fixtures once and begin transaction. + if run_in_transaction? + if @@already_loaded_fixtures[self.class] + @loaded_fixtures = @@already_loaded_fixtures[self.class] + else + @loaded_fixtures = load_fixtures(config) + @@already_loaded_fixtures[self.class] = @loaded_fixtures + end + + # Begin transactions for connections already established + @fixture_connections = enlist_fixture_connections + @fixture_connections.each do |connection| + connection.begin_transaction joinable: false + connection.pool.lock_thread = true if lock_threads + end + + # When connections are established in the future, begin a transaction too + @connection_subscriber = ActiveSupport::Notifications.subscribe("!connection.active_record") do |_, _, _, _, payload| + spec_name = payload[:spec_name] if payload.key?(:spec_name) + + if spec_name + begin + connection = ActiveRecord::Base.connection_handler.retrieve_connection(spec_name) + rescue ConnectionNotEstablished + connection = nil + end + + if connection && !@fixture_connections.include?(connection) + connection.begin_transaction joinable: false + connection.pool.lock_thread = true if lock_threads + @fixture_connections << connection + end + end + end + + # Load fixtures for every test. + else + ActiveRecord::FixtureSet.reset_cache + @@already_loaded_fixtures[self.class] = nil + @loaded_fixtures = load_fixtures(config) + end + + # Instantiate fixtures for every test if requested. + instantiate_fixtures if use_instantiated_fixtures + end + + def teardown_fixtures + # Rollback changes if a transaction is active. + if run_in_transaction? + ActiveSupport::Notifications.unsubscribe(@connection_subscriber) if @connection_subscriber + @fixture_connections.each do |connection| + connection.rollback_transaction if connection.transaction_open? + connection.pool.lock_thread = false + end + @fixture_connections.clear + else + ActiveRecord::FixtureSet.reset_cache + end + + ActiveRecord::Base.clear_active_connections! + end + + def enlist_fixture_connections + ActiveRecord::Base.connection_handler.connection_pool_list.map(&:connection) + end + + private + def load_fixtures(config) + fixtures = ActiveRecord::FixtureSet.create_fixtures(fixture_path, fixture_table_names, fixture_class_names, config) + Hash[fixtures.map { |f| [f.name, f] }] + end + + def instantiate_fixtures + if pre_loaded_fixtures + raise RuntimeError, "Load fixtures before instantiating them." if ActiveRecord::FixtureSet.all_loaded_fixtures.empty? + ActiveRecord::FixtureSet.instantiate_all_loaded_fixtures(self, load_instances?) + else + raise RuntimeError, "Load fixtures before instantiating them." if @loaded_fixtures.nil? + @loaded_fixtures.each_value do |fixture_set| + ActiveRecord::FixtureSet.instantiate_fixtures(self, fixture_set, load_instances?) + end + end + end + + def load_instances? + use_instantiated_fixtures != :no_instances + end + end +end diff --git a/activerecord/lib/arel/visitors/mysql.rb b/activerecord/lib/arel/visitors/mysql.rb index eb8a449079..081452caeb 100644 --- a/activerecord/lib/arel/visitors/mysql.rb +++ b/activerecord/lib/arel/visitors/mysql.rb @@ -65,12 +65,42 @@ module Arel # :nodoc: all collector end + # In the simple case, MySQL allows us to place JOINs directly into the UPDATE + # query. However, this does not allow for LIMIT, OFFSET and ORDER. To support + # these, we must use a subquery. + def prepare_update_statement(o) + if has_join_sources?(o) + if has_limit_or_offset_or_orders?(o) + super + else + o + end + elsif o.offset + super + else + o + end + end + + def prepare_delete_statement(o) + if has_join_sources?(o) || o.offset + super + else + o + end + end + + # MySQL is too stupid to create a temporary table for use subquery, so we have + # to give it some prompting in the form of a subsubquery. def build_subselect(key, o) subselect = super # Materialize subquery by adding distinct # to work with MySQL 5.7.6 which sets optimizer_switch='derived_merge=on' - subselect.distinct unless subselect.limit || subselect.offset || subselect.orders.any? + unless has_limit_or_offset_or_orders?(subselect) + core = subselect.cores.last + core.set_quantifier = Arel::Nodes::Distinct.new + end Nodes::SelectStatement.new.tap do |stmt| core = stmt.cores.last @@ -78,22 +108,6 @@ module Arel # :nodoc: all core.projections = [Arel.sql(quote_column_name(key.name))] end end - - def collect_where_for(o, collector) - return super if o.offset - - unless o.wheres.empty? - collector << " WHERE " - collector = inject_join o.wheres, collector, " AND " - end - - unless o.orders.empty? - collector << " ORDER BY " - collector = inject_join o.orders, collector, ", " - end - - maybe_visit o.limit, collector - end end end end diff --git a/activerecord/lib/arel/visitors/to_sql.rb b/activerecord/lib/arel/visitors/to_sql.rb index 0172204fc8..7c0f6c2e97 100644 --- a/activerecord/lib/arel/visitors/to_sql.rb +++ b/activerecord/lib/arel/visitors/to_sql.rb @@ -74,26 +74,17 @@ module Arel # :nodoc: all private def visit_Arel_Nodes_DeleteStatement(o, collector) + o = prepare_delete_statement(o) + collector << "DELETE FROM " collector = visit o.relation, collector collect_where_for(o, collector) end - # FIXME: we should probably have a 2-pass visitor for this - def build_subselect(key, o) - stmt = Nodes::SelectStatement.new - core = stmt.cores.first - core.froms = o.relation - core.wheres = o.wheres - core.projections = [key] - stmt.limit = o.limit - stmt.offset = o.offset - stmt.orders = o.orders - stmt - end - def visit_Arel_Nodes_UpdateStatement(o, collector) + o = prepare_update_statement(o) + collector << "UPDATE " collector = visit o.relation, collector unless o.values.empty? @@ -800,19 +791,57 @@ module Arel # :nodoc: all } end - def collect_where_for(o, collector) - if o.orders.empty? && o.limit.nil? && o.offset.nil? - wheres = o.wheres + def has_join_sources?(o) + o.relation.is_a?(Nodes::JoinSource) && !o.relation.right.empty? + end + + def has_limit_or_offset_or_orders?(o) + o.limit || o.offset || !o.orders.empty? + end + + # The default strategy for an UPDATE with joins is to use a subquery. This doesn't work + # on MySQL (even when aliasing the tables), but MySQL allows using JOIN directly in + # an UPDATE statement, so in the MySQL visitor we redefine this to do that. + def prepare_update_statement(o) + if o.key && (has_limit_or_offset_or_orders?(o) || has_join_sources?(o)) + stmt = o.clone + stmt.limit = nil + stmt.offset = nil + stmt.orders = [] + stmt.wheres = [Nodes::In.new(o.key, [build_subselect(o.key, o)])] + stmt.relation = o.relation.left if has_join_sources?(o) + stmt else - wheres = [Nodes::In.new(o.key, [build_subselect(o.key, o)])] + o end + end + alias :prepare_delete_statement :prepare_update_statement + + # FIXME: we should probably have a 2-pass visitor for this + def build_subselect(key, o) + stmt = Nodes::SelectStatement.new + core = stmt.cores.first + core.froms = o.relation + core.wheres = o.wheres + core.projections = [key] + stmt.limit = o.limit + stmt.offset = o.offset + stmt.orders = o.orders + stmt + end - unless wheres.empty? + def collect_where_for(o, collector) + unless o.wheres.empty? collector << " WHERE " - collector = inject_join wheres, collector, " AND " + collector = inject_join o.wheres, collector, " AND " end - collector + unless o.orders.empty? + collector << " ORDER BY " + collector = inject_join o.orders, collector, ", " + end + + maybe_visit o.limit, collector end def infix_value(o, collector, value) diff --git a/activerecord/test/cases/relation_test.rb b/activerecord/test/cases/relation_test.rb index fbeb617b29..68161f6a84 100644 --- a/activerecord/test/cases/relation_test.rb +++ b/activerecord/test/cases/relation_test.rb @@ -232,7 +232,7 @@ module ActiveRecord assert_equal 3, nb_inner_join, "Wrong amount of INNER JOIN in query" # using `\W` as the column separator - assert queries.any? { |sql| %r[INNER\s+JOIN\s+#{Author.quoted_table_name}\s+\Wauthors_categorizations\W]i.match?(sql) }, "Should be aliasing the child INNER JOINs in query" + assert queries.any? { |sql| %r[INNER\s+JOIN\s+#{Regexp.escape(Author.quoted_table_name)}\s+\Wauthors_categorizations\W]i.match?(sql) }, "Should be aliasing the child INNER JOINs in query" end def test_relation_with_merged_joins_aliased_works diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index 3a348a26bf..586ed28693 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -1,3 +1,16 @@ +* Rename `Module#parent`, `Module#parents`, and `Module#parent_name` to + `module_parent`, `module_parents`, and `module_parent_name`. + + *Gannon McGibbon* + +* Deprecate the use of `LoggerSilence` in favor of `ActiveSupport::LoggerSilence` + + *Edouard Chin* + +* Deprecate using negative limits in `String#first` and `String#last`. + + *Gannon McGibbon*, *Eric Turner* + * Fix bug where `#without` for `ActiveSupport::HashWithIndifferentAccess` would fail with symbol arguments diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb index a5d0c52b13..222ef2b515 100644 --- a/activesupport/lib/active_support/cache.rb +++ b/activesupport/lib/active_support/cache.rb @@ -596,9 +596,13 @@ module ActiveSupport # Merges the default options with ones specific to a method call. def merged_options(call_options) if call_options - options.merge(call_options) + if options.empty? + call_options + else + options.merge(call_options) + end else - options.dup + options end end diff --git a/activesupport/lib/active_support/configurable.rb b/activesupport/lib/active_support/configurable.rb index 2610114d8f..6159e45230 100644 --- a/activesupport/lib/active_support/configurable.rb +++ b/activesupport/lib/active_support/configurable.rb @@ -105,7 +105,7 @@ module ActiveSupport # end # # User.hair_colors # => [:brown, :black, :blonde, :red] - def config_accessor(*names) + def config_accessor(*names) #:doc: options = names.extract_options! names.each do |name| diff --git a/activesupport/lib/active_support/core_ext/module/introspection.rb b/activesupport/lib/active_support/core_ext/module/introspection.rb index c5bb598bd1..9b6df40596 100644 --- a/activesupport/lib/active_support/core_ext/module/introspection.rb +++ b/activesupport/lib/active_support/core_ext/module/introspection.rb @@ -5,8 +5,8 @@ require "active_support/inflector" class Module # Returns the name of the module containing this one. # - # M::N.parent_name # => "M" - def parent_name + # M::N.module_parent_name # => "M" + def module_parent_name if defined?(@parent_name) @parent_name else @@ -16,6 +16,14 @@ class Module end end + def parent_name + ActiveSupport::Deprecation.warn(<<-MSG.squish) + `Module#parent_name` has been renamed to `module_parent_name`. + `parent_name` is deprecated and will be removed in Rails 6.1. + MSG + module_parent_name + end + # Returns the module which contains this one according to its name. # # module M @@ -24,15 +32,23 @@ class Module # end # X = M::N # - # M::N.parent # => M - # X.parent # => M + # M::N.module_parent # => M + # X.module_parent # => M # # The parent of top-level and anonymous modules is Object. # - # M.parent # => Object - # Module.new.parent # => Object + # M.module_parent # => Object + # Module.new.module_parent # => Object + def module_parent + module_parent_name ? ActiveSupport::Inflector.constantize(module_parent_name) : Object + end + def parent - parent_name ? ActiveSupport::Inflector.constantize(parent_name) : Object + ActiveSupport::Deprecation.warn(<<-MSG.squish) + `Module#parent` has been renamed to `module_parent`. + `parent` is deprecated and will be removed in Rails 6.1. + MSG + module_parent end # Returns all the parents of this module according to its name, ordered from @@ -44,13 +60,13 @@ class Module # end # X = M::N # - # M.parents # => [Object] - # M::N.parents # => [M, Object] - # X.parents # => [M, Object] - def parents + # M.module_parents # => [Object] + # M::N.module_parents # => [M, Object] + # X.module_parents # => [M, Object] + def module_parents parents = [] - if parent_name - parts = parent_name.split("::") + if module_parent_name + parts = module_parent_name.split("::") until parts.empty? parents << ActiveSupport::Inflector.constantize(parts * "::") parts.pop @@ -59,4 +75,12 @@ class Module parents << Object unless parents.include? Object parents end + + def parents + ActiveSupport::Deprecation.warn(<<-MSG.squish) + `Module#parents` has been renamed to `module_parents`. + `parents` is deprecated and will be removed in Rails 6.1. + MSG + module_parents + end end diff --git a/activesupport/lib/active_support/core_ext/string/access.rb b/activesupport/lib/active_support/core_ext/string/access.rb index 58591bbaaf..4ca24028b0 100644 --- a/activesupport/lib/active_support/core_ext/string/access.rb +++ b/activesupport/lib/active_support/core_ext/string/access.rb @@ -75,6 +75,10 @@ class String # str.first(0) # => "" # str.first(6) # => "hello" def first(limit = 1) + ActiveSupport::Deprecation.warn( + "Calling String#first with a negative integer limit " \ + "will raise an ArgumentError in Rails 6.1." + ) if limit < 0 if limit == 0 "" elsif limit >= size @@ -95,6 +99,10 @@ class String # str.last(0) # => "" # str.last(6) # => "hello" def last(limit = 1) + ActiveSupport::Deprecation.warn( + "Calling String#last with a negative integer limit " \ + "will raise an ArgumentError in Rails 6.1." + ) if limit < 0 if limit == 0 "" elsif limit >= size diff --git a/activesupport/lib/active_support/dependencies.rb b/activesupport/lib/active_support/dependencies.rb index 063d8d1587..66e0bea00e 100644 --- a/activesupport/lib/active_support/dependencies.rb +++ b/activesupport/lib/active_support/dependencies.rb @@ -521,8 +521,8 @@ module ActiveSupport #:nodoc: end elsif mod = autoload_module!(from_mod, const_name, qualified_name, path_suffix) return mod - elsif (parent = from_mod.parent) && parent != from_mod && - ! from_mod.parents.any? { |p| p.const_defined?(const_name, false) } + elsif (parent = from_mod.module_parent) && parent != from_mod && + ! from_mod.module_parents.any? { |p| p.const_defined?(const_name, false) } # If our parents do not have a constant named +const_name+ then we are free # to attempt to load upwards. If they do have such a constant, then this # const_missing must be due to from_mod::const_name, which should not diff --git a/activesupport/lib/active_support/logger.rb b/activesupport/lib/active_support/logger.rb index 8152a182b4..b8555c887b 100644 --- a/activesupport/lib/active_support/logger.rb +++ b/activesupport/lib/active_support/logger.rb @@ -6,7 +6,6 @@ require "logger" module ActiveSupport class Logger < ::Logger - include ActiveSupport::LoggerThreadSafeLevel include LoggerSilence # Returns true if the logger destination matches one of the sources @@ -81,20 +80,6 @@ module ActiveSupport def initialize(*args) super @formatter = SimpleFormatter.new - after_initialize if respond_to? :after_initialize - end - - def add(severity, message = nil, progname = nil, &block) - return true if @logdev.nil? || (severity || UNKNOWN) < level - super - end - - Logger::Severity.constants.each do |severity| - class_eval(<<-EOT, __FILE__, __LINE__ + 1) - def #{severity.downcase}? # def debug? - Logger::#{severity} >= level # DEBUG >= level - end # end - EOT end # Simple formatter which only displays the message. diff --git a/activesupport/lib/active_support/logger_silence.rb b/activesupport/lib/active_support/logger_silence.rb index 4344ca0429..b2444c1e34 100644 --- a/activesupport/lib/active_support/logger_silence.rb +++ b/activesupport/lib/active_support/logger_silence.rb @@ -2,27 +2,44 @@ require "active_support/concern" require "active_support/core_ext/module/attribute_accessors" +require "active_support/logger_thread_safe_level" module LoggerSilence extend ActiveSupport::Concern included do - cattr_accessor :silencer, default: true + ActiveSupport::Deprecation.warn( + "Including LoggerSilence is deprecated and will be removed in Rails 6.1. " \ + "Please use `ActiveSupport::LoggerSilence` instead" + ) + + include ActiveSupport::LoggerSilence end +end + +module ActiveSupport + module LoggerSilence + extend ActiveSupport::Concern + + included do + cattr_accessor :silencer, default: true + include ActiveSupport::LoggerThreadSafeLevel + end - # Silences the logger for the duration of the block. - def silence(temporary_level = Logger::ERROR) - if silencer - begin - old_local_level = local_level - self.local_level = temporary_level + # Silences the logger for the duration of the block. + def silence(temporary_level = Logger::ERROR) + if silencer + begin + old_local_level = local_level + self.local_level = temporary_level + yield self + ensure + self.local_level = old_local_level + end + else yield self - ensure - self.local_level = old_local_level end - else - yield self end end end diff --git a/activesupport/lib/active_support/logger_thread_safe_level.rb b/activesupport/lib/active_support/logger_thread_safe_level.rb index 22ab4cc28c..f16c90cfc6 100644 --- a/activesupport/lib/active_support/logger_thread_safe_level.rb +++ b/activesupport/lib/active_support/logger_thread_safe_level.rb @@ -1,14 +1,30 @@ # frozen_string_literal: true require "active_support/concern" +require "active_support/core_ext/module/attribute_accessors" require "concurrent" module ActiveSupport module LoggerThreadSafeLevel # :nodoc: extend ActiveSupport::Concern + included do + cattr_accessor :local_levels, default: Concurrent::Map.new(initial_capacity: 2), instance_accessor: false + end + + Logger::Severity.constants.each do |severity| + class_eval(<<-EOT, __FILE__, __LINE__ + 1) + def #{severity.downcase}? # def debug? + Logger::#{severity} >= level # DEBUG >= level + end # end + EOT + end + def after_initialize - @local_levels = Concurrent::Map.new(initial_capacity: 2) + ActiveSupport::Deprecation.warn( + "Logger don't need to call #after_initialize directly anymore. It will be deprecated without replacement in " \ + "Rails 6.1." + ) end def local_log_id @@ -16,19 +32,24 @@ module ActiveSupport end def local_level - @local_levels[local_log_id] + self.class.local_levels[local_log_id] end def local_level=(level) if level - @local_levels[local_log_id] = level + self.class.local_levels[local_log_id] = level else - @local_levels.delete(local_log_id) + self.class.local_levels.delete(local_log_id) end end def level local_level || super end + + def add(severity, message = nil, progname = nil, &block) # :nodoc: + return true if @logdev.nil? || (severity || UNKNOWN) < level + super + end end end diff --git a/activesupport/test/broadcast_logger_test.rb b/activesupport/test/broadcast_logger_test.rb index 181113e70a..7dfa8a62bd 100644 --- a/activesupport/test/broadcast_logger_test.rb +++ b/activesupport/test/broadcast_logger_test.rb @@ -114,7 +114,17 @@ module ActiveSupport assert_equal [[::Logger::FATAL, "seen", nil]], log2.adds end + test "Including top constant LoggerSilence is deprecated" do + assert_deprecated("Please use `ActiveSupport::LoggerSilence`") do + Class.new(CustomLogger) do + include ::LoggerSilence + end + end + end + class CustomLogger + include ActiveSupport::LoggerSilence + attr_reader :adds, :closed, :chevrons attr_accessor :level, :progname, :formatter, :local_level @@ -166,7 +176,6 @@ module ActiveSupport end class FakeLogger < CustomLogger - include LoggerSilence end end end diff --git a/activesupport/test/core_ext/module/introspection_test.rb b/activesupport/test/core_ext/module/introspection_test.rb index 76d3012239..d8409d5e44 100644 --- a/activesupport/test/core_ext/module/introspection_test.rb +++ b/activesupport/test/core_ext/module/introspection_test.rb @@ -15,25 +15,43 @@ module ParentA end class IntrospectionTest < ActiveSupport::TestCase + def test_module_parent_name + assert_equal "ParentA", ParentA::B.module_parent_name + assert_equal "ParentA::B", ParentA::B::C.module_parent_name + assert_nil ParentA.module_parent_name + end + + def test_module_parent_name_when_frozen + assert_equal "ParentA", ParentA::FrozenB.module_parent_name + assert_equal "ParentA::B", ParentA::B::FrozenC.module_parent_name + end + def test_parent_name - assert_equal "ParentA", ParentA::B.parent_name - assert_equal "ParentA::B", ParentA::B::C.parent_name - assert_nil ParentA.parent_name + assert_deprecated do + assert_equal "ParentA", ParentA::B.parent_name + end end - def test_parent_name_when_frozen - assert_equal "ParentA", ParentA::FrozenB.parent_name - assert_equal "ParentA::B", ParentA::B::FrozenC.parent_name + def test_module_parent + assert_equal ParentA::B, ParentA::B::C.module_parent + assert_equal ParentA, ParentA::B.module_parent + assert_equal Object, ParentA.module_parent end def test_parent - assert_equal ParentA::B, ParentA::B::C.parent - assert_equal ParentA, ParentA::B.parent - assert_equal Object, ParentA.parent + assert_deprecated do + assert_equal ParentA, ParentA::B.parent + end + end + + def test_module_parents + assert_equal [ParentA::B, ParentA, Object], ParentA::B::C.module_parents + assert_equal [ParentA, Object], ParentA::B.module_parents end def test_parents - assert_equal [ParentA::B, ParentA, Object], ParentA::B::C.parents - assert_equal [ParentA, Object], ParentA::B.parents + assert_deprecated do + assert_equal [ParentA, Object], ParentA::B.parents + end end end diff --git a/activesupport/test/core_ext/string_ext_test.rb b/activesupport/test/core_ext/string_ext_test.rb index aa120a6f9a..2468fe3603 100644 --- a/activesupport/test/core_ext/string_ext_test.rb +++ b/activesupport/test/core_ext/string_ext_test.rb @@ -469,6 +469,15 @@ class StringAccessTest < ActiveSupport::TestCase assert_not_same different_string, string end + test "#first with negative Integer is deprecated" do + string = "hello" + message = "Calling String#first with a negative integer limit " \ + "will raise an ArgumentError in Rails 6.1." + assert_deprecated(message) do + string.first(-1) + end + end + test "#last returns the last character" do assert_equal "o", "hello".last assert_equal "x", "x".last @@ -487,6 +496,15 @@ class StringAccessTest < ActiveSupport::TestCase assert_not_same different_string, string end + test "#last with negative Integer is deprecated" do + string = "hello" + message = "Calling String#last with a negative integer limit " \ + "will raise an ArgumentError in Rails 6.1." + assert_deprecated(message) do + string.last(-1) + end + end + test "access returns a real string" do hash = {} hash["h"] = true diff --git a/activesupport/test/core_ext/time_ext_test.rb b/activesupport/test/core_ext/time_ext_test.rb index e1cb22fda8..7078f3506d 100644 --- a/activesupport/test/core_ext/time_ext_test.rb +++ b/activesupport/test/core_ext/time_ext_test.rb @@ -950,39 +950,39 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase end class TimeExtMarshalingTest < ActiveSupport::TestCase - def test_marshaling_with_utc_instance + def test_marshalling_with_utc_instance t = Time.utc(2000) - unmarshaled = Marshal.load(Marshal.dump(t)) - assert_equal "UTC", unmarshaled.zone - assert_equal t, unmarshaled + unmarshalled = Marshal.load(Marshal.dump(t)) + assert_equal "UTC", unmarshalled.zone + assert_equal t, unmarshalled end - def test_marshaling_with_local_instance + def test_marshalling_with_local_instance t = Time.local(2000) - unmarshaled = Marshal.load(Marshal.dump(t)) - assert_equal t.zone, unmarshaled.zone - assert_equal t, unmarshaled + unmarshalled = Marshal.load(Marshal.dump(t)) + assert_equal t.zone, unmarshalled.zone + assert_equal t, unmarshalled end - def test_marshaling_with_frozen_utc_instance + def test_marshalling_with_frozen_utc_instance t = Time.utc(2000).freeze - unmarshaled = Marshal.load(Marshal.dump(t)) - assert_equal "UTC", unmarshaled.zone - assert_equal t, unmarshaled + unmarshalled = Marshal.load(Marshal.dump(t)) + assert_equal "UTC", unmarshalled.zone + assert_equal t, unmarshalled end - def test_marshaling_with_frozen_local_instance + def test_marshalling_with_frozen_local_instance t = Time.local(2000).freeze - unmarshaled = Marshal.load(Marshal.dump(t)) - assert_equal t.zone, unmarshaled.zone - assert_equal t, unmarshaled + unmarshalled = Marshal.load(Marshal.dump(t)) + assert_equal t.zone, unmarshalled.zone + assert_equal t, unmarshalled end def test_marshalling_preserves_fractional_seconds t = Time.parse("00:00:00.500") - unmarshaled = Marshal.load(Marshal.dump(t)) - assert_equal t.to_f, unmarshaled.to_f - assert_equal t, unmarshaled + unmarshalled = Marshal.load(Marshal.dump(t)) + assert_equal t.to_f, unmarshalled.to_f + assert_equal t, unmarshalled end def test_last_quarter_on_31st diff --git a/activesupport/test/silence_logger_test.rb b/activesupport/test/silence_logger_test.rb new file mode 100644 index 0000000000..bd0c6b7f86 --- /dev/null +++ b/activesupport/test/silence_logger_test.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +require "abstract_unit" +require "active_support/logger_silence" +require "logger" + +class LoggerSilenceTest < ActiveSupport::TestCase + class MyLogger < ::Logger + include ActiveSupport::LoggerSilence + end + + setup do + @io = StringIO.new + @logger = MyLogger.new(@io) + end + + test "#silence silences the log" do + @logger.silence(Logger::ERROR) do + @logger.info("Foo") + end + @io.rewind + + assert_empty @io.read + end + + test "#debug? is true when setting the temporary level to Logger::DEBUG" do + @logger.level = Logger::INFO + + @logger.silence(Logger::DEBUG) do + assert_predicate @logger, :debug? + end + + assert_predicate @logger, :info? + end +end diff --git a/guides/source/3_0_release_notes.md b/guides/source/3_0_release_notes.md index e793146c2c..e936644daf 100644 --- a/guides/source/3_0_release_notes.md +++ b/guides/source/3_0_release_notes.md @@ -38,7 +38,7 @@ If you're upgrading an existing application, it's a great idea to have good test Rails 3.0 requires Ruby 1.8.7 or higher. Support for all of the previous Ruby versions has been dropped officially and you should upgrade as early as possible. Rails 3.0 is also compatible with Ruby 1.9.2. -TIP: Note that Ruby 1.8.7 p248 and p249 have marshaling bugs that crash Rails 3.0. Ruby Enterprise Edition have these fixed since release 1.8.7-2010.02 though. On the 1.9 front, Ruby 1.9.1 is not usable because it outright segfaults on Rails 3.0, so if you want to use Rails 3 with 1.9.x jump on 1.9.2 for smooth sailing. +TIP: Note that Ruby 1.8.7 p248 and p249 have marshalling bugs that crash Rails 3.0. Ruby Enterprise Edition have these fixed since release 1.8.7-2010.02 though. On the 1.9 front, Ruby 1.9.1 is not usable because it outright segfaults on Rails 3.0, so if you want to use Rails 3 with 1.9.x jump on 1.9.2 for smooth sailing. ### Rails Application object diff --git a/guides/source/3_1_release_notes.md b/guides/source/3_1_release_notes.md index 8c3dc3454d..d6981656ee 100644 --- a/guides/source/3_1_release_notes.md +++ b/guides/source/3_1_release_notes.md @@ -26,7 +26,7 @@ If you're upgrading an existing application, it's a great idea to have good test Rails 3.1 requires Ruby 1.8.7 or higher. Support for all of the previous Ruby versions has been dropped officially and you should upgrade as early as possible. Rails 3.1 is also compatible with Ruby 1.9.2. -TIP: Note that Ruby 1.8.7 p248 and p249 have marshaling bugs that crash Rails. Ruby Enterprise Edition have these fixed since release 1.8.7-2010.02 though. On the 1.9 front, Ruby 1.9.1 is not usable because it outright segfaults, so if you want to use 1.9.x jump on 1.9.2 for smooth sailing. +TIP: Note that Ruby 1.8.7 p248 and p249 have marshalling bugs that crash Rails. Ruby Enterprise Edition have these fixed since release 1.8.7-2010.02 though. On the 1.9 front, Ruby 1.9.1 is not usable because it outright segfaults, so if you want to use 1.9.x jump on 1.9.2 for smooth sailing. ### What to update in your apps diff --git a/guides/source/4_2_release_notes.md b/guides/source/4_2_release_notes.md index f7c40d19e9..51d06bd07d 100644 --- a/guides/source/4_2_release_notes.md +++ b/guides/source/4_2_release_notes.md @@ -44,7 +44,7 @@ to their respective adapters. Active Job comes pre-configured with an inline runner that executes jobs right away. Jobs often need to take Active Record objects as arguments. Active Job passes -object references as URIs (uniform resource identifiers) instead of marshaling +object references as URIs (uniform resource identifiers) instead of marshalling the object itself. The new [Global ID](https://github.com/rails/globalid) library builds URIs and looks up the objects they reference. Passing Active Record objects as job arguments just works by using Global ID internally. diff --git a/guides/source/active_support_core_extensions.md b/guides/source/active_support_core_extensions.md index f9fc7044ba..6b0554bb5f 100644 --- a/guides/source/active_support_core_extensions.md +++ b/guides/source/active_support_core_extensions.md @@ -590,9 +590,9 @@ NOTE: Defined in `active_support/core_ext/module/attribute_accessors.rb`. ### Parents -#### `parent` +#### `module_parent` -The `parent` method on a nested named module returns the module that contains its corresponding constant: +The `module_parent` method on a nested named module returns the module that contains its corresponding constant: ```ruby module X @@ -603,19 +603,19 @@ module X end M = X::Y::Z -X::Y::Z.parent # => X::Y -M.parent # => X::Y +X::Y::Z.module_parent # => X::Y +M.module_parent # => X::Y ``` -If the module is anonymous or belongs to the top-level, `parent` returns `Object`. +If the module is anonymous or belongs to the top-level, `module_parent` returns `Object`. -WARNING: Note that in that case `parent_name` returns `nil`. +WARNING: Note that in that case `module_parent_name` returns `nil`. NOTE: Defined in `active_support/core_ext/module/introspection.rb`. -#### `parent_name` +#### `module_parent_name` -The `parent_name` method on a nested named module returns the fully qualified name of the module that contains its corresponding constant: +The `module_parent_name` method on a nested named module returns the fully qualified name of the module that contains its corresponding constant: ```ruby module X @@ -626,19 +626,19 @@ module X end M = X::Y::Z -X::Y::Z.parent_name # => "X::Y" -M.parent_name # => "X::Y" +X::Y::Z.module_parent_name # => "X::Y" +M.module_parent_name # => "X::Y" ``` -For top-level or anonymous modules `parent_name` returns `nil`. +For top-level or anonymous modules `module_parent_name` returns `nil`. -WARNING: Note that in that case `parent` returns `Object`. +WARNING: Note that in that case `module_parent` returns `Object`. NOTE: Defined in `active_support/core_ext/module/introspection.rb`. -#### `parents` +#### `module_parents` -The method `parents` calls `parent` on the receiver and upwards until `Object` is reached. The chain is returned in an array, from bottom to top: +The method `module_parents` calls `module_parent` on the receiver and upwards until `Object` is reached. The chain is returned in an array, from bottom to top: ```ruby module X @@ -649,8 +649,8 @@ module X end M = X::Y::Z -X::Y::Z.parents # => [X::Y, X, Object] -M.parents # => [X::Y, X, Object] +X::Y::Z.module_parents # => [X::Y, X, Object] +M.module_parents # => [X::Y, X, Object] ``` NOTE: Defined in `active_support/core_ext/module/introspection.rb`. diff --git a/guides/source/asset_pipeline.md b/guides/source/asset_pipeline.md index bf046a3341..66cf9da33b 100644 --- a/guides/source/asset_pipeline.md +++ b/guides/source/asset_pipeline.md @@ -184,9 +184,8 @@ the file `scaffolds.css` (or `scaffolds.scss` if `sass-rails` is in the `Gemfile`.) For example, if you generate a `ProjectsController`, Rails will also add a new -file at `app/assets/javascripts/projects.coffee` and another at -`app/assets/stylesheets/projects.scss`. By default these files will be ready -to use by your application immediately using the `require_tree` directive. See +file at `app/assets/stylesheets/projects.scss`. By default these files will be +ready to use by your application immediately using the `require_tree` directive. See [Manifest Files and Directives](#manifest-files-and-directives) for more details on require_tree. diff --git a/guides/source/command_line.md b/guides/source/command_line.md index 7fa0a49203..5fd3ad17de 100644 --- a/guides/source/command_line.md +++ b/guides/source/command_line.md @@ -185,7 +185,7 @@ The controller generator is expecting parameters in the form of `generate contro ```bash $ rails generate controller Greetings hello create app/controllers/greetings_controller.rb - route get "greetings/hello" + route get 'greetings/hello' invoke erb create app/views/greetings create app/views/greetings/hello.html.erb @@ -193,9 +193,8 @@ $ rails generate controller Greetings hello create test/controllers/greetings_controller_test.rb invoke helper create app/helpers/greetings_helper.rb + invoke test_unit invoke assets - invoke coffee - create app/assets/javascripts/greetings.coffee invoke scss create app/assets/stylesheets/greetings.scss ``` diff --git a/guides/source/configuring.md b/guides/source/configuring.md index 18a377a02e..812565b207 100644 --- a/guides/source/configuring.md +++ b/guides/source/configuring.md @@ -119,12 +119,11 @@ defaults to `:debug` for all environments. The available log levels are: `:debug * `config.logger` is the logger that will be used for `Rails.logger` and any related Rails logging such as `ActiveRecord::Base.logger`. It defaults to an instance of `ActiveSupport::TaggedLogging` that wraps an instance of `ActiveSupport::Logger` which outputs a log to the `log/` directory. You can supply a custom logger, to get full compatibility you must follow these guidelines: * To support a formatter, you must manually assign a formatter from the `config.log_formatter` value to the logger. * To support tagged logs, the log instance must be wrapped with `ActiveSupport::TaggedLogging`. - * To support silencing, the logger must include `LoggerSilence` and `ActiveSupport::LoggerThreadSafeLevel` modules. The `ActiveSupport::Logger` class already includes these modules. + * To support silencing, the logger must include `ActiveSupport::LoggerSilence` module. The `ActiveSupport::Logger` class already includes these modules. ```ruby class MyLogger < ::Logger - include ActiveSupport::LoggerThreadSafeLevel - include LoggerSilence + include ActiveSupport::LoggerSilence end mylogger = MyLogger.new(STDOUT) diff --git a/guides/source/generators.md b/guides/source/generators.md index 89424a161b..f028d14998 100644 --- a/guides/source/generators.md +++ b/guides/source/generators.md @@ -203,8 +203,6 @@ $ rails generate scaffold User name:string create test/application_system_test_case.rb create test/system/users_test.rb invoke assets - invoke coffee - create app/assets/javascripts/users.coffee invoke scss create app/assets/stylesheets/users.scss invoke scss @@ -426,9 +424,8 @@ $ rails generate scaffold Comment body:text create test/application_system_test_case.rb create test/system/comments_test.rb invoke assets - invoke coffee - create app/assets/javascripts/comments.coffee invoke scss + create app/assets/stylesheets/scaffolds.scss ``` Fallbacks allow your generators to have a single responsibility, increasing code reuse and reducing the amount of duplication. diff --git a/guides/source/getting_started.md b/guides/source/getting_started.md index 197a198db7..9eb77d2a50 100644 --- a/guides/source/getting_started.md +++ b/guides/source/getting_started.md @@ -272,8 +272,6 @@ invoke helper create app/helpers/welcome_helper.rb invoke test_unit invoke assets -invoke coffee -create app/assets/javascripts/welcome.coffee invoke scss create app/assets/stylesheets/welcome.scss ``` @@ -1665,7 +1663,6 @@ This creates five files and one empty directory: | app/views/comments/ | Views of the controller are stored here | | test/controllers/comments_controller_test.rb | The test for the controller | | app/helpers/comments_helper.rb | A view helper file | -| app/assets/javascripts/comments.coffee | CoffeeScript for the controller | | app/assets/stylesheets/comments.scss | Cascading style sheet for the controller | Like with any blog, our readers will create their comments directly after diff --git a/guides/source/upgrading_ruby_on_rails.md b/guides/source/upgrading_ruby_on_rails.md index befd4e08c0..a0553c1ccc 100644 --- a/guides/source/upgrading_ruby_on_rails.md +++ b/guides/source/upgrading_ruby_on_rails.md @@ -41,7 +41,7 @@ Rails generally stays close to the latest released Ruby version when it's releas * Rails 3.2.x is the last branch to support Ruby 1.8.7. * Rails 3 and above require Ruby 1.8.7 or higher. Support for all of the previous Ruby versions has been dropped officially. You should upgrade as early as possible. -TIP: Ruby 1.8.7 p248 and p249 have marshaling bugs that crash Rails. Ruby Enterprise Edition has these fixed since the release of 1.8.7-2010.02. On the 1.9 front, Ruby 1.9.1 is not usable because it outright segfaults, so if you want to use 1.9.x, jump straight to 1.9.3 for smooth sailing. +TIP: Ruby 1.8.7 p248 and p249 have marshalling bugs that crash Rails. Ruby Enterprise Edition has these fixed since the release of 1.8.7-2010.02. On the 1.9 front, Ruby 1.9.1 is not usable because it outright segfaults, so if you want to use 1.9.x, jump straight to 1.9.3 for smooth sailing. ### The Update Task diff --git a/guides/source/working_with_javascript_in_rails.md b/guides/source/working_with_javascript_in_rails.md index 36f5039883..c36b3faa6c 100644 --- a/guides/source/working_with_javascript_in_rails.md +++ b/guides/source/working_with_javascript_in_rails.md @@ -494,10 +494,6 @@ replace the entire `<body>` of the page with the `<body>` of the response. It will then use PushState to change the URL to the correct one, preserving refresh semantics and giving you pretty URLs. -The only thing you have to do to enable Turbolinks is have it in your `Gemfile`, -and put `//= require turbolinks` in your JavaScript manifest, which is usually -`app/assets/javascripts/application.js`. - If you want to disable Turbolinks for certain links, add a `data-turbolinks="false"` attribute to the tag: diff --git a/railties/lib/rails/railtie.rb b/railties/lib/rails/railtie.rb index 88dd932370..a67b90e285 100644 --- a/railties/lib/rails/railtie.rb +++ b/railties/lib/rails/railtie.rb @@ -224,7 +224,7 @@ module Rails end def railtie_namespace #:nodoc: - @railtie_namespace ||= self.class.parents.detect { |n| n.respond_to?(:railtie_namespace) } + @railtie_namespace ||= self.class.module_parents.detect { |n| n.respond_to?(:railtie_namespace) } end protected |