.github/workflows | ||
bin | ||
lib | ||
spec | ||
.gitignore | ||
.rspec | ||
CODE_OF_CONDUCT.md | ||
Gemfile | ||
Gemfile.lock | ||
LICENSE.txt | ||
planet_express.gemspec | ||
Rakefile | ||
README.md |
PlanetExpress
Planet Express is your little helper to calculate possible discounts for your shipments based on a given rule set and an available discount budget.
Installation
Get the code from github and run bundle install
Afterwards you are ready to rumble.
I recommend ruby 2.6.6 or MRI 2.7 to run the code. Other ruby versions should work as well, but are not officially supported.
Usage
The input file has to adhere to the following format:
# ISO8601Date PackageSize Provider
2015-02-01 S PR
Now that you have an input file like that you can run the processing with the following rake task:
$ bundle exec rake 'process[path/to/input_file.txt]'
# 2015-02-01 S MR 1.50 0.50
# 2015-02-02 S MR 1.50 0.50
# 2015-02-03 L LP 6.90 -
# 2015-02-05 S LP 1.50 -
# 2015-02-06 S MR 1.50 0.50
# 2015-02-06 L LP 6.90 -
# 2015-02-07 L MR 4.00 -
# 2015-02-08 M MR 3.00 -
# 2015-02-09 L LP 0.00 6.90
# 2015-02-10 L LP 6.90 -
# 2015-02-10 S MR 1.50 0.50
# 2015-02-10 S MR 1.50 0.50
# 2015-02-11 L LP 6.90 -
# 2015-02-12 M MR 3.00 -
# 2015-02-13 M LP 4.90 -
# 2015-02-15 S MR 1.50 0.50
# 2015-02-17 L LP 6.90 -
# 2015-02-17 S MR 1.90 0.10
# 2015-02-24 L LP 6.90 -
# 2015-02-29 CUSPS IGNORED
# 2015-03-01 S MR 1.50 0.50
Architecture
The system is divided into four big groups:
- Discounts - provides a class to represent discounts as well as a budget to keep track of the discount budgets for a given month
- Shipments - Basic representation of a shipment as in "an order that will be shipped"
- ShippingOptions - Struct and repository to represent providers and their shipping options
- Rules - contains all the logic for discount rules including a methods to apply discounts to shipments
Discounts
The discount class represents a discount that originated from a rule and contains the monetary amount as well as the rule that was responsible for applying the discount.
Use the DiscountBudgetForMonthRepository
to get the discount budget for a given month.
Shipments
Shipments are the input for all calculation and have a date and a reference to a shipping option. They also keep track of their applied discounts.
Important: There's a distinction between a valid shipment (one that was parsed correctly)
and an invalid shipment. To not make you constantly check whether you handle
a valid shipment or an invalid one, you can use the if_valid
helper.
shipment.if_valid do |valid_shipment|
# code that is only executed in case you have a valid shipment on hand
end
The package also includes a ShipmentReader
that adheres to a simple enumerable
interface in order to decouple the IO/parsing from the remaining processing. If
you don't need it you can replace all instances of a ShipmentReader
with a simple
array.
To provide structure output you can use the ShipmentFormatter
which formats
a given Shipment
in a CSV (whitespace separated) format with their discounts.
Shipping Options
Shipping options are a struct to represent an individual shipping option, it includes the price, the provider as well as the package size.
Use the ShippingOptionRepository
to retrieve shipping options and query
for specific providers/package sizes.
ShipmentDiscountCalculator
This class is the central entry point for calculating discounts for a list of shipments. It takes a shipment_repository and a list of rules and applies the list of rules to the given shipments
Example:
calculator = ShipmentDiscountCalculator.new(
shipment_repository: ShipmentRepository.new(data: []),
rules: [Rule.new(...)])
calculator.run([Shipment.new])
Repository
The repository base class is the managing unit to retrieve and store shared state. When possible nobody should query the data directly, but define a query method in the repository to retrieve the required data.
It provides an ActiveRecord like where
method with which you can
query for multiple attributes in one call:
repo.where(attribute_a: 5, attribute_b: "Test") # => enumerable matching the criteria
Money Handling
In order to prevent floating errors when calculating discounts the system uses
integers and all prices are defined including the cents, so 1.5€ are represented
as 150
.
Development
After checking out the repo, run bin/setup
to install dependencies. Then, run rake spec
to run the tests. You can also run bin/console
for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run bundle exec rake install
. To release a new version, update the version number in version.rb
, and then run bundle exec rake release
, which will create a git tag for the version, push git commits and tags, and push the .gem
file to rubygems.org.
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/planet_express. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the code of conduct.
License
The gem is available as open source under the terms of the MIT License.
Code of Conduct
Everyone interacting in the PlanetExpress project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.