xkeys 0.0.2 → 1.0.1

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