有没有更好的方法在 Ruby 中添加 if not nil?
Is there a nicer way to prepend if not nil in 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}"
这样它在 development/test 中工作,其中 DB_PASSWORD
和 DB_PORTNUM
没有设置,并且在它们所在的生产环境中工作。但是这个有点丑:
ENV['DB_PASSWORD'] ? ":#{ENV['DB_PASSWORD']}" : nil
所需的语义是:如果不是 nil 则添加前缀,否则 return nil。理想情况下应该是这样的:
ENV['DB_PASSWORD'].try(:prepend, ':')
使用 Object.try 这样的东西:
def try method, *args
send(method, *args) if respond_to? method
end
但这不起作用,因为前置会改变字符串(为什么?)并且 env 字符串被冻结。备选方案:
ENV['DB_PASSWORD'].dup.try(:prepend, ':')
但是当没有设置环境变量时这不起作用,因为你不能复制nil。
这里有漂亮的单线还是我陷入了混乱?
不幸的是,String#insert
和 String#prepend
都修改了字符串,但 String#sub
应该可以:
ENV['DB_PASSWORD'].try(:sub,'',':')
或者多一点意图:
ENV['DB_PASSWORD'].try(:sub,/^/,':')
如果 Object#try
恰好支持块(如 ActiveSupport 的),
ENV['DB_PASSWORD'].try { |s| ":#{s}" }
使用对象和标准库:
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
呃。您的可读性确实受到了影响,因为您试图在一行中完成所有操作。不要那样做。
我会这样做:
为示例设置 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 时,它有助于加快代码速度,但今天的语言很少能从中受益。相反,发生的事情是它使代码无法破译。编写难以理解的代码是在代码审查中被要求解释自己的快速途径,然后被告知重写它并且永远不再这样做。
从 ruby 2.3
您可以使用 safe navigation operator in combination with String#sub
(As @Matt
)
ENV["DB_PASSWORD]&.sub(/^/, ":")
我有一堆环境变量,里面有我的配置:
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}"
这样它在 development/test 中工作,其中 DB_PASSWORD
和 DB_PORTNUM
没有设置,并且在它们所在的生产环境中工作。但是这个有点丑:
ENV['DB_PASSWORD'] ? ":#{ENV['DB_PASSWORD']}" : nil
所需的语义是:如果不是 nil 则添加前缀,否则 return nil。理想情况下应该是这样的:
ENV['DB_PASSWORD'].try(:prepend, ':')
使用 Object.try 这样的东西:
def try method, *args
send(method, *args) if respond_to? method
end
但这不起作用,因为前置会改变字符串(为什么?)并且 env 字符串被冻结。备选方案:
ENV['DB_PASSWORD'].dup.try(:prepend, ':')
但是当没有设置环境变量时这不起作用,因为你不能复制nil。
这里有漂亮的单线还是我陷入了混乱?
不幸的是,String#insert
和 String#prepend
都修改了字符串,但 String#sub
应该可以:
ENV['DB_PASSWORD'].try(:sub,'',':')
或者多一点意图:
ENV['DB_PASSWORD'].try(:sub,/^/,':')
如果 Object#try
恰好支持块(如 ActiveSupport 的),
ENV['DB_PASSWORD'].try { |s| ":#{s}" }
使用对象和标准库:
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
呃。您的可读性确实受到了影响,因为您试图在一行中完成所有操作。不要那样做。
我会这样做:
为示例设置 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 时,它有助于加快代码速度,但今天的语言很少能从中受益。相反,发生的事情是它使代码无法破译。编写难以理解的代码是在代码审查中被要求解释自己的快速途径,然后被告知重写它并且永远不再这样做。
从 ruby 2.3
您可以使用 safe navigation operator in combination with String#sub
(As @Matt
ENV["DB_PASSWORD]&.sub(/^/, ":")