map 1.3.0 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README +44 -15
- data/TODO +1 -0
- data/a.rb +9 -0
- data/lib/map.rb +38 -9
- data/lib/map/struct.rb +48 -0
- data/test/map_test.rb +19 -0
- metadata +6 -4
data/README
CHANGED
@@ -1,26 +1,55 @@
|
|
1
|
+
NAME
|
2
|
+
map.rb
|
1
3
|
|
2
|
-
|
4
|
+
SYNOPSIS
|
5
|
+
the ruby container you've always wanted: a string/symbol indifferent ordered
|
6
|
+
hash that works in all rubies
|
3
7
|
|
4
|
-
|
5
|
-
|
6
|
-
m[:c] = :c
|
8
|
+
INSTALL
|
9
|
+
gem install map
|
7
10
|
|
8
|
-
|
11
|
+
URI
|
12
|
+
http://github.com/ahoward/map
|
9
13
|
|
14
|
+
DESCRIPTION
|
10
15
|
|
11
|
-
|
12
|
-
|
16
|
+
# maps are ordered. constructing them in an ordered fashion builds them that
|
17
|
+
# way
|
18
|
+
#
|
19
|
+
m = Map[:k, :v, :key, :val]
|
20
|
+
m = Map(:k, :v, :key, :val)
|
13
21
|
|
22
|
+
m = Map[[:k, :v], [:key, :val]]
|
23
|
+
m = Map[{:k => :v, :key => :val}]
|
14
24
|
|
15
|
-
m = Map[:k, :v, :key, :val]
|
16
|
-
m = Map[[:k, :v], [:key, :val]]
|
17
|
-
m = Map[{:k => :v, :key => :val}]
|
18
25
|
|
19
|
-
m = Map
|
20
|
-
m
|
21
|
-
m =
|
26
|
+
m = Map.new
|
27
|
+
m[:a] = 0
|
28
|
+
m[:b] = 1
|
29
|
+
m[:c] = 2
|
22
30
|
|
31
|
+
p m.keys #=> ['a','b','c'] ### always ordered!
|
32
|
+
p m.keys #=> [0,1,2] ### always ordered!
|
23
33
|
|
24
|
-
|
25
|
-
|
34
|
+
# maps don't care between symbol and string keys
|
35
|
+
#
|
36
|
+
p m[:a] #=> 0
|
37
|
+
p m["a"] #=> 0
|
26
38
|
|
39
|
+
# many functions operate in a way one would expect from an ordered container
|
40
|
+
#
|
41
|
+
m.update(:k2 => :v2)
|
42
|
+
m.update(:k2, :v2)
|
43
|
+
|
44
|
+
# maps keep mapiness for even deep operations
|
45
|
+
#
|
46
|
+
m.update :nested => {:hashes => {:are => :converted}}
|
47
|
+
|
48
|
+
# maps can give back clever little struct objects
|
49
|
+
#
|
50
|
+
m = Map(:foo => {:bar => 42})
|
51
|
+
s = m.struct
|
52
|
+
p s.foo.bar #=> 42
|
53
|
+
|
54
|
+
USAGE
|
55
|
+
test/map_test.rb
|
data/a.rb
ADDED
data/lib/map.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
class Map < Hash
|
2
|
-
Version = '1.
|
2
|
+
Version = '1.4.0' unless defined?(Version)
|
3
3
|
Load = Kernel.method(:load) unless defined?(Load)
|
4
4
|
|
5
5
|
class << Map
|
@@ -7,9 +7,33 @@ class Map < Hash
|
|
7
7
|
Map::Version
|
8
8
|
end
|
9
9
|
|
10
|
+
def libdir(*args, &block)
|
11
|
+
@libdir ||= File.expand_path(__FILE__).sub(/\.rb$/,'')
|
12
|
+
libdir = args.empty? ? @libdir : File.join(@libdir, *args.map{|arg| arg.to_s})
|
13
|
+
ensure
|
14
|
+
if block
|
15
|
+
begin
|
16
|
+
$LOAD_PATH.unshift(libdir) unless $LOAD_PATH.first==libdir
|
17
|
+
module_eval(&block)
|
18
|
+
ensure
|
19
|
+
$LOAD_PATH.shift() if $LOAD_PATH.first==libdir
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def load(*args, &block)
|
25
|
+
libdir{ Load.call(*args, &block) }
|
26
|
+
end
|
27
|
+
|
28
|
+
def allocate
|
29
|
+
super.instance_eval do
|
30
|
+
@keys = []
|
31
|
+
self
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
10
35
|
def new(*args, &block)
|
11
36
|
allocate.instance_eval do
|
12
|
-
@keys = []
|
13
37
|
initialize(*args, &block)
|
14
38
|
self
|
15
39
|
end
|
@@ -17,14 +41,17 @@ class Map < Hash
|
|
17
41
|
|
18
42
|
def for(*args, &block)
|
19
43
|
first = args.first
|
20
|
-
|
21
44
|
if(args.size == 1 and block.nil?)
|
22
45
|
return first.to_map if first.respond_to?(:to_map)
|
23
46
|
end
|
24
|
-
|
25
47
|
new(*args, &block)
|
26
48
|
end
|
27
49
|
|
50
|
+
def coerce(other)
|
51
|
+
return other.to_map if other.respond_to?(:to_map)
|
52
|
+
allocate.update(other.to_hash)
|
53
|
+
end
|
54
|
+
|
28
55
|
# iterate over arguments in pairs smartly.
|
29
56
|
#
|
30
57
|
def each_pair(*args)
|
@@ -114,7 +141,7 @@ class Map < Hash
|
|
114
141
|
end
|
115
142
|
|
116
143
|
def map_for(hash)
|
117
|
-
map = klass.
|
144
|
+
map = klass.coerce(hash)
|
118
145
|
map.default = hash.default
|
119
146
|
map
|
120
147
|
end
|
@@ -127,7 +154,7 @@ class Map < Hash
|
|
127
154
|
return value.to_map if value.respond_to?(:to_map)
|
128
155
|
case value
|
129
156
|
when Hash
|
130
|
-
klass.
|
157
|
+
klass.coerce(value)
|
131
158
|
when Array
|
132
159
|
value.map{|v| convert_value(v)}
|
133
160
|
else
|
@@ -331,15 +358,15 @@ class Map < Hash
|
|
331
358
|
end
|
332
359
|
|
333
360
|
def <=>(other)
|
334
|
-
keys <=> klass.
|
361
|
+
keys <=> klass.coerce(other).keys
|
335
362
|
end
|
336
363
|
|
337
364
|
def =~(hash)
|
338
|
-
to_hash == klass.
|
365
|
+
to_hash == klass.coerce(hash).to_hash
|
339
366
|
end
|
340
367
|
|
341
368
|
def invert
|
342
|
-
inverted = klass.
|
369
|
+
inverted = klass.allocate
|
343
370
|
inverted.default = self.default
|
344
371
|
keys.each{|key| inverted[self[key]] = key }
|
345
372
|
inverted
|
@@ -423,3 +450,5 @@ private
|
|
423
450
|
Map.new(*args, &block)
|
424
451
|
end
|
425
452
|
end
|
453
|
+
|
454
|
+
Map.load('struct.rb')
|
data/lib/map/struct.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
class Map
|
2
|
+
class Struct
|
3
|
+
instance_methods.each { |m| undef_method m unless m =~ /^__/ }
|
4
|
+
|
5
|
+
attr :map
|
6
|
+
|
7
|
+
def initialize(map)
|
8
|
+
@map = map
|
9
|
+
end
|
10
|
+
|
11
|
+
def method_missing(method, *args, &block)
|
12
|
+
method = method.to_s
|
13
|
+
case method
|
14
|
+
when /=$/
|
15
|
+
key = method.chomp('=')
|
16
|
+
value = args.shift
|
17
|
+
@map[key] = value
|
18
|
+
else
|
19
|
+
key = method
|
20
|
+
raise(IndexError, key) unless @map.has_key?(key)
|
21
|
+
value = @map[key]
|
22
|
+
end
|
23
|
+
value.is_a?(Map) ? value.struct : value
|
24
|
+
end
|
25
|
+
|
26
|
+
Keys = lambda{|*keys| keys.flatten!; keys.compact!; keys.map!{|key| key.to_s}}
|
27
|
+
|
28
|
+
delegates = %w(
|
29
|
+
inspect
|
30
|
+
)
|
31
|
+
|
32
|
+
delegates.each do |delegate|
|
33
|
+
module_eval <<-__, __FILE__, __LINE__
|
34
|
+
def #{ delegate }(*args, &block)
|
35
|
+
@map.#{ delegate }(*args, &block)
|
36
|
+
end
|
37
|
+
__
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def struct
|
42
|
+
@struct ||= Struct.new(map=self)
|
43
|
+
end
|
44
|
+
|
45
|
+
def Map.struct(*args, &block)
|
46
|
+
new(*args, &block).struct
|
47
|
+
end
|
48
|
+
end
|
data/test/map_test.rb
CHANGED
@@ -175,6 +175,25 @@ Testing Map do
|
|
175
175
|
assert{ a != b}
|
176
176
|
end
|
177
177
|
|
178
|
+
testing 'simple struct usage' do
|
179
|
+
a = assert{ Map.new(:k => :v) }
|
180
|
+
s = assert{ a.struct }
|
181
|
+
assert{ s.k == :v }
|
182
|
+
end
|
183
|
+
|
184
|
+
testing 'nested struct usage' do
|
185
|
+
a = assert{ Map.new(:k => {:l => :v}) }
|
186
|
+
s = assert{ a.struct }
|
187
|
+
assert{ s.k.l == :v }
|
188
|
+
end
|
189
|
+
|
190
|
+
testing 'that subclassing and clobbering initialize does not kill nested coercion' do
|
191
|
+
c = Class.new(Map){ def initialize(arg) end }
|
192
|
+
o = assert{ c.new(42) }
|
193
|
+
assert{ Map === o }
|
194
|
+
assert{ o.update(:k => {:a => :b}) }
|
195
|
+
end
|
196
|
+
|
178
197
|
protected
|
179
198
|
def new_int_map(n = 1024)
|
180
199
|
map = assert{ Map.new }
|
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:
|
4
|
+
hash: 7
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 1
|
8
|
-
-
|
8
|
+
- 4
|
9
9
|
- 0
|
10
|
-
version: 1.
|
10
|
+
version: 1.4.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: 2010-10-
|
18
|
+
date: 2010-10-21 00:00:00 -06:00
|
19
19
|
default_executable:
|
20
20
|
dependencies: []
|
21
21
|
|
@@ -28,6 +28,8 @@ extensions: []
|
|
28
28
|
extra_rdoc_files: []
|
29
29
|
|
30
30
|
files:
|
31
|
+
- a.rb
|
32
|
+
- lib/map/struct.rb
|
31
33
|
- lib/map.rb
|
32
34
|
- Rakefile
|
33
35
|
- README
|