Skip to content

Instantly share code, notes, and snippets.

@fedorkk
Created October 2, 2022 15:05
Show Gist options
  • Save fedorkk/91b3e344b8acde53e26eb1ade8dc53b2 to your computer and use it in GitHub Desktop.
Save fedorkk/91b3e344b8acde53e26eb1ade8dc53b2 to your computer and use it in GitHub Desktop.
# README
## Installation
It's a standard Rails application. It can be installed with:
```
bundle
```
The application doesn't use database, so you don't have to initialize it.
Tests can be started with Rspec: `rspec`
## Start
You can start application with the standard command:
```
rails server
```
## Usage
There is only one controller called `FileController` with two endpoints:
```
POST /file(.:format) file#create
GET /file(.:format) file#show
```
The `show` method is used to render the file uploading form, it's also used as a root path for the application. You can access it using path `127.0.0.1:3000` or `127.0.0.1:3000/file`.
The `create` endpoint is used for uploading the file and calculating the data. It renders the same `show` page with the scores data on it.
I haven't written any specs for the controller because it's just a very simple interface, completely unusable in real life and it probably should be changed with something more efficient. For example, if we upload a huge file, it could take a lot of time to calculate the scores from it. Probably the calculation should be moved to a delayed job, the data should be saved in some external storage, e.g. database or Redis. And then it should be possible to find the data on another page, or it should be sent back through the WebSocket or Ajax request. But this part is more related to the frontend side and I prefer to focus on the backend.
## Description
The main class that is responsible for the file reading is the `FileReader`. It goes through the file and reads it line by line. Lines are parsed with the `TxtLineParser`. I've moved the parsing logic in an additional class because then it'll be easier to implement reading for different types of files (XLS, CSV, e.t.c). We can just implement the line reading logic in another class and use it in the `FileReader` or use a Dependency Injection approach.
`FileReader` decides rather the new user should be saved or not, and then save it to the `DataStorage` if it should be. The `DataStorage` is in-memory storage, based on the simple hash. Each user record is saved with the UID as a key, so we can access a user using his UID very fast. Here I expect that user's names (A, B, C in the example) can be used as unique identifiers. In another case, it's necessary to have another identifier to be able to identify the user.
`DataStorage` has special methods to find a user, add a new one, update its `accepted` field or increase the `score`. It also keeps the `recommender_uid` to make it simple to find the recommender.
There is also a `ScoreCalculator` class that is responcible for the updating the scores for all users in the recommendation chain. It uses an endless loop that brakes when there is no recommender in the storage.
For the more specific cases, you can find comments directly in the code.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment