nested_hash_builder 0.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 +7 -0
- data/.github/workflows/ci.yml +43 -0
- data/CHANGELOG.md +19 -0
- data/README.md +83 -0
- data/Rakefile +16 -0
- data/lib/nested_hash_builder/version.rb +6 -0
- data/lib/nested_hash_builder.rb +177 -0
- data/sig/nested_hash_builder.rbs +4 -0
- metadata +64 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: '03238f7b928506bf5341118b4436d3aad87ada5ba3c686b4779425ba8d0c46db'
|
|
4
|
+
data.tar.gz: 9dc3320dca3c8f6e2b1fe8c2f9df430ae7636110f92250db8e8e9aefdae32e48
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: a92b4f9fdad9763c7304caaa141e7893f984bdc6fbc754a941891e4e0c4dc1b18387c97204cca7056d492d23afabf9b2d1c6037b35aa562480dfc44e2f047c1c
|
|
7
|
+
data.tar.gz: '068689310d882ca23447531ea9a2cc919ec3de80db0546e63609a3673e0be7a23a018901d41b83d10d744b4a738e10e583ad1669a6ff97f7769359dec09eedc8'
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
name: CI Ruby
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [ 'main', 'master', 'develop' ]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [ 'main', 'master', 'develop' ]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
env:
|
|
13
|
+
CI: true
|
|
14
|
+
strategy:
|
|
15
|
+
matrix:
|
|
16
|
+
ruby-version: ['3.2', '3.3', '3.4']
|
|
17
|
+
steps:
|
|
18
|
+
- name: Checkout code
|
|
19
|
+
uses: actions/checkout@v3
|
|
20
|
+
- name: Install Ruby and gems
|
|
21
|
+
uses: ruby/setup-ruby@v1
|
|
22
|
+
with:
|
|
23
|
+
ruby-version: ${{ matrix.ruby-version }}
|
|
24
|
+
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
|
|
25
|
+
- name: Run unit tests
|
|
26
|
+
run: bundle exec rake test
|
|
27
|
+
|
|
28
|
+
typecheck_and_lint:
|
|
29
|
+
runs-on: ubuntu-latest
|
|
30
|
+
env:
|
|
31
|
+
CI: true
|
|
32
|
+
steps:
|
|
33
|
+
- name: Checkout code
|
|
34
|
+
uses: actions/checkout@v3
|
|
35
|
+
- name: Install Ruby and gems
|
|
36
|
+
uses: ruby/setup-ruby@v1
|
|
37
|
+
with:
|
|
38
|
+
ruby-version: '3.4'
|
|
39
|
+
bundler-cache: true
|
|
40
|
+
- name: Lint Ruby files
|
|
41
|
+
run: bundle exec rubocop --parallel
|
|
42
|
+
- name: Typecheck
|
|
43
|
+
run: bundle exec srb tc
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
Add changes in new features here. Do not change the gem's version in pull/merge requests.
|
|
11
|
+
|
|
12
|
+
### Changes
|
|
13
|
+
-
|
|
14
|
+
|
|
15
|
+
## [0.1.0] - 2025-08-13
|
|
16
|
+
|
|
17
|
+
[Diff](https://github.com/Verseth/ruby-nested_hash_builder/compare/v0.0.0...v0.1.0)
|
|
18
|
+
|
|
19
|
+
- Initial release
|
data/README.md
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# NestedHashBuilder
|
|
2
|
+
|
|
3
|
+
This Ruby gem allows you to easily build nested Ruby Hashes.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
Install the gem and add to the application's Gemfile by executing:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
bundle add nested_hash_builder
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
If bundler is not being used to manage dependencies, install the gem by executing:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
gem install nested_hash_builder
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
You can use the `NestedHashBuilder::build` method to easily
|
|
22
|
+
create nested hashed with arrays etc.
|
|
23
|
+
|
|
24
|
+
```ruby
|
|
25
|
+
require 'nested_hash_builder'
|
|
26
|
+
|
|
27
|
+
hash = NestedHashBuilder.build do |h|
|
|
28
|
+
h.user do
|
|
29
|
+
h.name "John"
|
|
30
|
+
h.address do
|
|
31
|
+
h.street "123 Main St"
|
|
32
|
+
h.city "Anytown"
|
|
33
|
+
h.zip "12345"
|
|
34
|
+
end
|
|
35
|
+
h.key!("SOME:STRANGE:KEY", 2)
|
|
36
|
+
h.array!(:contacts) do |c|
|
|
37
|
+
c << h.entry! do |e|
|
|
38
|
+
e.email "john@example.com"
|
|
39
|
+
e.phone "555-1234"
|
|
40
|
+
end
|
|
41
|
+
c << h.entry! do |e|
|
|
42
|
+
e.email "foo@example.com"
|
|
43
|
+
e.phone "222-1234"
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# The resulting hash looks like:
|
|
50
|
+
# {
|
|
51
|
+
# user: {
|
|
52
|
+
# name: "John",
|
|
53
|
+
# address: {
|
|
54
|
+
# street: "123 Main St",
|
|
55
|
+
# city: "Anytown",
|
|
56
|
+
# zip: "12345"
|
|
57
|
+
# },
|
|
58
|
+
# "SOME:STRANGE:KEY": 2,
|
|
59
|
+
# contacts: [
|
|
60
|
+
# {
|
|
61
|
+
# email: "john@example.com",
|
|
62
|
+
# phone: "555-1234"
|
|
63
|
+
# },
|
|
64
|
+
# {
|
|
65
|
+
# email: "foo@example.com",
|
|
66
|
+
# phone: "222-1234"
|
|
67
|
+
# },
|
|
68
|
+
# ],
|
|
69
|
+
# }
|
|
70
|
+
# }
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
## Development
|
|
76
|
+
|
|
77
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
78
|
+
|
|
79
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
|
80
|
+
|
|
81
|
+
## Contributing
|
|
82
|
+
|
|
83
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/Verseth/nested_hash_builder.
|
data/Rakefile
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'bundler/gem_tasks'
|
|
4
|
+
require 'rake/testtask'
|
|
5
|
+
|
|
6
|
+
::Rake::TestTask.new(:test) do |t|
|
|
7
|
+
t.libs << 'test'
|
|
8
|
+
t.libs << 'lib'
|
|
9
|
+
t.test_files = ::FileList['test/**/*_test.rb']
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
require 'rubocop/rake_task'
|
|
13
|
+
|
|
14
|
+
RuboCop::RakeTask.new
|
|
15
|
+
|
|
16
|
+
task default: %i[test]
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# typed: true
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require 'sorbet-runtime'
|
|
5
|
+
require_relative 'nested_hash_builder/version'
|
|
6
|
+
|
|
7
|
+
# A module that provides a simple DSL
|
|
8
|
+
# for defining complex, nested Hashes.
|
|
9
|
+
#
|
|
10
|
+
# hash = NestedHashBuilder.call do |h|
|
|
11
|
+
# h.price 10
|
|
12
|
+
# h.client do
|
|
13
|
+
# h.first_name 'Patrick'
|
|
14
|
+
# h.last_name = 'Stewart' # setter syntax is optional
|
|
15
|
+
# h.full_name = "#{h.local_dig!(:first_name)} #{h.local_dig!(:last_name)}"
|
|
16
|
+
# end
|
|
17
|
+
# h.inexistent = :i_wont_be_there if h.dig!(:price) == 15
|
|
18
|
+
# end
|
|
19
|
+
#
|
|
20
|
+
# p hash
|
|
21
|
+
# #=> {:price=>10, :client=>{:first_name=>"Patrick", :last_name=>"Stewart", :full_name=>"Patrick Stewart"}}
|
|
22
|
+
#
|
|
23
|
+
module NestedHashBuilder
|
|
24
|
+
class << self
|
|
25
|
+
#: (?base: Hash[untyped, untyped], ?symbolize: bool) { (Proxy) -> void } -> Hash[untyped, untyped]
|
|
26
|
+
def call(base: {}, symbolize: true, &block)
|
|
27
|
+
block.call(proxy = Proxy.new(base: base, symbolize: symbolize))
|
|
28
|
+
proxy.to_h
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
alias build call
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Object that is the receiver of all
|
|
35
|
+
# the building methods.
|
|
36
|
+
class Proxy < ::BasicObject
|
|
37
|
+
#: (?base: Hash[untyped, untyped], ?symbolize: bool) -> void
|
|
38
|
+
def initialize(base: {}, symbolize: true)
|
|
39
|
+
@hash = base.dup
|
|
40
|
+
@symbolize = symbolize
|
|
41
|
+
@current_parent = @hash
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Creates an array.
|
|
45
|
+
#
|
|
46
|
+
#: (Symbol | String) { (Array[untyped]) -> void } -> void
|
|
47
|
+
def ary!(name, &block)
|
|
48
|
+
array = []
|
|
49
|
+
block.call(array)
|
|
50
|
+
key! name, array
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
alias array! ary!
|
|
54
|
+
|
|
55
|
+
# Add a key with the specified value to the hash.
|
|
56
|
+
# Creates a nested hash if a block is passed.
|
|
57
|
+
#
|
|
58
|
+
#: (String | Symbol, ?top) ?{ -> void } -> void
|
|
59
|
+
def key!(name, value = nil, &block)
|
|
60
|
+
name = name.to_s.delete_suffix('=')
|
|
61
|
+
name = name.to_sym if @symbolize
|
|
62
|
+
|
|
63
|
+
return hash!(name, &block) if block
|
|
64
|
+
|
|
65
|
+
@current_parent[name] = value
|
|
66
|
+
@hash
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
#: { (Proxy) -> void } -> Hash[untyped, untyped]
|
|
70
|
+
def entry!(&block)
|
|
71
|
+
p = Proxy.new(symbolize: @symbolize)
|
|
72
|
+
block.call(p)
|
|
73
|
+
p.to_h
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Creates a nested hash.
|
|
77
|
+
#
|
|
78
|
+
#: (String | Symbol) -> void
|
|
79
|
+
def hash!(name)
|
|
80
|
+
previous_parent = @current_parent
|
|
81
|
+
@current_parent = (@current_parent[name] ||= {})
|
|
82
|
+
yield
|
|
83
|
+
@current_parent = previous_parent
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Gets a value of a particular key.
|
|
87
|
+
#
|
|
88
|
+
# HashBuilder.call do |h|
|
|
89
|
+
# h.price 10
|
|
90
|
+
# h.client do
|
|
91
|
+
# h.first_name 'Patrick'
|
|
92
|
+
# h.last_name 'Stewart'
|
|
93
|
+
# h.full_name "#{h.dig!(:client, :first_name)} #{h.dig!(:client, :last_name)}"
|
|
94
|
+
# h.card do
|
|
95
|
+
# h.number '4242424242424242'
|
|
96
|
+
# h.expiry_date = Time.now
|
|
97
|
+
# end
|
|
98
|
+
# end
|
|
99
|
+
# h.price_copy = h.dig!(:price) #=> 10
|
|
100
|
+
# h.card_copy = h.dig!(:client, :card, :number) #=> '4242424242424242'
|
|
101
|
+
# end
|
|
102
|
+
#
|
|
103
|
+
#: (*Symbol | String) -> untyped
|
|
104
|
+
def dig!(*names)
|
|
105
|
+
@hash.dig(*names)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Checks if a given key is defined.
|
|
109
|
+
#
|
|
110
|
+
# HashBuilder.call do |h|
|
|
111
|
+
# h.price 10
|
|
112
|
+
# h.client do
|
|
113
|
+
# h.name 'Patrick Stewart'
|
|
114
|
+
# h.card do
|
|
115
|
+
# h.number '4242424242424242'
|
|
116
|
+
# h.expiry_date = Time.now
|
|
117
|
+
# end
|
|
118
|
+
# end
|
|
119
|
+
# h.price_present = h.key?(:price) #=> true
|
|
120
|
+
# h.card_present = h.key?(:client, :card) #=> true
|
|
121
|
+
# end
|
|
122
|
+
#
|
|
123
|
+
# @param names [Array<Symbol>]
|
|
124
|
+
#: (*Symbol | String) -> bool
|
|
125
|
+
def key?(*names)
|
|
126
|
+
!::T.unsafe(self).dig!(*names).nil?
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Gets a value of a particular key in the current context.
|
|
130
|
+
#
|
|
131
|
+
# HashBuilder.call do |h|
|
|
132
|
+
# h.price 10
|
|
133
|
+
# h.client do
|
|
134
|
+
# h.first_name 'Patrick'
|
|
135
|
+
# h.last_name 'Stewart'
|
|
136
|
+
# h.full_name "#{h.local_dig!(:first_name)} #{h.local_dig!(:last_name)}"
|
|
137
|
+
# end
|
|
138
|
+
# end
|
|
139
|
+
#
|
|
140
|
+
#: (*Symbol | String) -> untyped
|
|
141
|
+
def local_dig!(*names)
|
|
142
|
+
@current_parent.dig(*names)
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# Checks if a given key is defined in the local context.
|
|
146
|
+
#
|
|
147
|
+
# HashBuilder.call do |h|
|
|
148
|
+
# h.price 10
|
|
149
|
+
# h.client do
|
|
150
|
+
# h.name 'Patrick Stewart'
|
|
151
|
+
# h.surname 'Stewart' if h.local_key?(:name) # true
|
|
152
|
+
# end
|
|
153
|
+
# end
|
|
154
|
+
#
|
|
155
|
+
#: (*Symbol | String) -> bool
|
|
156
|
+
def local_key?(*names)
|
|
157
|
+
!::T.unsafe(self).local_dig!(*names).nil?
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
#: -> Hash[Symbol | String, untyped]
|
|
161
|
+
def to_h
|
|
162
|
+
@hash
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def method_missing(method_name, *args, &block)
|
|
166
|
+
super if method_name.end_with?('?') || method_name.end_with?('!')
|
|
167
|
+
|
|
168
|
+
key!(method_name, args.first, &block)
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def respond_to_missing?(method_name, *_args)
|
|
172
|
+
return false if method_name.end_with?('?') || method_name.end_with?('!')
|
|
173
|
+
|
|
174
|
+
true
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: nested_hash_builder
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Mateusz Drewniak
|
|
8
|
+
bindir: exe
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: sorbet-runtime
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '0.5'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '0.5'
|
|
26
|
+
description: A Ruby library for building nested Hashes
|
|
27
|
+
email:
|
|
28
|
+
- m.drewniak@espago.com
|
|
29
|
+
executables: []
|
|
30
|
+
extensions: []
|
|
31
|
+
extra_rdoc_files: []
|
|
32
|
+
files:
|
|
33
|
+
- ".github/workflows/ci.yml"
|
|
34
|
+
- CHANGELOG.md
|
|
35
|
+
- README.md
|
|
36
|
+
- Rakefile
|
|
37
|
+
- lib/nested_hash_builder.rb
|
|
38
|
+
- lib/nested_hash_builder/version.rb
|
|
39
|
+
- sig/nested_hash_builder.rbs
|
|
40
|
+
homepage: https://github.com/Verseth/ruby-nested_hash_builder
|
|
41
|
+
licenses: []
|
|
42
|
+
metadata:
|
|
43
|
+
homepage_uri: https://github.com/Verseth/ruby-nested_hash_builder
|
|
44
|
+
source_code_uri: https://github.com/Verseth/ruby-nested_hash_builder
|
|
45
|
+
changelog_uri: https://raw.githubusercontent.com/Verseth/ruby-nested_hash_builder/refs/heads/main/CHANGELOG.md
|
|
46
|
+
rubygems_mfa_required: 'true'
|
|
47
|
+
rdoc_options: []
|
|
48
|
+
require_paths:
|
|
49
|
+
- lib
|
|
50
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - ">="
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: 3.2.0
|
|
55
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
56
|
+
requirements:
|
|
57
|
+
- - ">="
|
|
58
|
+
- !ruby/object:Gem::Version
|
|
59
|
+
version: '0'
|
|
60
|
+
requirements: []
|
|
61
|
+
rubygems_version: 3.7.1
|
|
62
|
+
specification_version: 4
|
|
63
|
+
summary: A Ruby library for building nested Hashes
|
|
64
|
+
test_files: []
|