【问题标题】:YAML - TypeError: can't dump anonymous moduleYAML - TypeError:无法转储匿名模块
【发布时间】:2013-03-25 06:47:20
【问题描述】:

在 application_controller 的一个动作中,如果我们尝试:

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'

我的问题是:如何将request.env 序列化为 yaml?

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

【问题讨论】:

  • 你测试过我的答案吗?有什么相关的吗?

标签: ruby-on-rails yaml delayed-job


【解决方案1】:

physic 的代码听起来有点奇怪,但是

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

可能会奏效。酷,哼?

【讨论】:

    【解决方案2】:

    问题是,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 哈希,以免意外修改它。然后我们从副本中删除这些键,这会导致错误。

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

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

    最后tmp_env["action_dispatch.remote_ip"] 看起来像一个字符串(当检查它时),但它是一个ActionDispatch::RemoteIp::GetIp 实例。它包含对原始env 哈希的另一个引用。我们将其转换为字符串,因为我不知道您以后是否对那个键感兴趣。

    此外,您可以删除更多键以减小 yaml 输出的大小。但是,这应该可以在不抛出您遇到的错误的情况下工作。更精简的解决方案是从一个空的 Hash 开始,并且只复制您在 yaml 输出中真正需要的键。

    用 ruby​​ 1.9.3 和 rails 3.2.13 测试

    【讨论】:

      【解决方案3】:

      这是我根据 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
      

      【讨论】:

      • 看起来不错,尤其是您在帮助程序中整理了一些东西。
      猜你喜欢
      • 1970-01-01
      • 2013-09-02
      • 2016-10-12
      • 2017-02-22
      • 2012-05-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多