map 6.6.0 → 8.0.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.
- checksums.yaml +7 -0
- data/LICENSE +1 -1
- data/README.md +268 -0
- data/Rakefile +140 -64
- data/images/giraffe.jpeg +0 -0
- data/images/map.png +0 -0
- data/lib/map/_lib.rb +85 -0
- data/lib/map/ordering.rb +126 -0
- data/lib/map.rb +151 -121
- data/map.gemspec +21 -12
- data/specs/001-ordered-map-module/checklists/requirements.md +62 -0
- data/specs/001-ordered-map-module/data-model.md +250 -0
- data/specs/001-ordered-map-module/plan.md +139 -0
- data/specs/001-ordered-map-module/quickstart.md +430 -0
- data/specs/001-ordered-map-module/research.md +189 -0
- data/specs/001-ordered-map-module/spec.md +108 -0
- data/specs/001-ordered-map-module/tasks.md +264 -0
- data/test/map_test.rb +21 -57
- metadata +39 -23
- data/a.rb +0 -10
- data/lib/map/integrations/active_record.rb +0 -140
- data/lib/map/params.rb +0 -79
- data/lib/map/struct.rb +0 -52
data/lib/map/_lib.rb
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
class Map
|
|
2
|
+
VERSION = '8.0.0'
|
|
3
|
+
|
|
4
|
+
class << Map
|
|
5
|
+
def version
|
|
6
|
+
VERSION
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def repo
|
|
10
|
+
'https://github.com/ahoward/map'
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def summary
|
|
14
|
+
<<~____
|
|
15
|
+
the perfect ruby data structure
|
|
16
|
+
____
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def description
|
|
20
|
+
<<~____
|
|
21
|
+
map.rb is a string/symbol indifferent ordered hash that works in all rubies.
|
|
22
|
+
|
|
23
|
+
out of the over 200 ruby gems i have written, this is the one i use
|
|
24
|
+
every day, in all my projects.
|
|
25
|
+
|
|
26
|
+
some may be accustomed to using ActiveSupport::HashWithIndiffentAccess
|
|
27
|
+
and, although there are some similarities, map.rb is more complete,
|
|
28
|
+
works without requiring a mountain of code, and has been in production
|
|
29
|
+
usage for over 15 years.
|
|
30
|
+
|
|
31
|
+
it has no dependencies, and suports a myriad of other, 'tree-ish'
|
|
32
|
+
operators that will allow you to slice and dice data like a giraffee
|
|
33
|
+
with a giant weed whacker.
|
|
34
|
+
____
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def libs
|
|
38
|
+
%w[
|
|
39
|
+
]
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def dependencies
|
|
43
|
+
{
|
|
44
|
+
}
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def libdir(*args, &block)
|
|
48
|
+
@libdir ||= File.dirname(File.expand_path(__FILE__))
|
|
49
|
+
args.empty? ? @libdir : File.join(@libdir, *args)
|
|
50
|
+
ensure
|
|
51
|
+
if block
|
|
52
|
+
begin
|
|
53
|
+
$LOAD_PATH.unshift(@libdir)
|
|
54
|
+
block.call
|
|
55
|
+
ensure
|
|
56
|
+
$LOAD_PATH.shift
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def load(*libs)
|
|
62
|
+
libs = libs.join(' ').scan(/[^\s+]+/)
|
|
63
|
+
libdir { libs.each { |lib| Kernel.load(lib) } }
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def load_dependencies!
|
|
67
|
+
libs.each do |lib|
|
|
68
|
+
require lib
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
begin
|
|
72
|
+
require 'rubygems'
|
|
73
|
+
rescue LoadError
|
|
74
|
+
nil
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
has_rubygems = defined?(gem)
|
|
78
|
+
|
|
79
|
+
dependencies.each do |lib, dependency|
|
|
80
|
+
gem(*dependency) if has_rubygems
|
|
81
|
+
require(lib)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
data/lib/map/ordering.rb
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
|
2
|
+
|
|
3
|
+
# Map::Ordering
|
|
4
|
+
#
|
|
5
|
+
# This module contains all ordering-related methods that use the @keys array
|
|
6
|
+
# to maintain insertion order. It is conditionally included in Map only when
|
|
7
|
+
# Ruby's Hash class does not maintain insertion order (Ruby < 1.9), or when
|
|
8
|
+
# explicitly forced via ENV['MAP_FORCE_ORDERING'].
|
|
9
|
+
#
|
|
10
|
+
# On Ruby 1.9+, Hash maintains insertion order natively, so these methods are
|
|
11
|
+
# not needed and Map delegates to Hash's implementation for memory optimization.
|
|
12
|
+
|
|
13
|
+
class Map < Hash
|
|
14
|
+
module Ordering
|
|
15
|
+
# Hook called when module is included into Map class
|
|
16
|
+
# This allows us to inject class methods into Map
|
|
17
|
+
def self.included(base)
|
|
18
|
+
base.class_eval do
|
|
19
|
+
# Override Map.allocate to initialize @keys array
|
|
20
|
+
def self.allocate
|
|
21
|
+
super.instance_eval do
|
|
22
|
+
@keys = []
|
|
23
|
+
self
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Instance methods that track ordering via @keys array
|
|
30
|
+
|
|
31
|
+
def keys
|
|
32
|
+
@keys ||= []
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def []=(key, val)
|
|
36
|
+
key, val = convert(key, val)
|
|
37
|
+
keys.push(key) unless has_key?(key)
|
|
38
|
+
__set__(key, val)
|
|
39
|
+
end
|
|
40
|
+
alias_method 'store', '[]='
|
|
41
|
+
|
|
42
|
+
def values
|
|
43
|
+
array = []
|
|
44
|
+
keys.each{|key| array.push(self[key])}
|
|
45
|
+
array
|
|
46
|
+
end
|
|
47
|
+
alias_method 'vals', 'values'
|
|
48
|
+
|
|
49
|
+
def first
|
|
50
|
+
[keys.first, self[keys.first]]
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def last
|
|
54
|
+
[keys.last, self[keys.last]]
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def each_with_index
|
|
58
|
+
keys.each_with_index{|key, index| yield([key, self[key]], index)}
|
|
59
|
+
self
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def each_key
|
|
63
|
+
keys.each{|key| yield(key)}
|
|
64
|
+
self
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def each_value
|
|
68
|
+
keys.each{|key| yield self[key]}
|
|
69
|
+
self
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def each
|
|
73
|
+
keys.each{|key| yield(key, self[key])}
|
|
74
|
+
self
|
|
75
|
+
end
|
|
76
|
+
alias_method 'each_pair', 'each'
|
|
77
|
+
|
|
78
|
+
def delete(key)
|
|
79
|
+
key = convert_key(key)
|
|
80
|
+
keys.delete(key)
|
|
81
|
+
super(key)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def clear
|
|
85
|
+
keys.clear
|
|
86
|
+
super
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Array-like ordered operations
|
|
90
|
+
def shift
|
|
91
|
+
unless empty?
|
|
92
|
+
key = keys.first
|
|
93
|
+
val = delete(key)
|
|
94
|
+
[key, val]
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def unshift(*args)
|
|
99
|
+
Map.each_pair(*args) do |key, val|
|
|
100
|
+
key = convert_key(key)
|
|
101
|
+
delete(key)
|
|
102
|
+
keys.unshift(key)
|
|
103
|
+
__set__(key, val)
|
|
104
|
+
end
|
|
105
|
+
self
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def push(*args)
|
|
109
|
+
Map.each_pair(*args) do |key, val|
|
|
110
|
+
key = convert_key(key)
|
|
111
|
+
delete(key)
|
|
112
|
+
keys.push(key)
|
|
113
|
+
__set__(key, val)
|
|
114
|
+
end
|
|
115
|
+
self
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def pop
|
|
119
|
+
unless empty?
|
|
120
|
+
key = keys.last
|
|
121
|
+
val = delete(key)
|
|
122
|
+
[key, val]
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
data/lib/map.rb
CHANGED
|
@@ -1,41 +1,10 @@
|
|
|
1
1
|
# -*- encoding : utf-8 -*-
|
|
2
2
|
class Map < Hash
|
|
3
|
-
|
|
4
|
-
Load = Kernel.method(:load) unless defined?(Load)
|
|
3
|
+
require_relative 'map/_lib.rb'
|
|
5
4
|
|
|
6
5
|
class << Map
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
def description
|
|
12
|
-
"the awesome ruby container you've always wanted: a string/symbol indifferent ordered hash that works in all rubies"
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def libdir(*args, &block)
|
|
16
|
-
@libdir ||= File.expand_path(__FILE__).sub(/\.rb$/,'')
|
|
17
|
-
libdir = args.empty? ? @libdir : File.join(@libdir, *args.map{|arg| arg.to_s})
|
|
18
|
-
ensure
|
|
19
|
-
if block
|
|
20
|
-
begin
|
|
21
|
-
$LOAD_PATH.unshift(libdir) unless $LOAD_PATH.first==libdir
|
|
22
|
-
module_eval(&block)
|
|
23
|
-
ensure
|
|
24
|
-
$LOAD_PATH.shift() if $LOAD_PATH.first==libdir
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
def load(*args, &block)
|
|
30
|
-
libdir{ Load.call(*args, &block) }
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
def allocate
|
|
34
|
-
super.instance_eval do
|
|
35
|
-
@keys = []
|
|
36
|
-
self
|
|
37
|
-
end
|
|
38
|
-
end
|
|
6
|
+
# allocate method moved to Map::Ordering module (conditionally included)
|
|
7
|
+
# When ordering module is not included (Ruby 1.9+), Hash.allocate is used
|
|
39
8
|
|
|
40
9
|
def new(*args, &block)
|
|
41
10
|
allocate.instance_eval do
|
|
@@ -195,11 +164,10 @@ class Map < Hash
|
|
|
195
164
|
end
|
|
196
165
|
end
|
|
197
166
|
|
|
198
|
-
# instance constructor
|
|
167
|
+
# instance constructor
|
|
199
168
|
#
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
end
|
|
169
|
+
# keys method moved to Map::Ordering module (conditionally included)
|
|
170
|
+
# When ordering module is not included (Ruby 1.9+), Hash#keys is used
|
|
203
171
|
|
|
204
172
|
def initialize(*args, &block)
|
|
205
173
|
case args.size
|
|
@@ -258,6 +226,17 @@ class Map < Hash
|
|
|
258
226
|
key.kind_of?(Symbol) ? key.to_s : key
|
|
259
227
|
end
|
|
260
228
|
|
|
229
|
+
def Map.mapify(object)
|
|
230
|
+
case
|
|
231
|
+
when object.is_a?(Array)
|
|
232
|
+
object.each{|it| Map.mapify(it)}
|
|
233
|
+
when object.is_a?(Hash)
|
|
234
|
+
Map.for(object)
|
|
235
|
+
else
|
|
236
|
+
object
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
|
|
261
240
|
def convert_key(key)
|
|
262
241
|
if klass.respond_to?(:convert_key)
|
|
263
242
|
klass.convert_key(key)
|
|
@@ -332,21 +311,32 @@ class Map < Hash
|
|
|
332
311
|
alias_method '__get__', '[]' unless method_defined?('__get__')
|
|
333
312
|
alias_method '__update__', 'update' unless method_defined?('__update__')
|
|
334
313
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
314
|
+
# []= method:
|
|
315
|
+
# - With ordering module (Ruby < 1.9 or forced): tracks keys in @keys array
|
|
316
|
+
# - Without module (Ruby >= 1.9, not forced): just converts and stores
|
|
317
|
+
#
|
|
318
|
+
# Only define for Ruby >= 1.9 without forced ordering (module provides it otherwise)
|
|
319
|
+
unless RUBY_VERSION < '1.9' || ENV['MAP_FORCE_ORDERING']
|
|
320
|
+
def []=(key, val)
|
|
321
|
+
key, val = convert(key, val)
|
|
322
|
+
__set__(key, val)
|
|
323
|
+
end
|
|
324
|
+
alias_method 'store', '[]='
|
|
339
325
|
end
|
|
340
|
-
alias_method 'store', '[]='
|
|
341
326
|
|
|
342
327
|
def [](key)
|
|
343
328
|
key = convert_key(key)
|
|
344
329
|
__get__(key)
|
|
345
330
|
end
|
|
346
331
|
|
|
347
|
-
def fetch(key, *
|
|
348
|
-
|
|
349
|
-
|
|
332
|
+
def fetch(key, *keys, &block)
|
|
333
|
+
keys.unshift(key)
|
|
334
|
+
|
|
335
|
+
if has?(*keys)
|
|
336
|
+
get(*keys)
|
|
337
|
+
else
|
|
338
|
+
Map.mapify(yield)
|
|
339
|
+
end
|
|
350
340
|
end
|
|
351
341
|
|
|
352
342
|
def key?(key)
|
|
@@ -377,59 +367,67 @@ class Map < Hash
|
|
|
377
367
|
replace(reverse_merge(hash))
|
|
378
368
|
end
|
|
379
369
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
end
|
|
385
|
-
alias_method 'vals', 'values'
|
|
370
|
+
# Ordering-dependent methods moved to Map::Ordering module (conditionally included):
|
|
371
|
+
# - values, each_with_index, each_key, each_value, each/each_pair (iterate via @keys)
|
|
372
|
+
# - clear (maintain @keys synchronization)
|
|
373
|
+
# When ordering module is not included (Ruby 1.9+), Hash methods are used
|
|
386
374
|
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
375
|
+
# For Ruby >= 1.9 without ordering module, provide optimized implementations
|
|
376
|
+
# These methods are only defined when the ordering module is NOT included
|
|
377
|
+
unless RUBY_VERSION < '1.9' || ENV['MAP_FORCE_ORDERING']
|
|
378
|
+
# delete needs key conversion
|
|
379
|
+
def delete(key)
|
|
380
|
+
key = convert_key(key)
|
|
381
|
+
super(key)
|
|
382
|
+
end
|
|
390
383
|
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
384
|
+
# first and last return [key, value] pairs
|
|
385
|
+
# Use keys array since Hash#first/Hash#last don't exist in all Ruby versions
|
|
386
|
+
def first
|
|
387
|
+
key = keys.first
|
|
388
|
+
[key, self[key]] if key
|
|
389
|
+
end
|
|
394
390
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
391
|
+
def last
|
|
392
|
+
key = keys.last
|
|
393
|
+
[key, self[key]] if key
|
|
394
|
+
end
|
|
398
395
|
|
|
399
|
-
#
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
self
|
|
404
|
-
end
|
|
396
|
+
# values uses Hash#values (ordered in 1.9+)
|
|
397
|
+
def values
|
|
398
|
+
Hash.instance_method(:values).bind(self).call
|
|
399
|
+
end
|
|
405
400
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
401
|
+
# Iterator methods delegate to Hash (ordered in 1.9+)
|
|
402
|
+
def each
|
|
403
|
+
Hash.instance_method(:each).bind(self).call{|k, v| yield(k, v)}
|
|
404
|
+
end
|
|
405
|
+
alias_method 'each_pair', 'each'
|
|
410
406
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
end
|
|
407
|
+
def each_key
|
|
408
|
+
Hash.instance_method(:each_key).bind(self).call{|k| yield(k)}
|
|
409
|
+
end
|
|
415
410
|
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
end
|
|
420
|
-
alias_method 'each_pair', 'each'
|
|
411
|
+
def each_value
|
|
412
|
+
Hash.instance_method(:each_value).bind(self).call{|v| yield(v)}
|
|
413
|
+
end
|
|
421
414
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
415
|
+
def each_with_index
|
|
416
|
+
i = 0
|
|
417
|
+
each do |k, v|
|
|
418
|
+
yield([k, v], i)
|
|
419
|
+
i += 1
|
|
420
|
+
end
|
|
421
|
+
self
|
|
422
|
+
end
|
|
423
|
+
|
|
424
|
+
def clear
|
|
425
|
+
Hash.instance_method(:clear).bind(self).call
|
|
426
|
+
end
|
|
428
427
|
end
|
|
429
428
|
|
|
430
|
-
def
|
|
431
|
-
keys.
|
|
432
|
-
super
|
|
429
|
+
def values_at(*keys)
|
|
430
|
+
keys.map{|key| self[key]}
|
|
433
431
|
end
|
|
434
432
|
|
|
435
433
|
def delete_if(&block)
|
|
@@ -460,39 +458,51 @@ class Map < Hash
|
|
|
460
458
|
|
|
461
459
|
# ordered container specific methods
|
|
462
460
|
#
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
461
|
+
# When Map::Ordering module is included (Ruby < 1.9 or forced):
|
|
462
|
+
# These methods use @keys array for efficient order manipulation
|
|
463
|
+
# When module is NOT included (Ruby 1.9+):
|
|
464
|
+
# These methods are defined below and work with Hash's native ordering
|
|
465
|
+
#
|
|
466
|
+
# Only define for Ruby >= 1.9 without forced ordering (module provides them otherwise)
|
|
467
|
+
unless RUBY_VERSION < '1.9' || ENV['MAP_FORCE_ORDERING']
|
|
468
|
+
def shift
|
|
469
|
+
unless empty?
|
|
470
|
+
key = keys.first
|
|
471
|
+
val = delete(key)
|
|
472
|
+
[key, val]
|
|
473
|
+
end
|
|
468
474
|
end
|
|
469
|
-
end
|
|
470
475
|
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
476
|
+
def unshift(*args)
|
|
477
|
+
# For Ruby 1.9+: process each pair in order, unshifting sequentially
|
|
478
|
+
# This matches the @keys array behavior
|
|
479
|
+
Map.each_pair(*args) do |key, val|
|
|
480
|
+
key = convert_key(key)
|
|
481
|
+
val = convert_value(val)
|
|
482
|
+
# Rebuild hash with this key at front
|
|
483
|
+
temp = {key => val}
|
|
484
|
+
each do |k, v|
|
|
485
|
+
temp[k] = v unless k == key
|
|
486
|
+
end
|
|
487
|
+
clear
|
|
488
|
+
temp.each{|k, v| __set__(k, v)}
|
|
489
|
+
end
|
|
490
|
+
self
|
|
477
491
|
end
|
|
478
|
-
self
|
|
479
|
-
end
|
|
480
492
|
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
__set__(key, val)
|
|
493
|
+
def push(*args)
|
|
494
|
+
Map.each_pair(*args) do |key, val|
|
|
495
|
+
self[key] = val # This naturally appends in Ruby 1.9+
|
|
496
|
+
end
|
|
497
|
+
self
|
|
487
498
|
end
|
|
488
|
-
self
|
|
489
|
-
end
|
|
490
499
|
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
500
|
+
def pop
|
|
501
|
+
unless empty?
|
|
502
|
+
key = keys.last
|
|
503
|
+
val = delete(key)
|
|
504
|
+
[key, val]
|
|
505
|
+
end
|
|
496
506
|
end
|
|
497
507
|
end
|
|
498
508
|
|
|
@@ -636,21 +646,26 @@ class Map < Hash
|
|
|
636
646
|
# a sane method missing that only supports writing values or reading
|
|
637
647
|
# *previously set* values
|
|
638
648
|
#
|
|
639
|
-
def method_missing(*args, &block)
|
|
649
|
+
def method_missing(*args, **kws, &block)
|
|
640
650
|
method = args.first.to_s
|
|
651
|
+
|
|
641
652
|
case method
|
|
642
653
|
when /=$/
|
|
643
654
|
key = args.shift.to_s.chomp('=')
|
|
644
655
|
value = args.shift
|
|
645
656
|
self[key] = value
|
|
657
|
+
|
|
646
658
|
when /\?$/
|
|
647
659
|
key = args.shift.to_s.chomp('?')
|
|
648
660
|
self.has?( key )
|
|
661
|
+
|
|
649
662
|
else
|
|
650
663
|
key = method
|
|
664
|
+
|
|
651
665
|
unless has_key?(key)
|
|
652
666
|
return(block ? fetch(key, &block) : super(*args))
|
|
653
667
|
end
|
|
668
|
+
|
|
654
669
|
self[key]
|
|
655
670
|
end
|
|
656
671
|
end
|
|
@@ -1172,6 +1187,24 @@ class Map < Hash
|
|
|
1172
1187
|
def mongoize
|
|
1173
1188
|
self
|
|
1174
1189
|
end
|
|
1190
|
+
|
|
1191
|
+
# Conditionally include ordering module based on Ruby version
|
|
1192
|
+
#
|
|
1193
|
+
# Ruby 1.9+ maintains Hash insertion order natively, so the @keys array
|
|
1194
|
+
# and associated methods are not needed. This provides memory optimization
|
|
1195
|
+
# (25% reduction per Map instance) and delegates to Hash's efficient ordering.
|
|
1196
|
+
#
|
|
1197
|
+
# For Ruby < 1.9, the ordering module provides manual insertion order tracking
|
|
1198
|
+
# via @keys array for backward compatibility.
|
|
1199
|
+
if RUBY_VERSION < '1.9'
|
|
1200
|
+
# Ruby < 1.9: MUST use ordering module (Hash is unordered)
|
|
1201
|
+
require_relative 'map/ordering'
|
|
1202
|
+
include Ordering
|
|
1203
|
+
elsif ENV['MAP_FORCE_ORDERING']
|
|
1204
|
+
# Ruby >= 1.9 ONLY: Optionally force module inclusion for testing legacy code path
|
|
1205
|
+
require_relative 'map/ordering'
|
|
1206
|
+
include Ordering
|
|
1207
|
+
end
|
|
1175
1208
|
end
|
|
1176
1209
|
|
|
1177
1210
|
module Kernel
|
|
@@ -1181,7 +1214,4 @@ private
|
|
|
1181
1214
|
end
|
|
1182
1215
|
end
|
|
1183
1216
|
|
|
1184
|
-
|
|
1185
|
-
Map.load('options.rb')
|
|
1186
|
-
Map.load('params.rb')
|
|
1187
|
-
|
|
1217
|
+
require_relative 'map/options.rb'
|
data/map.gemspec
CHANGED
|
@@ -3,26 +3,38 @@
|
|
|
3
3
|
|
|
4
4
|
Gem::Specification::new do |spec|
|
|
5
5
|
spec.name = "map"
|
|
6
|
-
spec.version = "
|
|
6
|
+
spec.version = "8.0.0"
|
|
7
|
+
spec.required_ruby_version = '>= 3.0'
|
|
7
8
|
spec.platform = Gem::Platform::RUBY
|
|
8
|
-
spec.summary = "
|
|
9
|
-
spec.description = "
|
|
10
|
-
spec.license = "
|
|
9
|
+
spec.summary = "the perfect ruby data structure"
|
|
10
|
+
spec.description = "map.rb is a string/symbol indifferent ordered hash that works in all rubies.\n\nout of the over 200 ruby gems i have written, this is the one i use\nevery day, in all my projects.\n\nsome may be accustomed to using ActiveSupport::HashWithIndiffentAccess\nand, although there are some similarities, map.rb is more complete,\nworks without requiring a mountain of code, and has been in production\nusage for over 15 years.\n\nit has no dependencies, and suports a myriad of other, 'tree-ish'\noperators that will allow you to slice and dice data like a giraffee\nwith a giant weed whacker."
|
|
11
|
+
spec.license = "Ruby"
|
|
11
12
|
|
|
12
13
|
spec.files =
|
|
13
14
|
["LICENSE",
|
|
14
15
|
"README",
|
|
16
|
+
"README.md",
|
|
15
17
|
"Rakefile",
|
|
16
|
-
"
|
|
18
|
+
"images",
|
|
19
|
+
"images/giraffe.jpeg",
|
|
20
|
+
"images/map.png",
|
|
17
21
|
"lib",
|
|
18
22
|
"lib/map",
|
|
19
23
|
"lib/map.rb",
|
|
20
|
-
"lib/map/
|
|
21
|
-
"lib/map/integrations/active_record.rb",
|
|
24
|
+
"lib/map/_lib.rb",
|
|
22
25
|
"lib/map/options.rb",
|
|
23
|
-
"lib/map/
|
|
24
|
-
"lib/map/struct.rb",
|
|
26
|
+
"lib/map/ordering.rb",
|
|
25
27
|
"map.gemspec",
|
|
28
|
+
"specs",
|
|
29
|
+
"specs/001-ordered-map-module",
|
|
30
|
+
"specs/001-ordered-map-module/checklists",
|
|
31
|
+
"specs/001-ordered-map-module/checklists/requirements.md",
|
|
32
|
+
"specs/001-ordered-map-module/data-model.md",
|
|
33
|
+
"specs/001-ordered-map-module/plan.md",
|
|
34
|
+
"specs/001-ordered-map-module/quickstart.md",
|
|
35
|
+
"specs/001-ordered-map-module/research.md",
|
|
36
|
+
"specs/001-ordered-map-module/spec.md",
|
|
37
|
+
"specs/001-ordered-map-module/tasks.md",
|
|
26
38
|
"test",
|
|
27
39
|
"test/leak.rb",
|
|
28
40
|
"test/lib",
|
|
@@ -33,13 +45,10 @@ Gem::Specification::new do |spec|
|
|
|
33
45
|
|
|
34
46
|
spec.require_path = "lib"
|
|
35
47
|
|
|
36
|
-
spec.test_files = nil
|
|
37
|
-
|
|
38
48
|
|
|
39
49
|
|
|
40
50
|
spec.extensions.push(*[])
|
|
41
51
|
|
|
42
|
-
spec.rubyforge_project = "codeforpeople"
|
|
43
52
|
spec.author = "Ara T. Howard"
|
|
44
53
|
spec.email = "ara.t.howard@gmail.com"
|
|
45
54
|
spec.homepage = "https://github.com/ahoward/map"
|