我正在使用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
这样的类变量,则上面的代码可以正常工作。但是为什么实例变量在这种情况下不起作用?
答案 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 为此任务提供了方法(如下所述)。