everythingrb 0.2.5 → 0.3.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 +28 -1
- data/README.md +62 -484
- data/lib/everythingrb/core/array.rb +72 -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: 747441fc1a0c8be071a4465edb190dead79eea7195b66b262976e8ae91b6bc0f
|
4
|
+
data.tar.gz: 491c3bc6c22bc02495425fecfb51166c8982d86ad0eede1b10980e2bf040b705
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f6605c462aeeb634fe3342a3befb424fe3a67f31059f486d4c835a3670192962a44f6237f51947629cbe1a27d7a21579c03a48f9c98e22f155821cdb62f1e8c1
|
7
|
+
data.tar.gz: e6d2ce8fca0b90b513c686dfc44f8010cfb2aca9cc3be84cdbd08c6fccbbac49727ffbe42805f5a0789496df7b3f68ace624b7c201b30f5ee5305be16af485fa
|
data/CHANGELOG.md
CHANGED
@@ -23,6 +23,31 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
23
23
|
|
24
24
|
### Removed
|
25
25
|
|
26
|
+
## [0.3.1] - 12025-04-09
|
27
|
+
|
28
|
+
### Added
|
29
|
+
|
30
|
+
### Changed
|
31
|
+
|
32
|
+
- Fixed `Hash#value_where` error when nothing is found
|
33
|
+
|
34
|
+
### Removed
|
35
|
+
|
36
|
+
## [0.3.0] - 12025-04-09
|
37
|
+
|
38
|
+
### Added
|
39
|
+
|
40
|
+
- Added `Array#to_or_sentence`, creates a sentence with "or" connector between items
|
41
|
+
- Added `#with_key` method to `Hash#transform_values` and `Hash#transform_values!`, grants access to both keys and values during transformations
|
42
|
+
- Added `Array#to_deep_h` and `Hash#to_deep_h`, recursively converts underlying values to hashes
|
43
|
+
- Added `Enumerable#group_by_key`, group an array of hashes by their keys
|
44
|
+
- Added `Hash#new_nested_hash`, creates a new Hash that automatically initializes the value to a hash
|
45
|
+
- Added `Hash#value_where` and `Hash#values_where`, easily find values in a hash based on key-value conditions
|
46
|
+
|
47
|
+
### Changed
|
48
|
+
|
49
|
+
### Removed
|
50
|
+
|
26
51
|
## [0.2.5] - 12025-03-29
|
27
52
|
|
28
53
|
### Added
|
@@ -124,7 +149,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
124
149
|
|
125
150
|
- Added alias `each` to `each_pair` in OpenStruct for better enumerable compatibility
|
126
151
|
|
127
|
-
[unreleased]: https://github.com/itsthedevman/everythingrb/compare/v0.
|
152
|
+
[unreleased]: https://github.com/itsthedevman/everythingrb/compare/v0.3.1...HEAD
|
153
|
+
[0.3.1]: https://github.com/itsthedevman/everythingrb/compare/v0.3.0...v0.3.1
|
154
|
+
[0.3.0]: https://github.com/itsthedevman/everythingrb/compare/v0.2.5...v0.3.0
|
128
155
|
[0.2.5]: https://github.com/itsthedevman/everythingrb/compare/v0.2.4...v0.2.5
|
129
156
|
[0.2.4]: https://github.com/itsthedevman/everythingrb/compare/v0.2.3...v0.2.4
|
130
157
|
[0.2.3]: https://github.com/itsthedevman/everythingrb/compare/v0.2.2...v0.2.3
|
data/README.md
CHANGED
@@ -4,97 +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
|
-
- [Array Trimming](#array-trimming)
|
26
|
-
- [Predicate Methods](#predicate-methods)
|
27
|
-
- [Core Extensions](#core-extensions)
|
28
|
-
- [Array](#array)
|
29
|
-
- [Enumerable](#enumerable)
|
30
|
-
- [Hash](#hash)
|
31
|
-
- [Module](#module)
|
32
|
-
- [OpenStruct](#openstruct)
|
33
|
-
- [String](#string)
|
34
|
-
- [Symbol](#symbol)
|
35
|
-
- [Advanced Usage](#advanced-usage)
|
36
|
-
- [Requirements](#requirements)
|
37
|
-
- [Contributing](#contributing)
|
38
|
-
- [License](#license)
|
39
|
-
- [Changelog](#changelog)
|
40
|
-
- [Credits](#credits)
|
41
|
-
|
42
|
-
Also see: [API Documentation](https://itsthedevman.com/docs/everythingrb)
|
43
|
-
|
44
|
-
## Introduction
|
45
|
-
|
46
|
-
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.
|
47
|
-
|
48
|
-
Whether you're transforming data, working with JSON, or building complex object structures, EverythingRB makes your code more readable and maintainable with minimal effort.
|
49
|
-
|
50
|
-
## Compatibility
|
51
|
-
|
52
|
-
Currently tested on:
|
53
|
-
- MRI Ruby 3.2+
|
54
|
-
- NixOS (see `flake.nix` for details)
|
14
|
+
# Write this:
|
15
|
+
users.join_map(", ") { |u| u[:name] if u[:role] == "admin" }
|
16
|
+
```
|
55
17
|
|
56
18
|
## Installation
|
57
19
|
|
58
|
-
Add this line to your application's Gemfile:
|
59
|
-
|
60
20
|
```ruby
|
21
|
+
# In your Gemfile
|
61
22
|
gem "everythingrb"
|
62
|
-
```
|
63
23
|
|
64
|
-
|
65
|
-
|
66
|
-
```bash
|
67
|
-
$ bundle install
|
24
|
+
# Or install manually
|
25
|
+
gem install everythingrb
|
68
26
|
```
|
69
27
|
|
70
|
-
|
71
|
-
|
72
|
-
```bash
|
73
|
-
$ gem install everythingrb
|
74
|
-
```
|
28
|
+
## What's Included
|
75
29
|
|
76
|
-
|
30
|
+
EverythingRB extends Ruby's core classes with intuitive methods that simplify common patterns.
|
77
31
|
|
78
32
|
### Data Structure Conversions
|
79
33
|
|
80
|
-
|
34
|
+
Convert between data structures with ease:
|
81
35
|
|
82
36
|
```ruby
|
83
|
-
# Convert
|
37
|
+
# Convert hashes to more convenient structures
|
84
38
|
config = { server: { host: "example.com", port: 443 } }.to_ostruct
|
85
39
|
config.server.host # => "example.com"
|
86
40
|
|
87
41
|
# Parse JSON directly to your preferred structure
|
88
42
|
'{"user":{"name":"Alice"}}'.to_istruct.user.name # => "Alice"
|
89
|
-
'{"items":[1,2,3]}'.to_struct.items # => [1, 2, 3]
|
90
43
|
```
|
91
44
|
|
45
|
+
**Extensions:** `to_struct`, `to_ostruct`, `to_istruct`, `to_h`, `to_deep_h`
|
46
|
+
|
92
47
|
### Collection Processing
|
93
48
|
|
94
|
-
|
49
|
+
Extract and transform data elegantly:
|
95
50
|
|
96
51
|
```ruby
|
97
|
-
# Extract
|
52
|
+
# Extract data from arrays of hashes in one step
|
98
53
|
users = [{ name: "Alice", roles: ["admin"] }, { name: "Bob", roles: ["user"] }]
|
99
54
|
users.key_map(:name) # => ["Alice", "Bob"]
|
100
55
|
users.dig_map(:roles, 0) # => ["admin", "user"]
|
@@ -102,27 +57,16 @@ users.dig_map(:roles, 0) # => ["admin", "user"]
|
|
102
57
|
# Filter, map, and join in a single operation
|
103
58
|
[1, 2, nil, 3, 4].join_map(" | ") { |n| "Item #{n}" if n&.odd? }
|
104
59
|
# => "Item 1 | Item 3"
|
105
|
-
```
|
106
|
-
|
107
|
-
### JSON & String Handling
|
108
|
-
|
109
|
-
Work with JSON and strings more naturally:
|
110
|
-
|
111
|
-
```ruby
|
112
|
-
# Parse JSON with symbolized keys
|
113
|
-
'{"name": "Alice"}'.to_h # => { name: "Alice" }
|
114
60
|
|
115
|
-
#
|
116
|
-
|
117
|
-
nested.to_deep_h # => { user: { profile: { name: "Bob" } } }
|
118
|
-
|
119
|
-
# Format strings with quotes
|
120
|
-
"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"
|
121
63
|
```
|
122
64
|
|
123
|
-
|
65
|
+
**Extensions:** `join_map`, `key_map`, `dig_map`, `to_or_sentence`, `group_by_key`
|
66
|
+
|
67
|
+
### Object Protection
|
124
68
|
|
125
|
-
|
69
|
+
Prevent unwanted modifications with a single call:
|
126
70
|
|
127
71
|
```ruby
|
128
72
|
config = {
|
@@ -134,429 +78,71 @@ config = {
|
|
134
78
|
|
135
79
|
# Everything is frozen!
|
136
80
|
config.frozen? # => true
|
137
|
-
config[:api].frozen? # => true
|
138
|
-
config[:api][:endpoints].frozen? # => true
|
139
81
|
config[:api][:endpoints][0].frozen? # => true
|
140
82
|
```
|
141
83
|
|
142
|
-
|
84
|
+
**Extensions:** `deep_freeze`
|
143
85
|
|
144
|
-
|
86
|
+
### Hash Convenience
|
145
87
|
|
146
|
-
|
147
|
-
# Remove nil values from the beginning or end
|
148
|
-
[nil, nil, 1, nil, 2, nil, nil].trim_nils # => [1, nil, 2]
|
149
|
-
|
150
|
-
# With ActiveSupport, remove any blank values (nil, "", etc.)
|
151
|
-
[nil, "", 1, "", 2, nil, ""].trim_blanks # => [1, "", 2]
|
152
|
-
|
153
|
-
# Only trim from one end if needed
|
154
|
-
[nil, nil, 1, 2, 3].compact_prefix # => [1, 2, 3]
|
155
|
-
[1, 2, 3, nil, nil].compact_suffix # => [1, 2, 3]
|
156
|
-
```
|
157
|
-
|
158
|
-
### Predicate Methods
|
159
|
-
|
160
|
-
Create boolean accessors with minimal code:
|
161
|
-
|
162
|
-
```ruby
|
163
|
-
class User
|
164
|
-
attr_accessor :admin, :verified
|
165
|
-
attr_predicate :admin, :verified
|
166
|
-
end
|
167
|
-
|
168
|
-
user = User.new
|
169
|
-
user.admin = true
|
170
|
-
user.admin? # => true
|
171
|
-
user.verified? # => false
|
172
|
-
|
173
|
-
# Works with Struct and Data objects too
|
174
|
-
Person = Struct.new(:active)
|
175
|
-
Person.attr_predicate(:active)
|
176
|
-
|
177
|
-
person = Person.new(true)
|
178
|
-
person.active? # => true
|
179
|
-
```
|
180
|
-
|
181
|
-
**ActiveSupport Integration:** When ActiveSupport is loaded, predicate methods automatically use `present?` instead of just checking truthiness:
|
88
|
+
Work with hashes more intuitively:
|
182
89
|
|
183
90
|
```ruby
|
184
|
-
#
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
end
|
189
|
-
|
190
|
-
product = Product.new
|
191
|
-
product.tags = []
|
192
|
-
product.tags? # => false (empty array is not "present")
|
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!
|
193
95
|
|
194
|
-
|
195
|
-
|
96
|
+
# Transform values with access to keys
|
97
|
+
users.transform_values.with_key { |k, v| "User #{k}: #{v[:name]}" }
|
196
98
|
|
197
|
-
|
198
|
-
|
99
|
+
# Find values based on conditions
|
100
|
+
users.values_where { |k, v| v[:role] == "admin" }
|
199
101
|
```
|
200
102
|
|
201
|
-
|
103
|
+
**Extensions:** `new_nested_hash`, `with_key`, `value_where`, `values_where`
|
202
104
|
|
203
|
-
### Array
|
105
|
+
### Array Cleaning
|
204
106
|
|
205
|
-
|
206
|
-
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:
|
207
108
|
|
208
109
|
```ruby
|
209
|
-
#
|
210
|
-
[1, 2, nil,
|
211
|
-
# => "1 3"
|
212
|
-
|
213
|
-
# With index
|
214
|
-
["a", "b", "c"].join_map(", ", with_index: true) { |char, i| "#{i}:#{char}" }
|
215
|
-
# => "0:a, 1:b, 2:c"
|
216
|
-
|
217
|
-
# Default behavior without block
|
218
|
-
[1, 2, nil, 3].join_map(", ")
|
219
|
-
# => "1, 2, 3"
|
220
|
-
```
|
221
|
-
|
222
|
-
#### `key_map`
|
223
|
-
Extracts a specific key from each hash in an array.
|
224
|
-
|
225
|
-
```ruby
|
226
|
-
users = [
|
227
|
-
{ name: "Alice", age: 30 },
|
228
|
-
{ name: "Bob", age: 25 }
|
229
|
-
]
|
230
|
-
|
231
|
-
users.key_map(:name)
|
232
|
-
# => ["Alice", "Bob"]
|
233
|
-
```
|
234
|
-
|
235
|
-
#### `dig_map`
|
236
|
-
Extracts nested values from each hash in an array using the `dig` method.
|
237
|
-
|
238
|
-
```ruby
|
239
|
-
data = [
|
240
|
-
{ user: { profile: { name: "Alice" } } },
|
241
|
-
{ user: { profile: { name: "Bob" } } }
|
242
|
-
]
|
243
|
-
|
244
|
-
data.dig_map(:user, :profile, :name)
|
245
|
-
# => ["Alice", "Bob"]
|
246
|
-
```
|
247
|
-
|
248
|
-
#### `compact_prefix` / `compact_suffix` / `trim_nils`
|
249
|
-
Remove nil values from the beginning, end, or both ends of an array without touching interior nil values.
|
250
|
-
|
251
|
-
```ruby
|
252
|
-
[nil, nil, 1, nil, 2, nil, nil].compact_prefix
|
253
|
-
# => [1, nil, 2, nil, nil]
|
254
|
-
|
255
|
-
[1, nil, 2, nil, nil].compact_suffix
|
256
|
-
# => [1, nil, 2]
|
257
|
-
|
258
|
-
[nil, nil, 1, nil, 2, nil, nil].trim_nils
|
259
|
-
# => [1, nil, 2]
|
260
|
-
```
|
261
|
-
|
262
|
-
#### `compact_blank_prefix` / `compact_blank_suffix` / `trim_blanks` (with ActiveSupport)
|
263
|
-
Remove blank values (nil, empty strings, etc.) from the beginning, end, or both ends of an array.
|
264
|
-
|
265
|
-
```ruby
|
266
|
-
[nil, "", 1, "", 2, nil, ""].compact_blank_prefix
|
267
|
-
# => [1, "", 2, nil, ""]
|
268
|
-
|
269
|
-
[nil, "", 1, "", 2, nil, ""].compact_blank_suffix
|
270
|
-
# => [nil, "", 1, "", 2]
|
271
|
-
|
272
|
-
[nil, "", 1, "", 2, nil, ""].trim_blanks
|
273
|
-
# => [1, "", 2]
|
274
|
-
```
|
275
|
-
|
276
|
-
#### `deep_freeze`
|
277
|
-
Recursively freezes an array and all of its nested elements.
|
278
|
-
|
279
|
-
```ruby
|
280
|
-
array = ["hello", { name: "Alice" }, [1, 2, 3]]
|
281
|
-
array.deep_freeze
|
282
|
-
# => All elements and nested structures are now frozen
|
283
|
-
```
|
284
|
-
|
285
|
-
### Enumerable
|
286
|
-
|
287
|
-
#### `join_map`
|
288
|
-
Combines filtering, mapping and joining operations into one convenient method.
|
289
|
-
|
290
|
-
```ruby
|
291
|
-
# Basic usage with arrays
|
292
|
-
[1, 2, nil, 3].join_map(", ") { |n| n&.to_s if n&.odd? }
|
293
|
-
# => "1, 3"
|
294
|
-
|
295
|
-
# Works with any Enumerable
|
296
|
-
(1..10).join_map(" | ") { |n| "num#{n}" if n.even? }
|
297
|
-
# => "num2 | num4 | num6 | num8 | num10"
|
298
|
-
|
299
|
-
# Supports with_index option
|
300
|
-
%w[a b c].join_map("-", with_index: true) { |char, i| "#{i}:#{char}" }
|
301
|
-
# => "0:a-1:b-2:c"
|
302
|
-
```
|
303
|
-
|
304
|
-
### Hash
|
305
|
-
|
306
|
-
#### `to_struct`
|
307
|
-
Recursively converts a hash into a Struct, including nested hashes and arrays.
|
308
|
-
|
309
|
-
```ruby
|
310
|
-
hash = { user: { name: "Alice", roles: ["admin", "user"] } }
|
311
|
-
struct = hash.to_struct
|
312
|
-
struct.class # => Struct
|
313
|
-
struct.user.name # => "Alice"
|
314
|
-
struct.user.roles # => ["admin", "user"]
|
315
|
-
```
|
316
|
-
|
317
|
-
#### `to_ostruct`
|
318
|
-
Recursively converts a hash into an OpenStruct, including nested hashes and arrays.
|
319
|
-
|
320
|
-
```ruby
|
321
|
-
hash = { config: { api_key: "secret", endpoints: ["v1", "v2"] } }
|
322
|
-
config = hash.to_ostruct
|
323
|
-
config.class # => OpenStruct
|
324
|
-
config.config.api_key # => "secret"
|
325
|
-
```
|
326
|
-
|
327
|
-
#### `to_istruct`
|
328
|
-
Recursively converts a hash into an immutable Data structure.
|
329
|
-
|
330
|
-
```ruby
|
331
|
-
hash = { person: { name: "Bob", age: 30 } }
|
332
|
-
data = hash.to_istruct
|
333
|
-
data.class # => Data
|
334
|
-
data.person.name # => "Bob"
|
335
|
-
```
|
336
|
-
|
337
|
-
#### `join_map`
|
338
|
-
Similar to Array#join_map but operates on hash values.
|
110
|
+
# Remove nil values from the beginning/end
|
111
|
+
[nil, nil, 1, nil, 2, nil, nil].trim_nils # => [1, nil, 2]
|
339
112
|
|
340
|
-
|
341
|
-
|
342
|
-
# => "a 1 b 2 d 3"
|
113
|
+
# With ActiveSupport, remove blank values
|
114
|
+
[nil, "", 1, "", 2, nil, ""].trim_blanks # => [1, "", 2]
|
343
115
|
```
|
344
116
|
|
345
|
-
|
346
|
-
Recursively freezes a hash and all of its nested values.
|
117
|
+
**Extensions:** `trim_nils`, `compact_prefix`, `compact_suffix`, `trim_blanks` (with ActiveSupport)
|
347
118
|
|
348
|
-
|
349
|
-
hash = { user: { name: "Alice", roles: ["admin", "user"] } }
|
350
|
-
hash.deep_freeze
|
351
|
-
# => Hash and all nested structures are now frozen
|
352
|
-
```
|
119
|
+
### Boolean Methods
|
353
120
|
|
354
|
-
|
355
|
-
|
356
|
-
#### `attr_predicate`
|
357
|
-
Creates predicate (boolean) methods for instance variables.
|
121
|
+
Create predicate methods with minimal code:
|
358
122
|
|
359
123
|
```ruby
|
360
124
|
class User
|
361
|
-
|
362
|
-
attr_predicate :admin
|
125
|
+
attr_accessor :admin
|
126
|
+
attr_predicate :admin
|
363
127
|
end
|
364
128
|
|
365
129
|
user = User.new
|
366
|
-
user.active? # => false
|
367
130
|
user.admin = true
|
368
|
-
user.admin?
|
369
|
-
```
|
370
|
-
|
371
|
-
### OpenStruct
|
372
|
-
|
373
|
-
#### `each`
|
374
|
-
Alias for `each_pair`.
|
375
|
-
|
376
|
-
```ruby
|
377
|
-
struct = OpenStruct.new(a: 1, b: 2)
|
378
|
-
struct.each { |key, value| puts "#{key}: #{value}" }
|
379
|
-
```
|
380
|
-
|
381
|
-
#### `map`
|
382
|
-
Maps over OpenStruct entries.
|
383
|
-
|
384
|
-
```ruby
|
385
|
-
struct = OpenStruct.new(a: 1, b: 2)
|
386
|
-
struct.map { |key, value| [key, value * 2] }
|
387
|
-
# => [[:a, 2], [:b, 4]]
|
388
|
-
```
|
389
|
-
|
390
|
-
#### `filter_map`
|
391
|
-
Combines `map` and `compact` operations in one convenient method.
|
392
|
-
|
393
|
-
```ruby
|
394
|
-
struct = OpenStruct.new(a: 1, b: nil, c: 2)
|
395
|
-
struct.filter_map { |key, value| value * 2 if value }
|
396
|
-
# => [2, 4]
|
397
|
-
```
|
398
|
-
|
399
|
-
#### `join_map`
|
400
|
-
Combines `filter_map` and `join` operations in one convenient method.
|
401
|
-
|
402
|
-
```ruby
|
403
|
-
config = OpenStruct.new(
|
404
|
-
alice: {roles: ["admin"]},
|
405
|
-
bob: {roles: ["user"]},
|
406
|
-
carol: {roles: ["admin"]}
|
407
|
-
)
|
408
|
-
|
409
|
-
config.join_map(", ") { |key, value| key if value[:roles].include?("admin") }
|
410
|
-
# => "alice, carol"
|
411
|
-
```
|
412
|
-
|
413
|
-
### String
|
414
|
-
|
415
|
-
#### `to_h` / `to_a`
|
416
|
-
Parses JSON string into a Ruby Hash or Array.
|
417
|
-
|
418
|
-
```ruby
|
419
|
-
'{"name": "Alice"}'.to_h
|
420
|
-
# => {name: "Alice"}
|
421
|
-
|
422
|
-
'["Alice"]'.to_h # Or you can use #to_a for different readability
|
423
|
-
# => ["Alice"]
|
424
|
-
```
|
425
|
-
|
426
|
-
#### `to_istruct`
|
427
|
-
Parses JSON string into an immutable Data structure.
|
428
|
-
|
429
|
-
```ruby
|
430
|
-
'{"user": {"name": "Alice"}}'.to_istruct
|
431
|
-
# => #<data user=#<data name="Alice">>
|
432
|
-
```
|
433
|
-
|
434
|
-
#### `to_ostruct`
|
435
|
-
Parses JSON string into an OpenStruct.
|
436
|
-
|
437
|
-
```ruby
|
438
|
-
'{"user": {"name": "Alice"}}'.to_ostruct
|
439
|
-
# => #<OpenStruct user=#<OpenStruct name="Alice">>
|
440
|
-
```
|
441
|
-
|
442
|
-
#### `to_struct`
|
443
|
-
Parses JSON string into a Struct.
|
444
|
-
|
445
|
-
```ruby
|
446
|
-
'{"user": {"name": "Alice"}}'.to_struct
|
447
|
-
# => #<struct user=#<struct name="Alice">>
|
448
|
-
```
|
449
|
-
|
450
|
-
#### `to_deep_h`
|
451
|
-
Recursively parses nested JSON strings within a structure.
|
452
|
-
|
453
|
-
```ruby
|
454
|
-
nested_json = {
|
455
|
-
users: [
|
456
|
-
{name: "Alice", roles: ["admin", "user"]}.to_json,
|
457
|
-
]
|
458
|
-
}.to_json
|
459
|
-
|
460
|
-
nested_json.to_deep_h
|
461
|
-
# => {users: [{name: "Alice", roles: ["admin", "user"]}]}
|
462
|
-
```
|
463
|
-
|
464
|
-
#### `with_quotes` / `in_quotes`
|
465
|
-
Wraps the string in double quotes
|
466
|
-
|
467
|
-
```ruby
|
468
|
-
"Hello World".with_quotes
|
469
|
-
# => "\"Hello World\""
|
470
|
-
```
|
471
|
-
|
472
|
-
### Symbol
|
473
|
-
|
474
|
-
#### `with_quotes` / `in_quotes`
|
475
|
-
Wraps the symbol in double quotes
|
476
|
-
|
477
|
-
```ruby
|
478
|
-
:hello_world.with_quotes
|
479
|
-
# => :"\"hello_world\""
|
480
|
-
```
|
481
|
-
|
482
|
-
## Advanced Usage
|
483
|
-
|
484
|
-
See how EverythingRB transforms your code from verbose to elegant:
|
485
|
-
|
486
|
-
### Extracting Data from Nested JSON
|
487
|
-
|
488
|
-
**Before:**
|
489
|
-
```ruby
|
490
|
-
# Standard Ruby approach
|
491
|
-
json_data = '[{"user":{"name":"Alice","role":"admin"}},{"user":{"name":"Bob","role":"guest"}}]'
|
492
|
-
|
493
|
-
parsed_data = JSON.parse(json_data, symbolize_names: true)
|
494
|
-
names = parsed_data.map { |item| item[:user][:name] }
|
495
|
-
result = names.join(", ")
|
496
|
-
# => "Alice, Bob"
|
497
|
-
```
|
498
|
-
|
499
|
-
**After:**
|
500
|
-
```ruby
|
501
|
-
# With EverythingRB
|
502
|
-
json_data = '[{"user":{"name":"Alice","role":"admin"}},{"user":{"name":"Bob","role":"guest"}}]'
|
503
|
-
result = json_data.to_a.dig_map(:user, :name).join(", ")
|
504
|
-
# => "Alice, Bob"
|
505
|
-
```
|
506
|
-
|
507
|
-
### Freezing Nested Configurations
|
508
|
-
|
509
|
-
**Before:**
|
510
|
-
```ruby
|
511
|
-
# Standard Ruby approach
|
512
|
-
config_json = File.read("config.json")
|
513
|
-
config = JSON.parse(config_json, symbolize_names: true)
|
514
|
-
|
515
|
-
deep_freeze = lambda do |obj|
|
516
|
-
case obj
|
517
|
-
when Hash
|
518
|
-
obj.each_value { |v| deep_freeze.call(v) }
|
519
|
-
obj.freeze
|
520
|
-
when Array
|
521
|
-
obj.each { |v| deep_freeze.call(v) }
|
522
|
-
obj.freeze
|
523
|
-
else
|
524
|
-
obj.freeze
|
525
|
-
end
|
526
|
-
end
|
131
|
+
user.admin? # => true
|
527
132
|
|
528
|
-
|
529
|
-
|
133
|
+
# Works with Data objects too!
|
134
|
+
Person = Data.define(:active)
|
135
|
+
Person.attr_predicate(:active)
|
530
136
|
|
531
|
-
|
532
|
-
|
533
|
-
# With EverythingRB
|
534
|
-
config_json = File.read("config.json")
|
535
|
-
frozen_config = config_json.to_h.deep_freeze
|
137
|
+
person = Person.new(active: false)
|
138
|
+
person.active? # => false
|
536
139
|
```
|
537
140
|
|
538
|
-
|
141
|
+
**Extensions:** `attr_predicate`
|
539
142
|
|
540
|
-
|
541
|
-
```ruby
|
542
|
-
# Standard Ruby approach
|
543
|
-
users_json = '[{"user":{"name":"Alice","admin":true,"active":true}},{"user":{"name":"Bob","admin":true,"active":false}}]'
|
544
|
-
|
545
|
-
users = JSON.parse(users_json, symbolize_names: true)
|
546
|
-
active_admins = users.map { |u| u[:user] }.select { |u| u[:admin] && u[:active] }
|
547
|
-
admin_names = active_admins.map { |u| u[:name] }.join(", ")
|
548
|
-
# => "Alice"
|
549
|
-
```
|
143
|
+
## Full Documentation
|
550
144
|
|
551
|
-
|
552
|
-
```ruby
|
553
|
-
# With EverythingRB
|
554
|
-
users_json = '[{"user":{"name":"Alice","admin":true,"active":true}},{"user":{"name":"Bob","admin":true,"active":false}}]'
|
555
|
-
admin_names = users_json.to_a.key_map(:user).join_map(", ") do |user|
|
556
|
-
user[:name] if user[:admin] && user[:active]
|
557
|
-
end
|
558
|
-
# => "Alice"
|
559
|
-
```
|
145
|
+
For complete method listings, examples, and detailed usage, see the [API Documentation](https://itsthedevman.com/docs/everythingrb).
|
560
146
|
|
561
147
|
## Requirements
|
562
148
|
|
@@ -564,22 +150,14 @@ end
|
|
564
150
|
|
565
151
|
## Contributing
|
566
152
|
|
567
|
-
|
568
|
-
2. Create your feature branch (`git checkout -b feature/my-new-feature`)
|
569
|
-
3. Commit your changes (`git commit -am 'Add some feature'`)
|
570
|
-
4. Push to the branch (`git push origin feature/my-new-feature`)
|
571
|
-
5. Create new Pull Request
|
572
|
-
|
573
|
-
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.
|
574
154
|
|
575
155
|
## License
|
576
156
|
|
577
|
-
|
578
|
-
|
579
|
-
## Changelog
|
157
|
+
[MIT License](LICENSE.txt)
|
580
158
|
|
581
|
-
|
159
|
+
## Looking for a Software Engineer?
|
582
160
|
|
583
|
-
|
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!
|
584
162
|
|
585
|
-
|
163
|
+
[bryan@itsthedevman.com](mailto:bryan@itsthedevman.com)
|
@@ -93,6 +93,10 @@ 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
|
@@ -177,5 +181,73 @@ class Array
|
|
177
181
|
def trim_blanks
|
178
182
|
compact_blank_prefix.compact_blank_suffix
|
179
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
|
180
252
|
end
|
181
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.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bryan
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-
|
11
|
+
date: 2025-04-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ostruct
|