gpt4 book ai didi

ruby-on-rails - ActiveSupport 如何计算月总和?

转载 作者:数据小太阳 更新时间:2023-10-29 07:48:35 25 4
gpt4 key购买 nike

我很高兴也很惊讶地发现 ActiveSupport 以我想要的方式计算月数。无论所讨论的月份有多少天,添加 1.month到特定的 Time将使您与 Time 在同一天.

> Time.utc(2012,2,1)
=> Wed Feb 01 00:00:00 UTC 2012
> Time.utc(2012,2,1) + 1.month
=> Thu Mar 01 00:00:00 UTC 2012
months Fixnum 中的方法由 activesupport 提供的没有提供线索:
def months
ActiveSupport::Duration.new(self * 30.days, [[:months, self]])
end

关注 + Time 中的方法...
def plus_with_duration(other) #:nodoc:
if ActiveSupport::Duration === other
other.since(self)
else
plus_without_duration(other)
end
end

...引导我们到 sinceFixnum ...
def since(time = ::Time.current)
time + self
end

......这使我们无处可去。

ActiveSupport(或其他东西)如何/在哪里进行巧妙的月份数学计算,而不是仅仅增加 30 天?

最佳答案

这真是个好问题。简短的回答是 1.monthActiveSupport::Duration对象(正如您已经看到的)及其身份以两种不同的方式定义:

  • 30.days (以防万一您需要/尝试将其转换为秒数)和
  • 作为 1 个月(如果您尝试将此持续时间添加到日期)。

  • 通过查看它的 parts可以看到它仍然知道它相当于1个月。方法:
    main > 1.month.parts
    => [[:months, 1]]

    一旦你看到它仍然知道现在正好是 1 个月的证据,像 Time.utc(2012,2,1) + 1.month 这样的计算方式就不那么神秘了。即使对于没有恰好 29 天的月份,也可以给出正确的结果,以及为什么它给出的结果与 Time.utc(2012,2,1) + 30.days 不同给。

    怎么办ActiveSupport::Duration隐瞒真实身份?

    对我来说,真正的谜团是它如何如此好地隐藏其真实身份。我们知道它是一个 ActiveSupport::Duration对象,但很难让它承认它是!

    当您在控制台中检查它时(我正在使用 Pry),它看起来完全像(并且声称是)一个普通的 Fixnum 对象:
    main > one_month = 1.month
    => 2592000

    main > one_month.class
    => Fixnum

    它甚至声称相当于 30.days (或 2592000.seconds ),我们已经证明这是不正确的(至少不是在所有情况下):
    main > one_month = 1.month
    => 2592000

    main > thirty_days = 30.days
    => 2592000

    main > one_month == thirty_days
    => true

    main > one_month == 2592000
    => true

    所以要找出一个对象是否是 ActiveSupport::Duration与否,您不能依赖 class方法。相反,您必须直截了本地问它:“您是还是不是 ActiveSupport::Duration 的实例?”面对如此直接的质问,被质问的对象只好坦白:
    main > one_month.is_a? ActiveSupport::Duration
    => true

    另一方面,单纯的 Fixnum 对象必须低下头并承认它们不是:
    main > 2592000.is_a? ActiveSupport::Duration
    => false

    您还可以通过检查它是否响应 :parts 来将其与常规 Fixnum 区分开来。 :
    main > one_month.parts
    => [[:months, 1]]

    main > 2592000.parts
    NoMethodError: undefined method `parts' for 2592000:Fixnum
    from (pry):60:in `__pry__'

    拥有一系列零件很棒

    拥有一组部件的好处在于,它允许您将持续时间定义为单位的混合,如下所示:
    main > (one_month + 5.days).parts
    => [[:months, 1], [:days, 5]]

    这使其能够准确计算以下内容:
    main > Time.utc(2012,2,1) + (one_month + 5.days)
    => 2012-03-06 00:00:00 UTC

    ...如果它只是将天数或秒数作为其值存储,则无法正确计算。如果我们先转换 1.month,你可以亲眼看到这个到它的“等效”秒数或天数:
    main > Time.utc(2012,2,1) + (one_month + 5.days).to_i
    => 2012-03-07 00:00:00 UTC

    main > Time.utc(2012,2,1) + (30.days + 5.days)
    => 2012-03-07 00:00:00 UTC

    怎么样ActiveSupport::Duration工作? (血腥的实现细节)
    ActiveSupport::Duration实际上(在 gems/activesupport-3.2.13/lib/active_support/duration.rb 中)定义为 BasicObject 的子类,根据 docs ,“可用于创建独立于 Ruby 对象层次结构的对象层次结构、代理对象(如 Delegator 类)或其他必须避免来自 Ruby 方法和类的命名空间污染的用途。”
    ActiveSupport::Duration用途 method_missing将方法委托(delegate)给它的 @value多变的。

    奖金问题 : 有谁知道为什么会出现 ActiveSupport::Duration对象声称不响应 :parts即使它确实如此,为什么parts 方法没有列在方法列表中?
    main > 1.month.respond_to? :parts
    => false

    main > 1.month.methods.include? :parts
    => false

    main > 1.month.methods.include? :since
    => true

    答案 : 因为 BasicObject没有定义 respond_to?方法,发送 respond_to?ActiveSupport::Duration对象最终会调用它的 method_missing方法,看起来像这样:
    def method_missing(method, *args, &block) #:nodoc:
    value.send(method, *args, &block)
    end
    1.month.value只是 Fixnum 2592000 ,所以它实际上最终调用了 2592000.respond_to? :parts ,当然是 false .

    不过,这很容易解决,只需添加 respond_to?方法到 ActiveSupport::Duration类(class):
    main > ActiveSupport::Duration.class_eval do
    def respond_to?(name, include_private = false)
    [:value, :parts].include?(name) or
    value.respond_to?(name, include_private) or
    super
    end
    end
    => nil

    main > 1.month.respond_to? :parts
    => true

    原因的解释 methods错误地省略了 :parts方法是一样的:因为 methods消息只是被委托(delegate)给值,当然它没有 parts方法。我们可以像添加我们自己的 methods 一样轻松地修复这个错误。方法:
    main > ActiveSupport::Duration.class_eval do
    def methods(*args)
    [:value, :parts] | super
    end
    end
    => nil

    main > 1.month.methods.include? :parts
    => true

    关于ruby-on-rails - ActiveSupport 如何计算月总和?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9883067/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com