angry_mob 0.1.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.
Files changed (125) hide show
  1. data/LICENSE +21 -0
  2. data/README.md +123 -0
  3. data/bin/mob +139 -0
  4. data/lib/angry_mob.rb +28 -0
  5. data/lib/angry_mob/act.rb +111 -0
  6. data/lib/angry_mob/act/scheduler.rb +143 -0
  7. data/lib/angry_mob/action.rb +11 -0
  8. data/lib/angry_mob/builder.rb +115 -0
  9. data/lib/angry_mob/extend.rb +10 -0
  10. data/lib/angry_mob/extend/array.rb +30 -0
  11. data/lib/angry_mob/extend/blank.rb +108 -0
  12. data/lib/angry_mob/extend/blankslate.rb +109 -0
  13. data/lib/angry_mob/extend/dictionary.rb +140 -0
  14. data/lib/angry_mob/extend/hash.rb +67 -0
  15. data/lib/angry_mob/extend/object.rb +21 -0
  16. data/lib/angry_mob/extend/pathname.rb +23 -0
  17. data/lib/angry_mob/extend/string.rb +8 -0
  18. data/lib/angry_mob/log.rb +28 -0
  19. data/lib/angry_mob/mob.rb +77 -0
  20. data/lib/angry_mob/mob_loader.rb +115 -0
  21. data/lib/angry_mob/node.rb +44 -0
  22. data/lib/angry_mob/notifier.rb +76 -0
  23. data/lib/angry_mob/target.rb +257 -0
  24. data/lib/angry_mob/target/arguments.rb +71 -0
  25. data/lib/angry_mob/target/call.rb +57 -0
  26. data/lib/angry_mob/target/default_resource_locator.rb +11 -0
  27. data/lib/angry_mob/target/defaults.rb +23 -0
  28. data/lib/angry_mob/target/mother.rb +66 -0
  29. data/lib/angry_mob/target/notify.rb +57 -0
  30. data/lib/angry_mob/target/tracking.rb +96 -0
  31. data/lib/angry_mob/ui.rb +247 -0
  32. data/lib/angry_mob/util.rb +11 -0
  33. data/lib/angry_mob/vendored.rb +8 -0
  34. data/lib/angry_mob/version.rb +3 -0
  35. data/vendor/angry_hash/Rakefile +17 -0
  36. data/vendor/angry_hash/VERSION +1 -0
  37. data/vendor/angry_hash/angry_hash.gemspec +47 -0
  38. data/vendor/angry_hash/examples/accessors_eg.rb +46 -0
  39. data/vendor/angry_hash/examples/creation_eg.rb +43 -0
  40. data/vendor/angry_hash/examples/dsl.eg.rb +18 -0
  41. data/vendor/angry_hash/examples/dup_eg.rb +86 -0
  42. data/vendor/angry_hash/examples/eg_helper.rb +24 -0
  43. data/vendor/angry_hash/examples/merge_eg.rb +135 -0
  44. data/vendor/angry_hash/lib/angry_hash.rb +215 -0
  45. data/vendor/angry_hash/lib/angry_hash/dsl.rb +44 -0
  46. data/vendor/angry_hash/lib/angry_hash/extension_tracking.rb +12 -0
  47. data/vendor/angry_hash/lib/angry_hash/merge_string.rb +58 -0
  48. data/vendor/json/COPYING +58 -0
  49. data/vendor/json/GPL +340 -0
  50. data/vendor/json/README +360 -0
  51. data/vendor/json/lib/json/common.rb +371 -0
  52. data/vendor/json/lib/json/pure.rb +77 -0
  53. data/vendor/json/lib/json/pure/generator.rb +443 -0
  54. data/vendor/json/lib/json/pure/parser.rb +303 -0
  55. data/vendor/json/lib/json/version.rb +8 -0
  56. data/vendor/thor/CHANGELOG.rdoc +89 -0
  57. data/vendor/thor/LICENSE +20 -0
  58. data/vendor/thor/README.rdoc +297 -0
  59. data/vendor/thor/Thorfile +69 -0
  60. data/vendor/thor/bin/rake2thor +86 -0
  61. data/vendor/thor/bin/thor +6 -0
  62. data/vendor/thor/lib/thor.rb +244 -0
  63. data/vendor/thor/lib/thor/actions.rb +275 -0
  64. data/vendor/thor/lib/thor/actions/create_file.rb +103 -0
  65. data/vendor/thor/lib/thor/actions/directory.rb +91 -0
  66. data/vendor/thor/lib/thor/actions/empty_directory.rb +134 -0
  67. data/vendor/thor/lib/thor/actions/file_manipulation.rb +223 -0
  68. data/vendor/thor/lib/thor/actions/inject_into_file.rb +104 -0
  69. data/vendor/thor/lib/thor/base.rb +540 -0
  70. data/vendor/thor/lib/thor/core_ext/file_binary_read.rb +9 -0
  71. data/vendor/thor/lib/thor/core_ext/hash_with_indifferent_access.rb +75 -0
  72. data/vendor/thor/lib/thor/core_ext/ordered_hash.rb +100 -0
  73. data/vendor/thor/lib/thor/error.rb +30 -0
  74. data/vendor/thor/lib/thor/group.rb +271 -0
  75. data/vendor/thor/lib/thor/invocation.rb +180 -0
  76. data/vendor/thor/lib/thor/parser.rb +4 -0
  77. data/vendor/thor/lib/thor/parser/argument.rb +67 -0
  78. data/vendor/thor/lib/thor/parser/arguments.rb +150 -0
  79. data/vendor/thor/lib/thor/parser/option.rb +128 -0
  80. data/vendor/thor/lib/thor/parser/options.rb +169 -0
  81. data/vendor/thor/lib/thor/rake_compat.rb +66 -0
  82. data/vendor/thor/lib/thor/runner.rb +314 -0
  83. data/vendor/thor/lib/thor/shell.rb +83 -0
  84. data/vendor/thor/lib/thor/shell/basic.rb +239 -0
  85. data/vendor/thor/lib/thor/shell/color.rb +108 -0
  86. data/vendor/thor/lib/thor/task.rb +102 -0
  87. data/vendor/thor/lib/thor/util.rb +224 -0
  88. data/vendor/thor/lib/thor/version.rb +3 -0
  89. data/vendor/thor/spec/actions/create_file_spec.rb +170 -0
  90. data/vendor/thor/spec/actions/directory_spec.rb +131 -0
  91. data/vendor/thor/spec/actions/empty_directory_spec.rb +91 -0
  92. data/vendor/thor/spec/actions/file_manipulation_spec.rb +271 -0
  93. data/vendor/thor/spec/actions/inject_into_file_spec.rb +135 -0
  94. data/vendor/thor/spec/actions_spec.rb +292 -0
  95. data/vendor/thor/spec/base_spec.rb +263 -0
  96. data/vendor/thor/spec/core_ext/hash_with_indifferent_access_spec.rb +43 -0
  97. data/vendor/thor/spec/core_ext/ordered_hash_spec.rb +115 -0
  98. data/vendor/thor/spec/fixtures/application.rb +2 -0
  99. data/vendor/thor/spec/fixtures/bundle/execute.rb +6 -0
  100. data/vendor/thor/spec/fixtures/bundle/main.thor +1 -0
  101. data/vendor/thor/spec/fixtures/doc/%file_name%.rb.tt +1 -0
  102. data/vendor/thor/spec/fixtures/doc/README +3 -0
  103. data/vendor/thor/spec/fixtures/doc/config.rb +1 -0
  104. data/vendor/thor/spec/fixtures/group.thor +90 -0
  105. data/vendor/thor/spec/fixtures/invoke.thor +112 -0
  106. data/vendor/thor/spec/fixtures/script.thor +145 -0
  107. data/vendor/thor/spec/fixtures/task.thor +10 -0
  108. data/vendor/thor/spec/group_spec.rb +171 -0
  109. data/vendor/thor/spec/invocation_spec.rb +107 -0
  110. data/vendor/thor/spec/parser/argument_spec.rb +47 -0
  111. data/vendor/thor/spec/parser/arguments_spec.rb +64 -0
  112. data/vendor/thor/spec/parser/option_spec.rb +202 -0
  113. data/vendor/thor/spec/parser/options_spec.rb +292 -0
  114. data/vendor/thor/spec/rake_compat_spec.rb +68 -0
  115. data/vendor/thor/spec/runner_spec.rb +210 -0
  116. data/vendor/thor/spec/shell/basic_spec.rb +205 -0
  117. data/vendor/thor/spec/shell/color_spec.rb +41 -0
  118. data/vendor/thor/spec/shell_spec.rb +34 -0
  119. data/vendor/thor/spec/spec.opts +1 -0
  120. data/vendor/thor/spec/spec_helper.rb +54 -0
  121. data/vendor/thor/spec/task_spec.rb +69 -0
  122. data/vendor/thor/spec/thor_spec.rb +237 -0
  123. data/vendor/thor/spec/util_spec.rb +163 -0
  124. data/vendor/thor/thor.gemspec +120 -0
  125. metadata +199 -0
