everythingrb 0.8.3 → 0.9.1

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: 6523ad82c57c693afadd2a17649c6628b49c0f775d0c3af09b5b207d8bace8a7
4
- data.tar.gz: 8c0c363214cf537d6ada6ad78d0da899ddbfdc17066325774b9e8f6f97b9f6f1
3
+ metadata.gz: 7361ef45ed502564c8e27fd3b2173702320f2076665ae9f495f5645fc7b57587
4
+ data.tar.gz: 411dc777df9851c7204903dde8ce12ce6951cf2308d9c02e6184bf0fef67a056
5
5
  SHA512:
6
- metadata.gz: 8a9f2620930ed9635287815679639cbd5403dc31b382717e107d8fbebf5fad0b6ba4be5398478f9b240141bbbbd2889cccbfc86d9330aeb1547386ab114bf034
7
- data.tar.gz: 8b6f1facb28da84ad99d7e52d14196ae023521c8ea26c075e1649f02364a29595f424b8aa9719438742ae2f5cc8ce047336534e94357693c7a678f225fdbc353
6
+ metadata.gz: 548f88f645e3d1731ccbe258e9e8d4157003f9b5f584942f8a2b14657bab1c04de965c16bd2a7bb0f2305edfc92c3e73c5bcd7a07f9627cccd3eef46e5488076
7
+ data.tar.gz: fb00c066860dc38a4b8b1091978618af0d6ee06bc477dbdb9d2a8603e043e0de8b7b93aab09964035f13efae77e0ec0a05fa00ea6caa3a70ad9d1c32b577c8d4
data/CHANGELOG.md CHANGED
@@ -15,6 +15,32 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
15
15
  ### Removed
16
16
  -->
17
17
 
18
+ ## [0.9.1] - 12026-01-22
19
+
20
+ ### Changed
21
+
22
+ - **Deprecated `Hash.new_nested_hash`** - This method is now deprecated and will be removed in v1.0.0. Consider using `Hash.new { |h, k| h[k] = Hash.new(&h.default_proc) }` instead.
23
+
24
+ ## [0.9.0] - 12025-08-01
25
+
26
+ ### Added
27
+
28
+ - **Added `Hash#compact_blank_merge` and `Hash#compact_blank_merge!`** - Merge only present (non-blank) values when ActiveSupport is loaded. Filters out nil, empty strings, empty arrays, false, and other blank values according to ActiveSupport's definition.
29
+ - Added `with_index` parameter to `Hash#join_map` for API consistency with `Array#join_map`.
30
+
31
+ ### Changed
32
+
33
+ - **BREAKING: Renamed `Hash#merge_compact` to `Hash#compact_merge`** - Updated method naming for consistency with other operation-first methods like `compact_prefix`, `trim_nils`, etc. The old method names will be removed in this version.
34
+ - **BREAKING: Renamed `Hash#merge_compact!` to `Hash#compact_merge!`** - In-place version follows the same naming convention.
35
+
36
+ ### Removed
37
+
38
+ - **BREAKING: Removed `Hash#merge_compact` and `Hash#merge_compact!`** - These methods have been renamed to `Hash#compact_merge` and `Hash#compact_merge!` respectively for naming consistency.
39
+ - **Removed deprecated Hash value filtering methods** - `Hash#select_values`, `Hash#select_values!`, `Hash#reject_values`, `Hash#reject_values!`, and `Hash#filter_values`, `Hash#filter_values!` have been removed as planned. These methods were deprecated in v0.8.3. Use the standard Ruby alternatives instead:
40
+ - `hash.select_values { |v| condition }` → `hash.select { |k, v| condition }`
41
+ - `hash.reject_values { |v| condition }` → `hash.reject { |k, v| condition }`
42
+ - For blank filtering: `hash.reject_values(&:blank?)` → `hash.compact_blank` (with ActiveSupport)
43
+
18
44
  ## [0.8.3] - 12025-05-31
19
45
 
20
46
  ### Added
@@ -22,6 +48,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
22
48
  ### Changed
23
49
 
24
50
  - **Deprecated Hash value filtering methods** - `Hash#select_values`, `Hash#reject_values`, `Hash#select_values!` and `Hash#reject_values!` are now deprecated and will be removed in v0.9.0. These methods largely duplicate existing Ruby/ActiveSupport functionality:
51
+
25
52
  - `hash.reject_values(&:blank?)` → use `hash.compact_blank` instead
26
53
  - `hash.select_values { |v| condition }` → use `hash.select { |k, v| condition }`
27
54
  - `hash.reject_values { |v| condition }` → use `hash.reject { |k, v| condition }`
@@ -95,6 +122,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
95
122
  - `Hash#reject_values!` - Same as `reject_values` but modifies the hash in place
96
123
  - Added `filter_values` and `filter_values!` as aliases for `select_values` and `select_values!` respectively
97
124
  - Added `Kernel#morph` as an alias for `then` (and `yield_self`) that better communicates transformation intent:
125
+
98
126
  ```ruby
99
127
  # Instead of this:
100
128
  value.then { |v| transform_somehow(v) }
@@ -124,12 +152,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
124
152
  The chainable `.with_key` method approach has been replaced with a more straightforward parameter-based approach.
125
153
 
126
154
  **Before:**
155
+
127
156
  ```ruby
128
157
  hash.transform_values.with_key { |value, key| "#{key}:#{value}" }
129
158
  hash.transform_values!.with_key { |value, key| "#{key}:#{value}" }
