有没有更好的方法在Ruby中提供前缀,如果不是nil?

时间:2015-01-03 03:18:20

标签: ruby

我的配置中有很多环境变量:

DB_HOSTNAME=something.rds.amazonaws.com
DB_PORTNUM=9999
DB_USERNAME=production
DB_PASSWORD=xyzzy

要从中创建数据库连接字符串,请执行以下操作:

"postgres://" +
"#{ENV['DB_USERNAME']}#{ENV['DB_PASSWORD'] ? ":#{ENV['DB_PASSWORD']}" : nil}" + 
"@#{ENV['DB_HOSTNAME']}#{ENV['DB_PORTNUM'] ? ":#{ENV['DB_PORTNUM']}" : nil}" +
"/proper_scraper_#{$environment}"

这样,它可以在开发/测试中工作,DB_PASSWORDDB_PORTNUM没有设置,并且可以在生产中工作。但这有点难看:

ENV['DB_PASSWORD'] ? ":#{ENV['DB_PASSWORD']}" : nil

所需的语义是:如果不是nil,则前置,否则返回nil。理想情况下会是这样的:

ENV['DB_PASSWORD'].try(:prepend, ':')

使用像这样的Object.try:

  def try method, *args
    send(method, *args) if respond_to? method
  end

但这不起作用,因为prepend会改变字符串(为什么?)和env字符串被冻结。替代方案:

ENV['DB_PASSWORD'].dup.try(:prepend, ':')

但是,如果未设置环境变量,则此功能无效,因为您无法复制nil。

这里有一个漂亮的单行或我是否陷入混乱?

5 个答案:

答案 0 :(得分:4)

使用对象和标准库:

require 'uri'

u = URI::Generic.build(
    scheme: "postgres", 
    host: ENV["DB_HOSTNAME"], 
    port: ENV["DB_PORTNUM"], 
    path: "/proper_scraper_#{$environment}",
)

u.user = ENV["DB_USERNAME"]
u.password = ENV["DB_PASSWORD"]

puts u.to_s

答案 1 :(得分:3)

遗憾的是,String#insertString#prepend都修改了字符串,但String#sub应该有效:

ENV['DB_PASSWORD'].try(:sub,'',':')

或者有点意图:

ENV['DB_PASSWORD'].try(:sub,/^/,':')

答案 2 :(得分:1)

如果Object#try恰好支持块(如ActiveSupport),

ENV['DB_PASSWORD'].try { |s| ":#{s}" }

答案 3 :(得分:1)

在红宝石2.3中,您可以将safe navigation operatorString#sub(作为@Matt pointed out)结合使用

ENV["DB_PASSWORD]&.sub(/^/, ":")

答案 4 :(得分:0)

唉。你的可读性确实受到了影响,因为你试图在一行中完成所有工作。不要那样做。

我会做类似的事情:

为示例设置ENV ...

ENV['DB_HOSTNAME'] = 'something.rds.amazonaws.com'
ENV['DB_PORTNUM'] = '9999'
ENV['DB_USERNAME'] = 'production'
ENV['DB_PASSWORD'] = 'xyzzy'

真正的代码从这里开始:

$environment = 'production'
db_hostname, db_portnum, db_username, db_password = %w[
  DB_HOSTNAME
  DB_PORTNUM
  DB_USERNAME
  DB_PASSWORD
].map{ |e| ENV[e] }

db_password = ':' + db_password if db_password
db_portnum = ':' + db_portnum if db_portnum

DSN = "postgres://%s%s@%s%s%s" % [
  db_username,
  db_password,
  db_hostname,
  db_portnum,
  "/proper_scraper_#{$environment}"
]
DSN # => "postgres://production:xyzzy@something.rds.amazonaws.com:9999/proper_scraper_production"

三元语句是if / then / else语句的替代,而非简单if / then。试图使它们合适只会导致代码混乱,所以不要去那里。

人们迷恋于一行中的填充代码,很久以前,当我们使用解释的BASIC时,它帮助加速了代码,但今天的语言很少从中受益。相反,它会使代码难以辨认。编写难以理解的代码是一种快速通道,可以在代码审查中调用自己来解释自己,然后被告知要重写它并且永远不会再这样做。