map 4.2.0 → 4.3.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.
data/LICENSE ADDED
@@ -0,0 +1 @@
1
+ same as ruby's
data/README CHANGED
@@ -139,3 +139,7 @@ DESCRIPTION
139
139
 
140
140
  USAGE
141
141
  see lib/map.rb and test/map_test.rb
142
+
143
+ HISTORY
144
+ 4.3.0:
145
+ - support for dot keys. map.set('a.b.c' => 42) #=> {'a'=>{'b'=>{'c'=>42}}}
data/Rakefile CHANGED
@@ -3,6 +3,9 @@ This.author = "Ara T. Howard"
3
3
  This.email = "ara.t.howard@gmail.com"
4
4
  This.homepage = "https://github.com/ahoward/#{ This.lib }"
5
5
 
6
+ task :license do
7
+ open('LICENSE', 'w'){|fd| fd.puts "same as ruby's"}
8
+ end
6
9
 
7
10
  task :default do
8
11
  puts((Rake::Task.tasks.map{|task| task.name.gsub(/::/,':')} - ['default']).sort)
@@ -26,11 +29,10 @@ def run_tests!(which = nil)
26
29
 
27
30
  div = ('=' * 119)
28
31
  line = ('-' * 119)
29
- helper = "-r ./test/helper.rb" if test(?e, "./test/helper.rb")
30
32
 
31
33
  test_rbs.each_with_index do |test_rb, index|
32
34
  testno = index + 1
33
- command = "#{ This.ruby } -I ./lib -I ./test/lib #{ helper } #{ test_rb }"
35
+ command = "#{ This.ruby } -I ./lib -I ./test/lib #{ test_rb }"
34
36
 
35
37
  puts
36
38
  say(div, :color => :cyan, :bold => true)
data/a.rb ADDED
@@ -0,0 +1,7 @@
1
+ require 'map'
2
+
3
+ m = Map.new
4
+
5
+ m.default = []
6
+
7
+ m.get(:a, :b)
data/lib/map.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  class Map < Hash
2
- Version = '4.2.0' unless defined?(Version)
2
+ Version = '4.3.0' unless defined?(Version)
3
3
  Load = Kernel.method(:load) unless defined?(Load)
4
4
 
5
5
  class << Map
@@ -184,6 +184,8 @@ class Map < Hash
184
184
  when 1
185
185
  first = args.first
186
186
  case first
187
+ when nil, false
188
+ nil
187
189
  when Hash
188
190
  initialize_from_hash(first)
189
191
  when Array
@@ -227,9 +229,16 @@ class Map < Hash
227
229
  klass.map_for(hash)
228
230
  end
229
231
 
232
+ =begin
230
233
  def self.convert_key(key)
231
234
  key.kind_of?(Symbol) ? key.to_s : key
232
235
  end
236
+ =end
237
+
238
+ def self.convert_key(key)
239
+ key = key.kind_of?(Symbol) ? key.to_s : key
240
+ end
241
+
233
242
  def convert_key(key)
234
243
  if klass.respond_to?(:convert_key)
235
244
  klass.convert_key(key)
@@ -312,11 +321,13 @@ class Map < Hash
312
321
  alias_method 'store', '[]='
313
322
 
314
323
  def [](key)
315
- __get__(convert_key(key))
324
+ key = convert_key(key)
325
+ __get__(key)
316
326
  end
317
327
 
318
328
  def fetch(key, *args, &block)
319
- super(convert_key(key), *args, &block)
329
+ key = convert_key(key)
330
+ super(key, *args, &block)
320
331
  end
321
332
 
322
333
  def key?(key)
@@ -553,19 +564,11 @@ class Map < Hash
553
564
  hash
554
565
  end
555
566
 
556
- def as_hash
557
- @class = Hash
558
- yield
559
- ensure
560
- @class = nil
561
- end
562
-
563
- def class
564
- @class || super
565
- end
566
-
567
- def to_yaml(*args, &block)
568
- as_hash{ super }
567
+ def to_yaml( opts = {} )
568
+ map = self
569
+ YAML.quick_emit(self.object_id, opts){|out|
570
+ out.map('!omap'){|m| map.each{|k,v| m.add(k, v)}}
571
+ }
569
572
  end
