将字符串转换为ActiveSupport :: Duration

时间:2015-12-29 07:07:49

标签: ruby-on-rails ruby

如何将字符串转换为ActiveSupport :: Duration?

在rails控制台中,此代码可以正常工作

Date.today + 1.month (or 22.days)

但这不起作用

Date.today + '1.month' 

它说 TypeError:预期数字

' 1.month'来自db记录。

6 个答案:

答案 0 :(得分:4)

是的,eval可以解决问题。但是你应该确保数据库中的代码始终是安全的。如果'22 .days','1。month'等来自用户输入,使用eval是一个巨大的安全漏洞。

在这种情况下,尝试使用某种自然语言解析器,例如https://github.com/hpoydar/chronic_duration

答案 1 :(得分:3)

正如其他人所指出的那样,在字符串上使用 eval 会产生安全漏洞。

相反,您可以将字符串转换为 ActiveSupport::Duration在字符串的第一部分使用.to_i将其转换为整数,然后将.send字符串的第二部分转换为整数,将其转换为持续时间。像这样:

parts = '1.month'.split('.')
parts.first.to_i.send(parts.last)

为方便起见,您可以通过添加此文件来扩展String类config / initializers / string.rb:

# Helper extension to String class
class String
  # Convert strings like "3.days" into Duration objects (via Integer)
  def to_duration
    super unless self =~ /\A\d+\.\w+\z/
    parts = split('.')
    parts.first.to_i.send(parts.last)
  end
end

然后在您的应用中,您可以在字符串上调用.to_duration

答案 2 :(得分:1)

在Rails中month'months'是整数方法。

所以当你使用:

1.month

您正在将Integer#month应用于1 (这是一个整数)

但是'1.month'只是一个字符串。你可以在引号之间写任何东西,它们被视为一个字符串,除非你特别要求,否则不会以任何方式进行评估。

eval('1.month')是一个这样的实例,您可以专门要求对您的String进行评估,从而获得所需的结果。但是,这可能很危险,因为您没有检查输入字符串。

You can find a lot of references as to why eval is nasty.

如果您这样做是因为您的输入是String而不是Integer,您始终可以使用to_i将其转换为Integer。例如:

time = "1"
time.to_i.month 
#=> 2592000

即使对于整数值也是如此:

time = 1
time.to_i.month 
=> 2592000

答案 3 :(得分:0)

如果您有这样的代码Date.today+30可能导致任何人在您的服务器上执行他们想要的任何代码),它会做什么?最后它与JSON.parse(some_string_from_db).inject(Date.today) do |date, inc| date+inc.last.send(inc.first.to_s) end 大致相同(即今天的日期增加1.个月。)

所以你可以通过存储或者只存储日期来摆脱eval。

但这取决于你的应用程序,日期对象加1.month实际上将日期移出正好1个月(所以如果你从2016年1月29日开始并添加3个月你将获得2016年3月29日不是90天离开

因此,假设您在该数据库中的字符串始终是1.month或2.months或7.months(即仅仅几个月),那么在这种情况下,您需要存储在DB中的是一个整数值(或将它存储为类似“7”的字符串,然后执行“7”.to_i)

但也许你想要允许几个月,几天,几周和几年......你可以这样做:{月:7,天:3,周:1,年:2} .to_json并存储在你的db,然后使用它你会说:

JSON.parse(some_string_from_db).inject(Date.today) do |date, inc|
  if [:years, :months, :weeks, :days].include? inc.first.to_s
    date+inc.last.send(inc.first.to_s) 
  else
    Raise "don't try it buddy"
  end
end

由于您允许评估任意对象和方法,因此这只比eval稍微好一些,但您可以添加一些这样的保护:

{{1}}

现在你回来控制你正在执行的东西!

当然这一切都取决于你的申请,但希望这能回答你的问题。

答案 4 :(得分:0)

如果可以选择字符串格式,则应使用ActiveSupport::Duration.parse,使用ISO8601持续时间格式:https://api.rubyonrails.org/classes/ActiveSupport/Duration.html#method-c-parse

示例:

ActiveSupport::Duration.parse('P1M')
=> 1 month

答案 5 :(得分:-1)

Ohhooo我自己得到了答案 需要像这样添加eval

Date.today + eval('1.month')

完美无缺

还有其他方法吗?使用这种方式有什么利弊?

这可能导致远程代码执行漏洞,例如随机的人在你的服务器上做任何他们想做的事