solidus_core 3.0.2 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of solidus_core might be problematic. Click here for more details.

Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/app/helpers/spree/base_helper.rb +1 -1
  3. data/app/helpers/spree/products_helper.rb +1 -1
  4. data/app/models/concerns/spree/default_price.rb +63 -10
  5. data/app/models/spree/adjustment.rb +6 -5
  6. data/app/models/spree/customer_return.rb +3 -2
  7. data/app/models/spree/image/active_storage_attachment.rb +2 -7
  8. data/app/models/spree/image/paperclip_attachment.rb +2 -2
  9. data/app/models/spree/line_item.rb +2 -2
  10. data/app/models/spree/order.rb +11 -6
  11. data/app/models/spree/price.rb +1 -1
  12. data/app/models/spree/product/scopes.rb +5 -5
  13. data/app/models/spree/product.rb +12 -0
  14. data/app/models/spree/promotion/rules/item_total.rb +50 -6
  15. data/app/models/spree/promotion.rb +2 -2
  16. data/app/models/spree/promotion_code.rb +1 -1
  17. data/app/models/spree/shipping_rate_tax.rb +1 -1
  18. data/app/models/spree/stock/availability.rb +11 -3
  19. data/app/models/spree/stock/simple_coordinator.rb +0 -10
  20. data/app/models/spree/stock_location.rb +1 -1
  21. data/app/models/spree/store_credit.rb +6 -1
  22. data/app/models/spree/tax_calculator/shipping_rate.rb +1 -1
  23. data/app/models/spree/taxon/active_storage_attachment.rb +2 -2
  24. data/app/models/spree/taxon/paperclip_attachment.rb +3 -3
  25. data/app/models/spree/variant/price_selector.rb +16 -3
  26. data/app/models/spree/variant.rb +26 -16
  27. data/config/locales/en.yml +2 -0
  28. data/db/migrate/20210312061050_change_column_null_on_prices.rb +7 -0
  29. data/lib/generators/solidus/install/install_generator.rb +1 -1
  30. data/lib/generators/solidus/install/templates/config/initializers/spree.rb.tt +3 -1
  31. data/lib/generators/solidus/update/templates/config/initializers/new_solidus_defaults.rb.tt +30 -0
  32. data/lib/generators/solidus/update/update_generator.rb +112 -0
  33. data/lib/generators/spree/dummy/templates/rails/application.rb.tt +0 -1
  34. data/lib/generators/spree/dummy/templates/rails/database.yml +78 -35
  35. data/lib/spree/app_configuration.rb +62 -0
  36. data/lib/spree/core/engine.rb +10 -11
  37. data/lib/spree/core/product_filters.rb +1 -1
  38. data/lib/spree/core/search/base.rb +1 -1
  39. data/lib/spree/core/state_machines/order.rb +1 -1
  40. data/lib/spree/core/version.rb +5 -1
  41. data/lib/spree/core/versioned_value.rb +75 -0
  42. data/lib/spree/core.rb +17 -0
  43. data/lib/spree/permitted_attributes.rb +1 -1
  44. data/lib/spree/preferences/configuration.rb +62 -0
  45. data/lib/spree/preferences/preferable.rb +8 -0
  46. data/lib/spree/preferences/preferable_class_methods.rb +5 -3
  47. data/lib/spree/preferences/preference_differentiator.rb +28 -0
  48. data/lib/spree/testing_support/dummy_app/database.yml +42 -22
  49. data/lib/spree/testing_support/dummy_app.rb +33 -18
  50. data/lib/tasks/solidus/delete_prices_with_nil_amount.rake +8 -0
  51. metadata +7 -3
  52. data/app/models/spree/tax/shipping_rate_taxer.rb +0 -24
  53. data/lib/tasks/upgrade.rake +0 -15
@@ -372,6 +372,28 @@ module Spree
372
372
  # Spree::Wallet::DefaultPaymentBuilder.
