Search code examples
rubyinheritancerubygemssinatrareusability

Sharing ruby code for Sinatra::Base & Sinatra::Application classes


I am quite new with Sinatra framework and I am trying to do a gem compatible with Sinatra::Base & Sinatra::Application based apps. I have this code in my gem and it is working fine in both apps:

health_check.rb

class App1 < Sinatra::Base
  get '/health/liveness' do
    halt 204
  end
end

class App2 < Sinatra::Application
  get '/health/liveness' do
    halt 204
  end
end

But I the code is repeated and I would like to have something like this, but it does not work:

health_check.rb

module HealthHelper
  get '/health/liveness' do
    halt 204
  end
end

class App1 < Sinatra::Base
  include HealthHelper
end

class App2 < Sinatra::Application
  include HealthHelper
end

When I try to init any app with the gem included I get this error

/lib/health_check.rb:3:in `<module:HealthHelper>': undefined method `get' for HealthHelper:Module (NoMethodError)
Did you mean?  gets
               gem

Any idea to make it cleaner?


Solution

  • Rather than simply using include, you can write a Sinatra extension that defines the routes.

    It might look something like this:

    require 'sinatra/base'
    
    module HealthHelper
      def self.registered(app)
        app.get '/health/liveness' do
          halt 204
        end
      end
    end
    
    # This line is so it will work in classic Sinatra apps.
    Sinatra.register(HealthHelper)
    

    Then in your actual apps, you use register instead of include:

    require 'sinatra/base'
    require 'health_helper'
    
    class App1 < Sinatra::Base
      register HealthHelper
    end
    

    Now the routes will be available in App1. Note that you probably don’t want to be extending Sinatra::Application, but rather Sinatra::Base.