0

我已经用 Ruby 编写了一个小应用程序,现在想向它添加一个简单的 API,以允许其他应用程序对其运行一些基本查询。

我一直在看 senatra,因为它看起来非常轻量级和简单。但我怀疑与其将 sinatra 添加到我的应用程序以提供 API,不如将应用程序编写为 sinatra 应用程序。

我现在的问题是我可以用一个简单的“你好!”来建立一个“瘦”服务器。端点,通过在我添加到我的应用程序对象的模块中定义它,如下所示:

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 的初始化方法调用的各种方法创建和填充。为简洁起见,此处未显示。

因此,如果有意义的话,我想做的是从 ApMessageIoModule 模块中的 ApiServer 类中调用 StateMachine 实例中的该方法。

实际上,我认为这个便笺簿使它更清楚:

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
4

1 回答 1

0

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
于 2018-01-12T17:01:28.427 回答