Ruby on rails ActiveSupport如何进行月度合计?
我很高兴也很惊讶地发现,ActiveSupport以我希望的方式完成了每月的合计。无论所讨论的月份有多少天,将Ruby on rails ActiveSupport如何进行月度合计?,ruby-on-rails,ruby,activesupport,Ruby On Rails,Ruby,Activesupport,我很高兴也很惊讶地发现,ActiveSupport以我希望的方式完成了每月的合计。无论所讨论的月份有多少天,将1.month添加到特定的时间将使您在该月的同一天与时间 > 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 activesupport提供的Fixnum中的months方法没有提供线
1.month
添加到特定的时间
将使您在该月的同一天与时间
> 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
activesupport提供的Fixnum
中的months
方法没有提供线索:
def months
ActiveSupport::Duration.new(self * 30.days, [[:months, self]])
end
遵循+
时间中的+
方法
def plus_with_duration(other) #:nodoc:
if ActiveSupport::Duration === other
other.since(self)
else
plus_without_duration(other)
end
end
def since(time = ::Time.current)
time + self
end
…将我们引向,因为Fixnum
中的
def plus_with_duration(other) #:nodoc:
if ActiveSupport::Duration === other
other.since(self)
else
plus_without_duration(other)
end
end
def since(time = ::Time.current)
time + self
end
…这让我们一事无成
ActiveSupport(或其他东西)如何/在哪里做聪明的月份计算而不是仅仅增加30天?看起来ActiveSupport的神奇之处在于:
看起来Ruby 1.9+可以处理这个问题,所以这段代码只在Rails与较旧的Ruby版本一起使用时使用。这是一个非常好的问题。简单的回答是,1.month
是一个ActiveSupport::Duration
对象(正如您已经看到的),它的标识以两种不同的方式定义:
- 如
30.天
(如果您需要/尝试将其转换为秒数),以及
- 1个月(如果您试图将此持续时间添加到日期)
通过检查其零件
方法,您可以看到它仍然知道它相当于1个月:
main > 1.month.parts
=> [[:months, 1]]
main > ActiveSupport::Duration.class_eval do
def methods(*args)
[:value, :parts] | super
end
end
=> nil
main > 1.month.methods.include? :parts
=> true
一旦你看到证据表明它仍然知道正好是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.天
(或2592000.秒
),我们已经证明这是不正确的(至少不是在所有情况下):
因此,要确定一个对象是否是ActiveSupport::Duration
,您不能依赖class
方法。相反,你必须直截了当地问:“你是否是ActiveSupport::Duration的实例?”面对这样一个直接的问题,所讨论的对象将别无选择,只能承认事实:
main > one_month.is_a? ActiveSupport::Duration
=> true
另一方面,纯粹的Fixnum对象必须低头承认它们不是:
main > 2592000.is_a? ActiveSupport::Duration
=> false
您还可以通过检查它是否响应:parts
:
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]]
def method_missing(method, *args, &block) #:nodoc:
value.send(method, *args, &block)
end
这使其能够准确计算以下各项:
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
实际上被定义为BasicObject
的一个子类,根据,可用于创建独立于Ruby对象层次结构的对象层次结构、代理对象(如Delegator类)或其他必须避免Ruby方法和类的命名空间污染的用途
ActiveSupport::Duration
使用method\u missing
将方法委托给其@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
Answer:由于BasicObject
没有定义respond\u to?
方法,因此将respond\u to?
发送到ActiveSupport::Duration
对象将调用其方法(缺少方法),如下所示:
main > (one_month + 5.days).parts
=> [[:months, 1], [:days, 5]]
def method_missing(method, *args, &block) #:nodoc:
value.send(method, *args, &block)
end
1.month.value
只是Fixnum2592000
,因此它实际上最终调用了2592000.response\u to?:parts
,这当然是false
不过,只要在ActiveSupport::Duration
类中添加一个respond\u to?
方法,就很容易解决这个问题:
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
为什么方法
错误地忽略了:parts
方法的解释是相同的:因为方法
消息只是被委托给值,而值当然没有parts
方法。我们可以像添加自己的方法一样轻松地修复此错误:
main > 1.month.parts
=> [[:months, 1]]
main > ActiveSupport::Duration.class_eval do
def methods(*args)
[:value, :parts] | super
end
end
=> nil
main > 1.month.methods.include? :parts
=> true
很好!你知道在哪里/什么时候调用advance
吗?如果你能解释一下为什么我在跟踪问题中的代码时没有找到这个,那会很有帮助。另外,你在哪里看到基于ruby版本的代码被选择性地使用?查看链接文件的顶部(第10行)对于Ruby版本的code.good stuff——我仍然不知道advance在哪里用于求和,我只看到它在月前
,周前
等中使用。