New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deprecate the behavior of AR::Dirty inside of after_(create|update|save) callbacks #25337

Merged
merged 1 commit into from Nov 1, 2016

Conversation

Projects
None yet
@sgrif
Member

sgrif commented Jun 9, 2016

We pretty frequently get bug reports that "dirty is broken inside of after callbacks". Intuitively they are correct. You'd expect Model.after_save { puts changed? }; model.save to do the same thing as model.save; puts model.changed?, but it does not.

However, changing this goes much farther than just making the behavior more intuitive. There are a ton of places inside of AR that can be drastically simplified with this change. Specifically, autosave associations, timestamps, touch, counter cache, and just about anything else in AR that works with callbacks have code to try to avoid "double save" bugs which we will be able to flat out remove with this change.

We introduce two new sets of methods, both with names that are meant to be more explicit than dirty. The first set maintains the old behavior, and their names are meant to center that they are about changes that occurred during the save that just happened. They are equivalent to previous_changes when called outside of after callbacks, or once the deprecation cycle moves.

The second set is the new behavior. Their names imply that they are talking about changes from the database representation. The fact that this is what we really care about became clear when looking at BelongsTo.touch_record when tests were failing. I'm unsure that this set of methods should be in the public API. Outside of after callbacks, they are equivalent to the existing methods on dirty.

I am not married to any of the method names. Please bikeshed the shit out of them, I am open to alternatives.

Dirty itself is not deprecated, nor are the methods inside of it. They will only emit the warning when called inside of after callbacks. The scope of this breakage is pretty large, but the migration path is simple. Given how much this can improve our codebase, and considering that it makes our API more intuitive, I think it's worth doing.

Unresolved questions

Do we want the "new behavior" methods to be in the public API at all? They are straight aliases to the existing methods in dirty after 5.2/6.0. However, since we get these bug reports, someone probably does want the new behavior today.

Still left todo

I need to improve the commit messages, and move the deprecation warning up to the caller of mutation_tracker to include the exact method to call instead. The current implementation is also emitting deprecation warnings in places where the calls are valid. This will be fixed by moving the warning to the right place.

@sgrif

This comment has been minimized.

Show comment
Hide comment
@sgrif

sgrif Jun 9, 2016

Member

/cc @matthewd @rafaelfranca This is not ready to merge, but the remaining changes I have to make are to commit messages and where the deprecation warning is emitted, which shouldn't affect the discussion around this change.

Member

sgrif commented Jun 9, 2016

/cc @matthewd @rafaelfranca This is not ready to merge, but the remaining changes I have to make are to commit messages and where the deprecation warning is emitted, which shouldn't affect the discussion around this change.

end
# Alias for `changed?`
def has_changes_to_save?

This comment has been minimized.

@olivierlacan

olivierlacan Jun 9, 2016

Contributor

Wouldn't changes_to_be_saved? be more coherent with attribute_change_to_be_saved?

@olivierlacan

olivierlacan Jun 9, 2016

Contributor

Wouldn't changes_to_be_saved? be more coherent with attribute_change_to_be_saved?

This comment has been minimized.

@sgrif

sgrif Jun 13, 2016

Member

The goal was to make the difference between some of the methods more clear. Do you think that model.changes_to_be_saved? reads clearly enough? Compared to model.has_changes_to_save?

@sgrif

sgrif Jun 13, 2016

Member

The goal was to make the difference between some of the methods more clear. Do you think that model.changes_to_be_saved? reads clearly enough? Compared to model.has_changes_to_save?

end
# Alias for `changed_attributes`
def attributes_in_database

This comment has been minimized.

@olivierlacan

olivierlacan Jun 9, 2016

Contributor

persisted_attributes seems a bit more straightforward as a method name, no?

@olivierlacan

olivierlacan Jun 9, 2016

Contributor

persisted_attributes seems a bit more straightforward as a method name, no?

This comment has been minimized.

@sgrif

sgrif Jun 13, 2016

Member

I'm not happy with either one. Both names imply to me that they would return the opposite of changed (e.g. return an array of all of the attribute names which are not changed)

@sgrif

sgrif Jun 13, 2016

Member

I'm not happy with either one. Both names imply to me that they would return the opposite of changed (e.g. return an array of all of the attribute names which are not changed)

# Behaves similarly to +attribute_was+. This method is useful in after
# callbacks to get the original value of an attribute before the save that
# just occurred
def attribute_before_last_save(attr_name)

This comment has been minimized.

@olivierlacan

olivierlacan Jun 9, 2016

Contributor

Will this return the attribute before the last save or the attribute before the last change?

@olivierlacan

olivierlacan Jun 9, 2016

Contributor

Will this return the attribute before the last save or the attribute before the last change?

This comment has been minimized.

@sgrif

sgrif Jun 13, 2016

Member

Neither? It will return the value of the attribute in the database before the most recent save. This could definitely be more clear in the method name. Not sure how to express it without going full cocoa

@sgrif

sgrif Jun 13, 2016

Member

Neither? It will return the value of the attribute in the database before the most recent save. This could definitely be more clear in the method name. Not sure how to express it without going full cocoa

@olivierlacan

This comment has been minimized.

Show comment
Hide comment
@olivierlacan

olivierlacan Jun 10, 2016

Contributor

