Define a class that inherits from Rack::Builder - ruby

I'm trying to build a Server class for a gem I'm building. This could define call (env) which would make it a rack app. However, I want to use Rack::Builder and map different kinds of URLs, according to my needs.
I'm not sure how to explain this but is there a way to inherit from Rack::Builder or something? I want to isolate the URL mappings into its own methods so I can test them in one class, so as to give meaning and isolation to it.
Thank you.

You do not need to inherit from Rack::Builder. You can do like that.
config.ru
class WrapperClass
def call(env)
#your_url_mapping_logic env
YourApp.new.call(env)
end
end
run WrapperClass.new
or
class WrapperClass
def call(env)
#your_url_mapping_logic env
#your app logic
# ....
[status, header, body]
end
end
run WrapperClass.new

Related

sinatra request scope like php

I'm not sure this is the best topic name I could use but is there a way in sinatra+rack to use classes/modules like in php+apache way? I mean is there a way you can use Singleton or any other static typed variables in just request scope? What I need(would want to) is:
class Config # might be just module
# some methods that are request scoped
# cant be shared between users/reuqest
def server_settings
# possible server dependent settings
# can be shared between requests on same server
end
end
class Something
def do_thing
# I would like to access Config class here without passing it as parameter
# setted up for the current request
end
end
class MyController < Sinatra::Base
# request handling, creates Config class, setting up request parameters once
def handle_it
Something.new.do_thing
end
end
No rails here and I'm very limited on things I can do / gems I can use. MyController comes from internal framework. I can ofc extend it though. Php+apache goes from nothing every request so all your code is request only unless you use sessions/cookies.
The reason why I would like to use this is to avoid passing config instance(or any other kind of data repository) into every other instance as parameter and simply access it "globaly".
I think you looking for something like this answer Page Views counter sinatra?
tl;dr version is using set
set :my_config_property, 'hello world'

When and where does Sinatra's request object exist?

