I have written a small application in Ruby and now want to add a simple API to it to allow other applications to run some basic queries against it.
I've been looking at senatra, as it seems very lightweight and simple. But I suspect that rather than add sinatra to my app to provide the API, I should probably have written the application as a sinatra app.
My problem at the moment is that I can stand up a "thin" server with a simple "Hello!" endpoint in it, by defining it in a module that I add to my application object, like this:
module ApMessageIoModule
require 'sinatra/base'
require 'thin'
def start_server
Thread::abort_on_exception
Thread.new do
ApiServer.run!
end
end
class ApiServer < Sinatra::Base
configure do
server.threaded = settings.threaded if server.respond_to? :threaded=
end
get "/" do
"Hello!"
end
end
end
By calling the start_server() method I can start the server as a background thread. But now I have the endpoint, I want it to refer to methods in the class I have added the module to.
So how can I access the namespace of the enclosing class?
e.g.
The class I have added the module to is called StateMachine and it has a method:
# Query the state table for a particular state
def query_status(flag)
execute_sql_query(
"select status from state where state_flag = '#{flag}';"
)[0][0]
end
How can I call this method from within the "get" route above?
I have found another post that seems to relate to this -
Accessing a class's containing namespace from within a module
But it's a bit above my head and I've had no luck trying to adapt the code example given.
To try and clarify this, I have a class, shown here stripped down:
class StateMachine
# Query the state table for a particular state
def query_status(flag)
execute_sql_query(
"select status from state where state_flag = '#{flag}';"
)[0][0]
end
end
And that class includes the module ApMessageIoModule shown above.
The StateMachine class has been instantiated, here in my unit test:
# Confirm that SM can load one of our modules
def test_it_loads_user_modules
sm = StateMachine.new
sm.include_module('ApMessageIoModule')
sm.start_server
sleep(600)
sm.stop_server
assert_equal(sm.test_method, 'Test String')
end
I currently have a long sleep in there to allow me to manually confirm that the server has started by going to the endpoint in my browser. Doing so shows me the Hello message expected on the endpoint.
The query status method talks to an underlying sqlite3 db that has been created and populated by various methods called from the initialise method for StateMachine. Not shown here for brevity.
So what I want to do is cal that method in the StateMachine instance from within the ApiServer class within the ApMessageIoModule module if that makes sense.
Actually I think this scratch pad makes it clearer:
require 'sinatra/base'
require 'thin'
module MyInclude
class SinatraServer < Sinatra::Base
get '/' do
test_method # This call fails with message shown below
end
get '/exit' do
exit!(0)
end
end
def startup
Thread.new do
SinatraServer.run!
end
end
end
class TopLevel
include MyInclude
def test_method
puts "TEST"
end
end
tl = TopLevel.new
tl.startup
sleep(600)
# Error message reads:
# NameError at /
# undefined local variable or method `test_method' for
# #<MyInclude::SinatraServer:0x00007fd002ac41d8>
# file: sinatra_server.rb location: block in <class:SinatraServer> line: 7