@@ -0,0 +1,47 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{angry_hash}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Lachie Cox"]
12
+ s.date = %q{2010-08-07}
13
+ s.description = %q{A stabler mash with different emphases. Used in plus2 projects AngryMob and Igor.}
14
+ s.email = %q{lachie@plus2.com.au}
15
+ s.files = [
16
+ ".gitignore",
17
+ "Rakefile",
18
+ "VERSION",
19
+ "angry_hash.gemspec",
20
+ "examples/accessors_eg.rb",
21
+ "examples/creation_eg.rb",
22
+ "examples/dsl.eg.rb",
23
+ "examples/dup_eg.rb",
24
+ "examples/eg_helper.rb",
25
+ "examples/merge_eg.rb",
26
+ "lib/angry_hash.rb",
27
+ "lib/angry_hash/dsl.rb",
28
+ "lib/angry_hash/extension_tracking.rb",
29
+ "lib/angry_hash/merge_string.rb"
30
+ ]
31
+ s.homepage = %q{http://github.com/plus2/angry_hash}
32
+ s.rdoc_options = ["--charset=UTF-8"]
33
+ s.require_paths = ["lib"]
34
+ s.rubygems_version = %q{1.3.6}
35
+ s.summary = %q{A stabler mash with different emphases.}
36
+
37
+ if s.respond_to? :specification_version then
38
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
39
+ s.specification_version = 3
40
+
41
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
42
+ else
43
+ end
44
+ else
45
+ end
46
+ end
47
+
@@ -0,0 +1,46 @@
1
+ require 'eg_helper'
2
+
3
+ eg.setup do
4
+ @a = AngryHash[
5
+ :b => {
6
+ :c => 'c',
7
+ :d => 'd'
8
+ }
9
+ ]
10
+ end
11
+
12
+ def same_obj(a,b)
13
+ a.__id__ == b.__id__
14
+ end
15
+
16
+ eg 'accessor! with existing subhash' do
17
+ Assert( same_obj( @a.b!, @a.b ) )
18
+ end
19
+
20
+ eg 'accessor! creating and returning' do
21
+ d = @a.d!
22
+ Assert( same_obj( d, @a.d ) )
23
+ end
24
+
25
+ eg 'accessor= AngryHash' do
26
+ d = AngryHash.new
27
+ @a.d = d
28
+ Assert( same_obj( d, @a.d ) )
29
+ end
30
+
31
+ eg 'accessor= Hash' do
32
+ d = {}
33
+ @a.d = d
34
+ Assert( ! same_obj( d, @a.d ) )
35
+ end
36
+
37
+ eg 'accessor?' do
38
+ Assert( @a.b? )
39
+ Assert( ! @a.c? )
40
+ Assert( @a.b.c? )
41
+ Assert( ! @a.b.x? )
42
+
43
+ @a.b.f = false
44
+
45
+ Assert( ! @a.b.f? )
46
+ end
@@ -0,0 +1,43 @@
1
+ require 'eg_helper'
2
+
3
+ eg.helpers do
4
+ def oids(v)
5
+ sub = case v
6
+ when Hash
7
+ hh = {}
8
+ v.each {|kk,vv| hh[kk] = oids(vv)}
9
+ hh
10
+ when Array
11
+ v.map {|vv| oids(vv)}
12
+ else
13
+ v
14
+ end
15
+
16
+ n = {:class => v.class, :oid => v.__id__, :sub => sub}
17
+ if v.respond_to?(:__id__)
18
+ n[:oid] = v.__id__
19
+ else
20
+ n[:oid] = 0
21
+ end
22
+ n
23
+ end
24
+ end
25
+
26
+ eg 'creation duplicates' do
27
+ a1 = AngryHash[
28
+ :a => { :b => 1 },
29
+ :c => [ 1, :d, {:e => :f}, nil, true, false, 1.0, [ 'x', 'y' ] ]
30
+ ]
31
+ a2 = AngryHash[ a1 ]
32
+
33
+ pp oids(a1)
34
+ pp oids(a2)
35
+
36
+ Assert(a1.a.__id__ != a2.a.__id__)
37
+ Assert(a1.c[2].__id__ != a2.c[2].__id__)
38
+ Assert(a1.c[7].__id__ != a2.c[7].__id__)
39
+ end
40
+
41
+
42
+ #eg 'cycle detection' do
43
+ #end
@@ -0,0 +1,18 @@
1
+ require 'eg_helper'
2
+
3
+ eg 'eval a block as dsl' do
4
+ original = AngryHash[ :a => 'x', :b => {:c => 1}, :d => 'z' ]
5
+
6
+ original.__eval_as_dsl do
7
+ foo 'bar'
8
+ a 'y'
9
+ b :e => 2
10
+ end
11
+
12
+ Assert(original.foo == 'bar')
13
+ Assert(original.a == 'y')
14
+ Assert(original.d == 'z')
15
+
16
+ Assert(original.b.c == 1)
17
+ Assert(original.b.e == 2)
18
+ end
@@ -0,0 +1,86 @@
1
+ require 'eg_helper'
2
+
3
+ require 'angry_hash/extension_tracking'
4
+ AngryHash.send :include, AngryHash::ExtensionTracking
5
+
6
+ eg.setup do
7
+ @original = { 'database' => {
8
+ "micropower_micropower_sandbox" => {"app_owner" => "micropower_micropower_sandbox", "server" => :db},
9
+ "post_froggy" => {"app_owner" => "froggy_owner", "backup" => {"to_s3" => true}, "server" => :postfrog},
10
+ "myfrogdb" => {"app_owner" => :myfroggy, "server" => :myfrog},
11
+ "eggs_hwag" => {"app_owner" => "eggs_hwag", "backup" => {"to_s3" => true}, "server" => :db},
12
+ "isg_sandbox_v5" => {"app_owner" => "isg_sandbox_v5", "server" => :isg_forums_mysql},
13
+ }}
14
+ end
15
+
16
+ eg 'duping copies symbols' do
17
+ ah = AngryHash[ @original ]
18
+ ah2 = AngryHash[ ah ]
19
+
20
+ Assert( ! ah2.database.isg_sandbox_v5.server.nil? )
21
+ Assert( ah2.database.isg_sandbox_v5.server == @original['database']['isg_sandbox_v5']['server'] )
22
+ end
23
+
24
+ def same_obj(a,b)
25
+ a.__id__ == b.__id__
26
+ end
27
+
28
+ def diff_obj(a,b)
29
+ ! same_obj(a,b)
30
+ end
31
+
32
+ eg 'dup is deep' do
33
+ ah = AngryHash[ @original ]
34
+ ah2 = ah.dup
35
+
36
+ Assert( diff_obj ah , ah2 )
37
+ Assert( diff_obj ah.database , ah2.database )
38
+ Assert( diff_obj ah.database.post_froggy , ah2.database.post_froggy )
39
+ Assert( diff_obj ah.database.post_froggy.backup, ah2.database.post_froggy.backup )
40
+
41
+ end
42
+
43
+ module Extendo
44
+ def as_dag
45
+ dag = dup
46
+ dag
47
+ end
48
+
49
+ def is_extended?
50
+ true
51
+ end
52
+ end
53
+
54
+ eg 'duping from ext' do
55
+ ah = AngryHash[ @original ]
56
+ ah.extend(Extendo)
57
+
58
+ ah2 = ah.as_dag
59
+
60
+ Show( ah.__id__ )
61
+ Show( ah2.__id__ )
62
+
63
+ Show( ah2 )
64
+ end
65
+
66
+ module ExtendoDb
67
+ def is_db_extended?
68
+ true
69
+ end
70
+ end
71
+
72
+ eg 'extension preservation' do
73
+ ah = AngryHash[ @original ]
74
+ Assert( ! ah.is_extended? )
75
+ Assert( ! ah.database.post_froggy.is_db_extended? )
76
+
77
+ ah.extend(Extendo)
78
+ ah.database.post_froggy.extend(ExtendoDb)
79
+
80
+ Assert( ah.is_extended? )
81
+ Assert( ah.database.post_froggy.is_db_extended? )
82
+
83
+ ah2 = ah.dup
84
+ Show( ah2.is_extended? )
85
+ Show( ah2.database.post_froggy.is_db_extended? )
86
+ end
@@ -0,0 +1,24 @@
1
+ require 'pathname'
2
+ root = Pathname('../..').expand_path(__FILE__)
3
+ require 'pp'
4
+
5
+ UseGem = false
6
+
7
+ if UseGem
8
+ require 'rubygems'
9
+ require 'angry_hash'
10
+ else
11
+ $:.unshift root+'lib'
12
+ require root+'lib/angry_hash'
13
+ end
14
+
15
+ require 'rubygems'
16
+ require 'exemplor'
17
+
18
+ class Object
19
+ def tapp(tag=nil)
20
+ print "#{tag}=" if tag
21
+ pp self
22
+ self
23
+ end
24
+ end
@@ -0,0 +1,135 @@
1
+ require 'eg_helper'
2
+ require 'yajl'
3
+
4
+ eg.setup do
5
+ @a = AngryHash[
6
+ :b => {
7
+ :c => 'c',
8
+ :d => 'd'
9
+ }
10
+ ]
11
+
12
+ @b = AngryHash[
13
+ :b => {
14
+ :d => 'dd',
15
+ :e => 'e'
16
+ }
17
+ ]
18
+ end
19
+
20
+ def same_obj(a,b)
21
+ a.__id__ == b.__id__
22
+ end
23
+
24
+ eg 'merges' do
25
+ merged = @a.merge(@b)
26
+
27
+ Assert( ! same_obj( merged, @a ) )
28
+ Assert( ! same_obj( merged, @b ) )
29
+
30
+ Assert( ! merged.b.c? )
31
+ Assert( merged.b.d == 'dd' )
32
+ Assert( merged.b.e == 'e' )
33
+ end
34
+
35
+ eg 'merge dups' do
36
+ a = AngryHash[
37
+ :a => {:b => "cows"}
38
+ ]
39
+ b = AngryHash[
40
+ :a => {:c => "bows"}
41
+ ]
42
+
43
+ merged = a.merge(b)
44
+
45
+ Assert( ! same_obj( merged.a.b, a.a.b ) )
46
+ Assert( same_obj( merged.a.c, b.a.c ) )
47
+ end
48
+
49
+ eg 'updates' do
50
+ merged = @a.update(@b)
51
+
52
+ Assert( same_obj( merged, @a ) )
53
+ Assert( ! same_obj( merged, @b ) )
54
+
55
+ Assert( ! merged.b.c? )
56
+ Assert( merged.b.d == 'dd' )
57
+ Assert( merged.b.e == 'e' )
58
+ end
59
+
60
+ eg 'deep_merge' do
61
+ merged = @a.deep_merge(@b)
62
+
63
+ Show(merged)
64
+
65
+ Assert( ! same_obj( merged, @a ) )
66
+ Assert( ! same_obj( merged, @b ) )
67
+
68
+ Assert( merged.b.c == 'c')
69
+ Assert( merged.b.d == 'dd')
70
+ Assert( merged.b.e == 'e')
71
+ end
72
+
73
+ eg 'deep_update' do
74
+ rv = @a.deep_update(@b)
75
+
76
+ Show(@a)
77
+ Assert( same_obj( rv, @a ) )
78
+ Assert( ! same_obj( @a, @b ) )
79
+
80
+ Assert( ! rv.nil? )
81
+ Assert( @a.b.c == 'c')
82
+ Assert( @a.b.d == 'dd')
83
+ Assert( @a.b.e == 'e')
84
+ end
85
+
86
+ eg 'reverse_deep_merge' do
87
+ merged = @a.reverse_deep_merge(@b)
88
+
89
+ Show(merged)
90
+ Assert( ! merged.nil? )
91
+ Assert( ! same_obj( merged, @a ) )
92
+ Assert( ! same_obj( merged, @b ) )
93
+
94
+ Assert( merged.b.c == 'c')
95
+ Assert( merged.b.d == 'd')
96
+ Assert( merged.b.e == 'e')
97
+ end
98
+
99
+ eg 'deep regression' do
100
+ provider = AngryHash[
101
+ :network => {
102
+ :public => {
103
+ :gateway => '69.164.204.1',
104
+ :netmask => '255.255.255.0'
105
+ },
106
+ :private => {
107
+ :netmask => '255.255.128.0'
108
+ }
109
+ }]
110
+
111
+ server = AngryHash[
112
+ :public_ip => '72.14.191.135',
113
+ :private_ip => '192.168.146.105',
114
+ :network => {
115
+ :public => {:gateway => '72.14.191.1'}
116
+ }]
117
+
118
+ server.provider = provider
119
+ server.network!.reverse_deep_merge!(server.provider.network || {})
120
+
121
+ Show( Yajl::Encoder.encode( server ) )
122
+ end
123
+
124
+ eg 'merge with symbol key' do
125
+ orig = AngryHash[{
126
+ "hosts_db"=>:db,
127
+ "run_list"=>["role[database]"],
128
+ "provider"=>"octopus"
129
+ }]
130
+
131
+ merged = orig.merge(:key=>"db")
132
+
133
+ Show( merged )
134
+ Assert( merged.keys[1].is_a?(String) )
135
+ end
@@ -0,0 +1,215 @@
1
+
2
+ class AngryHash < Hash
3
+ require 'angry_hash/dsl'
4
+ include AngryHash::DSL
5
+
6
+ def self.[](other)
7
+ super(__convert(other))
8
+ end
9
+
10
+ alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
11
+ alias_method :regular_reader, :[] unless method_defined?(:regular_reader)
12
+ alias_method :regular_update, :update unless method_defined?(:regular_update)
13
+
14
+ def []=(key, value)
15
+ regular_writer(__convert_key(key), self.class.__convert_value_without_dup(value))
16
+ end
17
+
18
+ def [](key)
19
+ regular_reader(__convert_key(key))
20
+ end
21
+
22
+ def id
23
+ regular_reader('id')
24
+ end
25
+
26
+ def dup_and_store(key,value)
27
+ regular_writer(__convert_key(key), self.class.__convert_value(value))
28
+ end
29
+
30
+ alias_method :regular_merge, :merge unless method_defined?(:regular_merge)
31
+ def merge(hash)
32
+ regular_merge(self.class.__convert_without_dup(hash))
33
+ end
34
+
35
+ def merge!(other_hash)
36
+ other_hash.each_pair { |key, value| dup_and_store(key,value) }
37
+ self
38
+ end
39
+ alias_method :update, :merge!
40
+
41
+ def dup
42
+ self.class[ self ]
43
+ end
44
+
45
+ def deep_merge(other_hash)
46
+ # XXX this should convert other to AHash!
47
+ self.regular_merge( other_hash ) do |key, oldval, newval|
48
+ oldval = AngryHash.__convert_value(oldval)
49
+ newval = AngryHash.__convert_value(newval)
50
+
51
+ AngryHash === oldval && AngryHash === newval ? oldval.deep_merge(newval) : newval
52
+ end
53
+ end
54
+
55
+ def deep_merge!(other_hash)
56
+ replace(deep_merge(other_hash))
57
+ self
58
+ end
59
+ alias_method :deep_update, :deep_merge!
60
+
61
+ def reverse_deep_merge(other_hash)
62
+ self.class.__convert_value(other_hash).deep_merge(self)
63
+ end
64
+
65
+ def reverse_deep_merge!(other_hash)
66
+ replace(reverse_deep_merge(other_hash))
67
+ self
68
+ end
69
+ alias_method :reverse_deep_update, :reverse_deep_merge!
70
+
71
+
72
+
73
+ def key?(key)
74
+ super(__convert_key(key))
75
+ end
76
+
77
+ alias_method :include?, :key?
78
+ alias_method :has_key?, :key?
79
+ alias_method :member?, :key?
80
+
81
+ def fetch(key, *extras)
82
+ super(__convert_key(key), *extras)
83
+ end
84
+ def values_at(*indices)
85
+ indices.collect {|key| self[__convert_key(key)]}
86
+ end
87
+
88
+ def delete(key)
89
+ super __convert_key(key)
90
+ end
91
+
92
+ def to_hash
93
+ self
94
+ end
95
+
96
+ def to_normal_hash
97
+ __to_hash(self)
98
+ end
99
+ def __to_hash(value,cycle_guard={})
100
+ return cycle_guard[value.hash] if cycle_guard.key?(value.hash)
101
+
102
+ case value
103
+ when Hash
104
+ new_hash = cycle_guard[value.hash] = {}
105
+
106
+ value.inject(new_hash) do |hash,(k,v)|
107
+ hash[k] = __to_hash(v,cycle_guard)
108
+ hash
109
+ end
110
+ when Array
111
+ new_array = cycle_guard[value.hash] = []
112
+
113
+ value.each {|v| new_array << __to_hash(v,cycle_guard)}
114
+ else
115
+ value
116
+ end
117
+ end
118
+
119
+
120
+ def method_missing(method,*args,&blk)
121
+ method_s = method.to_s
122
+
123
+ key = method_s[0..-2]
124
+
125
+ case method_s[-1]
126
+ when ?=
127
+ return super unless args.size == 1 && !block_given?
128
+ self[ key ] = args.first
129
+
130
+ when ??
131
+ return super unless args.empty? && !block_given?
132
+ !! self[key]
133
+
134
+ when ?!
135
+ return super unless args.empty?
136
+ self[key] = AngryHash.new unless self.key?(key)
137
+ self[key]
138
+
139
+ else
140
+ return super unless args.empty? && !block_given?
141
+ self[method_s]
142
+ end
143
+ end
144
+
145
+ def __convert_key(key)
146
+ Symbol === key ? key.to_s : key
147
+ end
148
+ def self.__convert_key(key)
149
+ Symbol === key ? key.to_s : key
150
+ end
151
+
152
+
153
+ # non-duplicating convert
154
+ def self.__convert_without_dup(hash)
155
+ hash.inject(AngryHash.new) do |newhash,(k,v)|
156
+ newhash[__convert_key(k)] = __convert_value_without_dup(v)
157
+ newhash
158
+ end
159
+ end
160
+
161
+ def self.__convert_value_without_dup(v)
162
+ v = v.to_hash if v.respond_to?(:to_hash)
163
+
164
+ case v
165
+ when AngryHash
166
+ v
167
+ when Hash
168
+ __convert_without_dup(v)
169
+ when Array
170
+ v.map {|vv| __convert_value_without_dup(vv)}
171
+ else
172
+ v
173
+ end
174
+ end
175
+
176
+
177
+ # duplicating convert
178
+ def self.__convert(hash,cycle_watch=[])
179
+ new_hash = hash.inject(AngryHash.new) do |hash,(k,v)|
180
+ hash.regular_writer( __convert_key(k), __convert_value(v,cycle_watch) )
181
+ hash
182
+ end
183
+
184
+ new_hash
185
+ end
186
+
187
+ def self.__convert_value(v,cycle_watch=[])
188
+ id = v.__id__
189
+
190
+ return if cycle_watch.include? id
191
+
192
+ begin
193
+ cycle_watch << id
194
+
195
+ original_v = v
196
+ v = v.to_hash if v.respond_to?(:to_hash)
197
+
198
+ case v
199
+ when Hash
200
+ __convert(v,cycle_watch)
201
+ when Array
202
+ v.map {|vv| __convert_value(vv,cycle_watch)}
203
+ when Fixnum,Symbol,NilClass,TrueClass,FalseClass,Float
204
+ v
205
+ else
206
+ v.dup
207
+ end
208
+ ensure
209
+ cycle_watch.pop
210
+ end
211
+ end
212
+
213
+ end
214
+
215
+