philiprehberger-env_loader 0.2.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 42735b84e3b28e4cbccc4f4e312f024047e529df42b5b603870536fae8a93d93
4
- data.tar.gz: fd5d06f1205345406bcf637ffef0417a0742931bcd5bc6186c3e7c2d3f921b0f
3
+ metadata.gz: eb02740e5ccfd37833abf860aab8ab0c2f8a46cab63f1b1bcd17746384212c3a
4
+ data.tar.gz: 8641f1e44aa514fa8dfc5d1ca2643431ff0e48de19bc4ea0825ef10f2bde506e
5
5
  SHA512:
6
- metadata.gz: f471d5d740157e74601daaf1fd6639192eef8d9161e96828503adb8be5860e71181dd9cf160153066f6f49c2fe7a283f20acd7e96d2bf0afe087c93e2067e50e
7
- data.tar.gz: 3ce313f928805b5f06ed3f8575bbc6fa0006ff5c3ae790fbe7b77531ddda14a41cf3c09a06d1c3965188fbe4dd616ef0e4d6166e4349072b32088106ea7ba6a1
6
+ metadata.gz: 3e4a088a9664ff7325464b2eac8330178e7bb7a83a91869e9938c80195597aa668676b306cf850e62de2250e05c09b4a3b3c354ee84b63eef3a0ceac822cafed
7
+ data.tar.gz: c5b2552f169cbc78f043441ea105c517b4fabef753469b71bd3cfef41775ac335a7bc3785faf80137d0f82a93ac1f7724b75064556a295725e6c37aad6fdf552
data/CHANGELOG.md CHANGED
@@ -7,6 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.4.0] - 2026-05-29
11
+
12
+ ### Added
13
+ - `EnvLoader.dump(hash)` serializes a hash to `.env`-formatted text suitable for `parse` or for writing to a file; alphabetically sorted keys, double-quotes for values containing whitespace/`=`/`#`/quotes, backslash-escapes inner `"` and `\`, trailing newline
14
+ - `parse` now unescapes `\"`, `\\`, and `\n` inside double-quoted values so `parse(dump(h))` round-trips cleanly for values with inner quotes
15
+
16
+ ## [0.3.0] - 2026-05-01
17
+
18
+ ### Added
19
+ - `EnvLoader.parse(content)` — parse `.env`-formatted content from a string into a hash without touching ENV; useful for tests and embedded configs
20
+
10
21
  ## [0.2.1] - 2026-04-15
11
22
 
12
23
  ### Changed
