key_dial 1.0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 80905cbb9d3d2375534b67233544440b17c3a9aa2e092089c304db1d7f48161e
4
+ data.tar.gz: 6fca290e125cf5cc240e7d420b9bf504c0932bb31bf916ab30cb4fe2f73e4de0
5
+ SHA512:
6
+ metadata.gz: e1c107522a250b151d0ff974d5acf41712adfeba5e1294464cd689c97265d488497c385f4eb181695e607c2bd353b7250a96dba5de80e0fa60f02b3f87ba9744
7
+ data.tar.gz: 523ccc88302b8ec64ae711b91b06556bf5a29d414accee9095ee1df0675ee37e7814628d7920cc13d9c0e7e871b5583954d7c6f5f6a930591ed49c403eeb9ec5
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ .git
2
+ .env
3
+ *.lnk
4
+ *.db
5
+ *.ini
6
+
7
+ /.bundle/
8
+ /.yardoc
9
+ /_yardoc/
10
+ /coverage/
11
+ /doc/
12
+ /pkg/
13
+ /spec/reports/
14
+ /tmp/
15
+
16
+ # rspec failure tracking
17
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ sudo: false
3
+ language: ruby
4
+ cache: bundler
5
+ rvm:
6
+ - 2.4.3
7
+ before_install: gem install bundler -v 1.17.2
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in hash_dial.gemspec
6
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,35 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ key_dial (1.0.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ diff-lcs (1.3)
10
+ rake (10.5.0)
11
+ rspec (3.8.0)
12
+ rspec-core (~> 3.8.0)
13
+ rspec-expectations (~> 3.8.0)
14
+ rspec-mocks (~> 3.8.0)
15
+ rspec-core (3.8.0)
16
+ rspec-support (~> 3.8.0)
17
+ rspec-expectations (3.8.2)
18
+ diff-lcs (>= 1.2.0, < 2.0)
19
+ rspec-support (~> 3.8.0)
20
+ rspec-mocks (3.8.0)
21
+ diff-lcs (>= 1.2.0, < 2.0)
22
+ rspec-support (~> 3.8.0)
23
+ rspec-support (3.8.0)
24
+
25
+ PLATFORMS
26
+ ruby
27
+
28
+ DEPENDENCIES
29
+ bundler (~> 1.17)
30
+ key_dial!
31
+ rake (~> 10.0)
32
+ rspec (~> 3.0)
33
+
34
+ BUNDLED WITH
35
+ 1.17.2
data/README.md ADDED
@@ -0,0 +1,82 @@
1
+ # KeyDial (Ruby Gem)
2
+
3
+ **Avoid all errors when accessing a deeply nested Hash key,** or Array or Struct key. KeyDial goes one step beyond Ruby 2.3's `dig()` method by quietly returning `nil` (or your default) if the keys requested are invalid for any reason, and never an error.
4
+
5
+ In particular, if you try to access a key on a value that can't have keys, `dig()` will cause an error where KeyDial will not.
6
+
7
+ ```ruby
8
+ hash = {a: {b: {c: true}, d: 5}}
9
+
10
+ hash.dig( :a, :d, :c) #=> TypeError: Integer does not have #dig method
11
+ hash.call(:a, :d, :c) #=> nil
12
+ hash.call(:a, :b, :c) #=> true
13
+ ```
14
+
15
+ **Bonus: you don't even need to fiddle with existing code.** If you have already written something to access a deeply nested key, just surround this with `dial` and `call` (rather than changing it to the form above as function parameters).
16
+
17
+ ```ruby
18
+ hash[:a][:d][:c] #=> TypeError: no implicit conversion of Symbol into Integer
19
+
20
+ #hash → [:a][:d][:c]
21
+ # ↓ ↓
22
+ hash.dial[:a][:d][:c].call #=> nil
23
+ ```
24
+
25
+ **KeyDial will work on mixed objects**, such as structs containing arrays containing hashes. It can be called from any hash, array or struct.
26
+
27
+ ## Explanation
28
+
29
+ We use the concept of placing a phone-call: you can 'dial' any set of keys regardless of whether they exist (like entering a phone number), then finally place the 'call'. If the key is invalid for any reason you get nil/default (like a wrong number); otherwise you get the value (you're connected).
30
+
31
+ This works by intermediating your request with a KeyDialler object. Trying to access keys on this object simply builds up a list of keys to use when you later place the 'call'. The call then `dig`s for the keys safely.
32
+
33
+ ## Usage
34
+
35
+ ```ruby
36
+ require 'hash_dial'
37
+ ```
38
+
39
+ ### Use it like `dig()`
40
+
41
+ If you want to follow this pattern, it works in the same way. You can't change the default return value when using this pattern.
42
+
43
+ ```ruby
44
+ array = [0, {a: [true, false], b: 'foo'}, 2]
45
+
46
+ array.call(1, :a, 0) #=> true (i.e. array[1][:a][0])
47
+ array.call(1, :b, 0) #=> nil (i.e. array[1][:b][0] doesn't exist)
48
+ ```
49
+
50
+ You can `call` on any Hash, Array or Struct after requiring this gem.
51
+
52
+ Note that KeyDial does not treat strings as arrays. Trying to access a key on a string will return nil or your default. (This is also how `dig()` works.)
53
+
54
+ ### Use key access syntax (allows default return value)
55
+
56
+ ```ruby
57
+ hash.dial[:a][4][:c].call # Returns the value at hash[:a][4][:c] or nil
58
+ hash.dial[:a][4][:c].call('Ooops') # Returns the value at hash[:a][4][:c] or 'Ooops'
59
+ ```
60
+
61
+ You can `dial` on any Hash, Array or Struct after requiring this gem.
62
+
63
+ ### Use the KeyDialler object
64
+
65
+ If you don't do this all in one line, you can access the KeyDialler object should you want to manipulate it:
66
+
67
+ ```ruby
68
+ dialler = KeyDial::KeyDialler.new(struct) # Returns a KeyDialler object referencing struct
69
+ dialler[:a] # Adds :a to the list of keys to dial (returns self)
70
+ dialler.dial!(:b, :c) # Longhand way of adding more keys (returns self)
71
+ dialler.undial! # Removes the last-added key (returns self)
72
+ dialler[:c][:d] # Adds two more keys (returns self)
73
+ dialler += :e # Adds yet one more (returns self)
74
+ dialler -= :a # Removes all such keys from the list (returns self)
75
+ # So far we have dialled [:b][:c][:d][:e]
76
+ dialler.call # Returns the value at struct[:b][:c][:d][:e] or nil
77
+ dialler.hangup # Returns the original keyed object by reference
78
+ ```
79
+
80
+ ## Note
81
+
82
+ KeyDial is a generic version of the gem HashDial, replacing and deprecating it.
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "key_dial"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/key_dial.gemspec ADDED
@@ -0,0 +1,29 @@
1
+
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "key_dial/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "key_dial"
8
+ spec.version = KeyDial::VERSION
9
+ spec.authors = ["Convincible"]
10
+ spec.email = ["development@convincible.media"]
11
+
12
+ spec.summary = "Access (deeply nested) Hash, Array or Struct keys. Get the value, or nil/default instead of any error. (Even safer than Ruby 2.3's dig method)."
13
+ spec.description = "Avoid all errors when accessing (deeply nested) Hash, Array or Struct keys. Safer than dig(), as will quietly return nil (or your default) if the keys requested are invalid for any reason at all. Bonus: you don't even need to fiddle with existing code. If you have already written something to access a deep key (e.g. hash[:a][:b][:c]), just surround this with '.dial' and '.call'."
14
+ spec.homepage = "https://github.com/ConvincibleMedia/ruby-gem-key_dial"
15
+
16
+ # Specify which files should be added to the gem when it is released.
17
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
18
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
19
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
20
+ end
21
+ spec.bindir = "exe"
22
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
+ spec.require_paths = ["lib"]
24
+ spec.required_ruby_version = '>= 2.3'
25
+
26
+ spec.add_development_dependency "bundler", "~> 1.17"
27
+ spec.add_development_dependency "rake", "~> 10.0"
28
+ spec.add_development_dependency "rspec", "~> 3.0"
29
+ end
data/lib/key_dial.rb ADDED
@@ -0,0 +1,37 @@
1
+ require "key_dial/version"
2
+ require "key_dial/key_dialler"
3
+
4
+ module KeyDial
5
+
6
+ # Called on a Hash, Array or Struct, returns a KeyDialler object.
7
+ #
8
+ # @param lookup Parameters to this method form initial keys to dial. This is unnecessary but works anyway. For simplicity, dial keys by accessing them as if KeyDialler were a keyed object itself.
9
+ #
10
+ def to_dial(*lookup)
11
+ return KeyDialler.new(self, *lookup)
12
+ end
13
+
14
+ alias_method :dial, :to_dial
15
+
16
+ # Called directly on a keyed object, immediately dials and calls the keys specified as arguments. Returns the value found, or nil.
17
+ #
18
+ # @param lookup The keys to attempt to retrieve.
19
+ #
20
+ def call(*lookup)
21
+ return KeyDialler.new(self, *lookup).call
22
+ end
23
+
24
+ end
25
+
26
+ # Extend core class so that .dial can be called seamlessly
27
+ class Hash
28
+ include KeyDial
29
+ end
30
+
31
+ class Array
32
+ include KeyDial
33
+ end
34
+
35
+ class Struct
36
+ include KeyDial
37
+ end
@@ -0,0 +1,76 @@
1
+ module KeyDial
2
+
3
+ class KeyDialler
4
+
5
+ @obj_with_keys
6
+ @lookup
7
+ @default = nil
8
+
9
+ def initialize(obj_with_keys, *lookup)
10
+ if obj_with_keys.respond_to?(:dig)
11
+ @obj_with_keys = obj_with_keys
12
+ else
13
+ raise ArgumentError.new('HashDialler must be initialized on a Hash, Array or Struct, or an object that responds to :dig.')
14
+ end
15
+ @lookup = []
16
+ if lookup.length > 0
17
+ dial!(*lookup)
18
+ end
19
+ end
20
+
21
+ # Adds a key to the list of nested keys to try, one level deeper.
22
+ #
23
+ # @param keys The key to add. Multiple arguments would add multiple keys.
24
+ #
25
+ def dial!(*keys)
26
+ #unless key.is_a(Symbol) || key.is_a(String)
27
+ @lookup += keys
28
+ return self
29
+ end
30
+
31
+ # Digs into the object to the list of keys specified by dialling. Returns nil or default if specified.
32
+ #
33
+ # @param default What to return if no key is found.
34
+ #
35
+ def call(default = nil)
36
+ begin
37
+ value = @obj_with_keys.dig(*@lookup)
38
+ rescue
39
+ value = default
40
+ end
41
+ return value
42
+ end
43
+
44
+ # Return the original keyed object.
45
+ def hangup
46
+ return @obj_with_keys
47
+ end
48
+
49
+ # Remove keys from the dialling list.
50
+ #
51
+ # @param keys If specified, these keys would be removed from wherever they appear in the dialling list. Otherwise, the last added key is removed.
52
+ #
53
+ def undial!(*keys)
54
+ if keys.length > 0
55
+ @lookup -= keys
56
+ elsif @lookup.length > 0
57
+ @lookup.pop
58
+ end
59
+ return self
60
+ end
61
+
62
+ # The preferred way to build up your dialling list. Access KeyDialler as if it were a keyed object, e.g. keydialler[a][b][c]. This does not actually return any value, rather it dials those keys (awaiting a call).
63
+ #
64
+ def [](key)
65
+ return dial!(key)
66
+ end
67
+ def +(key)
68
+ return dial!(key)
69
+ end
70
+ def -(key)
71
+ return undial!(key)
72
+ end
73
+
74
+ end
75
+
76
+ end
@@ -0,0 +1,3 @@
1
+ module KeyDial
2
+ VERSION = "1.0.0"
3
+ end
metadata ADDED
@@ -0,0 +1,102 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: key_dial
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Convincible
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2019-01-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.17'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.17'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ description: 'Avoid all errors when accessing (deeply nested) Hash, Array or Struct
56
+ keys. Safer than dig(), as will quietly return nil (or your default) if the keys
57
+ requested are invalid for any reason at all. Bonus: you don''t even need to fiddle
58
+ with existing code. If you have already written something to access a deep key (e.g.
59
+ hash[:a][:b][:c]), just surround this with ''.dial'' and ''.call''.'
60
+ email:
61
+ - development@convincible.media
62
+ executables: []
63
+ extensions: []
64
+ extra_rdoc_files: []
65
+ files:
66
+ - ".gitignore"
67
+ - ".rspec"
68
+ - ".travis.yml"
69
+ - Gemfile
70
+ - Gemfile.lock
71
+ - README.md
72
+ - Rakefile
73
+ - bin/console
74
+ - bin/setup
75
+ - key_dial.gemspec
76
+ - lib/key_dial.rb
77
+ - lib/key_dial/key_dialler.rb
78
+ - lib/key_dial/version.rb
79
+ homepage: https://github.com/ConvincibleMedia/ruby-gem-key_dial
80
+ licenses: []
81
+ metadata: {}
82
+ post_install_message:
83
+ rdoc_options: []
84
+ require_paths:
85
+ - lib
86
+ required_ruby_version: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: '2.3'
91
+ required_rubygems_version: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ requirements: []
97
+ rubygems_version: 3.0.0
98
+ signing_key:
99
+ specification_version: 4
100
+ summary: Access (deeply nested) Hash, Array or Struct keys. Get the value, or nil/default
101
+ instead of any error. (Even safer than Ruby 2.3's dig method).
102
+ test_files: []