373
373
  class_name_attribute :default_payment_builder_class, default: 'Spree::Wallet::DefaultPaymentBuilder'
374
374
 
375
+ # Allows providing your own class for managing the contents of an order.
376
+ #
377
+ # @!attribute [rw] order_contents_class
378
+ # @return [Class] a class with the same public interfaces as
379
+ # Spree::OrderContents.
380
+ class_name_attribute :order_contents_class, default: 'Spree::OrderContents'
381
+
382
+ # Allows providing your own class for shipping an order.
383
+ #
384
+ # @!attribute [rw] order_shipping_class
385
+ # @return [Class] a class with the same public interfaces as
386
+ # Spree::OrderShipping.
387
+ class_name_attribute :order_shipping_class, default: 'Spree::OrderShipping'
388
+
389
+ # Allows providing your own class for managing the inventory units of a
390
+ # completed order.
391
+ #
392
+ # @!attribute [rw] order_cancellations_class
393
+ # @return [Class] a class with the same public interfaces as
394
+ # Spree::OrderCancellations.
395
+ class_name_attribute :order_cancellations_class, default: 'Spree::OrderCancellations'
396
+
375
397
  # Allows providing your own class for canceling payments.
376
398
  #
377
399
  # @!attribute [rw] payment_canceller
@@ -453,6 +475,46 @@ module Spree
453
475
  # @return [Array]
454
476
  class_name_attribute :allowed_image_mime_types, default: %w(image/jpeg image/jpg image/png image/gif).freeze
455
477
 
478
+ # @!attribute [rw] product_image_style_default
479
+ #
480
+ # Defines which style to default to when style is not provided
481
+ # :product is the default.
482
+ #
483
+ # @return [Symbol]
484
+ class_name_attribute :product_image_style_default, default: :product
485
+
486
+ # @!attribute [rw] product_image_styles
487
+ #
488
+ # Defines image styles/sizes hash for styles
489
+ # `{ mini: '48x48>',
490
+ # small: '400x400>',
491
+ # product: '680x680>',
492
+ # large: '1200x1200>' } is the default.
493
+ #
494
+ # @return [Hash]
495
+ class_name_attribute :product_image_styles, default: { mini: '48x48>',
496
+ small: '400x400>',
497
+ product: '680x680>',
498
+ large: '1200x1200>' }
499
+ # @!attribute [rw] taxon_image_style_default
500
+ #
501
+ # Defines which style to default to when style is not provided
502
+ # :mini is the default.
503
+ #
504
+ # @return [Symbol]
505
+ class_name_attribute :taxon_image_style_default, default: :mini
506
+
507
+ # @!attribute [rw] taxon_styles
508
+ #
509
+ # Defines taxon styles/sizes hash for styles
510
+ # `{ mini: '48x48>',
511
+ # small: '400x400>',
512
+ # product: '680x680>',
513
+ # large: '1200x1200>' } is the default.
514
+ #
515
+ # @return [Hash]
516
+ class_name_attribute :taxon_image_styles, default: { mini: '32x32>', normal: '128x128>' }
517
+
456
518
  # Allows switching attachment library for Taxon
457
519
  #
458
520
  # `Spree::Taxon::ActiveStorageAttachment`
@@ -55,18 +55,17 @@ module Spree
55
55
  end
56
56
  end
57
57
 
58
- config.after_initialize do
59
- # Load in mailer previews for apps to use in development.
60
- # We need to make sure we call `Preview.all` before requiring our
61
- # previews, otherwise any previews the app attempts to add need to be
62
- # manually required.
63
- if Rails.env.development? || Rails.env.test?
64
- ActionMailer::Preview.all
58
+ # Load in mailer previews for apps to use in development.
59
+ initializer "spree.core.action_mailer.set_preview_path", after: "action_mailer.set_configs" do |app|
60
+ original_preview_path = app.config.action_mailer.preview_path
61
+ solidus_preview_path = Spree::Core::Engine.root.join 'lib/spree/mailer_previews'
65
62
 
