2013-04-03 21 views
5

W działaniu application_controller, jeśli spróbujemy:YAML - TypeError: nie można zrzucić moduł anonimowy

p request.env.to_yaml 

będzie mam ten błąd:

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' 

Moje pytanie brzmi: jak mogę serializować request.env do yaml?

Właściwie to miałem przekazać request.env do delayed_job i wysłać e-mail, i mam ten błąd, ponieważ delayed_job potrzeba serializacji obiektu do DB.

+0

Czy testowany moją odpowiedź? masz jakieś uwagi na ten temat? – fotanus

Odpowiedz

0

przez kod z physic, może wydawać się trochę dziwne, ale

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

może działać. Fajnie, szum?

3

Problem polega na tym, że hash request.env ma wiele zagnieżdżonych obiektów (a zwłaszcza modułów), których nie można przekonwertować na yaml. Sztuką jest usunięcie tych części hasha, których nie można przekonwertować.

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 

Najpierw sklonować oryginalnego env hash, aby nie przypadkowo go zmodyfikować. Następnie usuwamy te klucze z naszej kopii, które powodują błędy.

tmp_env["action_dispatch.routes"] zawiera odniesienie do nienazwanego modułu w obiekcie ActionDispatch::Routing::RouteSet, co jest przyczyną Twojego błędu. Lepiej go usuńmy.

tmp_env["action_controller.instance"] zawiera odniesienie do oryginalnego env -hash (którego nie możemy przekonwertować). Usuń to.

I wreszcie tmp_env["action_dispatch.remote_ip"] wygląda jak ciąg znaków (podczas sprawdzania), ale jest to instancja ActionDispatch::RemoteIp::GetIp. Zawiera inne odniesienie do pierwotnego skrótu env. Konwertujemy go na ciąg znaków, ponieważ nie wiem, czy interesuje Cię ten klucz później.

Dodatkowo można usunąć o wiele więcej klawiszy, aby zmniejszyć rozmiar wyjściowego sygnału yaml. Powinno to jednak działać bez rzucania błędu, którego doświadczyłeś. Szczuplejszym rozwiązaniem byłoby rozpoczynanie z pustym hasłem i kopiowanie tylko tych kluczy, których naprawdę potrzebujesz w swoim wyjściu yaml.

Testowane z ruby ​​1.9.3 i szyn 3.2.13

2

Oto co wymyśliłem, w oparciu o przykład Tessi za:

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

Wygląda dobrze, zwłaszcza że zorganizowałeś rzeczy w pomocniku. – tessi

Powiązane problemy