【问题标题】:Use application class instance variable inside get block在 get 块中使用应用程序类实例变量
【发布时间】:2014-06-02 11:12:51
【问题描述】:

我正在使用 sinatra,我有代码:

class App < Sinatra::Base
  configure do
    @logger = Logger.new "./log"
  end

  @logger.info "App started"          #this line works

  get "/info" do
    @logger.info "/info inquired"     #this does not work and complain @logger is nilClass
  end
end

为什么 @logger 在 get 块中给出一个 nil 对象?在这种情况下如何使用@logger


PS。如果我使用像@@logger 这样的类变量,上面的代码就可以工作。但是为什么实例变量在这种情况下不起作用?

【问题讨论】:

    标签: ruby sinatra


    【解决方案1】:

    在实例变量出现时,实例变量将自己附加到任何对象本身。

    从表面上看,这些是自我的价值观:

    class App < Sinatra::Base
      #In here, self=App
    
      #When a block executes, it sees the value for self that existed in 
      #the surrounding scope at the time the block was CREATED:
      configure do #a block
        #So...in here self=App 
        @logger = Logger.new "./log"
      end
    
      @logger.info "App started"          #this line works
    
      get "/info" do #a block
        #Similarly, in here self=App
        @logger.info "/info inquired"     #@logger is NilClass
      end
    end
    

    基于这种情况,你是正确的:看起来当 configure() 执行传递给它的块时,@logger 会出现并附加到 App,然后当 get()调用传递给它的块,@logger 实例变量将引用附加到 App 的实例变量。

    但是...ruby 提供了一些方法来更改当块执行时块看到的 self 的值。这是一个例子:

    p = Proc.new { puts self }
    p.call
    
    class Dog
      def initialize(a_proc)
        #In here, self is a Dog instance
        instance_eval &a_proc
      end
    end
    
    Dog.new p
    
    --output:--
    main
    #<Dog:0x000001009b6080>
    

    根据您的错误,您必须怀疑 Sinatra 在执行传递给 get() 的块时必须使用一些 ruby​​ 技巧来更改 self。

    我们怎么知道这个?

    Ruby 是编程语言的狂野西部,因此除非您查看源代码或好的文档(如果存在),否则您永远无法知道会发生什么。源代码相当复杂。我在文档中找到了这个:

    要想写出好的作品,需要对 Sinatra 的内部设计有所了解 扩展名。本节提供类的高级概述 和成语是 Sinatra 设计的核心。

    Sinatra 有两种不同的使用模式,扩展程序应注意 的:

    “经典”风格,其中应用程序在 main / the 顶级——大多数示例和文档都针对这种用法。 经典应用程序通常是单文件、独立的应用程序,它们是 直接从命令行运行或使用最小的 rackup 文件运行。什么时候 在经典应用程序中需要扩展,期望是 所有扩展功能都应该在没有额外的情况下存在 在应用程序开发人员部分设置(如包含/扩展 模块)。

    “模块化”风格,其中 Sinatra::Base 被显式子类化,并且 应用程序是在子类的范围内定义的。这些 应用程序通常捆绑为库并用作组件 在更大的基于机架的系统中。模块化应用程序必须包括 通过调用 register ExtensionModule 显式地进行任何所需的扩展 在应用程序的类范围内。

    大多数扩展都与这两种样式相关,但必须注意 扩展作者以确保扩展在以下情况下做正确的事情 每种风格。扩展 API(Sinatra.register 和 Sinatra.helpers) 提供以帮助扩展作者完成此任务。

    重要提示:关于 Sinatra::Base 和 Sinatra::Application 仅用于后台 - 扩展 作者不需要直接修改这些类。

    Sinatra::Base Sinatra::Base 类为所有 在 Sinatra 应用程序中进行评估。顶级的 DSLish 东西存在 在类范围内,而请求级的东西存在于实例范围内。

    应用程序在 Sinatra::Base 的类范围内定义 子类。 “DSL”(例如,get、post、before、configure、set 等)是 只是在 Sinatra::Base 上定义的一组类方法。扩展 DSL 是通过向 Sinatra::Base 或其其中之一添加类方法来实现的 子类。但是,基类不应该用 extend 来扩展; 为此提供了 Sinatra.register 方法(如下所述) 任务。

    请求在一个新的 Sinatra::Base 实例中进行评估——路由, 在过滤器、视图、帮助程序和错误页面都共享相同之前 请求级辅助方法的默认集合(例如 erb、 haml、halt、content_type 等)是定义在 Sinatra::Base 或包含在 Sinatra::Base 中的模块内。 在请求级别提供新功能是通过添加 Sinatra::Base 的实例方法。

    与 DSL 扩展一样,辅助模块不应直接添加到 Sinatra::Base by extension authors with include; Sinatra.helpers 为此任务提供了方法(如下所述)。

    http://www.sinatrarb.com/extensions.html

    【讨论】:

    • 这是有道理的。他们可能应该将上下文信息放在自述文件中,而不是普通用户看不到的地方。它甚至不是关于编写扩展......它只是一个普通的应用......但是谢谢!
    • @texasbruce,我认为这可能取决于营销。我的印象是 Sinatra 应该是 ruby​​ on rails 的简单版本。因此,如果 Sinatra 文档开始讨论 instance_eval 以及传递给各种方法的块将在不同的上下文中执行,那么 Sinatra 会显得很复杂,一半的人会退出。
    • 如果用户写了一些代码甚至没有执行,我相信营销效果会更差
    • @texasbruce,你说得有道理。 Sinatra 文档确实这样说:Logging. In the request scope, the logger helper exposes a Logger instance: get '/' do logger.info "loading data" # ... end 当然,如果你读到了,你可能不会记得或意识到 In the request scope.. 的重要性,
    • 我知道那个记录器。是rack logger,可以通过Rack中间件Rack::CommonLogger设置,但只存在于请求范围内,不存在配置
    【解决方案2】:

    您可以在 Sinatra::Base 中定义自己的记录器,并在您的 get 块中使用它:

    class App < Sinatra::Base
      set :logger, Logger.new("./log")
    
      helpers do
        def logger; self.class.logger; end
      end
    
      logger.info self
    
      get "/info" do
        logger.info self
      end
      # ...
    end
    

    或者使用您在编辑中记下的类变量。上述配置的日志文件显示了原因:

    I, [2014-06-01T16:36:51.593033 #16144]  INFO -- : App
    I, [2014-06-01T16:36:59.438078 #16144]  INFO -- : #<App:0x9aa919c @default_layout=:layout, @app=nil ...
    

    在第一种情况下,self 是应用程序类,而在 get 块中,self 是类的实例。

    为了澄清,在您的示例中:Ruby 将第一个 @logger.info(从您的类的上下文调用)解释为类实例变量,而第二个 @logger.info 被解释为实例变量(尚未定义)。您在 configure 块中定义的变量是在类上下文中设置的。

    【讨论】:

    • 为什么前两个在类的上下文中,第二个是实例?我们怎么知道呢?这是类的设计缺陷吗?
    • 在 Sinatra 中,直接使用 settings method 而不是 self.class 更为常见,因此您的助手看起来像 def logger; settings.logger; end,或者甚至不使用助手,直接使用 settings.logger您的路线代码。
    猜你喜欢
    • 2011-10-03
    • 2018-05-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多