aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.travis.yml2
-rw-r--r--actionview/CHANGELOG.md7
-rw-r--r--actionview/lib/action_view/helpers/tags/label.rb68
-rw-r--r--actionview/test/template/form_helper_test.rb9
-rw-r--r--activemodel/lib/active_model/model.rb14
-rw-r--r--activemodel/test/cases/validations_test.rb4
-rw-r--r--activerecord/CHANGELOG.md4
-rw-r--r--activerecord/lib/active_record/associations.rb15
-rw-r--r--activerecord/lib/active_record/attribute_methods/dirty.rb21
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb4
-rw-r--r--activerecord/lib/active_record/core.rb6
-rw-r--r--activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb12
-rw-r--r--activerecord/test/cases/base_test.rb14
-rw-r--r--activerecord/test/cases/finder_test.rb42
-rw-r--r--activerecord/test/cases/relations_test.rb4
-rw-r--r--activesupport/test/caching_test.rb2
-rw-r--r--activesupport/test/transliterate_test.rb2
-rw-r--r--guides/source/4_2_release_notes.md43
18 files changed, 192 insertions, 81 deletions
diff --git a/.travis.yml b/.travis.yml
index 3851070b41..c9755ae68e 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -11,7 +11,7 @@ rvm:
- jruby
env:
global:
- - JRUBY_OPTS='-J-XX:+TieredCompilation -J-XX:TieredStopAtLevel=1 -J-Djruby.compile.mode=OFF -J-Djruby.compile.invokedynamic=false -J-Xmx1024M'
+ - JRUBY_OPTS='-J-XX:+TieredCompilation -J-XX:TieredStopAtLevel=1 -J-Djruby.compile.mode=OFF -J-Djruby.compile.invokedynamic=false -J-Xmx1024M'
matrix:
- "GEM=railties"
- "GEM=ap"
diff --git a/actionview/CHANGELOG.md b/actionview/CHANGELOG.md
index 552a902349..396249ac37 100644
--- a/actionview/CHANGELOG.md
+++ b/actionview/CHANGELOG.md
@@ -1,3 +1,10 @@
+* Provide a `builder` object when using the `label` form helper in block form.
+
+ The new `builder` object responds to `translation`, allowing I18n fallback support
+ when you want to customize how a particular label is presented.
+
+ *Alex Robbin*
+
* Add I18n support for input/textarea placeholder text.
Placeholder I18n follows the same convention as `label` I18n.
diff --git a/actionview/lib/action_view/helpers/tags/label.rb b/actionview/lib/action_view/helpers/tags/label.rb
index 39b2f48c39..08a23e497e 100644
--- a/actionview/lib/action_view/helpers/tags/label.rb
+++ b/actionview/lib/action_view/helpers/tags/label.rb
@@ -2,6 +2,39 @@ module ActionView
module Helpers
module Tags # :nodoc:
class Label < Base # :nodoc:
+ class LabelBuilder # :nodoc:
+ attr_reader :object
+
+ def initialize(template_object, object_name, method_name, object, tag_value)
+ @template_object = template_object
+ @object_name = object_name
+ @method_name = method_name
+ @object = object
+ @tag_value = tag_value
+ end
+
+ def translation
+ method_and_value = @tag_value.present? ? "#{@method_name}.#{@tag_value}" : @method_name
+ @object_name.gsub!(/\[(.*)_attributes\]\[\d+\]/, '.\1')
+
+ if object.respond_to?(:to_model)
+ key = object.model_name.i18n_key
+ i18n_default = ["#{key}.#{method_and_value}".to_sym, ""]
+ end
+
+ i18n_default ||= ""
+ content = I18n.t("#{@object_name}.#{method_and_value}", :default => i18n_default, :scope => "helpers.label").presence
+
+ content ||= if object && object.class.respond_to?(:human_attribute_name)
+ object.class.human_attribute_name(method_and_value)
+ end
+
+ content ||= @method_name.humanize
+
+ content
+ end
+ end
+
def initialize(object_name, method_name, template_object, content_or_options = nil, options = nil)
options ||= {}
@@ -32,33 +65,24 @@ module ActionView
options.delete("namespace")
options["for"] = name_and_id["id"] unless options.key?("for")
- if block_given?
- content = @template_object.capture(&block)
- else
- method_and_value = tag_value.present? ? "#{@method_name}.#{tag_value}" : @method_name
- content = if @content.blank?
- @object_name.gsub!(/\[(.*)_attributes\]\[\d+\]/, '.\1')
-
- if object.respond_to?(:to_model)
- key = object.model_name.i18n_key
- i18n_default = ["#{key}.#{method_and_value}".to_sym, ""]
- end
-
- i18n_default ||= ""
- I18n.t("#{@object_name}.#{method_and_value}", :default => i18n_default, :scope => "helpers.label").presence
- else
- @content.to_s
- end
-
- content ||= if object && object.class.respond_to?(:human_attribute_name)
- object.class.human_attribute_name(method_and_value)
- end
+ builder = LabelBuilder.new(@template_object, @object_name, @method_name, @object, tag_value)
- content ||= @method_name.humanize
+ content = if block_given?
+ @template_object.capture(builder, &block)
+ elsif @content.present?
+ @content.to_s
+ else
+ render_component(builder)
end
label_tag(name_and_id["id"], content, options)
end
+
+ private
+
+ def render_component(builder)
+ builder.translation
+ end
end
end
end
diff --git a/actionview/test/template/form_helper_test.rb b/actionview/test/template/form_helper_test.rb
index 6910769183..6f82462425 100644
--- a/actionview/test/template/form_helper_test.rb
+++ b/actionview/test/template/form_helper_test.rb
@@ -319,6 +319,15 @@ class FormHelperTest < ActionView::TestCase
)
end
+ def test_label_with_block_and_builder
+ with_locale :label do
+ assert_dom_equal(
+ '<label for="post_body"><b>Write entire text here</b></label>',
+ label(:post, :body) { |b| "<b>#{b.translation}</b>".html_safe }
+ )
+ end
+ end
+
def test_label_with_block_in_erb
assert_equal(
%{<label for="post_message">\n Message\n <input id="post_message" name="post[message]" type="text" />\n</label>},
diff --git a/activemodel/lib/active_model/model.rb b/activemodel/lib/active_model/model.rb
index 640024eaa1..d51d6ddcc9 100644
--- a/activemodel/lib/active_model/model.rb
+++ b/activemodel/lib/active_model/model.rb
@@ -56,13 +56,13 @@ module ActiveModel
# refer to the specific modules included in <tt>ActiveModel::Model</tt>
# (see below).
module Model
- def self.included(base) #:nodoc:
- base.class_eval do
- extend ActiveModel::Naming
- extend ActiveModel::Translation
- include ActiveModel::Validations
- include ActiveModel::Conversion
- end
+ extend ActiveSupport::Concern
+ include ActiveModel::Validations
+ include ActiveModel::Conversion
+
+ included do
+ extend ActiveModel::Naming
+ extend ActiveModel::Translation
end
# Initializes a new model with the given +params+.
diff --git a/activemodel/test/cases/validations_test.rb b/activemodel/test/cases/validations_test.rb
index ba0aacc2a5..d876f73052 100644
--- a/activemodel/test/cases/validations_test.rb
+++ b/activemodel/test/cases/validations_test.rb
@@ -18,11 +18,11 @@ class ValidationsTest < ActiveModel::TestCase
def test_single_field_validation
r = Reply.new
r.title = "There's no content!"
- assert r.invalid?, "A reply without content shouldn't be savable"
+ assert r.invalid?, "A reply without content should be invalid"
assert r.after_validation_performed, "after_validation callback should be called"
r.content = "Messa content!"
- assert r.valid?, "A reply with content should be savable"
+ assert r.valid?, "A reply with content should be valid"
assert r.after_validation_performed, "after_validation callback should be called"
end
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 356911d340..b9af8584ae 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,3 +1,7 @@
+* `default_sequence_name` from the PostgreSQL adapter returns a `String`.
+
+ *Yves Senn*
+
* Fixed a regression where whitespaces were stripped from DISTINCT queries in
PostgreSQL.
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index 18d4291599..18da28d480 100644
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -1604,6 +1604,21 @@ module ActiveRecord
# where("default_category = ?", category.name)
# }
#
+ # === Extensions
+ #
+ # The +extension+ argument allows you to pass a block into a
+ # has_and_belongs_to_many association. This is useful for adding new
+ # finders, creators and other factory-type methods to be used as part of
+ # the association.
+ #
+ # Extension examples:
+ # has_and_belongs_to_many :contractors do
+ # def find_or_create_by_name(name)
+ # first_name, last_name = name.split(" ", 2)
+ # find_or_create_by(first_name: first_name, last_name: last_name)
+ # end
+ # end
+ #
# === Options
#
# [:class_name]
diff --git a/activerecord/lib/active_record/attribute_methods/dirty.rb b/activerecord/lib/active_record/attribute_methods/dirty.rb
index 01f126f1b3..2f02738f6d 100644
--- a/activerecord/lib/active_record/attribute_methods/dirty.rb
+++ b/activerecord/lib/active_record/attribute_methods/dirty.rb
@@ -54,7 +54,19 @@ module ActiveRecord
end
def changed_attributes
- super.reverse_merge(attributes_changed_in_place).freeze
+ # This should only be set by methods which will call changed_attributes
+ # multiple times when it is known that the computed value cannot change.
+ if defined?(@cached_changed_attributes)
+ @cached_changed_attributes
+ else
+ super.reverse_merge(attributes_changed_in_place).freeze
+ end
+ end
+
+ def changes
+ cache_changed_attributes do
+ super
+ end
end
private
@@ -157,6 +169,13 @@ module ActiveRecord
store_original_raw_attribute(attr)
end
end
+
+ def cache_changed_attributes
+ @cached_changed_attributes = changed_attributes
+ yield
+ ensure
+ remove_instance_variable(:@cached_changed_attributes)
+ end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
index 323da7b717..767b6b614a 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
@@ -281,9 +281,9 @@ module ActiveRecord
def default_sequence_name(table_name, pk = nil) #:nodoc:
result = serial_sequence(table_name, pk || 'id')
return nil unless result
- Utils.extract_schema_qualified_name(result)
+ Utils.extract_schema_qualified_name(result).to_s
rescue ActiveRecord::StatementInvalid
- PostgreSQL::Name.new(nil, "#{table_name}_#{pk || 'id'}_seq")
+ PostgreSQL::Name.new(nil, "#{table_name}_#{pk || 'id'}_seq").to_s
end
def serial_sequence(table, column)
diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb
index d22806fbdf..82b9c79533 100644
--- a/activerecord/lib/active_record/core.rb
+++ b/activerecord/lib/active_record/core.rb
@@ -151,7 +151,7 @@ module ActiveRecord
end
def find_by(*args)
- return super if current_scope || args.length > 1 || reflect_on_all_aggregations.any?
+ return super if current_scope || !(Hash === args.first) || reflect_on_all_aggregations.any?
hash = args.first
@@ -177,6 +177,10 @@ module ActiveRecord
end
end
+ def find_by!(*args)
+ find_by(*args) or raise RecordNotFound
+ end
+
def initialize_generated_modules
super
diff --git a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb
index 01b3f350b5..a71c0dfb26 100644
--- a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb
@@ -134,18 +134,18 @@ module ActiveRecord
end
def test_default_sequence_name
- assert_equal PostgreSQL::Name.new('public', 'accounts_id_seq'),
+ assert_equal 'public.accounts_id_seq',
@connection.default_sequence_name('accounts', 'id')
- assert_equal PostgreSQL::Name.new('public', 'accounts_id_seq'),
+ assert_equal 'public.accounts_id_seq',
@connection.default_sequence_name('accounts')
end
def test_default_sequence_name_bad_table
- assert_equal PostgreSQL::Name.new(nil, 'zomg_id_seq'),
+ assert_equal 'zomg_id_seq',
@connection.default_sequence_name('zomg', 'id')
- assert_equal PostgreSQL::Name.new(nil, 'zomg_id_seq'),
+ assert_equal 'zomg_id_seq',
@connection.default_sequence_name('zomg')
end
@@ -153,7 +153,7 @@ module ActiveRecord
with_example_table do
pk, seq = @connection.pk_and_sequence_for('ex')
assert_equal 'id', pk
- assert_equal @connection.default_sequence_name('ex', 'id'), seq
+ assert_equal @connection.default_sequence_name('ex', 'id'), seq.to_s
end
end
@@ -161,7 +161,7 @@ module ActiveRecord
with_example_table 'code serial primary key' do
pk, seq = @connection.pk_and_sequence_for('ex')
assert_equal 'code', pk
- assert_equal @connection.default_sequence_name('ex', 'code'), seq
+ assert_equal @connection.default_sequence_name('ex', 'code'), seq.to_s
end
end
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index 4c0b0c868a..fb535e74fc 100644
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -1540,20 +1540,6 @@ class BasicsTest < ActiveRecord::TestCase
assert_equal "", Company.new.description
end
- ["find_by", "find_by!"].each do |meth|
- test "#{meth} delegates to scoped" do
- record = stub
-
- scope = mock
- scope.expects(meth).with(:foo, :bar).returns(record)
-
- klass = Class.new(ActiveRecord::Base)
- klass.stubs(:all => scope)
-
- assert_equal record, klass.public_send(meth, :foo, :bar)
- end
- end
-
test "scoped can take a values hash" do
klass = Class.new(ActiveRecord::Base)
assert_equal ['foo'], klass.all.merge!(select: 'foo').select_values
diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb
index b42a60fea5..befbec4e1b 100644
--- a/activerecord/test/cases/finder_test.rb
+++ b/activerecord/test/cases/finder_test.rb
@@ -1027,6 +1027,48 @@ class FinderTest < ActiveRecord::TestCase
assert_nothing_raised(ActiveRecord::StatementInvalid) { Topic.offset("3").to_a }
end
+ test "find_by with hash conditions returns the first matching record" do
+ assert_equal posts(:eager_other), Post.find_by(id: posts(:eager_other).id)
+ end
+
+ test "find_by with non-hash conditions returns the first matching record" do
+ assert_equal posts(:eager_other), Post.find_by("id = #{posts(:eager_other).id}")
+ end
+
+ test "find_by with multi-arg conditions returns the first matching record" do
+ assert_equal posts(:eager_other), Post.find_by('id = ?', posts(:eager_other).id)
+ end
+
+ test "find_by returns nil if the record is missing" do
+ assert_equal nil, Post.find_by("1 = 0")
+ end
+
+ test "find_by doesn't have implicit ordering" do
+ assert_sql(/^((?!ORDER).)*$/) { Post.find_by(id: posts(:eager_other).id) }
+ end
+
+ test "find_by! with hash conditions returns the first matching record" do
+ assert_equal posts(:eager_other), Post.find_by!(id: posts(:eager_other).id)
+ end
+
+ test "find_by! with non-hash conditions returns the first matching record" do
+ assert_equal posts(:eager_other), Post.find_by!("id = #{posts(:eager_other).id}")
+ end
+
+ test "find_by! with multi-arg conditions returns the first matching record" do
+ assert_equal posts(:eager_other), Post.find_by!('id = ?', posts(:eager_other).id)
+ end
+
+ test "find_by! doesn't have implicit ordering" do
+ assert_sql(/^((?!ORDER).)*$/) { Post.find_by!(id: posts(:eager_other).id) }
+ end
+
+ test "find_by! raises RecordNotFound if the record is missing" do
+ assert_raises(ActiveRecord::RecordNotFound) do
+ Post.find_by!("1 = 0")
+ end
+ end
+
protected
def bind(statement, *vars)
if vars.first.is_a?(Hash)
diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb
index 88df997a2f..cc6a3888e3 100644
--- a/activerecord/test/cases/relations_test.rb
+++ b/activerecord/test/cases/relations_test.rb
@@ -1570,7 +1570,7 @@ class RelationTest < ActiveRecord::TestCase
end
test "find_by doesn't have implicit ordering" do
- assert_sql(/^((?!ORDER).)*$/) { Post.find_by(author_id: 2) }
+ assert_sql(/^((?!ORDER).)*$/) { Post.all.find_by(author_id: 2) }
end
test "find_by! with hash conditions returns the first matching record" do
@@ -1586,7 +1586,7 @@ class RelationTest < ActiveRecord::TestCase
end
test "find_by! doesn't have implicit ordering" do
- assert_sql(/^((?!ORDER).)*$/) { Post.find_by!(author_id: 2) }
+ assert_sql(/^((?!ORDER).)*$/) { Post.all.find_by!(author_id: 2) }
end
test "find_by! raises RecordNotFound if the record is missing" do
diff --git a/activesupport/test/caching_test.rb b/activesupport/test/caching_test.rb
index 8287e62f4c..5945605f7b 100644
--- a/activesupport/test/caching_test.rb
+++ b/activesupport/test/caching_test.rb
@@ -939,8 +939,8 @@ class MemCacheStoreTest < ActiveSupport::TestCase
def test_read_should_return_a_different_object_id_each_time_it_is_called
@cache.write('foo', 'bar')
- assert_not_equal @cache.read('foo').object_id, @cache.read('foo').object_id
value = @cache.read('foo')
+ assert_not_equal value.object_id, @cache.read('foo').object_id
value << 'bingo'
assert_not_equal value, @cache.read('foo')
end
diff --git a/activesupport/test/transliterate_test.rb b/activesupport/test/transliterate_test.rb
index e0f85f4e7c..6833ae68a7 100644
--- a/activesupport/test/transliterate_test.rb
+++ b/activesupport/test/transliterate_test.rb
@@ -11,7 +11,7 @@ class TransliterateTest < ActiveSupport::TestCase
end
def test_transliterate_should_approximate_ascii
- # create string with range of Unicode"s western characters with
+ # create string with range of Unicode's western characters with
# diacritics, excluding the division and multiplication signs which for
# some reason or other are floating in the middle of all the letters.
string = (0xC0..0x17E).to_a.reject {|c| [0xD7, 0xF7].include?(c)}.pack("U*")
diff --git a/guides/source/4_2_release_notes.md b/guides/source/4_2_release_notes.md
index 176cdadabf..5401b34542 100644
--- a/guides/source/4_2_release_notes.md
+++ b/guides/source/4_2_release_notes.md
@@ -36,11 +36,11 @@ Major Features
### Active Job, Action Mailer #deliver_later
Active Job is a new framework in Rails 4.2. It is an adapter layer on top of
-queuing systems like Resque, Delayed Job, Sidekiq, and more. You can write your
-jobs to Active Job, and it'll run on all these queues with no changes. (It comes
-pre-configured with an inline runner.)
+queuing systems like [Resque](https://github.com/resque/resque), [Delayed Job](https://github.com/collectiveidea/delayed_job), [Sidekiq](https://github.com/mperham/sidekiq), and more. You can write your
+jobs with the Active Job API, and it'll run on all these queues with no changes
+(it comes pre-configured with an inline runner).
-Building on top of Active Job, Action Mailer now comes with a #deliver_later
+Building on top of Active Job, Action Mailer now comes with a `#deliver_later`
method, which adds your email to be sent as a job to a queue, so it doesn't
bog down the controller or model.
@@ -63,16 +63,16 @@ TODO: add some technical details
New applications generated from Rails 4.2 now comes with the Web Console gem by
default.
-Web Console is a set of debugging tools for your Rails application. It comes
-with an interactive console for every error page, a `console` view helper and
-VT100 compatible terminal.
+Web Console is a set of debugging tools for your Rails application. It will add
+an interactive console on every error page, a `console` view helper and a VT100
+compatible terminal.
The interactive console on the error pages lets you execute code where the
exception originated. It's quite handy to introspect the state that led to the
error.
-The `console` view helper launches an interactive console with the context of
-the view right on the page it's invoked on.
+The `console` view helper launches an interactive console within the context of
+the view where it is invoked.
Finally, you can launch a VT100 terminal that runs `rails console`. If you need
to create or modify existing test data, you can do that straight from the
@@ -184,7 +184,8 @@ Please refer to the [Changelog][railties] for detailed changes.
* 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)`.
+* Introduced `Rails.gem_version` as a convenience method to return
+ `Gem::Version.new(Rails.version)`.
([Pull Request](https://github.com/rails/rails/pull/14101))
@@ -265,8 +266,8 @@ Please refer to the [Changelog][action-pack] for detailed changes.
* 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.
+* `*_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.
@@ -334,7 +335,7 @@ Please refer to the [Changelog][action-mailer] for detailed changes.
### Notable changes
* Introduced `deliver_later` which enqueues a job on the application's queue
- to deliver the mailer asynchronously.
+ to deliver emails asynchronously.
([Pull Request](https://github.com/rails/rails/pull/16485))
* Added the `show_previews` configuration option for enabling mailer previews
@@ -345,9 +346,7 @@ Please refer to the [Changelog][action-mailer] for detailed changes.
Active Record
-------------
-Please refer to the
-[Changelog](https://github.com/rails/rails/blob/4-2-stable/activerecord/CHANGELOG.md)
-for detailed changes.
+Please refer to the [Changelog][active-record] for detailed changes.
### Removals
@@ -363,7 +362,7 @@ for detailed changes.
* 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.
+ `ActiveRecord`, such as for XML serialization.
([Pull Request](https://github.com/rails/rails/pull/15184))
### Deprecations
@@ -458,7 +457,7 @@ for detailed changes.
* `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.)
+ `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?`.
@@ -490,17 +489,19 @@ Please refer to the [Changelog][active-model] for detailed changes.
### Deprecations
-* Deprecated reset_#{attribute} in favor of restore_#{attribute}.
+* Deprecated `reset_#{attribute}` in favor of `restore_#{attribute}`.
([Pull Request](https://github.com/rails/rails/pull/16180))
-* Deprecated ActiveModel::Dirty#reset_changes in favor of #clear_changes_information.
+* Deprecated `ActiveModel::Dirty#reset_changes` in favor of
+ `#clear_changes_information`.
([Pull Request](https://github.com/rails/rails/pull/16180))
### Notable changes
* Introduced the `restore_attributes` method in `ActiveModel::Dirty` to restore
the changed (dirty) attributes to their previous values.
- (Pull Request [1](https://github.com/rails/rails/pull/14861), [2](https://github.com/rails/rails/pull/16180))
+ (Pull Request [1](https://github.com/rails/rails/pull/14861),
+ [2](https://github.com/rails/rails/pull/16180))
* `has_secure_password` no longer disallow blank passwords (i.e. passwords
that contains only spaces) by default.