MonkeyPatching ActiveJobs

泄露秘密 提交于 2019-12-09 18:58:19

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!