130
159
  ```
131
160
 
132
161
  **After:**
162
+
133
163
  ```ruby
134
164
  hash.transform_values(with_key: true) { |value, key| "#{key}:#{value}" }
135
165
  hash.transform_values!(with_key: true) { |value, key| "#{key}:#{value}" }
@@ -148,11 +178,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
148
178
  The parameter order in `Hash#transform_values.with_key` has been changed to yield `|value, key|` instead of `|key, value|` to maintain consistency with Ruby's standard enumeration methods like `each_with_index`.
149
179
 
150
180
  **Before:**
181
+
151
182
  ```ruby
152
183
  hash.transform_values.with_key { |key, value| "#{key}: #{value}" }
153
184
  ```
154
185
 
155
186
  **After:**
187
+
156
188
  ```ruby
157
189
  hash.transform_values.with_key { |value, key| "#{key}: #{value}" }
158
190
  ```
@@ -164,11 +196,11 @@ This change aligns our method signatures with Ruby's conventions and matches our
164
196
  - Added `Hash#transform` and `Hash#transform!` for transforming a hash's keys and values at the same time.
165
197
 
166
198
  ### Changed
199
+
167
200
  - Changed parameter order in `Hash#transform_values.with_key` to yield `|value, key|` instead of `|key, value|` for consistency with Ruby conventions.
168
201
 
169
202
  ### Removed
170
203
 
171
-
172
204
  ## [0.4.0] - 12025-04-11
173
205
 
174
206
  ### Added
@@ -316,7 +348,10 @@ This change aligns our method signatures with Ruby's conventions and matches our
316
348
 
317
349
  - Added alias `each` to `each_pair` in OpenStruct for better enumerable compatibility
318
350
 
319
- [unreleased]: https://github.com/itsthedevman/everythingrb/compare/v0.8.3...HEAD
351
+ [unreleased]: https://github.com/itsthedevman/everythingrb/compare/v0.9.1...HEAD
352
+ [0.9.1]: https://github.com/itsthedevman/everythingrb/compare/v0.9.0...v0.9.1
353
+ [0.9.0]: https://github.com/itsthedevman/everythingrb/compare/v0.8.3...v0.9.0
354
+ [0.8.3]: https://github.com/itsthedevman/everythingrb/compare/v0.8.2...v0.8.3
320
355
  [0.8.3]: https://github.com/itsthedevman/everythingrb/compare/v0.8.2...v0.8.3
321
356
  [0.8.2]: https://github.com/itsthedevman/everythingrb/compare/v0.8.1...v0.8.2
322
357
  [0.8.1]: https://github.com/itsthedevman/everythingrb/compare/v0.8.0...v0.8.1
