Middleware or before hook?

39 views
Skip to first unread message

Tim Uckun

unread,
Sep 24, 2017, 4:58:00 AM9/24/17
to sinatrarb
I am writing an API in sinatra. It will be a pure JSON api so I need to parse the body as JSON and I am wondering whether it's more efficient to do that with a middleware or as a before hook?

Also a question about the general setup. I will have public paths and private paths and I will be using JWT. One approach is to create a namespace for the private and as a before or use setting make sure the JWT exists and is valid. Another approach would be to create two apps one for the private and one for the public and mount them on different paths. Is one way more efficient than the other?

Finally.  How about I group similar functionality into a separate app and have many apps each of which inherits from a base app but is mounted on different paths.  Does Sinatra initialize all the classes on every request or only initialize them when the mounted path is hit?

Thanks.

cher...@gmail.com

unread,
Sep 25, 2017, 3:00:34 AM9/25/17
to sinatrarb
>It will be a pure JSON api so I need to parse the body as JSON and I am wondering whether it's more efficient to do that with a middleware or as a before hook?

If you care a lot about efficiency, you might want to try Hobby. According to this benchmark, Hobby is a lot faster than Sinatra.
Also, hobby-json makes working with JSON more concise.

As the author of Hobby, I am obviously biased, but I will be happy to help you get started if you decide to give it a try.

Tim Uckun

unread,
Sep 25, 2017, 6:04:37 AM9/25/17
to sina...@googlegroups.com
That didn't answer my questions :)

I have been working on and off for a couple of weeks on this I must say it's been pretty frustrating. I think I finally got enough plumbing done to try and actually start writing my app now but it took me way longer than I ever thought it would.  At this point if I am going to throw away all this work and go someplace else I will either go with rails or maybe even a completely different language like elixir and phoenix or golang or something. 

Up to this point almost everything I have done has been to try and get a tiny bit of the functionality I get in rails. I am still missing a lot so more work to do yet! Although I appreciate the speed and efficiency of sinatra at this point I would not reccomend these types of micro frameworks for writing anything more than a trivial app. Even then I would say the tooling you get with rails is probably enough to choose it over something smaller and more efficient.

Maybe I should have started with padrino. I think that would have saved me a lot of work.






--
You received this message because you are subscribed to the Google Groups "sinatrarb" group.
To unsubscribe from this group and stop receiving emails from it, send an email to sinatrarb+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Anatoly Chernow

unread,
Sep 25, 2017, 8:06:21 AM9/25/17
to sina...@googlegroups.com
>Although I appreciate the speed and efficiency of sinatra at this point I would not reccomend these types of micro frameworks for writing anything more than a trivial app. Even then I would say the tooling you get with rails is probably enough to choose it over something smaller and more efficient.

Respectfully, I disagree with that. In my opinion, many Rails applications can benefit from using Hobby.

The value it brings lay not only in "speed and efficiency", but in higher code reusability. You can create only one Hobby application, and replace many Rails controllers by instantiating that Hobby application with different arguments(different models, for example).

Hence, in a long run it will actually save a lot of time and efforts.

--
You received this message because you are subscribed to a topic in the Google Groups "sinatrarb" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/sinatrarb/7_ZsExsAFB8/unsubscribe.
To unsubscribe from this group and all its topics, send an email to sinatrarb+unsubscribe@googlegroups.com.

Mike Pastore

unread,
Sep 25, 2017, 1:31:46 PM9/25/17
to sinatrarb
On Monday, September 25, 2017 at 2:00:34 AM UTC-5, cher...@gmail.com wrote:
If you care a lot about efficiency, you might want to try Hobby. According to this benchmark, Hobby is a lot faster than Sinatra.

This is a Sinatra discussion group. Please don't plug Sinatra alternatives here. I'm sure Hobby is great, but this is not the place to discuss it.

Mike Pastore

