Skip to content

Fix preferences columns to be always serialized#3997

Closed
mamhoff wants to merge 0 commit intosolidusio:masterfrom
mamhoff:fix-calculator-preferences
Closed

Fix preferences columns to be always serialized#3997
mamhoff wants to merge 0 commit intosolidusio:masterfrom
mamhoff:fix-calculator-preferences

Conversation

@mamhoff
Copy link
Copy Markdown
Contributor

@mamhoff mamhoff commented Mar 16, 2021

Description

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.

Checklist:

  • I have followed Pull Request guidelines
  • I have added a detailed description into each commit message
  • I have added tests to cover this change (if needed)

mamhoff referenced this pull request Mar 16, 2021
Since rails/rails@7b39197 serialezable hash attributes are not allocated
by default anymore if not used.
@mamhoff mamhoff force-pushed the fix-calculator-preferences branch from 553c700 to 670c2a5 Compare March 16, 2021 11:24
@kennyadsl
Copy link
Copy Markdown
Member

Thanks Martin, one question: does this require to add

serialize :preferences, Hash
after_initialize :initialize_preference_defaults

explicitly to any custom model defined in host applications and extensions that has preferences?

@mamhoff
Copy link
Copy Markdown
Contributor Author

mamhoff commented Mar 16, 2021

@kennyadsl Yes, that is true. I could add a changelog entry and/or raise an exception when it's not defined.

@kennyadsl
Copy link
Copy Markdown
Member

Can you please provide an example of the code broken by this change? It's still not super clear to me and would help to evaluate if this needed to be backported.

@mamhoff
Copy link
Copy Markdown
Contributor Author

mamhoff commented Mar 16, 2021

All of our calculators that do not have preferences (they're custom code) return nil instead of the previous {}. We have code that relies on that for setting preferences.

@mamhoff
Copy link
Copy Markdown
Contributor Author

mamhoff commented Mar 16, 2021

So, if you added a custom shipping calculator with no preferences, theses two lines would definitely break if preferences returns nil instead of a Hash: https://github.com/solidusio/solidus/blob/master/core/app/models/spree/stock/estimator.rb#L65-L66. And that's also what's breaking our build.

@kennyadsl
Copy link
Copy Markdown
Member

I have a failing spec for the estimator when there's a shipping calculator without preferences

          # core/spec/models/spree/stock/estimator_spec.rb:96

          context 'with a custom shipping calculator with no preference' do
            class Spree::Calculator::Shipping::NoPreferences < Spree::ShippingCalculator
              def compute_package(_package)
                # no op
              end
            end

            let!(:shipping_methods) do
              [
                create(:shipping_method, calculator: Spree::Calculator::Shipping::NoPreferences.new)
              ]
            end

            it 'does not raise an error' do
              expect { subject.shipping_rates(package) }.not_to raise_error
            end
          end

I think the best approach here is by finding a way to let preferences return a Hash even when there are preferences set. That change is needed for Rails 6.1 compatibility and we can't just revert it.

@kennyadsl
Copy link
Copy Markdown
Member

I should have a fix that doesn't involve reverting that commit, or introduce a breaking change. If that works well, I think we can still keep this PR for Solidus 3, I like that it's more explicit and can remove the hacky code I'm going to propose. I'd love to have a period when this is optional though and introduces a deprecation warning when the serialization is not explicit.

@kennyadsl
Copy link
Copy Markdown
Member

@mamhoff I've submitted #3998, what do you think?

end

def preference(name, type, options = {})
if Object.const_defined?("Spree::Base") && self < Spree::Base && !self.new.preferences.is_a?(Hash)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Style/RedundantSelf: Redundant self detected.

@mamhoff mamhoff force-pushed the fix-calculator-preferences branch 2 times, most recently from 670c2a5 to 4d1cef6 Compare March 29, 2021 12:56
@mamhoff mamhoff closed this Mar 29, 2021
@mamhoff mamhoff force-pushed the fix-calculator-preferences branch from 4d1cef6 to 39a9d7d Compare March 29, 2021 12:57
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.

3 participants