如何将字符串转换为ActiveSupport :: Duration?
在rails控制台中,此代码可以正常工作
Date.today + 1.month (or 22.days)
但这不起作用
Date.today + '1.month'
它说 TypeError:预期数字
' 1.month'来自db记录。
答案 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)
所以当你使用:
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')
完美无缺
还有其他方法吗?使用这种方式有什么利弊?
这可能导致远程代码执行漏洞,例如随机的人在你的服务器上做任何他们想做的事