提问人:S.Mueller 提问时间:12/13/2022 更新时间:12/13/2022 访问量:80
Monkey Patch 'at_with_coercion' (activesupport) / 处理来自纪元的毫秒
Monkey Patch 'at_with_coercion' (activesupport) / Handle milliseconds from epoch
问:
描述
我们有一个 Ruby 和 Rails 应用程序,带有一个 Web 前端和一个 API,用于支持来自前端外部的 Web 调用。在一个用例中,我们使用 API 来创建和更新工单(资源),方法是调用 Web URL 并将参数作为原始 JSON 数据发送到我们的 API 控制器(具有单独路由的单独控制器)。 工作订单的一个属性是“due_date”,它在模型中定义为 DateTime 类型的字段(通过 Mongoid)。 当 Rails 尝试使用给定的参数在控制器中实例化工单的新对象时,会完成向定义的数据库字段类型的转换。对于 DateTime 字段,这是通过在第 44 行 (https://github.com/rails/rails/blob/v5.2.6/activesupport/lib/active_support/core_ext/time/calculations.rb#L44) 的 rails/activesupport/lib/active_support/core_ext/time/calculations.rb 中调用“at_with_coercion”在内部完成的。 由于 API 是从外部调用的,因此有一个用例,即使用整数设置“due_date”字段,该整数包含从纪元时间 (1970-01-01 01:00:00) 开始的毫秒数。 Rails 只能处理一个整数,其中包含从纪元时间开始的秒数。 因此,我们尝试对内部的“at_with_coercion”方法进行猴子修补,但遇到了一些问题。
重现步骤
型:
include Mongoid::Document
include Mongoid::Timestamps
field :due_date, type: DateTime
控制器:
def create
@work_order = WorkOrder.new(params.require(:work_order).permit(:name, … , :due_date, … ))
@work_order.save
end
猴子补丁
lib/core_extensions/time.rb:
class Time
class << self
remove_method :at_without_coercion
remove_method :at_with_coercion
remove_method :at
# Layers additional behavior on Time.at so that ActiveSupport::TimeWithZone and DateTime
# instances can be used when called with a single argument
def at_with_coercion(*args)
return at_without_coercion(*args) if args.size != 1
# Time.at can be called with a time or numerical value
time_or_number = args.first
if time_or_number.is_a?(ActiveSupport::TimeWithZone) || time_or_number.is_a?(DateTime)
at_without_coercion(time_or_number.to_f).getlocal
elsif time_or_number.is_a?(Integer) && time_or_number.to_s.length == 13
time_or_number /= 1000.0
at_without_coercion(time_or_number)
else
at_without_coercion(time_or_number)
end
end
alias at_without_coercion at
alias at at_with_coercion
end
end
配置/初始值设定项/monkey_patches.rb:
Dir[Rails.root.join('lib', 'core_extensions', '*.rb')].sort.each do |f|
require f
end
API 调用:URL:
{{protocol}}://{{subdomain}}。{{url}}/api/v2/work_orders.json
参数(原始 JSON):{
"work_order":{
"name":"Test workorder",
... ,
"due_date":1669105754783,
...
}
}
预期行为
Rails 应该使用我们的 monkey patched 方法并返回正确格式化的 Time 值。
实际行为
如果我们不删除现有的别名,那么对“at_without_coercion”的调用将导致内部 Rails 方法别名,并且我们会得到一个 StackLevel 太深的异常。 如果我们重写别名,我们最终会在 StackLevel 之后进入一个无穷大循环,因为重写的方法多次调用自己。
系统配置
Rails 版本:5.2.6
Ruby 版本:2.5.9p229
Mongoid gem 版本:7.3.1
选择
如果不可能对内部方法进行猴子修补,是否有可能让 Rails 可以处理来自纪元时间的毫秒数?
答: 暂无答案
评论
config/initializers/
after_initialize