everythingrb 0.2.4 → 0.3.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 +32 -2
- data/README.md +62 -439
- data/lib/everythingrb/core/array.rb +153 -0
- data/lib/everythingrb/core/enumerable.rb +45 -0
- data/lib/everythingrb/core/hash.rb +227 -0
- data/lib/everythingrb/version.rb +1 -1
- 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: 140ec6261f323365ad8eeda8cd09fb76f81be1d53f3114611b20b22bf678c2c0
|
4
|
+
data.tar.gz: 575463b3302516a32606578fa3781781cf5d52652eb58e488d2e8963189409b7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f5aa7a5cc9c94019c2d91486a726b5778d9cc703d3e2b2b5d7fea25ba50f6d30465f2faace9de8eb9b67a1bb0e6ab012a92edb7a86c22d30f6d9bd94dd281e52
|
7
|
+
data.tar.gz: 1e79f1bf2eb2976973a0a98e9036392c983b0b0c7966964ab61844c6ad5b79a71cb589809d146a9e55735c1d95d4409b22ca6aa61530165a1685d501ccf0ab2d
|
data/CHANGELOG.md
CHANGED
@@ -23,6 +23,34 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
23
23
|
|
24
24
|
### Removed
|
25
25
|
|
26
|
+
## [0.3.0] - 12025-04-09
|
27
|
+
|
28
|
+
### Added
|
29
|
+
|
30
|
+
- Added `Array#to_or_sentence`, creates a sentence with "or" connector between items
|
31
|
+
- Added `#with_key` method to `Hash#transform_values` and `Hash#transform_values!`, grants access to both keys and values during transformations
|
32
|
+
- Added `Array#to_deep_h` and `Hash#to_deep_h`, recursively converts underlying values to hashes
|
33
|
+
- Added `Enumerable#group_by_key`, group an array of hashes by their keys
|
34
|
+
- Added `Hash#new_nested_hash`, creates a new Hash that automatically initializes the value to a hash
|
35
|
+
- Added `Hash#value_where` and `Hash#values_where`, easily find values in a hash based on key-value conditions
|
36
|
+
|
37
|
+
### Changed
|
38
|
+
|
39
|
+
### Removed
|
40
|
+
|
41
|
+
## [0.2.5] - 12025-03-29
|
42
|
+
|
43
|
+
### Added
|
44
|
+
|
45
|
+
- New array trimming methods that preserve internal structure:
|
46
|
+
- `compact_prefix` - Removes nil values from the beginning of an array
|
47
|
+
- `compact_suffix` - Removes nil values from the end of an array
|
48
|
+
- `trim_nils` - Removes nil values from both ends of an array
|
49
|
+
- ActiveSupport integration with blank-aware versions:
|
50
|
+
- `compact_blank_prefix` - Removes blank values from the beginning
|
51
|
+
- `compact_blank_suffix` - Removes blank values from the end
|
52
|
+
- `trim_blanks` - Removes blank values from both ends
|
53
|
+
|
26
54
|
## [0.2.4] - 12025-03-20
|
27
55
|
|
28
56
|
### Changed
|
@@ -111,8 +139,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
111
139
|
|
112
140
|
- Added alias `each` to `each_pair` in OpenStruct for better enumerable compatibility
|
113
141
|
|
114
|
-
[unreleased]: https://github.com/itsthedevman/everythingrb/compare/v0.
|
115
|
-
[0.
|
142
|
+
[unreleased]: https://github.com/itsthedevman/everythingrb/compare/v0.3.0...HEAD
|
143
|
+
[0.3.0]: https://github.com/itsthedevman/everythingrb/compare/v0.2.5...v0.3.0
|
144
|
+
[0.2.5]: https://github.com/itsthedevman/everythingrb/compare/v0.2.4...v0.2.5
|
145
|
+
[0.2.4]: https://github.com/itsthedevman/everythingrb/compare/v0.2.3...v0.2.4
|
116
146
|
[0.2.3]: https://github.com/itsthedevman/everythingrb/compare/v0.2.2...v0.2.3
|
117
147
|
[0.2.2]: https://github.com/itsthedevman/everythingrb/compare/v0.2.1...v0.2.2
|
118
148
|
[0.2.1]: https://github.com/itsthedevman/everythingrb/compare/v0.2.0...v0.2.1
|
data/README.md
CHANGED
@@ -4,96 +4,52 @@
|
|
4
4
|

|
5
5
|
[](https://github.com/everythingrb/sortsmith/actions/workflows/main.yml)
|
6
6
|
|
7
|
-
Super handy extensions to Ruby core classes that
|
7
|
+
Super handy extensions to Ruby core classes that make your code more expressive, readable, and fun to write.
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
[bryan@itsthedevman.com](mailto:bryan@itsthedevman.com)
|
9
|
+
```ruby
|
10
|
+
# Instead of this:
|
11
|
+
users = [{ name: "Alice", role: "admin" }, { name: "Bob", role: "user" }]
|
12
|
+
admin_names = users.select { |u| u[:role] == "admin" }.map { |u| u[:name] }.join(", ")
|
14
13
|
|
15
|
-
#
|
16
|
-
|
17
|
-
|
18
|
-
- [Compatibility](#compatibility)
|
19
|
-
- [Installation](#installation)
|
20
|
-
- [Features](#features)
|
21
|
-
- [Data Structure Conversions](#data-structure-conversions)
|
22
|
-
- [Collection Processing](#collection-processing)
|
23
|
-
- [JSON & String Handling](#json--string-handling)
|
24
|
-
- [Object Freezing](#object-freezing)
|
25
|
-
- [Predicate Methods](#predicate-methods)
|
26
|
-
- [Core Extensions](#core-extensions)
|
27
|
-
- [Array](#array)
|
28
|
-
- [Enumerable](#enumerable)
|
29
|
-
- [Hash](#hash)
|
30
|
-
- [Module](#module)
|
31
|
-
- [OpenStruct](#openstruct)
|
32
|
-
- [String](#string)
|
33
|
-
- [Symbol](#symbol)
|
34
|
-
- [Advanced Usage](#advanced-usage)
|
35
|
-
- [Requirements](#requirements)
|
36
|
-
- [Contributing](#contributing)
|
37
|
-
- [License](#license)
|
38
|
-
- [Changelog](#changelog)
|
39
|
-
- [Credits](#credits)
|
40
|
-
|
41
|
-
Also see: [API Documentation](https://itsthedevman.com/docs/everythingrb)
|
42
|
-
|
43
|
-
## Introduction
|
44
|
-
|
45
|
-
EverythingRB adds powerful, intuitive extensions to Ruby's core classes that help you write cleaner, more expressive code. It focuses on common patterns that typically require multiple method calls or temporary variables, turning them into single fluid operations.
|
46
|
-
|
47
|
-
Whether you're transforming data, working with JSON, or building complex object structures, EverythingRB makes your code more readable and maintainable with minimal effort.
|
48
|
-
|
49
|
-
## Compatibility
|
50
|
-
|
51
|
-
Currently tested on:
|
52
|
-
- MRI Ruby 3.2+
|
53
|
-
- NixOS (see `flake.nix` for details)
|
14
|
+
# Write this:
|
15
|
+
users.join_map(", ") { |u| u[:name] if u[:role] == "admin" }
|
16
|
+
```
|
54
17
|
|
55
18
|
## Installation
|
56
19
|
|
57
|
-
Add this line to your application's Gemfile:
|
58
|
-
|
59
20
|
```ruby
|
21
|
+
# In your Gemfile
|
60
22
|
gem "everythingrb"
|
61
|
-
```
|
62
|
-
|
63
|
-
And then execute:
|
64
23
|
|
65
|
-
|
66
|
-
|
24
|
+
# Or install manually
|
25
|
+
gem install everythingrb
|
67
26
|
```
|
68
27
|
|
69
|
-
|
28
|
+
## What's Included
|
70
29
|
|
71
|
-
|
72
|
-
$ gem install everythingrb
|
73
|
-
```
|
74
|
-
|
75
|
-
## Features
|
30
|
+
EverythingRB extends Ruby's core classes with intuitive methods that simplify common patterns.
|
76
31
|
|
77
32
|
### Data Structure Conversions
|
78
33
|
|
79
|
-
|
34
|
+
Convert between data structures with ease:
|
80
35
|
|
81
36
|
```ruby
|
82
|
-
# Convert
|
37
|
+
# Convert hashes to more convenient structures
|
83
38
|
config = { server: { host: "example.com", port: 443 } }.to_ostruct
|
84
39
|
config.server.host # => "example.com"
|
85
40
|
|
86
41
|
# Parse JSON directly to your preferred structure
|
87
42
|
'{"user":{"name":"Alice"}}'.to_istruct.user.name # => "Alice"
|
88
|
-
'{"items":[1,2,3]}'.to_struct.items # => [1, 2, 3]
|
89
43
|
```
|
90
44
|
|
45
|
+
**Extensions:** `to_struct`, `to_ostruct`, `to_istruct`, `to_h`, `to_deep_h`
|
46
|
+
|
91
47
|
### Collection Processing
|
92
48
|
|
93
|
-
|
49
|
+
Extract and transform data elegantly:
|
94
50
|
|
95
51
|
```ruby
|
96
|
-
# Extract
|
52
|
+
# Extract data from arrays of hashes in one step
|
97
53
|
users = [{ name: "Alice", roles: ["admin"] }, { name: "Bob", roles: ["user"] }]
|
98
54
|
users.key_map(:name) # => ["Alice", "Bob"]
|
99
55
|
users.dig_map(:roles, 0) # => ["admin", "user"]
|
@@ -101,27 +57,16 @@ users.dig_map(:roles, 0) # => ["admin", "user"]
|
|
101
57
|
# Filter, map, and join in a single operation
|
102
58
|
[1, 2, nil, 3, 4].join_map(" | ") { |n| "Item #{n}" if n&.odd? }
|
103
59
|
# => "Item 1 | Item 3"
|
104
|
-
```
|
105
|
-
|
106
|
-
### JSON & String Handling
|
107
60
|
|
108
|
-
|
109
|
-
|
110
|
-
```ruby
|
111
|
-
# Parse JSON with symbolized keys
|
112
|
-
'{"name": "Alice"}'.to_h # => { name: "Alice" }
|
113
|
-
|
114
|
-
# Recursively parse nested JSON strings
|
115
|
-
nested = '{"user":"{\"profile\":\"{\\\"name\\\":\\\"Bob\\\"}\"}"}'
|
116
|
-
nested.to_deep_h # => { user: { profile: { name: "Bob" } } }
|
117
|
-
|
118
|
-
# Format strings with quotes
|
119
|
-
"hello".with_quotes # => "\"hello\""
|
61
|
+
# With ActiveSupport loaded, join arrays in natural language with "or"
|
62
|
+
["red", "blue", "green"].to_or_sentence # => "red, blue, or green"
|
120
63
|
```
|
121
64
|
|
122
|
-
|
65
|
+
**Extensions:** `join_map`, `key_map`, `dig_map`, `to_or_sentence`, `group_by_key`
|
66
|
+
|
67
|
+
### Object Protection
|
123
68
|
|
124
|
-
|
69
|
+
Prevent unwanted modifications with a single call:
|
125
70
|
|
126
71
|
```ruby
|
127
72
|
config = {
|
@@ -133,385 +78,71 @@ config = {
|
|
133
78
|
|
134
79
|
# Everything is frozen!
|
135
80
|
config.frozen? # => true
|
136
|
-
config[:api].frozen? # => true
|
137
|
-
config[:api][:endpoints].frozen? # => true
|
138
81
|
config[:api][:endpoints][0].frozen? # => true
|
139
82
|
```
|
140
83
|
|
141
|
-
|
84
|
+
**Extensions:** `deep_freeze`
|
142
85
|
|
143
|
-
|
86
|
+
### Hash Convenience
|
144
87
|
|
145
|
-
|
146
|
-
class User
|
147
|
-
attr_accessor :admin, :verified
|
148
|
-
attr_predicate :admin, :verified
|
149
|
-
end
|
150
|
-
|
151
|
-
user = User.new
|
152
|
-
user.admin = true
|
153
|
-
user.admin? # => true
|
154
|
-
user.verified? # => false
|
155
|
-
|
156
|
-
# Works with Struct and Data objects too
|
157
|
-
Person = Struct.new(:active)
|
158
|
-
Person.attr_predicate(:active)
|
159
|
-
|
160
|
-
person = Person.new(true)
|
161
|
-
person.active? # => true
|
162
|
-
```
|
163
|
-
|
164
|
-
**ActiveSupport Integration:** When ActiveSupport is loaded, predicate methods automatically use `present?` instead of just checking truthiness:
|
88
|
+
Work with hashes more intuitively:
|
165
89
|
|
166
90
|
```ruby
|
167
|
-
#
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
end
|
91
|
+
# Create deeply nested structures without initialization
|
92
|
+
stats = Hash.new_nested_hash
|
93
|
+
stats[:server][:region][:us_east][:errors] << "Connection timeout"
|
94
|
+
# No need to initialize each level first!
|
172
95
|
|
173
|
-
|
174
|
-
|
175
|
-
product.tags? # => false (empty array is not "present")
|
96
|
+
# Transform values with access to keys
|
97
|
+
users.transform_values.with_key { |k, v| "User #{k}: #{v[:name]}" }
|
176
98
|
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
product.category = ""
|
181
|
-
product.category? # => false (blank string is not "present")
|
99
|
+
# Find values based on conditions
|
100
|
+
users.values_where { |k, v| v[:role] == "admin" }
|
182
101
|
```
|
183
102
|
|
184
|
-
|
103
|
+
**Extensions:** `new_nested_hash`, `with_key`, `value_where`, `values_where`
|
185
104
|
|
186
|
-
### Array
|
105
|
+
### Array Cleaning
|
187
106
|
|
188
|
-
|
189
|
-
Combines `filter_map` and `join` operations in one convenient method. Optionally provides the index to the block.
|
107
|
+
Clean up array boundaries while preserving internal structure:
|
190
108
|
|
191
109
|
```ruby
|
192
|
-
#
|
193
|
-
[1, 2, nil,
|
194
|
-
# => "1 3"
|
195
|
-
|
196
|
-
# With index
|
197
|
-
["a", "b", "c"].join_map(", ", with_index: true) { |char, i| "#{i}:#{char}" }
|
198
|
-
# => "0:a, 1:b, 2:c"
|
110
|
+
# Remove nil values from the beginning/end
|
111
|
+
[nil, nil, 1, nil, 2, nil, nil].trim_nils # => [1, nil, 2]
|
199
112
|
|
200
|
-
#
|
201
|
-
[1, 2, nil,
|
202
|
-
# => "1, 2, 3"
|
113
|
+
# With ActiveSupport, remove blank values
|
114
|
+
[nil, "", 1, "", 2, nil, ""].trim_blanks # => [1, "", 2]
|
203
115
|
```
|
204
116
|
|
205
|
-
|
206
|
-
Extracts a specific key from each hash in an array.
|
117
|
+
**Extensions:** `trim_nils`, `compact_prefix`, `compact_suffix`, `trim_blanks` (with ActiveSupport)
|
207
118
|
|
208
|
-
|
209
|
-
users = [
|
210
|
-
{ name: "Alice", age: 30 },
|
211
|
-
{ name: "Bob", age: 25 }
|
212
|
-
]
|
213
|
-
|
214
|
-
users.key_map(:name)
|
215
|
-
# => ["Alice", "Bob"]
|
216
|
-
```
|
119
|
+
### Boolean Methods
|
217
120
|
|
218
|
-
|
219
|
-
Extracts nested values from each hash in an array using the `dig` method.
|
220
|
-
|
221
|
-
```ruby
|
222
|
-
data = [
|
223
|
-
{ user: { profile: { name: "Alice" } } },
|
224
|
-
{ user: { profile: { name: "Bob" } } }
|
225
|
-
]
|
226
|
-
|
227
|
-
data.dig_map(:user, :profile, :name)
|
228
|
-
# => ["Alice", "Bob"]
|
229
|
-
```
|
230
|
-
|
231
|
-
#### `deep_freeze`
|
232
|
-
Recursively freezes an array and all of its nested elements.
|
233
|
-
|
234
|
-
```ruby
|
235
|
-
array = ["hello", { name: "Alice" }, [1, 2, 3]]
|
236
|
-
array.deep_freeze
|
237
|
-
# => All elements and nested structures are now frozen
|
238
|
-
```
|
239
|
-
|
240
|
-
### Enumerable
|
241
|
-
|
242
|
-
#### `join_map`
|
243
|
-
Combines filtering, mapping and joining operations into one convenient method.
|
244
|
-
|
245
|
-
```ruby
|
246
|
-
# Basic usage with arrays
|
247
|
-
[1, 2, nil, 3].join_map(", ") { |n| n&.to_s if n&.odd? }
|
248
|
-
# => "1, 3"
|
249
|
-
|
250
|
-
# Works with any Enumerable
|
251
|
-
(1..10).join_map(" | ") { |n| "num#{n}" if n.even? }
|
252
|
-
# => "num2 | num4 | num6 | num8 | num10"
|
253
|
-
|
254
|
-
# Supports with_index option
|
255
|
-
%w[a b c].join_map("-", with_index: true) { |char, i| "#{i}:#{char}" }
|
256
|
-
# => "0:a-1:b-2:c"
|
257
|
-
```
|
258
|
-
|
259
|
-
### Hash
|
260
|
-
|
261
|
-
#### `to_struct`
|
262
|
-
Recursively converts a hash into a Struct, including nested hashes and arrays.
|
263
|
-
|
264
|
-
```ruby
|
265
|
-
hash = { user: { name: "Alice", roles: ["admin", "user"] } }
|
266
|
-
struct = hash.to_struct
|
267
|
-
struct.class # => Struct
|
268
|
-
struct.user.name # => "Alice"
|
269
|
-
struct.user.roles # => ["admin", "user"]
|
270
|
-
```
|
271
|
-
|
272
|
-
#### `to_ostruct`
|
273
|
-
Recursively converts a hash into an OpenStruct, including nested hashes and arrays.
|
274
|
-
|
275
|
-
```ruby
|
276
|
-
hash = { config: { api_key: "secret", endpoints: ["v1", "v2"] } }
|
277
|
-
config = hash.to_ostruct
|
278
|
-
config.class # => OpenStruct
|
279
|
-
config.config.api_key # => "secret"
|
280
|
-
```
|
281
|
-
|
282
|
-
#### `to_istruct`
|
283
|
-
Recursively converts a hash into an immutable Data structure.
|
284
|
-
|
285
|
-
```ruby
|
286
|
-
hash = { person: { name: "Bob", age: 30 } }
|
287
|
-
data = hash.to_istruct
|
288
|
-
data.class # => Data
|
289
|
-
data.person.name # => "Bob"
|
290
|
-
```
|
291
|
-
|
292
|
-
#### `join_map`
|
293
|
-
Similar to Array#join_map but operates on hash values.
|
294
|
-
|
295
|
-
```ruby
|
296
|
-
{ a: 1, b: 2, c: nil, d: 3 }.join_map(" ") { |k, v| [k, v] if v }
|
297
|
-
# => "a 1 b 2 d 3"
|
298
|
-
```
|
299
|
-
|
300
|
-
#### `deep_freeze`
|
301
|
-
Recursively freezes a hash and all of its nested values.
|
302
|
-
|
303
|
-
```ruby
|
304
|
-
hash = { user: { name: "Alice", roles: ["admin", "user"] } }
|
305
|
-
hash.deep_freeze
|
306
|
-
# => Hash and all nested structures are now frozen
|
307
|
-
```
|
308
|
-
|
309
|
-
### Module
|
310
|
-
|
311
|
-
#### `attr_predicate`
|
312
|
-
Creates predicate (boolean) methods for instance variables.
|
121
|
+
Create predicate methods with minimal code:
|
313
122
|
|
314
123
|
```ruby
|
315
124
|
class User
|
316
|
-
|
317
|
-
attr_predicate :admin
|
125
|
+
attr_accessor :admin
|
126
|
+
attr_predicate :admin
|
318
127
|
end
|
319
128
|
|
320
129
|
user = User.new
|
321
|
-
user.active? # => false
|
322
130
|
user.admin = true
|
323
|
-
user.admin?
|
324
|
-
```
|
325
|
-
|
326
|
-
### OpenStruct
|
327
|
-
|
328
|
-
#### `each`
|
329
|
-
Alias for `each_pair`.
|
330
|
-
|
331
|
-
```ruby
|
332
|
-
struct = OpenStruct.new(a: 1, b: 2)
|
333
|
-
struct.each { |key, value| puts "#{key}: #{value}" }
|
334
|
-
```
|
335
|
-
|
336
|
-
#### `map`
|
337
|
-
Maps over OpenStruct entries.
|
338
|
-
|
339
|
-
```ruby
|
340
|
-
struct = OpenStruct.new(a: 1, b: 2)
|
341
|
-
struct.map { |key, value| [key, value * 2] }
|
342
|
-
# => [[:a, 2], [:b, 4]]
|
343
|
-
```
|
344
|
-
|
345
|
-
#### `filter_map`
|
346
|
-
Combines `map` and `compact` operations in one convenient method.
|
347
|
-
|
348
|
-
```ruby
|
349
|
-
struct = OpenStruct.new(a: 1, b: nil, c: 2)
|
350
|
-
struct.filter_map { |key, value| value * 2 if value }
|
351
|
-
# => [2, 4]
|
352
|
-
```
|
353
|
-
|
354
|
-
#### `join_map`
|
355
|
-
Combines `filter_map` and `join` operations in one convenient method.
|
356
|
-
|
357
|
-
```ruby
|
358
|
-
config = OpenStruct.new(
|
359
|
-
alice: {roles: ["admin"]},
|
360
|
-
bob: {roles: ["user"]},
|
361
|
-
carol: {roles: ["admin"]}
|
362
|
-
)
|
363
|
-
|
364
|
-
config.join_map(", ") { |key, value| key if value[:roles].include?("admin") }
|
365
|
-
# => "alice, carol"
|
366
|
-
```
|
367
|
-
|
368
|
-
### String
|
369
|
-
|
370
|
-
#### `to_h` / `to_a`
|
371
|
-
Parses JSON string into a Ruby Hash or Array.
|
372
|
-
|
373
|
-
```ruby
|
374
|
-
'{"name": "Alice"}'.to_h
|
375
|
-
# => {name: "Alice"}
|
376
|
-
|
377
|
-
'["Alice"]'.to_h # Or you can use #to_a for different readability
|
378
|
-
# => ["Alice"]
|
379
|
-
```
|
380
|
-
|
381
|
-
#### `to_istruct`
|
382
|
-
Parses JSON string into an immutable Data structure.
|
383
|
-
|
384
|
-
```ruby
|
385
|
-
'{"user": {"name": "Alice"}}'.to_istruct
|
386
|
-
# => #<data user=#<data name="Alice">>
|
387
|
-
```
|
388
|
-
|
389
|
-
#### `to_ostruct`
|
390
|
-
Parses JSON string into an OpenStruct.
|
391
|
-
|
392
|
-
```ruby
|
393
|
-
'{"user": {"name": "Alice"}}'.to_ostruct
|
394
|
-
# => #<OpenStruct user=#<OpenStruct name="Alice">>
|
395
|
-
```
|
396
|
-
|
397
|
-
#### `to_struct`
|
398
|
-
Parses JSON string into a Struct.
|
399
|
-
|
400
|
-
```ruby
|
401
|
-
'{"user": {"name": "Alice"}}'.to_struct
|
402
|
-
# => #<struct user=#<struct name="Alice">>
|
403
|
-
```
|
404
|
-
|
405
|
-
#### `to_deep_h`
|
406
|
-
Recursively parses nested JSON strings within a structure.
|
407
|
-
|
408
|
-
```ruby
|
409
|
-
nested_json = {
|
410
|
-
users: [
|
411
|
-
{name: "Alice", roles: ["admin", "user"]}.to_json,
|
412
|
-
]
|
413
|
-
}.to_json
|
414
|
-
|
415
|
-
nested_json.to_deep_h
|
416
|
-
# => {users: [{name: "Alice", roles: ["admin", "user"]}]}
|
417
|
-
```
|
418
|
-
|
419
|
-
#### `with_quotes` / `in_quotes`
|
420
|
-
Wraps the string in double quotes
|
421
|
-
|
422
|
-
```ruby
|
423
|
-
"Hello World".with_quotes
|
424
|
-
# => "\"Hello World\""
|
425
|
-
```
|
426
|
-
|
427
|
-
### Symbol
|
428
|
-
|
429
|
-
#### `with_quotes` / `in_quotes`
|
430
|
-
Wraps the symbol in double quotes
|
431
|
-
|
432
|
-
```ruby
|
433
|
-
:hello_world.with_quotes
|
434
|
-
# => :"\"hello_world\""
|
435
|
-
```
|
436
|
-
|
437
|
-
## Advanced Usage
|
438
|
-
|
439
|
-
See how EverythingRB transforms your code from verbose to elegant:
|
440
|
-
|
441
|
-
### Extracting Data from Nested JSON
|
442
|
-
|
443
|
-
**Before:**
|
444
|
-
```ruby
|
445
|
-
# Standard Ruby approach
|
446
|
-
json_data = '[{"user":{"name":"Alice","role":"admin"}},{"user":{"name":"Bob","role":"guest"}}]'
|
447
|
-
|
448
|
-
parsed_data = JSON.parse(json_data, symbolize_names: true)
|
449
|
-
names = parsed_data.map { |item| item[:user][:name] }
|
450
|
-
result = names.join(", ")
|
451
|
-
# => "Alice, Bob"
|
452
|
-
```
|
453
|
-
|
454
|
-
**After:**
|
455
|
-
```ruby
|
456
|
-
# With EverythingRB
|
457
|
-
json_data = '[{"user":{"name":"Alice","role":"admin"}},{"user":{"name":"Bob","role":"guest"}}]'
|
458
|
-
result = json_data.to_a.dig_map(:user, :name).join(", ")
|
459
|
-
# => "Alice, Bob"
|
460
|
-
```
|
461
|
-
|
462
|
-
### Freezing Nested Configurations
|
463
|
-
|
464
|
-
**Before:**
|
465
|
-
```ruby
|
466
|
-
# Standard Ruby approach
|
467
|
-
config_json = File.read("config.json")
|
468
|
-
config = JSON.parse(config_json, symbolize_names: true)
|
469
|
-
|
470
|
-
deep_freeze = lambda do |obj|
|
471
|
-
case obj
|
472
|
-
when Hash
|
473
|
-
obj.each_value { |v| deep_freeze.call(v) }
|
474
|
-
obj.freeze
|
475
|
-
when Array
|
476
|
-
obj.each { |v| deep_freeze.call(v) }
|
477
|
-
obj.freeze
|
478
|
-
else
|
479
|
-
obj.freeze
|
480
|
-
end
|
481
|
-
end
|
131
|
+
user.admin? # => true
|
482
132
|
|
483
|
-
|
484
|
-
|
133
|
+
# Works with Data objects too!
|
134
|
+
Person = Data.define(:active)
|
135
|
+
Person.attr_predicate(:active)
|
485
136
|
|
486
|
-
|
487
|
-
|
488
|
-
# With EverythingRB
|
489
|
-
config_json = File.read("config.json")
|
490
|
-
frozen_config = config_json.to_h.deep_freeze
|
137
|
+
person = Person.new(active: false)
|
138
|
+
person.active? # => false
|
491
139
|
```
|
492
140
|
|
493
|
-
|
141
|
+
**Extensions:** `attr_predicate`
|
494
142
|
|
495
|
-
|
496
|
-
```ruby
|
497
|
-
# Standard Ruby approach
|
498
|
-
users_json = '[{"user":{"name":"Alice","admin":true,"active":true}},{"user":{"name":"Bob","admin":true,"active":false}}]'
|
143
|
+
## Full Documentation
|
499
144
|
|
500
|
-
|
501
|
-
active_admins = users.map { |u| u[:user] }.select { |u| u[:admin] && u[:active] }
|
502
|
-
admin_names = active_admins.map { |u| u[:name] }.join(", ")
|
503
|
-
# => "Alice"
|
504
|
-
```
|
505
|
-
|
506
|
-
**After:**
|
507
|
-
```ruby
|
508
|
-
# With EverythingRB
|
509
|
-
users_json = '[{"user":{"name":"Alice","admin":true,"active":true}},{"user":{"name":"Bob","admin":true,"active":false}}]'
|
510
|
-
admin_names = users_json.to_a.key_map(:user).join_map(", ") do |user|
|
511
|
-
user[:name] if user[:admin] && user[:active]
|
512
|
-
end
|
513
|
-
# => "Alice"
|
514
|
-
```
|
145
|
+
For complete method listings, examples, and detailed usage, see the [API Documentation](https://itsthedevman.com/docs/everythingrb).
|
515
146
|
|
516
147
|
## Requirements
|
517
148
|
|
@@ -519,22 +150,14 @@ end
|
|
519
150
|
|
520
151
|
## Contributing
|
521
152
|
|
522
|
-
|
523
|
-
2. Create your feature branch (`git checkout -b feature/my-new-feature`)
|
524
|
-
3. Commit your changes (`git commit -am 'Add some feature'`)
|
525
|
-
4. Push to the branch (`git push origin feature/my-new-feature`)
|
526
|
-
5. Create new Pull Request
|
527
|
-
|
528
|
-
Please note that this project is released with a [Contributor Code of Conduct](CODE_OF_CONDUCT.md). By participating in this project you agree to abide by its terms.
|
153
|
+
Bug reports and pull requests are welcome! This project is intended to be a safe, welcoming space for collaboration.
|
529
154
|
|
530
155
|
## License
|
531
156
|
|
532
|
-
|
157
|
+
[MIT License](LICENSE.txt)
|
533
158
|
|
534
|
-
##
|
535
|
-
|
536
|
-
See [CHANGELOG.md](CHANGELOG.md) for a list of changes.
|
159
|
+
## Looking for a Software Engineer?
|
537
160
|
|
538
|
-
|
161
|
+
I'm currently looking for opportunities where I can tackle meaningful problems and help build reliable software while mentoring the next generation of developers. If you're looking for a senior engineer with full-stack Rails expertise and a passion for clean, maintainable code, let's talk!
|
539
162
|
|
540
|
-
|
163
|
+
[bryan@itsthedevman.com](mailto:bryan@itsthedevman.com)
|
@@ -93,8 +93,161 @@ class Array
|
|
93
93
|
# ["hello", { name: "Alice" }, [1, 2, 3]].deep_freeze
|
94
94
|
# # => All elements and nested structures are now frozen
|
95
95
|
#
|
96
|
+
# @note CAUTION: Be careful when freezing collections that contain class objects
|
97
|
+
# or singleton instances - this will freeze those classes/objects globally!
|
98
|
+
# Only use deep_freeze on pure data structures you want to make immutable.
|
99
|
+
#
|
96
100
|
def deep_freeze
|
97
101
|
each { |v| v.respond_to?(:deep_freeze) ? v.deep_freeze : v.freeze }
|
98
102
|
freeze
|
99
103
|
end
|
104
|
+
|
105
|
+
#
|
106
|
+
# Removes nil values from the beginning of an array
|
107
|
+
#
|
108
|
+
# @return [Array] Array with leading nil values removed
|
109
|
+
#
|
110
|
+
# @example
|
111
|
+
# [nil, nil, 1, 2, nil, 3].compact_prefix
|
112
|
+
# # => [1, 2, nil, 3]
|
113
|
+
#
|
114
|
+
def compact_prefix
|
115
|
+
drop_while(&:nil?)
|
116
|
+
end
|
117
|
+
|
118
|
+
#
|
119
|
+
# Removes nil values from the end of an array
|
120
|
+
#
|
121
|
+
# @return [Array] Array with trailing nil values removed
|
122
|
+
#
|
123
|
+
# @example
|
124
|
+
# [1, 2, nil, 3, nil, nil].compact_suffix
|
125
|
+
# # => [1, 2, nil, 3]
|
126
|
+
#
|
127
|
+
def compact_suffix
|
128
|
+
reverse.drop_while(&:nil?).reverse
|
129
|
+
end
|
130
|
+
|
131
|
+
#
|
132
|
+
# Removes nil values from both the beginning and end of an array
|
133
|
+
#
|
134
|
+
# @return [Array] Array with leading and trailing nil values removed
|
135
|
+
#
|
136
|
+
# @example
|
137
|
+
# [nil, nil, 1, 2, nil, 3, nil, nil].trim_nils
|
138
|
+
# # => [1, 2, nil, 3]
|
139
|
+
#
|
140
|
+
def trim_nils
|
141
|
+
compact_prefix.compact_suffix
|
142
|
+
end
|
143
|
+
|
144
|
+
# ActiveSupport integrations
|
145
|
+
if defined?(ActiveSupport)
|
146
|
+
#
|
147
|
+
# Removes blank values from the beginning of an array
|
148
|
+
#
|
149
|
+
# @return [Array] Array with leading blank values removed
|
150
|
+
#
|
151
|
+
# @example With ActiveSupport loaded
|
152
|
+
# [nil, "", 1, 2, "", 3].compact_blank_prefix
|
153
|
+
# # => [1, 2, "", 3]
|
154
|
+
#
|
155
|
+
def compact_blank_prefix
|
156
|
+
drop_while(&:blank?)
|
157
|
+
end
|
158
|
+
|
159
|
+
#
|
160
|
+
# Removes blank values from the end of an array
|
161
|
+
#
|
162
|
+
# @return [Array] Array with trailing blank values removed
|
163
|
+
#
|
164
|
+
# @example With ActiveSupport loaded
|
165
|
+
# [1, 2, "", 3, nil, ""].compact_blank_suffix
|
166
|
+
# # => [1, 2, "", 3]
|
167
|
+
#
|
168
|
+
def compact_blank_suffix
|
169
|
+
reverse.drop_while(&:blank?).reverse
|
170
|
+
end
|
171
|
+
|
172
|
+
#
|
173
|
+
# Removes blank values from both the beginning and end of an array
|
174
|
+
#
|
175
|
+
# @return [Array] Array with leading and trailing blank values removed
|
176
|
+
#
|
177
|
+
# @example With ActiveSupport loaded
|
178
|
+
# [nil, "", 1, 2, "", 3, nil, ""].trim_blanks
|
179
|
+
# # => [1, 2, "", 3]
|
180
|
+
#
|
181
|
+
def trim_blanks
|
182
|
+
compact_blank_prefix.compact_blank_suffix
|
183
|
+
end
|
184
|
+
|
185
|
+
#
|
186
|
+
# Joins array elements into a sentence with "or" connector
|
187
|
+
#
|
188
|
+
# Similar to ActiveSupport's #to_sentence but uses "or" instead of "and"
|
189
|
+
# as the joining word between elements.
|
190
|
+
#
|
191
|
+
# @param options [Hash] Options to pass to #to_sentence
|
192
|
+
#
|
193
|
+
# @return [String] Array elements joined in sentence form with "or" connector
|
194
|
+
#
|
195
|
+
# @example Basic usage
|
196
|
+
# ["red", "blue", "green"].to_or_sentence
|
197
|
+
# # => "red, blue, or green"
|
198
|
+
#
|
199
|
+
# @example With only two elements
|
200
|
+
# ["yes", "no"].to_or_sentence
|
201
|
+
# # => "yes or no"
|
202
|
+
#
|
203
|
+
def to_or_sentence(options = {})
|
204
|
+
options = options.reverse_merge(last_word_connector: ", or ", two_words_connector: " or ")
|
205
|
+
to_sentence(options)
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
#
|
210
|
+
# Recursively converts all elements that respond to #to_h
|
211
|
+
#
|
212
|
+
# Maps over the array and calls #to_deep_h on any Hash/String elements,
|
213
|
+
# #to_h on any objects that respond to it, and handles nested arrays.
|
214
|
+
#
|
215
|
+
# @return [Array] A new array with all convertible elements deeply converted
|
216
|
+
#
|
217
|
+
# @example Converting arrays with mixed object types
|
218
|
+
# users = [
|
219
|
+
# {name: "Alice", roles: ["admin"]},
|
220
|
+
# OpenStruct.new(name: "Bob", active: true),
|
221
|
+
# Data.define(:name).new(name: "Carol")
|
222
|
+
# ]
|
223
|
+
# users.to_deep_h
|
224
|
+
# # => [
|
225
|
+
# # {name: "Alice", roles: ["admin"]},
|
226
|
+
# # {name: "Bob", active: true},
|
227
|
+
# # {name: "Carol"}
|
228
|
+
# # ]
|
229
|
+
#
|
230
|
+
# @example With nested arrays and JSON strings
|
231
|
+
# data = [
|
232
|
+
# {profile: '{"level":"expert"}'},
|
233
|
+
# [OpenStruct.new(id: 1), OpenStruct.new(id: 2)]
|
234
|
+
# ]
|
235
|
+
# data.to_deep_h
|
236
|
+
# # => [{profile: {level: "expert"}}, [{id: 1}, {id: 2}]]
|
237
|
+
#
|
238
|
+
def to_deep_h
|
239
|
+
map do |value|
|
240
|
+
case value
|
241
|
+
when Hash
|
242
|
+
value.to_deep_h
|
243
|
+
when Array
|
244
|
+
value.to_deep_h
|
245
|
+
when String
|
246
|
+
# If the string is not valid JSON, #to_deep_h will return `nil`
|
247
|
+
value.to_deep_h || value
|
248
|
+
else
|
249
|
+
value.respond_to?(:to_h) ? value.to_h : value
|
250
|
+
end
|
251
|
+
end
|
252
|
+
end
|
100
253
|
end
|
@@ -44,4 +44,49 @@ module Enumerable
|
|
44
44
|
filter_map(&block).join(join_with)
|
45
45
|
end
|
46
46
|
end
|
47
|
+
|
48
|
+
#
|
49
|
+
# Groups elements by a given key or nested keys
|
50
|
+
#
|
51
|
+
# @param keys [Array<Symbol, String>] The key(s) to group by
|
52
|
+
#
|
53
|
+
# @yield [value] Optional block to transform the key value before grouping
|
54
|
+
# @yieldparam value [Object] The value at the specified key(s)
|
55
|
+
# @yieldreturn [Object] The transformed value to use as the group key
|
56
|
+
#
|
57
|
+
# @return [Hash] A hash where keys are the grouped values and values are arrays of elements
|
58
|
+
#
|
59
|
+
# @example Group by a single key
|
60
|
+
# users = [
|
61
|
+
# {name: "Alice", role: "admin"},
|
62
|
+
# {name: "Bob", role: "user"},
|
63
|
+
# {name: "Charlie", role: "admin"}
|
64
|
+
# ]
|
65
|
+
# users.group_by_key(:role)
|
66
|
+
# # => {"admin"=>[{name: "Alice", role: "admin"}, {name: "Charlie", role: "admin"}],
|
67
|
+
# # "user"=>[{name: "Bob", role: "user"}]}
|
68
|
+
#
|
69
|
+
# @example Group by nested keys
|
70
|
+
# data = [
|
71
|
+
# {department: {name: "Sales"}, employee: "Alice"},
|
72
|
+
# {department: {name: "IT"}, employee: "Bob"},
|
73
|
+
# {department: {name: "Sales"}, employee: "Charlie"}
|
74
|
+
# ]
|
75
|
+
# data.group_by_key(:department, :name)
|
76
|
+
# # => {"Sales"=>[{department: {name: "Sales"}, employee: "Alice"},
|
77
|
+
# # {department: {name: "Sales"}, employee: "Charlie"}],
|
78
|
+
# # "IT"=>[{department: {name: "IT"}, employee: "Bob"}]}
|
79
|
+
#
|
80
|
+
# @example With transformation block
|
81
|
+
# users.group_by_key(:role) { |role| role.upcase }
|
82
|
+
# # => {"ADMIN"=>[{name: "Alice", role: "admin"}, {name: "Charlie", role: "admin"}],
|
83
|
+
# # "USER"=>[{name: "Bob", role: "user"}]}
|
84
|
+
#
|
85
|
+
def group_by_key(*keys, &block)
|
86
|
+
group_by do |value|
|
87
|
+
result = value.respond_to?(:dig) ? value.dig(*keys) : nil
|
88
|
+
result = yield(result) if block
|
89
|
+
result
|
90
|
+
end
|
91
|
+
end
|
47
92
|
end
|
@@ -29,6 +29,32 @@ class Hash
|
|
29
29
|
#
|
30
30
|
EMPTY_STRUCT = Struct.new(:_).new(nil)
|
31
31
|
|
32
|
+
#
|
33
|
+
# Creates a new Hash that automatically initializes missing keys with nested hashes
|
34
|
+
#
|
35
|
+
# This method creates a hash where any missing key access will automatically
|
36
|
+
# create another nested hash with the same behavior, allowing for unlimited
|
37
|
+
# nesting depth without explicit initialization.
|
38
|
+
#
|
39
|
+
# @return [Hash] A hash that recursively creates nested hashes for missing keys
|
40
|
+
#
|
41
|
+
# @example Basic usage with two levels
|
42
|
+
# users = Hash.new_nested_hash
|
43
|
+
# users[:john][:role] = "admin" # No need to initialize users[:john] first
|
44
|
+
# users # => {john: {role: "admin"}}
|
45
|
+
#
|
46
|
+
# @example Deep nesting without initialization
|
47
|
+
# stats = Hash.new_nested_hash
|
48
|
+
# (stats[:server][:region][:us_east][:errors] = []) << "Some Error"
|
49
|
+
# stats # => {server: {region: {us_east: {errors: ["Some Error"]}}}}
|
50
|
+
#
|
51
|
+
# @note While extremely convenient, be cautious with very deep structures
|
52
|
+
# as this creates new hashes on demand for any key access
|
53
|
+
#
|
54
|
+
def self.new_nested_hash
|
55
|
+
new { |hash, key| hash[key] = new_nested_hash }
|
56
|
+
end
|
57
|
+
|
32
58
|
#
|
33
59
|
# Combines filter_map and join operations
|
34
60
|
#
|
@@ -54,6 +80,53 @@ class Hash
|
|
54
80
|
filter_map(&block).join(join_with)
|
55
81
|
end
|
56
82
|
|
83
|
+
#
|
84
|
+
# Recursively converts all values that respond to #to_h
|
85
|
+
#
|
86
|
+
# Similar to #to_h but recursively traverses the Hash structure
|
87
|
+
# and calls #to_h on any object that responds to it. Useful for
|
88
|
+
# normalizing nested data structures and parsing nested JSON.
|
89
|
+
#
|
90
|
+
# @return [Hash] A deeply converted hash with all nested objects converted
|
91
|
+
#
|
92
|
+
# @example Converting nested Data objects
|
93
|
+
# user = { name: "Alice", metadata: Data.define(:source).new(source: "API") }
|
94
|
+
# user.to_deep_h # => {name: "Alice", metadata: {source: "API"}}
|
95
|
+
#
|
96
|
+
# @example Parsing nested JSON strings
|
97
|
+
# nested = { profile: '{"role":"admin"}' }
|
98
|
+
# nested.to_deep_h # => {profile: {role: "admin"}}
|
99
|
+
#
|
100
|
+
# @example Mixed nested structures
|
101
|
+
# data = {
|
102
|
+
# config: OpenStruct.new(api_key: "secret"),
|
103
|
+
# users: [
|
104
|
+
# Data.define(:name).new(name: "Bob"),
|
105
|
+
# {role: "admin"}
|
106
|
+
# ]
|
107
|
+
# }
|
108
|
+
# data.to_deep_h
|
109
|
+
# # => {
|
110
|
+
# # config: {api_key: "secret"},
|
111
|
+
# # users: [{name: "Bob"}, {role: "admin"}]
|
112
|
+
# # }
|
113
|
+
#
|
114
|
+
def to_deep_h
|
115
|
+
transform_values do |value|
|
116
|
+
case value
|
117
|
+
when Hash
|
118
|
+
value.to_deep_h
|
119
|
+
when Array
|
120
|
+
value.to_deep_h
|
121
|
+
when String
|
122
|
+
# If the string is not valid JSON, #to_deep_h will return `nil`
|
123
|
+
value.to_deep_h || value
|
124
|
+
else
|
125
|
+
value.respond_to?(:to_h) ? value.to_h : value
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
57
130
|
#
|
58
131
|
# Converts hash to an immutable Data structure
|
59
132
|
#
|
@@ -143,8 +216,162 @@ class Hash
|
|
143
216
|
# { user: { name: "Alice", roles: ["admin"] } }.deep_freeze
|
144
217
|
# # => Hash and all nested structures are now frozen
|
145
218
|
#
|
219
|
+
# @note CAUTION: Be careful when freezing collections that contain class objects
|
220
|
+
# or singleton instances - this will freeze those classes/objects globally!
|
221
|
+
# Only use deep_freeze on pure data structures you want to make immutable.
|
222
|
+
#
|
146
223
|
def deep_freeze
|
147
224
|
each_value { |v| v.respond_to?(:deep_freeze) ? v.deep_freeze : v.freeze }
|
148
225
|
freeze
|
149
226
|
end
|
227
|
+
|
228
|
+
# Allows calling original method. See below
|
229
|
+
alias_method :og_transform_values, :transform_values
|
230
|
+
|
231
|
+
# Allows calling original method. See below
|
232
|
+
alias_method :og_transform_values!, :transform_values!
|
233
|
+
|
234
|
+
#
|
235
|
+
# Transforms hash values while allowing access to keys via the chainable with_key method
|
236
|
+
#
|
237
|
+
# This method either performs a standard transform_values operation if a block is given,
|
238
|
+
# or returns an enumerator with a with_key method that passes both the key and value
|
239
|
+
# to the block.
|
240
|
+
#
|
241
|
+
# @yield [value] Block to transform each value (standard behavior)
|
242
|
+
# @yieldparam value [Object] The value to transform
|
243
|
+
# @yieldreturn [Object] The transformed value
|
244
|
+
#
|
245
|
+
# @return [Hash, Enumerator] Result hash or Enumerator with with_key method
|
246
|
+
#
|
247
|
+
# @example Standard transform_values
|
248
|
+
# {a: 1, b: 2}.transform_values { |v| v * 2 }
|
249
|
+
# # => {a: 2, b: 4}
|
250
|
+
#
|
251
|
+
# @example Using with_key to access keys during transformation
|
252
|
+
# {a: 1, b: 2}.transform_values.with_key { |k, v| "#{k}_#{v}" }
|
253
|
+
# # => {a: "a_1", b: "b_2"}
|
254
|
+
#
|
255
|
+
def transform_values(&block)
|
256
|
+
return transform_values_enumerator if block.nil?
|
257
|
+
|
258
|
+
og_transform_values(&block)
|
259
|
+
end
|
260
|
+
|
261
|
+
#
|
262
|
+
# Transforms hash values in place while allowing access to keys via the chainable with_key method
|
263
|
+
#
|
264
|
+
# This method either performs a standard transform_values! operation if a block is given,
|
265
|
+
# or returns an enumerator with a with_key method that passes both the key and value
|
266
|
+
# to the block, updating the hash in place.
|
267
|
+
#
|
268
|
+
# @yield [value] Block to transform each value (standard behavior)
|
269
|
+
# @yieldparam value [Object] The value to transform
|
270
|
+
# @yieldreturn [Object] The transformed value
|
271
|
+
#
|
272
|
+
# @return [self, Enumerator]
|
273
|
+
# Original hash with transformed values or Enumerator with with_key method
|
274
|
+
#
|
275
|
+
# @example Standard transform_values!
|
276
|
+
# hash = {a: 1, b: 2}
|
277
|
+
# hash.transform_values! { |v| v * 2 }
|
278
|
+
# # => {a: 2, b: 4}
|
279
|
+
#
|
280
|
+
# @example Using with_key to access keys during in-place transformation
|
281
|
+
# hash = {a: 1, b: 2}
|
282
|
+
# hash.transform_values!.with_key { |k, v| "#{k}_#{v}" }
|
283
|
+
# # => {a: "a_1", b: "b_2"}
|
284
|
+
#
|
285
|
+
def transform_values!(&block)
|
286
|
+
return transform_values_bang_enumerator if block.nil?
|
287
|
+
|
288
|
+
og_transform_values!(&block)
|
289
|
+
end
|
290
|
+
|
291
|
+
#
|
292
|
+
# Returns the first value where the key-value pair satisfies the given condition
|
293
|
+
#
|
294
|
+
# @yield [key, value] Block that determines whether to include the value
|
295
|
+
# @yieldparam key [Object] The current key
|
296
|
+
# @yieldparam value [Object] The current value
|
297
|
+
# @yieldreturn [Boolean] Whether to include this value
|
298
|
+
#
|
299
|
+
# @return [Object, nil] The first matching value or nil if none found
|
300
|
+
# @return [Enumerator] If no block is given
|
301
|
+
#
|
302
|
+
# @example Find first admin user by role
|
303
|
+
# users = {
|
304
|
+
# alice: {name: "Alice", role: "admin"},
|
305
|
+
# bob: {name: "Bob", role: "user"},
|
306
|
+
# charlie: {name: "Charlie", role: "admin"}
|
307
|
+
# }
|
308
|
+
# users.value_where { |k, v| v[:role] == "admin" } # => {name: "Alice", role: "admin"}
|
309
|
+
#
|
310
|
+
def value_where(&block)
|
311
|
+
return to_enum(:value_where) if block.nil?
|
312
|
+
|
313
|
+
find(&block).last
|
314
|
+
end
|
315
|
+
|
316
|
+
#
|
317
|
+
# Returns all values where the key-value pairs satisfy the given condition
|
318
|
+
#
|
319
|
+
# @yield [key, value] Block that determines whether to include the value
|
320
|
+
# @yieldparam key [Object] The current key
|
321
|
+
# @yieldparam value [Object] The current value
|
322
|
+
# @yieldreturn [Boolean] Whether to include this value
|
323
|
+
#
|
324
|
+
# @return [Array] All matching values
|
325
|
+
# @return [Enumerator] If no block is given
|
326
|
+
#
|
327
|
+
# @example Find all admin users by role
|
328
|
+
# users = {
|
329
|
+
# alice: {name: "Alice", role: "admin"},
|
330
|
+
# bob: {name: "Bob", role: "user"},
|
331
|
+
# charlie: {name: "Charlie", role: "admin"}
|
332
|
+
# }
|
333
|
+
# users.values_where { |k, v| v[:role] == "admin" }
|
334
|
+
# # => [{name: "Alice", role: "admin"}, {name: "Charlie", role: "admin"}]
|
335
|
+
#
|
336
|
+
def values_where(&block)
|
337
|
+
return to_enum(:values_where) if block.nil?
|
338
|
+
|
339
|
+
select(&block).values
|
340
|
+
end
|
341
|
+
|
342
|
+
private
|
343
|
+
|
344
|
+
def transform_values_enumerator
|
345
|
+
original_hash = self
|
346
|
+
enum = to_enum(:transform_values)
|
347
|
+
|
348
|
+
# Add the with_key method directly to the enum
|
349
|
+
enum.define_singleton_method(:with_key) do |&block|
|
350
|
+
raise ArgumentError, "Missing block for Hash#transform_values.with_key" if block.nil?
|
351
|
+
|
352
|
+
original_hash.each_pair.with_object({}) do |(key, value), output|
|
353
|
+
output[key] = block.call(key, value)
|
354
|
+
end
|
355
|
+
end
|
356
|
+
|
357
|
+
enum
|
358
|
+
end
|
359
|
+
|
360
|
+
def transform_values_bang_enumerator
|
361
|
+
original_hash = self
|
362
|
+
enum = to_enum(:transform_values!)
|
363
|
+
|
364
|
+
# Add the with_key method directly to the enum
|
365
|
+
enum.define_singleton_method(:with_key) do |&block|
|
366
|
+
raise ArgumentError, "Missing block for Hash#transform_values!.with_key" if block.nil?
|
367
|
+
|
368
|
+
original_hash.each_pair do |key, value|
|
369
|
+
original_hash[key] = block.call(key, value)
|
370
|
+
end
|
371
|
+
|
372
|
+
original_hash
|
373
|
+
end
|
374
|
+
|
375
|
+
enum
|
376
|
+
end
|
150
377
|
end
|
data/lib/everythingrb/version.rb
CHANGED
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.3.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-04-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ostruct
|