570
573
 
571
574
  def to_array
@@ -625,8 +628,14 @@ class Map < Hash
625
628
  # support for compound key indexing and depth first iteration
626
629
  #
627
630
  def get(*keys)
628
- keys = keys.flatten
629
- return self[keys.first] if keys.size <= 1
631
+ keys = key_for(keys)
632
+ if keys.size <= 1
633
+ if !self.has_key?(keys.first) && block_given?
634
+ return yield
635
+ else
636
+ return self[keys.first]
637
+ end
638
+ end
630
639
  keys, key = keys[0..-2], keys[-1]
631
640
  collection = self
632
641
  keys.each do |k|
@@ -634,11 +643,17 @@ class Map < Hash
634
643
  collection = collection[k]
635
644
  return collection unless collection.respond_to?('[]')
636
645
  end
637
- collection[alphanumeric_key_for(key)]
646
+ alphanumeric_key = alphanumeric_key_for(key)
647
+
648
+ if !collection_has_key?(collection, alphanumeric_key) && block_given?
649
+ yield
650
+ else
651
+ collection[alphanumeric_key]
652
+ end
638
653
  end
639
654
 
640
655
  def has?(*keys)
641
- keys = keys.flatten
656
+ keys = key_for(keys)
642
657
  collection = self
643
658
  return collection_has_key?(collection, keys.first) if keys.size <= 1
644
659
  keys, key = keys[0..-2], keys[-1]
@@ -695,6 +710,9 @@ class Map < Hash
695
710
  keys = Array(keys).flatten
696
711
 
697
712
  collection = self
713
+
714
+ keys = key_for(keys)
715
+
698
716
  if keys.size <= 1
699
717
  key = keys.first
700
718
  collection[key] = value
@@ -789,6 +807,51 @@ class Map < Hash
789
807
  Map.alphanumeric_key_for(key)
790
808
  end
791
809
 
810
+ ## key path support
811
+ #
812
+ def self.dot_key_for(*keys)
813
+ dot = keys.compact.flatten.join('.')
814
+ dot.split(%r/\s*[,.:_-]\s*/).map{|part| part =~ %r/^\d+$/ ? Integer(part) : part}
815
+ end
816
+
817
+ def self.dot_keys
818
+ @@dot_keys = {} unless defined?(@@dot_keys)
819
+ @@dot_keys
820
+ end
821
+
822
+ def self.dot_keys?
823
+ ancestors.each do |ancestor|
824
+ return dot_keys[ancestor] if dot_keys.has_key?(ancestor)
825
+ end
826
+ false
827
+ end
828
+
829
+ def dot_keys?
830
+ @dot_keys = false unless defined?(@dot_keys)
831
+ @dot_keys
832
+ end
833
+
834
+ def self.dot_keys!(boolean = true)
835
+ dot_keys[self] = !!boolean
836
+ end
837
+
838
+ def dot_keys!(boolean = true)
839
+ @dot_keys = !!boolean
840
+ end
841
+
842
+ def self.key_for(*keys)
843
+ return keys.flatten unless dot_keys?
844
+ self.dot_key_for(*keys)
845
+ end
846
+
847
+ def key_for(*keys)
848
+ if dot_keys?
849
+ self.class.dot_key_for(*keys)
850
+ else
851
+ self.class.key_for(*keys)
852
+ end
853
+ end
854
+
792
855
  ## TODO - technically this returns only leaves so the name isn't *quite* right. re-factor for 3.0
793
856
  #
794
857
  def Map.depth_first_each(enumerable, path = [], accum = [], &block)
@@ -3,15 +3,17 @@
3
3
 
4
4
  Gem::Specification::new do |spec|
5
5
  spec.name = "map"
6
- spec.version = "4.2.0"
6
+ spec.version = "4.3.0"
7
7
  spec.platform = Gem::Platform::RUBY
