问题
I am having an issue monkey-patching part of ActiveJobs. I have the following code in config/initializers/extensions/arguements.rb
module ActiveJob
module Arguments
TYPE_WHITELIST = [ Date, DateTime, Time, NilClass, Fixnum, Float, String, TrueClass, FalseClass, Bignum ]
end
end
Basically, I am trying to add basic support for Date/Time objects for use in the ActiveJob created by ActionMailer#deliver_later
Upon loading the rails app I can see my whitelist is loaded, however when I call the deliver_later method on a mailer the original whitelist overrides my patch.
#List is correct when app loads
2.1.2 :002 > ActiveJob::Arguments::TYPE_WHITELIST
=> [Date, DateTime, Time, NilClass, Fixnum, Float, String, TrueClass, FalseClass, Bignum]
#List is overridden by default list in ActiveJobs after running #deliver_later
2.1.2 :005 > ActiveJob::Arguments::TYPE_WHITELIST
=> [NilClass, Fixnum, Float, String, TrueClass, FalseClass, Bignum]
How can I make my modified whitelist stick? I am pretty sure the error stems from the original ActiveJob::Arguments not loading until deliver_later is called, and therefore loads after my patch and overrides it, though I am not sure how to fix that.
回答1:
Edit: do not use, see https://stackoverflow.com/a/50743819/3293310
What about this ?
module ActiveJob
module Arguments
remove_const(:TYPE_WHITELIST)
TYPE_WHITELIST = [ Date, DateTime, Time, NilClass, Fixnum, Float, String, TrueClass, FalseClass, Bignum ]
end
end
Then, as said in the comments you should extend this module :
module ActionMailer
class DeliveryJob < ActiveJob::Base
extend ActiveJob::Arguments
end
end
A better way if you're using ruby 2+ would be tu use Refinements. Unfortunately, you can't change constants with refinements (read Matz' comment here)
回答2:
The solution given by @Pak can be dangerous !
TL;DR : it breaks with time objects in nested hashes, as during deserialization, they will be returned as string
So in production, you actually want to be able to serialize time to string and deserialize "time strings" as time objects. If you just add Time to the type whitelist, serialization and deserialization might work well enough in regular params, but if you send your time object in a hash argument, it will break, and it will become almost impossible to write a failing test.
Instead, monkeypatch your time classes as described there using GlobalIds, which is the regular way of serializing any param for use with ActiveJob
Basically you can patch your classes this way
class Time
include GlobalID::Identification
alias_method :id, :to_i
def self.find(seconds_since_epoch)
Time.at(seconds_since_epoch.to_i)
end
end
class ActiveSupport::TimeWithZone
include GlobalID::Identification
alias_method :id, :to_i
def self.find(seconds_since_epoch)
Time.zone.at(seconds_since_epoch.to_i)
end
end
As a result, you can properly serialize/deserialize your arguments
arguments = [1, { start: Time.now, end: 1.day.ago }]
serialized_arguments = ActiveJob::Arguments.serialize(arguments)
# => [1,
# {"start"=>{"_aj_globalid"=>"gid://my-job-glasses/Time/1528381306"},
# "end"=>{"_aj_globalid"=>"gid://my-job-glasses/Time/1528294906"},
# "_aj_symbol_keys"=>["start", "end"]}]
deserialized_arguments = ActiveJob::Arguments.deserialize(serialized_arguments)
# => [1, {:start=>"2018-06-07T16:21:46.000+02:00", :end=>"2018-06-06T16:21:46.000+02:00"}]
deserialized_arguments.last[:start].class # => Time
Note that this has for effect of converting AS:TWZ
to plain old Time
but this is better than nothing aye ?
来源:https://stackoverflow.com/questions/27629697/monkeypatching-activejobs