xkeys 0.0.2 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|