【问题标题】:Rails project: NoMethodError: undefined method `identifier' for StringRails 项目:NoMethodError:字符串的未定义方法“标识符”
【发布时间】:2013-12-25 11:34:10
【问题描述】:

我最近继承了一个 Rails + Spine.JS 项目。尽管它的主题不是非常神秘的东西(面向营销:人们制作自己的邮件列表,在发送之前准备内容,实际发送等等),但是它的实现是特定的并且依赖于多个 Rails 覆盖(就像下面问题 #2 中指定的那样)。

我有两个问题需要帮助。

问题一

直截了当:在 Spine.JS 应用程序(前端)中,我们有一个发票预览操作,它应该(并且确实)打开一个新选项卡来执行此操作。在我真正做到之前,一切看起来都很正常。没有太多毫无意义的解释,这就是发生的事情:

Started GET "/me/invoices/22/preview?oauth_token=XE03DFCGyCDYjdu8oWuU9zrN56Z6Wr7eZDuIjCbT&origin=http://localhost:9294" for 94.26.14.131 at 2013-12-08 00:55:27 +0200
  Oauth2Token Load (0.3ms)  SELECT `oauth_tokens`.* FROM `oauth_tokens` WHERE `oauth_tokens`.`type` IN ('Oauth2Token') AND (invalidated_at IS NULL AND authorized_at IS NOT NULL and token = 'XE03DFCGyCDYjdu8oWuU9zrN56Z6Wr7eZDuIjCbT') LIMIT 1
Processing by InvoicesController#preview as HTML
  Parameters: {"oauth_token"=>"XE03DFCGyCDYjdu8oWuU9zrN56Z6Wr7eZDuIjCbT", "origin"=>"http://localhost:9294", "id"=>"22"}
  User Load (0.4ms)  SELECT `users`.* FROM `users` WHERE `users`.`id` = 270932 LIMIT 1
  Invoice Load (0.2ms)  SELECT `receipts`.* FROM `receipts` WHERE `receipts`.`type` IN ('Invoice') AND `receipts`.`id` = 22 LIMIT 1
Completed 500 Internal Server Error in 8.3ms

NoMethodError (undefined method `identifier' for "preview":String):
  newrelic_rpm (3.6.9.171) lib/new_relic/agent/instrumentation/rails3/action_controller.rb:157:in     `render_with_newrelic'
  actionpack (3.2.16) lib/action_view/renderer/renderer.rb:36:in `render_template'
  actionpack (3.2.16) lib/action_view/renderer/renderer.rb:17:in `render'
  actionpack (3.2.16) lib/abstract_controller/rendering.rb:110:in `_render_template'
  actionpack (3.2.16) lib/action_controller/metal/streaming.rb:225:in `_render_template'
  actionpack (3.2.16) lib/abstract_controller/rendering.rb:103:in `render_to_body'
  actionpack (3.2.16) lib/action_controller/metal/renderers.rb:28:in `render_to_body'
  actionpack (3.2.16) lib/action_controller/metal/compatibility.rb:50:in `render_to_body'
  actionpack (3.2.16) lib/abstract_controller/rendering.rb:88:in `render'
  actionpack (3.2.16) lib/action_controller/metal/rendering.rb:16:in `render'
  actionpack (3.2.16) lib/action_controller/metal/instrumentation.rb:40:in `block (2 levels) in render'
  activesupport (3.2.16) lib/active_support/core_ext/benchmark.rb:5:in `block in ms'
  /usr/local/rvm/rubies/ruby-2.0.0-p353/lib/ruby/2.0.0/benchmark.rb:296:in `realtime'

...等等。如您所见,单词preview 是控制器操作的名称。

删除 NewRelic 没有帮助。然后错误被替换为:

NoMethodError (undefined method `formats' for "preview":String):

顺便说一句,Ruby 是 2.0.0-p353,Rails 是 3.2.16。 Rails 项目有一些自定义项,例如大多数操作的 OAuth 参数——因为 Rails 应用程序主要是 Node.JS 项目的服务器存储单元。

