【问题标题】:Why does Sinatra copy the method block of a route?为什么 Sinatra 复制路由的方法块?
【发布时间】:2011-11-29 16:04:44
【问题描述】:

这是我之前关于 Sinatra 处理路由方法的问题的一个更集中的版本。

根据我对源代码的理解,Sinatra 采用路由中的方法块,并传递一个包含相同主体的新方法,即:

get "some/url" do 
   return "Hello World" # this gets taken out
end

所以在这个例子中,方法体似乎被复制到一个新方法中,该方法应用于 Sinatra 对象。我只是想知道为什么会发生这种情况,我尝试上他们的 IRC 频道但没有人在那里,而且邮件列表也不那么忙。

我在他们的框架中谈论的主要源代码在 base.rb 中,大约在第 1180 行:

  def generate_method(method_name, &block)
    define_method(method_name, &block)
    method = instance_method method_name
    remove_method method_name
    method
  end

那么他们这样做有什么具体原因,而不仅仅是引用方法本身?

我问这个问题的原因是因为 Sinatra 目前处理这个问题的方式使得不可能有一个拥有自身之外的知识的方法,并且通过只采用一个没有上下文的方法来破坏类封装。

【问题讨论】:

    标签: ruby methods sinatra


    【解决方案1】:

    与上面的 cmets 一样,这会生成一个方法。适当的方法。如果 Sinatra 不会再次删除 generate_method 中的方法,您实际上可以通过执行类似 send("GET some/url") 的操作来调用它。问题是,为什么 Sinatra 又删除了这个方法?很简单,每条路由可能有多个处理程序:

    get 'some/route' do
      pass if request.referrer == '/foo'
      "didn't come from /foo"
    end
    
    get 'some/route' do
      "did come from /foo"
    end
    

    两种方法同名。

    至于您的 cmets 关于子类和方法,这应该可行:

    class MyApp < Sinatra::Base
      def content
        return "did come from /foo" if request.referrer == '/foo'
        "didn't come from /foo"
      end
    
      get('some/route') { content }
    end
    

    或者,在执行经典应用程序时:

    helper do
      def content
        return "did come from /foo" if request.referrer == '/foo'
        "didn't come from /foo"
      end
    end
    
    get('some/route') { content }
    

    【讨论】:

    • 我认为这个问题的答案已经很清楚了,这是我的误解,它是一流的方法而不是匿名的方法。它仍然无助于我遇到的 实际 问题,但那是在另一个问题中。
    • 刚刚想到的一件事...在您的最后一个示例中,您展示了一种访问匿名方法范围之外的事物的方法,这很棒... IS 我的问题,但从外观上看,这样做的唯一方法是将其添加到帮助程序或使用 set.这对于方法块实际上可以完成您需要的所有事情的小事情来说似乎很棒,但是当您依赖于其他类和实例时,您似乎会将几乎所有内容添加到帮助程序中以使其在范围内。显示我遇到的主要问题的一个问题是关于 KL-7 的一个 cmets。
    • 您应该研究模块化应用程序。
    • 您是在谈论模块化编程还是模块化 sinatra 应用程序?我认为是前者,因为由于缺乏背景,我已经在尝试做后者但没有取得多大成功。在前者中,虽然我试图使事物模块化,因此它们只是在运行时加载并分离,但问题是如果我这样做,那么我需要模块之间的一些通用合同,以便它们可以通信......无论是基于事件的,基于服务定位器等,但是似乎使用方法的方式希望我编写非分隔代码并直接使用 consts
    【解决方案2】:

    我的猜测是,他们希望每条路由都拥有成熟的方法(可以访问其他实例和类方法),但又不想污染命名空间。方法名称生成为"#{verb} #{path}",因此,例如,如果您有多个具有不同条件的相同路径冲突的路由,除非您在定义并存储在其他地方后立即删除方法,否则冲突是不可避免的。这正是他们所做的。方法是未绑定的,但这不是问题,因为他们可以稍后将其绑定到类的任何实例。

    请注意,这只是一个猜测。我对 Sinatra 不太熟悉,所以这个实现背后可能有完全不同的想法。

    【讨论】:

    • 也许我只是没有正确理解,但是不可能只有一个指向方法键的路由列表吗?然后该方法键引用 ACTUAL 方法,而不是新生成的方法。这将满足具有多个路由到同一方法的标准。我只是认为这对于我来说似乎是一个出色的框架/库来说是一个几乎严重的问题。由于几乎不可能在这些方法中执行任何业务逻辑,因为您只能真正使用静态/全局对象,这几乎停止了任何 OO 实践。
    • “实际”方法是什么意思?当您在 sinatra 中定义路由时,您将传递路径字符串、一些选项,最后是一个块到 get 方法。当时还没有针对这条特定路线的任何方法。该操作发生在generate_method 内部,其中生成了具有非常丑陋的名称(例如“GET /”)的新方法,并将其与其他一些选项一起存储到某个数组中。您在问题中引用的“方法体”和“复制方法”实际上分别是“块体”和“将块转换为方法”。
    • 顺便说一句,这如何阻止您使用 OO?
    • 因为如果你将你的路由方法放在一个类中(即来自 MVC 方法的控制器)然后尝试从控制器中调用你将无法调用的任何东西,因为 this/self 对象不再是控制器,它是 Sinatra 对象(或放置此方法的任何位置)。我在另一篇 SO 帖子中发布了关于这个问题的帖子,快速阅读,它应该完全解释导致我写这个问题的问题。 stackoverflow.com/questions/8225844/…
    • 我实际上并没有意识到它是一种匿名方法,它改变了事物并且理解了它为什么会这样做。我一直认为这些 get() 是对调用它的对象的本地方法的包装。但是即使知道这一点,问题仍然存在,就好像我要创建一个从 Sinatra::Base 继承的类并创建一个名为 my_get_method 的本地方法,只是告诉我的路线指向它它不能指向它,因为它不知道它之外的范围,这对我来说是问题的主要症结所在。有没有办法改变被调用方法的范围?
    【解决方案3】:

    我猜它只是模拟 instance_exec 来支持早于 1.8.7 的 Ruby

    【讨论】:

      猜你喜欢
      • 2014-01-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-05-15
      • 1970-01-01
      • 1970-01-01
      • 2013-01-02
      • 1970-01-01
      相关资源
      最近更新 更多