hash_of 1.0.1 → 1.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.
- checksums.yaml +4 -4
- data/.rubocop.yml +2 -2
- data/CHANGELOG.md +7 -0
- data/CLAUDE.md +28 -0
- data/README.md +61 -17
- data/Rakefile +6 -0
- data/hash_of.gemspec +31 -0
- data/lib/hash_of/version.rb +1 -1
- data/lib/hash_of.rb +8 -1
- data/sig/hash_of.rbs +6 -9
- metadata +8 -9
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1031246240479bb3f4c2256f542bb1530e34878371458c255615133eb14c3d25
|
|
4
|
+
data.tar.gz: 9589f482f2dc1d8e29c7b80b264ddac410b965654d55fbcffcd1e08c0d3961ea
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0053fc60c174a1fef807f5c8d6ef6976842961419a39fa4059d2aaaed23e45da5831bd1a95687c3d798e852aadb0ba54e4bfbe47898d9dd2cdaf5a28af64853b
|
|
7
|
+
data.tar.gz: d9301de5ec5afd829a3020922bce0a01d7544ea4b1775fbddc1d41f3235db65ef76275130a2839d15875f2a690b3443374ba9889ceadaa50924443d9daf95152
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
# 1.1.0 (February 27, 2026)
|
|
2
|
+
|
|
3
|
+
- Raise `ArgumentError` for invalid type arguments (e.g., `Hash.of(:string)`)
|
|
4
|
+
- Raise `ArgumentError` when passing `recursive: true` with `:array`
|
|
5
|
+
- Drop Ruby < 3.3 support; now tested on Ruby 3.3, 3.4, and 4.0
|
|
6
|
+
- Update all dependencies (rubocop 1.85, rubocop-rspec 3.x, etc.)
|
|
7
|
+
|
|
1
8
|
# 1.0.1 (February 12, 2024)
|
|
2
9
|
|
|
3
10
|
- Simpler internal approach for creating the recursive hash that avoids a new class
|
data/CLAUDE.md
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Project
|
|
6
|
+
|
|
7
|
+
hash_of is a Ruby gem that extends `Hash` with a `.of()` class method for creating auto-populating hashes (arrays, hashes, or recursive hashes). It targets Ruby 3.0+.
|
|
8
|
+
|
|
9
|
+
## Commands
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
bundle exec rake # Default: runs tests + rubocop
|
|
13
|
+
bundle exec rake spec # Run RSpec tests only
|
|
14
|
+
bundle exec rubocop # Run linting only
|
|
15
|
+
bundle exec rspec spec/hash_of_spec.rb # Run a single test file
|
|
16
|
+
bundle exec rake coverage # Generate coverage report and open in browser
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Architecture
|
|
20
|
+
|
|
21
|
+
The entire gem is a single module in `lib/hash_of.rb`. It defines three lambdas (`OF_ARRAY_LAMBDA`, `OF_HASH_LAMBDA`, `OF_HASH_RECURSIVE_LAMBDA`) used as `default_proc` values and a single `of(type, recursive: false)` method. The module is extended onto `Hash` directly via `Hash.extend(HashOf)`.
|
|
22
|
+
|
|
23
|
+
All tests live in `spec/hash_of_spec.rb`. Coverage is tracked with SimpleCov.
|
|
24
|
+
|
|
25
|
+
## Style
|
|
26
|
+
|
|
27
|
+
- RuboCop enforced: double-quoted strings, 120 char max line length
|
|
28
|
+
- CI tests against Ruby 3.0, 3.1, 3.2, 3.3
|
data/README.md
CHANGED
|
@@ -1,24 +1,69 @@
|
|
|
1
1
|
# Hash.of
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Syntactic sugar to tersely create a hash of arrays or hashes, and optionally, recursive hashes.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
While clear, creating a hash of hashes via `Hash.new { |hash, key| hash[key] = {} }` is verbose. If this pattern is common in your codebase you can express it more concisely as `Hash.of(:hash)`. This can help readability when iterating with `#reduce` or `#each_with_object` to create efficient lookup tables.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
## Example
|
|
8
|
+
|
|
9
|
+
We have a bunch of records containing the winners of the Academy Awards. We'd like to provide a simple interface for people to look up winners by year and category like the following:
|
|
10
|
+
|
|
11
|
+
```ruby
|
|
12
|
+
winners_by_year_by_category[2024][:best_picture]
|
|
13
|
+
# => "Oppenheimer"
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Let's say we have `AcademyAwardResult` objects which have the properties `year`, `category`, and `winner`. They may look something like:
|
|
17
|
+
|
|
18
|
+
```ruby
|
|
19
|
+
[
|
|
20
|
+
#<AcademyAwardResult year=2024, category=:best_picture, winner="Oppenheimer">,
|
|
21
|
+
#<AcademyAwardResult year=2024, category=:best_actor, winner="Cillian Murphy">,
|
|
22
|
+
…
|
|
23
|
+
#<AcademyAwardResult year=2023, category=:best_picture, winner="Everything Everywhere All at Once">,
|
|
24
|
+
…
|
|
25
|
+
#<AcademyAwardResult year=2022, category=:best_picture, winner="CODA">
|
|
26
|
+
]
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
The following turns this array of records into a lookup table:
|
|
8
30
|
|
|
9
31
|
```ruby
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
cache[record.id] = record
|
|
32
|
+
award_results.each_with_object(Hash.new { |hash, key| hash[key] = {} }) do |result, lookup|
|
|
33
|
+
lookup[result.year][result.category] = result.winner
|
|
13
34
|
end
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
`Hash.of` enables the terser:
|
|
14
38
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
39
|
+
```ruby
|
|
40
|
+
award_results.each_with_object(Hash.of(:hash)) do |result, lookup|
|
|
41
|
+
lookup[result.year][result.category] = result.winner
|
|
18
42
|
end
|
|
19
43
|
|
|
20
|
-
# or
|
|
21
|
-
|
|
44
|
+
# or the incredibly terse inline
|
|
45
|
+
|
|
46
|
+
award_results.each_with_object(Hash.of(:hash)) { _2[_1.year][_1.category] = _1.winner }
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Both result in the following object that powers the specified interface:
|
|
50
|
+
|
|
51
|
+
```ruby
|
|
52
|
+
{
|
|
53
|
+
2024 => {
|
|
54
|
+
:best_picture => "Oppenheimer",
|
|
55
|
+
:best_actor => "Cillian Murphy",
|
|
56
|
+
…
|
|
57
|
+
},
|
|
58
|
+
2023 => {
|
|
59
|
+
:best_picture => "Everything Everywhere All at Once",
|
|
60
|
+
…
|
|
61
|
+
},
|
|
62
|
+
2022 => {
|
|
63
|
+
:best_picture => "CODA",
|
|
64
|
+
…
|
|
65
|
+
}
|
|
66
|
+
}
|
|
22
67
|
```
|
|
23
68
|
|
|
24
69
|
## Installation
|
|
@@ -27,7 +72,7 @@ Install the gem and add to the application's Gemfile by executing:
|
|
|
27
72
|
|
|
28
73
|
$ bundle add hash_of
|
|
29
74
|
|
|
30
|
-
Alternatively add to your Gemfile via
|
|
75
|
+
Alternatively add to your `Gemfile` via
|
|
31
76
|
|
|
32
77
|
gem "hash_of", "~> 1.0"
|
|
33
78
|
|
|
@@ -35,10 +80,9 @@ If bundler is not being used to manage dependencies, install the gem by executin
|
|
|
35
80
|
|
|
36
81
|
$ gem install hash_of
|
|
37
82
|
|
|
38
|
-
|
|
39
83
|
## Usage
|
|
40
84
|
|
|
41
|
-
|
|
85
|
+
`Hash.of` provides the following uses
|
|
42
86
|
|
|
43
87
|
### 1. `Hash.of(:array)`
|
|
44
88
|
|
|
@@ -90,12 +134,12 @@ recursive_hash_of_hashes
|
|
|
90
134
|
|
|
91
135
|
## Development
|
|
92
136
|
|
|
93
|
-
After checking out the repo, run `
|
|
137
|
+
After checking out the repo, run `bundle install` to install dependencies. Then, run `bundle exec rake` to run the tests and RuboCop. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
94
138
|
|
|
95
139
|
## Contributing
|
|
96
140
|
|
|
97
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
|
141
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/agrberg/hash_of.
|
|
98
142
|
|
|
99
143
|
## License
|
|
100
144
|
|
|
101
|
-
The gem is available as open source under the terms of the [MIT License](
|
|
145
|
+
The gem is available as open source under the terms of the [MIT License](LICENSE.txt).
|
data/Rakefile
CHANGED
data/hash_of.gemspec
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "lib/hash_of/version"
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = "hash_of"
|
|
7
|
+
spec.version = HashOf::VERSION
|
|
8
|
+
spec.authors = ["Aaron Rosenberg"]
|
|
9
|
+
spec.email = ["aarongrosenberg@gmail.com"]
|
|
10
|
+
|
|
11
|
+
spec.summary = "Syntactic sugar to create hashes of hashes or arrays and ability to make them recursive."
|
|
12
|
+
spec.homepage = "https://github.com/agrberg/hash_of"
|
|
13
|
+
spec.license = "MIT"
|
|
14
|
+
spec.required_ruby_version = ">= 3.3.0"
|
|
15
|
+
|
|
16
|
+
spec.metadata["bug_tracker_uri"] = "https://github.com/agrberg/hash_of/issues"
|
|
17
|
+
spec.metadata["changelog_uri"] = "https://github.com/agrberg/hash_of/blob/main/CHANGELOG.md"
|
|
18
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
|
19
|
+
spec.metadata["rubygems_mfa_required"] = "true"
|
|
20
|
+
spec.metadata["source_code_uri"] = spec.homepage
|
|
21
|
+
|
|
22
|
+
# Specify which files should be added to the gem when it is released.
|
|
23
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
|
24
|
+
spec.files = Dir.chdir(__dir__) do
|
|
25
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
|
26
|
+
(File.expand_path(f) == __FILE__) ||
|
|
27
|
+
f.start_with?(*%w[bin/ test/ spec/ features/ .git .github appveyor Gemfile])
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
spec.require_paths = ["lib"]
|
|
31
|
+
end
|
data/lib/hash_of/version.rb
CHANGED
data/lib/hash_of.rb
CHANGED
|
@@ -2,13 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
require_relative "hash_of/version"
|
|
4
4
|
|
|
5
|
-
# Module to contain the constants
|
|
5
|
+
# Module to contain the constants and class method Hash will extend
|
|
6
6
|
module HashOf
|
|
7
7
|
OF_ARRAY_LAMBDA = ->(hash, key) { hash[key] = [] }
|
|
8
8
|
OF_HASH_LAMBDA = ->(hash, key) { hash[key] = {} }
|
|
9
9
|
OF_HASH_RECURSIVE_LAMBDA = ->(hash, key) { hash[key] = Hash.new(&hash.default_proc) }
|
|
10
10
|
|
|
11
|
+
VALID_TYPES = %i[array hash].freeze
|
|
12
|
+
|
|
11
13
|
def of(type, recursive: false)
|
|
14
|
+
unless VALID_TYPES.include?(type)
|
|
15
|
+
raise ArgumentError, "Invalid type: #{type.inspect}. Valid types are: #{VALID_TYPES.join(", ")}"
|
|
16
|
+
end
|
|
17
|
+
raise ArgumentError, "recursive is only supported with type: :hash" if recursive && type != :hash
|
|
18
|
+
|
|
12
19
|
case type
|
|
13
20
|
when :array
|
|
14
21
|
Hash.new(&OF_ARRAY_LAMBDA)
|
data/sig/hash_of.rbs
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
|
-
# Module to contain the constants
|
|
1
|
+
# Module to contain the constants and class method Hash will extend
|
|
2
2
|
module HashOf
|
|
3
|
-
OF_ARRAY_LAMBDA:
|
|
4
|
-
OF_HASH_LAMBDA:
|
|
3
|
+
OF_ARRAY_LAMBDA: ^(Hash, untyped) -> Array[untyped]
|
|
4
|
+
OF_HASH_LAMBDA: ^(Hash, untyped) -> Hash[untyped, untyped]
|
|
5
|
+
OF_HASH_RECURSIVE_LAMBDA: ^(Hash, untyped) -> Hash[untyped, untyped]
|
|
6
|
+
VALID_TYPES: Array[Symbol]
|
|
5
7
|
VERSION: String
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
class OfHash < Hash
|
|
9
|
-
def initialize: () -> void
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
def of: (Symbol type, ?recursive: bool) -> Hash
|
|
9
|
+
def of: (Symbol type, ?recursive: bool) -> Hash[untyped, untyped]
|
|
13
10
|
end
|
metadata
CHANGED
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: hash_of
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0
|
|
4
|
+
version: 1.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Aaron Rosenberg
|
|
8
|
-
|
|
9
|
-
bindir: exe
|
|
8
|
+
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies: []
|
|
13
|
-
description:
|
|
14
12
|
email:
|
|
15
13
|
- aarongrosenberg@gmail.com
|
|
16
14
|
executables: []
|
|
@@ -20,9 +18,11 @@ files:
|
|
|
20
18
|
- ".rspec"
|
|
21
19
|
- ".rubocop.yml"
|
|
22
20
|
- CHANGELOG.md
|
|
21
|
+
- CLAUDE.md
|
|
23
22
|
- LICENSE.txt
|
|
24
23
|
- README.md
|
|
25
24
|
- Rakefile
|
|
25
|
+
- hash_of.gemspec
|
|
26
26
|
- lib/hash_of.rb
|
|
27
27
|
- lib/hash_of/version.rb
|
|
28
28
|
- sig/hash_of.rbs
|
|
@@ -30,11 +30,11 @@ homepage: https://github.com/agrberg/hash_of
|
|
|
30
30
|
licenses:
|
|
31
31
|
- MIT
|
|
32
32
|
metadata:
|
|
33
|
+
bug_tracker_uri: https://github.com/agrberg/hash_of/issues
|
|
33
34
|
changelog_uri: https://github.com/agrberg/hash_of/blob/main/CHANGELOG.md
|
|
34
35
|
homepage_uri: https://github.com/agrberg/hash_of
|
|
35
36
|
rubygems_mfa_required: 'true'
|
|
36
37
|
source_code_uri: https://github.com/agrberg/hash_of
|
|
37
|
-
post_install_message:
|
|
38
38
|
rdoc_options: []
|
|
39
39
|
require_paths:
|
|
40
40
|
- lib
|
|
@@ -42,15 +42,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
42
42
|
requirements:
|
|
43
43
|
- - ">="
|
|
44
44
|
- !ruby/object:Gem::Version
|
|
45
|
-
version: 3.
|
|
45
|
+
version: 3.3.0
|
|
46
46
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
47
47
|
requirements:
|
|
48
48
|
- - ">="
|
|
49
49
|
- !ruby/object:Gem::Version
|
|
50
50
|
version: '0'
|
|
51
51
|
requirements: []
|
|
52
|
-
rubygems_version:
|
|
53
|
-
signing_key:
|
|
52
|
+
rubygems_version: 4.0.3
|
|
54
53
|
specification_version: 4
|
|
55
54
|
summary: Syntactic sugar to create hashes of hashes or arrays and ability to make
|
|
56
55
|
them recursive.
|