堆栈级别到深Rails 2.3.14

时间:2012-07-01 15:28:11

标签: ruby-on-rails redmine redmine-plugins

我正在为管理框架开发一个插件,当我在开发模式下启动webrick服务器时,会出现一个奇怪的错误(堆栈级别太深)。当一个动作(例如show动作)开始渲染模板时,就会发生这种情况。不幸的是,我不知道为什么会这样。

SystemStackError in Stories#show

Showing vendor/plugins/stories/app/views/stories/show.rhtml where line #5 raised:

stack level too deep

Extracted source (around line #5):

    5: link_to_if_authorized 'aa', {:controller => "stories", :action => "index", :id => @story.id, :project_id => @story.project.id}, :title => l(:view_story), :class => 'icon icon-zoom-out'
    6: link_to_if_authorized 'bb', {:controller => "stories", :action => "edit", :id => @story.id, :project_id => @story.project.id}, :title => l(:button_edit), :class => 'icon icon-edit'
    7: link_to 'bb', {:id => @story.id, :project_id => @story.project.id}, :confirm => 'Really  delete?', :method => :delete, :class => 'icon icon-del' if User.current.allowed_to? (:delete_stories, @project)

    RAILS_ROOT: /home/haendwic/Documents/Aptana Studio 3 Workspace/1.4-stable-SVN
    Application Trace | Framework Trace | Full Trace

    /var/lib/gems/1.8/gems/activerecord-2.3.14/lib/active_record/attribute_methods.rb:248:in method_missing'
    /var/lib/gems/1.8/gems/activerecord-2.3.14/lib/active_record/attribute_methods.rb:249:in method_missing'
     /var/lib/gems/1.8/gems/activerecord-2.3.14/lib/active_record/associations /association_proxy.rb:215:in send'
     /var/lib/gems/1.8/gems/activerecord-2.3.14/lib/active_record/associations/association_proxy.rb:215:in method_missing'
     /home/haendwic/Documents/Aptana Studio 3 Workspace/1.4-stable-SVN/vendor/plugins/stories/app/views/stories/show.rhtml:5:in  _run_rhtml_vendor47plugins47stories47app47views47stories47show46rhtml'
     /var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_view/renderable.rb:34:in send'
      /var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_view/renderable.rb:34:in render'
      /var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_view/base.rb:306:in with_template'
      /var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_view/renderable.rb:30:in render'
      /var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_view/template.rb:205:in render_template'
     /var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_view/base.rb:265:in render'
     /var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_view/base.rb:348:in _render_with_layout'
     /var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_view/base.rb:262:in render'
     /var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/base.rb:1252:in render_for_file'
     /var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/base.rb:936:in render_without_benchmark'
    /var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/benchmarking.rb:51:in render'
    /var/lib/gems/1.8/gems/activesupport-2.3.14/lib/active_support/core_ext/benchmark.rb:17:in ms'
    /usr/lib/ruby/1.8/benchmark.rb:308:in realtime'
    /var/lib/gems/1.8/gems/activesupport-2.3.14/lib/active_support/core_ext/benchmark.rb:17:in ms'
    /var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/benchmarking.rb:51:in render'
/home/haendwic/Documents/Aptana Studio 3 Workspace/1.4-stable-SVN/vendor/plugins/stories/app/controllers/stories_controller.rb:104:in show'
     /var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/mime_responds.rb:135:in call'
      /var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/mime_responds.rb:135:in custom'
     /var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/mime_responds.rb:179:in call'
     /var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/mime_responds.rb:179:in respond'
     /var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/mime_responds.rb:173:in each'
     /var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/mime_responds.rb:173:in respond'
      /var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/mime_responds.rb:107:in respond_to'
    /home/haendwic/Documents/Aptana Studio 3 Workspace/1.4-stable-SVN/vendor/plugins/stories/app/controllers/stories_controller.rb:102:in show'
     /var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/base.rb:1333:in send'
     /var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/base.rb:1333:in perform_action_without_filters'
/var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/filters.rb:617:in call_filters'
     /var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/filters.rb:610:in perform_action_without_benchmark'
     /var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/benchmarking.rb:68:in perform_action_without_rescue'
     /var/lib/gems/1.8/gems/activesupport-2.3.14/lib/active_support/core_ext/benchmark.rb:17:in ms'
     /usr/lib/ruby/1.8/benchmark.rb:308:in realtime'
     /var/lib/gems/1.8/gems/activesupport-2.3.14/lib/active_support/core_ext/benchmark.rb:17:in ms'
     /var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/benchmarking.rb:68:in perform_action_without_rescue'
     /var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/rescue.rb:160:in perform_action_without_flash'
     /var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/flash.rb:151:in perform_action'
     /var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/base.rb:532:in send'
     /var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/base.rb:532:in process_without_filters'
     /var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/filters.rb:606:in process'
     /var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/base.rb:391:in process'
     /var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/base.rb:386:in call'
     /var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/routing/route_set.rb:438:in call'
     /var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/dispatcher.rb:87:in dispatch'
    /var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/dispatcher.rb:121:in _call'
    /var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/dispatcher.rb:130:in build_middleware_stack'
    /var/lib/gems/1.8/gems/activerecord-2.3.14/lib/active_record/query_cache.rb:29:in call'
    /var/lib/gems/1.8/gems/activerecord-2.3.14/lib/active_record/query_cache.rb:29:in call'
    /var/lib/gems/1.8/gems/activerecord-2.3.14/lib/active_record/connection_adapters/abstract/query_cache.rb:34:in cache'
     /var/lib/gems/1.8/gems/activerecord-2.3.14/lib/active_record/query_cache.rb:9:in cache'
     /var/lib/gems/1.8/gems/activerecord-2.3.14/lib/active_record/query_cache.rb:28:in call'
     /var/lib/gems/1.8/gems/activerecord-2.3.14/lib/active_record/connection_adapters/abstract/connection_pool.rb:361:in call'
    /var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/string_coercion.rb:25:in call'
    /var/lib/gems/1.8/gems/rack-1.1.3/lib/rack/head.rb:9:in call'
    /var/lib/gems/1.8/gems/rack-1.1.3/lib/rack/methodoverride.rb:24:in call'
    /var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/params_parser.rb:15:in call'
    /var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/session /cookie_store.rb:99:in call'
    /var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/failsafe.rb:26:in call'
    /var/lib/gems/1.8/gems/rack-1.1.3/lib/rack/lock.rb:11:in call'
    /var/lib/gems/1.8/gems/rack-1.1.3/lib/rack/lock.rb:11:in synchronize'
    /var/lib/gems/1.8/gems/rack-1.1.3/lib/rack/lock.rb:11:in call'
    /var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/dispatcher.rb:114:in call'
    /var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/reloader.rb:34:in run'
    /var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/dispatcher.rb:108:in call'
    /var/lib/gems/1.8/gems/rails-2.3.14/lib/rails/rack/static.rb:31:in call'
    /var/lib/gems/1.8/gems/rack-1.1.3/lib/rack/urlmap.rb:47:in call'
    /var/lib/gems/1.8/gems/rack-1.1.3/lib/rack/urlmap.rb:41:in each'
    /var/lib/gems/1.8/gems/rack-1.1.3/lib/rack/urlmap.rb:41:in call'
    /var/lib/gems/1.8/gems/rails-2.3.14/lib/rails/rack/log_tailer.rb:17:in call'
    /var/lib/gems/1.8/gems/rack-1.1.3/lib/rack/content_length.rb:13:in call'
    /var/lib/gems/1.8/gems/rack-1.1.3/lib/rack/handler/webrick.rb:48:in service'
    /usr/lib/ruby/1.8/webrick/httpserver.rb:104:in service'
    /usr/lib/ruby/1.8/webrick/httpserver.rb:65:in run'
    /usr/lib/ruby/1.8/webrick/server.rb:173:in start_thread'
    /usr/lib/ruby/1.8/webrick/server.rb:162:in start'
    /usr/lib/ruby/1.8/webrick/server.rb:162:in start_thread'
    /usr/lib/ruby/1.8/webrick/server.rb:95:in start'
    /usr/lib/ruby/1.8/webrick/server.rb:92:in each'
    /usr/lib/ruby/1.8/webrick/server.rb:92:in start'
    /usr/lib/ruby/1.8/webrick/server.rb:23:in start'
    /usr/lib/ruby/1.8/webrick/server.rb:82:in start'
    /var/lib/gems/1.8/gems/rack-1.1.3/lib/rack/handler/webrick.rb:14:in run'
    /var/lib/gems/1.8/gems/rails-2.3.14/lib/commands/server.rb:111
    script/server:3:in require'
    script/server:3
Request

Parameters:

{"project_id"=>"1",
 "id"=>"2"}

Show session dump
Response

Headers:

{"Cache-Control"=>"no-cache",
 "Content-Type"=>"text/html"}

这是来自控制器的动作

def show 
 @edit_allowed = User.current.allowed_to?(:edit_stories, @project)
 respond_to do |format|
  format.html {
    render :template => 'stories/show'
  }
  format.api
  format.pdf  { send_data(story_to_pdf(@story), :type => 'application/pdf', :filename => "#{@project.identifier}-#{@story.id}.pdf") }
 end                                
end

最后是视图的一部分

     link_to_if_authorized 'aa', {:controller => "stories", :action => "index", :id => @story.id, :project_id => @story.project.id}, :title => l(:view_story), :class => 'icon icon-zoom-out'
     link_to_if_authorized 'bb', {:controller => "stories", :action => "edit", :id => @story.id, :project_id => @story.project.id}, :title => l(:button_edit), :class => 'icon icon-edit' 
    link_to 'bb', {:id => @story.id, :project_id => @story.project.id}, :confirm => 'Really delete?', :method => :delete, :class => 'icon icon-del' if User.current.allowed_to?(:delete_stories, @project) 

也许重要的是,在生产模式下,插件是稳定的并且正确地路由动作(包括show)

2 个答案:

答案 0 :(得分:1)

这通常是由您(或其他人)在不考虑rails重新加载器的情况下从插件修补核心模型(可能是Project模型)引起的。

如果覆盖方法(例如使用alias_method_chain)并且补丁加载了两次,则在调用旧方法时可以轻松创建无限循环。

# This is our initial class
class MyClass
  def foo
    puts "original foo"
  end
end

module Patch
  def self.included(base)
    base.alias_method_chain :foo, :feature
  end

  def foo_with_feature
    foo_without_feature # call the "original" method
    puts "foo with feature"
  end
end

# patch the class
MyClass.send(:include, Patch)

# Now call the patched method
MyClass.new.foo
# prints:
#   original foo
#   foo with feature

foo现在引用方法foo_with_feature,而现在可以从foo

访问原始foo_without_feature方法

好吧,直到现在一切都很好看。现在让我们看看如果我们再次加载补丁会发生什么

# patch again
MyClass.send(:include, Patch)

# And call the method again
MyClass.new.foo
# SystemStackError: stack level too deep
#   from (irb):7:in `foo_without_feature'
#   from (irb):7:in `foo'
#   from (irb):27

您会看到由无限循环引起的SystemStackError。这是因为在第二次加载补丁后,foo_without_feature现在引用第一个补丁中的foo_with_feature方法。在调用它时,它会一遍又一遍地调用foo_without_feature直到堆栈已满。

你说它只在第二次请求时崩溃了。这正是类重新加载器上某些东西奇怪时的典型行为。默认情况下,Rails将在开发模式下的每个请求上重新加载所有类,但在生产模式下只会重新加载一次。

有时候驯服rails重新加载器有点棘手。作为一些一般准则,你应该

  1. 在加载修补程序时使用require_dependency而不是require
  2. 使用Dispatcher.to_prepare
  3. 加载修补程序
  4. 始终声明已修补的课程unloadable
  5. 最关键的部分是使用Dispatcher.to_prepare。它是一个回调,在生产模式下和开发模式下的每个请求之前调用一次,因此是加载补丁的理想位置。

    作为旁注:但是当使用Redmine 2(或即将推出的ChiliProject 4),即Rails 3时,类修补将与此方法完全不同 - 最可能更容易。

答案 1 :(得分:0)

“堆栈级别太深”错误意味着您有堆栈溢出(它通常足以让以其命名的站点)。当函数无限期地调用自身或两个函数无限期地相互调用时,就会发生这种情况。

您的错误发生在第5行,因此我会检查link_to_if_authorized的来源。那里的东西导致无限循环。