今天遇到 ActionView::MissingTemplate 一个问题,大多数情况下遇到这样的问题,通常都伴随着 HTTP_ACCEPT "/的情况。网上已经有很多文章解答如何 fix 这个问题,大多是指明具体的类型。但是很明显,这样的解决方案不是我目前想要的。
在练习《craft rails 4 applications》的第一个创建 pdf 的 lib 例子中就出现了Missing template home/index, application/index with {:locale=>[:en], :formats=>[:pdf], :handlers=>[:erb, :builder, :raw, :ruby]}. Searched in: * "/Users/ane/projects/plugin/pdf_renderer/test/dummy/app/views"
这样讨厌的错误。
说说我的诊错流程:
ActionView::MissingTemplate
错误的前因后果。
2 .第二步 利用 debugger,
```
module PdfRenderer
require "prawn"ActionController::Renderers.add :pdf do |filename,options|
debugger
pdf = Prawn::Document.new
debugger
pdf.text render_to_string(options)
debugger
send_data(pdf.render,filename: "#{filename}.pdf", disposition: "attachment")
end
end
最终定位到`pdf.text render_to_string(options)`的错误。于是简单修改为`pdf.text "render_to_string(options)"`,测试通过。所以必然是 `render_to_string(options)`的问题。
3.第三步:追踪发现 `options=={:prefixes=>["home", "application"], :template=>"index", :layout=>nil}`
4.第四步:先吃饭,晚上继续搞。
5.第五步:继续追踪 render_to_string
module ActionController module Rendering extend ActiveSupport::Concern
include AbstractController::Rendering .... def render_to_string(*) if self.response_body = super string = "" response_body.each { |r| string << r } string end ensure self.response_body = nil end ....
这里调用了super也就是`AbstractController::Rendering`中的`render_to_string`
111 def render_to_string(*args, &block) 112 options = _normalize_render(*args, &block) => 113 render_to_body(options) 114 end
到了这里,笔者天真的使用了*args来看第一个参数是什么,结果发现原来是用args
(rdb:2) args ** SyntaxError Exception: /Users/ane/.rvm/gems/ruby-2.0.0-p451@rails400/gems/actionpack-4.0.5/lib/abstract_controller/rendering.rb:112: syntax error, unexpected end-of-input, expecting '=' *args ^
(rdb:2) args {:prefixes=>["home", "application"], :template=>"index", :layout=>nil} (rdb:2) block nil
进入`_normalize_render`
151 def _normalize_render(*args, &block) => 152 options = _normalize_args(*args, &block) 153 _normalize_options(options) 154 options 155 end
#返回一个 Hash def _normalize_args(action=nil, options={}) case action when NilClass when Hash options = action when String, Symbol action = action.to_s key = action.include?(?/) ? :file : :action options[key] = action else options[:partial] = action end
options end
因为本来就是hash,所以152直接返回。(表示对文档的缓存功能大赞)
进入153
def _normalize_options(options) #:nodoc: if options.key?(:text) && options[:text].respond_to?(:to_text) options[:text] = options[:text].to_text end
if options.delete(:nothing) || (options.key?(:text) && options[:text].nil?) options[:text] = " " end
if options[:status] options[:status] = Rack::Utils.status_code(options[:status]) end
super end
因为if条件都不满足,直接进入super,这里我原本以为进入`AbstractController::Rendering`.可实际进入的的是`AbstractController`里,一开始还觉得奇怪,也发现了原来super的确是这样工作的。
先进入大模块AbstractController中的方法,如果不存在,就进入include的子module
#AbstractController def _normalize_options(options) # :nodoc: super
if _include_layout?(options) layout = options.delete(:layout) { :default } options[:layout] = _layout_for_option(layout) end end
def _include_layout?(options) (options.keys & [:text, :inline, :partial]).empty? || options.key?(:layout) end
#AbstractController::Rendering def _normalize_options(options) if options[:partial] == true options[:partial] = action_name end
if (options.keys & [:partial, :file, :template]).empty? options[:prefixes] ||= _prefixes end
options[:template] ||= (options[:action] || action_name).to_s options end
可以看出在`AbstractController::Rendering`中options依旧没有变化。
跟踪进入_layout_for_option(layout)
def _layout_for_option(name) case name when String then _normalize_layout(name) when Proc then name when true then Proc.new { _default_layout(true) } when :default then Proc.new { _default_layout(false) } when false, nil then nil else raise ArgumentError, "String, Proc, :default, true, or false, expected for `layout'; you passed #{name.inspect}" end end
原来给 options[:layout]赋上了一个proc
(rdb:2) Proc.new { _default_layout(false) } #Proc:0x00000101a64418@/Users/ane/.rvm/gems/ruby-2.0.0-p451@rails400/gems/actionpack-4.0.5/lib/abstract_controller/layouts.rb:379 (rdb:2)
通过对proc的注释`# Optionally raises an exception if the layout could not be found.`,初步怀疑问题处在这个proc中
anyway,先进入原先的154返回我们的options
151 def _normalize_render(*args, &block) 152 options = _normalize_args(*args, &block) 153 _normalize_options(options) => 154 options 155 end 156 157 # Normalize args by converting render "foo" to render :action => "foo" and 158 # render "foo/bar" to render :file => "foo/bar". (rdb:2) options {:prefixes=>["home", "application"], :template=>"index", :layout=>#Proc:0x0000010129b088@/Users/ane/.rvm/gems/ruby-2.0.0-p451@rails400/gems/actionpack-4.0.5/lib/abstract_controller/layouts.rb:383} (rdb:2)
6: 第六步:进入最上面的113`render_to_body(options)`
25 def render_to_body(options) 26 debugger => 27 _handle_render_options(options) || super 28 end
先看看 _handle_render_options(options)
(rdb:2) _handle_render_options(options) nil (rdb:2)
再看看super
(rdb:2) super *** NoMethodError Exception: super: no superclass method `render_to_body' for #HomeController:0x000001090092b8
(rdb:2)
呃,难道发现bug了?
好吧,还记的最初的 ` pdf.text "render_to_string(options)" `吗?看看它什么情况
(rdb:2) _handle_render_options(options) || super *** NoMethodError Exception: super: no superclass method `render_to_body' for #HomeController:0x00000104120908
原来一模一样。但是确实成功下载了。
7: 第七步,记得一定要先发帖子,再更新,这样就不怕放弃了,免得丢人。
8: 第八步, 倒回去,重lol。发现 controller中的`format.pdf { render pdf: "contents", :layout => false }
end`之后就先进入一次
def render_to_body(options) 26 debugger => 27 _handle_render_options(options) || super 28 end
然后才进入我们定义的lib的
ActionController::Renderers.add :pdf do |filename,options|
debugger
pdf = Prawn::Document.new
debugger
pdf.text render_to_string(options)
debugger
send_data(pdf.render,filename: "#{filename}.pdf", disposition: "attachment")
end
。发现`pdf.text render_to_string(options)`的时候,_handle_render_options(options)是
(rdb:2) _handle_render_options(options) *** ActionView::MissingTemplate Exception: Missing template home/index, application/index with {:locale=>[:en], :formats=>[:pdf], :handlers=>[:erb, :builder, :raw, :ruby]}. Searched in:
(rdb:2)
`pdf.text "render_to_string(options)"`的时候,_handle_render_options(options)是
rdb:2) _handle_render_options(options) Rendered text template (0.0ms) Sent data contents.pdf (4.3ms) "%PDF-1.3\n%\xFF\xFF\xFF\xFF\n1 0 obj\n<< /Creator \n/Producer \n>>\nendobj\n2 0 obj\n<< /Type /Catalog\n/Pages 3 0 R\n>>\nendobj\n3 0 obj\n<< /Type /Pages\n/Count 1\n/Kids [5 0 R]\n>>\nendobj\n4 0 obj\n<< /Length 103\n>>\nstream\nq\n\nBT\n36 747.384 Td\n/F1.0 12 Tf\n[<72656e6465725f746f5f737472> -15 <696e67286f7074696f6e7329>] TJ\nET\n\nQ\n\nendstream\nendobj\n5 0 obj\n<< /Type /Page\n/Parent 3 0 R\n/MediaBox [0 0 612.0 792.0]\n/Contents 4 0 R\n/Resources << /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]\n/Font << /F1.0 6 0 R\n>>\n>>\n>>\nendobj\n6 0 obj\n<< /Type /Font\n/Subtype /Type1\n/BaseFont /Helvetica\n/Encoding /WinAnsiEncoding\n>>\nendobj\nxref\n0 7\n0000000000 65535 f \n0000000015 00000 n \n0000000109 00000 n \n0000000158 00000 n \n0000000215 00000 n \n0000000369 00000 n \n0000000547 00000 n \ntrailer\n<< /Size 7\n/Root 2 0 R\n/Info 1 0 R\n>>\nstartxref\n644\n%%EOF\n"
很明显两者的区别太大了。
9:第9步,停下来,重新lol.刨根问底有点深了,把自己给埋了。砰砰砰,请看下集
初步怀疑
render_to_body(options) _handle_render_options(options) || super end
中的super是不是个bug? format.pdf一定有故事。