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.
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