查看 API 文档(如这里:http://www.rubydoc.info/docs/rails/3.1.1/ActionView/Template)给我的印象是 determine_template Rails 方法(以及第二种情况下未指定的方法,没有 newrelic_rpm)不会返回 ActionView::Template但是String。知道为什么会这样吗?

问题二

在大多数情况下,调用 rake cucumber 实际上会产生这种情况:

stack level too deep (SystemStackError)

之前的开发者确实有这个作为ActiveRecord::Base的扩展:

module ActiveRecord
  class Base
    include Documentable
    ...
    def self.has_url
      include Rails.application.routes.url_helpers

      self.default_url_options = Rails.application.default_url_options

      define_method :url do
        send "#{self.class.to_s.underscore}_url", self
      end
    end
  end
end

当然,这可能是原因。我已经尝试通过ActiveRecord::Concern 方法替换用于扩展 ActiveRecord 的(相当过时的)方法,遗憾的是这没有任何区别。我已经对这个项目进行了相当广泛的 grep,这是其中唯一一个实际包含 Rails 模块的地方,所以现在应该是该错误的唯一候选者,不是吗?

对于这两个问题,我完全不知所措。我在 Rails(大约 15 个项目)方面获得了一些经验,并且从未遇到过这样的一年级问题。

感谢您的关注。任何想法都会受到赞赏,即使我已经尝试过了。

更新 #1

根据要求,将before_filters 粘贴到ApplicationController

  def remove_oauth_params
    params.reject! {|k,_| %w[oauth_token bearer_token access_token].include? k}
  end

  def save_action
    @current_action = params[:action]
  end

  def set_access_header
    if request.path == "/users/sign_out"
      headers["Access-Control-Allow-Credentials"] = "true"
      headers["Access-Control-Allow-Headers"] = %w{Origin Accept Content-Type X-Requested-With X-CSRF-Token X-File-Name X-File-Size Cache-Control}.join(",")
      headers["Access-Control-Allow-Methods"] = "DELETE"
      headers["Access-Control-Allow-Origin"] = "http://#{Settings.app_domain}"
    else
      headers["Access-Control-Allow-Origin"]  = "*"
      headers["Access-Control-Allow-Methods"] = %w{GET POST PUT DELETE PATCH}.join(",")
      headers["Access-Control-Allow-Headers"] = %w{Origin Accept Content-Type X-Requested-With X-CSRF-Token X-File-Name X-File-Size Cache-Control}.join(",")
    end
    head(:ok) if request.request_method == "OPTIONS"
  end

  def set_limit_and_offset
    @limit = params.delete(:limit).to_i
    @limit = default_limit unless (1..default_limit).include?(@limit)
    @offset = params.delete(:offset).to_i
    @offset = 0 unless @offset > 0
  end

至于InvoiceController,该死的基本:

class InvoicesController < ApplicationController
  oauthenticate :interactive => false

  layout "invoice"

  def preview
    @i = Invoice.find(params[:id])
    @origin = params[:origin]
  end
end

更新 #2

$ rake cucumber --trace
** Invoke cucumber (first_time)
** Invoke cucumber:ok (first_time)
** Invoke db:test:prepare (first_time)
** Invoke db:abort_if_pending_migrations (first_time)
** Invoke environment (first_time)
** Execute environment
[deprecated] I18n.enforce_available_locales will default to true in the future. If you really want to skip validation of your locale you can set I18n.enforce_available_locales = false to avoid this message.
** Invoke db:load_config (first_time)
** Execute db:load_config
** Execute db:abort_if_pending_migrations
** Execute db:test:prepare
** Invoke db:test:load (first_time)
** Invoke db:test:purge (first_time)
** Invoke environment 
** Invoke db:load_config 
** Execute db:test:purge
** Invoke environment 
** Execute db:test:load
** Invoke db:test:load_schema (first_time)
** Invoke db:test:purge 
** Execute db:test:load_schema
** Invoke db:schema:load (first_time)
** Invoke environment 
** Invoke db:load_config 
** Execute db:schema:load
** Invoke db:seed (first_time)
** Execute db:seed
** Invoke db:abort_if_pending_migrations 
building users for ce and ce-int brands
building example CE app owner
building CE beta app
** Execute cucumber:ok
/usr/local/rvm/rubies/ruby-2.0.0-p353/bin/ruby -S bundle exec cucumber  --profile default
Using the default profile...
[deprecated] I18n.enforce_available_locales will default to true in the future. If you really want to skip validation of your locale you can set I18n.enforce_available_locales = false to avoid this message.
Feature: Creation of robinson entries
  Create a robinson entry to prevent recipients from being mailed

  Background:                                                          # features/blocked_address/blocked_address_creation.feature:4
    Given there is the example customer with id 200                    # features/step_definitions/data_steps.rb:41
      stack level too deep (SystemStackError)
      features/blocked_address/blocked_address_creation.feature:5:in `Given there is the example customer with id 200'

【问题讨论】:

  • 至于问题#1,你能告诉我们控制器动作以及任何可能运行的before_filters(即ApplicationController)吗? InvoicesController 中混入了什么特别的东西?至于#2,你能用rake cucumber --trace 得到一个堆栈跟踪吗?不太可能,我知道。
  • 完成。希望对您有所帮助。
  • 对于递归问题,当然,是有道理的。您是否尝试过将其完全删除并使用静态字符串将其存根?
  • 预览模板中是否有渲染调用?

标签: ruby-on-rails ruby ruby-on-rails-3 activerecord ruby-on-rails-3.2


【解决方案1】:

编辑:请参阅dimitkos comment 了解解决方案。

恐怕我没有简单的答案,只有两个调试建议:

对于问题 1:

  • 包括 Ruby 调试器
  • 在控制台中,调用app.get "/me/invoices/22/preview?oauth_token=XE03DFCGyCDYjdu8oWuU9zrN56Z6Wr7eZDuIjCbT&amp;origin=http://localhost:9294"
  • 在堆栈中查找字符串的来源

至于2:你用git做源码控制吗?

如果是,那么现在是试用git bisect 的绝佳时机。你会找到一个较旧的提交,rake cucumber 仍在工作,而 git 可以帮助你找到破坏它的提交,只需几个步骤。更多内容:Debugging with Git - Binary search

【讨论】:

  • 我们正在取得进展。无论有没有调试器,通过app.get 调用操作都不会导致错误。因此,罪魁祸首可能是通常从新选项卡调用此操作这一事实。至于调试,我马上开始尝试使用binding.pry。我一定会让社区知道我最终如何解决这个问题。
  • 我们正在使用 SimpleDelegator 扩展来帮助我们通过网络(在本例中通过 JSON)序列化模型。我终于发现我已经包含了ActionView::Helpers(能够使用number_with_precision)。我删除了包含的模块并将调用替换为ActionView::Base.new.number_with_precision(...)。就那么简单。我很快就会回答我自己的问题(ish)。
  • 实际上,我会接受您的回答并附上一些注释(请更新您的回答,并注明答案在 cmets 中):感谢@janfoeh 为我指明了正确的方向。经过数十次 GIT 检查和一些平分,我确定了原因是我将 ActionView::Helpers 包含在派生自 SimpleDelegator 的类中——我们使用这些将我们的模型序列化为 JSON。因此,我没有包含模块,而是使用了相同的功能:ActionView::Base.new.number_with_precision(...)。谢谢。 =)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多