I have a bunch of environment variables with my config in them:
DB_HOSTNAME=something.rds.amazonaws.com
DB_PORTNUM=9999
DB_USERNAME=production
DB_PASSWORD=xyzzy
To make a DB connect string from them it's something like:
"postgres://" +
"#{ENV['DB_USERNAME']}#{ENV['DB_PASSWORD'] ? ":#{ENV['DB_PASSWORD']}" : nil}" +
"@#{ENV['DB_HOSTNAME']}#{ENV['DB_PORTNUM'] ? ":#{ENV['DB_PORTNUM']}" : nil}" +
"/proper_scraper_#{$environment}"
That way it works in development/test where DB_PASSWORD
and DB_PORTNUM
aren't set, and works in production where they are. But this bit is ugly:
ENV['DB_PASSWORD'] ? ":#{ENV['DB_PASSWORD']}" : nil
The desired semantics are: prepend if not nil and return nil otherwise. Ideally it would be something like this:
ENV['DB_PASSWORD'].try(:prepend, ':')
Using Object.try something like this:
def try method, *args
send(method, *args) if respond_to? method
end
But that doesn't work because prepend mutates the string (why?) and env strings are frozen. The alternative:
ENV['DB_PASSWORD'].dup.try(:prepend, ':')
But this doesn't work when the environment variable is not set because you can't dup nil.
Is there a nice one-liner here or am I stuck with the messiness?
Use objects and the standard library:
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
It's unfortunate that both String#insert
and String#prepend
modify the strings, but String#sub
should work:
ENV['DB_PASSWORD'].try(:sub,'',':')
Or with a bit more intention:
ENV['DB_PASSWORD'].try(:sub,/^/,':')
如果Object#try
恰好支持块(例如ActiveSupport的块),
ENV['DB_PASSWORD'].try { |s| ":#{s}" }
从ruby 2.3
您可以将安全导航运算符与String#sub
结合使用(如@Matt
指出的 )
ENV["DB_PASSWORD]&.sub(/^/, ":")
Ugh. Your readability has really suffered because you're trying to do it all in a single line. Don't do that.
I'd do something like:
Set up the ENV for the example...
ENV['DB_HOSTNAME'] = 'something.rds.amazonaws.com'
ENV['DB_PORTNUM'] = '9999'
ENV['DB_USERNAME'] = 'production'
ENV['DB_PASSWORD'] = 'xyzzy'
The real code starts here:
$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"
Ternary statements are replacements for if
/ then
/ else
statements, not for simple if
/ then
. Trying to make them fit only results in confusing code, so don't go there.
People get enamored with cramming code all in one line, and, a long time ago, back when we used interpreted BASIC, it helped speed up the code, but today's languages rarely benefit from that. Instead, what happens is it makes code indecipherable. Writing code that is indecipherable is a fast path to being called up to explain yourself in a code-review, followed by being told to rewrite it and never do it again.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.