我在模型中编写了一个函数来抓取站点并将某些属性存储在单独的模型中(story
):
def get_content
request = HTTParty.get("#{url}")
doc = Nokogiri::HTML(request.body)
doc.css("#{anchor}")["#{range}"].each do |entry|
story = self.stories.new
story.title = entry.text
story.url = entry[:href]
story.save
end
这使用部分变量的url
、anchor
和range
属性。范围属性存储为数组范围 - 即 0..2
或11..13
- 但是,我被告知它无法将字符串转换为变量。我尝试将range
存储为整数和字符串,但都失败了。
我意识到我可以在我的数据库中输入范围的开头和结尾作为两个单独的整数,然后放["#{beginrange}".."#{endrange}"]
但这似乎是一种混乱的方法。
还有其他想法吗? 提前非常感谢
如果您确定range
始终是像 ' 1..2
' (' <Integer >..<Integer>
' 这样的字符串,您可以使用 eval
方法:
在我的 IRB 控制台中:
1.9.3p0 :032 > (eval "1..2").each { |l| puts l }
1
2
=> 1..2
1.9.3p0 :033 > (eval "1..2").inspect
=> "1..2"
1.9.3p0 :034 > (eval "1..2").class
=> Range
在您的情况下:
doc.css("#{anchor}")[eval(range)].each do |entry|
#...
end
但eval
有点危险。如果您确定 range
属性是字符串形式的范围(验证和正则表达式可以提供帮助),则可以毫无风险地使用 eval
。
有几件事我看错了。
["#{beginrange}".."#{endrange}"]
创建字符范围,而不是整数范围,Array[]
需要:
beginrange = 1
endrange = 2
["#{beginrange}".."#{endrange}"]
=> ["1".."2"]
[beginrange..endrange]
=> [1..2]
但是,您将所需数组范围的表示形式存储为字符串。如果我有一个范围的字符串表示,我会使用这个:
range_value = '1..2'
[Range.new(*range_value.scan(/d+/).map(&:to_i))]
=> [1..2]
或者,如果有机会,我会遇到一个独家范围:
[Range.new(*range_value.scan(/d+/).map(&:to_i), range_value['...'])]
=> [1..2]
range_value = '1...2'
[Range.new(*range_value.scan(/d+/).map(&:to_i), range_value['...'])]
=> [1...2]
当您不能信任 Range 字符串表示形式的来源时,这些都很好,即值来自其他人创建的表单或文件。如果您拥有传入值,或者为方便起见,将其作为字符串存储在数据库中,则可以使用 eval
轻松重新创建范围:
eval('1..2').class
=> Range
eval('1..2')
=> 1..2
eval('1...2')
=> 1...2
人们害怕eval
,因为使用不当,它是危险的。这并不意味着我们应该避免使用它,相反,我们应该在安全的时候使用它。
您可以使用正则表达式来检查字符串的格式,如果不可接受,请引发异常,然后继续:
raise "Invalid range value received" if (!range_value[/Ad+s*.{2,3}s*d+z/])
[eval(range_value)]