Ruby Rack: startup and teardown operations (Tokyo Cabinet connection) - ruby

I have built a pretty simple REST service in Sinatra, on Rack. It's backed by 3 Tokyo Cabinet/Table datastores, which have connections that need to be opened and closed. I have two model classes written in straight Ruby that currently simply connect, get or put what they need, and then disconnect. Obviously, this isn't going to work long-term.
I also have some Rack middleware like Warden that rely on these model classes.
What's the best way to manage opening and closing the connections? Rack doesn't provide startup/shutdown hooks as I'm aware. I thought about inserting a piece of middleware that provides reference to the TC/TT object in env, but then I'd have to pipe that through Sinatra to the models, which doesn't seem efficient either; and that would only get be a per-request connection to TC. I'd imagine that per-server-instance-lifecycle would be a more appropriate lifespan.
Thanks!

Have you considered using Sinatra's configure blocks to set up your connections?
configure do
Connection.initialize_for_development
end
configure :production do
Connection.initialize_for_production
end
That's a pretty common idiom while using things like DataMapper with Sinatra
Check out the "Configuration" section at http://www.sinatrarb.com/intro

If you have other Rack middleware that depend on these connections (by way of a dependence on your model classes), then I wouldn't put the connection logic in Sinatra -- what happens if you rip out Sinatra and put in another endpoint?
Since you want connection-per-application rather than connection-per-request, you could easily write a middleware that initialized and cleaned up connections (sort of the Guard Idiom as applied to Rack) and install it ahead of any other middleware that need the connections.
class TokyoCabinetConnectionManagerMiddleware
class <<self
attr_accessor :connection
end
def initialize(app)
#app = app
end
def call(env)
open_connection_if_necessary!
#app.call(env)
end
protected
def open_connection_if_necessary!
self.class.connection ||= begin
... initialize the connection ..
add_finalizer_hook!
end
end
def add_finalizer_hook!
at_exit do
begin
TokyoCabinetConnectionManagerMiddleware.connection.close!
rescue WhateverTokyoCabinetCanRaise => e
puts "Error closing Tokyo Cabinet connection. You might have to clean up manually."
end
end
end
end
If you later decide you want connection-per-thread or connection-per-request, you can change this middleware to put the connection in the env Hash, but you'll need to change your models as well. Perhaps this middleware could set a connection variable in each model class instead of storing it internally? In that case, you might want to do more checking about the state of the connection in the at_exit hook because another thread/request might have closed it.

Related

Is it safe to reuse Faraday connection objects?

Is it safe to reuse Faraday connection objects, or is it better to recreate them every time?
def connection
#connection ||= Faraday.new('http://example.com') do |conn|
conn.request :url_encoded
# more configuration
end
end
I think it's safe to reuse them (I have, a lot). I don't see it really covered one way or another in the documentation but the presence of "Per-request options" (as opposed to per-connection) at least implies that you can rely on making multiple requests with the same connection.
https://github.com/lostisland/faraday/blob/52e30bf8e8d79159f332088189cb7f7e536d1ba1/lib/faraday/connection.rb#L502
connection.get .post and all other methods duplicates params etc here.
It means each request shares nothing with each other and the parent Connection object.
It's safe to reuse.

Add globals at startup and shutdown with Sinatra? [duplicate]

I have built a pretty simple REST service in Sinatra, on Rack. It's backed by 3 Tokyo Cabinet/Table datastores, which have connections that need to be opened and closed. I have two model classes written in straight Ruby that currently simply connect, get or put what they need, and then disconnect. Obviously, this isn't going to work long-term.
I also have some Rack middleware like Warden that rely on these model classes.
What's the best way to manage opening and closing the connections? Rack doesn't provide startup/shutdown hooks as I'm aware. I thought about inserting a piece of middleware that provides reference to the TC/TT object in env, but then I'd have to pipe that through Sinatra to the models, which doesn't seem efficient either; and that would only get be a per-request connection to TC. I'd imagine that per-server-instance-lifecycle would be a more appropriate lifespan.
Thanks!
Have you considered using Sinatra's configure blocks to set up your connections?
configure do
Connection.initialize_for_development
end
configure :production do
Connection.initialize_for_production
end
That's a pretty common idiom while using things like DataMapper with Sinatra
Check out the "Configuration" section at http://www.sinatrarb.com/intro
If you have other Rack middleware that depend on these connections (by way of a dependence on your model classes), then I wouldn't put the connection logic in Sinatra -- what happens if you rip out Sinatra and put in another endpoint?
Since you want connection-per-application rather than connection-per-request, you could easily write a middleware that initialized and cleaned up connections (sort of the Guard Idiom as applied to Rack) and install it ahead of any other middleware that need the connections.
class TokyoCabinetConnectionManagerMiddleware
class <<self
attr_accessor :connection
end
def initialize(app)
#app = app
end
def call(env)
open_connection_if_necessary!
#app.call(env)
end
protected
def open_connection_if_necessary!
self.class.connection ||= begin
... initialize the connection ..
add_finalizer_hook!
end
end
def add_finalizer_hook!
at_exit do
begin
TokyoCabinetConnectionManagerMiddleware.connection.close!
rescue WhateverTokyoCabinetCanRaise => e
puts "Error closing Tokyo Cabinet connection. You might have to clean up manually."
end
end
end
end
If you later decide you want connection-per-thread or connection-per-request, you can change this middleware to put the connection in the env Hash, but you'll need to change your models as well. Perhaps this middleware could set a connection variable in each model class instead of storing it internally? In that case, you might want to do more checking about the state of the connection in the at_exit hook because another thread/request might have closed it.

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.

Rails object lifecycle - cross-transaction objects creation and destroy

Is there a way to have objects on a Rails app that conserve their state between HTTP transactions?
For example, can I initialize a Net::LDAP connection somewhere and use it to retrieve data only restarting it on lost connection?
You can use class variables.
class Connection
def initialize
##connection ||= # Start connectio
end
end

Mock TCPSocket with RSpec

I'm attempting to write tests around an application that makes heavy use of TCPSockets (an IRC bot to be specific). While writing my first class's tests, I had been skimping by with doing:
#In the describe block
before(:all) { TCPServer.new 6667 }
...which allowed for my TCPSockets to function (by connecting to localhost:6667), though they are not actually being properly mocked. However, this has now caused problems when moving onto my second class as I cannot create a TCPServer on the same port.
How can I mock the TCPSocket class in such a way that will allow me to test things such as its(:socket) { should be_kind_of(TCPSocket) } and other common operations like #readline and #write?
You could try keeping track and closing the TCPServer in your before and after:
before do
#server = TCPServer.new 6667
end
after do
#server.close
end
it ... do
end
it ... do
end
After each of the individual tests, the TCPServer is killed so you can create a new one with the same port.
I'm not quite sure if I understand your problem, but why don't you just install some kind irc server on your local machine? ircd-irc2, ircd-hybrid or something like that?
Suppose you have irc client implemented this way:
class Bot
attr_accessor :socket
def initialize
socket = TCPSocket.new("localhost", 6667)
end
end
You can then test it like this
let(:bot) { Bot.new }
it "should be kind of TCP Socket"
bot.should be_kind_of(TCPSocket)
bot.should be_a(TCPSocket)
end

Resources