by Christina Chortaria, Carolyn Cole, Ryan Laddusaw, and Jane Sandberg
Allsearch is a search application with a VueJS frontend and a Ruby backend. The backend was originally in Rails, but we decided to move it to Rack. We chose Rack because it allowed us to do an in-place conversion – Rails applications are Rack applications already, we just needed to remove and replace the Rails pieces; no changes were needed to our deployment process. Deepening our understanding of Rack would also help us in our work with any Rack-based application, whether it is Rails, Hanami, or just Rack. Rack is quite simple and does not provide every feature that Rails does, so we supplemented Rack with:
Many Rails features had one-on-one replacements that you can adopt incrementally. We were able to do the following without much friction:
calldelegate method has a direct equivalent in pure Ruby: def_delegatorsRack::Test was very familiar after working with rails-rspec system testsDatabaseCleaner was a great replacement for rails-rspec transactional featuresReplacing the Rails router was an incremental process. We first used Rack::Builder and
its map method to create a Rack application that routed to all our Rack Applications,
and pointed our Rails router to this new “Router” application.
Rails’ router is tightly integrated into the built-in Rails initializers. Before we
could completely replace the Rails router with our own, we had to replace the
Rails.application.initialize! call to a new initialization process that is more
selective about which of Rails’ built-in initializers are run.
Zeitwerk is a nice autoloader. However, we can’t use our own autoloader and the Rails
autoloaders at the same time. The answer is the same as the routing: replace
Rails.application.initialize! with a more selective initialization process that does
not create Rails autoloaders.
present? and
blank? methods that made the code more explicit about possible states.rswag is an excellent gem for generating OpenAPI/Swagger documentation.
Unfortunately, since it is Rails-based, we had to say goodbye to it in
favor of a hacky OpenAPI/Swagger generator with a similar syntax.Here are a few key concepts that we now use in our application:
#call.Maybe monad – as opposed to other way of representing
data that may or may not exist like nil or an empty string – is that it
comes with many nice methods for querying, transforming, and combining with other monads.It has been 8 months since we finished the migration, and it has been working great so far! Please feel free to try out the Allsearch application or see the project Github.