8
8
  spec.summary = "map"
9
9
  spec.description = "description: map kicks the ass"
10
10
 
11
11
  spec.files =
12
- ["README",
12
+ ["LICENSE",
13
+ "README",
13
14
  "Rakefile",
14
15
  "TODO",
16
+ "a.rb",
15
17
  "lib",
16
18
  "lib/map",
17
19
  "lib/map.rb",
@@ -6,7 +6,7 @@
6
6
  Class.new(Test::Unit::TestCase) do
7
7
  eval("This=self")
8
8
 
9
- def This.slug_for(*args)
9
+ def self.slug_for(*args)
10
10
  string = args.flatten.compact.join('-')
11
11
  words = string.to_s.scan(%r/\w+/)
12
12
  words.map!{|word| word.gsub %r/[^0-9a-zA-Z_-]/, ''}
@@ -24,6 +24,11 @@ Testing Map do
24
24
  assert{ Map.new(*array) }
25
25
  end
26
26
 
27
+ testing 'that the constructor does not die when passed nil or false' do
28
+ assert{ Map.new(nil) }
29
+ assert{ Map.new(false) }
30
+ end
31
+
27
32
  testing 'that the contructor accepts an even sized array' do
28
33
  arrays = [
29
34
  [ %w( k v ), %w( key val ) ],
@@ -327,6 +332,35 @@ Testing Map do
327
332
  assert{ m[:x][:y][:z] == 42.0 }
328
333
  end
329
334
 
335
+ testing 'that maps can support compound key/val setting via key.dot notation' do
336
+ m = Class.new(Map){ dot_keys! }.new
337
+ assert{ m.set('a.b.c', 42) }
338
+ assert{ m[:a][:b][:c] == 42 }
339
+ assert{ m.get('a.b.c') == 42 }
340
+ assert{ m.set('x.y.z' => 42.0, 'A.2' => 'forty-two') }
341
+ assert{ m[:A].is_a?(Array) }
342
+ assert{ m[:A].size == 3}
343
+ assert{ m[:A][2] == 'forty-two' }
344
+ assert{ m[:x][:y].is_a?(Hash) }
345
+ assert{ m[:x][:y][:z] == 42.0 }
346
+ assert{ m.has?('a.b.c') }
347
+ assert{ m.has?('x.y.z') }
348
+ assert{ m.has?('A.2') }
349
+ end
350
+
351
+ testing 'that Map#get supports providing a default value in a block' do
352
+ m = Map.new
353
+ m.set(:a, :b, :c, 42)
354
+ m.set(:z, 1)
355
+
356
+ assert { m.get(:x) {1} == 1 }
357
+ assert { m.get(:z) {2} == 1 }
358
+ assert { m.get(:a, :b, :d) {1} == 1 }
359
+ assert { m.get(:a, :b, :c) {1} == 42 }
360
+ assert { m.get(:a, :b) {1} == Map.new({:c => 42}) }
361
+ assert { m.get(:a, :aa) {1} == 1 }
362
+ end
363
+
330
364
  testing 'that setting a sub-container does not eff up the container values' do
331
365
  m = Map.new
332
366
  assert{ m.set(:array => [0,1,2]) }
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: map
3
3
  version: !ruby/object:Gem::Version
4
- hash: 55
4
+ hash: 51
5
5
  prerelease:
6
6
  segments:
7
7
  - 4
8
- - 2
8
+ - 3
9
9
  - 0
10
- version: 4.2.0
10
+ version: 4.3.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Ara T. Howard
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-06-08 00:00:00 -06:00
18
+ date: 2011-08-17 00:00:00 -06:00
19
19
  default_executable:
20
20
  dependencies: []
21
21
 
@@ -28,9 +28,11 @@ extensions: []
28
28
  extra_rdoc_files: []
29
29
 
30
30
  files:
31
+ - LICENSE
31
32
  - README
32
33
  - Rakefile
33
34
  - TODO
35
+ - a.rb
34
36
  - lib/map.rb
35
37
  - lib/map/options.rb
36
38
  - lib/map/struct.rb