66
- Dir[root.join("lib/spree/mailer_previews/**/*_preview.rb")].each do |file|
67
- require_dependency file
68
- end
69
- end
63
+ app.config.action_mailer.preview_path = "{#{original_preview_path},#{solidus_preview_path}}"
64
+ ActionMailer::Base.preview_path = app.config.action_mailer.preview_path
65
+ end
66
+
67
+ config.after_initialize do
68
+ Spree::Config.check_load_defaults_called('Spree::Config')
70
69
  end
71
70
  end
72
71
  end
@@ -61,7 +61,7 @@ module Spree
61
61
  scope = scope.or(new_scope)
62
62
  end
63
63
 
64
- Spree::Product.joins(master: :default_price).where(scope)
64
+ Spree::Product.joins(master: :prices).where(scope)
65
65
  end
66
66
 
67
67
  def self.format_price(amount)
@@ -51,7 +51,7 @@ module Spree
51
51
  # separate queries most of the time but opt for a join as soon as any
52
52
  # `where` constraints affecting joined tables are added to the search;
53
53
  # which is the case as soon as a taxon is added to the base scope.
54
- scope = scope.preload(master: :currently_valid_prices)
54
+ scope = scope.preload(master: :prices)
55
55
  scope = scope.preload(master: :images) if @properties[:include_images]
56
56
  scope
57
57
  end
@@ -73,7 +73,7 @@ module Spree
73
73
  end
74
74
 
75
75
  event :complete do
76
- transition to: :complete, from: :confirm
76
+ transition to: :complete, from: klass.checkout_steps.keys.last
77
77
  end
78
78
 
79
79
  if states[:payment]
@@ -1,12 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Spree
4
- VERSION = "3.0.2"
4
+ VERSION = "3.1.0"
5
5
 
6
6
  def self.solidus_version
7
7
  VERSION
8
8
  end
9
9
 
10
+ def self.previous_solidus_minor_version
11
+ '3.0'
12
+ end
13
+
10
14
  def self.solidus_gem_version
11
15
  Gem::Version.new(solidus_version)
12
16
  end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spree
4
+ module Core
5
+ # Wrapper for a value that can be different depending on the Solidus version
6
+ #
7
+ # Some configuration defaults can be added or changed when a new Solidus
8
+ # version is released. This class encapsulates getting the correct value for a
9
+ # given Solidus version.
10
+ #
11
+ # The way it works is you provide an initial value in time, plus the version
12
+ # boundary where it got changed. Then you can fetch the value providing the
13
+ # desired Solidus version:
14
+ #
15
+ # @example
16
+ # value = VersionedValue.new(true, "3.0.0" => false)
17
+ # value.call("2.7.0") # => true
18
+ # value.call("3.0.0") # => false
19
+ # value.call("3.1.0") # => false
20
+ #
21
+ # Remember that you must provide the exact boundary when a value got changed,
22
+ # which could easily be during a pre-release:
23
+ #
24
+ # @example
25
+ # value = VersionedValue.new(true, "3.0.0" => false)
26
+ # value.call("3.0.0.alpha") # => true
27
+ #
28
+ # value = VersionedValue.new(true, "3.0.0.alpha" => false)
29
+ # value.call("3.0.0.alpha") # => false
30
+ #
31
+ # Multiple boundaries can also be provided:
32
+ #
33
+ # @example
34
+ # value = VersionedValue.new(0, "2.0.0" => 1, "3.0.0" => 2)
35
+ # value.call("1.0.0") # => 0
36
+ # value.call("2.1.0") # => 1
37
+ # value.call("3.0.0") # => 2
38
+ class VersionedValue
39
+ attr_reader :boundaries
40
+
41
+ # @param initial_value [Any]
42
+ # @param boundary [Hash<String, Any>] Map from version number to new value
43
+ def initialize(initial_value, boundaries = {})
44
+ @boundaries = Hash[
45
+ { '0' => initial_value }
46
+ .merge(boundaries)
47
+ .transform_keys { |version| to_gem_version(version) }
48
+ .sort
49
+ ]
50
+ end
51
+
52
+ # @param solidus_version [String]
53
+ def call(solidus_version = Spree.solidus_version)
54
+ solidus_version = to_gem_version(solidus_version)
55
+ boundaries.fetch(
56
+ boundaries
57
+ .keys
58
+ .reduce do |target, following|
59
+ if target <= solidus_version && solidus_version < following
60
+ target
61
+ else
62
+ following
63
+ end
64
+ end
65
+ )
66
+ end
67
+
68
+ private
69
+
70
+ def to_gem_version(string)
71
+ Gem::Version.new(string)
72
+ end
73
+ end
74
+ end
75
+ end
data/lib/spree/core.rb CHANGED
@@ -37,6 +37,16 @@ module Spree
37
37
  end
