2013-04-03 22 views
5

Trong một hành động của application_controller, nếu chúng ta thử:YAML - Lỗi Loại: không thể đổ nặc danh mô-đun

p request.env.to_yaml 

tôi sẽ nhận lỗi này:

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' 

Câu hỏi của tôi là: làm thế nào tôi có thể serialize request.env để yaml?

Thực ra, tôi được yêu cầu chuyển request.env đến delay_job và gửi email, và tôi gặp lỗi này vì delay_job cần serializing đối tượng vào DB.

+0

bạn đã kiểm tra câu trả lời của tôi không? có bất kỳ ý kiến ​​về nó? – fotanus

Trả lời

0

By mã từ physic, có vẻ hơi kỳ quặc, nhưng

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

có thể làm việc. Cool, hum?

3

Vấn đề là, băm request.env có nhiều đối tượng lồng nhau (và đặc biệt là các mô-đun), không thể chuyển đổi thành yaml. Bí quyết là xóa các phần của băm, không thể chuyển đổi được.

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 

Trước tiên, chúng tôi sao chép bản gốc env băm, để không vô tình sửa đổi nó. Sau đó, chúng tôi xóa các khóa đó khỏi bản sao của chúng tôi, gây ra lỗi.

tmp_env["action_dispatch.routes"] chứa tham chiếu đến mô-đun chưa đặt tên trong đối tượng ActionDispatch::Routing::RouteSet, là nguyên nhân gây ra lỗi của bạn. Chúng ta nên xóa nó đi.

tmp_env["action_controller.instance"] chứa tham chiếu đến env -ash ban đầu (mà chúng tôi không thể chuyển đổi). Xóa đi.

Và cuối cùng là tmp_env["action_dispatch.remote_ip"] trông giống như một chuỗi (khi kiểm tra nó), nhưng nó là một phiên bản ActionDispatch::RemoteIp::GetIp. Nó chứa một tham chiếu khác với mã băm env gốc. Chúng tôi chuyển đổi nó thành một chuỗi, bởi vì tôi không biết nếu bạn quan tâm đến chìa khóa đó sau này.

Ngoài ra, bạn có thể xóa nhiều khóa khác để giảm kích thước đầu ra yaml của mình. Tuy nhiên, điều này sẽ làm việc mà không cần ném lỗi bạn gặp phải. Một giải pháp gọn gàng hơn là bắt đầu với một Hash rỗng và chỉ sao chép các khóa bạn thực sự cần trong đầu ra yaml của bạn.

Tested với ruby ​​1.9.3 và 3.2.13 ray

2

Đây là những gì tôi đã đưa ra, dựa trên ví dụ tessi của:

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

Có vẻ tốt, đặc biệt là bạn đã tổ chức mọi thứ trong một người trợ giúp. – tessi