Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 0 additions & 18 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -20,24 +20,6 @@ namespace :test do
task(:unit){ run_tests!(:unit) }
task(:functional){ run_tests!(:functional) }
task(:integration){ run_tests!(:integration) }

# Test with Map::Ordering module forced (simulates Ruby < 1.9)
task(:with_ordering) do
ENV['MAP_FORCE_ORDERING'] = '1'
puts "\n#{ '=' * 60 }"
puts "Running tests WITH Map::Ordering module (MAP_FORCE_ORDERING=1)"
puts "#{ '=' * 60 }\n\n"
run_tests!
end

# Test without Map::Ordering module (Ruby 1.9+ native Hash ordering)
task(:without_ordering) do
ENV.delete('MAP_FORCE_ORDERING')
puts "\n#{ '=' * 60 }"
puts "Running tests WITHOUT Map::Ordering module (Ruby 1.9+ mode)"
puts "#{ '=' * 60 }\n\n"
run_tests!
end
end

def run_tests!(which = nil)
Expand Down
194 changes: 74 additions & 120 deletions lib/map.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@ class Map < Hash
require_relative 'map/_lib.rb'

class << Map
# allocate method moved to Map::Ordering module (conditionally included)
# When ordering module is not included (Ruby 1.9+), Hash.allocate is used

# no custom allocate: Hash.allocate is used (Hash is ordered in Ruby 1.9+).
def new(*args, &block)
allocate.instance_eval do
initialize(*args, &block)
Expand Down Expand Up @@ -166,9 +164,6 @@ def bcall(*args, &block)

# instance constructor
#
# keys method moved to Map::Ordering module (conditionally included)
# When ordering module is not included (Ruby 1.9+), Hash#keys is used

def initialize(*args, &block)
case args.size
when 0
Expand Down Expand Up @@ -311,18 +306,12 @@ def default=(value)
alias_method '__get__', '[]' unless method_defined?('__get__')
alias_method '__update__', 'update' unless method_defined?('__update__')

# []= method:
# - With ordering module (Ruby < 1.9 or forced): tracks keys in @keys array
# - Without module (Ruby >= 1.9, not forced): just converts and stores
#
# Only define for Ruby >= 1.9 without forced ordering (module provides it otherwise)
unless RUBY_VERSION < '1.9' || ENV['MAP_FORCE_ORDERING']
def []=(key, val)
key, val = convert(key, val)
__set__(key, val)
end
alias_method 'store', '[]='
# Hash preserves insertion order natively, so []= just converts and stores.
def []=(key, val)
key, val = convert(key, val)
__set__(key, val)
end
alias_method 'store', '[]='

def [](key)
key = convert_key(key)
Expand Down Expand Up @@ -367,63 +356,52 @@ def reverse_merge!(hash)
replace(reverse_merge(hash))
end

# Ordering-dependent methods moved to Map::Ordering module (conditionally included):
# - values, each_with_index, each_key, each_value, each/each_pair (iterate via @keys)
# - clear (maintain @keys synchronization)
# When ordering module is not included (Ruby 1.9+), Hash methods are used
# order-dependent methods — Hash is ordered in Ruby 1.9+, so they delegate
# straight to it (key conversion where the reader needs it).
def delete(key)
key = convert_key(key)
super(key)
end

# For Ruby >= 1.9 without ordering module, provide optimized implementations
# These methods are only defined when the ordering module is NOT included
unless RUBY_VERSION < '1.9' || ENV['MAP_FORCE_ORDERING']
# delete needs key conversion
def delete(key)
key = convert_key(key)
super(key)
end
# first and last return [key, value] pairs
def first
key = keys.first
[key, self[key]] if key
end

# first and last return [key, value] pairs
# Use keys array since Hash#first/Hash#last don't exist in all Ruby versions
def first
key = keys.first
[key, self[key]] if key
end
def last
key = keys.last
[key, self[key]] if key
end

def last
key = keys.last
[key, self[key]] if key
end
def values
Hash.instance_method(:values).bind(self).call
end

# values uses Hash#values (ordered in 1.9+)
def values
Hash.instance_method(:values).bind(self).call
end
def each
Hash.instance_method(:each).bind(self).call{|k, v| yield(k, v)}
end
alias_method 'each_pair', 'each'

