When and where does Sinatra's request object exist? - ruby

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.

Related

Using the session hash outside of a route

In my sinatra application, I would like to create some methods for manipulating the session. I would like to declare all of those methods in a module, and have that module outside any routes. For example:
enable :sessions
module SessionEditing
def setup
session[:value] = "something"
end
end
get "/" do
SessionEditing.setup
redirect "/test"
end
get "/otherRoute" do
SessionEditing.setup
redirect "/test"
end
get "/test"
puts session[:value] #=> "something"
end
I thought that there might be a way to elevate the scope of session, but I haven't been able to find out how. I also have learned that passing session into the method doesn't work because you can't set it back, even if the method returns a new session setting session = SessionEditing.setup(session) will not actually make the session change. How can I get this to work?
Calling SessionEditing.setup(session) will allow you to modify the session. The problem is `SessionEditing is incorrectly implemented. Try:
module SessionEditing
def self.setup
session[:value] = "something"
end
end
When you call SessionEditing.setup, you are calling the module method .setup, which is why you need to define the method with self, as it defines setup as a class method.

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'

Use application class instance variable inside get block

I am using sinatra and I have the code:
class App < Sinatra::Base
configure do
#logger = Logger.new "./log"
end
#logger.info "App started" #this line works
get "/info" do
#logger.info "/info inquired" #this does not work and complain #logger is nilClass
end
end
Why #logger inside get block gives a nil object? How can I use #logger in this case?
PS. If I use a class variable like ##logger, the code above works. But why the instance variable is not working in this case?
Instance variables attach themselves to whatever object is self at the time the instance variables spring into existence.
On the face of things, these are the values for self:
class App < Sinatra::Base
#In here, self=App
#When a block executes, it sees the value for self that existed in
#the surrounding scope at the time the block was CREATED:
configure do #a block
#So...in here self=App
#logger = Logger.new "./log"
end
#logger.info "App started" #this line works
get "/info" do #a block
#Similarly, in here self=App
#logger.info "/info inquired" ##logger is NilClass
end
end
Based on that state of things, you are right to be confused: it looks like when configure() executes the block that is passed to it, #logger will spring into existence and attach itself to App, then when get() calls the block that is passed to it, the #logger instance variable will refer to the instance variable attached to App.
But...ruby offers ways to change the value of self that a block sees when the block EXECUTES. Here is an example:
p = Proc.new { puts self }
p.call
class Dog
def initialize(a_proc)
#In here, self is a Dog instance
instance_eval &a_proc
end
end
Dog.new p
--output:--
main
#<Dog:0x000001009b6080>
Based on your error, you have to suspect that Sinatra must be employing some ruby tricks to change self when it executes the block passed to get().
How can we know this?
Ruby is the wild west of programming languages, so you can't ever know what is going to happen unless you look at the source code or good docs if they exist. The source code is pretty convoluted. I found this in the docs:
Some knowledge of Sinatra’s internal design is required to write good
extensions. This section provides a high level overview of the classes
and idioms at the core of Sinatra’s design.
Sinatra has two distinct modes of use that extensions should be aware
of:
The “Classic” style, where applications are defined on main / the
top-level – most of the examples and documentation target this usage.
Classic applications are often single-file, standalone apps that are
run directly from the command line or with a minimal rackup file. When
an extension is required in a classic application, the expectation is
that all extension functionality should be present without additional
setup on the application developers part (like included/extending
modules).
The “Modular” style, where Sinatra::Base is subclassed explicitly and
the application is defined within the subclass’s scope. These
applications are often bundled as libraries and used as components
within a larger Rack-based system. Modular applications must include
any desired extensions explicitly by calling register ExtensionModule
within the application’s class scope.
Most extensions are relevant to both styles but care must be taken by
extension authors to ensure that extensions do the right thing under
each style. The extension API (Sinatra.register and Sinatra.helpers)
is provided to help extension authors with this task.
Important: The following notes on Sinatra::Base and
Sinatra::Application are provided for background only - extension
authors should not need to modify these classes directly.
Sinatra::Base The Sinatra::Base class provides the context for all
evaluation in a Sinatra application. The top-level DSLish stuff exists
in class scope while request-level stuff exists at instance scope.
Applications are defined within the class scope of a Sinatra::Base
subclass. The “DSL” (e.g., get, post, before, configure, set, etc.) is
simply a set of class methods defined on Sinatra::Base. Extending the
DSL is achieved by adding class methods to Sinatra::Base or one of its
subclasses. However, Base classes should not be extended with extend;
the Sinatra.register method (described below) is provided for this
task.
Requests are evaluated within a new Sinatra::Base instance – routes,
before filters, views, helpers, and error pages all share this same
context. The default set of request-level helper methods (e.g, erb,
haml, halt, content_type, etc.) are simple instance methods defined on
Sinatra::Base or within modules that are included in Sinatra::Base.
Providing new functionality at the request level is achieved by adding
instance methods to Sinatra::Base.
As with DSL extensions, helper modules should not be added directly to
Sinatra::Base by extension authors with include; the Sinatra.helpers
method (described below) is provided for this task.
http://www.sinatrarb.com/extensions.html
You can define your own logger in your Sinatra::Base and use it in your get block by doing:
class App < Sinatra::Base
set :logger, Logger.new("./log")
helpers do
def logger; self.class.logger; end
end
logger.info self
get "/info" do
logger.info self
end
# ...
end
Or by using the class variable as you note in your edit. The log file from the above configuration shows why:
I, [2014-06-01T16:36:51.593033 #16144] INFO -- : App
I, [2014-06-01T16:36:59.438078 #16144] INFO -- : #<App:0x9aa919c #default_layout=:layout, #app=nil ...
In the first case, self is the application class, while in the get block, self is the instance of the class.
To clarify, in your example: Ruby interprets the first #logger.info (called from the context of your class) to be a class instance variable, while the second #logger.info is interpreted as an instance variable (which has not been defined). The variable you define in your configure block is set in the class context.

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.

rails 3 - where to put app startup initialization stuff?

In my app (jruby, rails 3, mongodb) I setup my data access objects (and other 'singletons') in an initializer /config/initializers/app_constants.rb
DATA_COLLECTION_STORE = DataCollectionStore.new(...)
SOME_OTHER_SINGLETON = SomeOtherClassTreatedLikeSingleton.new(...)
I'm new to rails (and ruby) and I realize a couple things. First, setting up these "singletons" must not be the correct approach, since those classes can be instantiated at any time anywhere else in the code (for now it's assumed that won't happen). Secondly, putting these "constants" in this initializer seems wrong b/c when I try to run tests (rake spec) or build a WAR (using warble) I can see that the initializer stuff is running so I'm creating connections to mongo, starting my "some_other_singleton", etc.
Where should this sort of stuff go?
thanks in advance for being patient with my noobness :)
You don't need to actually initialize your singletons in any config file.
Instead, you can just implement a class method in your class that will return a singleton. The following code will allow u to access the singleton using DataCollectionStore.instance
class DataCollectionStore
##instance = DataCollectionStore.new
def self.instance
##instance
end
def initialize
#intitialize you instance here
end
end
Keep in mind that you will have to set config.cache_classes to true in development.rb for this to work in your development environment. Classes are cached by default in production though.
Alternatively, you could just use the class itself as a singleton object and implement your functionality in class methods.
If your initialization is expensive, you can do it lazily using the snippet below. That way you only pay the price if your test actually invokes the instance class method.
You can test this in your app by outputting DataCollectionStore.instance in a view and using different browsers to load the page. If every thing works right, you should see the same timestamp in both browsers.
class DataCollectionStore
##instance = nil
def self.instance
##instance || ##instance = self.new
end
def initialize
#initialized_time = Time.now
end
def to_s
#initialized_time
end
end

Resources