Reputation: 18445
Hopefully this will be a simple answer, I am new to both Ruby and Sinatra.
I am looking at writing a sinatra app using the modular approach, however I am wanting to be able to add new routes in a plugin fashion.
Currently I have my SinatraApp class which inherits from Sinatra::Base, now I have a method in there called load_plugins which goes through the plugins folder and includes all routing-definition.rb files, which are modules containing more routes.
So with that context to the question, I am wondering how Sinatra manages its routing. Is it a case of when it first initializes it loads the SinatraApp and then keeps it in memory within the rack process (or whatever you are running it within), or is the SinatraApp re-evaluated every request?
Take the scenario, UserA loads up a route which returns a html page, UserA clicks a link and it returns a 404 as the plugin doesn't exist, AdminA then adds a new plugin into the plugins folder, UserA then refreshes their page and they get a html page rather than a 404, as the plugin has added the route.
Would the above happen, or would I need to restart the Sinatra server to get it to pick up the new plugin file?
Part of me hopes it would update loaded plugins each request... however part of me knows that would hammer performance if every request it needs to scan directories for plugins.
== EDIT ==
Have added an example of what I mean below. I am not sure if it should be structured like a module or left inline as below, if include literally code dumps into the current scope so the get "" would know it should be calling get within the scope of the SinatraMain app, either way it gives you an indication of what I am after.
require 'sinatra'
class SinatraMain < Sinatra::Base
def load_plugins
Dir["/plugins/**/routing-plugin.rb"].each do |plugin_file|
include plugin_file
end
end
get "/test" do
return "This is a test route"
end
get "/plugins/*" do
load_plugins
return "Plugins refreshed"
end
end
SinatraMain.run!
# Imagine this was within /plugins/SayHelloPlugin/routing-plugin.rb
get "/say-hello"
return "Saying hello"
end
So the idea is that whenever these plugin files are added to the plugins folder it should then add the routes to the application, and when they are removed take them out of the routing (although the latter point isn't as important for now).
Upvotes: 3
Views: 1917
Reputation: 15736
From my reading of the source (https://github.com/sinatra/sinatra/blob/master/lib/sinatra/base.rb) Sinatra stores routes as a class variable on the Sinatra::Base class so routes are not re-evaluated on each request, although Sinatra/Rack does create a new instance of your app for each request.
You can certainly add extra routes at runtime, without reloading all the routes. I don't know how you are loading your plugins but it should be possible to do what you want without restarting Sinatra, this is a crude demo/experiment that adds a new route:
require 'rubygems'
require 'sinatra/base'
class App < Sinatra::Base
get '/' do
"hello [self.object_id: #{self.object_id}, App.routes.object_id: #{App.routes.object_id}]"
end
get '/add_route' do
App.get '/new_route' do
"from new route [self.object_id: #{self.object_id}, App.routes.object_id: #{App.routes.object_id}]"
end
"new route added [self.object_id: #{self.object_id}, App.routes.object_id: #{App.routes.object_id}]"
end
run! if /app.rb$/ =~ $0
end
If you run this you will see that the App is getting a new object_id on each request but the App.routes hash is the same object throughout.
Upvotes: 2