您可以在此代码段中找到哪些Ruby“样式”问题?

时间:2013-02-04 12:03:42

标签: ruby

我最近申请了一个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}"

2 个答案:

答案 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] ||= ""

eachmap之类的内容可能更好时,您还使用了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

更清楚地阅读和理解,并且更清楚你想要实现的目标。还有其他方法可以做到这一点,也许这就是将你与人群分开的原因。