我最近申请了一个Ruby工作,并被要求编写一个方法来编写一个名为" query_to_hash"的Ruby函数。它将单个String参数作为HTTP查询字符串(例如foo=bar&abc=1%202%203
)并返回名称/值对的哈希,如下所示:
{"foo" => "bar", "abc" => "1 2 3"}
我提供了附带的代码示例,并得到了一些反馈,表明我的Ruby风格不是他们想要的。
我现在很想知道开发人员会在附件中看到哪些风格问题,并希望得到建设性的反馈。
require 'rubygems'
require 'uri'
require 'rack'
include Rack::Utils
$SAFE = 3
# HTTP Query string (from wikipedia)
#field1=value1&field2=value2&field3=value3...
# The query string is composed of a series of field-value pairs.
# Within each pair, the field name and value are separated by an equals sign. The equals sign may be omitted if the value is an empty string.
# The series of pairs is separated by the ampersand, '&' (or semicolon, ';' for URLs embedded in HTML and not generated by a <form>...</form>; see below).
# While there is no definitive standard, most web frameworks allow multiple values to be associated with a single field[3][4]:
# field1=value1&field1=value2&field1=value3...
def query_to_hash(qry, sep = '&')
# assume input string conforms to spec (no validation)
# assume only & or ; is used - not both
# return a null string if value is not defined
# return null hash if query is null string
# return array of values in hash if field has multiple values
#@qry = qry.gsub(/%20/, " ")
@qry = URI.unescape(qry)
rtn = Hash.new {|h,k| h[k]=[]}
if @qry == "" then
# return an empty hash
#
return {}
else
qry_a = @qry.split(sep)
end
qry_a.each do |fv_pair|
pair = fv_pair.split('=')
# append multiple values if needed and ensure that null values are accommodated
#
rtn[pair[0]] << pair[1] ||= ""
end
# collapse array if it contains only one item
#
rtn.each{|k,v| rtn[k] = *v if v.length == 1}
end
puts "Using 'query_to_hash' method:"
puts
test_values = %w[foo=bar&abc=1%202%203
foo&abc=1%202%203
foo=&abc=1%202%203
foo=bar&foo=boo&abc=1%202%203
]
test_values.each { |v| puts "#{sprintf("%30s",v)} is returned as #{query_to_hash(v).inspect}" }
test_values = %w[ foo=bar;foo=boo;abc=1%202%203
foo=bar;foo=boo;abc=1%0A2%203
foo=bar;foo=boo;abc=1%092%0D3
]
test_values.each { |v| puts "#{sprintf("%30s",v)} is returned as #{query_to_hash(v, ';').inspect}" }
puts "#{sprintf("%30s", "null string")} is returned as #{query_to_hash("").inspect}"
# compare with Rack::Utils::parse_query
#
puts
puts "Using 'Rack::Utils::parse_query' method:"
puts
test_values = %w[foo=bar&abc=1%202%203
foo&abc=1%202%203
foo=&abc=1%202%203
foo=bar&foo=boo&abc=1%202%203
]
test_values.each { |v| puts "#{sprintf("%30s",v)} is returned as #{parse_query(v).inspect}" }
test_values = %w[ foo=bar;foo=boo;abc=1%202%203
foo=bar;foo=boo;abc=1%0A2%203
foo=bar;foo=boo;abc=1%092%0D3
]
test_values.each { |v| puts "#{sprintf("%30s",v)} is returned as #{parse_query(v, ';').inspect}" }
puts "#{sprintf("%30s", "null string")} is returned as #{parse_query("").inspect}"
答案 0 :(得分:1)
我看不到任何尖叫声“这对我来说是错误的”。您的代码经过充分评论且非常清晰,您已经掌握了核心语言结构和基本API。
有具体的理由还是他们只是使用了一个“我们不喜欢你的风格”?
免责声明:我是一个转向ruby的java dev,所以我可能没有得到正确的ruby'风格'的最佳心理形象,但我可以说我们的系统中的代码差得多。< / p>
答案 1 :(得分:1)
只有两件事真的跳了出来。
qry_a = @qry.split(sep)
上面一行中的变量是本地变量,只在else
子句中出现,但是稍后再次引用它。
@qry = URI.unescape(qry)
除非你有一个对象,否则不需要使用实例变量,我建议这是一个问题,因为它会立即打开变量的范围而不是方法。我个人尝试尽可能多地使用当地人。
除此之外,对我来说似乎很好。也许你可以使用像Minitest或RSpec这样的测试框架进行测试,但它看起来很好。
我同意@mcfinnigan的观点,更具体的观点会对你更有帮助。
我会添加(因为我有点仓促),直接使用数组索引也不是那么好的风格,因为它很快就会混乱。例如,这看起来像Perl,即线路噪声:)
rtn[pair[0]] << pair[1] ||= ""
当each
或map
之类的内容可能更好时,您还使用了select
来影响某个值,因为它们明确地影响了价值而不是作为副作用。清晰明确是Ruby(IMO)的好风格。
还有一个想法......(我的咖啡开始了:)由于这是一次面试,也许他们正在寻找超出此范围的东西。例如,我第一次看到规范是“为什么将字符串作为参数传递?”你可能做得更好:
class String
def query_to_hash( separator="&" )
# more code
然后可以从"foo=bar&abc=1%202%203".query_to_hash
调用它,远更多Rubyish。
甚至比monkeypatch更好,继承(经常被忽视)。
class QueryString < String
def to_hash( separator="&" )
# some code that refers to `self`
然后可以通过以下方式使用:
QueryString.new("foo=bar&abc=1%202%203").to_hash
更清楚地阅读和理解,并且更清楚你想要实现的目标。还有其他方法可以做到这一点,也许这就是将你与人群分开的原因。