Skip to content

Commit

Permalink
Add deprecation path for arity-zero preference defaults
Browse files Browse the repository at this point in the history
Solidus 3.1 shipped with a new preferences system, where a default can
take different values depending on the Solidus version defaults that
have been loaded. See solidusio#4064 for details.

To achieve those above, when a proc is given as the default value
constructor, it now should take the loaded Solidus version as an
argument. That's a breaking change, as some extensions or user-defined
preferences may be using zero-arity lambdas.

This commit deprecates zero-arity lambdas but wraps them into another
lambda, taking and disregarding a single argument. That's only needed
for procs with lambda semantics, as raw procs will ignore the provided
extra argument.

When it comes to the implementation, as it's something to be ditched in
the next major release, we've opted for the more straightforward
solution. I.e., wrapping the lambda into the `Preferable` module even if
it only affects `AppConfiguration` classes. The default-handling logic
is very entangled into the former, and it'd take more work to extract
it.

Fixes solidusio#4165
  • Loading branch information
waiting-for-dev authored and rmparr committed Jun 1, 2022
1 parent 18762f7 commit 2efe065
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 2 deletions.
35 changes: 33 additions & 2 deletions core/lib/spree/preferences/preferable_class_methods.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# frozen_string_literal: true

require 'spree/deprecation'
require 'spree/encryptor'

module Spree::Preferences
Expand All @@ -26,8 +27,38 @@ def preference(name, type, options = {})
options[:default] = preference_encryptor.encrypt(options[:default])
end

default = options[:default]
default = proc { options[:default] } unless default.is_a?(Proc)
default = begin
given = options[:default]
if ancestors.include?(Spree::Preferences::Configuration) &&
given.is_a?(Proc) &&
given.lambda? &&
given.arity.zero?
Spree::Deprecation.warn <<~MSG
The arity of a proc given as the default for a preference
has changed from 0 to 1 on Solidus 3.1. The Solidus
version for the loaded preference defaults is given as the
proc's argument from this point on.
If you don't need to return a different default value
depending on the loaded Solidus version, you can change
the proc so that it doesn't have lambda semantics (lambdas
raise when extra arguments are supplied, while raw procs
don't). E.g.:
preference :foo, :string, default: proc { true }
If you want to branch on the provided Solidus version, you can do like the following:
preference :foo, :string, default: by_version(true, "3.2.0" => false)
MSG
->(_default_context) { given.call }
elsif given.is_a?(Proc)
given
else
proc { given }
end
end

# The defined preferences on a class are all those defined directly on
# that class as well as those defined on ancestors.
Expand Down
32 changes: 32 additions & 0 deletions core/spec/models/spree/preferences/configuration_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,38 @@
expect(config.get(:foo)).to be(false)
end

context "when default is a proc with arity zero" do
it "warns a deprecation message when it's a lambda" do
expect(Spree::Deprecation).to receive(:warn).with(/arity.*changed from 0 to 1/m)

config = Class.new(Spree::Preferences::Configuration) do
preference :lambda_with_arity_zero, :string, default: -> { 'foo' }
end.new

config.get(:lambda_with_arity_zero)
end

it "still takes the return value as the default" do
allow(Spree::Deprecation).to receive(:warn)

config = Class.new(Spree::Preferences::Configuration) do
preference :lambda_with_arity_zero, :string, default: -> { 'foo' }
end.new

expect(config.get(:lambda_with_arity_zero)).to eq('foo')
end

it "doesn't warn a deprecation message when it isn't a lambda" do
config = Class.new(Spree::Preferences::Configuration) do
preference :proc_with_arity_zero, :string, default: proc { 'foo' }
end.new

expect(Spree::Deprecation).not_to receive(:warn)

config.get(:proc_with_arity_zero)
end
end

describe '#load_defaults' do
it 'changes loaded_defaults' do
config.load_defaults '2.1'
Expand Down

0 comments on commit 2efe065

Please sign in to comment.