darthjee-core_ext 2.0.0 → 3.1.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.
Files changed (63) hide show
  1. checksums.yaml +5 -5
  2. data/.circleci/config.yml +61 -10
  3. data/.github/copilot-instructions.md +99 -0
  4. data/.github/core_ext-usage.md +324 -0
  5. data/.rubocop.yml +22 -4
  6. data/.rubocop_todo.yml +28 -8
  7. data/Dockerfile +19 -3
  8. data/Gemfile +20 -0
  9. data/Makefile +7 -0
  10. data/README.md +9 -6
  11. data/Rakefile +3 -0
  12. data/config/check_specs.yml +20 -0
  13. data/config/yardstick.yml +8 -36
  14. data/core_ext.gemspec +3 -13
  15. data/core_ext.jpg +0 -0
  16. data/docker-compose.yml +9 -0
  17. data/lib/darthjee/core_ext/array/hash_builder.rb +14 -1
  18. data/lib/darthjee/core_ext/array.rb +5 -2
  19. data/lib/darthjee/core_ext/class.rb +1 -0
  20. data/lib/darthjee/core_ext/enumerable.rb +6 -5
  21. data/lib/darthjee/core_ext/hash/cameliazable.rb +13 -13
  22. data/lib/darthjee/core_ext/hash/chain_fetcher.rb +23 -2
  23. data/lib/darthjee/core_ext/hash/changeable.rb +4 -4
  24. data/lib/darthjee/core_ext/hash/deep_hash_constructor/setter.rb +12 -1
  25. data/lib/darthjee/core_ext/hash/deep_hash_constructor.rb +9 -3
  26. data/lib/darthjee/core_ext/hash/key_changeable.rb +15 -15
  27. data/lib/darthjee/core_ext/hash/key_changer.rb +21 -11
  28. data/lib/darthjee/core_ext/hash/keys_sorter.rb +13 -0
  29. data/lib/darthjee/core_ext/hash/squasher.rb +8 -2
  30. data/lib/darthjee/core_ext/hash/transformable.rb +2 -1
  31. data/lib/darthjee/core_ext/hash/value_changer.rb +18 -0
  32. data/lib/darthjee/core_ext/hash.rb +2 -2
  33. data/lib/darthjee/core_ext/numeric.rb +3 -2
  34. data/lib/darthjee/core_ext/object.rb +0 -2
  35. data/lib/darthjee/core_ext/time.rb +1 -0
  36. data/lib/darthjee/core_ext/version.rb +1 -1
  37. data/lib/darthjee/core_ext.rb +1 -0
  38. data/lib/darthjee.rb +1 -0
  39. data/spec/integration/readme/class_spec.rb +3 -3
  40. data/spec/integration/readme/hash_spec.rb +10 -1
  41. data/spec/integration/yard/darthjee/core_ext/array_spec.rb +1 -1
  42. data/spec/integration/yard/darthjee/core_ext/class/default_value_spec.rb +10 -8
  43. data/spec/integration/yard/darthjee/core_ext/hash/deep_hash_constructor_spec.rb +10 -10
  44. data/spec/integration/yard/darthjee/core_ext/hash/squasher_spec.rb +3 -3
  45. data/spec/integration/yard/darthjee/core_ext/hash/transformable_spec.rb +3 -3
  46. data/spec/integration/yard/darthjee/core_ext/hash/transposeable_spec.rb +1 -1
  47. data/spec/integration/yard/darthjee/core_ext/hash/value_changer_spec.rb +1 -1
  48. data/spec/lib/array_spec.rb +2 -1
  49. data/spec/lib/darthjee/core_ext/hash/value_changer_spec.rb +1 -1
  50. data/spec/lib/hash_spec.rb +1 -7
  51. data/spec/lib/symbol_spec.rb +2 -2
  52. data/spec/spec_helper.rb +9 -2
  53. data/spec/support/models/client.rb +2 -1
  54. data/spec/support/models/dummy_iterator.rb +4 -4
  55. data/spec/support/models/hash/value_changer/dummy.rb +1 -0
  56. data/spec/support/shared_examples/array/array_random.rb +2 -3
  57. data/spec/support/shared_examples/hash/chain_hash_keys_changer.rb +1 -1
  58. data/spec/support/shared_examples/hash/hash_keys_changer.rb +3 -3
  59. data/spec/support/shared_examples/hash/map_to_hash.rb +1 -1
  60. data/spec/support/shared_examples/hash/remap.rb +4 -4
  61. data/spec/support/shared_examples/hash/value_changer.rb +2 -2
  62. metadata +15 -223
  63. data/mech.jpg +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 8fa1c8ad67a3ebb2c4e0d3e44d2ece2404cb679e
