Skip to content

Commit 94b7875

Browse files
Pass options to coordinator dependencies
It's very useful that the simple_coordinator can have so many of it's internal classes configured. However, they can only be configured for one set of hardcoded behavior, based on the two input values, an order and inventory_units If you want the coordinator to behave differently in different scenarios e.g, the admin and the frontend, then you have to start getting creative. The simple_coordinator (and all it's configured classes) in their current state can only react to the state of the order and inventory_units argument, or they can react to globally set state (which is not a great pattern). Currently, the simple_coordinator is only called in two places in Solidus: during exchanges, and during creating proprosed shipments. However, it is reasonable for a complicated store to want to build shipments in other scenarios. One workaround to getting the coordinator to behave differently in these different locations is to include any arguments that you want to pass to the coordinator on the order as an attribute or database column, and then read the order attribute in your configured custom class. However, this isn't even a perfect workaround, because not every configurable class is passed the order (e.g. the location_sorter_class). To truly have the coordinator behave differently in different locations you need to do minor to extensive monkey patching This solution addresses the problem by allowing generic options to be passed to the simple coordinator, which are then passed down to each configurable class. This means that any caller of the simple_coordinator can customize the behavior it wants through these options and overriding the configurable inner classes. This for example, allows for customizations like shipment building behavior that is specific to the admin, where a admin user could be allowed to rebuild shipments with a stock location that is not normally available to users. This is however a **breaking change** for certain consumers of Solidus. Since this change adds an argument to the constructor of the following classes, if a consumer of solidus has a.) configured their own custom class and b.) overrode the constructor of their own custom class then their custom class could break. However, this error will cause shipment building to fail, so it should be very obvious to spot and correct. Additionally, there were few reasons to override the constructor of these configurable classes unless you had also overridden the simple_coordinator, as you did not have control of the arguments passed to these configurable classes. `inventory_unit_builder_class` `location_filter_class` `location_sorter_class` `allocator_class` `estimator_class` e.g. a custom configured stock location filter like this would be broken class StockLocationFilter < Spree::Stock::LocationFilter::Base def initialize(stock_locations, order) super(stock_locations, order) @some_variable = 'Some initializer' end ... end However, initializers like this will be fine, as they implicitly pass the original arguments: class StockLocationFilter < Spree::Stock::LocationFilter::Base def initialize(_stock_locations, order) super @my_variable = 'Some Value' end ... end Co-authored-by: Benjamin Willems <[email protected]>
1 parent e8c9675 commit 94b7875

File tree

7 files changed

+36
-21
lines changed

7 files changed

+36
-21
lines changed

core/app/models/spree/stock/allocator/base.rb

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@ module Spree
44
module Stock
55
module Allocator
66
class Base
7-
attr_reader :availability
7+
attr_reader :availability, :coordinator_options
88

9-
def initialize(availability)
9+
def initialize(availability, coordinator_options: {})
1010
@availability = availability
11+
@coordinator_options = coordinator_options
1112
end
1213

1314
def allocate_inventory(_desired)

core/app/models/spree/stock/estimator.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ class Estimator
66
class ShipmentRequired < StandardError; end
77
class OrderRequired < StandardError; end
88

9+
def initialize(coordinator_options: {})
10+
@coordinator_options = coordinator_options
11+
end
12+
913
# Estimate the shipping rates for a package.
1014
#
1115
# @param package [Spree::Stock::Package] the package to be shipped

core/app/models/spree/stock/inventory_unit_builder.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@
33
module Spree
44
module Stock
55
class InventoryUnitBuilder
6-
def initialize(order)
6+
def initialize(order, coordinator_options: {})
77
@order = order
8+
@coordinator_options = coordinator_options
89
end
910

1011
def units

core/app/models/spree/stock/location_filter/base.rb

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ class Base
1212
# @!attribute [r] stock_locations
1313
# @return [Enumerable<Spree::StockLocation>]
1414
# a collection of locations to sort
15-
attr_reader :stock_locations
15+
attr_reader :stock_locations, :coordinator_options
1616

1717
# @!attribute [r] order
1818
# @return <Spree::Order>
@@ -25,9 +25,12 @@ class Base
2525
# a collection of locations to sort
2626
# @param order <Spree::Order>
2727
# the order we are creating the shipment for
28-
def initialize(stock_locations, order)
28+
# @param coordinator_options [Hash]
29+
# a set of options passed from the stock_coordinator
30+
def initialize(stock_locations, order, coordinator_options: {})
2931
@stock_locations = stock_locations
3032
@order = order
33+
@coordinator_options = coordinator_options
3134
end
3235

3336
# Filter the stock locations.

core/app/models/spree/stock/location_sorter/base.rb

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,17 @@ class Base
1515
# @!attribute [r] stock_locations
1616
# @return [Enumerable<Spree::StockLocation>]
1717
# a collection of locations to sort
18-
attr_reader :stock_locations
18+
attr_reader :stock_locations, :coordinator_options
1919

