everythingrb 0.2.3 → 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8567a1a0f80fd1582946019cd0c378f916648f70d9a9087944f83f5ba29669be
4
- data.tar.gz: 8f42ce110e7c57529f591615d7612d75556497fc3e80fe56f0ece352847df709
3
+ metadata.gz: 330f80c66964fd22cd85850c251963ea372957a2b3190f4abcb5d0f3346989f7
4
+ data.tar.gz: f28ebe4ab45e0bb7f1e68f2553af302d6ab2dfaa0509bc3d73615a84330b6d1e
5
5
  SHA512:
6
- metadata.gz: 065ef8975736ca530d915f4d7780b1a35a6e85834f735aadbc244f5c05bd39845be26bd07102e52e649e9946769b255fb50d951ea4b335204d55c75eae213828
7
- data.tar.gz: 6edcdc737cd2dda4395d5b685c463c3af0d2fedab7d1737193105a43ce4365e0b63bdbeb7bac336868b0dea1e9857d9e0d33b55ba0f693ab818f607611ca864d
6
+ metadata.gz: ae50b585bb625724ca3e7ae917074fef6ceb25278818c88965aa10ccfae42e067b2716fc36b2f4da0d2cd4b4228b764166c12a43c212c7b44b3874ebe00aa071
7
+ data.tar.gz: 5558f7dfe032d20489d7accf298eb889ffb22cd4a215509e4d2fd90fa35c23eebefa95ca2170695d4b82a637eb2583daa5a3b11b1660b9a9ccb1fa3b9dc5dcf9
data/CHANGELOG.md CHANGED
@@ -23,6 +23,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
23
23
 
24
24
  ### Removed
25
25
 
26
+ ## [0.2.5] - 12025-03-29
27
+
28
+ ### Added
29
+
30
+ - New array trimming methods that preserve internal structure:
31
+ - `compact_prefix` - Removes nil values from the beginning of an array
32
+ - `compact_suffix` - Removes nil values from the end of an array
33
+ - `trim_nils` - Removes nil values from both ends of an array
34
+ - ActiveSupport integration with blank-aware versions:
35
+ - `compact_blank_prefix` - Removes blank values from the beginning
36
+ - `compact_blank_suffix` - Removes blank values from the end
37
+ - `trim_blanks` - Removes blank values from both ends
38
+
39
+ ## [0.2.4] - 12025-03-20
40
+
41
+ ### Changed
42
+
43
+ - Improved documentation
44
+ - Fixed an issue with `Hash#to_struct` on Ruby 3.2 would raise an exception if called on an empty Hash
45
+
26
46
  ## [0.2.3] - 12025-03-09
27
47
 
28
48
  ### Added
@@ -104,7 +124,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
104
124
 
105
125
  - Added alias `each` to `each_pair` in OpenStruct for better enumerable compatibility
106
126
 
107
- [unreleased]: https://github.com/itsthedevman/everythingrb/compare/v0.2.3...HEAD
127
+ [unreleased]: https://github.com/itsthedevman/everythingrb/compare/v0.2.5...HEAD
128
+ [0.2.5]: https://github.com/itsthedevman/everythingrb/compare/v0.2.4...v0.2.5
129
+ [0.2.4]: https://github.com/itsthedevman/everythingrb/compare/v0.2.3...v0.2.4
108
130
  [0.2.3]: https://github.com/itsthedevman/everythingrb/compare/v0.2.2...v0.2.3
109
131
  [0.2.2]: https://github.com/itsthedevman/everythingrb/compare/v0.2.1...v0.2.2
110
132
  [0.2.1]: https://github.com/itsthedevman/everythingrb/compare/v0.2.0...v0.2.1