4
- data.tar.gz: 2362ff51b7d433bc82fb51314496af9801145bde
2
+ SHA256:
3
+ metadata.gz: df2eab1682af7c7411711127f5ed298a7084c802b5f13b9b2f0e809040488edf
4
+ data.tar.gz: 4bc429c6fbd6a6f920c6ea8657260a975ef475db5115919ee4f46927c1a49b1e
5
5
  SHA512:
6
- metadata.gz: ac6b7fe0376149cb813893fdb64822f69e0f78972b21bb19d8d1be2192b395b386a2d565956197c613576aff80075d1eb609683af342a3b0728553c6fb32e9a8
7
- data.tar.gz: 278121be9eb0a87815c2b878059a37c583b27296d9667c848e75a9f05bdd4b09b85051a0f2c254aafbb83da0691d6fa6fe5d611810536bac3e62b3056b52701f
6
+ metadata.gz: e85edf7601b5ba6196329fc472d15f6f67ef6df19bcf42addbea475fc0f8759000109d5d84a5888d2a5073d788648848404815c104ec15ebea0d6fc211a8f035
7
+ data.tar.gz: d726c8c5cf025ddf824fd9449392d496015f4ff83adec37531746446d0cdee7dfd07fff3381aa2baca50dad986097baca57a3151cc6ba76b26675d90220cdaf0
data/.circleci/config.yml CHANGED
@@ -1,31 +1,82 @@
1
1
  version: 2
2
+ workflows:
3
+ version: 2
4
+ test-and-build:
5
+ jobs:
6
+ - test:
7
+ filters:
8
+ tags:
9
+ only: /.*/
10
+ - checks:
11
+ filters:
12
+ tags:
13
+ only: /.*/
14
+ - build-and-release:
15
+ requires: [test, checks]
16
+ filters:
17
+ tags:
18
+ only: /\d+\.\d+\.\d+/
19
+ branches:
20
+ only:
21
+ - main
2
22
  jobs:
3
- build:
23
+ test:
4
24
  docker:
5
- - image: darthjee/circleci_ruby_gems_240:0.0.2
25
+ - image: darthjee/circleci_ruby_331:1.1.1
26
+ environment:
27
+ PROJECT: core_ext
6
28
  steps:
7
29
  - checkout
8
- - run:
9
- name: Prepare Coverage Test Report
10
- command: cc-test-reporter before-build
11
30
  - run:
12
31
  name: Bundle Install
13
32
  command: bundle install
14
33
  - run:
15
34
  name: RSpec
16
35
  command: bundle exec rspec
