模块中的Ruby类需要访问包含类名称空间

时间:2018-01-12 16:20:45

标签: ruby namespaces ruby-thread

我在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()方法,我可以将服务器作为后台线程启动。但是现在我有了端点,我想让它引用我已经添加模块的类中的方法。

那么如何访问封闭类的命名空间呢?

e.g。

我添加模块的类名为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

如何在上面的“获取”路线中调用此方法?

我找到了另一篇与此相关的帖子 -

Accessing a class's containing namespace from within a module

但它有点高于我的头脑,我没有运气试图调整给出的代码示例。

为了试图澄清这一点,我有一个课程,在这里显示:

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

1 个答案:

答案 0 :(得分: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