Rewriting to Haskell–Standing on the shoulders of Rails
Riccardo Odone
Posted on March 30, 2020
You can keep reading here or jump to my blog to get the full experience, including the wonderful pink, blue and white palette.
This is part of a series:
- Rewriting to Haskell–Intro
- Rewriting to Haskell–Project Setup
- Rewriting to Haskell–Deployment
- Rewriting to Haskell–Automatic Formatting
- Rewriting to Haskell–Configuration
In the intro we mentioned we wanted to work in "small valuable iterations". In fact, we know from experience scope creep is a thing and we want to keep our laser focus on the goal. Well, with the exception of the usual Oxford Discussion™ with Alex on functional code design.
When rewriting part of a mature codebase, a good way of breaking down work is to reuse what's already there. Turns out that with some outside-the-box thinking, we could make Rails our handrail.
We decided to start by rewriting one endpoint, the search one. As a matter of fact, in Stream, posts and comments can be found by submitting a query to the backend.
Rails takes care of authentication and, as said above, we wanted to keep the scope of the iteration small. Thus, we decided to leave it be and just delegate to Servant the search:
diff --git a/app/controllers/api/v1/search_controller.rb b/app/controllers/api/v1/search_controller.rb
class SearchController< Api::V1::ApiController
def index
- render json: posts, root: 'posts', status: :ok
+ q = { query: query, channel: channel, quantity: quantity, last_id: last_id, comments: comments }.compact
+ response = self.class.get("http://localhost:8080/servant/search", query: q)
+ render json: response, status: :ok
end
Furthermore, this enables us to reuse all the RSpec tests we have! Though we needed to change one line. In fact, to wipe the database we were running each spec inside a database transaction.
Since now Rails runs in one process and Servant in another, using a transaction would shadow data to the latter. This is because, until a transaction is committed, records are not available outside of it. Therefore, instead of cleaning the database with transactions, we will use truncation:
diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb
config.before(:each) do
- DatabaseCleaner.strategy = :transaction
+ DatabaseCleaner.strategy = :truncation
end
Also, in CI now we need to spin up the Servant application before running RSpec:
- run:
command: DATABASE=stream_test stack --no-terminal exec haskell-exe
background: true
Et voilà, now we don't have to write tests in Haskell. We will, we will but we bought ourselves some time at least 😎
Get the latest content via email from me personally. Reply with your thoughts. Let's learn from each other. Subscribe to my PinkLetter!
Posted on March 30, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.