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 +4 -4
- data/CHANGELOG.md +22 -1
- data/lib/physical/cuboid.rb +2 -21
- data/lib/physical/location.rb +7 -2
- data/lib/physical/package.rb +14 -9
- data/lib/physical/property_readers.rb +28 -0
- data/lib/physical/shipment.rb +3 -1
- data/lib/physical/spec_support/factories/location_factory.rb +1 -0
- data/lib/physical/spec_support/factories/shipment_factory.rb +1 -0
- data/lib/physical/spec_support/shared_examples/a_cuboid.rb +197 -0
- data/lib/physical/spec_support/shared_examples/has_property_readers.rb +87 -0
- data/lib/physical/spec_support/shared_examples.rb +2 -43
- data/lib/physical/version.rb +1 -1
- data/lib/physical.rb +1 -0
- metadata +6 -3
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 8198a4417d9f1dd923a4bfee9bf8695ecb1c270853182a92835e64c1b9f4e10c
         | 
| 4 | 
            +
              data.tar.gz: 8a007792624c95421bce9a09abb978de1142b8a2bfbfe6cef049c860ca101416
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 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)
         | 
    
        data/lib/physical/cuboid.rb
    CHANGED
    
    | @@ -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)
         | 
    
        data/lib/physical/location.rb
    CHANGED
    
    | @@ -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?
         | 
    
        data/lib/physical/package.rb
    CHANGED
    
    | @@ -3,24 +3,31 @@ | |
| 3 3 | 
             
            module Physical
         | 
| 4 4 | 
             
              class Package
         | 
| 5 5 | 
             
                extend Forwardable
         | 
| 6 | 
            -
                attr_reader :container, :items, :void_fill_density, : | 
| 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 | 
            -
                #  | 
| 32 | 
            -
                 | 
| 33 | 
            -
             | 
| 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
         | 
    
        data/lib/physical/shipment.rb
    CHANGED
    
    | @@ -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 | 
            -
             | 
| 4 | 
            -
             | 
| 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"
         | 
    
        data/lib/physical/version.rb
    CHANGED
    
    
    
        data/lib/physical.rb
    CHANGED
    
    
    
        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. | 
| 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:  | 
| 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. | 
| 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
         |