Browse files

dot keys. no-hack to_yaml. release 4.3.0

  • Loading branch information...
1 parent 464e2cb commit 57f06146311418b7da9507f2a7be41e597708ae4 @ahoward committed Aug 17, 2011
Showing with 90 additions and 19 deletions.
  1. +4 −0 README
  2. +66 −17 lib/map.rb
  3. +4 −2 map.gemspec
  4. +16 −0 test/map_test.rb
View
4 README
@@ -139,3 +139,7 @@ DESCRIPTION
USAGE
see lib/map.rb and test/map_test.rb
+
+HISTORY
+ 4.3.0:
+ - support for dot keys. map.set('a.b.c' => 42) #=> {'a'=>{'b'=>{'c'=>42}}}
View
83 lib/map.rb
@@ -229,9 +229,16 @@ def map_for(hash)
klass.map_for(hash)
end
+=begin
def self.convert_key(key)
key.kind_of?(Symbol) ? key.to_s : key
end
+=end
+
+ def self.convert_key(key)
+ key = key.kind_of?(Symbol) ? key.to_s : key
+ end
+
def convert_key(key)
if klass.respond_to?(:convert_key)
klass.convert_key(key)
@@ -314,11 +321,13 @@ def []=(key, val)
alias_method 'store', '[]='
def [](key)
- __get__(convert_key(key))
+ key = convert_key(key)
+ __get__(key)
end
def fetch(key, *args, &block)
- super(convert_key(key), *args, &block)
+ key = convert_key(key)
+ super(key, *args, &block)
end
def key?(key)
@@ -555,19 +564,11 @@ def to_hash
hash
end
- def as_hash
- @class = Hash
- yield
- ensure
- @class = nil
- end
-
- def class
- @class || super
- end
-
- def to_yaml(*args, &block)
- as_hash{ super }
+ def to_yaml( opts = {} )
+ map = self
+ YAML.quick_emit(self.object_id, opts){|out|
+ out.map('!omap'){|m| map.each{|k,v| m.add(k, v)}}
+ }
end
def to_array
@@ -627,7 +628,7 @@ def id
# support for compound key indexing and depth first iteration
#
def get(*keys)
- keys = keys.flatten
+ keys = key_for(keys)
return self[keys.first] if keys.size <= 1
keys, key = keys[0..-2], keys[-1]
collection = self
@@ -640,7 +641,7 @@ def get(*keys)
end
def has?(*keys)
- keys = keys.flatten
+ keys = key_for(keys)
collection = self
return collection_has_key?(collection, keys.first) if keys.size <= 1
keys, key = keys[0..-2], keys[-1]
@@ -697,6 +698,9 @@ def set(*args)
keys = Array(keys).flatten
collection = self
+
+ keys = key_for(keys)
+
if keys.size <= 1
key = keys.first
collection[key] = value
@@ -791,6 +795,51 @@ def alphanumeric_key_for(key)
Map.alphanumeric_key_for(key)
end
+## key path support
+#
+ def self.dot_key_for(*keys)
+ dot = keys.compact.flatten.join('.')
+ dot.split(%r/\s*[,.:_-]\s*/).map{|part| part =~ %r/^\d+$/ ? Integer(part) : part}
+ end
+
+ def self.dot_keys
+ @@dot_keys = {} unless defined?(@@dot_keys)
+ @@dot_keys
+ end
+
+ def self.dot_keys?
+ ancestors.each do |ancestor|
+ return dot_keys[ancestor] if dot_keys.has_key?(ancestor)
+ end
+ false
+ end
+
+ def dot_keys?
+ @dot_keys = false unless defined?(@dot_keys)
+ @dot_keys
+ end
+
+ def self.dot_keys!(boolean = true)
+ dot_keys[self] = !!boolean
+ end
+
+ def dot_keys!(boolean = true)
+ @dot_keys = !!boolean
+ end
+
+ def self.key_for(*keys)
+ return keys.flatten unless dot_keys?
+ self.dot_key_for(*keys)
+ end
+
+ def key_for(*keys)
+ if dot_keys?
+ self.class.dot_key_for(*keys)
+ else
+ self.class.key_for(*keys)
+ end
+ end
+
## TODO - technically this returns only leaves so the name isn't *quite* right. re-factor for 3.0
#
def Map.depth_first_each(enumerable, path = [], accum = [], &block)
View
6 map.gemspec
@@ -3,15 +3,17 @@
Gem::Specification::new do |spec|
spec.name = "map"
- spec.version = "4.2.0"
+ spec.version = "4.3.0"
spec.platform = Gem::Platform::RUBY
spec.summary = "map"
spec.description = "description: map kicks the ass"
spec.files =
-["README",
+["LICENSE",
+ "README",
"Rakefile",
"TODO",
+ "a.rb",
"lib",
"lib/map",
"lib/map.rb",
View
16 test/map_test.rb
@@ -332,6 +332,22 @@ def to_map() {:k => :b} end
assert{ m[:x][:y][:z] == 42.0 }
end
+ testing 'that maps can support compound key/val setting via key.dot notation' do
+ m = Class.new(Map){ dot_keys! }.new
+ assert{ m.set('a.b.c', 42) }
+ assert{ m[:a][:b][:c] == 42 }
+ assert{ m.get('a.b.c') == 42 }
+ assert{ m.set('x.y.z' => 42.0, 'A.2' => 'forty-two') }
+ assert{ m[:A].is_a?(Array) }
+ assert{ m[:A].size == 3}
+ assert{ m[:A][2] == 'forty-two' }
+ assert{ m[:x][:y].is_a?(Hash) }
+ assert{ m[:x][:y][:z] == 42.0 }
+ assert{ m.has?('a.b.c') }
+ assert{ m.has?('x.y.z') }
+ assert{ m.has?('A.2') }
+ end
+
testing 'that setting a sub-container does not eff up the container values' do
m = Map.new
assert{ m.set(:array => [0,1,2]) }

0 comments on commit 57f0614

Please sign in to comment.