angry_hash 0.0.1
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/.gitignore +2 -0
- data/Rakefile +15 -0
- data/VERSION +1 -0
- data/lib/angry_hash/merge_string.rb +58 -0
- data/lib/angry_hash.rb +115 -0
- metadata +66 -0
data/.gitignore
ADDED
data/Rakefile
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
begin
|
|
2
|
+
require 'jeweler'
|
|
3
|
+
Jeweler::Tasks.new do |gemspec|
|
|
4
|
+
gemspec.name = "angry_hash"
|
|
5
|
+
gemspec.summary = "A stabler mash with different emphases."
|
|
6
|
+
gemspec.description = "A stabler mash with different emphases. Used in plus2 projects AngryMob and Igor."
|
|
7
|
+
gemspec.email = "lachie@plus2.com.au"
|
|
8
|
+
gemspec.homepage = "http://github.com/plus2/angry_hash"
|
|
9
|
+
gemspec.authors = ["Lachie Cox"]
|
|
10
|
+
end
|
|
11
|
+
Jeweler::GemcutterTasks.new
|
|
12
|
+
rescue LoadError
|
|
13
|
+
puts "Jeweler not available. Install it with: gem install jeweler"
|
|
14
|
+
end
|
|
15
|
+
|
data/VERSION
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.0.1
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
class AngryHash
|
|
2
|
+
module MergeString
|
|
3
|
+
def merge_string(string)
|
|
4
|
+
match,key,op,value = *string.match(/^([\w\.]+)(\+?=)(.*)$/)
|
|
5
|
+
|
|
6
|
+
segments = key.split('.')
|
|
7
|
+
|
|
8
|
+
last_segment = segments[-1]
|
|
9
|
+
parent = fetch_path(segments[0..-2])
|
|
10
|
+
|
|
11
|
+
unless AngryHash === parent
|
|
12
|
+
raise "parent path element (at #{segments[0..-2] * '.'}) must be an AngryHash, not #{parent.class}"
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
target = parent[last_segment]
|
|
16
|
+
|
|
17
|
+
case target
|
|
18
|
+
when Array
|
|
19
|
+
__merge_with_op(target,op, [value].flatten.compact)
|
|
20
|
+
when Hash
|
|
21
|
+
raise "not implemented"
|
|
22
|
+
when String
|
|
23
|
+
__merge_with_op(target,op,value.to_s)
|
|
24
|
+
when NilClass
|
|
25
|
+
parent[last_segment] = value
|
|
26
|
+
else
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def __merge_with_op(target,op,value)
|
|
31
|
+
case op
|
|
32
|
+
when '='
|
|
33
|
+
target.replace(value)
|
|
34
|
+
when '+='
|
|
35
|
+
target.replace( target + value )
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def fetch_path(segments)
|
|
40
|
+
segments = segments.dup
|
|
41
|
+
return self if segments.empty?
|
|
42
|
+
ctx = self
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
location = []
|
|
46
|
+
while segment = segments.shift
|
|
47
|
+
unless AngryHash === ctx
|
|
48
|
+
raise "Path element at #{location * '.'} is #{ctx.class}, not an AngryHash. Can't descend to #{path}"
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
location << segment
|
|
52
|
+
ctx = ctx[segment] ||= AngryHash.new
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
ctx
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
data/lib/angry_hash.rb
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
class AngryHash < Hash
|
|
2
|
+
def self.[](other)
|
|
3
|
+
super(__convert(other))
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
|
|
7
|
+
alias_method :regular_reader, :[] unless method_defined?(:regular_reader)
|
|
8
|
+
alias_method :regular_update, :update unless method_defined?(:regular_update)
|
|
9
|
+
|
|
10
|
+
def []=(key, value)
|
|
11
|
+
regular_writer(__convert_key(key), AngryHash.__convert_value(value))
|
|
12
|
+
end
|
|
13
|
+
def [](key)
|
|
14
|
+
regular_reader(__convert_key(key))
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def update(other_hash)
|
|
18
|
+
other_hash.each_pair { |key, value| self[key] = value }
|
|
19
|
+
self
|
|
20
|
+
end
|
|
21
|
+
alias_method :merge!, :update
|
|
22
|
+
|
|
23
|
+
def deep_merge(other_hash)
|
|
24
|
+
self.merge(other_hash) do |key, oldval, newval|
|
|
25
|
+
oldval = AngryHash.__convert_value(oldval)
|
|
26
|
+
newval = AngryHash.__convert_value(newval)
|
|
27
|
+
|
|
28
|
+
AngryHash === oldval && AngryHash === newval ? oldval.deep_merge(newval) : newval
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def deep_merge!(other_hash)
|
|
33
|
+
replace(deep_merge(other_hash))
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def reverse_deep_merge!(other_hash)
|
|
37
|
+
replace(self.class.__convert_value(other_hash).deep_merge(self))
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def key?(key)
|
|
42
|
+
super(__convert_key(key))
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
alias_method :include?, :key?
|
|
46
|
+
alias_method :has_key?, :key?
|
|
47
|
+
alias_method :member?, :key?
|
|
48
|
+
|
|
49
|
+
def fetch(key, *extras)
|
|
50
|
+
super(__convert_key(key), *extras)
|
|
51
|
+
end
|
|
52
|
+
def values_at(*indices)
|
|
53
|
+
indices.collect {|key| self[__convert_key(key)]}
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def delete(key)
|
|
57
|
+
super __convert_key(key)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def to_hash
|
|
61
|
+
self
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def method_missing(method,*args,&blk)
|
|
65
|
+
method_s = method.to_s
|
|
66
|
+
|
|
67
|
+
key = method_s[0..-2]
|
|
68
|
+
|
|
69
|
+
case method_s[-1]
|
|
70
|
+
when ?=
|
|
71
|
+
self[ key ] = args.first
|
|
72
|
+
|
|
73
|
+
when ??
|
|
74
|
+
!! self[key]
|
|
75
|
+
|
|
76
|
+
when ?!
|
|
77
|
+
self[key] ||= AngryHash.new
|
|
78
|
+
|
|
79
|
+
else
|
|
80
|
+
self[method_s]
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def __convert_key(key)
|
|
85
|
+
Symbol === key ? key.to_s : key
|
|
86
|
+
end
|
|
87
|
+
def self.__convert_key(key)
|
|
88
|
+
Symbol === key ? key.to_s : key
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def self.__convert(hash)
|
|
92
|
+
hash.inject(AngryHash.new) do |hash,(k,v)|
|
|
93
|
+
hash[__convert_key(k)] = __convert_value(v)
|
|
94
|
+
hash
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def self.__convert_value(v)
|
|
99
|
+
v = v.to_hash if v.respond_to?(:to_hash)
|
|
100
|
+
|
|
101
|
+
case v
|
|
102
|
+
when AngryHash
|
|
103
|
+
v
|
|
104
|
+
when Hash
|
|
105
|
+
__convert(v)
|
|
106
|
+
when Array
|
|
107
|
+
v.map {|v| Hash === v ? __convert_value(v) : v}
|
|
108
|
+
else
|
|
109
|
+
v
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
|
metadata
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: angry_hash
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
prerelease: false
|
|
5
|
+
segments:
|
|
6
|
+
- 0
|
|
7
|
+
- 0
|
|
8
|
+
- 1
|
|
9
|
+
version: 0.0.1
|
|
10
|
+
platform: ruby
|
|
11
|
+
authors:
|
|
12
|
+
- Lachie Cox
|
|
13
|
+
autorequire:
|
|
14
|
+
bindir: bin
|
|
15
|
+
cert_chain: []
|
|
16
|
+
|
|
17
|
+
date: 2010-03-11 00:00:00 +11:00
|
|
18
|
+
default_executable:
|
|
19
|
+
dependencies: []
|
|
20
|
+
|
|
21
|
+
description: A stabler mash with different emphases. Used in plus2 projects AngryMob and Igor.
|
|
22
|
+
email: lachie@plus2.com.au
|
|
23
|
+
executables: []
|
|
24
|
+
|
|
25
|
+
extensions: []
|
|
26
|
+
|
|
27
|
+
extra_rdoc_files: []
|
|
28
|
+
|
|
29
|
+
files:
|
|
30
|
+
- .gitignore
|
|
31
|
+
- Rakefile
|
|
32
|
+
- VERSION
|
|
33
|
+
- lib/angry_hash.rb
|
|
34
|
+
- lib/angry_hash/merge_string.rb
|
|
35
|
+
has_rdoc: true
|
|
36
|
+
homepage: http://github.com/plus2/angry_hash
|
|
37
|
+
licenses: []
|
|
38
|
+
|
|
39
|
+
post_install_message:
|
|
40
|
+
rdoc_options:
|
|
41
|
+
- --charset=UTF-8
|
|
42
|
+
require_paths:
|
|
43
|
+
- lib
|
|
44
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
45
|
+
requirements:
|
|
46
|
+
- - ">="
|
|
47
|
+
- !ruby/object:Gem::Version
|
|
48
|
+
segments:
|
|
49
|
+
- 0
|
|
50
|
+
version: "0"
|
|
51
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
52
|
+
requirements:
|
|
53
|
+
- - ">="
|
|
54
|
+
- !ruby/object:Gem::Version
|
|
55
|
+
segments:
|
|
56
|
+
- 0
|
|
57
|
+
version: "0"
|
|
58
|
+
requirements: []
|
|
59
|
+
|
|
60
|
+
rubyforge_project:
|
|
61
|
+
rubygems_version: 1.3.6
|
|
62
|
+
signing_key:
|
|
63
|
+
specification_version: 3
|
|
64
|
+
summary: A stabler mash with different emphases.
|
|
65
|
+
test_files: []
|
|
66
|
+
|