I'm stumped I've tried everything I can think of to work around this and I got nothing.
When does Sinatra's request object start existing and where does it actually exist and how can I get to it from anywhere? i.e. another class that inherits from a class that inherits from a class that inherits from Sinatra::Base for example.
I've managed to get an idea of where it exists from this question but I just can't seem get any further.
Things I've tried:
def self.request
self.superclass.superclass.superclass.request
end
Various ways of changing the code execution context using `instance_eval or using:
def self.method_added method_name
a = self.new.method(method_name)
def self.define_method method_name do
a.call
end
end
and anything else I can think of but no matter what I do request is always nil, so I ask again, when and where does the request object come into existence during a request?
EDIT:
No offence but how is it hard to tell what I'm asking?
Here's the question, it's the in the title:
When and where does Sinatra's request object exist?
Sinatra has a request object, when does the object start to exist? (as in when is it not nil during the execution of code?)
When it does exist, where does it exist, within Sinatra::Base an instance of Sinatra::Base or within `Wrapper or some where else?
EDIT:
Here's what I'm doing:
in this example:
r[:action] is 'get'
r[:url] is '/'
method is get_root
instance is a variable storing self.new so I can access any instance methods.
def method_added method
return if Delegation::SIN_DSL[:basic].include?(method)
r,p = get_r_data method
m = self.instance.method(method)
self.send r[:action], r[:url].first, (#options || {}) do
(m.arity < 1 ) ? m.call : m.call(*p)
end
#options = nil
end
A sinatra app is a rack app. If you have
class MyApp < Sinatra::Base
end
then where the MyApp class gets instantiated depends on how you start up a webserver that runs it; generally speaking the rack handler (which may be of different types depending on the http server you're using) will store an instance of the sinatra app. When a rack request comes in, the server or another rack app will call the app instance with the rack env hash. Sinatra::Base#call will then do dup.call!(env), meaning that a shallow copy of the existing instance is made and then call! is invoked on the copy. The body of call! is where the request object is initialized:
def call!(env) # :nodoc:
#env = env
#request = Request.new(env)
and it's this duped app's request accessor for this instance variable that you're typically invoking when you refer to request in a route handler.
Not sure if that helps you, but it should at least answer the question.
WARNING: Answer valid for sinatra v1.4.5, but you should not expect it to remain valid. These implementation details are not part of the public sinatra API and are not documented for a reason -- you're not intended to mess with it, and doing so will quite possibly break your app if you upgrade sinatra versions. I don't recommend writing code that depends on these details.

Method definition in ruby (like sinatra)

I do not know where Sinatra methods (like get or params) are defined. According to base.rb, they are static parts of Sinatra's Base class. How can I call them anywhere by just writing get? Shouldn't I write something like Sinatra::Base.get instead? And how can I define things like that by myself?
The answers can be found here: https://github.com/sinatra/sinatra/blob/master/lib/sinatra/main.rb
When you do Sinatra in its simple mode, all methods like get or set or post are delegated through Sinatra::Delegator, which is defined here: https://github.com/sinatra/sinatra/blob/master/lib/sinatra/base.rb#L1977 and is mixed into global scope inside main.rb
Leaving all Sinatra tricks alone, for your own module you can achieve the effect with really simple code:
module MyMixin
def testme
puts 'testme'
end
end
extend MyMixin
testme # => testme

Monkeypatching from a Sinatra helper

I've defined a Sinatra helper in the usual way:
module Sinatra
module FooHelper
# code goes here
end
end
In my helper, among other things, I'd like to add a method to Numeric:
module Sinatra
module FooHelper
class ::Numeric
def my_new_method
end
end
end
end
However, in the interests of being unobtrusive, I only want to do add this method if my Sinatra helper is actually included in an application; if nobody runs helpers Sinatra::FooHelper, I don't want to affect anything (which seems like a reasonable thing to expect of a Sinatra extension).
Is there any hook that's fired when my helper is included, that would enable me to add my method only once that happens?
You can use the Module#included method to do this. I think you will need to modify your class definition slightly to use class_eval. I've tested the following and it works as expected:
module Sinatra
module FooHelper
def self.included(mod)
::Numeric.class_eval do
def my_new_method
return "whatever"
end
end
end
end
end

Calling a Sinatra app instance method from TestCase

I have an util method into a Sinatra application and I would like to tested from my TestCase.
The problem is that I don't know how to invoke it, if I just use app.util_method I have the error NameError: undefined local variable or method 'util_method' for #<Sinatra::ExtendedRack:0x007fc0c43305b8>
my_app.rb:
class MyApp < Sinatra::Base
# [...] routes methods
# utils methods
def util_method
return "hi"
end
end
my_app_test.rb:
require "my_app.rb"
require "test/unit"
require "rack/test"
class MyAppTest < Test::Unit::TestCase
include Rack::Test::Methods
def app
MyApp.new
end
# [...] routes methods tests
def test_util_method
assert_equal( "hi", app.util_method )
end
end
Sinatra aliases the new method to new! before redefining it, so the simplest solution is to use that instead:
def app
MyApp.new!
end
Of course I only noticed that after I’d come up with the following, which I’ll leave in as it could be useful/informative.
A possible way to get round Sinatra redefining the new method and returning a complete Rack app a get hold of an instance your actual base class is to do what the “real” new method does yourself:
def app
a = MyApp.allocate
a.send :initialize
a
end
This is a bit of a hack, but it might be useful for testing.
Another technique would be to “walk” the middleware stack until you got to your class. The following is a little fragile, as it depends on all the middleware involved to use the name #app to refer to the next app in the stack, but this is fairly common.
def app
a = MyApp.new
while a.class != MyApp
a = a.instance_variable_get(:#app)
end
a
end
That won’t work on the yet to be released Sinatra 1.4 though (at least not on the current master, which is commit 41840746e866e8e8e9a0eaafc53d8b9fe6615b12), as new now returns a Wrapper class and the loop never ends. In this case you can grab the base class directly from the #instance variable:
def app
MyApp.new.instance_variable_get :#instance
end
(note this last technique may well change before the final 1.4 release).
The problem you are encountering is, that MyApp.new does not return an instance of MyApp but an instance of the middleware wrapping your App (usually Rack::Head or Sinatra::ShowExceptions). A good explanation can be found in the thread Sinatra Usage Question / Rack App.
The only solution I can think of is to change your instance method to a class method which can be called without the instance itself. As the instance of your App may be freshly instantiated for every request, an instance method probably doesn't have much advantages over a class method in your scenario.
Edit:
In the upcoming Sinatra 1.4 the initialization will change. Sinatra::Base.new will return a Sinatra::Wrapper instance, which exposes #settings and #helpers. This may help solve the problem of accessing Sinatra::Base instance methods. See the Sinatra Changelog for more information.

Resources