Permalink
Browse files

Use ordered hashes internally when available

  • Loading branch information...
1 parent db5ae90 commit 47b6e64e85959ae380b91a2a3d2ab29cb3e0a8e8 @mbklein mbklein committed Nov 10, 2011
Showing with 46 additions and 10 deletions.
  1. +1 −0 .gitignore
  2. +12 −1 README.md
  3. +28 −9 lib/confstruct/hash_with_struct_access.rb
  4. +5 −0 spec/confstruct/hash_with_struct_access_spec.rb
View
1 .gitignore
@@ -4,3 +4,4 @@ Gemfile.lock
pkg/*
.yardoc
coverage
+rdoc
View
13 README.md
@@ -119,7 +119,18 @@ define read-only, dynamic configuration attributes
=> {:project=>"confstruct", :github=>{:branch=>"master", :url=>"http://www.github.com/mbklein/confstruct"}}
config.github.url
=> "http://www.github.com/mbklein/confstruct"
-
+
+### Notes
+
+* Confstruct will attempt to use ordered hashes internally when available.
+ * In Ruby 1.9 and above, this is automatic.
+ * In Rubies earlier than 1.9, Confstruct will try to require and use ActiveSupport::OrderedHash,
+ falling back to a regular Hash if necessary. The class/instance method `ordered?` can be used
+ to determine if the hash ordered or not.
+* In order to support struct access, all hash keys are converted to symbols, and are accessible
+ both as strings and symbols (like a `HashWithIndifferentAccess`). In other words, config['foo']
+ and config[:foo] refer to the same value.
+
## Release History
## Contributing to confstruct
View
37 lib/confstruct/hash_with_struct_access.rb
@@ -2,16 +2,30 @@
require 'confstruct/utils'
module Confstruct
- class HashWithStructAccess < DelegateClass(Hash)
-
+ if ::RUBY_VERSION < '1.9'
+ begin
+ require 'active_support/ordered_hash'
+ class HashWithStructAccess < DelegateClass(ActiveSupport::OrderedHash); @@ordered = true; @@hash_class = ActiveSupport::OrderedHash; end
+ rescue LoadError, NameError
+ class HashWithStructAccess < DelegateClass(Hash); @@ordered = false; @@hash_class = Hash; end
+ end
+ else
+ class HashWithStructAccess < DelegateClass(Hash); @@ordered = true; @@hash_class = Hash; end
+ end
+
+ class HashWithStructAccess
class << self
def from_hash hash
symbolized_hash = symbolize_hash hash
self.new(symbolized_hash)
end
+ def ordered?
+ @@ordered
+ end
+
def symbolize_hash hash
- hash.inject({}) do |h,(k,v)|
+ hash.inject(@@hash_class.new) do |h,(k,v)|
h[symbolize k] = v.is_a?(Hash) ? symbolize_hash(v) : v
h
end
@@ -22,7 +36,7 @@ def symbolize key
end
end
- def initialize hash = {}
+ def initialize hash = @@hash_class.new
super(hash)
end
@@ -36,15 +50,16 @@ def [] key
def []= key,value
k = symbolize(key)
- if value.is_a?(Hash) and self[k].is_a?(Hash)
- self[k].replace(value)
+ v = (value.is_a?(Hash) and not value.is_a?(HashWithStructAccess)) ? HashWithStructAccess.new(value) : value
+ if v.is_a?(Hash) and self[k].is_a?(Hash)
+ self[k].replace(v)
else
- result = super(k, value)
+ super(k, v)
end
end
def deep_copy
- result = self.class.new({})
+ result = self.class.new(@@hash_class.new)
self.each_pair do |k,v|
if v.respond_to?(:deep_copy)
result[k] = v.deep_copy
@@ -93,7 +108,7 @@ def method_missing sym, *args, &block
else
result = self[name]
if result.nil? and block_given?
- result = self[name] = HashWithStructAccess.new({})
+ result = self[name] = HashWithStructAccess.new(@@hash_class.new)
end
if result.is_a?(HashWithStructAccess) and block_given?
@@ -116,6 +131,10 @@ def methods
super + key_methods.compact.flatten
end
+ def ordered?
+ self.class.ordered?
+ end
+
def respond_to? arg
super(arg) || keys.include?(symbolize(arg.to_s.sub(/=$/,'')))
end
View
5 spec/confstruct/hash_with_struct_access_spec.rb
@@ -10,6 +10,11 @@
hwsa.should == {}
end
+ it "should respond to #ordered?" do
+ hwsa = Confstruct::HashWithStructAccess.new
+ [true,false].should include(hwsa.ordered?)
+ end
+
context "data manipulation" do
before :all do
@hash = {

0 comments on commit 47b6e64

Please sign in to comment.