36
+ - run:
37
+ name: Upload coverage to Codacy
38
+ command: bash <(curl -Ls https://coverage.codacy.com/get.sh) report -r coverage/lcov/project.lcov
39
+ checks:
40
+ docker:
41
+ - image: darthjee/circleci_ruby_331:1.1.1
42
+ environment:
43
+ PROJECT: core_ext
44
+ steps:
45
+ - checkout
46
+ - run:
47
+ name: Bundle Install
48
+ command: bundle install
17
49
  - run:
18
50
  name: Rubocop
19
51
  command: rubocop
20
- - run:
21
- name: Coverage Test Report
22
- command: cc-test-reporter after-build --exit-code $?
23
52
  - run:
24
53
  name: Yardstick coverage check
25
54
  command: bundle exec rake verify_measurements
26
55
  - run:
27
56
  name: Check version documentation
28
- command: scripts/check_readme.sh
57
+ command: PROJECT=darthjee-core_ext check_readme.sh
29
58
  - run:
30
59
  name: Rubycritcs check
31
- command: scripts/rubycritic.sh
60
+ command: rubycritic.sh
61
+ - run:
62
+ name: Check unit tests
63
+ command: check_specs
64
+ build-and-release:
65
+ docker:
66
+ - image: darthjee/circleci_ruby_331:1.1.1
67
+ environment:
68
+ PROJECT: core_ext
69
+ steps:
70
+ - checkout
71
+ - run:
72
+ name: Bundle Install
73
+ command: bundle install
74
+ - run:
75
+ name: Signin
76
+ command: build_gem.sh signin
77
+ - run:
78
+ name: Build Gem
79
+ command: PROJECT=darthjee-core_ext build_gem.sh build
80
+ - run:
81
+ name: Push Gem
82
+ command: PROJECT=darthjee-core_ext build_gem.sh push
@@ -0,0 +1,99 @@
1
+ # GitHub Copilot Instructions for `darthjee/core_ext`
2
+
3
+ ## Project Overview
4
+
5
+ `core_ext` is a Ruby gem that **adds methods to Ruby's standard objects** (core extensions). It extends built-in classes such as `Array`, `Hash`, `Symbol`, `Enumerable`, `Date`, `Object`, `Numeric`, and others with additional utility methods, while maintaining compatibility and predictable behavior.
6
+
7
+ - Gem name: `darthjee-core_ext`
8
+ - YARD docs: [rubydoc.info/gems/darthjee-core_ext](https://www.rubydoc.info/gems/darthjee-core_ext)
9
+ - Extensions live under `lib/darthjee/core_ext/`
10
+
11
+ ---
12
+
13
+ ## Language
14
+
15
+ - All code, comments, commit messages, pull request titles, PR descriptions, review comments, and documentation **must be written in English**.
16
+ - No exceptions: even if the author's primary language is not English, all contributions to this repository must use English.
17
+
18
+ ---
19
+
20
+ ## Testing
21
+
22
+ - **Every new feature or bug fix must include tests.**
23
+ - Tests are written using RSpec and live under `spec/`.
24
+ - Follow the existing test structure: unit tests mirror the `lib/` directory layout under `spec/lib/`.
25
+ - If a file does not have a corresponding spec file and **intentionally** has no tests (e.g., it is a pure re-export or namespace file), it must be listed in `config/check_specs.yml` under the `ignore:` key.
26
+ - Do **not** add files to `config/check_specs.yml` as a shortcut to avoid writing tests. Only files that genuinely cannot or should not have direct tests belong there.
27
+ - Run the full test suite before opening a PR: `bundle exec rspec`.
28
+
29
+ ---
30
+
31
+ ## Documentation
32
+
33
+ - All public methods must be documented using **YARD** syntax.
34
+ - Use `@param`, `@return`, `@example`, and `@raise` tags where applicable.
35
+ - Include at least one `@example` block for methods that demonstrate non-trivial behavior.
36
+ - Keep documentation close to the code (inline in the source file).
37
+ - Example:
38
+
39
+ ```ruby
40
+ # Converts the hash keys to camel case.
41
+ #
42
+ # @param [Symbol] type the case type (:lower or :upper)
43
+ # @return [Hash] a new hash with camel-cased keys
44
+ # @example
45
+ # { my_key: 1 }.camelize_keys #=> { myKey: 1 }
46
+ def camelize_keys(type = :lower)
47
+ # ...
48
+ end
49
+ ```
50
+
51
+ ---
52
+
53
+ ## Design Principles
54
+
55
+ ### Single Responsibility
56
+
57
+ - Classes and methods should have **one reason to change** (Single Responsibility Principle).
58
+ - Prefer small, focused methods. If a method is doing more than one thing, extract the extra responsibility into a helper method or a collaborator class.
59
+ - Follow the approach described by **Sandi Metz** in *99 Bottles of OOP*:
60
+ - Prefer simple, obvious code over clever code.
61
+ - Let the tests drive refactoring: make it work, make it right, make it fast.
62
+ - Use small objects that collaborate through messages.
63
+ - Avoid premature abstraction; introduce it only when duplication demands it.
64
+
65
+ ### Law of Demeter
66
+
67
+ - Avoid **Law of Demeter violations**: do not chain method calls across multiple object boundaries (e.g., `a.b.c.d`).
68
+ - Instead, delegate behavior closer to the data: expose a method on the intermediate object, or use a helper that encapsulates the traversal.
69
+ - Code that violates the Law of Demeter increases coupling and makes refactoring harder.
70
+
71
+ ```ruby
72
+ # Avoid
73
+ user.address.city.upcase
74
+
75
+ # Prefer
76
+ user.city_upcased
77
+ ```
78
+
79
+ ### General Style
80
+
81
+ - Prefer composition over inheritance.
82
+ - Keep class hierarchies shallow.
83
+ - Avoid monkey-patching outside of the intended extension pattern of this gem.
84
+ - New core extensions must be clearly scoped to a specific Ruby class and added in the appropriate file under `lib/darthjee/core_ext/`.
85
+
86
+ ---
87
+
88
+ ## Pull Request Guidelines
89
+
90
+ - PR titles and descriptions must be in **English**.
91
+ - Every PR should include:
92
+ - A clear description of what changed and why.
93
+ - References to any related issues.
94
+ - Confirmation that tests pass locally.
95
+ - Reviewers should check that:
96
+ - New code is covered by tests.
97
+ - Documentation follows YARD conventions.
98
+ - Design principles (single responsibility, Demeter) are respected.
99
+ - No file was added to `config/check_specs.yml` without a valid justification.
@@ -0,0 +1,324 @@
1
+ # Using `darthjee-core_ext` in Your Project
2
+
3
+ This file is intended to be copied into the `.github/` folder of other projects.
4
+ It provides GitHub Copilot (and developers) with a clear, actionable guide on how
5
+ to use the [`darthjee-core_ext`](https://github.com/darthjee/core_ext) Ruby gem.
6
+
7
+ ---
8
+
9
+ ## What Is `core_ext`?
10
+
11
+ `darthjee-core_ext` is a Ruby gem that extends Ruby's built-in (core) classes with
12
+ additional utility methods. It follows the convention of "core extensions" — monkey-patching
13
+ standard objects such as `Array`, `Hash`, `Symbol`, `Enumerable`, `Date`, `Object`,
14
+ `Numeric`, and `Math` with useful helpers, while maintaining predictable behavior.
15
+
16
+ - **Gem name**: `darthjee-core_ext`
17
+ - **Source**: <https://github.com/darthjee/core_ext>
18
+ - **YARD docs**: <https://www.rubydoc.info/gems/darthjee-core_ext>
19
+ - **Current stable release**: 3.0.0
20
+
21
+ ---
22
+
23
+ ## Adding the Gem to Your Project
24
+
25
+ ### Via RubyGems (recommended)
26
+
27
+ Add to your `Gemfile`:
28
+
29
+ ```ruby
30
+ gem 'darthjee-core_ext'
31
+ ```
32
+
33
+ Or pin a specific version for reproducible builds:
34
+
35
+ ```ruby
36
+ gem 'darthjee-core_ext', '~> 3.0'
37
+ ```
38
+
39
+ ### Via GitHub (source)
40
+
41
+ ```ruby
42
+ gem 'darthjee-core_ext',
43
+ github: 'darthjee/core_ext',
44
+ branch: 'main'
45
+ ```
46
+
47
+ ---
48
+
49
+ ## Installing
50
+
51
+ After adding the gem to your `Gemfile`, run:
52
+
53
+ ```shell
54
+ bundle install
55
+ ```
56
+
57
+ Or install it system-wide:
58
+
59
+ ```shell
60
+ gem install darthjee-core_ext
61
+ ```
62
+
63
+ ---
64
+
65
+ ## Loading the Gem
66
+
67
+ When using Bundler (Rails, typical Ruby apps), the gem is loaded automatically via
68
+ `Bundler.require`. No explicit `require` is needed.
69
+
70
+ In plain Ruby scripts or when Bundler auto-require is disabled, add:
71
+
72
+ ```ruby
73
+ require 'darthjee/core_ext'
74
+ ```
75
+
76
+ ---
77
+
78
+ ## Extensions Provided
79
+
80
+ ### `Hash`
81
+
82
+ | Method | Description |
83
+ |---|---|
84
+ | `#append_to_keys` | Appends a string to every key |
85
+ | `#prepend_to_keys` | Prepends a string to every key |
86
+ | `#camelize_keys` / `#camelize_keys!` | Converts keys to CamelCase |
87
+ | `#lower_camelize_keys` / `#lower_camelize_keys!` | Converts keys to lowerCamelCase |
88
+ | `#underscore_keys` / `#underscore_keys!` | Converts keys to snake_case |
89
+ | `#change_keys` / `#change_keys!` | Transforms keys with a block |
90
+ | `#chain_change_keys` / `#chain_change_keys!` | Transforms keys by chaining method calls |
91
+ | `#change_values` / `#change_values!` | Transforms values with a block (optionally recursive) |
92
+ | `#chain_fetch` | Fetches nested keys in a chain |
93
+ | `#exclusive_merge` / `#exclusive_merge!` | Merges only existing keys |
94
+ | `#remap_keys` / `#remap_keys!` | Renames keys based on a mapping hash |
95
+ | `#sort_keys` / `#sort_keys!` | Sorts the hash by its keys |
96
+ | `#squash` / `#squash!` | Flattens a deep hash into a single-level hash |
97
+ | `#to_deep_hash` / `#to_deep_hash!` | Expands a squashed hash back into a deep hash |
98
+ | `#map_to_hash` | Maps values keeping original keys |
99
+ | `#transpose` / `#transpose!` | Swaps keys with values |
100
+
101
+ ### `Array`
102
+
103
+ | Method | Description |
104
+ |---|---|
105
+ | `#as_hash` | Zips the array with a keys array into a Hash |
106
+ | `#average` | Returns the arithmetic average of the elements |
107
+ | `#chain_map` | Applies `.map` in a chain of method calls |
108
+ | `#mapk` | Maps by fetching nested keys from hashes inside the array |
109
+ | `#procedural_join` | Joins elements with a dynamically computed separator |
110
+ | `#random` | Returns a random element |
111
+ | `#random!` | Removes and returns a random element |
112
+
113
+ ### `Symbol`
114
+
115
+ | Method | Description |
116
+ |---|---|
117
+ | `#camelize` | Camelizes the symbol (`:my_sym` → `:MySym`) |
118
+ | `#underscore` | Underscores a camelized symbol (`:MySym` → `:my_sym`) |
119
+
120
+ ### `Enumerable` (available on `Array`, `Hash`, and any `Enumerable`)
121
+
122
+ | Method | Description |
123
+ |---|---|
124
+ | `#clean` | Returns a copy with empty values removed |
125
+ | `#clean!` | Removes empty values in place (recursive) |
126
+ | `#map_and_find` | Maps and stops at the first truthy result |
127
+ | `#map_and_select` | Maps and returns only truthy results |
128
+ | `#map_to_hash` | Maps values, using original elements as keys |
129
+
130
+ ### `Object` (available on every Ruby object)
131
+
132
+ | Method | Description |
133
+ |---|---|
134
+ | `#is_any?` | Returns `true` if the object is an instance of any of the given classes |
135
+ | `#trueful?` | Returns `true` only when the object is not `nil` (unlike `#present?`, `[]` and `{}` are trueful) |
136
+
137
+ ### `Date`
138
+
139
+ | Method | Description |
140
+ |---|---|
141
+ | `#days_between` | Returns the absolute number of days between two dates |
142
+
143
+ ### `Math`
144
+
145
+ | Method | Description |
146
+ |---|---|
147
+ | `.average` | Calculates the (optionally weighted) average of values |
148
+
149
+ ### `Class`
150
+
151
+ | Method | Description |
152
+ |---|---|
153
+ | `.default_value` | Adds a reader that returns the same default instance every time |
154
+ | `.default_values` | Adds multiple readers sharing the same default instance |
155
+ | `.default_reader` | Adds a reader that returns a default value only when the instance variable was never set |
156
+ | `.default_readers` | Adds multiple default readers sharing the same default value |
157
+
158
+ ---
159
+
160
+ ## Code Examples
161
+
162
+ ### Hash key transformations
163
+
164
+ ```ruby
165
+ # Converting API responses from camelCase to snake_case
166
+ response = { 'userId' => 1, 'firstName' => 'Alice' }
167
+ response.underscore_keys # => { 'user_id' => 1, 'first_name' => 'Alice' }
168
+
169
+ # Preparing a payload for a camelCase API
170
+ params = { user_id: 1, first_name: 'Alice' }
171
+ params.lower_camelize_keys
172
+ # => { userId: 1, firstName: 'Alice' }
173
+
174
+ # Equivalent long form with explicit option
175
+ params.camelize_keys(uppercase_first_letter: false)
176
+ # => { userId: 1, firstName: 'Alice' }
177
+ ```
178
+
179
+ ### Fetching nested values safely
180
+
181
+ ```ruby
182
+ config = { database: { primary: { host: 'localhost' } } }
183
+
184
+ config.chain_fetch(:database, :primary, :host) # => 'localhost'
185
+ config.chain_fetch(:database, :replica, :host) { |key, _rest| "default-#{key}" }
186
+ # => 'default-replica'
187
+ ```
188
+
189
+ ### Flattening and restoring deep hashes
190
+
191
+ ```ruby
192
+ deep = { a: { b: [1, 2] } }
193
+ flat = deep.squash # => { 'a.b[0]' => 1, 'a.b[1]' => 2 }
194
+ flat.to_deep_hash # => { 'a' => { 'b' => [1, 2] } }
195
+ ```
196
+
197
+ ### Exclusive merge (update only existing keys)
198
+
199
+ ```ruby
200
+ defaults = { timeout: 30, retries: 3 }
201
+ overrides = { retries: 5, unknown_key: 'ignored' }
202
+
203
+ defaults.exclusive_merge(overrides) # => { timeout: 30, retries: 5 }
204
+ ```
205
+
206
+ ### Array utilities
207
+
208
+ ```ruby
209
+ # Zip an array with keys into a Hash
210
+ values = [10, 20, 30]
211
+ values.as_hash(%i[x y z]) # => { x: 10, y: 20, z: 30 }
212
+
213
+ # Chain map calls
214
+ [:hello, :world].chain_map(:to_s, :upcase) # => ['HELLO', 'WORLD']
215
+
216
+ # Fetch nested keys from an array of hashes
217
+ records = [{ user: { id: 1 } }, { user: { id: 2 } }]
218
+ records.mapk(:user, :id) # => [1, 2]
219
+ ```
220
+
221
+ ### Symbol utilities
222
+
223
+ ```ruby
224
+ :my_method_name.camelize # => :MyMethodName
225
+ :my_method_name.camelize(:lower) # => :myMethodName
226
+ :MyMethodName.underscore # => :my_method_name
227
+ ```
228
+
229
+ ### Enumerable cleaning
230
+
231
+ ```ruby
232
+ data = { name: 'Alice', nickname: nil, tags: [], meta: {} }
233
+ data.clean # => { name: 'Alice' }
234
+
235
+ mixed = [1, nil, '', [], {}, 'hello']
236
+ mixed.clean # => [1, 'hello']
237
+ ```
238
+
239
+ ### Object helpers
240
+
241
+ ```ruby
242
+ value = 42
243
+ value.is_any?(String, Symbol) # => false
244
+ value.is_any?(String, Symbol, Integer) # => true
245
+
246
+ nil.trueful? # => false
247
+ [].trueful? # => true (unlike blank?/present?)
248
+ ''.trueful? # => true
249
+ ```
250
+
251
+ ### Class default readers
252
+
253
+ ```ruby
254
+ class Report
255
+ attr_writer :title
256
+ default_reader :title, 'Untitled Report'
257
+ end
258
+
259
+ r = Report.new
260
+ r.title # => 'Untitled Report'
261
+ r.title = 'Q1'
262
+ r.title # => 'Q1'
263
+ r.title = nil
264
+ r.title # => nil (nil is respected; differs from default_value)
265
+ ```
266
+
267
+ ### Math weighted average
268
+
269
+ ```ruby
270
+ # Simple average
271
+ Math.average([10, 20, 30]) # => 20.0
272
+
273
+ # Weighted average (value => weight)
274
+ Math.average(10 => 1, 20 => 2, 30 => 1) # => 20.0
275
+ ```
276
+
277
+ ---
278
+
279
+ ## Best Practices and Caveats
280
+
281
+ ### Monkey-patching awareness
282
+
283
+ `core_ext` extends Ruby's built-in classes globally. This means:
284
+
285
+ - All objects of the extended classes gain the new methods **throughout your entire application**, including third-party gems.
286
+ - **Name collision risk**: if another gem or your application already defines a method with the same name on the same class, the last `require`/`load` wins. Review the method list above before adoption and check for conflicts.
287
+ - In libraries (gems) intended for wide reuse, prefer not requiring `core_ext` at the top level unless you own all consumers.
288
+
289
+ ### Recursive options
290
+
291
+ Several `Hash` methods (`change_keys`, `camelize_keys`, `underscore_keys`, `change_values`, `clean!`) operate recursively by default, descending into nested arrays and hashes. Pass `recursive: false` when you only need shallow transformation to avoid unintended side-effects on nested structures.
292
+
293
+ ### Bang (`!`) methods vs. non-bang methods
294
+
295
+ Methods ending with `!` mutate the receiver **in place**. Use them when you are sure you do not need the original structure. Use the non-bang variants to return a transformed copy.
296
+
297
+ ### Versioning
298
+
299
+ Pin to a minor version to avoid unexpected breaking changes:
300
+
301
+ ```ruby
302
+ gem 'darthjee-core_ext', '~> 3.0'
303
+ ```
304
+
305
+ Check the [CHANGELOG / releases page](https://github.com/darthjee/core_ext/releases) before upgrading.
306
+
307
+ ---
308
+
309
+ ## Running the Test Suite / Contributing
310
+
311
+ If you are contributing to `core_ext` itself or want to verify its behavior locally:
312
+
313
+ ```shell
314
+ # Install dependencies
315
+ bundle install
316
+
317
+ # Run the full test suite
318
+ bundle exec rspec
319
+
320
+ # Run a single spec file
321
+ bundle exec rspec spec/lib/darthjee/core_ext/hash_spec.rb
322
+ ```
323
+
324
+ Tests live under `spec/` and mirror the structure of `lib/`. Every public method must have a corresponding spec. See the [README](https://github.com/darthjee/core_ext/blob/main/README.md) and the repository's Copilot instructions (`.github/copilot-instructions.md`) for more details.
data/.rubocop.yml CHANGED
@@ -1,12 +1,15 @@
1
- require: rubocop-rspec
1
+ plugins:
2
+ - rubocop-rspec
3
+ - rubocop-rake
2
4
  inherit_from: .rubocop_todo.yml
3
5
 
4
6
  AllCops:
5
- TargetRubyVersion: 2.4
7
+ TargetRubyVersion: 3.3
8
+ NewCops: enable
6
9
 
7
- Naming/PredicateName:
10
+ RSpec/MultipleExpectations:
8
11
  Exclude:
9
- - 'lib/darthjee/core_ext/object.rb'
12
+ - 'spec/integration/**/*_spec.rb'
10
13
 
11
14
  Metrics/BlockLength:
12
15
  Exclude:
@@ -18,3 +21,18 @@ RSpec/AlignLeftLetBrace:
18
21
 
19
22
  RSpec/NestedGroups:
20
23
  Max: 5
24
+
25
+ RSpec/IndexedLet:
26
+ Enabled: false
27
+
28
+ RSpec/SpecFilePathFormat:
29
+ Exclude:
30
+ - 'spec/integration/**/*_spec.rb'
31
+
32
+ Style/HashEachMethods:
33
+ Exclude:
34
+ - 'lib/darthjee/core_ext/hash/deep_hash_constructor.rb'
35
+ - 'lib/darthjee/core_ext/hash/squasher.rb'
36
+
37
+ Gemspec/RequireMFA:
38
+ Enabled: false
data/.rubocop_todo.yml CHANGED
@@ -1,16 +1,36 @@
1
1
  # This configuration was generated by
2
2
  # `rubocop --auto-gen-config`
3
- # on 2019-02-18 18:10:37 +0000 using RuboCop version 0.58.1.
3
+ # on 2026-03-11 13:23:23 UTC using RuboCop version 1.85.1.
4
4
  # The point is for the user to remove these configuration records
5
5
  # one by one as the offenses are removed from the code base.
6
6
  # Note that changes in the inspected code, or installation of new
7
7
  # versions of RuboCop, may require this file to be generated again.
8
8
 
9
- # Offense count: 3
10
- # Configuration parameters: AggregateFailuresByDefault.
11
- RSpec/MultipleExpectations:
12
- Max: 2
9
+ # Offense count: 1
10
+ # Configuration parameters: NamePrefix, ForbiddenPrefixes, AllowedMethods, MethodDefinitionMacros, UseSorbetSigs.
11
+ # NamePrefix: is_, has_, have_, does_
12
+ # ForbiddenPrefixes: is_, has_, have_, does_
13
+ # AllowedMethods: is_a?
14
+ # MethodDefinitionMacros: define_method, define_singleton_method
15
+ Naming/PredicatePrefix:
16
+ Exclude:
17
+ - 'spec/**/*'
18
+ - 'lib/darthjee/core_ext/object.rb'
13
19
 
14
- # Offense count: 24
15
- Style/Documentation:
16
- Enabled: false
20
+ # Offense count: 4
21
+ # Configuration parameters: AllowSubject.
22
+ RSpec/MultipleMemoizedHelpers:
23
+ Max: 6
24
+
25
+ # Offense count: 8
26
+ # Configuration parameters: AllowedClasses.
27
+ Style/OneClassPerFile:
28
+ Exclude:
29
+ - 'lib/darthjee/core_ext/array.rb'
30
+ - 'lib/darthjee/core_ext/class.rb'
31
+ - 'lib/darthjee/core_ext/date.rb'
32
+ - 'lib/darthjee/core_ext/hash.rb'
33
+ - 'lib/darthjee/core_ext/math.rb'
34
+ - 'lib/darthjee/core_ext/numeric.rb'
35
+ - 'lib/darthjee/core_ext/object.rb'
36
+ - 'lib/darthjee/core_ext/symbol.rb'
data/Dockerfile CHANGED
@@ -1,6 +1,22 @@
1
- FROM darthjee/ruby_gems_240:0.0.2
1
+ FROM darthjee/scripts:0.7.0 as scripts
2
2
 
3
- USER app
4
- COPY --chown=app ./ /home/app/app/
3
+ FROM darthjee/ruby_331:1.1.1 as base
5
4
 
5
+ COPY --chown=app:app ./ /home/app/app/
6
+
7
+ ######################################
8
+
9
+ FROM base as builder
10
+
11
+ COPY --chown=app:app --from=scripts /home/scripts/builder/bundle_builder.sh /usr/local/sbin/bundle_builder.sh
12
+
13
+ ENV HOME_DIR /home/app
14
+ RUN bundle_builder.sh
15
+
16
+ #######################
17
+ #FINAL IMAGE
18
+ FROM base
19
+
20
+ COPY --chown=app:app --from=builder /home/app/bundle/ /usr/local/bundle/
6
21
  RUN bundle install
22
+
data/Gemfile CHANGED
@@ -4,3 +4,23 @@ source 'https://rubygems.org'
4
4
 
5
5
  # Specify your gem's dependencies in credential_builder.gemspec
6
6
  gemspec
7
+
8
+ gem 'bundler', '>= 2.5.13'
9
+ gem 'pry', '0.14.2'
10
+ gem 'pry-nav', '1.0.0'
11
+ gem 'rake', '13.2.1'
12
+ gem 'reek', '6.5.0'
13
+ gem 'rspec', '3.13.2'
14
+ gem 'rspec-core', '3.13.6'
15
+ gem 'rspec-expectations', '3.13.5'
16
+ gem 'rspec-mocks', '3.13.8'
17
+ gem 'rspec-support', '3.13.7'
18
+ gem 'rubocop', '1.85.1'
19
+ gem 'rubocop-rake', '0.7.1'
20
+ gem 'rubocop-rspec', '3.9.0'
21
+ gem 'rubycritic', '5.0.0'
22
+ gem 'simplecov', '0.22.0'
23
+ gem 'simplecov-html', '0.13.2'
24
+ gem 'simplecov-lcov', '0.9.0'
25
+ gem 'yard', '0.9.38'
26
+ gem 'yardstick', '0.9.9'
data/Makefile ADDED
@@ -0,0 +1,7 @@
1
+ PROJECT?=core_ext
2
+
3
+ dev:
4
+ docker-compose run $(PROJECT) /bin/bash
5
+
6
+ ci:
7
+ docker-compose run $(PROJECT)_circleci /bin/bash