2020
# Initializes the stock location sorter.
2121
#
2222
# @param stock_locations [Enumerable<Spree::StockLocation>]
2323
# a collection of locations to sort
24-
def initialize(stock_locations)
24+
# @param coordinator_options [Hash]
25+
# a set of options passed from the stock_coordinator
26+
def initialize(stock_locations, coordinator_options: {})
2527
@stock_locations = stock_locations
28+
@coordinator_options = coordinator_options
2629
end
2730

2831
# Sorts the stock locations.

core/app/models/spree/stock/simple_coordinator.rb

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,17 @@ class SimpleCoordinator
2525
# @api private
2626
attr_reader :inventory_units, :splitters, :stock_locations,
2727
:filtered_stock_locations, :inventory_units_by_variant, :desired,
28-
:availability, :allocator, :packages
28+
:availability, :allocator, :packages, :coordinator_options
2929

30-
def initialize(order, inventory_units = nil)
30+
def initialize(order, inventory_units = nil, coordinator_options: {})
3131
@order = order
32+
@coordinator_options = coordinator_options
3233
@inventory_units =
33-
inventory_units || Spree::Config.stock.inventory_unit_builder_class.new(order).units
34+
inventory_units || Spree::Config.stock.inventory_unit_builder_class.new(order, coordinator_options:).units
3435
@splitters = Spree::Config.environment.stock_splitters
3536

36-
@filtered_stock_locations = Spree::Config.stock.location_filter_class.new(load_stock_locations, order).filter
37-
sorted_stock_locations = Spree::Config.stock.location_sorter_class.new(filtered_stock_locations).sort
37+
@filtered_stock_locations = Spree::Config.stock.location_filter_class.new(load_stock_locations, order, coordinator_options:).filter
38+
sorted_stock_locations = Spree::Config.stock.location_sorter_class.new(filtered_stock_locations, coordinator_options:).sort
3839
@stock_locations = sorted_stock_locations
3940

4041
@inventory_units_by_variant = @inventory_units.group_by(&:variant)
@@ -44,7 +45,7 @@ def initialize(order, inventory_units = nil)
4445
stock_locations: stock_locations
4546
)
4647

47-
@allocator = Spree::Config.stock.allocator_class.new(availability)
48+
@allocator = Spree::Config.stock.allocator_class.new(availability, coordinator_options:)
4849
end
4950

5051
def shipments
@@ -69,7 +70,7 @@ def build_shipments
6970
# Turn the Stock::Packages into a Shipment with rates
7071
packages.map do |package|
7172
shipment = package.shipment = package.to_shipment
72-
shipment.shipping_rates = Spree::Config.stock.estimator_class.new.shipping_rates(package)
73+
shipment.shipping_rates = Spree::Config.stock.estimator_class.new(coordinator_options:).shipping_rates(package)
7374
shipment
7475
end
7576
end

core/spec/models/spree/stock/simple_coordinator_spec.rb

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,22 +11,26 @@ module Stock
1111

1212
describe "#shipments" do
1313
it 'uses the pluggable estimator class' do
14-
expect(Spree::Config.stock).to receive(:estimator_class).and_call_original
14+
expect(Spree::Config.stock.estimator_class).to receive(:new).with(coordinator_options: {}).and_call_original
15+
1516
subject.shipments
1617
end
1718

1819
it 'uses the configured stock location filter' do
19-
expect(Spree::Config.stock).to receive(:location_filter_class).and_call_original
20+
expect(Spree::Config.stock.location_filter_class).to receive(:new).with(anything, anything, coordinator_options: {}).and_call_original
21+
2022
subject.shipments
2123
end
2224

2325
it 'uses the configured stock location sorter' do
24-
expect(Spree::Config.stock).to receive(:location_sorter_class).and_call_original
26+
expect(Spree::Config.stock.location_sorter_class).to receive(:new).with(anything, coordinator_options: {}).and_call_original
27+
2528
subject.shipments
2629
end
2730

2831
it 'uses the pluggable allocator class' do
29-
expect(Spree::Config.stock).to receive(:allocator_class).and_call_original
32+
expect(Spree::Config.stock.allocator_class).to receive(:new).with(anything, coordinator_options: {}).and_call_original
33+
3034
subject.shipments
3135
end
3236

@@ -39,9 +43,7 @@ module Stock
3943
end
4044

4145
it 'uses the pluggable inventory unit builder class' do
42-
expect(Spree::Config.stock)
43-
.to receive(:inventory_unit_builder_class)
44-
.and_call_original
46+
expect(Spree::Config.stock.inventory_unit_builder_class).to receive(:new).with(anything, coordinator_options: {}).and_call_original
4547

4648
subject.shipments
4749
end

0 commit comments

Comments
 (0)