在get块中使用应用程序类实例变量

时间:2014-06-01 21:14:03

标签: ruby sinatra

我正在使用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

为什么{block}内的@logger给出一个nil对象?在这种情况下如何使用@logger


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

2 个答案:

答案 0 :(得分:1)

您可以在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块中定义的变量是在类上下文中设置的。

答案 1 :(得分: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技巧来改变自我。

  

我们怎么知道这个?

Ruby是编程语言的狂野西部,所以除非你查看源代码或好的文档(如果它们存在),否则你无法知道会发生什么。源代码非常复杂。我在文档中找到了这个:

  

一些关于Sinatra内部设计的知识需要写得好   扩展。本节提供了类的高级概述   和习语是Sinatra设计的核心。

     

Sinatra有两种不同的使用模式,扩展应该知道   的:

     

“经典”样式,其中应用程序在main / the上定义   顶级 - 大多数示例和文档都以此用法为目标。   经典应用程序通常是单文件独立应用程序   直接从命令行或使用最小的rackup文件运行。什么时候   在经典应用程序中需要扩展,期望是   所有扩展功能都应该存在而无需额外的   应用程序开发人员部分的设置(如包含/扩展   模块)。

     

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

     

大多数扩展都与两种风格相关,但必须小心谨慎   扩展作者,以确保扩展做正确的事情   每种风格。扩展API(Sinatra.register和Sinatra.helpers)   用于帮助扩展作者完成此任务。

     

重要提示:以下关于Sinatra :: Base和的说明   Sinatra ::应用程序仅供参考 - 扩展   作者不应该直接修改这些类。

     

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

     

应用程序在Sinatra :: Base的类范围内定义   子类。 “DSL”(例如,获取,发布,之前,配置,设置等)是   只是在Sinatra :: Base上定义的一组类方法。延伸   通过向Sinatra :: Base或其中一个添加类方法来实现DSL   子类。但是,Base类不应该使用extend进行扩展;   为此提供了Sinatra.register方法(如下所述)   任务。

     

在新的Sinatra :: Base实例中评估请求 - 路由,   在过滤器,视图,帮助器和错误页面之前都共享这一点   context。默认的请求级辅助方法集(例如,erb,   haml,halt,content_type等)是定义的简单实例方法   Sinatra :: Base或Sinatra :: Base中包含的模块。   通过添加来实现在请求级别提供新功能   Sinatra :: Base的实例方法。

     

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

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