This repo is a Ruby on Rails API for a server to receive and host device counts and readings while storing all data in memory.
- Check that local ruby version matches with project version (3.0.1).
- Install Memcached
- For MacOS with HomeBrew, run
brew install memcached
- Ensure that memcached is working with
brew services restart memcached
- For MacOS with HomeBrew, run
- Run
bundle install
to install all third party packages are installed - Run
bin/rails s
to start the server
config/routes.rb
for endpoints to the APIapp/controllers/v1/devices_controller.rb
for actions and routing requestsapp/models/device.rb
for Device specific logiclib/memory_store.rb
for connection to the Memcached data layer
- Create a device
post "/record_count"
- Update a device
post "/record_count"
- Check device's latest reading
get "/latest_timestamp/:uuid"
- Check device's cumulative count
get "/cumulative_count/:uuid"
- Reset the Memecached process (
brew services restart memcached
) and verify that all previous data in memory has been removed
- Ruby on Rails API -- The server is set up using the Ruby on Rails web framework. While some may find that Ruby on Rails would be heavy handed for a simple web server, the framework provides a lot of benefits and features out of the box. https://guides.rubyonrails.org/api_app.html#why-use-rails-for-json-apis-questionmark
- Memcached -- The data is stored in memory using Memcached
- Separate Process -- Having the memory storage run on a separate process from the web server means that the server can be started and stopped without resetting the data in memory.
- Guaranteed to store data in memory
- Non-RESTful API -- The endpoints provided in the API match the business logic provided in the scope of work.
Due to the time limit provided, the API in the repo here is not up to production standards. Much time was spent investing in reading and writing data from the memory layer to the application.
For models in Ruby on Rails, classes would inherit from the ActiveRecord::Base class. ActiveRecord is an ORM that provides functionality for reading and writing data to and from the database. Since we are not using a standard database, we opted to not inherit from ActiveRecord and instead implement models at Plain Old Ruby Objects. This can be seen in the class methods find
and create
.
We also invested time in moving the client to write to and from the Memecached data into its own module that could be included in other models moving forward.
Overall, I don't think any single decision was the wrong decision. I view this API in its current state as a good foundation, both to serve its initial need, and also as being flexible and maintanable to include other features. There would be more work needed to get the API to a production-ready state, but that work is identified and could be implemented in a straight forward and confident manner.
Tests for the Device model, the Devices controller, the routes and parameters of the API, and the MemoryStore module would provide easier maintenance and stability in the application moving forward.
My choice in tests would be to add RSpec for writing test cases and assertions. Many reach for FactoryBot for adding test data -- I think in our current case that this choice would be overkill, and instead would opt to use RSpec doubles for mock data.
The API has simple guard clauses and nil checks to ensure that missing or malformed data does not cause the server to raise exceptions; however, error handling could be improved to cover more cases, as well as writing custom exceptions to raise more detailed messages to the user and to improve monitoring and visibility into application metrics.
The application has a few checks at the controller and model level to ensure that data is not passed to memory that we do not want to store; however, more could be done to ensure that data adheres to our standards. Adding in structs for readings would help improve how we can read and write data, and controlling what data we write to the memory layer from our Device#create
and Device#update
methods would also allow for better data validation.
In its current state, the API can receive and return data to anyone who knows a device's ID. Since each device has a long alphanumeric UUID, there is a layer of security through obfuscation; however, this would be insufficient to serve clients in the real world. Adding a token generation layer and check for each device would be a way to step up security and ensure