physical 0.4.8 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 45adbe9fa036b31165e380d114a9742467b551368e2a2dfc720452f2fba97e4c
4
- data.tar.gz: 351f94658f866fe313b7499bcacef7de4a00c8370587dbbde9d81b093f6dd04d
3
+ metadata.gz: 03d9eb10ac73675e3aa2bac745e7a6d8a30f4b9c0c6cb810d4dac0235d0cc55f
4
+ data.tar.gz: ed902ddd5a9349009a43d0a883a5526f7097d759e742c6ff43b8baf904765f63
5
5
  SHA512:
6
- metadata.gz: 7fd937c2f74862b1d28a0f819622e8d7a29b141bca24b175707adea171ef1a7e95f86494757fe32d9d24088356ebc31967688a0b1c6b895171f0e5a746fd4e35
7
- data.tar.gz: df819d350b08f636b8afd2d56294982564f2662205b091b429fd58cc1d1c7db507a385a04966b2c153c336ae5eb46887ae1f9f226267ae227db21a69ef2ecb36
6
+ metadata.gz: 52d08252ebda560e9abf682eb011c5e2281afbf0c3eead4acc650b9af8714555f465ffc9c613318eb2bed9e86caf72edddde1ad8edb5f26aea2233b7ebc38b96
7
+ data.tar.gz: cfdb971e4bdf9d1ce7c830b8f6da93614e630251ca7307f91baddf05353f43e1c1ef51aa84dc3db505ffb8221bf7fde68ee1e6348462e4fd9cacc9c820c2efb8
data/CHANGELOG.md CHANGED
@@ -6,6 +6,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
6
6
 
7
7
  ## Unreleased
8
8
 
