Ruby on rails YAML-类型错误:can';t转储匿名模块

Ruby on rails YAML-类型错误:can';t转储匿名模块,ruby-on-rails,yaml,delayed-job,Ruby On Rails,Yaml,Delayed Job,在应用程序控制器的操作中,如果我们尝试: p request.env.to_yaml 我将得到以下错误: TypeError: can't dump anonymous module: #<Module:0x007fee26e34ad8> from /Users/twer/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:267:in `visit_Module' f

在应用程序控制器的操作中,如果我们尝试:

p request.env.to_yaml
我将得到以下错误:

    TypeError: can't dump anonymous module: #<Module:0x007fee26e34ad8>
    from /Users/twer/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:267:in `visit_Module'
    from /Users/twer/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:102:in `accept'
    from /Users/twer/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:447:in `block in dump_ivars'
    from /Users/twer/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:445:in `each'
    from /Users/twer/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:445:in `dump_ivars'
    from /Users/twer/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:124:in `visit_Object'
    from /Users/twer/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:102:in `accept'
    from /Users/twer/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:447:in `block in dump_ivars'
    from /Users/twer/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:445:in `each'
    from /Users/twer/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:445:in `dump_ivars'
    from /Users/twer/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:124:in `visit_Object'
    from /Users/twer/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:102:in `accept'
    from /Users/twer/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:292:in `block in visit_Hash'
    from /Users/twer/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:290:in `each'
    from /Users/twer/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:290:in `visit_Hash'
    from /Users/twer/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:102:in `accept'
TypeError:无法转储匿名模块:#
from/Users/twer/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:267:in'visit_Module'
from/Users/twer/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:102:in“接受”
from/Users/twer/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:447:in'block in dump_ivars'
from/Users/twer/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:445:in'each'
from/Users/twer/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:445:in'dump_ivars'
from/Users/twer/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:124:in'visit_Object'
from/Users/twer/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:102:in“接受”
from/Users/twer/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:447:in'block in dump_ivars'
from/Users/twer/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:445:in'each'
from/Users/twer/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:445:in'dump_ivars'
from/Users/twer/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:124:in'visit_Object'
from/Users/twer/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:102:in“接受”
from/Users/twer/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:292:in`block in visit_Hash'
from/Users/twer/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:290:in'each'
from/Users/twer/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:290:in'visit_Hash'
from/Users/twer/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/1.9.1/psych/visitors/yaml_tree.rb:102:in“接受”
我的问题是:如何将
request.env
序列化到yaml

实际上,我应该将request.env传递给delayed_job并发送电子邮件,我得到了这个错误,因为delayed_job需要将对象序列化到DB中。

根据中的代码,可能听起来有点奇怪,但是

request.env.instance_eval "def name; 'some_name'; end"

可能有用。酷,哼?

问题是,request.env散列有很多嵌套对象(尤其是模块),它们无法转换为yaml。诀窍是删除散列中那些无法转换的部分

tmp_env = request.env.clone
tmp_env.delete "action_dispatch.routes"
tmp_env.delete "action_controller.instance"
tmp_env["action_dispatch.remote_ip"] = tmp_env["action_dispatch.remote_ip"].to_s 
p tmp_env.to_yaml # now it works
我们首先克隆原始的
env
hash,以避免意外修改它。然后我们从副本中删除这些键,这会导致错误

tmp_env[“action_dispatch.routes”]
包含对
ActionDispatch::Routing::RouteSet
对象中未命名模块的引用,这是导致错误的原因。我们最好删除它

tmp_env[“action_controller.instance”]
包含对原始
env
-hash的引用(我们无法转换)。删除它

最后,
tmp_env[“action\u dispatch.remote\u ip”]
看起来像一个字符串(在检查它时),但它是一个
ActionDispatch::RemoteIp::GetIp
实例。它包含对原始
env
hash的另一个引用。我们将其转换为字符串,因为我不知道您以后是否对该键感兴趣

此外,您可以删除更多的键以减小yaml输出的大小。但是,这应该不会抛出您所经历的错误。一个更精简的解决方案是从一个空哈希开始,只复制yaml输出中真正需要的键


使用ruby 1.9.3和rails 3.2.13进行测试,以下是我根据tessi的示例得出的结论:

module RequestSerializationHelper
  ::SerializableRequest = Struct.new(
    :env,
    :filtered_parameters,
    :fullpath,
    :headers,
    :request_method,
    :remote_ip
  )

  ## From http://stackoverflow.com/questions/7604153/rails-2-3-14-how-to-serialise-an-actioncontrollerrequest-object
  ## with additional modifications

  # build a serializable Struct that out of the given request object, which looks like a real request
  def make_request_serializable(request)
    serializable_request = ::SerializableRequest.new
    serializable_request.env = request.env.clone
    serializable_request.filtered_parameters = request.filtered_parameters.clone if request.respond_to? :filtered_parameters
    serializable_request.fullpath = request.fullpath
    serializable_request.headers = request.respond_to?(:headers) ? request.headers.clone : {}
    serializable_request.request_method = request.request_method

    delete_identified_unserializable_values(serializable_request.env)
    delete_identified_unserializable_values(serializable_request.headers)

    # Some jobs want this, so set it after it's been converted to a string in the env
    serializable_request.remote_ip = serializable_request.env["action_dispatch.remote_ip"]

    # automatically delete anything left that's non-serializable.  If we end up deleting
    # too much and breaking something, here's where to debug it based on info in warning
    delete_unidentified_unserializable_values :env, serializable_request.env
    delete_unidentified_unserializable_values :headers, serializable_request.headers

    serializable_request
  end

  def delete_identified_unserializable_values(hash)
    hash.delete "async.callback"
    hash.delete "action_dispatch.backtrace_cleaner"
    hash.delete "action_dispatch.cookies"
    hash.delete "action_dispatch.request.accepts"
    hash.delete "action_dispatch.routes"
    hash.delete "action_dispatch.logger"
    hash.delete "action_controller.instance"
    hash.delete "rack.input"
    hash.delete "rack.errors"
    hash.delete "rack.session"
    hash.delete "rack.session.options"
    hash["action_dispatch.remote_ip"] = hash["action_dispatch.remote_ip"].to_s
    hash.delete "warden"
    hash.delete_if { |key, _| key =~ /^rack-cache/ }
  end

  private

  def delete_unidentified_unserializable_values(hash_name, hash)
    hash.each do |key, value|
      begin
        serialized = value.to_yaml
        YAML.load(serialized)
      rescue => e
        warning = "RequestSerializationHelper: Automatically removing un(re)serializable entry in " +
          "'#{hash_name}' for key '#{key}' and value '#{value}'.  Exception was: '#{e}'"
        Rails.logger.warn(warning)
        hash.delete key
      end
    end
  end
end

你测试过我的答案了吗?对此有何评论?看起来不错,尤其是你在助手中组织了一些事情。