Is there a nicer way to prepend if not nil in Ruby?

ruby array
ruby string
ruby hash
ruby map
ruby check if nil or empty
ruby any
ruby not nil
ruby present method

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

Understanding Absence in Ruby: Present, Blank, Nil, Empty, “blank?”, “nil?”, and “empty?”. We're here to clear up the confusion. We're going to dive into the different ways in which the Ruby programming language Or better yet, they think of data as the digital representation of a number of If someone asks me whether the current king of Brazil is bald or not… Is there a nicer way to prepend if not nil in Ruby Microsoft USD - Closing all active tabs inside USD Pyspark: How to pass the argument in the sql scrip Enum associated value confusing; QGIS: mean of 9 neighboring pixels calculation in Google cloud ssl certificate “The SSL certificate

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,/^/,':')

Class: Array (Ruby 2.5.0), Another way to access a particular array element is by using the at method In the first form, if no arguments are sent, the new array will be empty. When a size  However, in terms of how it’s implemented, nil is fundamentally different than in other languages. In Ruby, nil is—you’ve guessed it—an object. It’s the single instance of the NilClass class. Since nil in Ruby is just an object like virtually anything else, this means that handling it is not a special case.

If Object#try happens to support blocks (like ActiveSupport’s),

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

String Concatenation & Interpolation in Ruby (With Examples), What if you want to merge a variable, or even numbers into another string? We start with an empty string & we build a bigger one by appending to it. You can use the Ruby concat method to merge strings efficiently. You have learned about string concatenation, appending, prepending & interpolation in Ruby, this  There are a few ways to modularize code in classes (or modules) in Ruby, namely: Include class Human include Bipedalism. What this does is adds the methods in Bipedalism to the Human class as instance methods. Extend class Human extend Bipedalism. What this does is adds the methods in Bipedalism to the Human class as class methods. Prepend

From ruby 2.3 you can use the safe navigation operator in combination with String#sub (As @Matt pointed out)

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

How to Use Ruby Conversion Methods (to_s, to_a, to_str , Convert it into an string (with to_s ) then convert it back to an integer (with to_i ). There are ways in which Ruby calls these conversion methods for you implicitly. "to_s called"; super; end; end; class Integer; prepend Log; end; puts "#{1}"; # "​to_s called" But unless your class is equivalent to a string you shouldn't do this. As you may expect, there should be a Ruby way to check if object is nil. Here it is: # Ruby code # if my_object.nil? puts "There is no object!" else my_object.display_info() end Wait a second… What we’ve actually done to check if object is nil, was to call the nil? method. Calling a method on a not existing object? Isn’t it any kind of trap?

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.

Official Ruby FAQ, When you assign to a variable, or initialize a constant, you set the object that the variable number 42 or the constant true actually holds the value, and not a reference to it. var = 1 # (1) class Demo var = 2 # (2) def method var = 3 # (3) puts "in If you do bump into it, try putting an assignment such as a = nil before the first  Ruby 2.0. Whilst the same implementation used for 1.9 would work in 2.0, due to Module#prepend we have a second option involving anonymous modules. Rather than renaming the method, we can create an anoymous module that has a method with the same name.

Feature #16150: Add a way to request a frozen string from to_s , This method is heavily used by almost all Ruby code that intermingles Symbols Another possibility: make/let #to_s methods of core types return frozen strings. IMO, since nil is a singleton, nil.to_s should also return the same empty string on -name if name name end end Module.singleton_class.prepend(FreezeName)​  There does not seem to be a method in Ruby to check if an object is not nil. Such a method could help with readability.

Module, Please use Module#prepend that comes with Ruby 2.0 or newer instead. If the target is nil and does not respond to the delegated method a NoMethodError is  The nil value in Ruby. As it is just a way to express the “lack of an object” notion. In another hand, the NilClass encapsulates all the logic and is used when the receiver of a message is

How to use .nil? .empty? .blank? .present? in Rails 6, Ruby methods can seem pretty daunting to a new programmer and knowing Checking for .nil? will only return true if the object itself is nil. The name of your permalink column. make_permalink first checks if there is a column. :prepend_id => [true|false] Do you want to prepend the ID to the permalink? for URLs like: posts/123-my-post-title - find_by_param uses the ID column to search.

Comments
  • That's true and may well be the best solution. But it feels like overkill to pull in a whole new require just for some simple string concatenation.
  • If I'm not mistaken "Object#try" comes from Rails, it's arguably the more heavy-handed, require-ful solution.
  • Actually I monkey-patched it in myself using the implementation I showed in the question. But you're right, and I think your solution is probably the most intention-revealing that I've seen so far.
  • Using URI is a nice path, but I'd be concerned with URI inadvertently encoding characters that the DBM expects to be unencoded. Using URI::Generic should avoid that, but it'd be good to check for those sort of cases.
  • Right! It would be ideal if prepend was prepend! and there was also the symmetric prepend. sub is a solid answer, I hadn't thought of that and it has the right semantics, but it's not as intention-revealing as I'd like - takes a bit of reading.
  • I've monkey-patched it in as I showed in the question, so my implementation doesn't currently support blocks. But the look of this is probably my preference. Maybe I'll steal the ActiveSupport implementation.
  • I agree in general but disagree in this case. The semantics here aren't complicated so the code shouldn't be. Your implementation will take me two minutes to understand because there's so much happening there. That's probably because I'm dense but we all are at times. It should be possible to reveal the intention of this code much more quickly than two minutes. It's just not complicated. Amit's answer is the best for that, I think - though I still think Ruby should provide a nicer way to do it as string concat.