2013-04-03 4 views
5

In einer Aktion von application_controller Dump können, wenn wir versuchen:YAML - Typeerror: nicht anonym Modul

p request.env.to_yaml 

ich diesen Fehler bekam werden:

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' 

Meine Frage ist: Wie kann ich serialisieren request.env zu Yaml?

Eigentlich sollte ich request.env zu delayed_job übergeben und E-Mail senden, und ich habe diesen Fehler bekommen, weil delayed_job Objekt in DB serialisieren muss.

+0

Haben Sie meine Antwort getestet? Hast du irgendwelche Kommentare dazu? – fotanus

Antwort

0

durch den Code von physic, vielleicht ein wenig seltsam klingen, aber

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

funktionieren könnte. Cool, summ?

3

Das Problem ist, dass der request.env Hash eine Menge verschachtelter Objekte (und insbesondere Module) hat, die nicht in Yaml konvertiert werden können. Der Trick besteht darin, diejenigen Teile des Hash zu löschen, die nicht konvertiert werden können.

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 

Wir klonen zuerst die ursprünglichen env Hash, sie nicht versehentlich ändern. Dann löschen wir diese Schlüssel aus unserer Kopie, die Fehler verursachen.

tmp_env["action_dispatch.routes"] enthält einen Verweis auf ein unbenanntes Modul innerhalb eines ActionDispatch::Routing::RouteSet Objekts, das die Ursache für Ihren Fehler ist. Wir löschen es besser.

tmp_env["action_controller.instance"] enthält einen Verweis auf das Original env -hash (die wir nicht konvertieren können). Lösche es.

Und schließlich tmp_env["action_dispatch.remote_ip"] sieht aus wie eine Zeichenfolge (bei der Überprüfung), aber es ist eine ActionDispatch::RemoteIp::GetIp Instanz. Es enthält einen weiteren Verweis auf den ursprünglichen env Hash. Wir konvertieren es in eine Zeichenfolge, weil ich nicht weiß, ob Sie später an diesem Schlüssel interessiert sind.

Zusätzlich können Sie viele weitere Schlüssel entfernen, um die Größe Ihrer yaml-Ausgabe zu reduzieren. Dies sollte jedoch funktionieren, ohne den Fehler zu verursachen, den Sie erfahren haben. Eine schlankere Lösung wäre, mit einem leeren Hash zu beginnen und nur die Schlüssel zu kopieren, die Sie wirklich in Ihrer Yaml-Ausgabe benötigen.

mit Ruby 1.9.3 getestet und Schienen 3.2.13

2

Hier ist, was ich mit Tessi Vorbild kam, basiert auf:

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 
+0

Sieht gut aus, vor allem, dass Sie Dinge in einem Helfer organisiert haben. – tessi