9
+ ## [0.5.0] - 2023-12-19
10
+
11
+ ### Added
12
+ - Introduce `Physical::Structure` class [#31]
13
+
14
+ ### Changed
15
+ - Use legitimate state/zip in location factory [#30]
16
+
17
+ ## [0.4.9] - 2023-08-02
18
+
19
+ ### Added
20
+ - Extract cuboid property handling into mixin [#25]
21
+ - Add properties to `Physical::Location` [#26]
22
+ - Add pallets to `Physical::Shipment` [#29]
23
+
24
+ ### Changed
25
+ - Faster package weight and volume calculations [#23]
26
+
9
27
  ## [0.4.8] - 2023-03-21
10
28
 
11
29
  ### Added
@@ -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,32 @@
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, :description
7
7
 
8
- def initialize(id: nil, container: nil, items: [], void_fill_density: Measured::Density(0, :g_ml), dimensions: nil, weight: nil, properties: {})
8
+ def initialize(id: nil, container: nil, items: [], void_fill_density: Measured::Density(0, :g_ml), dimensions: nil, weight: nil, description: 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
+ @description = description
13
+
12
14
  @items = Set[*items]
15
+ @items_weight = @items.map(&:weight).reduce(Measured::Weight(0, :g), &:+)
16
+ @used_volume = @items.map(&:volume).reduce(Measured::Volume(0, :ml), &:+)
13
17
  end
14
18
 
15
19
  delegate [:dimensions, :width, :length, :height, :properties, :volume] => :container
16
20
 
17
21
  def <<(other)
18
22
  @items.add(other)
23
+ @items_weight += other.weight
24
+ @used_volume += other.volume
19
25
  end
20
26
  alias_method :add, :<<
21
27
 
22
28
  def >>(other)
23
29
  @items.delete(other)
30
+ @items_weight -= other.weight
31
+ @used_volume -= other.volume
24
32
  end
25
33
  alias_method :delete, :>>
26
34
 
@@ -29,29 +37,19 @@ module Physical
29
37
  end
30
38
 
31
39
  # Cost is optional. We will only return an aggregate if all items
32
- # have cost defined. Otherwise we will retun nil.
40
+ # have cost defined. Otherwise we will return nil.
33
41
  # @return Money
34
42
  def items_value
35
43
  items_cost = items.map(&:cost)
36
44
  items_cost.reduce(&:+) if items_cost.compact.size == items_cost.size
37
45
  end
38
46
 
39
- # @return [Measured::Weight]
40
- def items_weight
41
- items.map(&:weight).reduce(Measured::Weight(0, :g), &:+)
42
- end
43
-
44
47
  def void_fill_weight
45
48
  return Measured::Weight(0, :g) if container.volume.value.infinite?
46
49
 
47
50
  Measured::Weight(void_fill_density.convert_to(:g_ml).value * remaining_volume.convert_to(:ml).value, :g)
48
51
  end
49
52
 
50
- # @return [Measured::Volume]
51
- def used_volume
52
- items.map(&:volume).reduce(Measured::Volume(0, :ml), &:+)
53
- end
54
-
55
53
  def remaining_volume
56
54
  container.inner_volume - used_volume
57
55
  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,16 +6,24 @@ module Physical
6
6
  :origin,
7
7
  :destination,
8
8
  :service_code,
9
+ :pallets,
10
+ :structures,
9
11
  :packages,
10
12
  :options
11
13
 
12
- def initialize(id: nil, origin: nil, destination: nil, service_code: nil, packages: [], options: {})
14
+ def initialize(id: nil, origin: nil, destination: nil, service_code: nil, pallets: [], structures: [], packages: [], options: {})
13
15
  @id = id || SecureRandom.uuid
14
16
  @origin = origin
15
17
  @destination = destination
16
18
  @service_code = service_code
19
+ @structures = structures
17
20
  @packages = packages
18
21
  @options = options
22
+
23
+ return unless pallets.any?
24
+
25
+ warn "[DEPRECATION] `pallets` is deprecated. Please use `structures` instead."
26
+ @pallets = pallets
19
27
  end
20
28
  end
21
29
  end
@@ -4,16 +4,17 @@ FactoryBot.define do
4
4
  factory :physical_location, class: 'Physical::Location' do
5
5
  transient do
6
6
  country_code { 'US' }
7
- region_code { 'IL' }
7
+ region_code { 'VA' }
8
8
  end
9
9
 
10
10
  name { 'Jane Doe' }
11
11
  company_name { 'Company' }
12
12
  address1 { '11 Lovely Street' }
13
- address2 { 'South' }
13
+ address2 { 'Suite 100' }
14
14
  city { 'Herndon' }
15
- sequence(:zip, 10_001, &:to_s)
15
+ zip { '20170' }
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,8 +4,14 @@ 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) } # deprecated, will be removed
7
8
  packages { build_list(:physical_package, 2) }
8
9
  service_code { "usps_priority_mail" }
9
10
  initialize_with { new(**attributes) }
11
+
12
+ trait :freight do
13
+ structures { build_list(:physical_structure, 1) }
14
+ service_code { "tforce_freight" }
15
+ end
10
16
  end
11
17
  end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ FactoryBot.define do
4
+ factory :physical_structure, class: "Physical::Structure" do
5
+ container { FactoryBot.build(:physical_pallet) }
6
+ packages { build_list(:physical_package, 2) }
7
+ initialize_with { new(**attributes) }
8
+ end
9
+ end
@@ -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"
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Physical
4
+ class Structure
5
+ extend Forwardable
6
+ attr_reader :id, :container, :packages, :packages_weight, :used_volume
7
+
8
+ def initialize(id: nil, container: nil, packages: [], dimensions: nil, weight: nil, properties: {})
9
+ @id = id || SecureRandom.uuid
10
+ @container = container || Physical::Pallet.new(dimensions: dimensions || [], weight: weight || Measured::Weight(0, :g), properties: properties)
11
+
12
+ @packages = Set[*packages]
13
+ @packages_weight = @packages.map(&:weight).reduce(Measured::Weight(0, :g), &:+)
14
+ @used_volume = @packages.map(&:volume).reduce(Measured::Volume(0, :ml), &:+)
15
+ end
16
+
17
+ delegate [:dimensions, :width, :length, :height, :properties, :volume] => :container
18
+
19
+ def <<(other)
20
+ @packages.add(other)
21
+ @packages_weight += other.weight
22
+ @used_volume += other.volume
23
+ end
24
+ alias_method :add, :<<
25
+
26
+ def >>(other)
27
+ @packages.delete(other)
28
+ @packages_weight -= other.weight
29
+ @used_volume -= other.volume
30
+ end
31
+ alias_method :delete, :>>
32
+
33
+ def weight
34
+ container.weight + packages_weight
35
+ end
36
+
37
+ # Cost is optional. We will only return an aggregate if all packages
38
+ # have items value defined. Otherwise we will return nil.
39
+ # @return Money
40
+ def packages_value
41
+ packages_cost = packages.map(&:items_value)
42
+ packages_cost.reduce(&:+) if packages_cost.compact.size == packages_cost.size
43
+ end
44
+
45
+ def remaining_volume
46
+ container.inner_volume - used_volume
47
+ end
48
+
49
+ def density
50
+ return Measured::Density(Float::INFINITY, :g_ml) if container.volume.value.zero?
51
+ return Measured::Density(0.0, :g_ml) if container.volume.value.infinite?
52
+
53
+ Measured::Density(weight.convert_to(:g).value / container.volume.convert_to(:ml).value, :g_ml)
54
+ end
55
+ end
56
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Physical
4
- VERSION = "0.4.8"
4
+ VERSION = "0.5.0"
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"
@@ -11,6 +12,7 @@ require "physical/pallet"
11
12
  require "physical/item"
12
13
  require "physical/location"
13
14
  require "physical/shipment"
15
+ require "physical/structure"
14
16
 
15
17
  module Physical
16
18
  # Your code goes here...
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.8
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Martin Meyerhoff
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-03-21 00:00:00.000000000 Z
11
+ date: 2023-12-19 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
@@ -206,7 +207,11 @@ files:
206
207
  - lib/physical/spec_support/factories/package_factory.rb
207
208
  - lib/physical/spec_support/factories/pallet_factory.rb
208
209
  - lib/physical/spec_support/factories/shipment_factory.rb
210
+ - lib/physical/spec_support/factories/structure_factory.rb
209
211
  - lib/physical/spec_support/shared_examples.rb
212
+ - lib/physical/spec_support/shared_examples/a_cuboid.rb
213
+ - lib/physical/spec_support/shared_examples/has_property_readers.rb
214
+ - lib/physical/structure.rb
210
215
  - lib/physical/test_support.rb
211
216
  - lib/physical/types.rb
212
217
  - lib/physical/version.rb
@@ -230,7 +235,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
230
235
  - !ruby/object:Gem::Version
231
236
  version: '0'
232
237
  requirements: []
233
- rubygems_version: 3.3.26
238
+ rubygems_version: 3.4.22
234
239
  signing_key:
235
240
  specification_version: 4
236
241
  summary: A facade to deal with physical packages