# Iterator methods delegate to Hash (ordered in 1.9+)
def each
Hash.instance_method(:each).bind(self).call{|k, v| yield(k, v)}
end
alias_method 'each_pair', 'each'
def each_key
Hash.instance_method(:each_key).bind(self).call{|k| yield(k)}
end

def each_key
Hash.instance_method(:each_key).bind(self).call{|k| yield(k)}
end
def each_value
Hash.instance_method(:each_value).bind(self).call{|v| yield(v)}
end

def each_value
Hash.instance_method(:each_value).bind(self).call{|v| yield(v)}
end

def each_with_index
i = 0
each do |k, v|
yield([k, v], i)
i += 1
end
self
def each_with_index
i = 0
each do |k, v|
yield([k, v], i)
i += 1
end
self
end

def clear
Hash.instance_method(:clear).bind(self).call
end
def clear
Hash.instance_method(:clear).bind(self).call
end

def values_at(*keys)
Expand Down Expand Up @@ -458,51 +436,44 @@ def replace(*args)

# ordered container specific methods
#
# When Map::Ordering module is included (Ruby < 1.9 or forced):
# These methods use @keys array for efficient order manipulation
# When module is NOT included (Ruby 1.9+):
# These methods are defined below and work with Hash's native ordering
#
# Only define for Ruby >= 1.9 without forced ordering (module provides them otherwise)
unless RUBY_VERSION < '1.9' || ENV['MAP_FORCE_ORDERING']
def shift
unless empty?
key = keys.first
val = delete(key)
[key, val]
end
# ordered-container methods, riding Hash's native insertion order.
def shift
unless empty?
key = keys.first
val = delete(key)
[key, val]
end
end

def unshift(*args)
# For Ruby 1.9+: process each pair in order, unshifting sequentially
# This matches the @keys array behavior
Map.each_pair(*args) do |key, val|
key = convert_key(key)
val = convert_value(val)
# Rebuild hash with this key at front
temp = {key => val}
each do |k, v|
temp[k] = v unless k == key
end
clear
temp.each{|k, v| __set__(k, v)}
# unshift moves each pair to the FRONT (rebuild with the key first).
def unshift(*args)
Map.each_pair(*args) do |key, val|
key = convert_key(key)
val = convert_value(val)
temp = {key => val}
each do |k, v|
temp[k] = v unless k == key
end
self
clear
temp.each{|k, v| __set__(k, v)}
end
self
end

def push(*args)
Map.each_pair(*args) do |key, val|
self[key] = val # This naturally appends in Ruby 1.9+
end
self
# push appends. NOTE: for a key that already exists, Hash keeps its original
# position (native []= semantics) — re-pushing does NOT move it to the end.
def push(*args)
Map.each_pair(*args) do |key, val|
self[key] = val
end
self
end

def pop
unless empty?
key = keys.last
val = delete(key)
[key, val]
end
def pop
unless empty?
key = keys.last
val = delete(key)
[key, val]
end
end

Expand Down Expand Up @@ -1188,23 +1159,6 @@ def mongoize
self
end

# Conditionally include ordering module based on Ruby version
#
# Ruby 1.9+ maintains Hash insertion order natively, so the @keys array
# and associated methods are not needed. This provides memory optimization
# (25% reduction per Map instance) and delegates to Hash's efficient ordering.
#
# For Ruby < 1.9, the ordering module provides manual insertion order tracking
# via @keys array for backward compatibility.
if RUBY_VERSION < '1.9'
# Ruby < 1.9: MUST use ordering module (Hash is unordered)
require_relative 'map/ordering'
include Ordering
elsif ENV['MAP_FORCE_ORDERING']
# Ruby >= 1.9 ONLY: Optionally force module inclusion for testing legacy code path
require_relative 'map/ordering'
include Ordering
end
end

module Kernel
Expand Down
2 changes: 1 addition & 1 deletion lib/map/_lib.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
class Map
VERSION = '8.0.0'
VERSION = '9.0.0'

class << Map
def version
Expand Down
Loading