38
38
  end
39
39
 
40
+ # Load the same version defaults for all available Solidus components
41
+ #
42
+ # @see Spree::Preferences::Configuration#load_defaults
43
+ def self.load_defaults(version)
44
+ Spree::Config.load_defaults(version)
45
+ Spree::Frontend::Config.load_defaults(version) if defined?(Spree::Frontend::Config)
46
+ Spree::Backend::Config.load_defaults(version) if defined?(Spree::Backend::Config)
47
+ Spree::Api::Config.load_defaults(version) if defined?(Spree::Api::Config)
48
+ end
49
+
40
50
  # Used to configure Spree.
41
51
  #
42
52
  # Example:
@@ -52,6 +62,13 @@ module Spree
52
62
  end
53
63
 
54
64
  module Core
65
+ def self.has_install_generator_been_run?
66
+ (Rails.env.test? && Rails.application.class.name == 'DummyApp::Application') ||
67
+ Rails.application.paths['config/initializers'].paths.any? do |path|
68
+ File.exist?(path.join('spree.rb'))
69
+ end
70
+ end
71
+
55
72
  class GatewayError < RuntimeError; end
56
73
  end
57
74
  end
@@ -74,7 +74,7 @@ module Spree
74
74
  @@product_properties_attributes = [:property_name, :value, :position]
75
75
 