unread,
Sep 25, 2017, 1:53:44 PM9/25/17
to sinatrarb
On Sunday, September 24, 2017 at 3:58:00 AM UTC-5, Tim Uckun wrote:
I am writing an API in sinatra. It will be a pure JSON api so I need to parse the body as JSON and I am wondering whether it's more efficient to do that with a middleware or as a before hook?

I have not benchmarked it, but my intuition tells me that doing something locally in your current app will always be faster than doing the same operation in a newly-introduced middleware layer. That being said, I don't think the performance delta would be significant. Since you're struggling to get started, you might consider worrying less about efficiency, and more about getting it up and running. 

Also a question about the general setup. I will have public paths and private paths and I will be using JWT. One approach is to create a namespace for the private and as a before or use setting make sure the JWT exists and is valid. Another approach would be to create two apps one for the private and one for the public and mount them on different paths. Is one way more efficient than the other?

A namespace uses Sinatra's internal routing. With a separate path prefix you could use an external routing library like Rack::URLMap. So you're really asking which is faster. 

Again, I haven't benchmarked this exact scenario, but: Rack::URLMap was faster, hands down, prior to Sinatra 2.0. With Mustermann 1.0, I think Sinatra 2.0's routing performance is approaching parity with Rack::URLMap. And again, I think your worry about efficiency and performance is premature at this stage in the game. 

Finally.  How about I group similar functionality into a separate app and have many apps each of which inherits from a base app but is mounted on different paths.  Does Sinatra initialize all the classes on every request or only initialize them when the mounted path is hit?

A Sinatra app will initialize new class(es) when it is called, so when the mounted path is hit. 

Mike Pastore

unread,
Sep 25, 2017, 2:08:06 PM9/25/17
to sinatrarb
On Monday, September 25, 2017 at 5:04:37 AM UTC-5, Tim Uckun wrote:
I have been working on and off for a couple of weeks on this I must say it's been pretty frustrating. I think I finally got enough plumbing done to try and actually start writing my app now but it took me way longer than I ever thought it would.  At this point if I am going to throw away all this work and go someplace else I will either go with rails or maybe even a completely different language like elixir and phoenix or golang or something. 

Up to this point almost everything I have done has been to try and get a tiny bit of the functionality I get in rails. I am still missing a lot so more work to do yet! Although I appreciate the speed and efficiency of sinatra at this point I would not reccomend these types of micro frameworks for writing anything more than a trivial app. Even then I would say the tooling you get with rails is probably enough to choose it over something smaller and more efficient.

Maybe I should have started with padrino. I think that would have saved me a lot of work.

There is a steep learning curve, and yes, you pretty much have to do everything yourself. It's helpful to keep in mind that Sinatra really is just a thin DSL over Rack. It's a couple thousand SLOC vs. tens (hundreds?) of thousands SLOC for Rails. 

Fortunately, there are a lot of extensions for and of Sinatra that are available to help with this problem. I would highly recommending exploring Padrino and other options and alternatives. Or keep chipping away at it if you have the time, it's a great learning experience! 

Tim Uckun

unread,
Sep 27, 2017, 7:09:41 AM9/27/17
to sina...@googlegroups.com
I already know ruby and the ecosystem pretty well so for me it wasn't so much the learning curve as the lack of plumbing and trying to figure out how to arrange the app. With rails everything is ready to go, the paths are included in proper order, rake tasks are there for everything you need, tests are integrated, and of course there is copious documentation and a wealth of gems to do everything you need. Just pop in devise and a couple of other plugins and you are ready to go.   

What took me a long time as trying to hunt down the bits and pieces I need and putting them in the right place.

BTW the namespace feature of sinatra conflicts with rake namespaces. I found a workaround but just wanted to let you know in case you didn't.



--

Mike Pastore