I feel like I need to wrap my mind around the name changes in the public API outside of the code to fully focus on their semantics (I hope that's not weird and distracting from the PR convo). I hope this helps.

Legend:

  • => in the meaning column means there's a non-boolean return value
  • empty columns in Prior Name mean the method didn't exist before
  • * before a new method name means it's an alias to the prior method
Prior Name New Name Meaning
attribute_was(attr_name) *attribute_in_database(attr_name) current database value of attribute
attribute_change(attr_name) *attribute_change_to_be_saved => the changed value for attribute
attribute_changed?(attr_name) *will_save_change_to_attribute?(attr_name) was attribute modified since last save
attribute_before_last_save(attr_name) => attribute value from before last save
changed *changed_attribute_names_to_save => name of attributes with changed values since last save
changes *changes_to_save => the changes to be saved to DB
changed? *has_changes_to_save? are there any changes to be saved to the DB?
changed_attributes *attributes_in_database => Hash of attribute names & new unsaved values
changed? has_changes_to_save? record has unsaved changes
saved_changes? did last save change record attributes?
saved_changes => Hash of all changes in last save
saved_change_to_attribute?(attr_name) did attribute change when record last saved?
saved_change_to_attribute(attr_name) => array of original & saved value from last save

@sgrif Please feel free to edit the above to correct any incorrect meaning.

Contributor

olivierlacan commented Jun 10, 2016

I feel like I need to wrap my mind around the name changes in the public API outside of the code to fully focus on their semantics (I hope that's not weird and distracting from the PR convo). I hope this helps.

Legend:

  • => in the meaning column means there's a non-boolean return value
  • empty columns in Prior Name mean the method didn't exist before
  • * before a new method name means it's an alias to the prior method
Prior Name New Name Meaning
attribute_was(attr_name) *attribute_in_database(attr_name) current database value of attribute
attribute_change(attr_name) *attribute_change_to_be_saved => the changed value for attribute
attribute_changed?(attr_name) *will_save_change_to_attribute?(attr_name) was attribute modified since last save
attribute_before_last_save(attr_name) => attribute value from before last save
changed *changed_attribute_names_to_save => name of attributes with changed values since last save
changes *changes_to_save => the changes to be saved to DB
changed? *has_changes_to_save? are there any changes to be saved to the DB?
changed_attributes *attributes_in_database => Hash of attribute names & new unsaved values
changed? has_changes_to_save? record has unsaved changes
saved_changes? did last save change record attributes?
saved_changes => Hash of all changes in last save
saved_change_to_attribute?(attr_name) did attribute change when record last saved?
saved_change_to_attribute(attr_name) => array of original & saved value from last save

@sgrif Please feel free to edit the above to correct any incorrect meaning.

@nateberkopec

This comment has been minimized.

Show comment
Hide comment
@nateberkopec

nateberkopec Jun 10, 2016

Contributor

Are those aliases intended to be public? Wasn't sure. If they are public, I agree, they were sort of hard to wrap my head around.

Contributor

nateberkopec commented Jun 10, 2016

Are those aliases intended to be public? Wasn't sure. If they are public, I agree, they were sort of hard to wrap my head around.

Deprecate the behavior of AR::Dirty inside of after_(create|update|sa…
…ve) callbacks

We pretty frequently get bug reports that "dirty is broken inside of
after callbacks". Intuitively they are correct. You'd expect
`Model.after_save { puts changed? }; model.save` to do the same thing as
`model.save; puts model.changed?`, but it does not.

However, changing this goes much farther than just making the behavior
more intuitive. There are a _ton_ of places inside of AR that can be
drastically simplified with this change. Specifically, autosave
associations, timestamps, touch, counter cache, and just about anything
else in AR that works with callbacks have code to try to avoid "double
save" bugs which we will be able to flat out remove with this change.

We introduce two new sets of methods, both with names that are meant to
be more explicit than dirty. The first set maintains the old behavior,
and their names are meant to center that they are about changes that
occurred during the save that just happened. They are equivalent to
`previous_changes` when called outside of after callbacks, or once the
deprecation cycle moves.

The second set is the new behavior. Their names imply that they are
talking about changes from the database representation. The fact that
this is what we really care about became clear when looking at
`BelongsTo.touch_record` when tests were failing. I'm unsure that this
set of methods should be in the public API. Outside of after callbacks,
they are equivalent to the existing methods on dirty.

Dirty itself is not deprecated, nor are the methods inside of it. They
will only emit the warning when called inside of after callbacks. The
scope of this breakage is pretty large, but the migration path is
simple. Given how much this can improve our codebase, and considering
that it makes our API more intuitive, I think it's worth doing.
@sgrif

This comment has been minimized.

Show comment
Hide comment
@sgrif

sgrif Nov 1, 2016

Member

Going to move forward with this change. If anyone has suggested improvements to the names of the new methods, please open a PR.

Member

sgrif commented Nov 1, 2016

Going to move forward with this change. If anyone has suggested improvements to the names of the new methods, please open a PR.

@sgrif sgrif merged commit 29b3b5d into rails:master Nov 1, 2016

0 of 2 checks passed

codeclimate Code Climate is analyzing this code.
Details
continuous-integration/travis-ci/pr The Travis CI build is in progress
Details

@sgrif sgrif deleted the sgrif:sg-changes-in-callbacks branch Nov 1, 2016

sgrif added a commit that referenced this pull request Nov 1, 2016

Allow `autosave: true` to be used with inverse of
With the changes in #25337, double save bugs are pretty much impossible,
so we can just lift this restriction with pretty much no change. There
were a handful of cases where we were relying on specific quirks in
tests that had to be updated. The change to has_one associations was due
to a particularly interesting test where an autosaved has_one
association was replaced with a new child, where the child failed to
save but the test wanted to check that the parent id persisted to `nil`.

I think this is almost certainly the wrong behavior, and I may change
that behavior later. But ultimately the root cause was because we never
remove the parent in memory when nullifying the child. This makes #23197
no longer needed, but it is what we'll do to fix some issues on 5.0

Close #23197

@jaredbeck jaredbeck referenced this pull request Nov 22, 2016

Merged

Constrain AR < 5.1 #892

@dhh

This comment has been minimized.

Show comment
Hide comment
@dhh

dhh Dec 14, 2016

Member

If you have a before_action callback that uses changes/attribute_changed?, then it'll emit a warning if that's triggered twice in a transaction, like on double save. Here's a failing test:

  test "changed? in before callback thats run twice in a transaction should not be deprecated" do
    klass = Class.new(ActiveRecord::Base) do
      self.table_name = "people"
      @@save_counter = 0

      before_update do
        first_name_changed?
      end

      after_update do
        @@save_counter = @@save_counter + 1
        save if @@save_counter < 2
      end
    end

    assert_not_deprecated do
      person = klass.create!(first_name: "Sean")
      person.update! first_name: 'Blue!'
    end
  end
Member

dhh commented on 16ae3db Dec 14, 2016

If you have a before_action callback that uses changes/attribute_changed?, then it'll emit a warning if that's triggered twice in a transaction, like on double save. Here's a failing test:

  test "changed? in before callback thats run twice in a transaction should not be deprecated" do
    klass = Class.new(ActiveRecord::Base) do
      self.table_name = "people"
      @@save_counter = 0

      before_update do
        first_name_changed?
      end

      after_update do
        @@save_counter = @@save_counter + 1
        save if @@save_counter < 2
      end
    end

    assert_not_deprecated do
      person = klass.create!(first_name: "Sean")
      person.update! first_name: 'Blue!'
    end
  end

This comment has been minimized.

Show comment
Hide comment
@dhh

dhh Dec 14, 2016

Member

I guess this isn't a big deal if people are just encouraged to switch to the specific checks against changes pending save / changes that have been saved. But don't feel like we're currently doing the best job of guiding in that direction.

Member

dhh replied Dec 14, 2016

I guess this isn't a big deal if people are just encouraged to switch to the specific checks against changes pending save / changes that have been saved. But don't feel like we're currently doing the best job of guiding in that direction.

This comment has been minimized.

Show comment
Hide comment
@Schwad

Schwad May 23, 2018

Just wanted to say @sgrif thanks for the discussion here, currently implementing this as a part of my 5.2 upgrade. I couldn't find places in the 5.1/5.2 Changelog/README updates where this was discussed or documented, is this commit considered the official documentation on this change?

Schwad replied May 23, 2018

Just wanted to say @sgrif thanks for the discussion here, currently implementing this as a part of my 5.2 upgrade. I couldn't find places in the 5.1/5.2 Changelog/README updates where this was discussed or documented, is this commit considered the official documentation on this change?

@krtschmr

This comment has been minimized.

Show comment
Hide comment
@krtschmr

krtschmr Apr 19, 2017

@sgrif what happened to attribute_changed? like after_save :analyze_file_async, if: :file_changed?

i can't figure the new naming. this looks ugly to me though: if: ->{ saved_change_to_attribute?(:file?) }

why we don't make if : :file_was_updated? so we make

def #{attribute}_was_updated?
  saved_change_to_attribute?(attr)
end

then we can use it `after_save :analyze_file_async, if: :file_was_updated?

krtschmr commented Apr 19, 2017

@sgrif what happened to attribute_changed? like after_save :analyze_file_async, if: :file_changed?

i can't figure the new naming. this looks ugly to me though: if: ->{ saved_change_to_attribute?(:file?) }

why we don't make if : :file_was_updated? so we make

def #{attribute}_was_updated?
  saved_change_to_attribute?(attr)
end

then we can use it `after_save :analyze_file_async, if: :file_was_updated?

@dsandstrom

This comment has been minimized.

Show comment
Hide comment
@dsandstrom

dsandstrom May 19, 2017

Are the new method names documented anywhere besides here? Maybe update http://api.rubyonrails.org/classes/ActiveModel/Dirty.html .

dsandstrom commented May 19, 2017

Are the new method names documented anywhere besides here? Maybe update http://api.rubyonrails.org/classes/ActiveModel/Dirty.html .

reneklacan added a commit to reneklacan/mastodon that referenced this pull request Jun 11, 2017

Fix Account model deprecation warnings
```
DEPRECATION WARNING: The behavior of `attribute_changed?` inside of after callbacks will be changing in the next version of Rails. The new return value will reflect the behavior of calling the method after `save` returned (e.g. the opposite of what it returns now). To maintain the current behavior, use `saved_change_to_attribute?` instead. (called from block in <class:Account> at /Users/rene/Workspace/personal/ruby/mastodon/app/models/account.rb:60)
DEPRECATION WARNING: The behavior of `attribute_changed?` inside of after callbacks will be changing in the next version of Rails. The new return value will reflect the behavior of calling the method after `save` returned (e.g. the opposite of what it returns now). To maintain the current behavior, use `saved_change_to_attribute?` instead. (called from block in <class:Account> at /Users/rene/Workspace/personal/ruby/mastodon/app/models/account.rb:60)
DEPRECATION WARNING: The behavior of `attribute_changed?` inside of after callbacks will be changing in the next version of Rails. The new return value will reflect the behavior of calling the method after `save` returned (e.g. the opposite of what it returns now). To maintain the current behavior, use `saved_change_to_attribute?` instead. (called from block in <class:Account> at /Users/rene/Workspace/personal/ruby/mastodon/app/models/account.rb:60)
DEPRECATION WARNING: The behavior of `attribute_changed?` inside of after callbacks will be changing in the next version of Rails. The new return value will reflect the behavior of calling the method after `save` returned (e.g. the opposite of what it returns now). To maintain the current behavior, use `saved_change_to_attribute?` instead. (called from block in <class:Account> at /Users/rene/Workspace/personal/ruby/mastodon/app/models/account.rb:61)
DEPRECATION WARNING: The behavior of `attribute_changed?` inside of after callbacks will be changing in the next version of Rails. The new return value will reflect the behavior of calling the method after `save` returned (e.g. the opposite of what it returns now). To maintain the current behavior, use `saved_change_to_attribute?` instead. (called from block in <class:Account> at /Users/rene/Workspace/personal/ruby/mastodon/app/models/account.rb:62)
DEPRECATION WARNING: The behavior of `attribute_changed?` inside of after callbacks will be changing in the next version of Rails. The new return value will reflect the behavior of calling the method after `save` returned (e.g. the opposite of what it returns now). To maintain the current behavior, use `saved_change_to_attribute?` instead. (called from block in <class:Account> at /Users/rene/Workspace/personal/ruby/mastodon/app/models/account.rb:63)
```

Here's PR describing changes to Dirty API rails/rails#25337

Gargron added a commit to tootsuite/mastodon that referenced this pull request Jun 11, 2017

Fix Account model deprecation warnings (#3689)
```
DEPRECATION WARNING: The behavior of `attribute_changed?` inside of after callbacks will be changing in the next version of Rails. The new return value will reflect the behavior of calling the method after `save` returned (e.g. the opposite of what it returns now). To maintain the current behavior, use `saved_change_to_attribute?` instead. (called from block in <class:Account> at /Users/rene/Workspace/personal/ruby/mastodon/app/models/account.rb:60)
DEPRECATION WARNING: The behavior of `attribute_changed?` inside of after callbacks will be changing in the next version of Rails. The new return value will reflect the behavior of calling the method after `save` returned (e.g. the opposite of what it returns now). To maintain the current behavior, use `saved_change_to_attribute?` instead. (called from block in <class:Account> at /Users/rene/Workspace/personal/ruby/mastodon/app/models/account.rb:60)
DEPRECATION WARNING: The behavior of `attribute_changed?` inside of after callbacks will be changing in the next version of Rails. The new return value will reflect the behavior of calling the method after `save` returned (e.g. the opposite of what it returns now). To maintain the current behavior, use `saved_change_to_attribute?` instead. (called from block in <class:Account> at /Users/rene/Workspace/personal/ruby/mastodon/app/models/account.rb:60)
DEPRECATION WARNING: The behavior of `attribute_changed?` inside of after callbacks will be changing in the next version of Rails. The new return value will reflect the behavior of calling the method after `save` returned (e.g. the opposite of what it returns now). To maintain the current behavior, use `saved_change_to_attribute?` instead. (called from block in <class:Account> at /Users/rene/Workspace/personal/ruby/mastodon/app/models/account.rb:61)
DEPRECATION WARNING: The behavior of `attribute_changed?` inside of after callbacks will be changing in the next version of Rails. The new return value will reflect the behavior of calling the method after `save` returned (e.g. the opposite of what it returns now). To maintain the current behavior, use `saved_change_to_attribute?` instead. (called from block in <class:Account> at /Users/rene/Workspace/personal/ruby/mastodon/app/models/account.rb:62)
DEPRECATION WARNING: The behavior of `attribute_changed?` inside of after callbacks will be changing in the next version of Rails. The new return value will reflect the behavior of calling the method after `save` returned (e.g. the opposite of what it returns now). To maintain the current behavior, use `saved_change_to_attribute?` instead. (called from block in <class:Account> at /Users/rene/Workspace/personal/ruby/mastodon/app/models/account.rb:63)
```

Here's PR describing changes to Dirty API rails/rails#25337

koteitan added a commit to koteitan/mastodon that referenced this pull request Jun 25, 2017

Fix Account model deprecation warnings (#3689)
```
DEPRECATION WARNING: The behavior of `attribute_changed?` inside of after callbacks will be changing in the next version of Rails. The new return value will reflect the behavior of calling the method after `save` returned (e.g. the opposite of what it returns now). To maintain the current behavior, use `saved_change_to_attribute?` instead. (called from block in <class:Account> at /Users/rene/Workspace/personal/ruby/mastodon/app/models/account.rb:60)
DEPRECATION WARNING: The behavior of `attribute_changed?` inside of after callbacks will be changing in the next version of Rails. The new return value will reflect the behavior of calling the method after `save` returned (e.g. the opposite of what it returns now). To maintain the current behavior, use `saved_change_to_attribute?` instead. (called from block in <class:Account> at /Users/rene/Workspace/personal/ruby/mastodon/app/models/account.rb:60)
DEPRECATION WARNING: The behavior of `attribute_changed?` inside of after callbacks will be changing in the next version of Rails. The new return value will reflect the behavior of calling the method after `save` returned (e.g. the opposite of what it returns now). To maintain the current behavior, use `saved_change_to_attribute?` instead. (called from block in <class:Account> at /Users/rene/Workspace/personal/ruby/mastodon/app/models/account.rb:60)
DEPRECATION WARNING: The behavior of `attribute_changed?` inside of after callbacks will be changing in the next version of Rails. The new return value will reflect the behavior of calling the method after `save` returned (e.g. the opposite of what it returns now). To maintain the current behavior, use `saved_change_to_attribute?` instead. (called from block in <class:Account> at /Users/rene/Workspace/personal/ruby/mastodon/app/models/account.rb:61)
DEPRECATION WARNING: The behavior of `attribute_changed?` inside of after callbacks will be changing in the next version of Rails. The new return value will reflect the behavior of calling the method after `save` returned (e.g. the opposite of what it returns now). To maintain the current behavior, use `saved_change_to_attribute?` instead. (called from block in <class:Account> at /Users/rene/Workspace/personal/ruby/mastodon/app/models/account.rb:62)
DEPRECATION WARNING: The behavior of `attribute_changed?` inside of after callbacks will be changing in the next version of Rails. The new return value will reflect the behavior of calling the method after `save` returned (e.g. the opposite of what it returns now). To maintain the current behavior, use `saved_change_to_attribute?` instead. (called from block in <class:Account> at /Users/rene/Workspace/personal/ruby/mastodon/app/models/account.rb:63)
```

Here's PR describing changes to Dirty API rails/rails#25337

tagliala added a commit to DavyJonesLocker/client_side_validations that referenced this pull request Jul 15, 2017

Validate will_save_change_to? conditionals
This commit reflects changes in ActiveRecord::Dirty API version 5.1

Ref: #710, plataformatec/devise#4574, rails/rails#25337

jfly added a commit to jfly/worldcubeassociation.org that referenced this pull request Jul 21, 2017

@jfly jfly referenced this pull request Jul 21, 2017

Merged

Rails 5.1 #1783

jfly added a commit to jfly/worldcubeassociation.org that referenced this pull request Jul 21, 2017

jfly added a commit to jfly/worldcubeassociation.org that referenced this pull request Jul 21, 2017

@jcoyne jcoyne referenced this pull request Aug 8, 2017

Closed

Deprecations misused? #30137

sarken added a commit to otwcode/otwarchive that referenced this pull request Aug 18, 2017

AO3-5034 Rails 5 dot 1 upgrade (#2980)
* Updates to Rails 5.0.3 and clears up all dependency conflicts

* Removes test_after_commit gem - now available in Rails by default

Comments out rpm_contrib - gem triggers `cannot call config on nil` in Rails,
but cannot be upgraded because the version specified in the Gemfile was the last
version because the Gem was deprecated

* Removes rpm_contrib gem for the time dealing to deal with other issues

* Uses version of google_visualr that protects from undefined method helper_method

winston/google_visualr#104

* Removes unnecessary primary key definition from RolesUser model

* Exchanges deprecated *_filter methods with *_action

* Updates post syntax for specs in api/api_works

* Fixes two typos in api works spec

* Fixes :store_location has not been defined error

skip_* actions in Rails 5 will now raise if the provided method is not defined.

plataformatec/devise#4207

* Ensures an AdminSetting will always exist when signing in an admin

* Updates to Rails 5-compatible version of Authlogic

* Fixes UserSession's inability to recognize params

Due to changes in controller params, UserSession was not recognizing the raw
params as valid login credentials. Separating the params out into a hash solved
this issue.

* Removes association rule that barred chapters from being saved on work

* Sets halt_callback_chains_on_return to false, as is Rails 5 default

* Replaces deprecated 'uniq' method on AR objects with 'distinct' in works
controller

* Adds gem to maintain controller test assertions

* Updates outdated get/post/put argument syntax in works/defautl rails actions
spec

* Uses updated Devise test helper method module in spec helper

* Adds some Rails 5 default files

* Only includes Devise helpers in controller specs

* Sets up filter chain to not halt when a method returns false

* Adds updates to callback methods responding to new callback functionality

* Updates call to resque scheduler tasks in resque.rake

* Fixes bug where work does not save when setting 'authors' attribute

* Fixes spec/lib/collectible_spec by moving observer into a model callback

* Moves observer methods to model callbacks

* Fixes organization of callbacks to prevent MySql2 bugs

* Ensures require locale is loaded when comment callbacks are fired

* Reverts work factories to old behavior that uses authors virtual attribute over
pseuds

* Ensures required locale exists in all tests

* Uses root_url instead of root_path in user_mailer view

* Ensures that AdminSetting exists in admin invitations feature spec

* Fixes test bug that didn't set the default locale as a user's preferred locale

Hard-coding '1' as the preferred_locale for a new preference makes tests nearly
impossible to maintain when the database cleaner is removing and adding Locales
on a per-test basis.

* Allows params passed by view to be sent to controller

This prevents an error indicating that the use of `params.merge` in the
AlphabetHelper is unsave. However, because the parameters will be whitelisted
once handed to the controller, this should not be a problem in the helper.

* Fixes bug where archive questions were not considered 'changed' after save

* Makes admins/admin_post_news.feature pass

* Ensures AdminSetting exists where required in admins/admin_skins feature

* Makes admins/admin_works features pass

TODO: Figure out a way to ensure AdminSetting exists wherever it's needed
without changing all of the tests like this

* Makes authenticate admins features pass

* Fixes admnin_fnok.feature failures

* Moves AdminSetting, Language, and Locale location requirement to before block

* Updates page.body check to capture expected html tags in content

* Extracts out text testing step that checks for html tags

Capybara intentionally does not see html with the `have_content` method, but
there are features in admin_post_news that are testing specifically for the
presence of user-entered html tags. In order to ensure those continue testing
what they're supposed to, I wrote a separate step specifically for features
where matching against certain html tags is part of what's under test.

* Removes monkey patch that should no longer be necessary

* Allows edit_search to be passed along to params in search results

Same concept as this commit:

littlelines@e2807bc

* Extracts html-aware step assertion versus non html-aware

* Fixes argument references in creatable module

* Fixes typo

* Fixes typo

* Fixes string concatenation and mailer template issue

* Fix bug in the Work model related to Rails 5 callback functionality changes

* Fixes undefined method in works controller

* Adds unit test to capture work saving bug on smaller scale

* Fixes validation on creatorship model

* Fixes the passing of params to a url_for method in a view

* Fixes unpermitted params passed to url in view

* Removes call to undefined method

* Updates deprecated use of uniq in several places

* burn me to the ground

* Fixes params mutation not picking up on new class name for ActionController
params

* Fixes nested parameter identifiers in strong params

* Removes unused line of code in feature step definition

* Removes outdated fix that has been addressed by other code

* Fixes bug in collection_item after_commit callback

In Rails 4.2 and before, ActiveRecord suppressed errors raised within callbacks.
Therefore it was suppressing a case in the collection_item after_commit callback
being called when a collection has been destroyed. The callback reference's the
collection's collection_preference object and calls a method on it
(email_notify). The collection_preference record has been destroyed at this
point. In 4.2 and before, AR suppressed the error and moved on. Since it's
happening in an if statement, it worked effectively to not send out the
notification and treat the error as a false case.

This PR just explicitly spells out the false case so that an error isn't raised,
since it will not be suppressed.

* Fixes reference to comment in comment callback

* Updates destroy_all call in work model

destroy_all with args is deprecated

* Removes deprecated passing of :reload as argument

* Replaces deprecated uniq with distinct

* Fixes deprecated use of Mime::HTML

* Fixes params.merge! issues on stats page

* Fixes stack level too deep error

rails/rails#28908

The *_changed? method no longer gets reset/gets reset in a different way now

* Updates outdated method

* Removes deprecated uniq and replaces it with distinct

* Fixes the use of a class in FactoryGirl lookup, which is deprecated

* Fixes use of deprecated Hash methods on Parameters objects in tag model

* Permits params to be passed from link on search results page

* Updates deprecated use of controller methods to include named args

* Updates deprecated use of 'uniq' with 'distinct'

* Updates deprecated `redirect_to :back` usage

* Fixes series controller spec failures & deprecation

* Fixes wrangling guidelines controller spec failure & remaining deprecations

* Passes an id, not an AR record, to ActiveRecord.find

* Ensures that archivist is an archivist

* Ensures that cache is cleared between tests

* Allows nested ActionController::Parameters to be manipulated in
ApplicationController

* Ensures batch urls are handed as array to api works controller

* Adds @work.destroy back to after(:all) block now that underlying problem has
been addressed

* Fixes mistake in inbox controller spec

* Ensures params[:prompt] is not nil in prompts controller spec

* Checks for blank param, instead of nil, to evade blank strings

* Changes params checking in works controller to reflect new methods of changing
params

rails/rails#26075

* Updates call to skip sanitize_params in autocomplete controller

* Fixes mistakes in mailer classes

* Comments out all observers

* Prevents error in work callback

Weird problem.

@work.destroy is triggering the *_save callbacks when running:

`RAILS_ENV=test bundle exec rspec spec/lib/works_owner_spec.rb[1:4:2:1:2]`

(when the work's owner is a collection)

This is happening on master. I'd consider it unexpected behavior, but it doesn't
seem to have any side-effects. The only reason it's causing an error now is
because Rails 5 no longer swallows errors raised in callbacks (in this case,
undefined method published_at for nil, because the work's chapters have already
been deleted by the time the *_save callbacks are re-triggered).

* Fixes expectation that expected double error

Upgrade fixed email has already been taken error to only appear once. Yay!

* Fixes reliance on specific implementation details of errors#add from Rails

* Ensures halt_callback_chains_on_return_false behaves as expected

* Fixes before_validation check on tag model

* Fixes bug

work.comments is not an AR Association Collection, it's a Relation object, and
therefore no longer responds to << as a method on work.comments. But
work.first_chapter.comments << comment does the same thing for the purposes
of the tests in requests/comments_spec.

* Makes definition of importing_for_others in works controller explicit

* Re-implements reverse_merge! in tag model method

It looks like reverse_merge! will be available for ActionController::Parameters in 5.1:

It looks like it was merged in here: rails/rails#28355

And that pull is referenced by an issue that references the current deprecationg
warning, here: rails/rails#28353

Adding reverse_merge back makes the other_a tests pass. So it seems like the
deprecation warning can be ignored.

* Pagination no longer wraps current page in a 'span' tag

* Removes unused observer files

* Removes unused commented out code

* Addresses deprecation warnings in specs

* Updates post_first_chapter method in work to save chapter as expected

* Fixed failures in chapters controller spec

* Cleans up

* Fixes deprecated use of render :nothing

* Fixes problem with the tests running too fast to catch difference in updated_at
where it matters

* Forces work.cache_key to bust when coauthor destroys their account

* Ensures updated_at comparison is not on reloaded work object

* Upgrades to Rails 5.1

Required upgrades to cucumber-rails, authlogic, acts_as_list, and
activerecord-mysql-connect as well

* Replaces alias_method_chain with module#prepend

* Specifies version of all migrations

Not entirely certain how specific this has to be, but because all of these
migrations *worked* in 4.2, it seems reasonable they can inherit from
ActiveRecord::Migration[4.2].

* Removes unnecessary initialization file

ActiveSupport.halt_callback_chains_on_return_false= is deprecated, and the
behavior exists by default in 5.1, so this initializer is no longer needed.

* Adds note about deprecated :controller and :action segments

* Updates validation with deprecated syntax in tag_nomination

* Uses much easier method of replacing alias_method_chain

* Fixes unpermitted params not being able to be converted to a hash

* Replaces deprecated code with functional equivalents

* Upgrades bullet gem

* Fixes undefined method 'destroy' for nil:NilClass

* Adds 'skip_pipeline: true' option to javascript_include_tags

* Hands the expected empty hash as params, instead of nil

* Adds `skip_pipeline: true` to stylesheet_link_tag

* Updates deprecated method calls

* Fixes controller spec failures

* Passes id to method that expects an id, instead of an AR object

* Removes `saved_change_for_attribute` from before_save callback

According to the reasoning [here](rails/rails#25337),
`changed_attribute?` methods will only raise a deprecation warning in `after_*`
callbacks. `changed_attribute?` is still perfectly legitimate used elsewhere.

* Adds `skip_pipeline: true` to image tag evoking public image

* Whitelists session params

* Changes saved_change_to_attribute back to attribute_changed? where applicable

* Changes attribute_changed? to saved_change_to_attribute? in after_* callback

* Updates _changed to saved_change_to_ in after_* callback

* Fixes Rails.cache returning incorrect hash in work tag_groups

* Fixes deprecated method in after_* callbacks

* Uses compatible globalize fork

* Adds `skip_pipeline` to all image_tags that reference image in public/

* Fixes deprecations in user model

* Removes removed 'uniq' method from AR objects

* Fixes wrong method in collection validation

* Fixes the has_one definition in tag_set saving a prompt multiple times when a
challenge signup is created

* Fixes before_save callback

* Ensures saved_change_* update is only made in after_* callbacks

* Fixes delete_all method call

* Removes _before_last_save methods

* Fixes typo

* Fixes bug in work after_save callback

Because of the change in AR's checking of dirty attributes, changing an
attribute within an after_* callback and then checking whether or not that
attribute has been changed - using either `attribute_changed?` or
`saved_change_to_attribute` - will always return false. Since
`attribute_changed?` still works in before_* callbacks, changing this particular
callback to a before_* callback maintains the original behavior.

* Deals with change to attribute not being noticed in after_* callbacks whilst
preserving after_* callback behavior

* Removes outdated route definition

* Removes catch-all route definition and starts addressing associated test
failures

* WIP - replace catch-all routes with whitelisted routes

* There is no reason for get :show to exist in the api controller

It 'show' action in admin/api_controller redirects to index,
the route being searched for is "admin/api/show" which
should never be hit and is never hit anywhere else in the application anyway.

* Adds more whitelisted routes

* WIP continued for whitelisting used routes

* WIP - Fixes route failures in controller specs

* Finishes (hopeful) whitelisting routes used by app

* Fixes deprecated use of atribute_was? in user model

* Removes deprecation warnings caused by autocomplete methods

* Fixes line issue

* Fixes indentation in feature

* Makes protected methods protected in comment model

* Renames update_sanitizer_version back to original filename

* Adds ApplicationRecord class & inherits all models from it

Moves ActiveRecord::Base monkeypatch into ApplicationRecord class

* Fixes ApplicationRecord class

1. It's an abstract class
2. There was a typo

* Specifies Rails version in migration file

* Ensures models inheriting from ApplicationRecord see the correct
WillPaginate.per_page default

* Updates spec syntax in works spec

* Whitelists new autocomplete route

* Explicitly allows use of :should syntax for RSpec

* Fix merge conflict

* Tidy up the diff

* I think this is right...

* Removes files that were unintentionally added back in by a merge

* Re-adds required line to notification method in comment model

* Adds required check to tag model method

dylanahsmith added a commit to dylanahsmith/rails that referenced this pull request Aug 30, 2017

activerecord: Fix deprecated behaviour of changed_attributes
Pull request rails#25337 made some
breaking changes to the way changed_attributes works for rails 5.1,
but the PR was only supposed to deprecate using it in after
callbacks.

The problem was that changed_attributes got a snapshot of the saved
changes in after callbacks, so this returns the previous behaviour
of it including unsaved changes and changes from saves that happen
in the after callbacks.

dylanahsmith added a commit to dylanahsmith/rails that referenced this pull request Aug 30, 2017

activerecord: Fix deprecated behaviour of changed_attributes
Pull request rails#25337 made some
breaking changes to the way changed_attributes works for rails 5.1,
but the PR was only supposed to deprecate using it in after
callbacks.

The problem was that `forget_attribute_assignments` in
changes_internally_applied disconnected mutation_tracker from the
current attributes, so didn't receive any changes made to it in
after callbacks. To preserve the deprecated behaviour, changes are
applied to calculate changed_attributes and after a save nested in
an after callback.

dylanahsmith added a commit to dylanahsmith/rails that referenced this pull request Aug 30, 2017

activerecord: Fix deprecated behaviour of changed_attributes
Pull request rails#25337 made some
breaking changes to the way changed_attributes works for rails 5.1,
but the PR was only supposed to deprecate using it in after
callbacks.

The problem was that `forget_attribute_assignments` in
changes_internally_applied disconnected mutation_tracker from the
current attributes, so didn't receive any changes made to it in
after callbacks. To preserve the deprecated behaviour, changes are
applied to calculate changed_attributes and after a save nested in
an after callback.

dylanahsmith added a commit to dylanahsmith/rails that referenced this pull request Aug 30, 2017

activerecord: Fix deprecated behaviour of changed_attributes
Pull request rails#25337 made some
breaking changes to the way changed_attributes works for rails 5.1,
but the PR was only supposed to deprecate using it in after
callbacks.

The problem was that `forget_attribute_assignments` in
changes_internally_applied disconnected mutation_tracker from the
current attributes, so didn't receive any changes made to it in
after callbacks. To preserve the deprecated behaviour, changes are
applied to calculate changed_attributes and after a save nested in
an after callback.

YaQ00 added a commit to YaQ00/mastodon that referenced this pull request Sep 5, 2017

Fix Account model deprecation warnings (#3689)
```
DEPRECATION WARNING: The behavior of `attribute_changed?` inside of after callbacks will be changing in the next version of Rails. The new return value will reflect the behavior of calling the method after `save` returned (e.g. the opposite of what it returns now). To maintain the current behavior, use `saved_change_to_attribute?` instead. (called from block in <class:Account> at /Users/rene/Workspace/personal/ruby/mastodon/app/models/account.rb:60)
DEPRECATION WARNING: The behavior of `attribute_changed?` inside of after callbacks will be changing in the next version of Rails. The new return value will reflect the behavior of calling the method after `save` returned (e.g. the opposite of what it returns now). To maintain the current behavior, use `saved_change_to_attribute?` instead. (called from block in <class:Account> at /Users/rene/Workspace/personal/ruby/mastodon/app/models/account.rb:60)
DEPRECATION WARNING: The behavior of `attribute_changed?` inside of after callbacks will be changing in the next version of Rails. The new return value will reflect the behavior of calling the method after `save` returned (e.g. the opposite of what it returns now). To maintain the current behavior, use `saved_change_to_attribute?` instead. (called from block in <class:Account> at /Users/rene/Workspace/personal/ruby/mastodon/app/models/account.rb:60)
DEPRECATION WARNING: The behavior of `attribute_changed?` inside of after callbacks will be changing in the next version of Rails. The new return value will reflect the behavior of calling the method after `save` returned (e.g. the opposite of what it returns now). To maintain the current behavior, use `saved_change_to_attribute?` instead. (called from block in <class:Account> at /Users/rene/Workspace/personal/ruby/mastodon/app/models/account.rb:61)
DEPRECATION WARNING: The behavior of `attribute_changed?` inside of after callbacks will be changing in the next version of Rails. The new return value will reflect the behavior of calling the method after `save` returned (e.g. the opposite of what it returns now). To maintain the current behavior, use `saved_change_to_attribute?` instead. (called from block in <class:Account> at /Users/rene/Workspace/personal/ruby/mastodon/app/models/account.rb:62)
DEPRECATION WARNING: The behavior of `attribute_changed?` inside of after callbacks will be changing in the next version of Rails. The new return value will reflect the behavior of calling the method after `save` returned (e.g. the opposite of what it returns now). To maintain the current behavior, use `saved_change_to_attribute?` instead. (called from block in <class:Account> at /Users/rene/Workspace/personal/ruby/mastodon/app/models/account.rb:63)
```

Here's PR describing changes to Dirty API rails/rails#25337

klacointe added a commit to klacointe/algoliasearch-rails that referenced this pull request Sep 20, 2017

klacointe added a commit to klacointe/algoliasearch-rails that referenced this pull request Sep 20, 2017

@chrysalag chrysalag referenced this pull request Sep 29, 2017

Merged

Features/reimplementing volunteer state #229

0 of 10 tasks complete

@toupeira toupeira referenced this pull request Oct 3, 2017

Merged

Implement restrictions for external volunteers #250

6 of 10 tasks complete

komagata added a commit to komagata/conditional_counter_cache that referenced this pull request Dec 4, 2017

komagata added a commit to komagata/conditional_counter_cache that referenced this pull request Dec 4, 2017

raphi added a commit to algolia/algoliasearch-rails that referenced this pull request Jan 8, 2018

evazion added a commit to evazion/danbooru that referenced this pull request Feb 4, 2018

Fix dirty attributes deprecation warnings (wip).
Where possible, convert after_save callbacks to before_save to avoid
calling *_changed? methods inside after_save.

ref: rails/rails#25337:

redox added a commit to algolia/algoliasearch-rails that referenced this pull request Mar 20, 2018

@pedrofurtado

This comment has been minimized.

Show comment
Hide comment
@pedrofurtado

pedrofurtado Apr 10, 2018

@olivierlacan In your previous comment (#25337 (comment)), If I make this changes the behavior will be the same as the old behavior, right? If not, How do I maintain the old behavior?

pedrofurtado commented Apr 10, 2018

@olivierlacan In your previous comment (#25337 (comment)), If I make this changes the behavior will be the same as the old behavior, right? If not, How do I maintain the old behavior?

@akaspick

This comment has been minimized.

Show comment
Hide comment
@akaspick

akaspick Apr 10, 2018

Contributor

I'm still confused about the use of these methods. @olivierlacan has provided a nice summary of the different possible method names and what they're for, but as @dsandstrom mentions, the only documentation appears to be in the Dirty module and almost none of the methods @olivierlacan has listed are even mentioned in the official documentation.

When using these methods in before callbacks the deprecation notices don't provide the correct suggestions on how to resolve the issue. Why is documentation on these methods so limited?

Contributor

akaspick commented Apr 10, 2018

I'm still confused about the use of these methods. @olivierlacan has provided a nice summary of the different possible method names and what they're for, but as @dsandstrom mentions, the only documentation appears to be in the Dirty module and almost none of the methods @olivierlacan has listed are even mentioned in the official documentation.

When using these methods in before callbacks the deprecation notices don't provide the correct suggestions on how to resolve the issue. Why is documentation on these methods so limited?

@pedrofurtado

This comment has been minimized.

Show comment
Hide comment
@pedrofurtado

pedrofurtado Apr 10, 2018

@akaspick @olivierlacan @sgrif May be a good idea to provide, in public and official documentation of Rails, a section explaining how to upgrade to Rails 5.2.0, including especially what these news methods do, and how we could maintain the current behavior of Rails 5.1.x.

pedrofurtado commented Apr 10, 2018

@akaspick @olivierlacan @sgrif May be a good idea to provide, in public and official documentation of Rails, a section explaining how to upgrade to Rails 5.2.0, including especially what these news methods do, and how we could maintain the current behavior of Rails 5.1.x.

@choonggg choonggg referenced this pull request Apr 17, 2018

Closed

Rails 5.1 #816

@yoasyo25 yoasyo25 referenced this pull request Apr 18, 2018

Open

rails 5.1 #11

@vlymar

This comment has been minimized.

Show comment
Hide comment
@vlymar

vlymar Apr 24, 2018

A point of confusion is that the table above and all the blog posts/stackoverflow answers around this deprecation notice instruct you to rename your <attribute>_changed? calls to saved_change_to_<attribute>?, while the Rails 5.2 AR::Dirty api docs say to use <attribute>_previously_changed?. Can we get some guidance on this?

vlymar commented Apr 24, 2018

A point of confusion is that the table above and all the blog posts/stackoverflow answers around this deprecation notice instruct you to rename your <attribute>_changed? calls to saved_change_to_<attribute>?, while the Rails 5.2 AR::Dirty api docs say to use <attribute>_previously_changed?. Can we get some guidance on this?

@feliperaul

This comment has been minimized.

Show comment
Hide comment
@feliperaul

feliperaul May 24, 2018

@vlymar I agree, this is so far our hugest pain point in upgrading from Rails 4.2 to Rails 5.2

feliperaul commented May 24, 2018

@vlymar I agree, this is so far our hugest pain point in upgrading from Rails 4.2 to Rails 5.2

@feliperaul

This comment has been minimized.

Show comment
Hide comment
@feliperaul

feliperaul May 24, 2018

Just want to point that this blog posts was the best resource I could find about theses changes, and it's a must-read for now specially considering that, as far as I could tell, the changelogs and upgrade guides didn't mention this huge breaking change anywhere :/

Update: for some reason, when googling for ActiveRecord Dirty I'm taken to the ActiveModel::Dirty API page on the docs, and there's no sign (up until page 5 of google results) of the ActiveRecord::AttributeMethods::Dirty, which has different methods and docs: http://api.rubyonrails.org/v5.2.0/classes/ActiveRecord/AttributeMethods/Dirty.html

feliperaul commented May 24, 2018

Just want to point that this blog posts was the best resource I could find about theses changes, and it's a must-read for now specially considering that, as far as I could tell, the changelogs and upgrade guides didn't mention this huge breaking change anywhere :/

Update: for some reason, when googling for ActiveRecord Dirty I'm taken to the ActiveModel::Dirty API page on the docs, and there's no sign (up until page 5 of google results) of the ActiveRecord::AttributeMethods::Dirty, which has different methods and docs: http://api.rubyonrails.org/v5.2.0/classes/ActiveRecord/AttributeMethods/Dirty.html

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment