1

The gist

Is it possible to bake feature-toggle-like functionality into a Sinatra application?

A bit about feature toggles, just in-case ;)

Back story

I've set up a modular Sinatra project, and I tend to implement a GET/POST/PUT/DELETE endpoint for all my resources; it makes it easier to test the app and manipulate the data while in development.

Problem

When I go into production I don't want the unneeded endpoints to exist (e.g DELETE '/users').

Question

Can I annotate the methods with some kind of a :development flag, or maybe intercept the request in a before block? Would you do this using a helper? I'm not sure if I'm heading down the right path here, I'm probably over complicating it(?)

How would one go about this?

If you've done something like this it would be great if you can share your findings with the nation.

Community
  • 1
  • 1
MrHaze
  • 3,786
  • 3
  • 26
  • 47

1 Answers1

3

You can use the current environment to decide whether you define an action. For example:

class MyApp < Sinatra::Application
  if settings.development?
    get '/admin' do
      'VIPs only'
    end
  end
end

If you have a lot to toggle, you might want to isolate them in one file that you can decide to require or not:

# routes/init.rb
require_relative 'main'
require_relative 'debug' if settings.development?
# routes/main.rb
class MyApp < Sinatra::Application
  get '/' do
    'Hello!'
  end
end
# routes/debug.rb
class MyApp < Sinatra::Application
  get '/admin' do
    'VIPs only'
  end
end

Or if you want to list your development-only paths in one place, here's a filter version:

class MyApp < Sinatra::Application
  DEVELOPMENT_PATHS = %w[
    /admin
  ]

  before do
    unless settings.development? || !DEVELOPMENT_PATHS.include?(request.path)
      halt 404 
    end
  end
end

Then you could also build some decorator-like methods that add to the list:

class MyApp < Sinatra::Application
  def self.development_only(path)
    DEVELOPMENT_PATHS << path
  end

  get '/admin' do
    'VIPs only'
  end
  development_only '/admin
end

In general, I'd recommend caution when introducing significant differences between the code that runs in development vs. production. Inevitably, the dev code is either left untested or becomes cumbersome to maintain properly. In this case, there's the danger that you miss a route you intended to hide and it becomes available to everyone in production. I'd tend towards not having these routes at all and manipulating my dev environment from a console, or going all the way to the other end and building fully-tested and production-ready user permissions with something like sinatra-authentication.

Kristján
  • 18,165
  • 5
  • 50
  • 62
  • Thanks for the comprehensive answer. I am using authentication, I guess I don't really need to hide the endpoints if i'm checking for admin credentials, but I still thought another layer of encapsulation would be good. Of the 3 methods you've shown above which would you recommend? – MrHaze Sep 27 '15 at 23:46
  • 1
    I'd choose to isolate everything in the `debug` file, since it makes very clear what's only for dev and keeps the code entirely out of production with one switch. I don't like the per-route `if` statement just because you'll have so many scattered around. If you have a bunch of different debug routes throughout files you organize by resource (users, posts, etc.), that's when I'd pick the decorator version. – Kristján Sep 28 '15 at 01:40
  • Cheers for that, I had the same idea about the per-route `if`, it could get messy. – MrHaze Sep 28 '15 at 01:43