# Ruby Regex to update various variables inside a file

In our applications we have a version file that has three variables that are then joined to create a string that can be used for the semantic version.

The file looks like:

# frozen_string_literal: true

major = 0
minor = 1
patch = 0

PORTAL_VERSION = [major, minor, patch].join('.')


And then we update it using the following from our CI server:

@version = '0.1.1' # this is passed from somewhere

puts 'updating version'
version = @version.split('.')
version_file = version_file.gsub(/(major = \w)/, "major = #{version[0]}")
version_file = version_file.gsub(/(minor = \w)/, "minor = #{version[1]}")
version_file = version_file.gsub(/(patch = \w)/, "patch = #{version[2]}")
File.open('version.rb', 'wb') { |file| file.write(version_file) }
puts "updated version to #{@version}"


Is there a better way to do the replacement using the regex? As by having to find each line and then replace it feels a little dirty, plus if there was or wasn't spaces between the variable name and value it wouldn't work. So it feels a little fragile.

• Why not generate a new version.rb from scratch instead of trying to edit it? – 200_success May 8 '18 at 20:44

There might be two easier options:

• Generate the file using a template file something like the following

major = {{major}}
minor = {{minor}}
patch = {{patch}}


that has strings that are easy to search for. You could also use ERB to generate the file.

• Put the logic in your version.rb file something like:

PORTAL_VERSION = '1.2.3'
major, minor, path = PORTAL_VERSION.split('.').map(&:to_i)


This way you would just have to replace the first line.

• If you want to do it using your logic I would change the regex from /(patch = \w)/ to /(patch\s*=\s*\w+)/ this would handle extra whitespace and version numbers bigger than 9

• Note you can also do variable substitution in Regexes so you could write your code as:

['major', 'minor', 'patch'].each.with_index do |key, i|
version_file.sub!(/(#{key}\s*=\s*\w+)/, "#{key} = #{version[i]}")
end


Note that gsub isn't necessary as you only expect one occurrence.