data/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
  ![Ruby Version](https://img.shields.io/badge/ruby-3.3.7-ruby)
5
5
  [![Tests](https://github.com/itsthedevman/everythingrb/actions/workflows/main.yml/badge.svg)](https://github.com/everythingrb/sortsmith/actions/workflows/main.yml)
6
6
 
7
- Super handy extensions to Ruby core classes that make your code more expressive, readable, and fun to write. Stop writing boilerplate and start writing code that *actually matters*!
7
+ Super handy extensions to Ruby core classes that make your code more expressive, readable, and fun to write. Stop writing boilerplate and start writing code that _actually matters_!
8
8
 
9
9
  ## Express Your Intent, Not Your Logic
10
10
 
@@ -31,7 +31,7 @@ users.join_map(", ") { |u| u[:name] if u[:role] == "admin" }
31
31
  # => "Alice, Charlie"
32
32
  ```
33
33
 
34
- *Methods used: [`join_map`](https://itsthedevman.com/docs/everythingrb/Array.html#join_map-instance_method)*
34
+ _Methods used: [`join_map`](https://itsthedevman.com/docs/everythingrb/Array.html#join_map-instance_method)_
35
35
 
36
36
  Because life's too short to write all that boilerplate!
37
37
 
@@ -84,6 +84,7 @@ require "everythingrb/string" # Just String extensions
84
84
  ```
85
85
 
86
86
  Available modules:
87
+
87
88
  - `array`: Array extensions (join_map, key_map, etc.)
88
89
  - `boolean`: Boolean extensions (in_quotes, with_quotes)
89
90
  - `data`: Data extensions (to_deep_h, in_quotes)
@@ -143,7 +144,7 @@ With EverythingRB, it's one elegant step:
143
144
  '{"user":{"name":"Alice","roles":["admin"]}}'.to_ostruct.user.name # => "Alice"
144
145
  ```
145
146
 
146
- *Methods used: [`to_ostruct`](https://itsthedevman.com/docs/everythingrb/String.html#to_ostruct-instance_method)*
147
+ _Methods used: [`to_ostruct`](https://itsthedevman.com/docs/everythingrb/String.html#to_ostruct-instance_method)_
147
148
 
148
149
  Convert between data structures with ease:
149
150
 
@@ -161,7 +162,7 @@ config = { server: { host: "example.com", port: 443 } }.to_struct
161
162
  config.server.host # => "example.com"
162
163
  ```
163
164
 
164
- *Methods used: [`to_struct`](https://itsthedevman.com/docs/everythingrb/Hash.html#to_struct-instance_method)*
165
+ _Methods used: [`to_struct`](https://itsthedevman.com/docs/everythingrb/Hash.html#to_struct-instance_method)_
165
166
 
166
167
  Deep conversion to plain hashes is just as easy:
167
168
 
@@ -181,7 +182,7 @@ data = OpenStruct.new(user: Data.define(:name).new(name: "Bob"))
181
182
  data.to_deep_h # => {user: {name: "Bob"}}
182
183
  ```
183
184
 
184
- *Methods used: [`to_deep_h`](https://itsthedevman.com/docs/everythingrb/OpenStruct.html#to_deep_h-instance_method)*
185
+ _Methods used: [`to_deep_h`](https://itsthedevman.com/docs/everythingrb/OpenStruct.html#to_deep_h-instance_method)_
185
186
 
186
187
  **Extensions:** [`to_struct`](https://itsthedevman.com/docs/everythingrb/Hash.html#to_struct-instance_method), [`to_ostruct`](https://itsthedevman.com/docs/everythingrb/Hash.html#to_ostruct-instance_method), [`to_istruct`](https://itsthedevman.com/docs/everythingrb/Hash.html#to_istruct-instance_method), [`to_h`](https://itsthedevman.com/docs/everythingrb/String.html#to_h-instance_method), [`to_deep_h`](https://itsthedevman.com/docs/everythingrb/Hash.html#to_deep_h-instance_method)
187
188
 
@@ -191,7 +192,7 @@ Extract and transform data with elegant, expressive code:
191
192
 
192
193
  ```ruby
193
194
  # BEFORE
194
- users = [{ name: "Alice", roles: ["admin"] }, { name: "Bob", roles: ["user"] }]
195
+ users = [{ name: "Alice", role: "admin" }, { name: "Bob", role: "user" }]
195
196
  names = users.map { |user| user[:name] }
196
197
  # => ["Alice", "Bob"]
197
198
  ```
@@ -201,7 +202,7 @@ names = users.map { |user| user[:name] }
201
202
  users.key_map(:name) # => ["Alice", "Bob"]
202
203
  ```
203
204
 
204
- *Methods used: [`key_map`](https://itsthedevman.com/docs/everythingrb/Array.html#key_map-instance_method)*
205
+ _Methods used: [`key_map`](https://itsthedevman.com/docs/everythingrb/Array.html#key_map-instance_method)_
205
206
 
206
207
  Simplify nested data extraction:
207
208
 
@@ -220,7 +221,7 @@ names = users.map { |u| u.dig(:user, :profile, :name) }
220
221
  users.dig_map(:user, :profile, :name) # => ["Alice", "Bob"]
221
222
  ```
222
223
 
223
- *Methods used: [`dig_map`](https://itsthedevman.com/docs/everythingrb/Array.html#dig_map-instance_method)*
224
+ _Methods used: [`dig_map`](https://itsthedevman.com/docs/everythingrb/Array.html#dig_map-instance_method)_
224
225
 
225
226
  Combine filter, map, and join operations in one step:
226
227
 
@@ -237,7 +238,24 @@ result = data.compact.filter_map { |n| "Item #{n}" if n.odd? }.join(" | ")
237
238
  # => "Item 1 | Item 3"
238
239
  ```
239
240
 
240
- *Methods used: [`join_map`](https://itsthedevman.com/docs/everythingrb/Array.html#join_map-instance_method)*
241
+ _Methods used: [`join_map`](https://itsthedevman.com/docs/everythingrb/Array.html#join_map-instance_method)_
242
+
243
+ Need position-aware processing? Both Array and Hash support `with_index`:
244
+
245
+ ```ruby
246
+ # BEFORE
247
+ users = {alice: "Alice", bob: "Bob", charlie: "Charlie"}
248
+ users.filter_map.with_index { |(k, v), i| "#{i + 1}. #{v}" }.join(", ")
249
+ # => "1. Alice, 2. Bob, 3. Charlie"
250
+ ```
251
+
252
+ ```ruby
253
+ # AFTER
254
+ users.join_map(", ", with_index: true) { |(k, v), i| "#{i + 1}. #{v}" }
255
+ # => "1. Alice, 2. Bob, 3. Charlie"
256
+ ```
257
+
258
+ _Methods used: [`join_map`](https://itsthedevman.com/docs/everythingrb/Hash.html#join_map-instance_method)_
241
259
 
242
260
  Group elements with natural syntax:
243
261
 
@@ -258,7 +276,7 @@ users.group_by_key(:department, :name)
258
276
  # => {"Engineering"=>[{name: "Alice",...}, {name: "Charlie",...}], "Sales"=>[{name: "Bob",...}]}
259
277
  ```
260
278
 
261
- *Methods used: [`group_by_key`](https://itsthedevman.com/docs/everythingrb/Enumerable.html#group_by_key-instance_method)*
279
+ _Methods used: [`group_by_key`](https://itsthedevman.com/docs/everythingrb/Enumerable.html#group_by_key-instance_method)_
262
280
 
263
281
  Create natural language lists:
264
282
 
@@ -283,32 +301,13 @@ end
283
301
  ["red", "blue", "green"].to_or_sentence # => "red, blue, or green"
284
302
  ```
285
303
 
286
- *Methods used: [`to_or_sentence`](https://itsthedevman.com/docs/everythingrb/Array.html#to_or_sentence-instance_method)*
304
+ _Methods used: [`to_or_sentence`](https://itsthedevman.com/docs/everythingrb/Array.html#to_or_sentence-instance_method)_
287
305
 
288
306
  **Extensions:** [`join_map`](https://itsthedevman.com/docs/everythingrb/Array.html#join_map-instance_method), [`key_map`](https://itsthedevman.com/docs/everythingrb/Array.html#key_map-instance_method), [`dig_map`](https://itsthedevman.com/docs/everythingrb/Array.html#dig_map-instance_method), [`to_or_sentence`](https://itsthedevman.com/docs/everythingrb/Array.html#to_or_sentence-instance_method), [`group_by_key`](https://itsthedevman.com/docs/everythingrb/Enumerable.html#group_by_key-instance_method)
289
307
 
290
308
  ### Hash Convenience
291
309
 
292
- Work with hashes more intuitively:
293
-
294
- ```ruby
295
- # BEFORE
296
- stats = {}
297
- stats[:server] ||= {}
298
- stats[:server][:region] ||= {}
299
- stats[:server][:region][:us_east] ||= {}
300
- stats[:server][:region][:us_east][:errors] ||= []
301
- stats[:server][:region][:us_east][:errors] << "Connection timeout"
302
- ```
303
-
304
- ```ruby
305
- # AFTER
306
- stats = Hash.new_nested_hash(depth: 3)
307
- (stats[:server][:region][:us_east][:errors] ||= []) << "Connection timeout"
308
- # No need to initialize each level first!
309
- ```
310
-
311
- *Methods used: [`new_nested_hash`](https://itsthedevman.com/docs/everythingrb/Hash.html#new_nested_hash-class_method)*
310
+ Work with hashes more intuitively.
312
311
 
313
312
  Transform values with access to their keys:
314
313
 
@@ -328,7 +327,7 @@ users.transform_values(with_key: true) { |v, k| "User #{k}: #{v[:name]}" }
328
327
  # => {alice: "User alice: Alice", bob: "User bob: Bob"}
329
328
  ```
330
329
 
331
- *Methods used: [`transform_values(with_key: true)`](https://itsthedevman.com/docs/everythingrb/Hash.html#transform_values-instance_method)*
330
+ _Methods used: [`transform_values(with_key: true)`](https://itsthedevman.com/docs/everythingrb/Hash.html#transform_values-instance_method)_
332
331
 
333
332
  Find values based on conditions:
334
333
 
@@ -349,7 +348,7 @@ users.values_where { |_k, v| v[:role] == "admin" }
349
348
  # => [{name: "Alice", role: "admin"}, {name: "Charlie", role: "admin"}]
350
349
  ```
351
350
 
352
- *Methods used: [`values_where`](https://itsthedevman.com/docs/everythingrb/Hash.html#values_where-instance_method)*
351
+ _Methods used: [`values_where`](https://itsthedevman.com/docs/everythingrb/Hash.html#values_where-instance_method)_
353
352
 
354
353
  Just want the first match? Even simpler:
355
354
 
@@ -365,7 +364,7 @@ users.value_where { |_k, v| v[:role] == "admin" }
365
364
  # => {name: "Alice", role: "admin"}
366
365
  ```
367
366
 
368
- *Methods used: [`value_where`](https://itsthedevman.com/docs/everythingrb/Hash.html#value_where-instance_method)*
367
+ _Methods used: [`value_where`](https://itsthedevman.com/docs/everythingrb/Hash.html#value_where-instance_method)_
369
368
 
370
369
  Rename keys while preserving order:
371
370
 
@@ -395,23 +394,7 @@ config.rename_keys(api_key: :key, timeout: :request_timeout)
395
394
  # => {key: "secret", request_timeout: 30}
396
395
  ```
397
396
 
398
- *Methods used: [`rename_keys`](https://itsthedevman.com/docs/everythingrb/Hash.html#rename_keys-instance_method)*
399
-
400
- Filter hash by values:
401
-
402
- ```ruby
403
- # BEFORE
404
- result = {a: 1, b: nil, c: 2}.select { |_k, v| v.is_a?(Integer) && v > 1 }
405
- # => {c: 2}
406
- ```
407
-
408
- ```ruby
409
- # AFTER
410
- {a: 1, b: nil, c: 2}.select_values { |v| v.is_a?(Integer) && v > 1 }
411
- # => {c: 2}
412
- ```
413
-
414
- *Methods used: [`select_values`](https://itsthedevman.com/docs/everythingrb/Hash.html#select_values-instance_method)*
397
+ _Methods used: [`rename_keys`](https://itsthedevman.com/docs/everythingrb/Hash.html#rename_keys-instance_method)_
415
398
 
416
399
  Conditionally merge hashes with clear intent:
417
400
 
@@ -430,7 +413,7 @@ user_params.merge_if(verified: true, admin: true) { |k, v| v == true && k == :ve
430
413
  # => {name: "Alice", role: "user", verified: true}
431
414
  ```
432
415
 
433
- *Methods used: [`merge_if`](https://itsthedevman.com/docs/everythingrb/Hash.html#merge_if-instance_method)*
416
+ _Methods used: [`merge_if`](https://itsthedevman.com/docs/everythingrb/Hash.html#merge_if-instance_method)_
434
417
 
435
418
  The nil-filtering pattern we've all written dozens of times:
436
419
 
@@ -445,13 +428,33 @@ params.merge(filtered)
445
428
  ```ruby
446
429
  # AFTER
447
430
  params = {sort: "created_at"}
448
- params.merge_compact(filter: "active", search: nil)
431
+ params.compact_merge(filter: "active", search: nil)
449
432
  # => {sort: "created_at", filter: "active"}
450
433
  ```
451
434
 
452
- *Methods used: [`merge_compact`](https://itsthedevman.com/docs/everythingrb/Hash.html#merge_compact-instance_method)*
435
+ _Methods used: [`compact_merge`](https://itsthedevman.com/docs/everythingrb/Hash.html#compact_merge-instance_method)_
436
+
437
+ Filter out blank values too when ActiveSupport is loaded:
438
+
439
+ ```ruby
440
+ # BEFORE
441
+ config = {api_key: "secret", timeout: 30}
442
+ user_settings = {timeout: "", retries: nil, debug: true, tags: []}
443
+ clean_settings = user_settings.reject { |k, v| v.blank? }
444
+ config.merge(clean_settings)
445
+ # => {api_key: "secret", timeout: 30, debug: true}
446
+ ```
447
+
448
+ ```ruby
449
+ # AFTER
450
+ config = {api_key: "secret", timeout: 30}
451
+ config.compact_blank_merge(timeout: "", retries: nil, debug: true, tags: [])
452
+ # => {api_key: "secret", timeout: 30, debug: true}
453
+ ```
454
+
455
+ _Methods used: [`compact_blank_merge`](https://itsthedevman.com/docs/everythingrb/Hash.html#compact_blank_merge-instance_method)_
453
456
 
454
- **Extensions:** [`new_nested_hash`](https://itsthedevman.com/docs/everythingrb/Hash.html#new_nested_hash-class_method), [`transform_values(with_key: true)`](https://itsthedevman.com/docs/everythingrb/Hash.html#transform_values-instance_method), [`value_where`](https://itsthedevman.com/docs/everythingrb/Hash.html#value_where-instance_method), [`values_where`](https://itsthedevman.com/docs/everythingrb/Hash.html#values_where-instance_method), [`rename_key`](https://itsthedevman.com/docs/everythingrb/Hash.html#rename_key-instance_method), [`rename_keys`](https://itsthedevman.com/docs/everythingrb/Hash.html#rename_keys-instance_method), [`select_values`](https://itsthedevman.com/docs/everythingrb/Hash.html#select_values-instance_method), [`reject_values`](https://itsthedevman.com/docs/everythingrb/Hash.html#reject_values-instance_method), [`merge_if`](https://itsthedevman.com/docs/everythingrb/Hash.html#merge_if-instance_method), [`merge_if!`](https://itsthedevman.com/docs/everythingrb/Hash.html#merge_if%21-instance_method), [`merge_if_values`](https://itsthedevman.com/docs/everythingrb/Hash.html#merge_if_values-instance_method), [`merge_if_values!`](https://itsthedevman.com/docs/everythingrb/Hash.html#merge_if_values%21-instance_method), [`merge_compact`](https://itsthedevman.com/docs/everythingrb/Hash.html#merge_compact-instance_method), [`merge_compact!`](https://itsthedevman.com/docs/everythingrb/Hash.html#merge_compact%21-instance_method)
457
+ **Extensions:** [`transform_values(with_key: true)`](https://itsthedevman.com/docs/everythingrb/Hash.html#transform_values-instance_method), [`value_where`](https://itsthedevman.com/docs/everythingrb/Hash.html#value_where-instance_method), [`values_where`](https://itsthedevman.com/docs/everythingrb/Hash.html#values_where-instance_method), [`rename_key`](https://itsthedevman.com/docs/everythingrb/Hash.html#rename_key-instance_method), [`rename_keys`](https://itsthedevman.com/docs/everythingrb/Hash.html#rename_keys-instance_method), [`merge_if`](https://itsthedevman.com/docs/everythingrb/Hash.html#merge_if-instance_method), [`merge_if!`](https://itsthedevman.com/docs/everythingrb/Hash.html#merge_if%21-instance_method), [`merge_if_values`](https://itsthedevman.com/docs/everythingrb/Hash.html#merge_if_values-instance_method), [`merge_if_values!`](https://itsthedevman.com/docs/everythingrb/Hash.html#merge_if_values%21-instance_method), [`compact_merge`](https://itsthedevman.com/docs/everythingrb/Hash.html#compact_merge-instance_method), [`compact_merge!`](https://itsthedevman.com/docs/everythingrb/Hash.html#compact_merge%21-instance_method), [`compact_blank_merge`](https://itsthedevman.com/docs/everythingrb/Hash.html#compact_blank_merge-instance_method), [`compact_blank_merge!`](https://itsthedevman.com/docs/everythingrb/Hash.html#compact_blank_merge%21-instance_method)
455
458
 
456
459
  ### Array Cleaning
457
460
 
@@ -469,7 +472,7 @@ data.drop_while(&:nil?).reverse.drop_while(&:nil?).reverse
469
472
  [nil, nil, 1, nil, 2, nil, nil].trim_nils # => [1, nil, 2]
470
473
  ```
471
474
 
472
- *Methods used: [`trim_nils`](https://itsthedevman.com/docs/everythingrb/Array.html#trim_nils-instance_method)*
475
+ _Methods used: [`trim_nils`](https://itsthedevman.com/docs/everythingrb/Array.html#trim_nils-instance_method)_
473
476
 
474
477
  With ActiveSupport, remove blank values too:
475
478
 
@@ -485,7 +488,7 @@ data.drop_while(&:blank?).reverse.drop_while(&:blank?).reverse
485
488
  [nil, "", 1, "", 2, nil, ""].trim_blanks # => [1, "", 2]
486
489
  ```
487
490
 
488
- *Methods used: [`trim_blanks`](https://itsthedevman.com/docs/everythingrb/Array.html#trim_blanks-instance_method)*
491
+ _Methods used: [`trim_blanks`](https://itsthedevman.com/docs/everythingrb/Array.html#trim_blanks-instance_method)_
489
492
 
490
493
  **Extensions:** [`trim_nils`](https://itsthedevman.com/docs/everythingrb/Array.html#trim_nils-instance_method), [`compact_prefix`](https://itsthedevman.com/docs/everythingrb/Array.html#compact_prefix-instance_method), [`compact_suffix`](https://itsthedevman.com/docs/everythingrb/Array.html#compact_suffix-instance_method), [`trim_blanks`](https://itsthedevman.com/docs/everythingrb/Array.html#trim_blanks-instance_method) (with ActiveSupport)
491
494
 
@@ -528,7 +531,7 @@ Time.now.in_quotes # => "\"2025-05-04 12:34:56 +0000\""
528
531
  message = "You selected #{selection.in_quotes}"
529
532
  ```
530
533
 
531
- *Methods used: [`in_quotes`](https://itsthedevman.com/docs/everythingrb/Everythingrb/InspectQuotable.html#in_quotes-instance_method), [`with_quotes`](https://itsthedevman.com/docs/everythingrb/Everythingrb/InspectQuotable.html#with_quotes-instance_method)*
534
+ _Methods used: [`in_quotes`](https://itsthedevman.com/docs/everythingrb/Everythingrb/InspectQuotable.html#in_quotes-instance_method), [`with_quotes`](https://itsthedevman.com/docs/everythingrb/Everythingrb/InspectQuotable.html#with_quotes-instance_method)_
532
535
 
533
536
  Convert strings to camelCase with ease:
534
537
 
@@ -555,7 +558,7 @@ camel_case
555
558
  "please-WAIT while_loading...".to_camelcase # => "PleaseWaitWhileLoading"
556
559
  ```
557
560
 
558
- *Methods used: [`to_camelcase`](https://itsthedevman.com/docs/everythingrb/String.html#to_camelcase-instance_method)*
561
+ _Methods used: [`to_camelcase`](https://itsthedevman.com/docs/everythingrb/String.html#to_camelcase-instance_method)_
559
562
 
560
563
  **Extensions:** [`in_quotes`](https://itsthedevman.com/docs/everythingrb/Everythingrb/InspectQuotable.html#in_quotes-instance_method), [`with_quotes`](https://itsthedevman.com/docs/everythingrb/Everythingrb/InspectQuotable.html#with_quotes-instance_method) (alias), [`to_camelcase`](https://itsthedevman.com/docs/everythingrb/String.html#to_camelcase-instance_method)
561
564
 
@@ -590,7 +593,7 @@ user.admin = true
590
593
  user.admin? # => true
591
594
  ```
592
595
 
593
- *Methods used: [`attr_predicate`](https://itsthedevman.com/docs/everythingrb/Module.html#attr_predicate-instance_method)*
596
+ _Methods used: [`attr_predicate`](https://itsthedevman.com/docs/everythingrb/Module.html#attr_predicate-instance_method)_
594
597
 
595
598
  Works with Data objects too:
596
599
 
@@ -610,7 +613,7 @@ person = Person.new(active: false)
610
613
  person.active? # => false
611
614
  ```
612
615
 
613
- *Methods used: [`attr_predicate`](https://itsthedevman.com/docs/everythingrb/Module.html#attr_predicate-instance_method)*
616
+ _Methods used: [`attr_predicate`](https://itsthedevman.com/docs/everythingrb/Module.html#attr_predicate-instance_method)_
614
617
 
615
618
  **Extensions:** [`attr_predicate`](https://itsthedevman.com/docs/everythingrb/Module.html#attr_predicate-instance_method)
616
619
 
@@ -628,7 +631,7 @@ result = value.then { |v| transform_it(v) }
628
631
  result = value.morph { |v| transform_it(v) }
629
632
  ```
630
633
 
631
- *Methods used: [`morph`](https://itsthedevman.com/docs/everythingrb/Kernel.html#morph-instance_method)*
634
+ _Methods used: [`morph`](https://itsthedevman.com/docs/everythingrb/Kernel.html#morph-instance_method)_
632
635
 
633
636
  **Extensions:** [`morph`](https://itsthedevman.com/docs/everythingrb/Kernel.html#morph-instance_method) (alias for `then`/`yield_self`)
634
637
 
data/flake.lock CHANGED
@@ -20,11 +20,11 @@
20
20
  },
21
21
  "nixpkgs": {
22
22
  "locked": {
23
- "lastModified": 1742422364,
24
- "narHash": "sha256-mNqIplmEohk5jRkqYqG19GA8MbQ/D4gQSK0Mu4LvfRQ=",
23
+ "lastModified": 1750506804,
24
+ "narHash": "sha256-VLFNc4egNjovYVxDGyBYTrvVCgDYgENp5bVi9fPTDYc=",
25
25
  "owner": "NixOS",
26
26
  "repo": "nixpkgs",
27
- "rev": "a84ebe20c6bc2ecbcfb000a50776219f48d134cc",
27
+ "rev": "4206c4cb56751df534751b058295ea61357bbbaa",
28
28
  "type": "github"
29
29
  },
30
30
  "original": {
data/flake.nix CHANGED
@@ -21,7 +21,7 @@
21
21
  devShells.default = pkgs.mkShell {
22
22
  buildInputs = with pkgs; [
23
23
  (ruby_3_2.override {
24
- jemallocSupport = true;
24
+ jemallocSupport = false;
25
25
  docSupport = false;
26
26
  })
27
27
 
@@ -13,7 +13,8 @@
13
13
  # - ::new_nested_hash: Create automatically nesting hashes
14
14
  # - #merge_if, #merge_if!: Conditionally merge based on key-value pairs
15
15
  # - #merge_if_values, #merge_if_values!: Conditionally merge based on values
16
- # - #merge_compact, #merge_compact!: Merge only non-nil values
16
+ # - #compact_merge, #compact_merge!: Merge only non-nil values
17
+ # - #compact_blank_merge, #compact_blank_merge!: Merge only present values (ActiveSupport)
17
18
  #
18
19
  # @example
19
20
  # require "everythingrb/hash"
@@ -77,6 +78,11 @@ class Hash
77
78
  # parameter to control this behavior.
78
79
  #
79
80
  def self.new_nested_hash(depth: nil)
81
+ Everythingrb.deprecator.warn(
82
+ "Hash.new_nested_hash is deprecated and will be removed in v1.0.0. " \
83
+ "Consider using Hash.new { |h, k| h[k] = Hash.new(&h.default_proc) } instead."
84
+ )
85
+
80
86
  new do |hash, key|
81
87
  next if depth == 0
82
88
 
@@ -93,25 +99,37 @@ class Hash
93
99
  # Combines filter_map and join operations
94
100
  #
95
101
  # @param join_with [String] The delimiter to join elements with (defaults to empty string)
102
+ # @param with_index [Boolean] Whether to include the index in the block (defaults to false)
96
103
  #
97
- # @yield [key, value] Block that filters and transforms hash entries
98
- # @yieldparam key [Object] The current key
99
- # @yieldparam value [Object] The current value
104
+ # @yield [key_value_pair, index] Block that filters and transforms hash entries
105
+ # @yieldparam key_value_pair [Array] Two-element array containing [key, value] (can be destructured as |key, value|)
106
+ # @yieldparam index [Integer] The index of the current entry (only if with_index: true)
100
107
  #
101
108
  # @return [String] Joined string of filtered and transformed entries
102
109
  #
103
- # @example
110
+ # @example Basic usage without index
104
111
  # { a: 1, b: nil, c: 2, d: nil, e: 3 }.join_map(", ") { |k, v| "#{k}-#{v}" if v }
105
112
  # # => "a-1, c-2, e-3"
106
113
  #
107
- # @example Without a block
114
+ # @example With index for position-aware formatting
115
+ # users = {alice: "Alice", bob: "Bob", charlie: "Charlie"}
116
+ # users.join_map(", ", with_index: true) do |(k, v), i|
117
+ # "#{i + 1}. #{v}"
118
+ # end
119
+ # # => "1. Alice, 2. Bob, 3. Charlie"
120
+ #
121
+ # @example Without a block (default behavior)
108
122
  # { a: 1, b: nil, c: 2 }.join_map(" ")
109
123
  # # => "a 1 b c 2"
110
124
  #
111
- def join_map(join_with = "", &block)
125
+ def join_map(join_with = "", with_index: false, &block)
112
126
  block = ->(kv_pair) { kv_pair.compact } if block.nil?
113
127
 
114
- filter_map(&block).join(join_with)
128
+ if with_index
129
+ filter_map.with_index(&block).join(join_with)
130
+ else
131
+ filter_map(&block).join(join_with)
132
+ end
115
133
  end
116
134
 
117
135
  #
@@ -661,70 +679,6 @@ class Hash
661
679
  self
662
680
  end
663
681
 
664
- #
665
- # Selects hash entries based only on their values
666
- #
667
- # @deprecated Hash#select_values will be removed in 0.9.0. Please use `select { |k, v| condition }` instead.
668
- #
669
- def select_values(&block)
670
- Everythingrb.deprecator.warn(
671
- "Hash#select_values will be removed in 0.9.0. Please use `select { |k, v| condition }` instead."
672
- )
673
-
674
- return to_enum(:select_values) if block.nil?
675
-
676
- select { |_k, v| block.call(v) }
677
- end
678
-
679
- alias_method :filter_values, :select_values
680
-
681
- #
682
- # Selects hash entries based only on their values, modifying the hash in place
683
- #
684
- # @deprecated Hash#select_values! will be removed in 0.9.0. Please use `select! { |k, v| condition }` instead.
685
- #
686
- def select_values!(&block)
687
- Everythingrb.deprecator.warn(
688
- "Hash#select_values! will be removed in 0.9.0. Please use `select! { |k, v| condition }` instead."
689
- )
690
-
691
- return to_enum(:select_values!) if block.nil?
692
-
693
- select! { |_k, v| block.call(v) }
694
- end
695
-
696
- alias_method :filter_values!, :select_values!
697
-
698
- #
699
- # Rejects hash entries based only on their values
700
- #
701
- # @deprecated Hash#reject_values will be removed in 0.9.0. Please use `reject { |k, v| condition }` instead.
702
- #
703
- def reject_values(&block)
704
- Everythingrb.deprecator.warn(
705
- "Hash#reject_values will be removed in 0.9.0. Please use `reject { |k, v| condition }` instead."
706
- )
707
-
708
- return to_enum(:reject_values) if block.nil?
709
-
710
- reject { |_k, v| block.call(v) }
711
- end
712
-
713
- #
714
- # Rejects hash entries based only on their values, modifying the hash in place
715
- #
716
- # @deprecated Hash#reject_values! will be removed in 0.9.0. Please use `reject! { |k, v| condition }` instead.
717
- #
718
- def reject_values!(&block)
719
- Everythingrb.deprecator.warn(
720
- "Hash#reject_values! will be removed in 0.9.0. Please use `reject! { |k, v| condition }` instead."
721
- )
722
-
723
- return to_enum(:reject_values!) if block.nil?
724
-
725
- reject! { |_k, v| block.call(v) }
726
- end
727
-
728
682
  #
729
683
  # Conditionally merges key-value pairs from another hash based on a block
730
684
  #
@@ -828,15 +782,15 @@ class Hash
828
782
  # email = nil
829
783
  # name = "Alice"
830
784
  #
831
- # {}.merge_compact(
785
+ # {}.compact_merge(
832
786
  # id: user_id,
833
787
  # email: email,
834
788
  # name: name
835
789
  # )
836
790
  # # => {id: 42, name: "Alice"}
837
791
  #
838
- def merge_compact(other = {})
839
- merge_if_values(other, &:itself)
792
+ def compact_merge(other = {})
793
+ merge_if_values(other) { |i| i.itself }
840
794
  end
841
795
 
842
796
  #
@@ -851,14 +805,76 @@ class Hash
851
805
  #
852
806
  # @example Merge only non-nil values in place
853
807
  # params = {format: "json"}
854
- # params.merge_compact!(
808
+ # params.compact_merge!(
855
809
  # page: 1,
856
810
  # per_page: nil,
857
811
  # sort: "created_at"
858
812
  # )
859
813
  # # => {format: "json", page: 1, sort: "created_at"}
860
814
  #
861
- def merge_compact!(other = {})
862
- merge_if_values!(other, &:itself)
815
+ def compact_merge!(other = {})
816
+ merge_if_values!(other) { |i| i.itself }
817
+ end
818
+
819
+ # ActiveSupport integrations
820
+ if defined?(ActiveSupport)
821
+ #
822
+ # Merges only present (non-blank) values from another hash
823
+ #
824
+ # This method merges key-value pairs from another hash, but only includes
825
+ # values that are present according to ActiveSupport's definition (not nil,
826
+ # not empty strings, not empty arrays, etc.).
827
+ #
828
+ # @param other [Hash] The hash to merge from
829
+ #
830
+ # @return [Hash] A new hash with present values merged
831
+ #
832
+ # @note Only available when ActiveSupport is loaded
833
+ #
834
+ # @example Building API responses without blank values
835
+ # user_data = {name: "Alice", role: "admin"}
836
+ # user_data.compact_blank_merge(
837
+ # bio: "", # excluded - blank string
838
+ # tags: [], # excluded - empty array
839
+ # email: "a@example.com", # included - present
840
+ # phone: nil # excluded - nil
841
+ # )
842
+ # # => {name: "Alice", role: "admin", email: "a@example.com"}
843
+ #
844
+ # @example Configuration merging with blank filtering
845
+ # defaults = {timeout: 30, retries: 3}
846
+ # user_config = {timeout: "", retries: 5, debug: true}
847
+ # defaults.compact_blank_merge(user_config)
848
+ # # => {timeout: 30, retries: 5, debug: true}
849
+ #
850
+ def compact_blank_merge(other = {})
851
+ merge_if_values(other) { |i| i.present? }
852
+ end
853
+
854
+ #
855
+ # Merges only present (non-blank) values from another hash, in place
856
+ #
857
+ # This method merges key-value pairs from another hash into the current hash,
858
+ # but only includes values that are present according to ActiveSupport's
859
+ # definition (not nil, not empty strings, not empty arrays, etc.).
860
+ #
861
+ # @param other [Hash] The hash to merge from
862
+ #
863
+ # @return [self] The modified hash
864
+ #
865
+ # @note Only available when ActiveSupport is loaded
866
+ #
867
+ # @example Building parameters while excluding blanks
868
+ # params = {action: "search", format: "json"}
869
+ # params.compact_blank_merge!(
870
+ # query: "", # excluded - blank
871
+ # page: 2, # included - present
872
+ # tags: [] # excluded - empty array
873
+ # )
874
+ # # => {action: "search", format: "json", page: 2}
875
+ #
876
+ def compact_blank_merge!(other = {})
877
+ merge_if_values!(other) { |i| i.present? }
878
+ end
863
879
  end
864
880
  end
@@ -7,5 +7,5 @@
7
7
  #
8
8
  module Everythingrb
9
9
  # Current version of the everythingrb gem
10
- VERSION = "0.8.3"
10
+ VERSION = "0.9.1"
11
11
  end
data/lib/railtie.rb CHANGED
@@ -21,9 +21,7 @@ module Everythingrb
21
21
  config.everythingrb.extensions = nil # Default to loading all
22
22
 
23
23
  initializer "everythingrb.initialize" do
24
- # I learned that, whereas ActiveSupport is defined at this point, the core_ext files are
25
- # required later down the line.
26
- ActiveSupport.on_load(:active_record) do
24
+ ActiveSupport.on_load(:after_initialize) do
27
25
  require_relative "everythingrb/prelude"
28
26
 
29
27
  extensions = Rails.configuration.everythingrb.extensions
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: everythingrb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.3
4
+ version: 0.9.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bryan
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-06-01 00:00:00.000000000 Z
11
+ date: 2026-01-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ostruct