unread,
Sep 27, 2017, 1:31:21 PM9/27/17
to sinatrarb
On Wednesday, September 27, 2017 at 6:09:41 AM UTC-5, Tim Uckun wrote:
BTW the namespace feature of sinatra conflicts with rake namespaces. I found a workaround but just wanted to let you know in case you didn't.

That seems... unlikely. Do you have an example of such a conflict?  

Tim Uckun

unread,
Sep 27, 2017, 8:40:29 PM9/27/17
to sina...@googlegroups.com
My app is the example :)

In my app I have the following file in config/environment


APP_ENV = ENV['APP_ENV'] ||= ENV['RACK_ENV'] ||= ENV['RAILS_ENV'] ||= 'development'
#This is the "real" app root. It will work even when this file is included in specs and elsewhere
APP_ROOT = File.expand_path('../..', __FILE__)

require 'bundler'
Bundler.require :default, APP_ENV.to_sym

#dotenv will not override the actual env vars...
Dotenv.load(File.join(APP_ROOT, '.env.development')) if APP_ENV != 'production'

I include this file in my Rakefile as well as my spec_helper and app.rb

I ran into problems trying to run rake tasks so I googled around a bit and found this workaround in my gemfile.

gem 'sinatra-contrib', require: false



--

Mike Pastore

unread,
Sep 27, 2017, 9:28:35 PM9/27/17
to sinatrarb
On Wednesday, September 27, 2017 at 7:40:29 PM UTC-5, Tim Uckun wrote:
My app is the example :)

That doesn't really illustrate the issue, but I was able to google around and find an old issue that I think is the same thing as you're dealing with. Here's a simple Rakefile that clearly shows the conflict (just run "rake" in the same directory to see the output):

require 'sinatra'
require 'sinatra/namespace'

p method
(:namespace).source_location # => ["/Users/mwp/.rbenv/versions/2.4.2/lib/ruby/gems/2.4.0/gems/sinatra-2.0.0/lib/sinatra/base.rb", 1921]

# I can't do a Rake namespace here!

task
(:default) {}

It's a weird issue that I've never run into because I almost always use modular mode (subclassing Sinatra::Application or Sinatra::Base). In this mode, you require "sinatra/base", not "sinatra", and Sinatra doesn't set up the delegation from the top-level context to itself. (In "classic" mode, Sinatra::Namespace just piggybacks off this mechanism to add its :namespace method to the list of delegated methods.)

I know you already have a workaround—and there's an around-alias workaround described in that issue report—but I'd like to offer a few more options:
  1. If you're not using any sinatra-contrib features, just remove it from your Gemfile.
  2. If you don't need Sinatra::Namespace but you're using other sinatra-contrib features, use the :require=>false workaround and manually require the features you need (e.g. require "sinatra/multi_route", require "sinatra/respond_with"). (Or you can do e.g. :require=>["sinatra/multi_route", "sinatra/respond_with"] in your Gemfile.)
  3. If you need Sinatra::Namespace, consider requiring "sinatra/base" instead of "sinatra" and modifying your app to run in modular mode (subclassing Sinatra::Application or Sinatra::Base).
  4. If you need Sinatra::Namespace and modular mode isn't an option for some reason, you just need to restructure your requires so that "sinatra" and "sinatra/namespace" aren't loaded until you're already within the context of a Rake task. This basically means that you have to set :require=>false for both gems in your Gemfile, put all your Sinatra-related requires in app.rb (or whatever), and don't require app.rb as a part of your global boot-up routine. Just require it from config.ru, spec_helper.rb, et al as needed.
Hope that helps a bit. 

Tim Uckun

unread,
Sep 28, 2017, 12:49:32 AM9/28/17
to sina...@googlegroups.com
I have done #4. 

I was thinking about the subclassing route but it's going to take a little bit of work to redo the app. I probably should have started there in the first place.  One thing I have done is to split the routes into different files so those will have to be wrapped in the class definition they belong in. 

--
Reply all
Reply to author
Forward
0 new messages