data/README.md CHANGED
@@ -4,6 +4,8 @@
4
4
  [![Gem Version](https://badge.fury.io/rb/philiprehberger-env_loader.svg)](https://rubygems.org/gems/philiprehberger-env_loader)
5
5
  [![Last updated](https://img.shields.io/github/last-commit/philiprehberger/rb-env-loader)](https://github.com/philiprehberger/rb-env-loader/commits/main)
6
6
 
7
+ ![philiprehberger-env_loader](https://raw.githubusercontent.com/philiprehberger/rb-env-loader/main/package-card.webp)
8
+
7
9
  Multi-source environment variable loader with precedence and validation
8
10
 
9
11
  ## Requirements
@@ -73,6 +75,36 @@ Philiprehberger::EnvLoader.generate_template(
73
75
  )
74
76
  ```
75
77
 
78
+ ### Parse from a String
79
+
80
+ Parse `.env`-formatted text without reading from disk and without touching ENV:
81
+
82
+ ```ruby
83
+ content = <<~ENV
84
+ APP_HOST=localhost
85
+ APP_PORT=3000
86
+ # comments and blank lines are ignored
87
+ ENV
88
+
89
+ Philiprehberger::EnvLoader.parse(content)
90
+ # => { "APP_HOST" => "localhost", "APP_PORT" => "3000" }
91
+ ```
92
+
93
+ ### Dump to `.env` Format
94
+
95
+ Serialize a hash back to `.env`-formatted text. Round-trips with `parse` for any input. Values that contain whitespace, `=`, `#`, or quote characters are double-quoted with inner `"` and `\` backslash-escaped. Keys are sorted alphabetically.
96
+
97
+ ```ruby
98
+ text = Philiprehberger::EnvLoader.dump(
99
+ 'APP_HOST' => 'localhost',
100
+ 'APP_PORT' => '3000',
101
+ 'NOTE' => 'has "quote"'
102
+ )
103
+ # => "APP_HOST=localhost\nAPP_PORT=3000\nNOTE=\"has \\\"quote\\\"\"\n"
104
+
105
+ File.write('.env', text)
106
+ ```
107
+
76
108
  ## API
77
109
 
78
110
  | Method | Description |
@@ -80,6 +112,8 @@ Philiprehberger::EnvLoader.generate_template(
80
112
  | `.load(*files, required:, types:, defaults:, prefix:, strip_prefix:)` | Load variables from .env files with options |
81
113
  | `.validate!(*keys)` | Raise if any keys are missing or empty in ENV |
82
114
  | `.generate_template(output:, keys:)` | Generate a .env.template file |
115
+ | `.parse(content)` | Parse `.env`-formatted content from a string into a hash without touching ENV |
116
+ | `.dump(hash)` | Serialize a hash to `.env`-formatted text (round-trips with `parse`) |
83
117
  | `EnvLoader::Error` | Base error class for all gem errors |
84
118
  | `EnvLoader::ValidationError` | Raised when required keys are missing or empty |
85
119
 
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Philiprehberger
4
4
  module EnvLoader
5
- VERSION = '0.2.1'
5
+ VERSION = '0.4.0'
6
6
  end
7
7
  end
@@ -65,13 +65,18 @@ module Philiprehberger
65
65
  File.write(output, "#{content}\n")
66
66
  end
67
67
 
68
- # Parse a .env file into a hash of key-value pairs.
68
+ # Parse `.env`-formatted content from a string into a hash.
69
69
  #
70
- # @param path [String] the file path
71
- # @return [Hash<String, String>] parsed key-value pairs
72
- def self.parse_file(path)
70
+ # Useful for tests, embedded configurations, and any scenario where the
71
+ # `.env` content does not live in a file. Comments (`#`), blank lines,
72
+ # and surrounding whitespace are ignored. Single- and double-quoted
73
+ # values are unwrapped. ENV is not touched.
74
+ #
75
+ # @param content [String] the `.env`-formatted text
76
+ # @return [Hash{String => String}] parsed key-value pairs
77
+ def self.parse(content)
73
78
  result = {}
74
- File.readlines(path).each do |line|
79
+ content.to_s.each_line do |line|
75
80
  line = line.strip
76
81
  next if line.empty? || line.start_with?('#')
77
82
 
@@ -80,14 +85,63 @@ module Philiprehberger
80
85
 
81
86
  key = key.strip
82
87
  value = value.strip
83
- value = value[1..-2] if (value.start_with?('"') && value.end_with?('"')) ||
84
- (value.start_with?("'") && value.end_with?("'"))
88
+ if value.length >= 2 && value.start_with?('"') && value.end_with?('"')
89
+ value = value[1..-2].gsub(/\\(.)/) { Regexp.last_match(1) == 'n' ? "\n" : Regexp.last_match(1) }
90
+ elsif value.length >= 2 && value.start_with?("'") && value.end_with?("'")
91
+ value = value[1..-2]
92
+ end
85
93
  result[key] = value
86
94
  end
87
95
  result
88
96
  end
97
+
98
+ # Parse a .env file into a hash of key-value pairs.
99
+ #
100
+ # @param path [String] the file path
101
+ # @return [Hash<String, String>] parsed key-value pairs
102
+ def self.parse_file(path)
103
+ parse(File.read(path))
104
+ end
89
105
  private_class_method :parse_file
90
106
 
107
+ # Serialize a hash to `.env`-formatted text suitable for writing to a
108
+ # file or passing to {parse}.
109
+ #
110
+ # Keys are emitted in alphabetical order. Values that contain
111
+ # whitespace, `=`, `#`, or quote characters are wrapped in double
112
+ # quotes with inner `"` and `\` backslash-escaped. `nil` values become
113
+ # empty (`KEY=`). The output always ends with a single trailing
114
+ # newline.
115
+ #
116
+ # @example Round trip
117
+ # Philiprehberger::EnvLoader.parse(
118
+ # Philiprehberger::EnvLoader.dump('A' => '1', 'B' => 'two words')
119
+ # )
120
+ # # => { "A" => "1", "B" => "two words" }
121
+ #
122
+ # @param hash [Hash{String, Symbol => Object}] the key-value pairs to serialize
123
+ # @return [String] `.env`-formatted text
124
+ def self.dump(hash)
125
+ lines = hash.to_h
126
+ .transform_keys(&:to_s)
127
+ .sort_by(&:first)
128
+ .map { |key, value| "#{key}=#{format_value(value)}" }
129
+ "#{lines.join("\n")}\n"
130
+ end
131
+
132
+ # @api private
133
+ def self.format_value(value)
134
+ return '' if value.nil?
135
+
136
+ str = value.to_s
137
+ return '' if str.empty?
138
+ return str unless str.match?(/[\s="'#]/)
139
+
140
+ escaped = str.gsub('\\') { '\\\\' }.gsub('"') { '\\"' }
141
+ %("#{escaped}")
142
+ end
143
+ private_class_method :format_value
144
+
91
145
  # Apply type coercions to ENV values.
92
146
  #
93
147
  # @param types [Hash<String, Symbol>] key => type mapping
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: philiprehberger-env_loader
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Philip Rehberger
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-04-15 00:00:00.000000000 Z
11
+ date: 2026-05-30 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Load environment variables from multiple .env files with configurable
14
14
  precedence, type coercion, required key validation, default values, and template