map 1.3.0 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (7) hide show
  1. data/README +44 -15
  2. data/TODO +1 -0
  3. data/a.rb +9 -0
  4. data/lib/map.rb +38 -9
  5. data/lib/map/struct.rb +48 -0
  6. data/test/map_test.rb +19 -0
  7. metadata +6 -4
data/README CHANGED
@@ -1,26 +1,55 @@
1
+ NAME
2
+ map.rb
1
3
 
2
- m = Map.new
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
- m[:a] = :b
5
- m[:b] = :b
6
- m[:c] = :c
8
+ INSTALL
9
+ gem install map
7
10
 
8
- p m.keys #=> [:a, :b, :c] ### always ordered!
11
+ URI
12
+ http://github.com/ahoward/map
9
13
 
14
+ DESCRIPTION
10
15
 
11
- p m[:a] #=> :a
12
- p m["a"] #=> :a
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(:k, :v, :key, :val)
20
- m = Map([:k, :v], [:key, :val])
21
- m = Map(:k => :v, :key => :val)
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
- m.update(:k2, :v2)
25
- m.update(:k2 => :v2)
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/TODO CHANGED
@@ -1,4 +1,5 @@
1
1
  todo:
2
+ - struct tests
2
3
  - map.push(object)
3
4
 
4
5
  done:
data/a.rb ADDED
@@ -0,0 +1,9 @@
1
+
2
+ class M < ::Map
3
+ def initialize(arg)
4
+ @arg = arg
5
+ end
6
+ end
7
+
8
+ m = M.new(42)
9
+ m.update :k => {:a => :b}
data/lib/map.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  class Map < Hash
2
- Version = '1.3.0' unless defined?(Version)
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.new(hash)
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.for(value)
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.for(other).keys
361
+ keys <=> klass.coerce(other).keys
335
362
  end
336
363
 
337
364
  def =~(hash)
338
- to_hash == klass.for(hash).to_hash
365
+ to_hash == klass.coerce(hash).to_hash
339
366
  end
340
367
 
341
368
  def invert
342
- inverted = klass.new
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')
@@ -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
@@ -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: 27
4
+ hash: 7
5
5
  prerelease: false
6
6
  segments:
7
7
  - 1
8
- - 3
8
+ - 4
9
9
  - 0
10
- version: 1.3.0
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-11 00:00:00 -06:00
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