76
76
  @@product_attributes = [
77
- :name, :description, :available_on, :discontinue_on, :permalink, :meta_description,
77
+ :name, :description, :available_on, :discontinue_on, :meta_description,
78
78
  :meta_keywords, :price, :sku, :deleted_at,
79
79
  :option_values_hash, :weight, :height, :width, :depth,
80
80
  :shipping_category_id, :tax_category_id,
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'spree/core/versioned_value'
3
4
  require 'spree/preferences/preferable'
4
5
 
5
6
  module Spree::Preferences
@@ -29,6 +30,44 @@ module Spree::Preferences
29
30
  class Configuration
30
31
  include Spree::Preferences::Preferable
31
32
 
33
+ # @!attribute [r] loaded_defaults
34
+ # @return [String]
35
+ # Some configuration defaults can be added or changed when a new Solidus
36
+ # version is released. Setting this to an older Solidus version allows keeping
37
+ # backward compatibility until the application code is updated to the new
38
+ # defaults. Set via {#load_defaults}
39
+ attr_reader :loaded_defaults
40
+
41
+ attr_reader :load_defaults_called
42
+
43
+ def initialize
44
+ @loaded_defaults = Spree.solidus_version
45
+ @load_defaults_called = false
46
+ end
47
+
48
+ # @param [String] Solidus version from which take defaults when not
49
+ # overriden.
50
+ # @see #load_defaults
51
+ def load_defaults(version)
52
+ @loaded_defaults = version
53
+ @load_defaults_called = true
54
+ reset
55
+ end
56
+
57
+ def check_load_defaults_called(instance_constant_name = nil)
58
+ return if load_defaults_called || !Spree::Core.has_install_generator_been_run?
59
+
60
+ target_name = instance_constant_name || "#{self.class.name}.new"
61
+ Spree::Deprecation.warn <<~MSG
62
+ It's recommended that you explicitly load the default configuration for
63
+ your current Solidus version. You can do it by adding the following call
64
+ to your Solidus initializer within the #{target_name} block:
65
+
66
+ config.load_defaults('#{Spree.solidus_version}')
67
+
68
+ MSG
69
+ end
70
+
32
71
  # @yield [config] Yields this configuration object to a block
33
72
  def configure
34
73
  yield(self)
@@ -79,6 +118,23 @@ module Spree::Preferences
79
118
  end
80
119
  end
81
120
 
121
+ # Generates a different preference default depending on {#version_defaults}
122
+ #
123
+ # This method is meant to be used in the `default:` keyword argument for
124
+ # {.preference}. For instance, in the example, `foo`'s default was `true`
125
+ # until version 3.0.0.alpha, when it became `false`:
126
+ #
127
+ # @example
128
+ # preference :foo, :boolean, default: by_version(true, "3.0.0.alpha" => false)
129
+ #
130
+ # @see #loaded_defaults
131
+ # @see Spree::Core::VersionedValue
132
+ def self.by_version(*args)
133
+ proc do |loaded_defaults|
134
+ Spree::Core::VersionedValue.new(*args).call(loaded_defaults)
135
+ end
136
+ end
137
+
82
138
  def self.preference(name, type, options = {})
83
139
  super
84
140
  alias_method name.to_s, "preferred_#{name}"
@@ -103,5 +159,11 @@ module Spree::Preferences
103
159
  class_name
104
160
  end
105
161
  end
162
+
163
+ private
164
+
165
+ def context_for_default
166
+ [loaded_defaults]
167
+ end
106
168
  end
107
169
  end
@@ -11,6 +11,10 @@ module Spree
11
11
  # A class including Preferable must implement #preferences which should return
12
12
  # an object responding to .fetch(key), []=(key, val), and .delete(key).
13
13
  #
14
+ # It may also define a `#context_for_default` method. It should return an
15
+ # array with the arguments to be provided to a proc used as the `default:`
16
+ # keyword for a preference.
17
+ #
14
18
  # The generated writer method performs typecasting before assignment into the
15
19
  # preferences object.
16
20
  #
@@ -176,6 +180,10 @@ module Spree
176
180
  value
177
181
  end
178
182
  end
183
+
184
+ def context_for_default
185
+ [].freeze
186
+ end
179
187
  end
180
188
  end
181
189
  end
@@ -27,7 +27,7 @@ module Spree::Preferences
27
27
  end
28
28
 
29
29
  default = options[:default]
30
- default = ->{ options[:default] } unless default.is_a?(Proc)
30
+ default = proc { options[:default] } unless default.is_a?(Proc)
31
31
 
32
32
  # The defined preferences on a class are all those defined directly on
33
33
  # that class as well as those defined on ancestors.
@@ -44,7 +44,7 @@ module Spree::Preferences
44
44
  # is a pending preference before going to default
45
45
  define_method preference_getter_method(name) do
46
46
  value = preferences.fetch(name) do
47
- default.call
47
+ default.call(*context_for_default)
48
48
  end
49
49
  value = preference_encryptor.decrypt(value) if preference_encryptor.present?
50
50
  value
@@ -60,7 +60,9 @@ module Spree::Preferences
60
60
  preferences_will_change! if respond_to?(:preferences_will_change!)
61
61
  end
62
62
 
63
- define_method preference_default_getter_method(name), &default
63
+ define_method preference_default_getter_method(name) do
64
+ default.call(*context_for_default)
65
+ end
64
66
 
65
67
  define_method preference_type_getter_method(name) do
66
68
  type
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spree
4
+ module Preferences
5
+ class PreferenceDifferentiator
6
+ attr_reader :config_class
7
+
8
+ def initialize(config_class)
9
+ @config_class = config_class
10
+ end
11
+
12
+ def call(from:, to:)
13
+ preferences_from = config_class.new.load_defaults(from)
14
+ preferences_to = config_class.new.load_defaults(to)
15
+ preferences_from.reduce({}) do |changes, (pref_key, value_from)|
16
+ value_to = preferences_to[pref_key]
17
+ if value_from == value_to
18
+ changes
19
+ else
20
+ changes.merge(
21
+ pref_key => { from: value_from, to: value_to }
22
+ )
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -1,34 +1,54 @@
1
- <%
2
- database_prefix = ENV['LIB_NAME'].presence || "solidus"
3
- %>
4
- <% case ENV['DB']
1
+ <% db = case ENV['DB']
2
+ when 'mysql'
3
+ 'mysql'
4
+ when 'postgres', 'postgresql'
5
+ 'postgres'
6
+ when 'sqlite', '', nil
7
+ 'sqlite'
8
+ else
9
+ raise "Invalid DB specified: #{ENV['DB']}"
10
+ end %>
11
+ <% db_host = case db
12
+ when 'mysql'
13
+ ENV['DB_MYSQL_HOST'] || ENV['DB_HOST']
14
+ when 'postgres'
15
+ ENV['DB_POSTGRES_HOST'] || ENV['DB_HOST']
16
+ else
17
+ ENV['DB_HOST']
18
+ end %>
19
+ <% db_prefix = ENV['LIB_NAME'].presence || "solidus" %>
20
+ <% db_username = ENV['DB_USERNAME'] %>
21
+ <% db_password = ENV['DB_PASSWORD'] %>
22
+ <% case db
5
23
  when 'mysql' %>
6
24
  test:
7
25
  adapter: mysql2
8
- <% if ENV['CI'] %>
9
- username: root
10
- password:
26
+ database: <%= db_prefix %>_test
27
+ <% unless db_username.blank? %>
28
+ username: <%= db_username %>
11
29
  <% end %>
12
- database: <%= database_prefix %>_test
13
- encoding: utf8
14
- <% unless ENV['DB_HOST'].blank? %>
15
- host: <%= ENV['DB_HOST'] %>
30
+ <% unless db_password.blank? %>
31
+ password: <%= db_password %>
32
+ <% end %>
33
+ <% unless db_host.blank? %>
34
+ host: <%= db_host %>
16
35
  <% end %>
17
- <% when 'postgres', 'postgresql' %>
18
- <% db_host = ENV['DB_HOST'] %>
36
+ encoding: utf8
37
+ <% when 'postgres' %>
19
38
  test:
20
39
  adapter: postgresql
21
- database: <%= database_prefix %>_test
22
- username: postgres
23
- min_messages: warning
24
- <% unless db_host.blank? %>
40
+ database: <%= db_prefix %>_test
41
+ username: <%= db_username.presence || "postgres" %>
42
+ <% unless db_password.blank? %>
43
+ password: <%= db_password %>
44
+ <% end %>
45
+ <% unless db_host.blank? %>
25
46
  host: <%= db_host %>
26
- <% end %>
27
- <% when 'sqlite', '', nil %>
47
+ <% end %>
48
+ min_messages: warning
49
+ <% when 'sqlite' %>
28
50
  test:
29
51
  adapter: sqlite3
30
- database: db/<%= database_prefix %>_test.sqlite3
52
+ database: db/<%= db_prefix %>_test.sqlite3
31
53
  timeout: 10000
32
- <% else %>
33
- <% raise "Invalid DB specified: #{ENV['DB']}" %>
34
54
  <% end %>