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 +4 -4
- data/CHANGELOG.md +37 -2
- data/README.md +65 -62
- data/flake.lock +3 -3
- data/flake.nix +1 -1
- data/lib/everythingrb/hash.rb +94 -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: 7361ef45ed502564c8e27fd3b2173702320f2076665ae9f495f5645fc7b57587
|
|
4
|
+
data.tar.gz: 411dc777df9851c7204903dde8ce12ce6951cf2308d9c02e6184bf0fef67a056
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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.
|
|
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
|

|
|
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,32 +301,13 @@ 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
|
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
431
|
+
params.compact_merge(filter: "active", search: nil)
|
|
449
432
|
# => {sort: "created_at", filter: "active"}
|
|
450
433
|
```
|
|
451
434
|
|
|
452
|
-
|
|
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:** [`
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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":
|
|
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"
|
|
@@ -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 [
|
|
98
|
-
# @yieldparam
|
|
99
|
-
# @yieldparam
|
|
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
|
|
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
|
-
|
|
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
|
-
# {}.
|
|
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
|
|
839
|
-
merge_if_values(other
|
|
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.
|
|
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
|
|
862
|
-
merge_if_values!(other
|
|
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
|
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.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Bryan
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2026-01-22 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: ostruct
|