【问题标题】:How to map routes to controllers in Sinatra?如何将路由映射到 Sinatra 中的控制器?
【发布时间】:2024-04-25 08:00:02
【问题描述】:

我想使用 Sinatra 创建一个简单的实验性 MVC 框架。

我想按名称"pages" 定义资源,例如应该解析为:

/pages (index)
/pages/new
/pages/:id/show (show)

映射到app/controllers/PagesController.rb,对应的get('/')负责索引,post('/pages/create')负责创建等。

问题是即使在阅读了官方文档后我也感到非常困惑。我想我需要为此使用非经典的 Sinatra 模型,但谁能指出我正确的方向?

谢谢

【问题讨论】:

  • 对应get('/'),你不是说get('/pages')吗?
  • 是的,我就是这个意思。基本上我如何模仿 Rails 对控制器的作用?

标签: ruby model-view-controller routing sinatra


【解决方案1】:

如果你想要我认为你想要的,我会一直这样做。最初对于这个方案,我使用 travis-api 源作为参考,但基本上你想要做的是在“控制器”类中扩展 Sinatra::Base,然后在机架中安装你的个人 Sinatra“控制器”,就像这样:

module Endpoint
  def self.included(base)
    base.class_eval do
      set(:prefix) { "/" << name[/[^:]+$/].downcase }
    end
  end
end

class Users < Sinatra::Base
  include Endpoint

  get '/' do
    #logic here
  end

  get '/:id' do
    #logic here
  end

  post '/' do
    #logic here
  end

  patch '/:id' do
    #logic here
  end
end

class Posts < Sinatra::Base
  include Endpoint

  post '/' do
    #logic here
  end
end

然后是这样的:

class App
  require "lib/endpoints/users"
  require "lib/endpoints/posts"

  attr_reader :app

  def initialize
    @app = Rack::Builder.app do
      [Users, Posts].each do |e|
        map(e.prefix) { run(e.new) }
      end
    end
  end

  def call(env)
    app.call(env)
  end
end

您可以根据需要对其进行调整,但想法是相同的,您将应用程序分成可组合的 Sinatra 应用程序,每个应用程序都有一个前缀,它们使用 Rack 挂载在下面。这个特定示例将为您提供以下路线:

得到'/用户'

获取'/users/:id'

发布“/用户”

补丁'/users/:id'

得到'/posts'

【讨论】:

  • 仅供参考,你可以通过调用run App.new来运行这个应用程序
  • 您能否解释一下self.included(base) 的来源以及它的具体作用?谢谢
  • self.included(base) 逻辑是一种(可能过于复杂)每个控制器根据类名自动设置其路由前缀的方法。当Endpoint 类包含在每个控制器中时,prefix 选项设置为小写的类名。然后从map(e.prefix) { run(e.new) } 行中的每个控制器检索此前缀。
【解决方案2】:

我在这里给你一个非常简单的例子:

创建文件controller.rb

get '/pages' do
  @pages = Pages.all
  erb :pages
end

接下来在与控制器相同的文件夹中创建一个views目录,并创建一个名为pages.html.erb的文件

这是您之前创建的控制器操作的对应视图。

在这里,您可以输入如下内容:

<% @pages.each do |p| %>
<%= p.title %>
<% end %>

重新启动服务器,访问localhost:PORT/pages,您将看到所有页面标题的列表。

您可以查看此链接以获取简单的 sinatra 教程 - http://code.tutsplus.com/tutorials/singing-with-sinatra--net-18965

【讨论】:

  • 为什么空的controller.rb 有get() 可用?
  • 正如我发布的链接所暗示的,您必须在控制器中包含 gem 'sinatra'
【解决方案3】:

您可以根据需要将其复杂化或简单化。例如:

Rails 使许多神奇的事情发生在幕后,而 Sinatra 更加灵活,但需要您自己实现其中的一些东西。

controller_map = {
  'pages' => PagesController
}

post '/:controller/new' do
  c = params[:controller]
  module = controller_map[c]
  module.create_new()
  ...
end

get '/:controller/:id/show' do
  c = params[:controller]
  id = params[:id]
  module = controller_map[c]
  module.get(id)
  ...
end

【讨论】:

  • 这样做而不是通过map { MyController }config.ru 中声明它们有什么好处?
  • 不管你在哪里声明,这个例子是为了演示。