A business is only as smart as its data. Let's build a system to manage our data and execute business intelligence queries against the data from a typical e-commerce business.
- Use tests to drive both the design and implementation of code
- Decompose a large application into components
- Design a solution that is functional, readable, maintainable, and testable
- Fork this repository
- Clone your forked repository
- Complete as much of the activity below as you have time for during this exploration
- Push your code to the main branch of your repository
- Submit a Pull Request back to the turingschool-examples repository (this repo)
From a technical perspective, this project will emphasize:
- CSV I/O
- Database Operations
- Encapsulating Responsibilities
The idea of a DAL is to write classes which load and parse your raw data, allowing your system to then interact with rich Ruby objects to do more complex analysis.
In many applications, including the Rails projects you will build in Module 2, the DAL is incorporated in an ORM (object-relational mapping tool) that works with a database to load and parse data into rich Ruby objects.
In this exercise, we'll build the beginnings of a DAL with some ORM-like functionality by building the classes described below.
In order to interact with our DAL, let's start with one common root, a SalesEngine
instance:
sales_engine = SalesEngine.from_csv({
:items => "./data/items.csv",
:merchants => "./data/merchants.csv",
})
From there we can find the child instances:
item_collection
returns an array of all items (This method will end up referencing an instance ofItemCollection
)merchant_collection
returns an array of all merchants (This method will end up referencing an instance ofMerchantCollection
)
Note: Some of the tests for the SalesEngine
class have been written for you, but likely need expanded to make the tests more robust.
The merchant is one of the critical concepts in our data hierarchy.
id
- returns the integer id of the merchantname
- returns the name of the merchant
We create an instance like this:
merchant = Merchant.new({:id => 5, :name => "Turing School"})
The MerchantCollection
is responsible for holding and searching our Merchant
instances. It offers the following methods:
all
- returns an array of all knownMerchant
instancesfind()
- returns eithernil
or an instance ofMerchant
with a matching ID
The data can be found in data/merchants.csv
so the instance is created and used like this:
sales_engine = SalesEngine.from_csv({
:items => "./data/items.csv",
:merchants => "./data/merchants.csv",
})
merchant_collection = sales_engine.merchant_collection
merchant = merchant_collection.find(34)
# => <Merchant>
merchants = merchant_collection.all
# => [<Merchant>, <Merchant>...]
The Item instance offers the following methods:
id
- returns the integer id of the itemname
- returns the name of the itemdescription
- returns the description of the itemunit_price
- returns the price of the itemmerchant_id
- returns the integer merchant id of the item
We create an instance like this:
item = Item.new({
:id => 1,
:name => "Pencil",
:description => "You can use it to write things",
:unit_price => 1099,
:merchant_id => 2
})
The ItemCollection
is responsible for holding and searching our Item
instances. This object represents one line of data from the file items.csv
.
It offers the following methods:
all
- returns an array of all knownItem
instanceswhere(merchant_id)
- returns either an empty array, or an array of all items with a merchant_id matching the given argument.
It's initialized and used like this:
sales_engine = SalesEngine.from_csv({
:items => "./data/items.csv",
:merchants => "./data/merchants.csv"
})
merchant_collection = sales_engine.merchants
item_collection = sales_engine.item_collection
merchant = merchant_collection.find(34)
items = item_collection.where(merchant.id)
# => [<Item>, <Item>, ...., <Item>]
Up to this point, we have only asked for information, but in the real world, we would not only need to Read
information from our database, but also Create
, Update
, and Destroy
records. Put together, these 4 actions (CRUD) constitute everything you might need to do to a record in a database. Let's practice these concepts by updating our MerchantCollection
class to respond to the following methods:
-
create({name: 'Monster Merchant'})
- This should create a new instance of Merchant with a unique ID, and store it in our Merchant Collection. -
update({id: 34, name: 'New Merchant Name'})
- This should change the name of the Merchant instance with the matching id to the given value. -
destroy(34)
- This should remove the merchant with the given id from the Merchant Collection.
Now that you can search for a merchant by id, and search for items by merchant id, let's change our Item's where
method to satisfy the following interaction pattern:
sales_engine = SalesEngine.from_csv({
:items => "./data/items.csv",
:merchants => "./data/merchants.csv"
})
merchant_collection = sales_engine.merchants
item_collection = sales_engine.items
all_pencils = item_collection.where({merchant_id: 34})
# => [<Item>, <Item>, <Item>]
all_pencils = item_collection.where({name: 'Pencil'})
# => [<Item>, <Item>, <Item>]
all_fifty_cent_items = item_collection.where({price: 50})
# => [<Item>, <Item>, <Item>]
With this new implementation of where
, you should be able to send a key/value pair into the method to return all items where the attribute(key) matches the value given.
You have now created your very own DAL/ORM!! Going into Mod 2, you will start using the ActiveRecord ORM (much more powerful that the one we created today). If you are interested in learning more, take a look at the resources below: