DEV Community

Riccardo Odone
Riccardo Odone

Posted on • Updated on • Originally published at odone.io

Rewriting to Haskell–Standing on the shoulders of Rails

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:


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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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!

Top comments (0)