physical 0.4.7 → 0.4.9

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4afd0ceee652dcc6cfb4cd62b89784d25991d99102f65c11a4905a8b48ea1525
4
- data.tar.gz: 8e1bb6c41964c9c51ae5dd38d5c8bd7e16a1401221f0895cab3628f6c9cb1ff0
3
+ metadata.gz: 8198a4417d9f1dd923a4bfee9bf8695ecb1c270853182a92835e64c1b9f4e10c
4
+ data.tar.gz: 8a007792624c95421bce9a09abb978de1142b8a2bfbfe6cef049c860ca101416
5
5
  SHA512:
6
- metadata.gz: bb8ca914487384660d59b352b5b7732cddcc251c8e3355e59549080b54d469a6f9d3f5b55424acf6467b90289a7d47e59f9a34adbf78ebe8c48b69b7582fa21a
7
- data.tar.gz: 3caa65b04977f5a091077fe2e6ebcc9c11fa335a6b11cd8091dc1d8da6c4e2e7ca3312b3561b3200537ef8210d22bfbcca5dd7bafc6c5cf8c8ec39fb587589a7
6
+ metadata.gz: b39b9310b28cd5bd9cfe3ea0511f6c615f3c130060693296e6aa01b18d3c893c9cd1704954133e9655c6a47fbe66f636eabc95c4a012f1e34ae4a0e634f6c0b3
7
+ data.tar.gz: 4881137e7cd922d5a9e56bcaec2fd5701b1e21f3e4ed53528585c14a2c24e0f2b90d93b2b36c7327160dae03cdc9c3e0f3a6171abb898d17843de862bf1f02a1
data/CHANGELOG.md CHANGED
@@ -6,12 +6,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
6
6
 
7
7
  ## Unreleased
8
8
 
