intellihash 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/.rspec +3 -0
- data/.rubocop.yml +17 -0
- data/Gemfile +9 -0
- data/Gemfile.lock +66 -0
- data/LICENSE.txt +21 -0
- data/README.md +274 -0
- data/Rakefile +8 -0
- data/bin/console +14 -0
- data/bin/setup +6 -0
- data/intellihash.gemspec +36 -0
- data/lib/intellihash.rb +12 -0
- data/lib/intellihash/callbacks.rb +33 -0
- data/lib/intellihash/configuration.rb +45 -0
- data/lib/intellihash/mixins.rb +78 -0
- data/lib/intellihash/version.rb +5 -0
- metadata +106 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 17ac847b242baeffdc96ca1bb5b53c352b151834f69982221d30d73048b0ae35
|
4
|
+
data.tar.gz: e1f386d85a69f2af333b1c2c85bba7f66e7d2977bfd0fab69aa19174a8e22cd7
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 2dcc030b8a4013d1ed190eafb7ce8ef0c075db1ab77dc254524f7594addcbfad5c717ff8af198fdaa09a07b90548719ffefc40ae1e206d4601f4995215238e26
|
7
|
+
data.tar.gz: 8145467934645236a1819456705cc45e1d08d2be75518af8db5a0027849699b94965735b5ab9b1158744ff03de99a1ca9374deeb889280dc2bb848ee660d24f2
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
AllCops:
|
2
|
+
NewCops: enable
|
3
|
+
SuggestExtensions: false
|
4
|
+
TargetRubyVersion: 2.6.5
|
5
|
+
|
6
|
+
Metrics/BlockLength:
|
7
|
+
Exclude:
|
8
|
+
- spec/*
|
9
|
+
|
10
|
+
Metrics/MethodLength:
|
11
|
+
Max: 15
|
12
|
+
|
13
|
+
Naming/PredicateName:
|
14
|
+
Enabled: false
|
15
|
+
|
16
|
+
Style/Documentation:
|
17
|
+
Enabled: false
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
intellihash (0.1.0)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
ast (2.4.2)
|
10
|
+
benchmark-malloc (0.1.0)
|
11
|
+
benchmark-perf (0.5.0)
|
12
|
+
benchmark-trend (0.3.0)
|
13
|
+
byebug (11.1.3)
|
14
|
+
diff-lcs (1.4.4)
|
15
|
+
parallel (1.20.1)
|
16
|
+
parser (3.0.2.0)
|
17
|
+
ast (~> 2.4.1)
|
18
|
+
rainbow (3.0.0)
|
19
|
+
rake (12.3.3)
|
20
|
+
regexp_parser (2.1.1)
|
21
|
+
rexml (3.2.5)
|
22
|
+
rspec (3.10.0)
|
23
|
+
rspec-core (~> 3.10.0)
|
24
|
+
rspec-expectations (~> 3.10.0)
|
25
|
+
rspec-mocks (~> 3.10.0)
|
26
|
+
rspec-benchmark (0.5.1)
|
27
|
+
benchmark-malloc (~> 0.1.0)
|
28
|
+
benchmark-perf (~> 0.5.0)
|
29
|
+
benchmark-trend (~> 0.3.0)
|
30
|
+
rspec (>= 3.0.0, < 4.0.0)
|
31
|
+
rspec-core (3.10.1)
|
32
|
+
rspec-support (~> 3.10.0)
|
33
|
+
rspec-expectations (3.10.1)
|
34
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
35
|
+
rspec-support (~> 3.10.0)
|
36
|
+
rspec-mocks (3.10.2)
|
37
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
38
|
+
rspec-support (~> 3.10.0)
|
39
|
+
rspec-support (3.10.2)
|
40
|
+
rubocop (1.18.4)
|
41
|
+
parallel (~> 1.10)
|
42
|
+
parser (>= 3.0.0.0)
|
43
|
+
rainbow (>= 2.2.2, < 4.0)
|
44
|
+
regexp_parser (>= 1.8, < 3.0)
|
45
|
+
rexml
|
46
|
+
rubocop-ast (>= 1.8.0, < 2.0)
|
47
|
+
ruby-progressbar (~> 1.7)
|
48
|
+
unicode-display_width (>= 1.4.0, < 3.0)
|
49
|
+
rubocop-ast (1.8.0)
|
50
|
+
parser (>= 3.0.1.1)
|
51
|
+
ruby-progressbar (1.11.0)
|
52
|
+
unicode-display_width (2.0.0)
|
53
|
+
|
54
|
+
PLATFORMS
|
55
|
+
ruby
|
56
|
+
|
57
|
+
DEPENDENCIES
|
58
|
+
byebug
|
59
|
+
intellihash!
|
60
|
+
rake (~> 12.0)
|
61
|
+
rspec (~> 3.0)
|
62
|
+
rspec-benchmark (~> 0.5.0)
|
63
|
+
rubocop (~> 1.18)
|
64
|
+
|
65
|
+
BUNDLED WITH
|
66
|
+
2.1.4
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2021 Tyler Porter
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,274 @@
|
|
1
|
+
# Intellihash
|
2
|
+
|
3
|
+
A fast implementation of hashes as objects, benchmarked against OpenStruct. It allows chaining hash attributes as method calls instead of standard hash syntax.
|
4
|
+
|
5
|
+
Since an Intellihash extends the native Ruby `Hash`, this means instantiating a new smart hash is just as fast as a normal hash, _and_ you retain the ability to call all the normal instance methods on it.
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
intellihash = {
|
9
|
+
foo: {
|
10
|
+
bar: {
|
11
|
+
baz: {
|
12
|
+
bat: :bam
|
13
|
+
}
|
14
|
+
}
|
15
|
+
}
|
16
|
+
}
|
17
|
+
|
18
|
+
intellihash.foo.bar.baz.bat
|
19
|
+
|
20
|
+
#=> :bam
|
21
|
+
```
|
22
|
+
|
23
|
+
You can achieve similar results by converting your hash to an OpenStruct:
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
hash = {
|
27
|
+
foo: {
|
28
|
+
bar: {
|
29
|
+
baz: {
|
30
|
+
bat: :bam
|
31
|
+
}
|
32
|
+
}
|
33
|
+
}
|
34
|
+
}
|
35
|
+
|
36
|
+
open_struct = OpenStruct.new(hash)
|
37
|
+
open_struct.foo.bar.baz.bat
|
38
|
+
|
39
|
+
#=> :bam
|
40
|
+
```
|
41
|
+
|
42
|
+
But you lose access to your instance methods, and pay a performance penalty to instantiate it!
|
43
|
+
|
44
|
+
```ruby
|
45
|
+
intellihash.size
|
46
|
+
#=> 1
|
47
|
+
|
48
|
+
open_struct.size
|
49
|
+
#=> nil
|
50
|
+
```
|
51
|
+
|
52
|
+
## Performance vs. OpenStruct
|
53
|
+
|
54
|
+
Creating an Intellihash is approximately 5x faster than instantiating an OpenStruct.
|
55
|
+
|
56
|
+
```
|
57
|
+
user system total real
|
58
|
+
OpenStruct: 4.046875 0.906250 4.953125 ( 4.979611)
|
59
|
+
Intellihash: 0.828125 0.125000 0.953125 ( 0.956110)
|
60
|
+
```
|
61
|
+
|
62
|
+
See [Testing Performance](#testing-performance) for details on running benchmarks.
|
63
|
+
|
64
|
+
## Installation
|
65
|
+
|
66
|
+
Add this line to your application's Gemfile:
|
67
|
+
|
68
|
+
```ruby
|
69
|
+
gem 'intellihash'
|
70
|
+
```
|
71
|
+
|
72
|
+
And then execute:
|
73
|
+
|
74
|
+
$ bundle install
|
75
|
+
|
76
|
+
Or install it yourself as:
|
77
|
+
|
78
|
+
$ gem install intellihash
|
79
|
+
|
80
|
+
## Configuration
|
81
|
+
|
82
|
+
If you need to customize Intellihash, you may create a configuration:
|
83
|
+
|
84
|
+
```ruby
|
85
|
+
Intellihash.configure do |config|
|
86
|
+
config.enabled = true # (Boolean) Default: false
|
87
|
+
config.default_format = :symbol # (Symbol) Default: :symbol
|
88
|
+
config.smart_by_default = false # (Boolean) Default: false
|
89
|
+
end
|
90
|
+
```
|
91
|
+
|
92
|
+
### Options
|
93
|
+
|
94
|
+
- **enabled**
|
95
|
+
- Whether Intellihash is enabled or not
|
96
|
+
- You may wish to set this conditionally, such as enabling it only in a test environment
|
97
|
+
- i.e. `config.enabled = Rails.env.test?`
|
98
|
+
- **NOTE**: Once Intellihash is enabled, it cannot easily be disabled. Enable Intellihash with caution.
|
99
|
+
- **default_format**
|
100
|
+
- Valid values: `:sym, :symbol, :str, :string, :any`
|
101
|
+
- This determines the default storage and retrieval options
|
102
|
+
- **smart_by_default**
|
103
|
+
- Whether newly created hashes are smart
|
104
|
+
- When `false`, new hashes can still be converted to smart hashes via `hash.is_smart = true` or `hash.to_intellihash!`
|
105
|
+
|
106
|
+
### Rails
|
107
|
+
|
108
|
+
Place the above configuration in an initializer (such as `config/initializers/intellihash.rb`)
|
109
|
+
|
110
|
+
## Usage
|
111
|
+
|
112
|
+
### Instantiating an Intellihash
|
113
|
+
|
114
|
+
If you've configured Intellihash `smart_by_default = true`, you need to do nothing else as _all_ new hashes are Intellihashes.
|
115
|
+
|
116
|
+
However, if you prefer not to use this configuration, you can create an Intellihash from any existing hash:
|
117
|
+
|
118
|
+
```ruby
|
119
|
+
hash = { foo: :bar }
|
120
|
+
|
121
|
+
hash.is_smart?
|
122
|
+
#=> false
|
123
|
+
|
124
|
+
hash.is_smart = true
|
125
|
+
# or
|
126
|
+
hash.to_intellihash!
|
127
|
+
|
128
|
+
hash.is_smart?
|
129
|
+
#=> true
|
130
|
+
```
|
131
|
+
|
132
|
+
### Methods
|
133
|
+
|
134
|
+
Intellihash-powered hashes can access values via method chaining:
|
135
|
+
|
136
|
+
```ruby
|
137
|
+
intellihash = { foo: :bar }
|
138
|
+
|
139
|
+
intellihash.foo
|
140
|
+
|
141
|
+
#=> :bar
|
142
|
+
```
|
143
|
+
|
144
|
+
They can also store values in a similar way:
|
145
|
+
|
146
|
+
```ruby
|
147
|
+
intellihash.baz = :bat
|
148
|
+
intellihash
|
149
|
+
|
150
|
+
#=> { foo: :bar, baz: :bat }
|
151
|
+
```
|
152
|
+
|
153
|
+
When configured correctly, they work even when the object contains arrays and other classes:
|
154
|
+
|
155
|
+
```ruby
|
156
|
+
Intellihash.configure do |config|
|
157
|
+
config.enabled = true
|
158
|
+
config.smart_by_default = true
|
159
|
+
config.default_format = :any
|
160
|
+
end
|
161
|
+
|
162
|
+
intellihash = {
|
163
|
+
a: {
|
164
|
+
complicated: [
|
165
|
+
{
|
166
|
+
object: {
|
167
|
+
"containing_many" => {
|
168
|
+
types_of_things: Also::Works
|
169
|
+
}
|
170
|
+
}
|
171
|
+
}
|
172
|
+
]
|
173
|
+
}
|
174
|
+
}
|
175
|
+
|
176
|
+
intellihash.a.complicated.first.object.containing_many.types_of_things
|
177
|
+
|
178
|
+
#=> Also::Works
|
179
|
+
```
|
180
|
+
|
181
|
+
### Ambiguous Keys
|
182
|
+
|
183
|
+
When using an Intellihash that has an ambiguous key (i.e. the key exists both as a symbol and a string in the same hash), you can define which you would like to return at runtime:
|
184
|
+
|
185
|
+
```ruby
|
186
|
+
intellihash ={
|
187
|
+
foo: :bar
|
188
|
+
'foo' => 'bar'
|
189
|
+
}
|
190
|
+
|
191
|
+
intellihash.foo(from: :symbol) # or from: :sym
|
192
|
+
#=> :bar
|
193
|
+
|
194
|
+
intellihash.foo(from: :string) # or from: :str
|
195
|
+
#=> 'bar'
|
196
|
+
```
|
197
|
+
|
198
|
+
If you prefer, you can use the original hash syntax:
|
199
|
+
|
200
|
+
```ruby
|
201
|
+
intellihash[:foo]
|
202
|
+
#=> :bar
|
203
|
+
|
204
|
+
intellihash['foo']
|
205
|
+
#=> 'bar'
|
206
|
+
```
|
207
|
+
|
208
|
+
**NOTE**: Intellihashes always **store** values as the configured `default_format`, which defaults to `:symbol`. Override this in the config as needed, or use hash syntax: `intellihash['bar'] = 'bat'`.
|
209
|
+
|
210
|
+
### Collisions with Hash Methods
|
211
|
+
|
212
|
+
A powerful feature of Intellihash is the ability to use Ruby Hash instance methods on Intellihash. This also means that it's possible for some of the keys in the hash to collide with the names of the instance method you're trying to call.
|
213
|
+
|
214
|
+
For instance, consider `Hash#size`, which returns the number of keys in the hash:
|
215
|
+
|
216
|
+
```ruby
|
217
|
+
intellihash = { size: 2 }
|
218
|
+
|
219
|
+
intellihash.size
|
220
|
+
#=> 1
|
221
|
+
```
|
222
|
+
|
223
|
+
For this reason, it's better to use hash syntax to fetch these keys:
|
224
|
+
|
225
|
+
```ruby
|
226
|
+
# Bad
|
227
|
+
intellihash.size
|
228
|
+
|
229
|
+
# Good
|
230
|
+
intellihash[:size]
|
231
|
+
intellihash.fetch(:size)
|
232
|
+
```
|
233
|
+
|
234
|
+
You _can_, however, **set** keys in the hash using these instance methods. This is not recommended as the syntax leads to confusion when attempting to access the key.
|
235
|
+
|
236
|
+
```ruby
|
237
|
+
intellihash.size = 3
|
238
|
+
intellihash
|
239
|
+
|
240
|
+
#=> { size: 3 }
|
241
|
+
|
242
|
+
intellihash.size
|
243
|
+
#=> 1
|
244
|
+
```
|
245
|
+
|
246
|
+
## Development
|
247
|
+
|
248
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
249
|
+
|
250
|
+
## Testing
|
251
|
+
|
252
|
+
### Unit Tests
|
253
|
+
|
254
|
+
Run unit tests:
|
255
|
+
|
256
|
+
```
|
257
|
+
bundle exec rspec
|
258
|
+
```
|
259
|
+
|
260
|
+
### Testing Performance
|
261
|
+
|
262
|
+
Run performance benchmarks:
|
263
|
+
|
264
|
+
```
|
265
|
+
bundle exec rspec --tag performance:true
|
266
|
+
```
|
267
|
+
|
268
|
+
## Contributing
|
269
|
+
|
270
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/ty-porter/intellihash.
|
271
|
+
|
272
|
+
## License
|
273
|
+
|
274
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'intellihash'
|
6
|
+
require 'byebug'
|
7
|
+
|
8
|
+
Intellihash.configure do |config|
|
9
|
+
config.enabled = true
|
10
|
+
config.smart_by_default = true
|
11
|
+
end
|
12
|
+
|
13
|
+
require 'irb'
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
data/intellihash.gemspec
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'lib/intellihash/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = 'intellihash'
|
7
|
+
spec.version = Intellihash::VERSION
|
8
|
+
spec.authors = ['Tyler Porter']
|
9
|
+
spec.email = ['tyler.b.porter@gmail.com']
|
10
|
+
|
11
|
+
spec.summary = 'A fast implementation of hashes as objects'
|
12
|
+
spec.description = <<~DESC
|
13
|
+
A fast implementation of hashes as objects, benchmarked against OpenStruct.
|
14
|
+
It allows chaining hash attributes as method calls instead of standard hash syntax.
|
15
|
+
DESC
|
16
|
+
spec.homepage = 'https://github.com/ty-porter/intellihash'
|
17
|
+
spec.license = 'MIT'
|
18
|
+
spec.required_ruby_version = Gem::Requirement.new('>= 2.6.5')
|
19
|
+
spec.metadata['allowed_push_host'] = 'https://rubygems.org'
|
20
|
+
|
21
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
22
|
+
spec.metadata['source_code_uri'] = 'https://github.com/ty-porter/intellihash'
|
23
|
+
|
24
|
+
# Specify which files should be added to the gem when it is released.
|
25
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
26
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
27
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
28
|
+
end
|
29
|
+
spec.bindir = 'exe'
|
30
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
31
|
+
spec.require_paths = ['lib']
|
32
|
+
|
33
|
+
spec.add_development_dependency('byebug', ['>= 0'])
|
34
|
+
spec.add_development_dependency('rspec-benchmark', ['~> 0.5.0'])
|
35
|
+
spec.add_development_dependency('rubocop', ['~> 1.18'])
|
36
|
+
end
|
data/lib/intellihash.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'intellihash/callbacks'
|
4
|
+
require 'intellihash/configuration'
|
5
|
+
require 'intellihash/mixins'
|
6
|
+
require 'intellihash/version'
|
7
|
+
|
8
|
+
module Intellihash
|
9
|
+
def self.enabled?
|
10
|
+
Intellihash.configuration.enabled
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Intellihash
|
4
|
+
module Callbacks
|
5
|
+
# https://ruby-doc.org/core-3.0.1/Hash.html
|
6
|
+
#
|
7
|
+
# Methods that return a copy of :self: need this callback to populate :smart: from :self:
|
8
|
+
AFTER_CALLBACK_TARGETS = %i[
|
9
|
+
compact
|
10
|
+
invert
|
11
|
+
merge
|
12
|
+
reject
|
13
|
+
select
|
14
|
+
slice
|
15
|
+
to_h
|
16
|
+
transform_keys
|
17
|
+
transform_values
|
18
|
+
].freeze
|
19
|
+
|
20
|
+
# Callback registration happens as soon as Intellihash::Callbacks is prepended
|
21
|
+
AFTER_CALLBACK_TARGETS.each do |instance_method|
|
22
|
+
define_method(instance_method) do |*args, &block|
|
23
|
+
# Call original method
|
24
|
+
result = super(*args, &block)
|
25
|
+
|
26
|
+
# Register :after: callbacks
|
27
|
+
result.is_smart = smart if result.respond_to?(:is_smart=) && !result.frozen?
|
28
|
+
|
29
|
+
result
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Intellihash
|
4
|
+
class << self
|
5
|
+
attr_reader :configuration
|
6
|
+
|
7
|
+
def configure
|
8
|
+
yield(@configuration)
|
9
|
+
|
10
|
+
inject_dependencies! if Intellihash.enabled?
|
11
|
+
end
|
12
|
+
|
13
|
+
def configuration=(config)
|
14
|
+
raise InvalidConfiguration, config.class unless config.is_a?(Intellihash::Configuration)
|
15
|
+
|
16
|
+
@configuration = config
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def inject_dependencies!
|
22
|
+
Hash.include Intellihash::Mixins
|
23
|
+
Hash.prepend Intellihash::Callbacks
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class Configuration
|
28
|
+
attr_accessor :enabled, :smart_by_default
|
29
|
+
attr_reader :default_format
|
30
|
+
|
31
|
+
def initialize
|
32
|
+
@default_format = :symbol
|
33
|
+
@enabled = false
|
34
|
+
@smart_by_default = false
|
35
|
+
end
|
36
|
+
|
37
|
+
def default_format=(other)
|
38
|
+
@default_format = Intellihash::Mixins::FORMATTER.member?(other) ? other : :symbol
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class InvalidConfiguration < StandardError; end
|
43
|
+
|
44
|
+
@configuration = Intellihash::Configuration.new
|
45
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Intellihash
|
4
|
+
module Mixins
|
5
|
+
def smart
|
6
|
+
@smart = @smart.nil? ? Intellihash.configuration.smart_by_default : @smart
|
7
|
+
end
|
8
|
+
|
9
|
+
def is_smart=(value)
|
10
|
+
# Ensure this is a boolean
|
11
|
+
@smart = value == true
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_intellihash
|
15
|
+
@smart = true
|
16
|
+
self
|
17
|
+
end
|
18
|
+
|
19
|
+
def is_smart?
|
20
|
+
smart
|
21
|
+
end
|
22
|
+
|
23
|
+
def default_format
|
24
|
+
@default_format ||= Intellihash.configuration.default_format
|
25
|
+
end
|
26
|
+
|
27
|
+
def default_format=(other)
|
28
|
+
@default_format = FORMATTER.member?(other) ? other : FORMATTER[:symbol]
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
FORMATTER = {
|
34
|
+
str: :to_s,
|
35
|
+
string: :to_s,
|
36
|
+
sym: :to_sym,
|
37
|
+
symbol: :to_sym,
|
38
|
+
any: :any
|
39
|
+
}.freeze
|
40
|
+
|
41
|
+
def method_missing(method_name, *args, **kwargs, &block)
|
42
|
+
super unless respond_to?(:is_smart?) && is_smart?
|
43
|
+
|
44
|
+
if method_name[-1] == '='
|
45
|
+
send(:store, method_name[0, method_name.size - 1].send(key_store_as), args.first)
|
46
|
+
else
|
47
|
+
format_method = key_retrieve_from(kwargs)
|
48
|
+
case format_method
|
49
|
+
when :any then fetch_where_present(method_name)
|
50
|
+
else send(:[], method_name.send(format_method))
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def respond_to_missing?(*)
|
56
|
+
is_smart? ? true : super
|
57
|
+
end
|
58
|
+
|
59
|
+
def key_store_as
|
60
|
+
key_format = default_format == :any ? FORMATTER[:symbol] : FORMATTER[default_format]
|
61
|
+
key_format || FORMATTER[:symbol]
|
62
|
+
end
|
63
|
+
|
64
|
+
def key_retrieve_from(options)
|
65
|
+
from_option = options.fetch(:from) { default_format }
|
66
|
+
format(from_option)
|
67
|
+
end
|
68
|
+
|
69
|
+
def format(value)
|
70
|
+
FORMATTER.include?(value) ? FORMATTER[value] : FORMATTER[:symbol]
|
71
|
+
end
|
72
|
+
|
73
|
+
def fetch_where_present(method_name)
|
74
|
+
return send(:[], method_name.to_sym) if member?(method_name.to_sym)
|
75
|
+
return send(:[], method_name.to_s) if member?(method_name.to_s)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
metadata
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: intellihash
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Tyler Porter
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-08-02 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: byebug
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec-benchmark
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.5.0
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.5.0
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rubocop
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.18'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.18'
|
55
|
+
description: |
|
56
|
+
A fast implementation of hashes as objects, benchmarked against OpenStruct.
|
57
|
+
It allows chaining hash attributes as method calls instead of standard hash syntax.
|
58
|
+
email:
|
59
|
+
- tyler.b.porter@gmail.com
|
60
|
+
executables: []
|
61
|
+
extensions: []
|
62
|
+
extra_rdoc_files: []
|
63
|
+
files:
|
64
|
+
- ".gitignore"
|
65
|
+
- ".rspec"
|
66
|
+
- ".rubocop.yml"
|
67
|
+
- Gemfile
|
68
|
+
- Gemfile.lock
|
69
|
+
- LICENSE.txt
|
70
|
+
- README.md
|
71
|
+
- Rakefile
|
72
|
+
- bin/console
|
73
|
+
- bin/setup
|
74
|
+
- intellihash.gemspec
|
75
|
+
- lib/intellihash.rb
|
76
|
+
- lib/intellihash/callbacks.rb
|
77
|
+
- lib/intellihash/configuration.rb
|
78
|
+
- lib/intellihash/mixins.rb
|
79
|
+
- lib/intellihash/version.rb
|
80
|
+
homepage: https://github.com/ty-porter/intellihash
|
81
|
+
licenses:
|
82
|
+
- MIT
|
83
|
+
metadata:
|
84
|
+
allowed_push_host: https://rubygems.org
|
85
|
+
homepage_uri: https://github.com/ty-porter/intellihash
|
86
|
+
source_code_uri: https://github.com/ty-porter/intellihash
|
87
|
+
post_install_message:
|
88
|
+
rdoc_options: []
|
89
|
+
require_paths:
|
90
|
+
- lib
|
91
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - ">="
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: 2.6.5
|
96
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
97
|
+
requirements:
|
98
|
+
- - ">="
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
version: '0'
|
101
|
+
requirements: []
|
102
|
+
rubygems_version: 3.0.3
|
103
|
+
signing_key:
|
104
|
+
specification_version: 4
|
105
|
+
summary: A fast implementation of hashes as objects
|
106
|
+
test_files: []
|