data/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
  ![Ruby Version](https://img.shields.io/badge/ruby-3.3.7-ruby)
5
5
  [![Tests](https://github.com/itsthedevman/everythingrb/actions/workflows/main.yml/badge.svg)](https://github.com/everythingrb/sortsmith/actions/workflows/main.yml)
6
6
 
7
- Useful extensions to Ruby core classes that you never knew you needed until now.
7
+ Super handy extensions to Ruby core classes that you never knew you needed until now. Write more expressive, readable, and maintainable code with less boilerplate.
8
8
 
9
9
  ## Looking for a Software Engineer?
10
10
 
@@ -14,36 +14,25 @@ I'm currently looking for opportunities where I can tackle meaningful problems a
14
14
 
15
15
  # Table of Contents
16
16
 
17
+ - [Introduction](#introduction)
17
18
  - [Compatibility](#compatibility)
18
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)
19
27
  - [Core Extensions](#core-extensions)
20
28
  - [Array](#array)
21
- - [join_map](#join_map)
22
- - [key_map](#key_map)
23
- - [dig_map](#dig_map)
24
29
  - [Enumerable](#enumerable)
25
- - [join_map](#join_map-1)
26
30
  - [Hash](#hash)
27
- - [to_struct](#to_struct)
28
- - [to_ostruct](#to_ostruct)
29
- - [to_istruct](#to_istruct)
30
- - [join_map](#join_map-2)
31
31
  - [Module](#module)
32
- - [attr_predicate](#attr_predicate)
33
32
  - [OpenStruct](#openstruct)
34
- - [each](#each)
35
- - [map](#map)
36
- - [filter_map](#filter_map)
37
- - [join_map](#join_map-3)
38
33
  - [String](#string)
39
- - [to_h / to_a](#to_h--to_a)
40
- - [to_istruct](#to_istruct-1)
41
- - [to_ostruct](#to_ostruct-1)
42
- - [to_struct](#to_struct-1)
43
- - [to_deep_h](#to_deep_h)
44
- - [with_quotes / in_quotes](#with_quotes--in_quotes)
45
34
  - [Symbol](#symbol)
46
- - [with_quotes / in_quotes](#with_quotes--in_quotes-1)
35
+ - [Advanced Usage](#advanced-usage)
47
36
  - [Requirements](#requirements)
48
37
  - [Contributing](#contributing)
49
38
  - [License](#license)
@@ -52,6 +41,12 @@ I'm currently looking for opportunities where I can tackle meaningful problems a
52
41
 
53
42
  Also see: [API Documentation](https://itsthedevman.com/docs/everythingrb)
54
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
+
55
50
  ## Compatibility
56
51
 
57
52
  Currently tested on:
@@ -78,6 +73,131 @@ Or install it yourself as:
78
73
  $ gem install everythingrb
79
74
  ```
80
75
 
76
+ ## Features
77
+
78
+ ### Data Structure Conversions
79
+
80
+ Easily convert between different Ruby data structures:
81
+
82
+ ```ruby
83
+ # Convert any hash to an OpenStruct, Struct, or Data (immutable) object
84
+ config = { server: { host: "example.com", port: 443 } }.to_ostruct
85
+ config.server.host # => "example.com"
86
+
87
+ # Parse JSON directly to your preferred structure
88
+ '{"user":{"name":"Alice"}}'.to_istruct.user.name # => "Alice"
89
+ '{"items":[1,2,3]}'.to_struct.items # => [1, 2, 3]
90
+ ```
91
+
92
+ ### Collection Processing
93
+
94
+ Process collections with elegant, chainable methods:
95
+
96
+ ```ruby
97
+ # Extract specific data from arrays of hashes in one step
98
+ users = [{ name: "Alice", roles: ["admin"] }, { name: "Bob", roles: ["user"] }]
99
+ users.key_map(:name) # => ["Alice", "Bob"]
100
+ users.dig_map(:roles, 0) # => ["admin", "user"]
101
+
102
+ # Filter, map, and join in a single operation
103
+ [1, 2, nil, 3, 4].join_map(" | ") { |n| "Item #{n}" if n&.odd? }
104
+ # => "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
+
115
+ # Recursively parse nested JSON strings
116
+ nested = '{"user":"{\"profile\":\"{\\\"name\\\":\\\"Bob\\\"}\"}"}'
117
+ nested.to_deep_h # => { user: { profile: { name: "Bob" } } }
118
+
119
+ # Format strings with quotes
120
+ "hello".with_quotes # => "\"hello\""
121
+ ```
122
+
123
+ ### Object Freezing
124
+
125
+ Freeze nested structures with a single call:
126
+
127
+ ```ruby
128
+ config = {
129
+ api: {
130
+ key: "secret",
131
+ endpoints: ["v1", "v2"]
132
+ }
133
+ }.deep_freeze
134
+
135
+ # Everything is frozen!
136
+ config.frozen? # => true
137
+ config[:api].frozen? # => true
138
+ config[:api][:endpoints].frozen? # => true
139
+ config[:api][:endpoints][0].frozen? # => true
140
+ ```
141
+
142
+ ### Array Trimming
143
+
144
+ Clean up array boundaries without losing internal structure:
145
+
146
+ ```ruby
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:
182
+
183
+ ```ruby
184
+ # With ActiveSupport loaded
185
+ class Product
186
+ attr_accessor :tags, :category
187
+ attr_predicate :tags, :category
188
+ end
189
+
190
+ product = Product.new
191
+ product.tags = []
192
+ product.tags? # => false (empty array is not "present")
193
+
194
+ product.tags = ["sale"]
195
+ product.tags? # => true (non-empty array is "present")
196
+
197
+ product.category = ""
198
+ product.category? # => false (blank string is not "present")
199
+ ```
200
+
81
201
  ## Core Extensions
82
202
 
83
203
  ### Array
@@ -125,6 +245,34 @@ data.dig_map(:user, :profile, :name)
125
245
  # => ["Alice", "Bob"]
126
246
  ```
127
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
+
128
276
  #### `deep_freeze`
129
277
  Recursively freezes an array and all of its nested elements.
130
278
 
@@ -331,6 +479,89 @@ Wraps the symbol in double quotes
331
479
  # => :"\"hello_world\""
332
480
  ```
333
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
527
+
528
+ frozen_config = deep_freeze.call(config)
529
+ ```
530
+
531
+ **After:**
532
+ ```ruby
533
+ # With EverythingRB
534
+ config_json = File.read("config.json")
535
+ frozen_config = config_json.to_h.deep_freeze
536
+ ```
537
+
538
+ ### Filtering and Formatting Nested Collections
539
+
540
+ **Before:**
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
+ ```
550
+
551
+ **After:**
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
+ ```
560
+
561
+ ## Requirements
562
+
563
+ - Ruby 3.2 or higher
564
+
334
565
  ## Contributing
335
566
 
336
567
  1. Fork it
data/flake.lock CHANGED
@@ -20,11 +20,11 @@
20
20
  },
21
21
  "nixpkgs": {
22
22
  "locked": {
23
- "lastModified": 1736883708,
24
- "narHash": "sha256-uQ+NQ0/xYU0N1CnXsa2zghgNaOPxWpMJXSUJJ9W7140=",
23
+ "lastModified": 1742422364,
24
+ "narHash": "sha256-mNqIplmEohk5jRkqYqG19GA8MbQ/D4gQSK0Mu4LvfRQ=",
25
25
  "owner": "NixOS",
26
26
  "repo": "nixpkgs",
27
- "rev": "eb62e6aa39ea67e0b8018ba8ea077efe65807dc8",
27
+ "rev": "a84ebe20c6bc2ecbcfb000a50776219f48d134cc",
28
28
  "type": "github"
29
29
  },
30
30
  "original": {
data/flake.nix CHANGED
@@ -6,8 +6,14 @@
6
6
  flake-utils.url = "github:numtide/flake-utils";
7
7
  };
8
8
 
9
- outputs = { self, nixpkgs, flake-utils }:
10
- flake-utils.lib.eachDefaultSystem (system:
9
+ outputs =
10
+ {
11
+ self,
12
+ nixpkgs,
13
+ flake-utils,
14
+ }:
15
+ flake-utils.lib.eachDefaultSystem (
16
+ system:
11
17
  let
12
18
  pkgs = nixpkgs.legacyPackages.${system};
13
19
  in
@@ -1,5 +1,21 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ #
4
+ # Extensions to Ruby's core Array class
5
+ #
6
+ # This module adds convenient mapping, joining, and freezing functionality
7
+ # to all Arrays in your application.
8
+ #
9
+ # @example Using the extensions
10
+ # numbers = [1, 2, nil, 3]
11
+ #
12
+ # # Filter out nils and format odd numbers
13
+ # numbers.join_map(", ") { |n| "odd: #{n}" if n&.odd? }
14
+ # # => "odd: 1, odd: 3"
15
+ #
16
+ # users = [{name: "Alice", role: "admin"}, {name: "Bob", role: "user"}]
17
+ # users.key_map(:name) # => ["Alice", "Bob"]
18
+ #
3
19
  class Array
4
20
  #
5
21
  # Combines filter_map and join operations
@@ -40,11 +56,11 @@ class Array
40
56
  #
41
57
  # @param key [Symbol, String] The key to extract
42
58
  #
43
- # @return [Array] Array of values
59
+ # @return [Array] Array of values extracted from each hash
44
60
  #
45
61
  # @example
46
- # [{name: 'Alice', age: 30}, {name: 'Bob', age: 25}].key_map(:name)
47
- # # => ['Alice', 'Bob']
62
+ # [{name: "Alice", age: 30}, {name: "Bob", age: 25}].key_map(:name)
63
+ # # => ["Alice", "Bob"]
48
64
  #
49
65
  def key_map(key)
50
66
  map { |v| v[key] }
@@ -59,10 +75,10 @@ class Array
59
75
  #
60
76
  # @example
61
77
  # [
62
- # {user: {profile: {name: 'Alice'}}},
63
- # {user: {profile: {name: 'Bob'}}}
78
+ # {user: {profile: {name: "Alice"}}},
79
+ # {user: {profile: {name: "Bob"}}}
64
80
  # ].dig_map(:user, :profile, :name)
65
- # # => ['Alice', 'Bob']
81
+ # # => ["Alice", "Bob"]
66
82
  #
67
83
  def dig_map(*keys)
68
84
  map { |v| v.dig(*keys) }
@@ -81,4 +97,85 @@ class Array
81
97
  each { |v| v.respond_to?(:deep_freeze) ? v.deep_freeze : v.freeze }
82
98
  freeze
83
99
  end
100
+
101
+ #
102
+ # Removes nil values from the beginning of an array
103
+ #
104
+ # @return [Array] Array with leading nil values removed
105
+ #
106
+ # @example
107
+ # [nil, nil, 1, 2, nil, 3].compact_prefix
108
+ # # => [1, 2, nil, 3]
109
+ #
110
+ def compact_prefix
111
+ drop_while(&:nil?)
112
+ end
113
+
114
+ #
115
+ # Removes nil values from the end of an array
116
+ #
117
+ # @return [Array] Array with trailing nil values removed
118
+ #
119
+ # @example
120
+ # [1, 2, nil, 3, nil, nil].compact_suffix
121
+ # # => [1, 2, nil, 3]
122
+ #
123
+ def compact_suffix
124
+ reverse.drop_while(&:nil?).reverse
125
+ end
126
+
127
+ #
128
+ # Removes nil values from both the beginning and end of an array
129
+ #
130
+ # @return [Array] Array with leading and trailing nil values removed
131
+ #
132
+ # @example
133
+ # [nil, nil, 1, 2, nil, 3, nil, nil].trim_nils
134
+ # # => [1, 2, nil, 3]
135
+ #
136
+ def trim_nils
137
+ compact_prefix.compact_suffix
138
+ end
139
+
140
+ # ActiveSupport integrations
141
+ if defined?(ActiveSupport)
142
+ #
143
+ # Removes blank values from the beginning of an array
144
+ #
145
+ # @return [Array] Array with leading blank values removed
146
+ #
147
+ # @example With ActiveSupport loaded
148
+ # [nil, "", 1, 2, "", 3].compact_blank_prefix
149
+ # # => [1, 2, "", 3]
150
+ #
151
+ def compact_blank_prefix
152
+ drop_while(&:blank?)
153
+ end
154
+
155
+ #
156
+ # Removes blank values from the end of an array
157
+ #
158
+ # @return [Array] Array with trailing blank values removed
159
+ #
160
+ # @example With ActiveSupport loaded
161
+ # [1, 2, "", 3, nil, ""].compact_blank_suffix
162
+ # # => [1, 2, "", 3]
163
+ #
164
+ def compact_blank_suffix
165
+ reverse.drop_while(&:blank?).reverse
166
+ end
167
+
168
+ #
169
+ # Removes blank values from both the beginning and end of an array
170
+ #
171
+ # @return [Array] Array with leading and trailing blank values removed
172
+ #
173
+ # @example With ActiveSupport loaded
174
+ # [nil, "", 1, 2, "", 3, nil, ""].trim_blanks
175
+ # # => [1, 2, "", 3]
176
+ #
177
+ def trim_blanks
178
+ compact_blank_prefix.compact_blank_suffix
179
+ end
180
+ end
84
181
  end
@@ -1,5 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ #
4
+ # Extensions to Ruby's core Enumerable module
5
+ #
6
+ # These additions make working with any enumerable collection more expressive
7
+ # by combining common operations into convenient methods.
8
+ #
9
+ # @example Using join_map with a Range
10
+ # (1..5).join_map(" | ") { |n| "item-#{n}" if n.even? }
11
+ # # => "item-2 | item-4"
12
+ #
3
13
  module Enumerable
4
14
  #
5
15
  # Combines filter_map and join operations
@@ -7,7 +17,7 @@ module Enumerable
7
17
  # @param join_with [String] The delimiter to join elements with (defaults to empty string)
8
18
  # @param with_index [Boolean] Whether to include the index in the block (defaults to false)
9
19
  #
10
- # @yield [element, index] Block that filters and transforms array elements
20
+ # @yield [element, index] Block that filters and transforms elements
11
21
  # @yieldparam element [Object] The current element
12
22
  # @yieldparam index [Integer] The index of the current element (only if with_index: true)
13
23
  #
@@ -21,9 +31,9 @@ module Enumerable
21
31
  # ["a", "b", "c"].join_map(", ", with_index: true) { |char, i| "#{i}:#{char}" }
22
32
  # # => "0:a, 1:b, 2:c"
23
33
  #
24
- # @example Default behavior without block
25
- # [1, 2, nil, 3].join_map(", ")
26
- # # => "1, 2, 3"
34
+ # @example Using with other enumerables
35
+ # (1..10).join_map(" | ") { |n| "num#{n}" if n.even? }
36
+ # # => "num2 | num4 | num6 | num8 | num10"
27
37
  #
28
38
  def join_map(join_with = "", with_index: false, &block)
29
39
  block = ->(i) { i } if block.nil?
@@ -1,24 +1,52 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ #
4
+ # Extensions to Ruby's core Hash class
5
+ #
6
+ # These additions make working with hashes more convenient by adding
7
+ # conversion methods to different data structures and string formatting helpers.
8
+ #
9
+ # @example Converting to different structures
10
+ # user = { name: "Alice", roles: ["admin"] }
11
+ # user.to_struct # => #<struct name="Alice", roles=["admin"]>
12
+ # user.to_ostruct # => #<OpenStruct name="Alice", roles=["admin"]>
13
+ #
14
+ # # Filtering and joining hash entries
15
+ # { a: 1, b: nil, c: 3 }.join_map(", ") { |k, v| "#{k}:#{v}" if v }
16
+ # # => "a:1, c:3"
17
+ #
3
18
  class Hash
4
19
  #
5
- # Combines filter_map and join operations
20
+ # A minimal empty struct for Ruby 3.2+ compatibility
21
+ #
22
+ # Ruby 3.2 enforces stricter argument handling for Struct. This means trying to create
23
+ # a Struct from an empty Hash will result in an ArgumentError being raised.
24
+ # This is trying to keep a consistent experience with that version and newer versions.
25
+ #
26
+ # @return [Struct] A struct with a single nil-valued field
27
+ #
28
+ # @api private
6
29
  #
7
- # @see Array#join_map
30
+ EMPTY_STRUCT = Struct.new(:_).new(nil)
31
+
32
+ #
33
+ # Combines filter_map and join operations
8
34
  #
9
35
  # @param join_with [String] The delimiter to join elements with (defaults to empty string)
10
36
  #
11
- # @yield [Object] Block that filters and transforms hash values
37
+ # @yield [key, value] Block that filters and transforms hash entries
38
+ # @yieldparam key [Object] The current key
39
+ # @yieldparam value [Object] The current value
12
40
  #
13
- # @return [String] Joined string of filtered and transformed values
41
+ # @return [String] Joined string of filtered and transformed entries
14
42
  #
15
43
  # @example
16
- # { a: 1, b: 2, c: nil, d: 3 }.join_map(" ") { |v| v&.to_s if v&.odd? }
17
- # # => "1 3"
44
+ # { a: 1, b: nil, c: 2, d: nil, e: 3 }.join_map(", ") { |k, v| "#{k}-#{v}" if v }
45
+ # # => "a-1, c-2, e-3"
18
46
  #
19
- # @example
20
- # { a: 1, b: 2, c: nil, d: 3 }.join_map(", ")
21
- # # => "a, 2, b, 2, c, d, 3"
47
+ # @example Without a block
48
+ # { a: 1, b: nil, c: 2 }.join_map(" ")
49
+ # # => "a 1 b c 2"
22
50
  #
23
51
  def join_map(join_with = "", &block)
24
52
  block = ->(kv_pair) { kv_pair.compact } if block.nil?
@@ -29,7 +57,13 @@ class Hash
29
57
  #
30
58
  # Converts hash to an immutable Data structure
31
59
  #
32
- # @return [Data]
60
+ # @return [Data] An immutable Data object with the same structure
61
+ #
62
+ # @example
63
+ # hash = { person: { name: "Bob", age: 30 } }
64
+ # data = hash.to_istruct
65
+ # data.person.name # => "Bob"
66
+ # data.class # => Data
33
67
  #
34
68
  def to_istruct
35
69
  recurse = lambda do |input|
@@ -49,9 +83,18 @@ class Hash
49
83
  #
50
84
  # Converts hash to a Struct recursively
51
85
  #
52
- # @return [Struct]
86
+ # @return [Struct] A struct with methods matching hash keys
87
+ #
88
+ # @example
89
+ # hash = { user: { name: "Alice", roles: ["admin"] } }
90
+ # struct = hash.to_struct
91
+ # struct.user.name # => "Alice"
92
+ # struct.class # => Struct
53
93
  #
54
94
  def to_struct
95
+ # For Ruby 3.2, it raises if you attempt to create a Struct with no keys
96
+ return EMPTY_STRUCT if RUBY_VERSION.start_with?("3.2") && empty?
97
+
55
98
  recurse = lambda do |value|
56
99
  case value
57
100
  when Hash
@@ -69,7 +112,12 @@ class Hash
69
112
  #
70
113
  # Converts hash to an OpenStruct recursively
71
114
  #
72
- # @return [OpenStruct]
115
+ # @return [OpenStruct] An OpenStruct with methods matching hash keys
116
+ #
117
+ # @example
118
+ # hash = { config: { api_key: "secret" } }
119
+ # config = hash.to_ostruct
120
+ # config.config.api_key # => "secret"
73
121
  #
74
122
  def to_ostruct
75
123
  recurse = lambda do |value|
@@ -91,7 +139,7 @@ class Hash
91
139
  #
92
140
  # @return [self] Returns the frozen hash
93
141
  #
94
- # @example Freeze a hash with nested structures
142
+ # @example
95
143
  # { user: { name: "Alice", roles: ["admin"] } }.deep_freeze
96
144
  # # => Hash and all nested structures are now frozen
97
145
  #
@@ -1,5 +1,21 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ #
4
+ # Extensions to Ruby's core Module class
5
+ #
6
+ # These additions provide a convenient way to create boolean-style accessor
7
+ # methods for any class.
8
+ #
9
+ # @example Creating predicate methods
10
+ # class User
11
+ # attr_accessor :admin
12
+ # attr_predicate :admin
13
+ # end
14
+ #
15
+ # user = User.new
16
+ # user.admin = true
17
+ # user.admin? # => true
18
+ #
3
19
  class Module
4
20
  #
5
21
  # Creates predicate (boolean) methods that return true/false
@@ -8,7 +24,7 @@ class Module
8
24
  #
9
25
  # Note: If ActiveSupport is loaded, this will check if the value is present? instead of truthy
10
26
  #
11
- # @param *attributes [Array<Symbol, String>] Attribute names
27
+ # @param attributes [Array<Symbol, String>] Attribute names
12
28
  #
13
29
  # @return [nil]
14
30
  #
@@ -1,11 +1,22 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ #
4
+ # Extensions to Ruby's OpenStruct class
5
+ #
6
+ # These additions make OpenStructs way more flexible with enumeration
7
+ # methods and ActiveSupport integration.
8
+ #
9
+ # @example Using enumeration methods
10
+ # person = OpenStruct.new(name: "Alice", age: 30)
11
+ # person.map { |k, v| "#{k} is #{v}" } # => ["name is Alice", "age is 30"]
12
+ #
3
13
  class OpenStruct
14
+ # ActiveSupport integrations
4
15
  if defined?(ActiveSupport)
5
16
  #
6
17
  # Checks if the OpenStruct has no attributes
7
18
  #
8
- # @return [Boolean]
19
+ # @return [Boolean] true if the OpenStruct has no attributes
9
20
  #
10
21
  def blank?
11
22
  @table.blank?
@@ -14,7 +25,7 @@ class OpenStruct
14
25
  #
15
26
  # Checks if the OpenStruct has any attributes
16
27
  #
17
- # @return [Boolean]
28
+ # @return [Boolean] true if the OpenStruct has attributes
18
29
  #
19
30
  def present?
20
31
  @table.present?
@@ -26,7 +37,15 @@ class OpenStruct
26
37
  #
27
38
  # Maps over OpenStruct entries and returns an array
28
39
  #
29
- # @return [Enumerator, Array] Returns a new array, or enumerator if block is nil
40
+ # @yield [key, value] Block that transforms each key-value pair
41
+ # @yieldparam key [Symbol] The attribute name
42
+ # @yieldparam value [Object] The attribute value
43
+ #
44
+ # @return [Array, Enumerator] Results of mapping or an Enumerator if no block given
45
+ #
46
+ # @example
47
+ # struct = OpenStruct.new(a: 1, b: 2)
48
+ # struct.map { |key, value| [key, value * 2] } # => [[:a, 2], [:b, 4]]
30
49
  #
31
50
  def map(&)
32
51
  @table.map(&)
@@ -35,7 +54,15 @@ class OpenStruct
35
54
  #
36
55
  # Maps over OpenStruct entries and returns an array without nil values
37
56
  #
38
- # @return [Enumerator, Array] Returns a new array, or enumerator if block is nil
57
+ # @yield [key, value] Block that transforms each key-value pair
58
+ # @yieldparam key [Symbol] The attribute name
59
+ # @yieldparam value [Object] The attribute value
60
+ #
61
+ # @return [Array, Enumerator] Non-nil results of mapping or an Enumerator if no block given
62
+ #
63
+ # @example
64
+ # struct = OpenStruct.new(a: 1, b: nil, c: 2)
65
+ # struct.filter_map { |key, value| value * 2 if value } # => [2, 4]
39
66
  #
40
67
  def filter_map(&block)
41
68
  return enum_for(:filter_map) unless block
@@ -48,16 +75,18 @@ class OpenStruct
48
75
  #
49
76
  # @param join_with [String] The delimiter to join elements with (defaults to empty string)
50
77
  #
51
- # @yield [Object] Block that filters and transforms hash elements
78
+ # @yield [key, value] Block that filters and transforms OpenStruct entries
79
+ # @yieldparam key [Symbol] The attribute name
80
+ # @yieldparam value [Object] The attribute value
52
81
  #
53
- # @return [String] Joined string of filtered and transformed elements
82
+ # @return [String] Joined string of filtered and transformed entries
54
83
  #
55
84
  # @example
56
85
  # object = OpenStruct.new(a: 1, b: nil, c: 3)
57
86
  # object.join_map(" ") { |k, v| "#{k}-#{v}" if v }
58
87
  # # => "a-1 c-3"
59
88
  #
60
- # @example
89
+ # @example Default behavior without block
61
90
  # object = OpenStruct.new(a: 1, b: nil, c: 3)
62
91
  # object.join_map(", ")
63
92
  # # => "a, 1, b, c, 3"
@@ -69,7 +98,9 @@ class OpenStruct
69
98
  end
70
99
 
71
100
  #
72
- # @return [self]
101
+ # Returns self (identity method for consistent interfaces)
102
+ #
103
+ # @return [self] Returns the OpenStruct
73
104
  #
74
105
  def to_ostruct
75
106
  self
@@ -1,10 +1,26 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ #
4
+ # Extensions to Ruby's core String class
5
+ #
6
+ # These additions make working with JSON strings easy by providing methods for conversion
7
+ # to various Ruby data structures. Plus some nice formatting helpers that saves tons of typing
8
+ #
9
+ # @example Converting JSON to different structures
10
+ # json = '{"user": {"name": "Alice", "admin": true}}'
11
+ # json.to_h # => {user: {name: "Alice", admin: true}}
12
+ # json.to_istruct.user.name # => "Alice"
13
+ # json.to_ostruct.user.name # => "Alice"
14
+ #
3
15
  class String
4
16
  #
5
17
  # Converts JSON string to Hash, returning nil if it failed
6
18
  #
7
- # @return [Hash, nil] Parsed JSON as hash
19
+ # @return [Hash, nil] Parsed JSON as hash or nil if invalid JSON
20
+ #
21
+ # @example
22
+ # '{"name": "Alice"}'.to_h # => {name: "Alice"}
23
+ # "invalid json".to_h # => nil
8
24
  #
9
25
  def to_h
10
26
  JSON.parse(self, symbolize_names: true)
@@ -18,7 +34,14 @@ class String
18
34
  # Deep parsing of nested JSON strings
19
35
  # Recursively attempts to parse string values as JSON
20
36
  #
21
- # @return [Hash] Deeply parsed hash
37
+ # @return [Hash] Deeply parsed hash with all nested JSON strings converted
38
+ #
39
+ # @example
40
+ # nested_json = '{
41
+ # "user": "{\"name\":\"Alice\",\"roles\":[\"admin\"]}"
42
+ # }'
43
+ # nested_json.to_deep_h
44
+ # # => {user: {name: "Alice", roles: ["admin"]}}
22
45
  #
23
46
  def to_deep_h
24
47
  recursive_convert = lambda do |object|
@@ -48,7 +71,11 @@ class String
48
71
  # Attempts to parse JSON and convert to Data struct.
49
72
  # Returns nil if string does not contain valid JSON
50
73
  #
51
- # @return [nil, Data]
74
+ # @return [Data, nil] Immutable Data structure or nil if invalid JSON
75
+ #
76
+ # @example
77
+ # '{"name": "Alice"}'.to_istruct # => #<data name="Alice">
78
+ # "not json".to_istruct # => nil
52
79
  #
53
80
  def to_istruct
54
81
  to_h&.to_istruct
@@ -58,7 +85,12 @@ class String
58
85
  # Attempts to parse JSON and convert to OpenStruct.
59
86
  # Returns nil if string does not contain valid JSON
60
87
  #
61
- # @return [nil, OpenStruct]
88
+ # @return [OpenStruct, nil] OpenStruct or nil if invalid JSON
89
+ #
90
+ # @example
91
+ # '{"name": "Alice"}'.to_ostruct # => #<OpenStruct name="Alice">
92
+ # "not json".to_ostruct # => nil
93
+ #
62
94
  def to_ostruct
63
95
  to_h&.to_ostruct
64
96
  end
@@ -67,16 +99,24 @@ class String
67
99
  # Attempts to parse JSON and convert to Struct.
68
100
  # Returns nil if string does not contain valid JSON
69
101
  #
70
- # @return [nil, Struct]
102
+ # @return [Struct, nil] Struct or nil if invalid JSON
103
+ #
104
+ # @example
105
+ # '{"name": "Alice"}'.to_struct # => #<struct name="Alice">
106
+ # "not json".to_struct # => nil
71
107
  #
72
108
  def to_struct
73
109
  to_h&.to_struct
74
110
  end
75
111
 
76
112
  #
77
- # Returns self wrapped in double quotes.
113
+ # Returns self wrapped in double quotes
114
+ #
115
+ # @return [String] The string with surrounding double quotes
78
116
  #
79
- # @return [String]
117
+ # @example
118
+ # "Hello World".with_quotes # => "\"Hello World\""
119
+ # "Quote \"me\"".with_quotes # => "\"Quote \\\"me\\\"\""
80
120
  #
81
121
  def with_quotes
82
122
  %("#{self}")
@@ -1,10 +1,22 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ #
4
+ # Extensions to Ruby's core Symbol class
5
+ #
6
+ # These additions provide handy formatting helpers for symbols.
7
+ #
8
+ # @example
9
+ # :hello_world.with_quotes # => :"\"hello_world\""
10
+ #
3
11
  class Symbol
4
12
  #
5
- # Returns self wrapped in double quotes.
13
+ # Returns self wrapped in double quotes
6
14
  #
7
- # @return [Symbol]
15
+ # @return [Symbol] The symbol with surrounding double quotes
16
+ #
17
+ # @example
18
+ # :hello_world.with_quotes # => :"\"hello_world\""
19
+ # :hello_world.in_quotes # => :"\"hello_world\""
8
20
  #
9
21
  def with_quotes
10
22
  :"\"#{self}\""
@@ -1,5 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ #
4
+ # EverythingRB - Super handy extensions to Ruby's core classes
5
+ #
6
+ # @author Bryan "itsthedevman"
7
+ #
3
8
  module Everythingrb
4
- VERSION = "0.2.3"
9
+ # Current version of the everythingrb gem
10
+ VERSION = "0.2.5"
5
11
  end
data/lib/everythingrb.rb CHANGED
@@ -12,5 +12,26 @@ require_relative "everythingrb/core/ostruct"
12
12
  require_relative "everythingrb/core/string"
13
13
  require_relative "everythingrb/core/symbol"
14
14
 
15
+ #
16
+ # EverythingRB - Super handy extensions to Ruby's core classes
17
+ #
18
+ # This gem enhances Ruby's built-in classes with useful methods that make
19
+ # your code more expressive and fun to write. Just require "everythingrb"
20
+ # and all the extensions are automatically available!
21
+ #
22
+ # @author Bryan "itsthedevman"
23
+ # @since 0.1.0
24
+ #
25
+ # @example Basic usage
26
+ # # In your Gemfile
27
+ # gem "everythingrb"
28
+ #
29
+ # # In your code
30
+ # require "everythingrb"
31
+ #
32
+ # # Now you have access to all the extensions!
33
+ # users = [{name: "Alice"}, {name: "Bob"}]
34
+ # users.key_map(:name).join(", ") # => "Alice, Bob"
35
+ #
15
36
  module Everythingrb
16
37
  end
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.2.3
4
+ version: 0.2.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bryan
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-03-10 00:00:00.000000000 Z
11
+ date: 2025-03-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ostruct