Skip to content
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 calling preferences without serialization #4013

Conversation

mamhoff
Copy link
Contributor

@mamhoff mamhoff commented Mar 29, 2021

Description

In Solidus < 2.11.4, Spree::Base had a serialize :preferences, Hash statement. This statement fails in Rails 6.1ff, as after that Rails will try deserializing the column even if it isn't there.

What this commit does is:

  • Remove the serialize call from Spree::Base, as many inheriting classes do not have a preferences column
  • Add the serialize and after_initialize call to those base classes that need it
    • Spree::Calculator
    • Spree::PromotionAction
    • Spree::PaymentMethod
    • Spree::PromotionRule
  • Add a new preferences getter to Spree::Base that exhibits the following behaviour: If preferences is not a Hash, we can safely assume that the class of the instance we're trying to get preferences from expects Spree::Base to perform the serialize call for them. In that case, call serialize on the instance's class, ensuring that all future loads of this type of object will have preferences serialized, and parse the attribute using ActiveRecord::Type::Serialized (as that's how Spree::Base worked).
  • Remove the preferences_coder_class method. I don't think it's prudent to add an extension point here for something that was hardcoded in the past and can easily be circumnavigated by e.g. setting one's custom class to serialize using JSON:
class MyPreferences
  serialize :preferences, JSON
  after_initialize :initialize_preference_defaults
  ...
end

This PR goes against the v2.11 branch of Solidus; I would like to uphold my PR #3997 for the master branch.

This means that the deprecation window for this change would be v2.11.8 => v3.0.0, but I think that's doable. There is a bit of work in related projects, but it just consists in adding to lines to configuration classes.

Checklist:

  • I have followed Pull Request guidelines
  • I have added a detailed description into each commit message
  • I have updated Guides and README accordingly to this change (if needed)
  • I have added tests to cover this change (if needed)
  • II have attached screenshots to this PR for visual changes (if needed)

core/app/models/spree/base.rb Outdated Show resolved Hide resolved
core/app/models/spree/base.rb Outdated Show resolved Hide resolved
core/spec/models/spree/calculator_spec.rb Outdated Show resolved Hide resolved
@mamhoff mamhoff force-pushed the deprecate-calling-preferences-without-serialization branch 4 times, most recently from a91da40 to 48ee499 Compare March 29, 2021 11:52
Copy link
Member

@tvdeyen tvdeyen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this approach.

@mamhoff mamhoff force-pushed the deprecate-calling-preferences-without-serialization branch 3 times, most recently from 376870c to 2a88e12 Compare March 29, 2021 16:20
Copy link
Member

@kennyadsl kennyadsl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great stuff Martin! I only left some comments/questions but overall I think it's good to merge both in 2.11 and 3.0. If that was breaking your application, I think it's safe to consider this a patch.

.circleci/config.yml Outdated Show resolved Hide resolved
core/app/models/spree/promotion_rule.rb Outdated Show resolved Hide resolved
core/app/models/spree/base.rb Outdated Show resolved Hide resolved
core/app/models/spree/base.rb Show resolved Hide resolved
core/spec/models/spree/base_spec.rb Show resolved Hide resolved
@mamhoff mamhoff force-pushed the deprecate-calling-preferences-without-serialization branch from 2a88e12 to dd8db61 Compare March 29, 2021 20:54
Copy link
Contributor Author

@mamhoff mamhoff left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about providing a module/concern for this? I'm not particularly interested in DRYing things, but if we need to make other changes in the future (eg. deprecate the way we serialize/initialize defaults) it could be handy.

I like that idea. Especially because that means we can have the callback and the after_initialize call in the same file. Pretty nice!

core/app/models/spree/base.rb Show resolved Hide resolved
core/app/models/spree/base.rb Outdated Show resolved Hide resolved
core/spec/models/spree/base_spec.rb Show resolved Hide resolved
@mamhoff mamhoff force-pushed the deprecate-calling-preferences-without-serialization branch from dd8db61 to 2313219 Compare March 29, 2021 21:07
core/app/models/spree/base.rb Outdated Show resolved Hide resolved
core/app/models/spree/base.rb Outdated Show resolved Hide resolved
core/app/models/spree/base.rb Outdated Show resolved Hide resolved
core/app/models/spree/base.rb Outdated Show resolved Hide resolved
@mamhoff mamhoff force-pushed the deprecate-calling-preferences-without-serialization branch 2 times, most recently from 17ca6d5 to e3d47e4 Compare March 30, 2021 07:31
core/app/models/spree/base.rb Outdated Show resolved Hide resolved
core/app/models/spree/calculator.rb Show resolved Hide resolved
core/app/models/spree/base.rb Outdated Show resolved Hide resolved
This fixes an issue that arose because of commit b947015, which
initialized the serialization of the preference column only if the
respective subclass had any preferences defined. This is not always the
case, and previous versions of Solidus would always allow a subclass of
e.g. `Spree::Calculator` to access their `preferences` attribute as a
Hash.

The intention of the commit in question was to speed up initialization
for models that do not have a preferences column. Instead of complex
metamagic, this moves the initialization of the preferences column and
preference defaults to those classes that actually needed, actually
yielding MORE performance benefits, as `serialize` and the
`after_initialize` hook are only called once, not once per preference
being defined.

For classes that expect Spree::Base to call `serialize`, this commit
adds deprecation messaging and parsing to Spree::Base#preferences.
@mamhoff mamhoff force-pushed the deprecate-calling-preferences-without-serialization branch from e3d47e4 to 9c472a9 Compare March 30, 2021 11:55
@mamhoff mamhoff force-pushed the deprecate-calling-preferences-without-serialization branch from 8a97d6b to 302b078 Compare March 30, 2021 12:06
mamhoff added 5 commits March 30, 2021 14:36
This removes even more code from Spree::Base, and groups the few lines
of code that are needed for persisted preferences in one module.
The use of any of the methods in `Preferable` without `Persistable`
makes no sense. Let's declutter Spree::Base some more!
The error message here calls for "persistable", because all objects that
inherit from Spree::Base *actually* need the Persistable module.
If you try accessing a calculator with preferences in the DB, and that
calculator hasn't seen the `serialize` call, Rails will try accessing
the preferences column directly and return a string :(
These methods aren't available automatically any longer, thanks to Rails
6.1. They actually weren't in the first place, because not every model
has a `preferences` column.
@mamhoff mamhoff force-pushed the deprecate-calling-preferences-without-serialization branch from 302b078 to 167299e Compare March 30, 2021 12:52
Copy link
Member

@kennyadsl kennyadsl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left a non-blocking comment, let me know your thoughts but I'm fine merging even as is.

Thanks!

end

before(:each) do
allow(Spree::Deprecation).to receive(:warn).
Copy link
Member

@kennyadsl kennyadsl Mar 31, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about turning this into an expect? The concern is that it may not be emitting the deprecation warning when we expect this happens. I mean, stubbing the deprecation should not be used to avoid failures and clean the logs only but to actually test that users get a deprecation message.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't do that, because on the second test that runs, the method will already be defined and an expect would fail. Undoing that include turns out to be a lot of work...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it, and I can't think at a clean way to do it.

@kennyadsl kennyadsl requested a review from a team March 31, 2021 06:47
@gmacdougall
Copy link
Member

I like this change and what it's doing. Thanks for the contribution Martin!

@kennyadsl kennyadsl merged commit 7566958 into solidusio:v2.11 Apr 1, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants