diff options
21 files changed, 430 insertions, 274 deletions
diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index 135cf35ac0..048f2ddc35 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -394,7 +394,7 @@ module ActionMailer # implement for a custom delivery agent. # # * <tt>perform_deliveries</tt> - Determines whether emails are actually sent from Action Mailer when you - # call <tt>.deliver</tt> on an mail message or on an Action Mailer method. This is on by default but can + # call <tt>.deliver</tt> on an email message or on an Action Mailer method. This is on by default but can # be turned off to aid in functional testing. # # * <tt>deliveries</tt> - Keeps an array of all the emails sent out through the Action Mailer with diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index d63e5c4d6e..f73f5bc797 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,8 +1,19 @@ +* Preserve original path in `ShowExceptions` middleware by stashing it as + `env["action_dispatch.original_path"]` + + `ActionDispatch::ShowExceptions` overwrites `PATH_INFO` with the status code + for the exception defined in `ExceptionWrapper`, so the path + the user was visiting when an exception occurred was not previously + available to any custom exceptions_app. The original `PATH_INFO` is now + stashed in `env["action_dispatch.original_path"]`. + + *Grey Baker* + * Use `String#bytesize` instead of `String#size` when checking for cookie overflow. *Agis Anastasopoulos* - + * `render nothing: true` or rendering a `nil` body no longer add a single space to the response body. diff --git a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb index 1d4f0f89a6..f0779279c1 100644 --- a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb +++ b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb @@ -42,6 +42,7 @@ module ActionDispatch wrapper = ExceptionWrapper.new(env, exception) status = wrapper.status_code env["action_dispatch.exception"] = wrapper.exception + env["action_dispatch.original_path"] = env["PATH_INFO"] env["PATH_INFO"] = "/#{status}" response = @exceptions_app.call(env) response[1]['X-Cascade'] == 'pass' ? pass_response(status) : response diff --git a/actionpack/test/controller/new_base/render_template_test.rb b/actionpack/test/controller/new_base/render_template_test.rb index b7a9cf92f2..e87811776a 100644 --- a/actionpack/test/controller/new_base/render_template_test.rb +++ b/actionpack/test/controller/new_base/render_template_test.rb @@ -9,7 +9,7 @@ module RenderTemplate "locals.html.erb" => "The secret is <%= secret %>", "xml_template.xml.builder" => "xml.html do\n xml.p 'Hello'\nend", "with_raw.html.erb" => "Hello <%=raw '<strong>this is raw</strong>' %>", - "with_implicit_raw.html.erb" => "Hello <%== '<strong>this is also raw</strong>' %> in a html template", + "with_implicit_raw.html.erb" => "Hello <%== '<strong>this is also raw</strong>' %> in an html template", "with_implicit_raw.text.erb" => "Hello <%== '<strong>this is also raw</strong>' %> in a text template", "test/with_json.html.erb" => "<%= render :template => 'test/with_json', :formats => [:json] %>", "test/with_json.json.erb" => "<%= render :template => 'test/final', :formats => [:json] %>", @@ -114,7 +114,7 @@ module RenderTemplate get :with_implicit_raw - assert_body "Hello <strong>this is also raw</strong> in a html template" + assert_body "Hello <strong>this is also raw</strong> in an html template" assert_status 200 get :with_implicit_raw, format: 'text' diff --git a/actionpack/test/dispatch/show_exceptions_test.rb b/actionpack/test/dispatch/show_exceptions_test.rb index 38bd234f37..323fbc285e 100644 --- a/actionpack/test/dispatch/show_exceptions_test.rb +++ b/actionpack/test/dispatch/show_exceptions_test.rb @@ -37,7 +37,7 @@ class ShowExceptionsTest < ActionDispatch::IntegrationTest get "/", {}, {'action_dispatch.show_exceptions' => true} assert_response 500 assert_equal "500 error fixture\n", body - + get "/bad_params", {}, {'action_dispatch.show_exceptions' => true} assert_response 400 assert_equal "400 error fixture\n", body @@ -92,6 +92,7 @@ class ShowExceptionsTest < ActionDispatch::IntegrationTest exceptions_app = lambda do |env| assert_kind_of AbstractController::ActionNotFound, env["action_dispatch.exception"] assert_equal "/404", env["PATH_INFO"] + assert_equal "/not_found_original_exception", env["action_dispatch.original_path"] [404, { "Content-Type" => "text/plain" }, ["YOU FAILED BRO"]] end diff --git a/actionview/lib/action_view/helpers/output_safety_helper.rb b/actionview/lib/action_view/helpers/output_safety_helper.rb index b0d9c7c7f9..f03362d0f5 100644 --- a/actionview/lib/action_view/helpers/output_safety_helper.rb +++ b/actionview/lib/action_view/helpers/output_safety_helper.rb @@ -17,7 +17,7 @@ module ActionView #:nodoc: stringish.to_s.html_safe end - # This method returns a html safe string similar to what <tt>Array#join</tt> + # This method returns an html safe string similar to what <tt>Array#join</tt> # would return. The array is flattened, and all items, including # the supplied separator, are html escaped unless they are html # safe, and the returned string is marked as html safe. diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb index 917d3b9142..1d025beeef 100644 --- a/activemodel/lib/active_model/errors.rb +++ b/activemodel/lib/active_model/errors.rb @@ -23,7 +23,7 @@ module ActiveModel # attr_reader :errors # # def validate! - # errors.add(:name, "cannot be nil") if name == nil + # errors.add(:name, "cannot be nil") if name.nil? # end # # # The following methods are needed to be minimally implemented diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index d229254da4..1ba01d3618 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,10 @@ +* Fix the schema dump generated for tables without constraints and with + primary key with default value of custom PostgreSQL function result. + + Fixes #16111 + + *Andrey Novikov* + * Fix the SQL generated when a `delete_all` is run on an association to not produce an `IN` statements. diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index d9b339a1f6..7ec7eb1b24 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -717,9 +717,9 @@ module ActiveRecord # == Eager loading of associations # # Eager loading is a way to find objects of a certain class and a number of named associations. - # This is one of the easiest ways of to prevent the dreaded 1+N problem in which fetching 100 + # This is one of the easiest ways of to prevent the dreaded N+1 problem in which fetching 100 # posts that each need to display their author triggers 101 database queries. Through the - # use of eager loading, the 101 queries can be reduced to 2. + # use of eager loading, the number of queries will be reduced from 101 to 2. # # class Post < ActiveRecord::Base # belongs_to :author @@ -1587,7 +1587,7 @@ module ActiveRecord scope = nil end - habtm_reflection = ActiveRecord::Reflection::HasAndBelongsToManyReflection.new(:has_and_belongs_to_many, name, scope, options, self) + habtm_reflection = ActiveRecord::Reflection::HasAndBelongsToManyReflection.new(name, scope, options, self) builder = Builder::HasAndBelongsToMany.new name, self, options diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index 28c39bdd5c..12208aecf7 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -13,14 +13,21 @@ module ActiveRecord end def self.create(macro, name, scope, options, ar) - case macro - when :has_many, :belongs_to, :has_one - klass = options[:through] ? ThroughReflection : AssociationReflection - when :composed_of - klass = AggregateReflection - end - - klass.new(macro, name, scope, options, ar) + klass = case macro + when :composed_of + AggregateReflection + when :has_many + HasManyReflection + when :has_one + HasOneReflection + when :belongs_to + BelongsToReflection + else + raise "Unsupported Macro: #{macro}" + end + + reflection = klass.new(name, scope, options, ar) + options[:through] ? ThroughReflection.new(reflection) : reflection end def self.add_reflection(ar, name, reflection) @@ -110,6 +117,52 @@ module ActiveRecord end end + # Holds all the methods that are shared between MacroReflection, AssociationReflection + # and ThroughReflection + class AbstractReflection # :nodoc: + def table_name + klass.table_name + end + + # Returns a new, unsaved instance of the associated class. +attributes+ will + # be passed to the class's constructor. + def build_association(attributes, &block) + klass.new(attributes, &block) + end + + def quoted_table_name + klass.quoted_table_name + end + + def primary_key_type + klass.type_for_attribute(klass.primary_key) + end + + # Returns the class name for the macro. + # + # <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>'Money'</tt> + # <tt>has_many :clients</tt> returns <tt>'Client'</tt> + def class_name + @class_name ||= (options[:class_name] || derive_class_name).to_s + end + + JoinKeys = Struct.new(:key, :foreign_key) # :nodoc: + + def join_keys(assoc_klass) + if source_macro == :belongs_to + if polymorphic? + reflection_key = association_primary_key(assoc_klass) + else + reflection_key = association_primary_key + end + reflection_foreign_key = foreign_key + else + reflection_foreign_key = active_record_primary_key + reflection_key = foreign_key + end + JoinKeys.new(reflection_key, reflection_foreign_key) + end + end # Base class for AggregateReflection and AssociationReflection. Objects of # AggregateReflection and AssociationReflection are returned by the Reflection::ClassMethods. # @@ -117,7 +170,7 @@ module ActiveRecord # AggregateReflection # AssociationReflection # ThroughReflection - class MacroReflection + class MacroReflection < AbstractReflection # Returns the name of the macro. # # <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>:balance</tt> @@ -142,8 +195,7 @@ module ActiveRecord attr_reader :plural_name # :nodoc: - def initialize(macro, name, scope, options, active_record) - @macro = macro + def initialize(name, scope, options, active_record) @name = name @scope = scope @options = options @@ -167,15 +219,11 @@ module ActiveRecord # <tt>composed_of :balance, class_name: 'Money'</tt> returns the Money class # <tt>has_many :clients</tt> returns the Client class def klass - @klass ||= class_name.constantize + @klass ||= compute_class(class_name) end - # Returns the class name for the macro. - # - # <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>'Money'</tt> - # <tt>has_many :clients</tt> returns <tt>'Client'</tt> - def class_name - @class_name ||= (options[:class_name] || derive_class_name).to_s + def compute_class(name) + name.constantize end # Returns +true+ if +self+ and +other_aggregation+ have the same +name+ attribute, +active_record+ attribute, @@ -188,23 +236,6 @@ module ActiveRecord active_record == other_aggregation.active_record end - JoinKeys = Struct.new(:key, :foreign_key) # :nodoc: - - def join_keys(assoc_klass) - if source_macro == :belongs_to - if polymorphic? - reflection_key = association_primary_key(assoc_klass) - else - reflection_key = association_primary_key - end - reflection_foreign_key = foreign_key - else - reflection_key = foreign_key - reflection_foreign_key = active_record_primary_key - end - JoinKeys.new(reflection_key, reflection_foreign_key) - end - private def derive_class_name name.to_s.camelize @@ -237,15 +268,18 @@ module ActiveRecord # a new association object. Use +build_association+ or +create_association+ # instead. This allows plugins to hook into association object creation. def klass - @klass ||= active_record.send(:compute_type, class_name) + @klass ||= compute_class(class_name) + end + + def compute_class(name) + active_record.send(:compute_type, name) end attr_reader :type, :foreign_type attr_accessor :parent_reflection # [:name, Reflection] - def initialize(macro, name, scope, options, active_record) + def initialize(name, scope, options, active_record) super - @collection = macro == :has_many @automatic_inverse_of = nil @type = options[:as] && "#{options[:as]}_type" @foreign_type = options[:foreign_type] || "#{name}_type" @@ -264,24 +298,10 @@ module ActiveRecord } end - # Returns a new, unsaved instance of the associated class. +attributes+ will - # be passed to the class's constructor. - def build_association(attributes, &block) - klass.new(attributes, &block) - end - def constructable? # :nodoc: @constructable end - def table_name - klass.table_name - end - - def quoted_table_name - klass.quoted_table_name - end - def join_table @join_table ||= options[:join_table] || derive_join_table end @@ -290,10 +310,6 @@ module ActiveRecord @foreign_key ||= options[:foreign_key] || derive_foreign_key end - def primary_key_type - klass.type_for_attribute(klass.primary_key) - end - def association_foreign_key @association_foreign_key ||= options[:association_foreign_key] || class_name.foreign_key end @@ -396,7 +412,7 @@ Joining, Preloading and eager loading of these associations is deprecated and wi # association. Returns +true+ if the +macro+ is either +has_many+ or # +has_and_belongs_to_many+, +false+ otherwise. def collection? - @collection + false end # Returns whether or not the association should be validated as part of @@ -560,22 +576,57 @@ Joining, Preloading and eager loading of these associations is deprecated and wi end end + class HasManyReflection < AssociationReflection #:nodoc: + def initialize(name, scope, options, active_record) + @macro = :has_many + super(name, scope, options, active_record) + end + + def collection? + true + end + end + + class HasOneReflection < AssociationReflection #:nodoc: + def initialize(name, scope, options, active_record) + @macro = :has_one + super(name, scope, options, active_record) + end + end + + class BelongsToReflection < AssociationReflection #:nodoc: + def initialize(name, scope, options, active_record) + @macro = :belongs_to + super(name, scope, options, active_record) + end + end + class HasAndBelongsToManyReflection < AssociationReflection #:nodoc: - def initialize(macro, name, scope, options, active_record) + def initialize(name, scope, options, active_record) + @macro = :has_and_belongs_to_many super - @collection = true + end + + def collection? + true end end # Holds all the meta-data about a :through association as it was specified # in the Active Record class. - class ThroughReflection < AssociationReflection #:nodoc: + class ThroughReflection < AbstractReflection #:nodoc: + attr_reader :delegate_reflection delegate :foreign_key, :foreign_type, :association_foreign_key, :active_record_primary_key, :type, :to => :source_reflection - def initialize(macro, name, scope, options, active_record) - super - @source_reflection_name = options[:source] + def initialize(delegate_reflection) + @delegate_reflection = delegate_reflection + @klass = delegate_reflection.options[:class] + @source_reflection_name = delegate_reflection.options[:source] + end + + def klass + @klass ||= delegate_reflection.compute_class(class_name) end # Returns the source of the through reflection. It checks both a singularized @@ -777,15 +828,25 @@ directive on your declaration like: protected - def actual_source_reflection # FIXME: this is a horrible name - source_reflection.actual_source_reflection - end + def actual_source_reflection # FIXME: this is a horrible name + source_reflection.send(:actual_source_reflection) + end + + def primary_key(klass) + klass.primary_key || raise(UnknownPrimaryKey.new(klass)) + end private def derive_class_name # get the class_name of the belongs_to association of the through reflection options[:source_type] || source_reflection.class_name end + + delegate_methods = AssociationReflection.public_instance_methods - + public_instance_methods + + delegate(*delegate_methods, to: :delegate_reflection) + end end end diff --git a/activerecord/lib/active_record/schema_dumper.rb b/activerecord/lib/active_record/schema_dumper.rb index 64bc68eefd..a94364bde1 100644 --- a/activerecord/lib/active_record/schema_dumper.rb +++ b/activerecord/lib/active_record/schema_dumper.rb @@ -120,7 +120,8 @@ HEADER # first dump primary key column if @connection.respond_to?(:pk_and_sequence_for) pk, _ = @connection.pk_and_sequence_for(table) - elsif @connection.respond_to?(:primary_key) + end + if !pk && @connection.respond_to?(:primary_key) pk = @connection.primary_key(table) end diff --git a/activerecord/test/cases/adapters/postgresql/uuid_test.rb b/activerecord/test/cases/adapters/postgresql/uuid_test.rb index f5b199b46a..df8faff546 100644 --- a/activerecord/test/cases/adapters/postgresql/uuid_test.rb +++ b/activerecord/test/cases/adapters/postgresql/uuid_test.rb @@ -87,10 +87,26 @@ class PostgresqlUUIDGenerationTest < ActiveRecord::TestCase t.string 'name' t.uuid 'other_uuid', default: 'uuid_generate_v4()' end + + # Create custom PostgreSQL function to generate UUIDs + # to test dumping tables which columns have defaults with custom functions + connection.execute <<-SQL + CREATE OR REPLACE FUNCTION my_uuid_generator() RETURNS uuid + AS $$ SELECT * FROM uuid_generate_v4() $$ + LANGUAGE SQL VOLATILE; + SQL + + # Create such a table with custom function as default value generator + connection.create_table('pg_uuids_2', id: :uuid, default: 'my_uuid_generator()') do |t| + t.string 'name' + t.uuid 'other_uuid_2', default: 'my_uuid_generator()' + end end teardown do drop_table "pg_uuids" + drop_table 'pg_uuids_2' + connection.execute 'DROP FUNCTION IF EXISTS my_uuid_generator();' end if ActiveRecord::Base.connection.supports_extensions? @@ -122,6 +138,13 @@ class PostgresqlUUIDGenerationTest < ActiveRecord::TestCase assert_match(/\bcreate_table "pg_uuids", id: :uuid, default: "uuid_generate_v1\(\)"/, schema.string) assert_match(/t\.uuid "other_uuid", default: "uuid_generate_v4\(\)"/, schema.string) end + + def test_schema_dumper_for_uuid_primary_key_with_custom_default + schema = StringIO.new + ActiveRecord::SchemaDumper.dump(connection, schema) + assert_match(/\bcreate_table "pg_uuids_2", id: :uuid, default: "my_uuid_generator\(\)"/, schema.string) + assert_match(/t\.uuid "other_uuid_2", default: "my_uuid_generator\(\)"/, schema.string) + end end end diff --git a/activerecord/test/cases/reflection_test.rb b/activerecord/test/cases/reflection_test.rb index b41a7ee787..84abaf0291 100644 --- a/activerecord/test/cases/reflection_test.rb +++ b/activerecord/test/cases/reflection_test.rb @@ -87,7 +87,7 @@ class ReflectionTest < ActiveRecord::TestCase end def test_reflection_klass_for_nested_class_name - reflection = MacroReflection.new(:company, nil, nil, { :class_name => 'MyApplication::Business::Company' }, ActiveRecord::Base) + reflection = ActiveRecord::Reflection.create(:has_many, nil, nil, { :class_name => 'MyApplication::Business::Company' }, ActiveRecord::Base) assert_nothing_raised do assert_equal MyApplication::Business::Company, reflection.klass end @@ -97,21 +97,21 @@ class ReflectionTest < ActiveRecord::TestCase ActiveSupport::Inflector.inflections do |inflect| inflect.irregular 'plural_irregular', 'plurales_irregulares' end - reflection = AssociationReflection.new(:has_many, 'plurales_irregulares', nil, {}, ActiveRecord::Base) + reflection = ActiveRecord::Reflection.create(:has_many, 'plurales_irregulares', nil, {}, ActiveRecord::Base) assert_equal 'PluralIrregular', reflection.class_name end def test_aggregation_reflection reflection_for_address = AggregateReflection.new( - :composed_of, :address, nil, { :mapping => [ %w(address_street street), %w(address_city city), %w(address_country country) ] }, Customer + :address, nil, { :mapping => [ %w(address_street street), %w(address_city city), %w(address_country country) ] }, Customer ) reflection_for_balance = AggregateReflection.new( - :composed_of, :balance, nil, { :class_name => "Money", :mapping => %w(balance amount) }, Customer + :balance, nil, { :class_name => "Money", :mapping => %w(balance amount) }, Customer ) reflection_for_gps_location = AggregateReflection.new( - :composed_of, :gps_location, nil, { }, Customer + :gps_location, nil, { }, Customer ) assert Customer.reflect_on_all_aggregations.include?(reflection_for_gps_location) @@ -135,7 +135,7 @@ class ReflectionTest < ActiveRecord::TestCase end def test_has_many_reflection - reflection_for_clients = AssociationReflection.new(:has_many, :clients, nil, { :order => "id", :dependent => :destroy }, Firm) + reflection_for_clients = ActiveRecord::Reflection.create(:has_many, :clients, nil, { :order => "id", :dependent => :destroy }, Firm) assert_equal reflection_for_clients, Firm.reflect_on_association(:clients) @@ -147,7 +147,7 @@ class ReflectionTest < ActiveRecord::TestCase end def test_has_one_reflection - reflection_for_account = AssociationReflection.new(:has_one, :account, nil, { :foreign_key => "firm_id", :dependent => :destroy }, Firm) + reflection_for_account = ActiveRecord::Reflection.create(:has_one, :account, nil, { :foreign_key => "firm_id", :dependent => :destroy }, Firm) assert_equal reflection_for_account, Firm.reflect_on_association(:account) assert_equal Account, Firm.reflect_on_association(:account).klass @@ -284,12 +284,12 @@ class ReflectionTest < ActiveRecord::TestCase end def test_association_primary_key_raises_when_missing_primary_key - reflection = ActiveRecord::Reflection::AssociationReflection.new(:fuu, :edge, nil, {}, Author) + reflection = ActiveRecord::Reflection.create(:has_many, :edge, nil, {}, Author) assert_raises(ActiveRecord::UnknownPrimaryKey) { reflection.association_primary_key } through = Class.new(ActiveRecord::Reflection::ThroughReflection) { define_method(:source_reflection) { reflection } - }.new(:fuu, :edge, nil, {}, Author) + }.new(reflection) assert_raises(ActiveRecord::UnknownPrimaryKey) { through.association_primary_key } end @@ -299,7 +299,7 @@ class ReflectionTest < ActiveRecord::TestCase end def test_active_record_primary_key_raises_when_missing_primary_key - reflection = ActiveRecord::Reflection::AssociationReflection.new(:fuu, :author, nil, {}, Edge) + reflection = ActiveRecord::Reflection.create(:has_many, :author, nil, {}, Edge) assert_raises(ActiveRecord::UnknownPrimaryKey) { reflection.active_record_primary_key } end @@ -317,32 +317,28 @@ class ReflectionTest < ActiveRecord::TestCase end def test_default_association_validation - assert AssociationReflection.new(:has_many, :clients, nil, {}, Firm).validate? + assert ActiveRecord::Reflection.create(:has_many, :clients, nil, {}, Firm).validate? - assert !AssociationReflection.new(:has_one, :client, nil, {}, Firm).validate? - assert !AssociationReflection.new(:belongs_to, :client, nil, {}, Firm).validate? - assert !AssociationReflection.new(:has_and_belongs_to_many, :clients, nil, {}, Firm).validate? + assert !ActiveRecord::Reflection.create(:has_one, :client, nil, {}, Firm).validate? + assert !ActiveRecord::Reflection.create(:belongs_to, :client, nil, {}, Firm).validate? end def test_always_validate_association_if_explicit - assert AssociationReflection.new(:has_one, :client, nil, { :validate => true }, Firm).validate? - assert AssociationReflection.new(:belongs_to, :client, nil, { :validate => true }, Firm).validate? - assert AssociationReflection.new(:has_many, :clients, nil, { :validate => true }, Firm).validate? - assert AssociationReflection.new(:has_and_belongs_to_many, :clients, nil, { :validate => true }, Firm).validate? + assert ActiveRecord::Reflection.create(:has_one, :client, nil, { :validate => true }, Firm).validate? + assert ActiveRecord::Reflection.create(:belongs_to, :client, nil, { :validate => true }, Firm).validate? + assert ActiveRecord::Reflection.create(:has_many, :clients, nil, { :validate => true }, Firm).validate? end def test_validate_association_if_autosave - assert AssociationReflection.new(:has_one, :client, nil, { :autosave => true }, Firm).validate? - assert AssociationReflection.new(:belongs_to, :client, nil, { :autosave => true }, Firm).validate? - assert AssociationReflection.new(:has_many, :clients, nil, { :autosave => true }, Firm).validate? - assert AssociationReflection.new(:has_and_belongs_to_many, :clients, nil, { :autosave => true }, Firm).validate? + assert ActiveRecord::Reflection.create(:has_one, :client, nil, { :autosave => true }, Firm).validate? + assert ActiveRecord::Reflection.create(:belongs_to, :client, nil, { :autosave => true }, Firm).validate? + assert ActiveRecord::Reflection.create(:has_many, :clients, nil, { :autosave => true }, Firm).validate? end def test_never_validate_association_if_explicit - assert !AssociationReflection.new(:has_one, :client, nil, { :autosave => true, :validate => false }, Firm).validate? - assert !AssociationReflection.new(:belongs_to, :client, nil, { :autosave => true, :validate => false }, Firm).validate? - assert !AssociationReflection.new(:has_many, :clients, nil, { :autosave => true, :validate => false }, Firm).validate? - assert !AssociationReflection.new(:has_and_belongs_to_many, :clients, nil, { :autosave => true, :validate => false }, Firm).validate? + assert !ActiveRecord::Reflection.create(:has_one, :client, nil, { :autosave => true, :validate => false }, Firm).validate? + assert !ActiveRecord::Reflection.create(:belongs_to, :client, nil, { :autosave => true, :validate => false }, Firm).validate? + assert !ActiveRecord::Reflection.create(:has_many, :clients, nil, { :autosave => true, :validate => false }, Firm).validate? end def test_foreign_key @@ -364,11 +360,11 @@ class ReflectionTest < ActiveRecord::TestCase category = Struct.new(:table_name, :pluralize_table_names).new('categories', true) product = Struct.new(:table_name, :pluralize_table_names).new('products', true) - reflection = AssociationReflection.new(:has_and_belongs_to_many, :categories, nil, {}, product) + reflection = ActiveRecord::Reflection.create(:has_many, :categories, nil, {}, product) reflection.stubs(:klass).returns(category) assert_equal 'categories_products', reflection.join_table - reflection = AssociationReflection.new(:has_and_belongs_to_many, :products, nil, {}, category) + reflection = ActiveRecord::Reflection.create(:has_many, :products, nil, {}, category) reflection.stubs(:klass).returns(product) assert_equal 'categories_products', reflection.join_table end @@ -377,11 +373,11 @@ class ReflectionTest < ActiveRecord::TestCase category = Struct.new(:table_name, :pluralize_table_names).new('catalog_categories', true) product = Struct.new(:table_name, :pluralize_table_names).new('catalog_products', true) - reflection = AssociationReflection.new(:has_and_belongs_to_many, :categories, nil, {}, product) + reflection = ActiveRecord::Reflection.create(:has_many, :categories, nil, {}, product) reflection.stubs(:klass).returns(category) assert_equal 'catalog_categories_products', reflection.join_table - reflection = AssociationReflection.new(:has_and_belongs_to_many, :products, nil, {}, category) + reflection = ActiveRecord::Reflection.create(:has_many, :products, nil, {}, category) reflection.stubs(:klass).returns(product) assert_equal 'catalog_categories_products', reflection.join_table end @@ -390,11 +386,11 @@ class ReflectionTest < ActiveRecord::TestCase category = Struct.new(:table_name, :pluralize_table_names).new('catalog_categories', true) page = Struct.new(:table_name, :pluralize_table_names).new('content_pages', true) - reflection = AssociationReflection.new(:has_and_belongs_to_many, :categories, nil, {}, page) + reflection = ActiveRecord::Reflection.create(:has_many, :categories, nil, {}, page) reflection.stubs(:klass).returns(category) assert_equal 'catalog_categories_content_pages', reflection.join_table - reflection = AssociationReflection.new(:has_and_belongs_to_many, :pages, nil, {}, category) + reflection = ActiveRecord::Reflection.create(:has_many, :pages, nil, {}, category) reflection.stubs(:klass).returns(page) assert_equal 'catalog_categories_content_pages', reflection.join_table end @@ -403,11 +399,11 @@ class ReflectionTest < ActiveRecord::TestCase category = Struct.new(:table_name, :pluralize_table_names).new('categories', true) product = Struct.new(:table_name, :pluralize_table_names).new('products', true) - reflection = AssociationReflection.new(:has_and_belongs_to_many, :categories, nil, { :join_table => 'product_categories' }, product) + reflection = ActiveRecord::Reflection.create(:has_many, :categories, nil, { :join_table => 'product_categories' }, product) reflection.stubs(:klass).returns(category) assert_equal 'product_categories', reflection.join_table - reflection = AssociationReflection.new(:has_and_belongs_to_many, :products, nil, { :join_table => 'product_categories' }, category) + reflection = ActiveRecord::Reflection.create(:has_many, :products, nil, { :join_table => 'product_categories' }, category) reflection.stubs(:klass).returns(product) assert_equal 'product_categories', reflection.join_table end diff --git a/activerecord/test/models/face.rb b/activerecord/test/models/face.rb index 3d7f0626e2..91e46f83e5 100644 --- a/activerecord/test/models/face.rb +++ b/activerecord/test/models/face.rb @@ -1,7 +1,8 @@ class Face < ActiveRecord::Base belongs_to :man, :inverse_of => :face belongs_to :polymorphic_man, :polymorphic => true, :inverse_of => :polymorphic_face - belongs_to :polymorphic_man_without_inverse, :polymorphic => true + # Oracle identifier lengh is limited to 30 bytes or less, `polymorphic` renamed `poly` + belongs_to :poly_man_without_inverse, :polymorphic => true # These is a "broken" inverse_of for the purposes of testing belongs_to :horrible_man, :class_name => 'Man', :inverse_of => :horrible_face belongs_to :horrible_polymorphic_man, :polymorphic => true, :inverse_of => :horrible_polymorphic_face diff --git a/activerecord/test/models/man.rb b/activerecord/test/models/man.rb index a26491ce61..4fbb6b226b 100644 --- a/activerecord/test/models/man.rb +++ b/activerecord/test/models/man.rb @@ -1,7 +1,7 @@ class Man < ActiveRecord::Base has_one :face, :inverse_of => :man has_one :polymorphic_face, :class_name => 'Face', :as => :polymorphic_man, :inverse_of => :polymorphic_man - has_one :polymorphic_face_without_inverse, :class_name => 'Face', :as => :polymorphic_man_without_inverse + has_one :polymorphic_face_without_inverse, :class_name => 'Face', :as => :poly_man_without_inverse has_many :interests, :inverse_of => :man has_many :polymorphic_interests, :class_name => 'Interest', :as => :polymorphic_man, :inverse_of => :polymorphic_man # These are "broken" inverse_of associations for the purposes of testing diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index 932c9ba5d9..146190fc19 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -781,8 +781,8 @@ ActiveRecord::Schema.define do t.integer :man_id t.integer :polymorphic_man_id t.string :polymorphic_man_type - t.integer :polymorphic_man_without_inverse_id - t.string :polymorphic_man_without_inverse_type + t.integer :poly_man_without_inverse_id + t.string :poly_man_without_inverse_type t.integer :horrible_polymorphic_man_id t.string :horrible_polymorphic_man_type end diff --git a/activesupport/lib/active_support/core_ext/hash/keys.rb b/activesupport/lib/active_support/core_ext/hash/keys.rb index 8657f34be2..f4105f66b0 100644 --- a/activesupport/lib/active_support/core_ext/hash/keys.rb +++ b/activesupport/lib/active_support/core_ext/hash/keys.rb @@ -6,7 +6,8 @@ class Hash # hash.transform_keys{ |key| key.to_s.upcase } # # => {"NAME"=>"Rob", "AGE"=>"28"} def transform_keys - result = {} + return enum_for(:transform_keys) unless block_given? + result = self.class.new each_key do |key| result[yield(key)] = self[key] end @@ -16,6 +17,7 @@ class Hash # Destructively convert all keys using the block operations. # Same as transform_keys but modifies +self+. def transform_keys! + return enum_for(:transform_keys!) unless block_given? keys.each do |key| self[yield(key)] = delete(key) end diff --git a/activesupport/lib/active_support/core_ext/hash/transform_values.rb b/activesupport/lib/active_support/core_ext/hash/transform_values.rb index 6ff7e91212..e9bcce761f 100644 --- a/activesupport/lib/active_support/core_ext/hash/transform_values.rb +++ b/activesupport/lib/active_support/core_ext/hash/transform_values.rb @@ -4,7 +4,8 @@ class Hash # # { a: 1, b: 2, c: 3 }.transform_values { |x| x * 2 } # # => { a: 2, b: 4, c: 6 } - def transform_values(&block) + def transform_values + return enum_for(:transform_values) unless block_given? result = self.class.new each do |key, value| result[key] = yield(value) @@ -14,6 +15,7 @@ class Hash # Destructive +transform_values+ def transform_values! + return enum_for(:transform_values!) unless block_given? each do |key, value| self[key] = yield(value) end diff --git a/activesupport/test/core_ext/hash/transform_keys_test.rb b/activesupport/test/core_ext/hash/transform_keys_test.rb new file mode 100644 index 0000000000..a7e12117f3 --- /dev/null +++ b/activesupport/test/core_ext/hash/transform_keys_test.rb @@ -0,0 +1,32 @@ +require 'abstract_unit' +require 'active_support/core_ext/hash/keys' + +class TransformKeysTest < ActiveSupport::TestCase + test "transform_keys returns a new hash with the keys computed from the block" do + original = { a: 'a', b: 'b' } + mapped = original.transform_keys { |k| "#{k}!".to_sym } + + assert_equal({ a: 'a', b: 'b' }, original) + assert_equal({ a!: 'a', b!: 'b' }, mapped) + end + + test "transform_keys! modifies the keys of the original" do + original = { a: 'a', b: 'b' } + mapped = original.transform_keys! { |k| "#{k}!".to_sym } + + assert_equal({ a!: 'a', b!: 'b' }, original) + assert_same original, mapped + end + + test "transform_keys returns an Enumerator if no block is given" do + original = { a: 'a', b: 'b' } + enumerator = original.transform_keys + assert_equal Enumerator, enumerator.class + end + + test "transform_keys is chainable with Enumerable methods" do + original = { a: 'a', b: 'b' } + mapped = original.transform_keys.with_index { |k, i| [k, i].join.to_sym } + assert_equal({ a0: 'a', b1: 'b' }, mapped) + end +end diff --git a/activesupport/test/core_ext/hash/transform_values_test.rb b/activesupport/test/core_ext/hash/transform_values_test.rb index 4449c94701..45ed11fef7 100644 --- a/activesupport/test/core_ext/hash/transform_values_test.rb +++ b/activesupport/test/core_ext/hash/transform_values_test.rb @@ -46,4 +46,16 @@ class TransformValuesTest < ActiveSupport::TestCase assert_equal 'a!', mapped[:a] assert_nil mapped[:b] end + + test "transform_values returns an Enumerator if no block is given" do + original = { a: 'a', b: 'b' } + enumerator = original.transform_values + assert_equal Enumerator, enumerator.class + end + + test "transform_values is chainable with Enumerable methods" do + original = { a: 'a', b: 'b' } + mapped = original.transform_values.with_index { |v, i| [v, i].join } + assert_equal({ a: 'a0', b: 'b1' }, mapped) + end end diff --git a/guides/source/4_2_release_notes.md b/guides/source/4_2_release_notes.md index 2729ca6588..12db528b91 100644 --- a/guides/source/4_2_release_notes.md +++ b/guides/source/4_2_release_notes.md @@ -59,31 +59,32 @@ Please refer to the [Changelog][railties] for detailed changes. ### Removals -* The `rails application` command has been removed without replacement. - ([Pull Request](https://github.com/rails/rails/pull/11616)) +* The `rails application` command has been removed without replacement. + ([Pull Request](https://github.com/rails/rails/pull/11616)) ### Deprecations -* Deprecated `Rails::Rack::LogTailer` without replacement. - ([Commit](https://github.com/rails/rails/commit/84a13e019e93efaa8994b3f8303d635a7702dbce)) +* Deprecated `Rails::Rack::LogTailer` without replacement. + ([Commit](https://github.com/rails/rails/commit/84a13e019e93efaa8994b3f8303d635a7702dbce)) ### Notable changes -* Introduced `--skip-gems` option in the app generator to skip gems such as - `turbolinks` and `coffee-rails` that does not have their own specific flags. - ([Commit](https://github.com/rails/rails/commit/10565895805887d4faf004a6f71219da177f78b7)) +* Introduced `--skip-gems` option in the app generator to skip gems such as + `turbolinks` and `coffee-rails` that does not have their own specific flags. + ([Commit](https://github.com/rails/rails/commit/10565895805887d4faf004a6f71219da177f78b7)) -* Introduced `bin/setup` script to bootstrap an application. - ([Pull Request](https://github.com/rails/rails/pull/15189)) +* Introduced `bin/setup` script to bootstrap an application. + ([Pull Request](https://github.com/rails/rails/pull/15189)) -* Changed default value for `config.assets.digest` to `true` in development. - ([Pull Request](https://github.com/rails/rails/pull/15155)) +* Changed default value for `config.assets.digest` to `true` in development. + ([Pull Request](https://github.com/rails/rails/pull/15155)) -* Introduced an API to register new extensions for `rake notes`. - ([Pull Request](https://github.com/rails/rails/pull/14379)) +* Introduced an API to register new extensions for `rake notes`. + ([Pull Request](https://github.com/rails/rails/pull/14379)) + +* Introduced `Rails.gem_version` as a convenience method to return `Gem::Version.new(Rails.version)`. + ([Pull Request](https://github.com/rails/rails/pull/14101)) -* Introduced `Rails.gem_version` as a convenience method to return `Gem::Version.new(Rails.version)`. - ([Pull Request](https://github.com/rails/rails/pull/14101)) Action Pack ----------- @@ -92,8 +93,8 @@ Please refer to the [Changelog][action-pack] for detailed changes. ### Deprecations -* Deprecated support for setting the `:to` option of a router to a symbol or a - string that does not contain a `#` character: +* Deprecated support for setting the `:to` option of a router to a symbol or a + string that does not contain a `#` character: ```ruby get '/posts', to: MyRackApp => (No change necessary) @@ -106,17 +107,17 @@ Please refer to the [Changelog][action-pack] for detailed changes. ### Notable changes -* `render nothing: true` or rendering a `nil` body no longer add a single space - padding to the response body. - ([Pull Request](https://github.com/rails/rails/pull/14883)) +* `render nothing: true` or rendering a `nil` body no longer add a single + space padding to the response body. + ([Pull Request](https://github.com/rails/rails/pull/14883)) -* Introduced the `always_permitted_parameters` option to configure which - parameters are permitted globally. The default value of this configuration is - `['controller', 'action']`. - ([Pull Request](https://github.com/rails/rails/pull/15933)) +* Introduced the `always_permitted_parameters` option to configure which + parameters are permitted globally. The default value of this configuration + is `['controller', 'action']`. + ([Pull Request](https://github.com/rails/rails/pull/15933)) -* The `*_filter` family methods has been removed from the documentation. Their - usage are discouraged in favor of the `*_action` family methods: +* The `*_filter` family methods has been removed from the documentation. Their + usage are discouraged in favor of the `*_action` family methods: ``` after_filter => after_action @@ -141,22 +142,21 @@ Please refer to the [Changelog][action-pack] for detailed changes. (Commit [1](https://github.com/rails/rails/commit/6c5f43bab8206747a8591435b2aa0ff7051ad3de), [2](https://github.com/rails/rails/commit/489a8f2a44dc9cea09154ee1ee2557d1f037c7d4)) +* Added HTTP method `MKCALENDAR` from RFC-4791 + ([Pull Request](https://github.com/rails/rails/pull/15121)) -* Added HTTP method `MKCALENDAR` from RFC-4791 - ([Pull Request](https://github.com/rails/rails/pull/15121)) - -* `*_fragment.action_controller` notifications now include the controller and action name - in the payload. - ([Pull Request](https://github.com/rails/rails/pull/14137)) +* `*_fragment.action_controller` notifications now include the controller and action name + in the payload. + ([Pull Request](https://github.com/rails/rails/pull/14137)) -* Segments that are passed into URL helpers are now automatically escaped. - ([Commit](https://github.com/rails/rails/commit/5460591f0226a9d248b7b4f89186bd5553e7768f)) +* Segments that are passed into URL helpers are now automatically escaped. + ([Commit](https://github.com/rails/rails/commit/5460591f0226a9d248b7b4f89186bd5553e7768f)) -* Improved Routing Error page with fuzzy matching for route search. - ([Pull Request](https://github.com/rails/rails/pull/14619)) +* Improved Routing Error page with fuzzy matching for route search. + ([Pull Request](https://github.com/rails/rails/pull/14619)) -* Added option to disable logging of CSRF failures. - ([Pull Request](https://github.com/rails/rails/pull/14280)) +* Added option to disable logging of CSRF failures. + ([Pull Request](https://github.com/rails/rails/pull/14280)) Action View @@ -166,20 +166,20 @@ Please refer to the [Changelog][action-view] for detailed changes. ### Deprecations -* Deprecated `AbstractController::Base.parent_prefixes`. - Override `AbstractController::Base.local_prefixes` when you want to change - where to find views. - ([Pull Request](https://github.com/rails/rails/pull/15026)) +* Deprecated `AbstractController::Base.parent_prefixes`. + Override `AbstractController::Base.local_prefixes` when you want to change + where to find views. + ([Pull Request](https://github.com/rails/rails/pull/15026)) -* Deprecated `ActionView::Digestor#digest(name, format, finder, options = {})`, - arguments should be passed as a hash instead. - ([Pull Request](https://github.com/rails/rails/pull/14243)) +* Deprecated `ActionView::Digestor#digest(name, format, finder, options = {})`, + arguments should be passed as a hash instead. + ([Pull Request](https://github.com/rails/rails/pull/14243)) ### Notable changes -* The form helpers no longer generate a `<div>` element with inline CSS around - the hidden fields. - ([Pull Request](https://github.com/rails/rails/pull/14738)) +* The form helpers no longer generate a `<div>` element with inline CSS around + the hidden fields. + ([Pull Request](https://github.com/rails/rails/pull/14738)) Action Mailer @@ -189,9 +189,9 @@ Please refer to the [Changelog][action-mailer] for detailed changes. ### Notable changes -* Added the `show_previews` configuration option for enabling mailer previews - outside of the development environment. - ([Pull Request](https://github.com/rails/rails/pull/15970)) +* Added the `show_previews` configuration option for enabling mailer previews + outside of the development environment. + ([Pull Request](https://github.com/rails/rails/pull/15970)) Active Record @@ -203,111 +203,113 @@ for detailed changes. ### Removals -* Removed `cache_attributes` and friends. All attributes are cached. - ([Pull Request](https://github.com/rails/rails/pull/15429)) +* Removed `cache_attributes` and friends. All attributes are cached. + ([Pull Request](https://github.com/rails/rails/pull/15429)) -* Removed deprecated method `ActiveRecord::Base.quoted_locking_column`. - ([Pull Request](https://github.com/rails/rails/pull/15612)) +* Removed deprecated method `ActiveRecord::Base.quoted_locking_column`. + ([Pull Request](https://github.com/rails/rails/pull/15612)) -* Removed deprecated `ActiveRecord::Migrator.proper_table_name`. Use the - `proper_table_name` instance method on `ActiveRecord::Migration` instead. - ([Pull Request](https://github.com/rails/rails/pull/15512)) +* Removed deprecated `ActiveRecord::Migrator.proper_table_name`. Use the + `proper_table_name` instance method on `ActiveRecord::Migration` instead. + ([Pull Request](https://github.com/rails/rails/pull/15512)) -* Removed unused `:timestamp` type. Transparently alias it to `:datetime` - in all cases. Fixes inconsistencies when column types are sent outside of - `ActiveRecord`, such as for XML Serialization. - ([Pull Request](https://github.com/rails/rails/pull/15184)) +* Removed unused `:timestamp` type. Transparently alias it to `:datetime` + in all cases. Fixes inconsistencies when column types are sent outside of + `ActiveRecord`, such as for XML Serialization. + ([Pull Request](https://github.com/rails/rails/pull/15184)) ### Deprecations -* Deprecated broken support for automatic detection of counter caches on - `has_many :through` associations. You should instead manually specify the - counter cache on the `has_many` and `belongs_to` associations for the through - records. - ([Pull Request](https://github.com/rails/rails/pull/15754)) +* Deprecated broken support for automatic detection of counter caches on + `has_many :through` associations. You should instead manually specify the + counter cache on the `has_many` and `belongs_to` associations for the + through records. + ([Pull Request](https://github.com/rails/rails/pull/15754)) -* Deprecated `serialized_attributes` without replacement. - ([Pull Request](https://github.com/rails/rails/pull/15704)) +* Deprecated `serialized_attributes` without replacement. + ([Pull Request](https://github.com/rails/rails/pull/15704)) -* Deprecated returning `nil` from `column_for_attribute` when no column exists. - It will return a null object in Rails 5.0 - ([Pull Request](https://github.com/rails/rails/pull/15878)) +* Deprecated returning `nil` from `column_for_attribute` when no column + exists. It will return a null object in Rails 5.0 + ([Pull Request](https://github.com/rails/rails/pull/15878)) -* Deprecated using `.joins`, `.preload` and `.eager_load` with associations that - depends on the instance state (i.e. those defined with a scope that takes an - argument) without replacement. - ([Commit](https://github.com/rails/rails/commit/ed56e596a0467390011bc9d56d462539776adac1)) +* Deprecated using `.joins`, `.preload` and `.eager_load` with associations + that depends on the instance state (i.e. those defined with a scope that + takes an argument) without replacement. + ([Commit](https://github.com/rails/rails/commit/ed56e596a0467390011bc9d56d462539776adac1)) -* Deprecated passing Active Record objects to `.find` or `.exists?`. Call `#id` - on the objects first. - (Commit [1](https://github.com/rails/rails/commit/d92ae6ccca3bcfd73546d612efaea011270bd270), - [2](https://github.com/rails/rails/commit/d35f0033c7dec2b8d8b52058fb8db495d49596f7)) +* Deprecated passing Active Record objects to `.find` or `.exists?`. Call + `#id` on the objects first. + (Commit [1](https://github.com/rails/rails/commit/d92ae6ccca3bcfd73546d612efaea011270bd270), + [2](https://github.com/rails/rails/commit/d35f0033c7dec2b8d8b52058fb8db495d49596f7)) -* Deprecated half-baked support for PostgreSQL range values with excluding - beginnings. We currently map PostgreSQL ranges to Ruby ranges. This conversion - is not fully possible because the Ruby range does not support excluded - beginnings. +* Deprecated half-baked support for PostgreSQL range values with excluding + beginnings. We currently map PostgreSQL ranges to Ruby ranges. This conversion + is not fully possible because the Ruby range does not support excluded + beginnings. - The current solution of incrementing the beginning is not correct - and is now deprecated. For subtypes where we don't know how to increment - (e.g. `#succ` is not defined) it will raise an `ArgumentError` for ranges with - excluding beginnings. + The current solution of incrementing the beginning is not correct + and is now deprecated. For subtypes where we don't know how to increment + (e.g. `#succ` is not defined) it will raise an `ArgumentError` for ranges + with excluding beginnings. - ([Commit](https://github.com/rails/rails/commit/91949e48cf41af9f3e4ffba3e5eecf9b0a08bfc3)) + ([Commit](https://github.com/rails/rails/commit/91949e48cf41af9f3e4ffba3e5eecf9b0a08bfc3)) ### Notable changes -* Added a `:required` option to singular associations, which defines a - presence validation on the association. - ([Pull Request](https://github.com/rails/rails/pull/16056)) +* Added a `:required` option to singular associations, which defines a + presence validation on the association. + ([Pull Request](https://github.com/rails/rails/pull/16056)) -* Introduced `ActiveRecord::Base#validate!` that raises `RecordInvalid` if the - record is invalid. - ([Pull Request](https://github.com/rails/rails/pull/8639)) +* Introduced `ActiveRecord::Base#validate!` that raises `RecordInvalid` if the + record is invalid. + ([Pull Request](https://github.com/rails/rails/pull/8639)) -* `ActiveRecord::Base#reload` now behaves the same as `m = Model.find(m.id)`, - meaning that it no longer retains the extra attributes from custom `select`s. - ([Pull Request](https://github.com/rails/rails/pull/15866)) +* `ActiveRecord::Base#reload` now behaves the same as `m = Model.find(m.id)`, + meaning that it no longer retains the extra attributes from custom + `select`s. + ([Pull Request](https://github.com/rails/rails/pull/15866)) -* Introduced the `bin/rake db:purge` task to empty the database for the current - environment. - ([Commit](https://github.com/rails/rails/commit/e2f232aba15937a4b9d14bd91e0392c6d55be58d)) +* Introduced the `bin/rake db:purge` task to empty the database for the + current environment. + ([Commit](https://github.com/rails/rails/commit/e2f232aba15937a4b9d14bd91e0392c6d55be58d)) -* `ActiveRecord::Dirty` now detects in-place changes to mutable values. - Serialized attributes on ActiveRecord models will no longer save when - unchanged. This also works with other types such as string columns and - json columns on PostgreSQL. - (Pull Requests [1](https://github.com/rails/rails/pull/15674), - [2](https://github.com/rails/rails/pull/15786), - [3](https://github.com/rails/rails/pull/15788)) +* `ActiveRecord::Dirty` now detects in-place changes to mutable values. + Serialized attributes on ActiveRecord models will no longer save when + unchanged. This also works with other types such as string columns and json + columns on PostgreSQL. + (Pull Requests [1](https://github.com/rails/rails/pull/15674), + [2](https://github.com/rails/rails/pull/15786), + [3](https://github.com/rails/rails/pull/15788)) -* Added support for `#pretty_print` in `ActiveRecord::Base` objects. - ([Pull Request](https://github.com/rails/rails/pull/15172)) +* Added support for `#pretty_print` in `ActiveRecord::Base` objects. + ([Pull Request](https://github.com/rails/rails/pull/15172)) -* PostgreSQL and SQLite adapters no longer add a default limit of 255 characters - on string columns. - ([Pull Request](https://github.com/rails/rails/pull/14579)) +* PostgreSQL and SQLite adapters no longer add a default limit of 255 + characters on string columns. + ([Pull Request](https://github.com/rails/rails/pull/14579)) -* `sqlite3:///some/path` now resolves to the absolute system path `/some/path`. - For relative paths, use `sqlite3:some/path` instead. (Previously, `sqlite3:///some/path` - resolved to the relative path `some/path`. This behaviour was deprecated on - Rails 4.1.) - ([Pull Request](https://github.com/rails/rails/pull/14569)) +* `sqlite3:///some/path` now resolves to the absolute system path + `/some/path`. For relative paths, use `sqlite3:some/path` instead. + (Previously, `sqlite3:///some/path` resolved to the relative path + `some/path`. This behaviour was deprecated on Rails 4.1.) + ([Pull Request](https://github.com/rails/rails/pull/14569)) -* Introduced `#validate` as an alias for `#valid?`. - ([Pull Request](https://github.com/rails/rails/pull/14456)) +* Introduced `#validate` as an alias for `#valid?`. + ([Pull Request](https://github.com/rails/rails/pull/14456)) -* `#touch` now accepts multiple attributes to be touched at once. - ([Pull Request](https://github.com/rails/rails/pull/14423)) +* `#touch` now accepts multiple attributes to be touched at once. + ([Pull Request](https://github.com/rails/rails/pull/14423)) -* Added support for fractional seconds for MySQL 5.6 and above. - (Pull Request [1](https://github.com/rails/rails/pull/8240), [2](https://github.com/rails/rails/pull/14359)) +* Added support for fractional seconds for MySQL 5.6 and above. + (Pull Request [1](https://github.com/rails/rails/pull/8240), + [2](https://github.com/rails/rails/pull/14359)) -* Added support for the `citext` column type in PostgreSQL adapter. - ([Pull Request](https://github.com/rails/rails/pull/12523)) +* Added support for the `citext` column type in PostgreSQL adapter. + ([Pull Request](https://github.com/rails/rails/pull/12523)) -* Added support for user-created range types in PostgreSQL adapter. - ([Commit](https://github.com/rails/rails/commit/4cb47167e747e8f9dc12b0ddaf82bdb68c03e032)) +* Added support for user-created range types in PostgreSQL adapter. + ([Commit](https://github.com/rails/rails/commit/4cb47167e747e8f9dc12b0ddaf82bdb68c03e032)) Active Model @@ -317,21 +319,21 @@ Please refer to the [Changelog][active-model] for detailed changes. ### Removals -* Removed deprecated `Validator#setup` without replacement. - ([Pull Request](https://github.com/rails/rails/pull/15617)) +* Removed deprecated `Validator#setup` without replacement. + ([Pull Request](https://github.com/rails/rails/pull/15617)) ### Notable changes -* Introduced `undo_changes` method in `ActiveModel::Dirty` to restore the - changed (dirty) attributes to their previous values. - ([Pull Request](https://github.com/rails/rails/pull/14861)) +* Introduced `undo_changes` method in `ActiveModel::Dirty` to restore the + changed (dirty) attributes to their previous values. + ([Pull Request](https://github.com/rails/rails/pull/14861)) -* `has_secure_password` now verifies that the given password is less than 72 - characters if validations are enabled. - ([Pull Request](https://github.com/rails/rails/pull/15708)) +* `has_secure_password` now verifies that the given password is less than 72 + characters if validations are enabled. + ([Pull Request](https://github.com/rails/rails/pull/15708)) -* Introduced `#validate` as an alias for `#valid?`. - ([Pull Request](https://github.com/rails/rails/pull/14456)) +* Introduced `#validate` as an alias for `#valid?`. + ([Pull Request](https://github.com/rails/rails/pull/14456)) Active Support @@ -341,33 +343,37 @@ Please refer to the [Changelog][active-support] for detailed changes. ### Removals -* Removed deprecated `Numeric#ago`, `Numeric#until`, `Numeric#since`, - `Numeric#from_now`. ([Commit](https://github.com/rails/rails/commit/f1eddea1e3f6faf93581c43651348f48b2b7d8bb)) +* Removed deprecated `Numeric#ago`, `Numeric#until`, `Numeric#since`, + `Numeric#from_now`. + ([Commit](https://github.com/rails/rails/commit/f1eddea1e3f6faf93581c43651348f48b2b7d8bb)) -* Removed deprecated string based terminators for `ActiveSupport::Callbacks`. - ([Pull Request](https://github.com/rails/rails/pull/15100)) +* Removed deprecated string based terminators for `ActiveSupport::Callbacks`. + ([Pull Request](https://github.com/rails/rails/pull/15100)) ### Deprecations -* Deprecated `Class#superclass_delegating_accessor`, use `Class#class_attribute` - instead. ([Pull Request](https://github.com/rails/rails/pull/14271)) +* Deprecated `Class#superclass_delegating_accessor`, use + `Class#class_attribute` instead. + ([Pull Request](https://github.com/rails/rails/pull/14271)) -* Deprecated `ActiveSupport::SafeBuffer#prepend!` as `ActiveSupport::SafeBuffer#prepend` - now performs the same function. ([Pull Request](https://github.com/rails/rails/pull/14529)) +* Deprecated `ActiveSupport::SafeBuffer#prepend!` as + `ActiveSupport::SafeBuffer#prepend` now performs the same function. + ([Pull Request](https://github.com/rails/rails/pull/14529)) ### Notable changes -* Added `Hash#transform_values` and `Hash#transform_values!` to simplify a - common pattern where the values of a hash must change, but the keys are left - the same. - ([Pull Request](https://github.com/rails/rails/pull/15819)) +* Added `Hash#transform_values` and `Hash#transform_values!` to simplify a + common pattern where the values of a hash must change, but the keys are left + the same. + ([Pull Request](https://github.com/rails/rails/pull/15819)) -* The `humanize` inflector helper now strips any leading underscores. - ([Commit](https://github.com/rails/rails/commit/daaa21bc7d20f2e4ff451637423a25ff2d5e75c7)) +* The `humanize` inflector helper now strips any leading underscores. + ([Commit](https://github.com/rails/rails/commit/daaa21bc7d20f2e4ff451637423a25ff2d5e75c7)) -* Introduce `Concern#class_methods` as an alternative to `module ClassMethods`, - as well as `Kernel#concern` to avoid the `module Foo; extend ActiveSupport::Concern; end` - boilerplate. ([Commit](https://github.com/rails/rails/commit/b16c36e688970df2f96f793a759365b248b582ad)) +* Introduce `Concern#class_methods` as an alternative to + `module ClassMethods`, as well as `Kernel#concern` to avoid the + `module Foo; extend ActiveSupport::Concern; end` boilerplate. + ([Commit](https://github.com/rails/rails/commit/b16c36e688970df2f96f793a759365b248b582ad)) Credits @@ -375,8 +381,8 @@ Credits See the [full list of contributors to Rails](http://contributors.rubyonrails.org/) for -the many people who spent many hours making Rails, the stable and robust -framework it is. Kudos to all of them. +the many people who spent many hours making Rails the stable and robust +framework it is today. Kudos to all of them. [railties]: https://github.com/rails/rails/blob/4-2-stable/railties/CHANGELOG.md [action-pack]: https://github.com/rails/rails/blob/4-2-stable/actionpack/CHANGELOG.md |