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.
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
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With