YAML - TypeError: can't dump anonymous module

元气小坏坏 提交于 2019-12-01 17:44:57

问题


In an action of application_controller, if we try:

p request.env.to_yaml

I will got this error:

    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'

My question is: how can I serialize request.env to yaml?

Actually, I was supposed to pass request.env to delayed_job and send out email, and I got this error because delayed_job need serializing object into DB.


回答1:


The problem is, that the request.env hash has a lot of nested objects (and especially modules), which can't be converted to yaml. The trick is to delete those parts of the hash, that can't be converted.

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

We first clone the original env hash, to not accidentally modify it. Then we delete those keys from our copy, which cause errors.

tmp_env["action_dispatch.routes"] contains a reference to an unnamed module within a ActionDispatch::Routing::RouteSet object, which is the cause of your error. We better delete it.

tmp_env["action_controller.instance"] contains a reference to the original env-hash (which we cannot convert). Delete it.

And finally tmp_env["action_dispatch.remote_ip"] looks like a string (when inspecting it), but it is a ActionDispatch::RemoteIp::GetIp instance. It contains another reference to the original env hash. We convert it to a string, because I don't know if your are interested in that key later.

Additionally, you may remove many more keys to reduce the size of your yaml output. However, this should work without throwing the error you experienced. A leaner solution would be to start with an empty Hash and only copy the keys you really need in your yaml output.

Tested with ruby 1.9.3 and rails 3.2.13




回答2:


Here's what I came up with, based on tessi's example:

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



回答3:


By the code from physic, might sound a little odd, but

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

might work. Cool, hum?



来源:https://stackoverflow.com/questions/15790124/yaml-typeerror-cant-dump-anonymous-module

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!