Rails object lifecycle - cross-transaction objects creation and destroy - ruby

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

Related

How to get access to Hanami connection instance

Hanami uses Sequel as default ORM
There are different plugins for Sequel
Sometimes to use it you need to access to database connection object
For example to use PostgreSQL advisory locking for mutexes
There are such gems for this: sequel-advisory-locking, sequel-pg_advisory_lock
First, you should load an extension for Sequel::Database instance
DB.extension(:advisory_locking)
Then use it in your app like this
DB.advisory_lock('my_key') do
# do stuff with lock
end
But there is problem to use such libraries with Hanami
There is no information about it in official guide
On database configuration page such info:
Hanami models use ROM as a low-level backend. This means that you can easily use any Sequel plugins in your app. For this you need to define a gateway block in your model configuration, add the extension by calling extension on gateway.connection and pass the extension name in:
# config/environment.rb
Hanami.configure do
model do
gateway do |g|
g.connection.extension(:connection_validator)
end
end
end
What I tried is define constant in this config
Hanami.configure do
model do
gateway do |g|
DB = g.connection
DB.extension(:connection_validator)
DB.extension(:advisory_locking)
end
end
end
And then use this DB somewhere in the app, for example as DB.advisory_lock('my_key') { do_stuff_with_lock }
But such constants define within a block looks not good
How to get connection instance in Hanami app?
There is config object in Hanami -- Hanami::Model.configuration
In fact it is the getter for #configuration instance variable
It is instance of Hanami::Model::Configuration and has such instance variables like #url, #entities, #logger and others. So it's possible to call getters to get them
It also has connection method. This method returns gateway.connection
And this gateway.connection is just what is needed. This is exactly the instance of connection to the database, on which you can call methods
For example
Hanami::Model.configuration.connection.advisory_lock('my_lock') do
# do stuff with lock
end
I want to note that all these methods are indicated by the comment # #api private. It means that their implementation may be changed in future versions.

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.

Writing an API wrapper gem - what's the best way to persist API Token per instance?

I'm writing an REST API Wrapper ruby gem, here's how I want it to work:
# to start, initialize a client
client = APIWrapper.new "someToken"
# then you can fetch a collection of resources
client.some_resources.fetch
# you don't have to always start with `client` when you have a resource loaded
resource = client.some_resource.first
resource.do_stuff
moar_resource = resource.some_nested_resources.find({foo: 'bar'})
moar_resource.do_other_stuff
# and you can run multiple clients with different tokens
client_1 = APIWrapper.new "token 1"
client_2 = APIWrapper.new "token 2"
so the service (which is out of my control) requires an "apiToken" to be passed as a parameter in every request. So I need to pass the token in Client to each nested resources so they can work without relying on information stored in client object. (some api wrappers will always require you to start your action from client, eg. client.resources["someId"].do_stuff
What I'm doing right now is that I have a connection object, which is a Faraday instance, with apiToken set as a default parameter, in Client once it is initialized. Then every time I'm initializing a resource instance (eg. resource = client.resources.first ) I pass down the connection object, so it can use the existing connection to perform API calls.
After writing 5,6 classes I started thinking if there is a better way to do this since in every class I'm doing something like
Module APIWrapper
class Resource(options = {})
#conn = options[:connection]
#attribute_1 = options[:attribute_1]
# ... assign other attributes
end
end
I've looked at some other API Wrapper's repos but I haven't found an answer. Any suggestions?
This is the nature of Object Oriented programming.
There are hacky ways to do this in ruby, such as saving state on a module as such:
module Client
class << self
attr_accessor :connection
end
end
Client.connection = # Your faraday object, with API token
class Resource
def some_call
Client.connection.post #whatever
end
end
This is generally considered pretty ugly.
If I were you I would simply create a Resource superclass that takes the #conn in the constructor. In every instance, call the superclass's constructor.
You will still have to pass in the #conn object when you initialize a resource, but the assignment will be all in one place and DRY.

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

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

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.

Resources