map 1.3.0 → 1.4.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.
- 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
|