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.
@@ -0,0 +1,4 @@
1
+ --tag copyright:Copyright
2
+ --tag license:License
3
+ -
4
+ HISTORY.txt
@@ -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
 
@@ -3,21 +3,22 @@
3
3
  #
4
4
  # Synopsis:
5
5
  # root = {}.extend XKeys::Hash
6
- # root[:my, :list, nil] = 'value 1'
7
- # root[:my, :list, nil] = 'value 2'
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, {}] # => raises KeyError
13
+ # root[:missing, :raise => true] # => raises KeyError
14
14
  #
15
15
  # root = [].extend XKeys::Auto
16
- # root[1, nil] = 'value 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[nil, 1, 0] # => 'value 1'
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([nil,] key1, ..., keyN [, :else => default_value])
35
+ # xfetch(key1, ..., keyN [, option_hash])
35
36
  #
36
- # An optional leading nil key is ignored (see []). If the specified
37
- # keys do not exist, the default value is returned (if provided) or
38
- # the standard exception (e.g. KeyError or IndexError) is raised.
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
- first = (args[0] == nil) ? 1 : 0
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
- return options[:else] if options.has_key? :else
48
- raise
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
- # Prepend a nil key and/or append an option hash to force nested index
61
- # behavior for two integer array indexes: array[nil, index1, index2].
75
+ # Append an option hash to force nested index behavior for two
76
+ # integer array indexes: array[index1, index2, {}].
62
77
  #
63
- # [[nil,] key1, ..., keyN[, option_hash]] traverses a tree of nested
64
- # hashes and/or arrays using xfetch. The optional leading nil key is
65
- # always ignored. In the absence of an option hash, the default is
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 || (self.is_a?(Array) && args.count == 2 &&
69
- args[0].is_a?(Integer) && args[1].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
- elsif args[-1].is_a?(Hash) then xfetch(*args)
73
- else xfetch(*args, :else => nil)
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.count == 2
85
- if self.is_a?(Array) && args[0] == nil
86
- self << args[1] # array[nil] = value
87
- else return false # [key] = value ==> super *args
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..-2].inject([self, args[0]]) do |node, key|
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 || node[0][node[1]].size]
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 then node[key || node.size] = args[-1]
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. Nil keys create nested arrays as needed. Other
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 *args unless _xset(*args) { |key| key == nil }
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. Nil keys and integer keys
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 unless
137
- _xset(*args) { |key| key == nil || key.is_a?(Integer) }
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
 
@@ -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, {}]') { 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[nil, 1, 1], 'a[nil, 1, 1]')
55
- assert_raises(IndexError, 'a[1, 1, {}]') { 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
@@ -23,8 +23,11 @@ class TestXK < MiniTest::Unit::TestCase
23
23
 
24
24
  assert_respond_to(a, :[]=)
25
25
 
26
- a[0] = '0'
27
- assert_equal(['0'], a, "a[0] = '0'")
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'")
@@ -31,6 +31,9 @@ class TestXK < MiniTest::Unit::TestCase
31
31
 
32
32
  a.clear; a[0, 1] = '01'
33
33
  assert_equal([ [ nil, '01' ] ], a, "a[0, 1] = '01'")
34
+
35
+ a[0, :[]] = '02'
36
+ assert_equal([ [ nil, '01', '02' ] ], a, "a[0, :[]] = '02'")
34
37
  end
35
38
 
36
39
  end
@@ -1,7 +1,7 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "xkeys"
3
- s.version = "0.0.2"
4
- s.date = "2013-07-08"
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/**/*") + %w{xkeys.gemspec HISTORY.txt}
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
- - 0
8
- - 2
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-08 00:00:00 -05:00
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