1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
|
require "dotenv/substitutions/variable"
require "dotenv/substitutions/command" if RUBY_VERSION > "1.8.7"
module Dotenv
class FormatError < SyntaxError; end
# This class enables parsing of a string for key value pairs to be returned
# and stored in the Environment. It allows for variable substitutions and
# exporting of variables.
class Parser
@substitutions =
[Dotenv::Substitutions::Variable, Dotenv::Substitutions::Command]
LINE = /
\A
\s*
(?:export\s+)? # optional export
([\w\.]+) # key
(?:\s*=\s*|:\s+?) # separator
( # optional value begin
'(?:\'|[^'])*' # single quoted value
| # or
"(?:\"|[^"])*" # double quoted value
| # or
[^#\n]+ # unquoted value
)? # value end
\s*
(?:\#.*)? # optional comment
\z
/x
class << self
attr_reader :substitutions
def call(string, is_load = false)
new(string, is_load).call
end
end
def initialize(string, is_load = false)
@string = string
@hash = {}
@is_load = is_load
end
def call
@string.split(/[\n\r]+/).each do |line|
parse_line(line)
end
@hash
end
private
def parse_line(line)
if (match = line.match(LINE))
key, value = match.captures
@hash[key] = parse_value(value || "")
elsif line.split.first == "export"
if variable_not_set?(line)
raise FormatError, "Line #{line.inspect} has an unset variable"
end
end
end
def parse_value(value)
# Remove surrounding quotes
value = value.strip.sub(/\A(['"])(.*)\1\z/, '\2')
if Regexp.last_match(1) == '"'
value = unescape_characters(expand_newlines(value))
end
if Regexp.last_match(1) != "'"
self.class.substitutions.each do |proc|
value = proc.call(value, @hash, @is_load)
end
end
value
end
def unescape_characters(value)
value.gsub(/\\([^$])/, '\1')
end
def expand_newlines(value)
value.gsub('\n', "\n").gsub('\r', "\r")
end
def variable_not_set?(line)
!line.split[1..-1].all? { |var| @hash.member?(var) }
end
end
end
|