9
+ ## [0.4.9] - 2023-08-02
10
+
11
+ ### Added
12
+ - Extract cuboid property handling into mixin [#25]
13
+ - Add properties to `Physical::Location` [#26]
14
+ - Add pallets to `Physical::Shipment` [#29]
15
+
16
+ ### Changed
17
+ - Faster package weight and volume calculations [#23]
18
+
19
+ ## [0.4.8] - 2023-03-21
20
+
21
+ ### Added
22
+ - Add `#items_value` to `Physical::Package` [#21]
23
+
9
24
  ## [0.4.7] - 2022-12-14
10
25
 
11
26
  ### Changed
12
- - Relax Dry::Types dependency to "~> 1.0"
27
+ - Relax Dry::Types dependency to "~> 1.0" [#20]
13
28
 
14
29
  ## [0.4.5] - 2022-09-28
30
+
15
31
  ### Added
16
32
  - Add `Physical::Pallet` class [#12]
17
33
  - Convenience methods for weight, volume, and fill [#15]
@@ -24,22 +40,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
24
40
  - Ruby 3 support [#18]
25
41
 
26
42
  ## [0.4.4] - 2019-10-29
43
+
27
44
  ### Added
28
45
  - Add `#sku`, `#cost` and `#description` to `Physical::Item`
29
46
 
30
47
  ## [0.4.3] - 2019-10-14
48
+
31
49
  ### Added
32
50
  - Add `#latitude` and `#longitude` to `Physical::Location`
33
51
 
34
52
  ## [0.4.2] - 2019-09-04
53
+
35
54
  ### Changed
36
55
  - Relax `Measured` Gem dependency
37
56
 
38
57
  ## [0.4.1] - 2019-07-15
58
+
39
59
  ### Added
40
60
  - Add `max_weight` to `Physical::Package`
41
61
 
42
62
  ## [0.4.0] - 2019-07-10
63
+
43
64
  ### Added
44
65
  - `Measured::Density` Type and density calculations [@tvdeyen](https://github.com/mamhoff/physical/pull/19)
45
66
  - Use `Measured::Density` Type when initializing `Physical::Package#void_fill_density` [@mamhoff](https://github.com/mamhoff/physical/pull/22)
@@ -4,6 +4,8 @@ require 'measured'
4
4
 
5
5
  module Physical
6
6
  class Cuboid
7
+ include PropertyReaders
8
+
7
9
  attr_reader :dimensions, :length, :width, :height, :weight, :id, :properties
8
10
 
9
11
  def initialize(id: nil, dimensions: [], weight: Measured::Weight(0, :g), properties: {})
@@ -33,27 +35,6 @@ module Physical
33
35
 
34
36
  private
35
37
 
36
- NORMALIZED_METHOD_REGEX = /(\w+)\??$/.freeze
37
-
38
- def method_missing(method)
39
- symbolized_properties = properties.symbolize_keys
40
- method_name = normalize_method_name(method)
41
- if symbolized_properties.key?(method_name)
42
- symbolized_properties[method_name]
43
- else
44
- super
45
- end
46
- end
47
-
48
- def respond_to_missing?(method, *args)
49
- method_name = normalize_method_name(method)
50
- properties.symbolize_keys.key?(method_name) || super
51
- end
52
-
53
- def normalize_method_name(method)
54
- method.to_s.sub(NORMALIZED_METHOD_REGEX, '\1').to_sym
55
- end
56
-
57
38
  def fill_dimensions(dimensions)
58
39
  dimensions.fill(dimensions.length..2) do |index|
59
40
  @dimensions[index] || Measured::Length(self.class::DEFAULT_LENGTH, :cm)
@@ -4,6 +4,8 @@ require 'carmen'
4
4
 
5
5
  module Physical
6
6
  class Location
7
+ include PropertyReaders
8
+
7
9
  ADDRESS_TYPES = %w(residential commercial po_box).freeze
8
10
 
9
11
  attr_reader :country,
@@ -20,7 +22,8 @@ module Physical
20
22
  :address_type,
21
23
  :company_name,
22
24
  :latitude,
23
- :longitude
25
+ :longitude,
26
+ :properties
24
27
 
25
28
  def initialize(
26
29
  name: nil,
@@ -37,7 +40,8 @@ module Physical
37
40
  email: nil,
38
41
  address_type: nil,
39
42
  latitude: nil,
40
- longitude: nil
43
+ longitude: nil,
44
+ properties: {}
41
45
  )
42
46
 
43
47
  @country = if country.is_a?(Carmen::Country)
@@ -65,6 +69,7 @@ module Physical
65
69
  @address_type = address_type
66
70
  @latitude = latitude
67
71
  @longitude = longitude
72
+ @properties = properties
68
73
  end
69
74
 
70
75
  def residential?
@@ -3,24 +3,31 @@
3
3
  module Physical
4
4
  class Package
5
5
  extend Forwardable
6
- attr_reader :container, :items, :void_fill_density, :id
6
+ attr_reader :id, :container, :items, :void_fill_density, :items_weight, :used_volume
7
7
 
8
8
  def initialize(id: nil, container: nil, items: [], void_fill_density: Measured::Density(0, :g_ml), dimensions: nil, weight: nil, properties: {})
9
9
  @id = id || SecureRandom.uuid
10
10
  @void_fill_density = Types::Density[void_fill_density]
11
11
  @container = container || Physical::Box.new(dimensions: dimensions || [], weight: weight || Measured::Weight(0, :g), properties: properties)
12
+
12
13
  @items = Set[*items]
14
+ @items_weight = @items.map(&:weight).reduce(Measured::Weight(0, :g), &:+)
15
+ @used_volume = @items.map(&:volume).reduce(Measured::Volume(0, :ml), &:+)
13
16
  end
14
17
 
15
18
  delegate [:dimensions, :width, :length, :height, :properties, :volume] => :container
16
19
 
17
20
  def <<(other)
18
21
  @items.add(other)
22
+ @items_weight += other.weight
23
+ @used_volume += other.volume
19
24
  end
20
25
  alias_method :add, :<<
21
26
 
22
27
  def >>(other)
23
28
  @items.delete(other)
29
+ @items_weight -= other.weight
30
+ @used_volume -= other.volume
24
31
  end
25
32
  alias_method :delete, :>>
26
33
 
@@ -28,9 +35,12 @@ module Physical
28
35
  container.weight + items_weight + void_fill_weight
29
36
  end
30
37
 
31
- # @return [Measured::Weight]
32
- def items_weight
33
- items.map(&:weight).reduce(Measured::Weight(0, :g), &:+)
38
+ # Cost is optional. We will only return an aggregate if all items
39
+ # have cost defined. Otherwise we will return nil.
40
+ # @return Money
41
+ def items_value
42
+ items_cost = items.map(&:cost)
43
+ items_cost.reduce(&:+) if items_cost.compact.size == items_cost.size
34
44
  end
35
45
 
36
46
  def void_fill_weight
@@ -39,11 +49,6 @@ module Physical
39
49
  Measured::Weight(void_fill_density.convert_to(:g_ml).value * remaining_volume.convert_to(:ml).value, :g)
40
50
  end
41
51
 
42
- # @return [Measured::Volume]
43
- def used_volume
44
- items.map(&:volume).reduce(Measured::Volume(0, :ml), &:+)
45
- end
46
-
47
52
  def remaining_volume
48
53
  container.inner_volume - used_volume
49
54
  end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Physical
4
+ module PropertyReaders
5
+ private
6
+
7
+ NORMALIZED_METHOD_REGEX = /(\w+)\??$/.freeze
8
+
9
+ def method_missing(method)
10
+ symbolized_properties = properties.symbolize_keys
11
+ method_name = normalize_method_name(method)
12
+ if symbolized_properties.key?(method_name)
13
+ symbolized_properties[method_name]
14
+ else
15
+ super
16
+ end
17
+ end
18
+
19
+ def respond_to_missing?(method, *args)
20
+ method_name = normalize_method_name(method)
21
+ properties.symbolize_keys.key?(method_name) || super
22
+ end
23
+
24
+ def normalize_method_name(method)
25
+ method.to_s.sub(NORMALIZED_METHOD_REGEX, '\1').to_sym
26
+ end
27
+ end
28
+ end
@@ -6,14 +6,16 @@ module Physical
6
6
  :origin,
7
7
  :destination,
8
8
  :service_code,
9
+ :pallets,
9
10
  :packages,
10
11
  :options
11
12
 
12
- def initialize(id: nil, origin: nil, destination: nil, service_code: nil, packages: [], options: {})
13
+ def initialize(id: nil, origin: nil, destination: nil, service_code: nil, pallets: [], packages: [], options: {})
13
14
  @id = id || SecureRandom.uuid
14
15
  @origin = origin
15
16
  @destination = destination
16
17
  @service_code = service_code
18
+ @pallets = pallets
17
19
  @packages = packages
18
20
  @options = options
19
21
  end
@@ -14,6 +14,7 @@ FactoryBot.define do
14
14
  city { 'Herndon' }
15
15
  sequence(:zip, 10_001, &:to_s)
16
16
  phone { '555-555-0199' }
17
+ email { 'jane@company.com' }
17
18
  region { country.subregions.coded(region_code) }
18
19
  country { Carmen::Country.coded(country_code) }
19
20
  initialize_with { new(**attributes) }
@@ -4,6 +4,7 @@ FactoryBot.define do
4
4
  factory :physical_shipment, class: "Physical::Shipment" do
5
5
  origin { FactoryBot.build(:physical_location) }
6
6
  destination { FactoryBot.build(:physical_location) }
7
+ pallets { build_list(:physical_pallet, 1) }
7
8
  packages { build_list(:physical_package, 2) }
8
9
  service_code { "usps_priority_mail" }
9
10
  initialize_with { new(**attributes) }
@@ -0,0 +1,197 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.shared_examples "a cuboid" do
4
+ subject(:cuboid) { described_class.new(**args) }
5
+
6
+ it_behaves_like "has property readers"
7
+
8
+ let(:args) do
9
+ {
10
+ dimensions: [
11
+ Measured::Length.new(1.1, :cm),
12
+ Measured::Length.new(3.3, :cm),
13
+ Measured::Length.new(2.2, :cm)
14
+ ]
15
+ }
16
+ end
17
+
18
+ it { is_expected.to be_a(Physical::Cuboid) }
19
+ it { is_expected.to respond_to(:id) }
20
+
21
+ describe "#dimensions" do
22
+ subject(:dimensions) { cuboid.dimensions }
23
+
24
+ it "has dimensions as Measured::Length objects with rational values" do
25
+ expect(dimensions).to eq(
26
+ [
27
+ Measured::Length.new(1.1, :cm),
28
+ Measured::Length.new(3.3, :cm),
29
+ Measured::Length.new(2.2, :cm)
30
+ ]
31
+ )
32
+ end
33
+
34
+ context "when given a one-element dimensions array" do
35
+ let(:args) do
36
+ {
37
+ dimensions: [Measured::Length(2, :cm)]
38
+ }
39
+ end
40
+
41
+ specify "the other dimensions are filled up with default length" do
42
+ expect(dimensions).to eq(
43
+ [
44
+ Measured::Length.new(2, :cm),
45
+ Measured::Length.new(default_length, :cm),
46
+ Measured::Length.new(default_length, :cm)
47
+ ]
48
+ )
49
+ end
50
+ end
51
+
52
+ context "when given a two-element dimensions array" do
53
+ let(:args) do
54
+ {
55
+ dimensions: [1, 2].map { |d| Measured::Length(d, :cm) }
56
+ }
57
+ end
58
+
59
+ it "the last dimension is filled up with default length" do
60
+ expect(dimensions).to eq(
61
+ [
62
+ Measured::Length.new(1, :cm),
63
+ Measured::Length.new(2, :cm),
64
+ Measured::Length.new(default_length, :cm)
65
+ ]
66
+ )
67
+ end
68
+ end
69
+
70
+ context "when given no arguments" do
71
+ let(:args) { {} }
72
+
73
+ it "assumes cm as the dimension_unit and the default length as value" do
74
+ expect(dimensions).to eq(
75
+ [
76
+ Measured::Length.new(default_length, :cm),
77
+ Measured::Length.new(default_length, :cm),
78
+ Measured::Length.new(default_length, :cm)
79
+ ]
80
+ )
81
+ end
82
+ end
83
+ end
84
+
85
+ describe "dimension methods" do
86
+ it "has getter methods for each dimension as Measured::Length object" do
87
+ expect(cuboid.length).to eq(Measured::Length.new(1.1, :cm))
88
+ expect(cuboid.width).to eq(Measured::Length.new(3.3, :cm))
89
+ expect(cuboid.height).to eq(Measured::Length.new(2.2, :cm))
90
+ end
91
+ end
92
+
93
+ describe "#weight" do
94
+ subject(:weight) { cuboid.weight }
95
+
96
+ context "with no weight given" do
97
+ let(:args) { {} }
98
+ it { is_expected.to eq(Measured::Weight(0, :g)) }
99
+ end
100
+
101
+ context "with a weight" do
102
+ let(:args) { { weight: Measured::Weight(1, :lb) } }
103
+ it { is_expected.to eq(Measured::Weight(453.59237, :g)) }
104
+ end
105
+ end
106
+
107
+ describe "#volume" do
108
+ subject(:volume) { cuboid.volume }
109
+
110
+ context "if all three dimensions are given" do
111
+ let(:args) do
112
+ {
113
+ dimensions: [1.1, 2.1, 3.2].map { |d| Measured::Length(d, :cm) }
114
+ }
115
+ end
116
+
117
+ it { is_expected.to eq(Measured::Volume(7.392, :ml)) }
118
+ end
119
+
120
+ context "if a dimension is missing" do
121
+ let(:args) do
122
+ {
123
+ dimensions: [1.1, 2.1].map { |d| Measured::Length(d, :cm) }
124
+ }
125
+ end
126
+
127
+ it { is_expected.to eq(Measured::Volume(default_length, :ml)) }
128
+ end
129
+ end
130
+
131
+ describe "#density" do
132
+ subject(:density) { cuboid.density.value.to_f }
133
+
134
+ let(:args) do
135
+ {
136
+ dimensions: dimensions,
137
+ weight: weight
138
+ }
139
+ end
140
+
141
+ context "if volume is larger than 0" do
142
+ let(:dimensions) do
143
+ [1.1, 2.1, 3.2].map { |d| Measured::Length(d, :in) }
144
+ end
145
+
146
+ context "if weight is 1" do
147
+ let(:weight) { Measured::Weight(1, :pound) }
148
+
149
+ it "returns the density in gramms per cubiq centimeter (ml)" do
150
+ is_expected.to eq(3.7445758536530196)
151
+ end
152
+ end
153
+
154
+ context "if weight is 0" do
155
+ let(:weight) { Measured::Weight(0, :pound) }
156
+
157
+ it { is_expected.to eq(0.0) }
158
+ end
159
+ end
160
+
161
+ context "if volume is 0" do
162
+ let(:dimensions) do
163
+ [1.1, 2.1].map { |d| Measured::Length(d, :in) }
164
+ end
165
+
166
+ let(:weight) { Measured::Weight(1, :pound) }
167
+ let(:expected_volume) { default_length.zero? ? BigDecimal::INFINITY : 0 }
168
+
169
+ it { is_expected.to eq(expected_volume) }
170
+ end
171
+
172
+ context "if volume is infinite" do
173
+ let(:dimensions) do
174
+ [1.1, 2.1].map { |d| Measured::Length(d, :in) }
175
+ end
176
+
177
+ let(:weight) { Measured::Weight(1, :pound) }
178
+ let(:expected_volume) { default_length.zero? ? BigDecimal::INFINITY : 0 }
179
+
180
+ it { is_expected.to eq(expected_volume) }
181
+ end
182
+ end
183
+
184
+ describe "#==" do
185
+ let(:args) { Hash[id: 123] }
186
+ let(:other_cuboid) { described_class.new(**args) }
187
+ let(:non_cuboid) { double(id: 123) }
188
+
189
+ it "compares cuboids" do
190
+ aggregate_failures do
191
+ expect(cuboid == other_cuboid).to be(true)
192
+ expect(cuboid == non_cuboid).to be(false)
193
+ expect(cuboid.nil?).to be(false)
194
+ end
195
+ end
196
+ end
197
+ end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.shared_examples 'has property readers' do
4
+ subject(:instance) { described_class.new(**args) }
5
+
6
+ describe "#properties" do
7
+ subject(:properties) { instance.properties }
8
+
9
+ let(:args) do
10
+ {
11
+ properties: { flammable: true }
12
+ }
13
+ end
14
+
15
+ it { is_expected.to eq({ flammable: true }) }
16
+ end
17
+
18
+ describe "properties methods" do
19
+ context "if method is a property" do
20
+ let(:args) do
21
+ {
22
+ properties: { already_packaged: true }
23
+ }
24
+ end
25
+
26
+ it "returns its value" do
27
+ expect(instance.already_packaged).to be(true)
28
+ end
29
+
30
+ it { is_expected.to respond_to(:already_packaged?) }
31
+ end
32
+
33
+ context "if method is a string property" do
34
+ let(:args) do
35
+ {
36
+ properties: { "already_packaged" => true }
37
+ }
38
+ end
39
+
40
+ it "returns its value" do
41
+ expect(instance.already_packaged).to be(true)
42
+ end
43
+
44
+ it { is_expected.to respond_to(:already_packaged?) }
45
+ end
46
+
47
+ context "if method is a boolean property" do
48
+ let(:args) do
49
+ {
50
+ properties: { already_packaged: true }
51
+ }
52
+ end
53
+
54
+ it "it is also accessible by its predicate method" do
55
+ expect(instance.already_packaged?).to be(true)
56
+ end
57
+
58
+ it { is_expected.to respond_to(:already_packaged?) }
59
+
60
+ context "with a falsey value" do
61
+ let(:args) do
62
+ {
63
+ properties: { already_packaged: false }
64
+ }
65
+ end
66
+
67
+ it "returns its value" do
68
+ expect(instance.already_packaged).to be(false)
69
+ end
70
+ end
71
+ end
72
+
73
+ context "if method is not a property" do
74
+ let(:args) do
75
+ {
76
+ properties: {}
77
+ }
78
+ end
79
+
80
+ it "raises method missing" do
81
+ expect { instance.already_packaged? }.to raise_error(NoMethodError)
82
+ end
83
+
84
+ it { is_expected.not_to respond_to(:already_packaged?) }
85
+ end
86
+ end
87
+ end
@@ -1,45 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- RSpec.shared_examples 'a cuboid' do
4
- let(:args) do
5
- {
6
- dimensions: [
7
- Measured::Length.new(1.1, :cm),
8
- Measured::Length.new(3.3, :cm),
9
- Measured::Length.new(2.2, :cm)
10
- ]
11
- }
12
- end
13
-
14
- it { is_expected.to be_a(Physical::Cuboid) }
15
-
16
- it "has dimensions as Measured::Length objects with rational values" do
17
- expect(subject.dimensions).to eq(
18
- [
19
- Measured::Length.new(1.1, :cm),
20
- Measured::Length.new(3.3, :cm),
21
- Measured::Length.new(2.2, :cm)
22
- ]
23
- )
24
- end
25
-
26
- it "has getter methods for each dimension as Measured::Length object" do
27
- expect(subject.length).to eq(Measured::Length.new(1.1, :cm))
28
- expect(subject.width).to eq(Measured::Length.new(3.3, :cm))
29
- expect(subject.height).to eq(Measured::Length.new(2.2, :cm))
30
- end
31
-
32
- describe "#==" do
33
- let(:args) { Hash[id: 123] }
34
- let(:other_cuboid) { described_class.new(**args) }
35
- let(:non_cuboid) { double(id: 123) }
36
-
37
- it "compares cuboids" do
38
- aggregate_failures do
39
- expect(subject == other_cuboid).to be(true)
40
- expect(subject == non_cuboid).to be(false)
41
- expect(subject.nil?).to be(false)
42
- end
43
- end
44
- end
45
- end
3
+ require "physical/spec_support/shared_examples/a_cuboid"
4
+ require "physical/spec_support/shared_examples/has_property_readers"
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Physical
4
- VERSION = "0.4.7"
4
+ VERSION = "0.4.9"
5
5
  end
data/lib/physical.rb CHANGED
@@ -4,6 +4,7 @@ require "money"
4
4
  require "measured/density"
5
5
  require "physical/types"
6
6
  require "physical/version"
7
+ require "physical/property_readers"
7
8
  require "physical/cuboid"
8
9
  require "physical/box"
9
10
  require "physical/package"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: physical
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.7
4
+ version: 0.4.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Martin Meyerhoff
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-12-14 00:00:00.000000000 Z
11
+ date: 2023-08-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: carmen
@@ -199,6 +199,7 @@ files:
199
199
  - lib/physical/location.rb
200
200
  - lib/physical/package.rb
201
201
  - lib/physical/pallet.rb
202
+ - lib/physical/property_readers.rb
202
203
  - lib/physical/shipment.rb
203
204
  - lib/physical/spec_support/factories/box_factory.rb
204
205
  - lib/physical/spec_support/factories/item_factory.rb
@@ -207,6 +208,8 @@ files:
207
208
  - lib/physical/spec_support/factories/pallet_factory.rb
208
209
  - lib/physical/spec_support/factories/shipment_factory.rb
209
210
  - lib/physical/spec_support/shared_examples.rb
211
+ - lib/physical/spec_support/shared_examples/a_cuboid.rb
212
+ - lib/physical/spec_support/shared_examples/has_property_readers.rb
210
213
  - lib/physical/test_support.rb
211
214
  - lib/physical/types.rb
212
215
  - lib/physical/version.rb
@@ -230,7 +233,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
230
233
  - !ruby/object:Gem::Version
231
234
  version: '0'
232
235
  requirements: []
233
- rubygems_version: 3.2.33
236
+ rubygems_version: 3.4.10
234
237
  signing_key:
235
238
  specification_version: 4
236
239
  summary: A facade to deal with physical packages