Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.git
.dockerignore
.env
boilerplate/node_modules/
boilerplate/vendor/bundle/
boilerplate/tmp/
68 changes: 68 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
*.rbc
capybara-*.html
.rspec
/db/*.sqlite3
/db/*.sqlite3-journal
/db/*.sqlite3-[0-9]*
/public/system
/coverage/
/spec/tmp
*.orig
rerun.txt
pickle-email-*.html

# Ignore all logfiles and tempfiles.
/log/*
/tmp/*
!/log/.keep
!/tmp/.keep

# TODO Comment out this rule if you are OK with secrets being uploaded to the repo
config/initializers/secret_token.rb
config/master.key

# Only include if you have production secrets in this file, which is no longer a Rails default
# config/secrets.yml

# dotenv
# TODO Comment out this rule if environment variables can be committed
.env

## Environment normalization:
/.bundle
/vendor/bundle

# these should all be checked in to normalize the environment:
# Gemfile.lock, .ruby-version, .ruby-gemset

# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
.rvmrc

# if using bower-rails ignore default bower_components path bower.json files
/vendor/assets/bower_components
*.bowerrc
bower.json

# Ignore pow environment settings
.powenv

# Ignore Byebug command history file.
.byebug_history

# Ignore node_modules
node_modules/

# Ignore precompiled javascript packs
/public/packs
/public/packs-test
/public/assets

# Ignore yarn files
/yarn-error.log
yarn-debug.log*
.yarn-integrity

# Ignore uploaded files in development
/storage/*
!/storage/.keep
.irb_history
30 changes: 30 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Dockerfile development version
FROM ruby:latest AS source-development

# Install yarn
# RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg -o /root/yarn-pubkey.gpg && apt-key add /root/yarn-pubkey.gpg
# RUN echo "deb https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list
# RUN apt-get update && apt-get install -y --no-install-recommends nodejs yarn

ARG UID
ARG GID

# Default directory
ENV INSTALL_PATH /opt/app
RUN mkdir -p $INSTALL_PATH

# Cache directory
RUN mkdir -p /.cache
RUN chown $UID:$GID /.cache

# Install gems
WORKDIR $INSTALL_PATH
COPY source/ .
# RUN rm -rf node_modules
RUN rm -rf vendor
RUN gem install rails bundler
RUN bundle install
# RUN yarn install

# Start server
CMD bundle exec unicorn -c config/unicorn.rb
5 changes: 5 additions & 0 deletions Dockerfile.nginx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
FROM nginx:latest
COPY reverse-proxy.conf /etc/nginx/conf.d/reverse-proxy.conf
EXPOSE 3000
STOPSIGNAL SIGTERM
CMD ["nginx", "-g", "daemon off;"]
214 changes: 180 additions & 34 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,53 +1,199 @@
# List Posts by Rating Challenge

# Challenges
Code challenges for developers and designers.
![Ruby](https://img.shields.io/badge/ruby-%23CC342D.svg?style=for-the-badge&logo=ruby&logoColor=white)
![Rails](https://img.shields.io/badge/rails-%23CC0000.svg?style=for-the-badge&logo=ruby-on-rails&logoColor=white)
![Postgres](https://img.shields.io/badge/postgres-%23316192.svg?style=for-the-badge&logo=postgresql&logoColor=white)

We believe recruitment is the most critical part of an organization. People should experience some time working together before deciding on making it permanent.
This repo contains code challenges we'd like to see solved by people interested in working with us.
A Rails application that implements a Reddit-style post scoring system with logarithmic ranking algorithms and time decay factors.

We tend not to place job ads, as we prefer references and proactive candidates. With this in mind, feel free to take a challenge and let us know you're working on it.
Mostly, we work with Ruby and java, but also have some stuff in javascript. Even if you don't have experience in these technologies, as long as you are willing to learn them and want to build great stuff for the web, we can probably be a good fit.
## Technology Stack

- **Ruby**: 3.4.5
- **Rails**: 8.0.2
- **PostgreSQL**: Latest (Docker)
- **Docker**: Containerized development environment (Initial docker from https://github.com/muromeo1/rails-docker-boilerplate)

### Available challenges
## Challenge

#### Backend Development
* [User Changes](/1-development/user-changes.md)
* [Client from the Bahamas](/1-development/client-from-the-bahamas.md)
* [List Posts by Ratings](/1-development/list_posts_by_rating.md)
### List Posts by Rating

#### Frontend Development
* [React Challenge](/2-frontend/react-challenge.md)
* [Design Implementation Challenge](/3-design-frontend/design-frontend-challenge.md)
You are a web programmer. You have users. Your users rate posts on your site. You want to put the highest-rated posts at the top and lowest-rated at the bottom. You need some sort of "score" to sort by.

### Objectives

### How we work ##
We'd like to see a working web service with the following endpoints:

* **People** - you know, the guys in the team(s). We're not afraid to ask for help and we're not afraid to express our opinions. We're here to help.eachother.
```
/upvote/:post_id
/downvote/:post_id
/posts/
```

* **Culture** - We follow RUPEAL's guiding principles:
No UI is required. You can use a database of your choice.

* Give your best
* Show That You Care
* Build an environment of strong, open and honest relationships
* Deliver WOW through your service
* Stay humble
* Do What's Right
* Be coachable and don’t take it personally
* Do more with less
* Pursue growth and personal development
* Have fun
Tech stack: Feel free to use the stack that you feel more comfortable.This means you can choose any language you need to get the job done.

* **You** - by joining our team, feel free to question all the items below and propose new ideas on how we work. This is definitely not a static thing.
Your solution should consider that if you have a first post with 600 up votes and 400 down votes means that you have 60% of up votes and 40% of down votes, and if you have another post with 6 up votes and 4 down votes you have the same % of the previous post, 60% of up votes and 40% of down votes.

* **GitHub** - all our code is hosted here. We use Pull Requests and do code reviews for those. Everyone on the team reviews PRs. It's expected that you write quality code with automated tests. Once a PR is reviewed and accepted, the person who opened the PR should merge it and delete the branch.
Hint: Note that the score in % of the two posts are equal, but the real "values" are significantly differents

* **Continuous Integration** - we use Semaphore and CircleCI for our CI needs. Everytime we push to a branch, our test suite runs on Semaphore on some projects, CircleCI on others.
You should be doing the solution on a specific branch. Once you have something to delivery, you can open a Pull Request. You can use the Pull Request if you'd like some feedback on your code or to discuss something with us.

* **Slack** - we mostly communicate asynchronously between the development team and with other teams. This is our chat tool. Abuse it.
### Things We value

* **Jira** - our products' backlog is managed in Jira. Once we estimate all known user-stories, the sprint backlog is automatically built. Each team member will select a user-story to work from that sprint backlog and will update the needed tasks' status. This way we keep focused until the end of the sprint.
- Clean code, we want you to show us the best code you can do
- If you are familiar with TDD, BDD or any testing process, please show us your skills

* **AWS** - our products are mainly hosted on AWS, so a basic knowledge of it is appreciated.
Tech stack: please use preferably Ruby / Rails or JAVA, but if you are more versatile in a different tech stack, you may use one of your choice.

* **Support tasks** - We are paranoid about providing top notch support to our customers. We have a dedicated customer support team working full-time communicating with clients. Our development team works very closely with the customer support team delivering happiness to our clients.

## Features

- **Post Scoring System**: Implements a logarithmic scoring algorithm similar to Reddit's ranking system
- **Vote Management**: Supports upvotes and downvotes with proper score calculations

## Scoring Algorithm
I decided to use Logarithmic Score (Reddit-style) because:
- Balances popularity and recency (old content naturally decays in ranking even if it has many votes)
- New content with even moderate upvotes can rise quickly
- Simple and fast to compute
- No confidence intervals or priors (just basic math and log)
- Directional control with sign
- Separates positive vs. negative reactions (posts with more downvotes than upvotes sink faster)
- Encourages early engagement (the time boost helps early upvoting significantly, rewarding viral momentum)

## Getting Started

### Prerequisites

- Docker
- Docker Compose

### Installation

1. Clone the repository:
```bash
git clone <repository-url>
cd list_posts_by_rating_challenge
```

2. Start the application:
```bash
docker compose build
docker compose up
```

4. Access the application at `http://localhost:3000`

## Usage

### Running Commands

Use the `bin/run` script to execute commands within the Docker container:

```bash
# Rails commands
bin/run rails console
bin/run rails routes
bin/run rails db:migrate
bin/run rails db:seed

# Ruby commands
bin/run ruby -v
bin/run bundle install
```

### API Endpoints

- `GET /posts` - List all posts with scores
- `POST /posts/:id/upvote` - Upvote a post
- `POST /posts/:id/downvote` - Downvote a post

### Interface
Access the application at `http://localhost:3000` and interact with the visual interface, it will show you the upvote and downvote endpoints working plus the score system working.

## Development

### Project Structure

```
source/
├── app/
│ ├── controllers/
│ │ ├── application_controller.rb
│ │ ├── posts_controller.rb # Web interface controller
│ │ └── api/
│ │ └── v1/
│ │ └── posts_controller.rb # API endpoints for posts
│ ├── models/
│ │ ├── application_record.rb
│ │ └── post.rb # Post model with scoring logic
│ ├── services/
│ │ └── post_scoring_service.rb # Core scoring algorithm
│ ├── views/
│ │ ├── layouts/
│ │ │ └── application.html.erb
│ │ └── posts/
│ │ ├── index.html.erb # Main posts listing page
│ │ └── _table.html.erb # Posts table partial
│ └── assets/
│ ├── stylesheets/
│ └── javascript/
├── config/
├── db/
│ ├── migrate/
│ │ └── 20250811120000_create_posts.rb # Posts table migration

│ └── seeds.rb # Sample data
├── spec/
│ ├── models/
│ │ └── post_spec.rb # Post model tests
│ ├── controllers/
│ │ ├── posts_controller_spec.rb # Web controller tests
│ │ └── api/
│ │ └── v1/
│ │ └── posts_controller_spec.rb # API controller tests
│ ├── services/
│ │ └── post_scoring_service_spec.rb # Scoring service tests
│ ├── requests/
│ │ └── api/
│ │ └── v1/
│ │ └── posts_spec.rb # API request tests
│ ├── factories/
│ │ └── posts.rb # Test data factories
│ └── features/
│ └── posts_spec.rb # Integration tests

```

### Key Files

- `app/services/post_scoring_service.rb` - Main scoring algorithm implementation
- `app/models/post.rb` - Post model with vote tracking
- `app/controllers/api/v1/posts_controller.rb` - API endpoints for posts

### Testing

Run the test suite:

```bash
bin/run rspec
```

## Configuration

### Time Decay Factor

The time decay factor is configurable in `PostScoringService`:

```ruby
TIME_DECAY_FACTOR = 45_000 # 12.5 hours in seconds, used by reddit
```

### Database

The application uses PostgreSQL for data persistence and efficient scoring queries.

## Notes
- The scoring system was translated to ruby with the help of AI
- The tests where created with the help of AI
12 changes: 12 additions & 0 deletions bin/new_app
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/bin/bash

read -p 'Enter new project name: ' name
cp -a ../rails-docker-boilerplate ../$name
cd ../$name
rm -rf .bin/new_app
grep -lR "????" ./env-example | xargs sed -i "s/????/$name/g"
sudo rm -rf ../rails-docker-boilerplate
rm -rf .git
git init -q
git checkout -qb main
$SHELL
3 changes: 3 additions & 0 deletions bin/run
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/bash

docker compose run --rm source $@
25 changes: 25 additions & 0 deletions bin/setup
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/bin/bash

echo == Copying env files ==
cp env-example .env
echo "UID=$(id -u)" >> .env
echo "GID=$(id -g)" >> .env
echo

echo == Creating volumes ==
docker volume create --name source-postgres
echo

echo == Building docker compose ==
docker compose build
echo

echo == Initializing database ==
docker compose run --rm source rake db:reset
docker compose run --rm source rake db:migrate
docker compose run --rm source rake db:test:prepare
echo

echo == Generating secret ==
echo "SECRET_TOKEN=$(docker compose run --rm source rake secret)" >> .env
echo == END ==
8 changes: 8 additions & 0 deletions bin/uninstall
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/bin/bash

echo == Stopping containers ==
# docker stop $(docker ps -aqf "name=rails-docker-boilerplate")
docker stop $(docker ps -q)

echo == Delete containers ==
docker system prune -a -f --volumes
Loading