everythingrb 0.8.3 → 0.9.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 +4 -4
- data/CHANGELOG.md +30 -2
- data/README.md +65 -43
- data/flake.lock +3 -3
- data/flake.nix +1 -1
- data/lib/everythingrb/hash.rb +89 -78
- data/lib/everythingrb/version.rb +1 -1
- data/lib/railtie.rb +1 -3
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cd5be3e26181ed61cc29aee8e316f3fd10d3bc9f5ea1d424069c4aefdc1c8125
|
4
|
+
data.tar.gz: 86517a10235873c3075237ff7f774315dc390d68fe3cf23ee751f15ca750508f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 643b0c34ffa81f136d1abb91def2e6bdbb65a00888a735cdef95950db9877d3ea0d06c22119c36f3909283985711792bc0530944107ed76d744742daff40dd3c
|
7
|
+
data.tar.gz: 40db61bcf4b8c77a791b07fa30fcdd8e4d3b1f0054ff9124030d15b67ad8194db312cbb6f1ba2261499e5431132952746a5ef184ced15e212401207c15bf6dd6
|
data/CHANGELOG.md
CHANGED
@@ -15,6 +15,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
15
15
|
### Removed
|
16
16
|
-->
|
17
17
|
|
18
|
+
## [0.9.0] - 12025-08-01
|
19
|
+
|
20
|
+
### Added
|
21
|
+
|
22
|
+
- **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.
|
23
|
+
- Added `with_index` parameter to `Hash#join_map` for API consistency with `Array#join_map`.
|
24
|
+
|
25
|
+
### Changed
|
26
|
+
|
27
|
+
- **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.
|
28
|
+
- **BREAKING: Renamed `Hash#merge_compact!` to `Hash#compact_merge!`** - In-place version follows the same naming convention.
|
29
|
+
|
30
|
+
### Removed
|
31
|
+
|
32
|
+
- **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.
|
33
|
+
- **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:
|
34
|
+
- `hash.select_values { |v| condition }` → `hash.select { |k, v| condition }`
|
35
|
+
- `hash.reject_values { |v| condition }` → `hash.reject { |k, v| condition }`
|
36
|
+
- For blank filtering: `hash.reject_values(&:blank?)` → `hash.compact_blank` (with ActiveSupport)
|
37
|
+
|
18
38
|
## [0.8.3] - 12025-05-31
|
19
39
|
|
20
40
|
### Added
|
@@ -22,6 +42,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
22
42
|
### Changed
|
23
43
|
|
24
44
|
- **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:
|
45
|
+
|
25
46
|
- `hash.reject_values(&:blank?)` → use `hash.compact_blank` instead
|
26
47
|
- `hash.select_values { |v| condition }` → use `hash.select { |k, v| condition }`
|
27
48
|
- `hash.reject_values { |v| condition }` → use `hash.reject { |k, v| condition }`
|
@@ -95,6 +116,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
95
116
|
- `Hash#reject_values!` - Same as `reject_values` but modifies the hash in place
|
96
117
|
- Added `filter_values` and `filter_values!` as aliases for `select_values` and `select_values!` respectively
|
97
118
|
- Added `Kernel#morph` as an alias for `then` (and `yield_self`) that better communicates transformation intent:
|
119
|
+
|
98
120
|
```ruby
|
99
121
|
# Instead of this:
|
100
122
|
value.then { |v| transform_somehow(v) }
|
@@ -124,12 +146,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
124
146
|
The chainable `.with_key` method approach has been replaced with a more straightforward parameter-based approach.
|
125
147
|
|
126
148
|
**Before:**
|
149
|
+
|
127
150
|
```ruby
|
128
151
|
hash.transform_values.with_key { |value, key| "#{key}:#{value}" }
|
129
152
|
hash.transform_values!.with_key { |value, key| "#{key}:#{value}" }
|
130
153
|
```
|
131
154
|
|
132
155
|
**After:**
|
156
|
+
|
133
157
|
```ruby
|
134
158
|
hash.transform_values(with_key: true) { |value, key| "#{key}:#{value}" }
|
135
159
|
hash.transform_values!(with_key: true) { |value, key| "#{key}:#{value}" }
|
@@ -148,11 +172,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
148
172
|
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
173
|
|
150
174
|
**Before:**
|
175
|
+
|
151
176
|
```ruby
|
152
177
|
hash.transform_values.with_key { |key, value| "#{key}: #{value}" }
|
153
178
|
```
|
154
179
|
|
155
180
|
**After:**
|
181
|
+
|
156
182
|
```ruby
|
157
183
|
hash.transform_values.with_key { |value, key| "#{key}: #{value}" }
|
158
184
|
```
|
@@ -164,11 +190,11 @@ This change aligns our method signatures with Ruby's conventions and matches our
|
|
164
190
|
- Added `Hash#transform` and `Hash#transform!` for transforming a hash's keys and values at the same time.
|
165
191
|
|
166
192
|
### Changed
|
193
|
+
|
167
194
|
- Changed parameter order in `Hash#transform_values.with_key` to yield `|value, key|` instead of `|key, value|` for consistency with Ruby conventions.
|
168
195
|
|
169
196
|
### Removed
|
170
197
|
|
171
|
-
|
172
198
|
## [0.4.0] - 12025-04-11
|
173
199
|
|
174
200
|
### Added
|
@@ -316,7 +342,9 @@ This change aligns our method signatures with Ruby's conventions and matches our
|
|
316
342
|
|
317
343
|
- Added alias `each` to `each_pair` in OpenStruct for better enumerable compatibility
|
318
344
|
|
319
|
-
[unreleased]: https://github.com/itsthedevman/everythingrb/compare/v0.
|
345
|
+
[unreleased]: https://github.com/itsthedevman/everythingrb/compare/v0.9.0...HEAD
|
346
|
+
[0.9.0]: https://github.com/itsthedevman/everythingrb/compare/v0.8.3...v0.9.0
|
347
|
+
[0.8.3]: https://github.com/itsthedevman/everythingrb/compare/v0.8.2...v0.8.3
|
320
348
|
[0.8.3]: https://github.com/itsthedevman/everythingrb/compare/v0.8.2...v0.8.3
|
321
349
|
[0.8.2]: https://github.com/itsthedevman/everythingrb/compare/v0.8.1...v0.8.2
|
322
350
|
[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
|

|
5
5
|
[](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
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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",
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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,7 +301,7 @@ end
|
|
283
301
|
["red", "blue", "green"].to_or_sentence # => "red, blue, or green"
|
284
302
|
```
|
285
303
|
|
286
|
-
|
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
|
|
@@ -308,7 +326,7 @@ stats = Hash.new_nested_hash(depth: 3)
|
|
308
326
|
# No need to initialize each level first!
|
309
327
|
```
|
310
328
|
|
311
|
-
|
329
|
+
_Methods used: [`new_nested_hash`](https://itsthedevman.com/docs/everythingrb/Hash.html#new_nested_hash-class_method)_
|
312
330
|
|
313
331
|
Transform values with access to their keys:
|
314
332
|
|
@@ -328,7 +346,7 @@ users.transform_values(with_key: true) { |v, k| "User #{k}: #{v[:name]}" }
|
|
328
346
|
# => {alice: "User alice: Alice", bob: "User bob: Bob"}
|
329
347
|
```
|
330
348
|
|
331
|
-
|
349
|
+
_Methods used: [`transform_values(with_key: true)`](https://itsthedevman.com/docs/everythingrb/Hash.html#transform_values-instance_method)_
|
332
350
|
|
333
351
|
Find values based on conditions:
|
334
352
|
|
@@ -349,7 +367,7 @@ users.values_where { |_k, v| v[:role] == "admin" }
|
|
349
367
|
# => [{name: "Alice", role: "admin"}, {name: "Charlie", role: "admin"}]
|
350
368
|
```
|
351
369
|
|
352
|
-
|
370
|
+
_Methods used: [`values_where`](https://itsthedevman.com/docs/everythingrb/Hash.html#values_where-instance_method)_
|
353
371
|
|
354
372
|
Just want the first match? Even simpler:
|
355
373
|
|
@@ -365,7 +383,7 @@ users.value_where { |_k, v| v[:role] == "admin" }
|
|
365
383
|
# => {name: "Alice", role: "admin"}
|
366
384
|
```
|
367
385
|
|
368
|
-
|
386
|
+
_Methods used: [`value_where`](https://itsthedevman.com/docs/everythingrb/Hash.html#value_where-instance_method)_
|
369
387
|
|
370
388
|
Rename keys while preserving order:
|
371
389
|
|
@@ -395,23 +413,7 @@ config.rename_keys(api_key: :key, timeout: :request_timeout)
|
|
395
413
|
# => {key: "secret", request_timeout: 30}
|
396
414
|
```
|
397
415
|
|
398
|
-
|
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)*
|
416
|
+
_Methods used: [`rename_keys`](https://itsthedevman.com/docs/everythingrb/Hash.html#rename_keys-instance_method)_
|
415
417
|
|
416
418
|
Conditionally merge hashes with clear intent:
|
417
419
|
|
@@ -430,7 +432,7 @@ user_params.merge_if(verified: true, admin: true) { |k, v| v == true && k == :ve
|
|
430
432
|
# => {name: "Alice", role: "user", verified: true}
|
431
433
|
```
|
432
434
|
|
433
|
-
|
435
|
+
_Methods used: [`merge_if`](https://itsthedevman.com/docs/everythingrb/Hash.html#merge_if-instance_method)_
|
434
436
|
|
435
437
|
The nil-filtering pattern we've all written dozens of times:
|
436
438
|
|
@@ -445,13 +447,33 @@ params.merge(filtered)
|
|
445
447
|
```ruby
|
446
448
|
# AFTER
|
447
449
|
params = {sort: "created_at"}
|
448
|
-
params.
|
450
|
+
params.compact_merge(filter: "active", search: nil)
|
449
451
|
# => {sort: "created_at", filter: "active"}
|
450
452
|
```
|
451
453
|
|
452
|
-
|
454
|
+
_Methods used: [`compact_merge`](https://itsthedevman.com/docs/everythingrb/Hash.html#compact_merge-instance_method)_
|
455
|
+
|
456
|
+
Filter out blank values too when ActiveSupport is loaded:
|
457
|
+
|
458
|
+
```ruby
|
459
|
+
# BEFORE
|
460
|
+
config = {api_key: "secret", timeout: 30}
|
461
|
+
user_settings = {timeout: "", retries: nil, debug: true, tags: []}
|
462
|
+
clean_settings = user_settings.reject { |k, v| v.blank? }
|
463
|
+
config.merge(clean_settings)
|
464
|
+
# => {api_key: "secret", timeout: 30, debug: true}
|
465
|
+
```
|
466
|
+
|
467
|
+
```ruby
|
468
|
+
# AFTER
|
469
|
+
config = {api_key: "secret", timeout: 30}
|
470
|
+
config.compact_blank_merge(timeout: "", retries: nil, debug: true, tags: [])
|
471
|
+
# => {api_key: "secret", timeout: 30, debug: true}
|
472
|
+
```
|
473
|
+
|
474
|
+
_Methods used: [`compact_blank_merge`](https://itsthedevman.com/docs/everythingrb/Hash.html#compact_blank_merge-instance_method)_
|
453
475
|
|
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), [`
|
476
|
+
**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), [`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
477
|
|
456
478
|
### Array Cleaning
|
457
479
|
|
@@ -469,7 +491,7 @@ data.drop_while(&:nil?).reverse.drop_while(&:nil?).reverse
|
|
469
491
|
[nil, nil, 1, nil, 2, nil, nil].trim_nils # => [1, nil, 2]
|
470
492
|
```
|
471
493
|
|
472
|
-
|
494
|
+
_Methods used: [`trim_nils`](https://itsthedevman.com/docs/everythingrb/Array.html#trim_nils-instance_method)_
|
473
495
|
|
474
496
|
With ActiveSupport, remove blank values too:
|
475
497
|
|
@@ -485,7 +507,7 @@ data.drop_while(&:blank?).reverse.drop_while(&:blank?).reverse
|
|
485
507
|
[nil, "", 1, "", 2, nil, ""].trim_blanks # => [1, "", 2]
|
486
508
|
```
|
487
509
|
|
488
|
-
|
510
|
+
_Methods used: [`trim_blanks`](https://itsthedevman.com/docs/everythingrb/Array.html#trim_blanks-instance_method)_
|
489
511
|
|
490
512
|
**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
513
|
|
@@ -528,7 +550,7 @@ Time.now.in_quotes # => "\"2025-05-04 12:34:56 +0000\""
|
|
528
550
|
message = "You selected #{selection.in_quotes}"
|
529
551
|
```
|
530
552
|
|
531
|
-
|
553
|
+
_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
554
|
|
533
555
|
Convert strings to camelCase with ease:
|
534
556
|
|
@@ -555,7 +577,7 @@ camel_case
|
|
555
577
|
"please-WAIT while_loading...".to_camelcase # => "PleaseWaitWhileLoading"
|
556
578
|
```
|
557
579
|
|
558
|
-
|
580
|
+
_Methods used: [`to_camelcase`](https://itsthedevman.com/docs/everythingrb/String.html#to_camelcase-instance_method)_
|
559
581
|
|
560
582
|
**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
583
|
|
@@ -590,7 +612,7 @@ user.admin = true
|
|
590
612
|
user.admin? # => true
|
591
613
|
```
|
592
614
|
|
593
|
-
|
615
|
+
_Methods used: [`attr_predicate`](https://itsthedevman.com/docs/everythingrb/Module.html#attr_predicate-instance_method)_
|
594
616
|
|
595
617
|
Works with Data objects too:
|
596
618
|
|
@@ -610,7 +632,7 @@ person = Person.new(active: false)
|
|
610
632
|
person.active? # => false
|
611
633
|
```
|
612
634
|
|
613
|
-
|
635
|
+
_Methods used: [`attr_predicate`](https://itsthedevman.com/docs/everythingrb/Module.html#attr_predicate-instance_method)_
|
614
636
|
|
615
637
|
**Extensions:** [`attr_predicate`](https://itsthedevman.com/docs/everythingrb/Module.html#attr_predicate-instance_method)
|
616
638
|
|
@@ -628,7 +650,7 @@ result = value.then { |v| transform_it(v) }
|
|
628
650
|
result = value.morph { |v| transform_it(v) }
|
629
651
|
```
|
630
652
|
|
631
|
-
|
653
|
+
_Methods used: [`morph`](https://itsthedevman.com/docs/everythingrb/Kernel.html#morph-instance_method)_
|
632
654
|
|
633
655
|
**Extensions:** [`morph`](https://itsthedevman.com/docs/everythingrb/Kernel.html#morph-instance_method) (alias for `then`/`yield_self`)
|
634
656
|
|
data/flake.lock
CHANGED
@@ -20,11 +20,11 @@
|
|
20
20
|
},
|
21
21
|
"nixpkgs": {
|
22
22
|
"locked": {
|
23
|
-
"lastModified":
|
24
|
-
"narHash": "sha256-
|
23
|
+
"lastModified": 1750506804,
|
24
|
+
"narHash": "sha256-VLFNc4egNjovYVxDGyBYTrvVCgDYgENp5bVi9fPTDYc=",
|
25
25
|
"owner": "NixOS",
|
26
26
|
"repo": "nixpkgs",
|
27
|
-
"rev": "
|
27
|
+
"rev": "4206c4cb56751df534751b058295ea61357bbbaa",
|
28
28
|
"type": "github"
|
29
29
|
},
|
30
30
|
"original": {
|
data/flake.nix
CHANGED
data/lib/everythingrb/hash.rb
CHANGED
@@ -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
|
-
# - #
|
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"
|
@@ -93,25 +94,37 @@ class Hash
|
|
93
94
|
# Combines filter_map and join operations
|
94
95
|
#
|
95
96
|
# @param join_with [String] The delimiter to join elements with (defaults to empty string)
|
97
|
+
# @param with_index [Boolean] Whether to include the index in the block (defaults to false)
|
96
98
|
#
|
97
|
-
# @yield [
|
98
|
-
# @yieldparam
|
99
|
-
# @yieldparam
|
99
|
+
# @yield [key_value_pair, index] Block that filters and transforms hash entries
|
100
|
+
# @yieldparam key_value_pair [Array] Two-element array containing [key, value] (can be destructured as |key, value|)
|
101
|
+
# @yieldparam index [Integer] The index of the current entry (only if with_index: true)
|
100
102
|
#
|
101
103
|
# @return [String] Joined string of filtered and transformed entries
|
102
104
|
#
|
103
|
-
# @example
|
105
|
+
# @example Basic usage without index
|
104
106
|
# { a: 1, b: nil, c: 2, d: nil, e: 3 }.join_map(", ") { |k, v| "#{k}-#{v}" if v }
|
105
107
|
# # => "a-1, c-2, e-3"
|
106
108
|
#
|
107
|
-
# @example
|
109
|
+
# @example With index for position-aware formatting
|
110
|
+
# users = {alice: "Alice", bob: "Bob", charlie: "Charlie"}
|
111
|
+
# users.join_map(", ", with_index: true) do |(k, v), i|
|
112
|
+
# "#{i + 1}. #{v}"
|
113
|
+
# end
|
114
|
+
# # => "1. Alice, 2. Bob, 3. Charlie"
|
115
|
+
#
|
116
|
+
# @example Without a block (default behavior)
|
108
117
|
# { a: 1, b: nil, c: 2 }.join_map(" ")
|
109
118
|
# # => "a 1 b c 2"
|
110
119
|
#
|
111
|
-
def join_map(join_with = "", &block)
|
120
|
+
def join_map(join_with = "", with_index: false, &block)
|
112
121
|
block = ->(kv_pair) { kv_pair.compact } if block.nil?
|
113
122
|
|
114
|
-
|
123
|
+
if with_index
|
124
|
+
filter_map.with_index(&block).join(join_with)
|
125
|
+
else
|
126
|
+
filter_map(&block).join(join_with)
|
127
|
+
end
|
115
128
|
end
|
116
129
|
|
117
130
|
#
|
@@ -661,70 +674,6 @@ class Hash
|
|
661
674
|
self
|
662
675
|
end
|
663
676
|
|
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
677
|
#
|
729
678
|
# Conditionally merges key-value pairs from another hash based on a block
|
730
679
|
#
|
@@ -828,15 +777,15 @@ class Hash
|
|
828
777
|
# email = nil
|
829
778
|
# name = "Alice"
|
830
779
|
#
|
831
|
-
# {}.
|
780
|
+
# {}.compact_merge(
|
832
781
|
# id: user_id,
|
833
782
|
# email: email,
|
834
783
|
# name: name
|
835
784
|
# )
|
836
785
|
# # => {id: 42, name: "Alice"}
|
837
786
|
#
|
838
|
-
def
|
839
|
-
merge_if_values(other
|
787
|
+
def compact_merge(other = {})
|
788
|
+
merge_if_values(other) { |i| i.itself }
|
840
789
|
end
|
841
790
|
|
842
791
|
#
|
@@ -851,14 +800,76 @@ class Hash
|
|
851
800
|
#
|
852
801
|
# @example Merge only non-nil values in place
|
853
802
|
# params = {format: "json"}
|
854
|
-
# params.
|
803
|
+
# params.compact_merge!(
|
855
804
|
# page: 1,
|
856
805
|
# per_page: nil,
|
857
806
|
# sort: "created_at"
|
858
807
|
# )
|
859
808
|
# # => {format: "json", page: 1, sort: "created_at"}
|
860
809
|
#
|
861
|
-
def
|
862
|
-
merge_if_values!(other
|
810
|
+
def compact_merge!(other = {})
|
811
|
+
merge_if_values!(other) { |i| i.itself }
|
812
|
+
end
|
813
|
+
|
814
|
+
# ActiveSupport integrations
|
815
|
+
if defined?(ActiveSupport)
|
816
|
+
#
|
817
|
+
# Merges only present (non-blank) values from another hash
|
818
|
+
#
|
819
|
+
# This method merges key-value pairs from another hash, but only includes
|
820
|
+
# values that are present according to ActiveSupport's definition (not nil,
|
821
|
+
# not empty strings, not empty arrays, etc.).
|
822
|
+
#
|
823
|
+
# @param other [Hash] The hash to merge from
|
824
|
+
#
|
825
|
+
# @return [Hash] A new hash with present values merged
|
826
|
+
#
|
827
|
+
# @note Only available when ActiveSupport is loaded
|
828
|
+
#
|
829
|
+
# @example Building API responses without blank values
|
830
|
+
# user_data = {name: "Alice", role: "admin"}
|
831
|
+
# user_data.compact_blank_merge(
|
832
|
+
# bio: "", # excluded - blank string
|
833
|
+
# tags: [], # excluded - empty array
|
834
|
+
# email: "a@example.com", # included - present
|
835
|
+
# phone: nil # excluded - nil
|
836
|
+
# )
|
837
|
+
# # => {name: "Alice", role: "admin", email: "a@example.com"}
|
838
|
+
#
|
839
|
+
# @example Configuration merging with blank filtering
|
840
|
+
# defaults = {timeout: 30, retries: 3}
|
841
|
+
# user_config = {timeout: "", retries: 5, debug: true}
|
842
|
+
# defaults.compact_blank_merge(user_config)
|
843
|
+
# # => {timeout: 30, retries: 5, debug: true}
|
844
|
+
#
|
845
|
+
def compact_blank_merge(other = {})
|
846
|
+
merge_if_values(other) { |i| i.present? }
|
847
|
+
end
|
848
|
+
|
849
|
+
#
|
850
|
+
# Merges only present (non-blank) values from another hash, in place
|
851
|
+
#
|
852
|
+
# This method merges key-value pairs from another hash into the current hash,
|
853
|
+
# but only includes values that are present according to ActiveSupport's
|
854
|
+
# definition (not nil, not empty strings, not empty arrays, etc.).
|
855
|
+
#
|
856
|
+
# @param other [Hash] The hash to merge from
|
857
|
+
#
|
858
|
+
# @return [self] The modified hash
|
859
|
+
#
|
860
|
+
# @note Only available when ActiveSupport is loaded
|
861
|
+
#
|
862
|
+
# @example Building parameters while excluding blanks
|
863
|
+
# params = {action: "search", format: "json"}
|
864
|
+
# params.compact_blank_merge!(
|
865
|
+
# query: "", # excluded - blank
|
866
|
+
# page: 2, # included - present
|
867
|
+
# tags: [] # excluded - empty array
|
868
|
+
# )
|
869
|
+
# # => {action: "search", format: "json", page: 2}
|
870
|
+
#
|
871
|
+
def compact_blank_merge!(other = {})
|
872
|
+
merge_if_values!(other) { |i| i.present? }
|
873
|
+
end
|
863
874
|
end
|
864
875
|
end
|
data/lib/everythingrb/version.rb
CHANGED
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
|
-
|
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.
|
4
|
+
version: 0.9.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bryan
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-
|
11
|
+
date: 2025-08-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ostruct
|