Классу Ruby внутри модуля необходим доступ к пространству имен класса
Я написал небольшое приложение на Ruby и теперь хочу добавить к нему простой API, чтобы другие приложения могли выполнять некоторые базовые запросы к нему.
Я смотрю на сенатру, так как он кажется очень легким и простым. Но я подозреваю, что вместо добавления sinatra в мое приложение для предоставления API, я, вероятно, должен был написать приложение как приложение sinatra.
Моя проблема на данный момент заключается в том, что я могу встать на "тонкий" сервер с простым "Hello!" конечная точка в нем, определяя его в модуле, который я добавляю в объект приложения, например так:
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
Вызвав метод start_server(), я могу запустить сервер как фоновый поток. Но теперь у меня есть конечная точка, я хочу, чтобы она ссылалась на методы в классе, к которому я добавил модуль.
Итак, как я могу получить доступ к пространству имен включающего класса?
например
Класс, к которому я добавил модуль, называется 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
Как я могу вызвать этот метод из маршрута "get" выше?
Я нашел другой пост, который, кажется, связан с этим -
Доступ к пространству имен, содержащему класс, из модуля
Но это немного над моей головой, и мне не повезло, пытаясь адаптировать приведенный пример кода.
Чтобы попытаться уточнить это, у меня есть класс, показанный здесь, урезанный:
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
И этот класс включает в себя модуль ApMessageIoModule, показанный выше.
Класс StateMachine был создан здесь, в моем модульном тесте:
# 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
В настоящее время я долго сплю, чтобы вручную подтвердить, что сервер запущен, перейдя к конечной точке в моем браузере. Это показывает мне сообщение Hello, ожидаемое в конечной точке.
Метод состояния запроса взаимодействует с базовой базой данных sqlite3, которая была создана и заполнена различными методами, вызываемыми из метода инициализации для StateMachine. Не показано здесь для краткости.
Поэтому я хочу, чтобы этот метод вызывался в экземпляре StateMachine из класса ApiServer в модуле ApMessageIoModule, если это имеет смысл.
На самом деле я думаю, что этот блокнот делает это более ясным:
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
1 ответ
Ваш query_status
это чистая функция, которая не зависит ни от чего. Поместите его в полностью отделенный модуль, сделайте его функцией модуля и вызовите его как функцию модуля отовсюду:
module Helpers
def query_status(flag)
...
end
module_function :query_status
end
сейчас в вашем get
:
get "/" do
"Hello, " + Helpers.query_status('yes')
end
Отвечая на поставленный вопрос: поскольку эта функция не мешает каким-либо данным (является чистой функцией), сделайте ее функцией уровня класса ApiServer
и назовите его по имени:
class ApiServer < Sinatra::Base
def self.query_status(flag)
flag.to_s
end
get "/" do
"Hello, " + query_status('yes')
end
end