xkeys 0.0.2 → 1.0.1
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.
- data/.yardopts +4 -0
- data/HISTORY.txt +24 -0
- data/lib/xkeys.rb +81 -40
- data/test/01get.rb +13 -6
- data/test/02hash.rb +5 -2
- data/test/03auto.rb +3 -0
- data/xkeys.gemspec +4 -3
- metadata +5 -4
data/.yardopts
ADDED
data/HISTORY.txt
CHANGED
@@ -1,3 +1,27 @@
|
|
1
|
+
2013-07-25 Version 1.0.1
|
2
|
+
The auto-indexing array key for set has been changed from nil to
|
3
|
+
:[] in order to support nil keys (e.g. in case a NULL value from a
|
4
|
+
database field is being used as a nil key).
|
5
|
+
|
6
|
+
Get and set now have compatible syntax so that you can use ||=:
|
7
|
+
root[key1, ..., keyN[, option_hash]]
|
8
|
+
root[key1, ..., keyN[, option_hash]] = value
|
9
|
+
|
10
|
+
root = {}.extend XKeys::Hash
|
11
|
+
root[:top, :[], :[] => false] ||= 'just because'
|
12
|
+
# root => { :top => { :[] => 'just because' } }
|
13
|
+
|
14
|
+
Note that a leading nil is no longer ignored on get. Use
|
15
|
+
array[int1, int2, {}] to avoid array[start, length] slice
|
16
|
+
interpretation.
|
17
|
+
|
18
|
+
Option :raise => true raises a KeyError or IndexError on a missing
|
19
|
+
value. Option :raise => *parameters raises the specified exception,
|
20
|
+
e.g. :raise => RuntimeError or :raise => [RuntimeError, 'SNAFU'].
|
21
|
+
|
22
|
+
The :else option defaults to nil on get. Use :raise to force an
|
23
|
+
exception.
|
24
|
+
|
1
25
|
2013-07-08 Version 0.0.2
|
2
26
|
Packaging and documentation cleanup.
|
3
27
|
|
data/lib/xkeys.rb
CHANGED
@@ -3,21 +3,22 @@
|
|
3
3
|
#
|
4
4
|
# Synopsis:
|
5
5
|
# root = {}.extend XKeys::Hash
|
6
|
-
# root[:my, :list,
|
7
|
-
# root[:my, :list,
|
6
|
+
# root[:my, :list, :[]] = 'value 1'
|
7
|
+
# root[:my, :list, :[]] = 'value 2'
|
8
8
|
# root[:sparse, 10] = 'value 3'
|
9
9
|
# # => { :my => { :list => [ 'value 1', 'value 2' ] },
|
10
10
|
# # :sparse => { 10 => 'value 3' } }
|
11
11
|
# root[:missing] # => nil
|
12
12
|
# root[:missing, :else => false] # => false
|
13
|
-
# root[:missing,
|
13
|
+
# root[:missing, :raise => true] # => raises KeyError
|
14
14
|
#
|
15
15
|
# root = [].extend XKeys::Auto
|
16
|
-
# root[1,
|
16
|
+
# root[1, :[]] = 'value 1'
|
17
17
|
# root[1, 3] = 'value 2'
|
18
18
|
# # => [ nil, [ 'value 1', nil, nil, 'value 2' ] ]
|
19
19
|
# root[0, 1] # => [ nil ] (slice of length 1 at 0)
|
20
|
-
# root[
|
20
|
+
# root[1, 0, {}] # => 'value 1'
|
21
|
+
# root[1, 4, {}] # => nil
|
21
22
|
#
|
22
23
|
# @author Brian Katzung <briank@kappacs.com>, Kappa Computer Solutions, LLC
|
23
24
|
# @copyright 2013 Brian Katzung and Kappa Computer Solutions, LLC
|
@@ -31,21 +32,35 @@ module XKeys::Get
|
|
31
32
|
# Perform an extended fetch using successive keys to traverse a tree
|
32
33
|
# of nested hashes and/or arrays.
|
33
34
|
#
|
34
|
-
# xfetch(
|
35
|
+
# xfetch(key1, ..., keyN [, option_hash])
|
35
36
|
#
|
36
|
-
#
|
37
|
-
#
|
38
|
-
#
|
37
|
+
# Options:
|
38
|
+
#
|
39
|
+
# :else => default value
|
40
|
+
# The default value to return if the specified keys do not exist.
|
41
|
+
# The :raise option takes precedence.
|
42
|
+
#
|
43
|
+
# :raise => true
|
44
|
+
# Raise a KeyError or IndexError if the specified keys do not
|
45
|
+
# exist. This is the default behavior for xfetch in the absence
|
46
|
+
# of an :else option.
|
47
|
+
#
|
48
|
+
# :raise => *parameters
|
49
|
+
# Like :raise => true but does raise *parameters instead, e.g.
|
50
|
+
# :raise => RuntimeError or :raise => [RuntimeError, 'SNAFU']
|
39
51
|
def xfetch (*args)
|
40
52
|
if args[-1].is_a?(Hash) then options, last = args[-1], -2
|
41
53
|
else options, last = {}, -1
|
42
54
|
end
|
43
|
-
|
44
|
-
args[first..last].inject(self) do |node, key|
|
55
|
+
args[0..last].inject(self) do |node, key|
|
45
56
|
begin node.fetch key
|
46
57
|
rescue KeyError, IndexError
|
47
|
-
|
48
|
-
|
58
|
+
if options[:raise] and options[:raise] != true
|
59
|
+
raise *options[:raise]
|
60
|
+
elsif options[:raise] or !options.has_key? :else
|
61
|
+
raise
|
62
|
+
else return options[:else]
|
63
|
+
end
|
49
64
|
end
|
50
65
|
end
|
51
66
|
end
|
@@ -57,20 +72,26 @@ module XKeys::Get
|
|
57
72
|
# as normal.
|
58
73
|
#
|
59
74
|
# array[int1, int2] returns a length-based array slice as normal.
|
60
|
-
#
|
61
|
-
#
|
75
|
+
# Append an option hash to force nested index behavior for two
|
76
|
+
# integer array indexes: array[index1, index2, {}].
|
62
77
|
#
|
63
|
-
# [
|
64
|
-
# hashes and/or arrays using xfetch.
|
65
|
-
#
|
66
|
-
# :else => nil.
|
78
|
+
# [key1, ..., keyN[, option_hash]] traverses a tree of nested
|
79
|
+
# hashes and/or arrays using xfetch.
|
80
|
+
#
|
81
|
+
# Option :else => nil is used if no :else option is supplied.
|
82
|
+
# See xfetch for option details.
|
67
83
|
def [] (*args)
|
68
|
-
if args.count == 1
|
69
|
-
args[0].is_a?(Integer)
|
84
|
+
if args.count == 1 or (self.is_a?(Array) and args.count == 2 and
|
85
|
+
args[0].is_a?(Integer) and args[1].is_a?(Integer))
|
70
86
|
# [key] or array[start, length]
|
71
87
|
super *args
|
72
|
-
|
73
|
-
|
88
|
+
else
|
89
|
+
def_opts = { :else => nil } # Default options
|
90
|
+
if args[-1].is_a? Hash
|
91
|
+
options, last = def_opts.merge(args[-1]), -2
|
92
|
+
else options, last = def_opts, -1
|
93
|
+
end
|
94
|
+
xfetch *args[0..last], options
|
74
95
|
end
|
75
96
|
end
|
76
97
|
|
@@ -79,25 +100,42 @@ end
|
|
79
100
|
# "Private" module for XKeys::Set_* common code
|
80
101
|
module XKeys::Set_
|
81
102
|
|
82
|
-
# Common code for XKeys::Set_Hash and XKeys::Set_Auto
|
103
|
+
# Common code for XKeys::Set_Hash and XKeys::Set_Auto. This method
|
104
|
+
# returns true if it is handling the set, or false if super should
|
105
|
+
# handle the set.
|
106
|
+
#
|
107
|
+
# _xset(key1, ..., keyN[, options_hash], value) { |key, options| block }
|
108
|
+
#
|
109
|
+
# The block should return true to auto-vivify an array or false to
|
110
|
+
# auto-vivify a hash.
|
111
|
+
#
|
112
|
+
# Options:
|
113
|
+
#
|
114
|
+
# :[] => false
|
115
|
+
# Disable :[] auto-indexing
|
83
116
|
def _xset (*args)
|
84
|
-
if args.
|
85
|
-
|
86
|
-
|
87
|
-
|
117
|
+
if args[-2].is_a?(Hash) then options, last = args[-2], -3
|
118
|
+
else options, last = {}, -2
|
119
|
+
end
|
120
|
+
if args.count + last == 0
|
121
|
+
if self.is_a?(Array) && args[0] == :[]
|
122
|
+
self << args[-1] # array[:[]] = value
|
123
|
+
else return false # [key] = value ==> super
|
88
124
|
end
|
89
125
|
else
|
90
|
-
# root[key1, ..., keyN] = value
|
91
|
-
(node, key) = args[1
|
92
|
-
if yield key
|
126
|
+
# root[key1, ..., keyN[, option_hash]] = value
|
127
|
+
(node, key) = args[1..last].inject([self, args[0]]) do |node, key|
|
128
|
+
if yield key, options
|
93
129
|
node[0][node[1]] ||= []
|
94
|
-
[node[0][node[1]], key
|
130
|
+
[node[0][node[1]], (key != :[]) ? key :
|
131
|
+
node[0][node[1]].size]
|
95
132
|
else
|
96
133
|
node[0][node[1]] ||= {}
|
97
134
|
[node[0][node[1]], key]
|
98
135
|
end
|
99
136
|
end
|
100
|
-
if yield key
|
137
|
+
if yield key, options
|
138
|
+
node[(key != :[])? key : node.size] = args[-1]
|
101
139
|
else node[key] = args[-1]
|
102
140
|
end
|
103
141
|
end
|
@@ -111,12 +149,14 @@ module XKeys::Set_Hash
|
|
111
149
|
include XKeys::Set_
|
112
150
|
|
113
151
|
# Auto-vivify nested hash trees using extended hash key/array index
|
114
|
-
# assignment syntax.
|
152
|
+
# assignment syntax. :[] keys create nested arrays as needed. Other
|
115
153
|
# keys, including integer keys, create nested hashes as needed.
|
116
154
|
#
|
117
|
-
# root[key1, ..., keyN] = value
|
155
|
+
# root[key1, ..., keyN[, options_hash]] = value
|
118
156
|
def []= (*args)
|
119
|
-
super
|
157
|
+
super args[0], args[-1] unless _xset(*args) do |key, opts|
|
158
|
+
key == :[] and opts[:[]] != false
|
159
|
+
end
|
120
160
|
args[-1]
|
121
161
|
end
|
122
162
|
|
@@ -127,14 +167,15 @@ module XKeys::Set_Auto
|
|
127
167
|
include XKeys::Set_
|
128
168
|
|
129
169
|
# Auto-vivify nested hash and/or array trees using extended hash
|
130
|
-
# key/array index assignment syntax.
|
170
|
+
# key/array index assignment syntax. :[] keys and integer keys
|
131
171
|
# create nested arrays as needed. Other keys create nested hashes
|
132
172
|
# as needed.
|
133
173
|
#
|
134
|
-
# root[key1, ..., keyN] = value
|
174
|
+
# root[key1, ..., keyN[, options_hash]] = value
|
135
175
|
def []= (*args)
|
136
|
-
super *args
|
137
|
-
|
176
|
+
super args[0], args[-1] unless _xset(*args) do |key, opts|
|
177
|
+
(key == :[] and opts[:[]] != false) or key.is_a?(Integer)
|
178
|
+
end
|
138
179
|
args[-1]
|
139
180
|
end
|
140
181
|
|
data/test/01get.rb
CHANGED
@@ -24,7 +24,15 @@ class TestXK < MiniTest::Unit::TestCase
|
|
24
24
|
|
25
25
|
assert_equal(false, h[:b, :d, :else=>false], 'h[:b, :d, :else=>false]')
|
26
26
|
assert_equal(nil, h[:b, :d], 'h[:b, :d]')
|
27
|
-
assert_raises(KeyError, 'h[:b, :d, {}]')
|
27
|
+
assert_raises(KeyError, 'h[:b, :d, {:raise=>true}]') do
|
28
|
+
h[:b, :d, {:raise=>true}]
|
29
|
+
end
|
30
|
+
assert_raises(RuntimeError, 'h[:b, :d, {:raise=>RuntimeError}]') do
|
31
|
+
h[:b, :d, {:raise=>RuntimeError}]
|
32
|
+
end
|
33
|
+
assert_raises(RuntimeError, 'h[:b, :d, {:raise=>[RuntimeError]}]') do
|
34
|
+
h[:b, :d, {:raise=>[RuntimeError]}]
|
35
|
+
end
|
28
36
|
end
|
29
37
|
|
30
38
|
def test_array_get
|
@@ -35,7 +43,6 @@ class TestXK < MiniTest::Unit::TestCase
|
|
35
43
|
|
36
44
|
assert_equal('0', a.xfetch(0), 'a.xfetch 0')
|
37
45
|
assert_equal('1.0', a.xfetch(1, 0), 'a.xfetch 1, 0')
|
38
|
-
assert_equal('1.0', a.xfetch(nil, 1, 0), 'a.xfetch nil, 1, 0')
|
39
46
|
assert_equal('1.0', a.xfetch(1, 0, {}), 'a.xfetch 1, 0, {}')
|
40
47
|
assert_equal('2.1.1', a.xfetch(2, 1, 1), 'a.xfetch 2, 1, 1')
|
41
48
|
|
@@ -45,14 +52,14 @@ class TestXK < MiniTest::Unit::TestCase
|
|
45
52
|
|
46
53
|
assert_equal('0', a[0], 'a[0]')
|
47
54
|
assert_equal([['1.0']], a[1, 1], 'a[1, 1]')
|
48
|
-
assert_equal('1.0', a[nil, 1, 0], 'a[nil, 1, 0]')
|
49
55
|
assert_equal('1.0', a[1, 0, {}], 'a[1, 0, {}]')
|
50
|
-
assert_equal('1.0', a[nil, 1, 0, {}], 'a[nil, 1, 0, {}]')
|
51
56
|
assert_equal('2.1.1', a[2, 1, 1], 'a[2, 1, 1]')
|
52
57
|
|
53
58
|
assert_equal(false, a[1, 1, :else=>false], 'a[1, 1, :else=>false]')
|
54
|
-
assert_equal(nil, a[
|
55
|
-
assert_raises(IndexError, 'a[1, 1, {}]')
|
59
|
+
assert_equal(nil, a[1, 1, {}], 'a[1, 1, {}]')
|
60
|
+
assert_raises(IndexError, 'a[1, 1, {:raise=>true}]') do
|
61
|
+
a[1, 1, {:raise=>true}]
|
62
|
+
end
|
56
63
|
end
|
57
64
|
|
58
65
|
end
|
data/test/02hash.rb
CHANGED
@@ -23,8 +23,11 @@ class TestXK < MiniTest::Unit::TestCase
|
|
23
23
|
|
24
24
|
assert_respond_to(a, :[]=)
|
25
25
|
|
26
|
-
a[0] =
|
27
|
-
assert_equal([
|
26
|
+
a[0] = ?0
|
27
|
+
assert_equal([?0], a, "a[0] = ?0")
|
28
|
+
|
29
|
+
a[:[]] = ?1
|
30
|
+
assert_equal([?0, ?1], a, "a[:[]] = ?1")
|
28
31
|
|
29
32
|
a.clear; a[0, :a] = '0:a'
|
30
33
|
assert_equal([ { :a => '0:a' } ], a, "a[0, :a] = '0:a'")
|
data/test/03auto.rb
CHANGED
data/xkeys.gemspec
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = "xkeys"
|
3
|
-
s.version = "
|
4
|
-
s.date = "2013-07-
|
3
|
+
s.version = "1.0.1"
|
4
|
+
s.date = "2013-07-25"
|
5
5
|
s.authors = ["Brian Katzung"]
|
6
6
|
s.email = ["briank@kappacs.com"]
|
7
7
|
s.homepage = "http://rubygems.org/gems/xkeys"
|
@@ -9,7 +9,8 @@ Gem::Specification.new do |s|
|
|
9
9
|
s.description = "Extended keys to facilitate fetching and storing in nested hash and array structures with Perl-ish auto-vivification."
|
10
10
|
s.license = "MIT"
|
11
11
|
|
12
|
-
s.files = Dir.glob("lib/**/*") +
|
12
|
+
s.files = Dir.glob("lib/**/*") +
|
13
|
+
%w{xkeys.gemspec .yardopts HISTORY.txt}
|
13
14
|
s.test_files = Dir.glob("test/**/*.rb")
|
14
15
|
s.require_path = 'lib'
|
15
16
|
end
|
metadata
CHANGED
@@ -3,10 +3,10 @@ name: xkeys
|
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
|
+
- 1
|
6
7
|
- 0
|
7
|
-
-
|
8
|
-
|
9
|
-
version: 0.0.2
|
8
|
+
- 1
|
9
|
+
version: 1.0.1
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Brian Katzung
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2013-07-
|
17
|
+
date: 2013-07-25 00:00:00 -05:00
|
18
18
|
default_executable:
|
19
19
|
dependencies: []
|
20
20
|
|
@@ -30,6 +30,7 @@ extra_rdoc_files: []
|
|
30
30
|
files:
|
31
31
|
- lib/xkeys.rb
|
32
32
|
- xkeys.gemspec
|
33
|
+
- .yardopts
|
33
34
|
- HISTORY.txt
|
34
35
|
- test/03auto.rb
|
35
36
|
- test/01get.rb
|