if (RUBY_VERSION < '1.9') && (not defined?(KeyError)) class KeyError < IndexError ; end end module Gorillib # # Your class must provide #[], #[]=, #delete, and #keys -- # # * hsh[key] Element Reference -- Retrieves the value stored for +key+. # * hsh[key] = val Element Assignment -- Associates +val+ with +key+. # * hsh.delete(key) Deletes & returns the value whose key is equal to +key+. # * hsh.keys Returns a new array populated with the keys. # # (see Hashlike::HashlikeViaAccessors for example) # # Given the above, hashlike will provide the rest, defining the methods # # :each_pair, :each, :each_key, :each_value, :values_at, :values_of, :values, # :size, :length, :has_key?, :include?, :key?, :member?, :has_value?, :value?, # :fetch, :key, :assoc, :rassoc, :empty?, :merge, :update, :merge!, :reject!, # :select!, :delete_if, :keep_if, :reject, :clear, :store, :to_hash, :invert, # :flatten # # and these methods added by Enumerable: # # :each_cons, :each_entry, :each_slice, :each_with_index, :each_with_object, # :entries, :to_a, :map, :collect, :collect_concat, :group_by, :flat_map, # :inject, :reduce, :chunk, :reverse_each, :slice_before, :drop, :drop_while, # :take, :take_while, :detect, :find, :find_all, :select, :find_index, :grep, # :all?, :any?, :none?, :one?, :first, :count, :zip, :max, :max_by, :min, # :min_by, :minmax, :minmax_by, :sort, :sort_by, :cycle, :partition, # # It does not define these methods that do exist on hash: # # :default, :default=, :default_proc, :default_proc=, # :compare_by_identity, :compare_by_identity?, # :replace, :rehash, :shift # # === Chinese wall # # With a few exceptions, all methods are defined only in terms of # # #[], #[]=, #delete, #keys, #each_pair and #has_key? # # (exceptions: merge family depend on #update; the reject/select/xx_if family # depend on each other; #invert & #flatten call #to_hash; #rassoc calls #key) # # === custom iterators # # Hashlike typically defines the following fundamental iterators by including # Gorillib::Hashlike::EnumerateFromKeys: # # :each_pair, :each, :values, :values_at, :length # # However, if the #each_pair method already exists on the class (as it does # for Struct), those methods will *not* be defined. The class is held # responsible for the implementation of all five. (Of these, #each_pair # is the only method called from elsewhere in Hashlike, while #each is the # only method called from Enumerable). # # === #convert_key (Indifferent Access) # # If you define #convert_key the #values_at, #has_key?, #fetch, and #assoc # methods will use it to sanitize keys coming in from the outside. It's # assumed that you will do the same with #[], #[]= and #delete. (see # Gorillib::HashWithIndifferentAccess for an example). # module Hashlike # # Provides a natural default iteration behavior by iterating over #keys. # Since most classes will want this behaviour, it is included by default # *unless* the class has already defined an #each method. # # Classes that wish to define their own iteration behavior (Struct for # example, or a database facade) must define all of the methods within this # module. # module EnumerateFromKeys # # Calls +block+ once for each key in +hsh+, passing the key/value pair as # parameters. # # If no block is given, an enumerator is returned instead. # # @example # hsh = { :a => 100, :b => 200 } # hsh.each_pair{|key, value| puts "#{key} is #{value}" } # # produces: # a is 100 # b is 200 # # @example with block arity: # hsh = {[:a,:b] => 3, [:c, :d] => 4, :e => 5} # seen_args = [] # hsh.each_pair{|arg1, arg2, arg3| seen_args << [arg1, arg2, arg3] } # # => [[[:a, :b], 3, nil], [[:c, :d], 4, nil], [:e, 5, nil]] # # seen_args = [] # hsh.each_pair{|(arg1, arg2), arg3| seen_args << [arg1, arg2, arg3] } # # => [[:a, :b, 3], [:c, :d, 4], [:e, nil, 5]] # # @overload hsh.each_pair{|key, val| block } -> hsh # Calls block once for each key in +hsh+ # @yield [key, val] in order, each key and its associated value # @return [Hashlike] # # @overload hsh.each_pair -> an_enumerator # with no block, returns a raw enumerator # @return [Enumerator] # def each_pair return enum_for(:each_pair) unless block_given? keys.each do |key| yield([key, self[key]]) end self end # # Calls +block+ once for each key in +hsh+, passing the key/value pair as # parameters. # # If no block is given, an enumerator is returned instead. # # @example # hsh = { :a => 100, :b => 200 } # hsh.each{|key, value| puts "#{key} is #{value}" } # # produces: # a is 100 # b is 200 # # @example with block arity: # hsh = {[:a,:b] => 3, [:c, :d] => 4, :e => 5} # seen_args = [] # hsh.each{|arg1, arg2, arg3| seen_args << [arg1, arg2, arg3] } # # => [[[:a, :b], 3, nil], [[:c, :d], 4, nil], [:e, 5, nil]] # # seen_args = [] # hsh.each{|(arg1, arg2), arg3| seen_args << [arg1, arg2, arg3] } # # => [[:a, :b, 3], [:c, :d, 4], [:e, nil, 5]] # # @overload hsh.each{|key, val| block } -> hsh # Calls block once for each key in +hsh+ # @yield [key, val] in order, each key and its associated value # @return [Hashlike] # # @overload hsh.each -> an_enumerator # with no block, returns a raw enumerator # @return [Enumerator] # def each(&block) return enum_for(:each) unless block_given? each_pair(&block) end # # Returns the number of key/value pairs in the hashlike. # # @example # hsh = { :d => 100, :a => 200, :v => 300, :e => 400 } # hsh.length # => 4 # hsh.delete(:a) # => 200 # hsh.length # => 3 # # @return [Fixnum] number of key-value pairs # def length keys.length end # # A new array populated with the values from +hsh+. # # @see Hashlike#keys. # # @example # hsh = { :a => 100, :b => 200, :c => 300 } # hsh.values # => [100, 200, 300] # # @return [Array] the values, in order by their key. # def values [].tap{|arr| each_pair{|key, val| arr << val } } end # # Array containing the values associated with the given keys. # # @see Hashlike#select. # # @example # hsh = { "cat" => "feline", "dog" => "canine", "cow" => "bovine" } # hsh.values_at("cow", "cat") # => ["bovine", "feline"] # # @example # hsh = { :a => 100, :b => 200, :c => 300 } # hsh.values_at(:c, :a, :c, :z, :a) # # => [300, 100, 300, nil, 100] # # @param *allowed_keys [Object] the keys to retrieve. # @return [Array] the values, in order according to allowed_keys. # def values_at(*allowed_keys) allowed_keys.map do |key| key = convert_key(key) if respond_to?(:convert_key) self[key] if has_key?(key) end end end # alias for #[]= def store(key, val) self[key] = val end # Hashlike#each_key # # Calls +block+ once for each key in +hsh+, passing the key as a parameter. # # If no block is given, an enumerator is returned instead. # # @example # hsh = { :a => 100, :b => 200 } # hsh.each_key{|key| puts key } # # produces: # a # b # # @example with block arity: # hsh = {[:a,:b] => 3, [:c, :d] => 4, :e => 5} # seen_args = [] # hsh.each_key{|arg1, arg2, arg3| seen_args << [arg1, arg2, arg3] } # # => [[:a, :b, nil], [:c, :d, nil], [:e, nil, nil]] # # seen_args = [] # hsh.each_key{|(arg1, arg2), arg3| seen_args << [arg1, arg2, arg3] } # # => [[:a, nil, :b], [:c, nil, :d], [:e, nil, nil]] # # @overload hsh.each_key{|key| block } -> hsh # Calls +block+ once for each key in +hsh+ # @yield [key] in order, each key # @return [Hashlike] # # @overload hsh.each_key -> an_enumerator # with no block, returns a raw enumerator # @return [Enumerator] # def each_key return enum_for(:each_key) unless block_given? each_pair{|k,v| yield k } self end # # Calls +block+ once for each key in +hsh+, passing the value as a parameter. # # If no block is given, an enumerator is returned instead. # # @example # hsh = { :a => 100, :b => 200 } # hsh.each_value{|value| puts value } # # produces: # 100 # 200 # # @example with block arity: # hsh = {:a => [300,333], :b => [400,444], :e => 500}) # seen_args = [] # hsh.each_value{|arg1, arg2, arg3| seen_args << [arg1, arg2, arg3] } # # => [[300, 333, nil], [400, 444, nil], [500, nil, nil]] # # seen_args = [] # hsh.each_value{|(arg1, arg2), arg3| seen_args << [arg1, arg2, arg3] } # # => [[300, nil, 333], [400, nil, 444], [500, nil, nil]] # # @overload hsh.each_value{|val| block } -> hsh # Calls +block+ once for each value in +hsh+ # @yield [val] in order by its key, each value # @return [Hashlike] # # @overload hsh.each_value -> an_enumerator # with no block, returns a raw enumerator # @return [Enumerator] # def each_value return enum_for(:each_value) unless block_given? each_pair{|k,v| yield v } self end # # Array containing the values associated with the given keys. # # @see Hashlike#select. # # @example # hsh = { "cat" => "feline", "dog" => "canine", "cow" => "bovine" } # hsh.values_of("cow", "cat") # => ["bovine", "feline"] # # @example # hsh = { :a => 100, :b => 200, :c => 300 } # hsh.values_of(:c, :a, :c, :z, :a) # # => [300, 100, 300, nil, 100] # # @param *allowed_keys [Object] the keys to retrieve. # @return [Array] the values, in order according to allowed_keys. # def values_of(*allowed_keys) allowed_keys.map do |key| key = convert_key(key) if respond_to?(:convert_key) self[key] if has_key?(key) end end # # Returns true if the given key is present in +hsh+. # # @example # hsh = { :a => 100, :b => 200 } # hsh.has_key?(:a) # => true # hsh.has_key?(:z) # => false # # @param key [Object] the key to check for. # @return [true, false] true if the key is present, false otherwise # def has_key?(key) key = convert_key(key) if respond_to?(:convert_key) keys.include?(key) end # # Returns true if the given value is present for some key in +hsh+. # # @example # hsh = { :a => 100, :b => 200 } # hsh.has_value?(100) # => true # hsh.has_value?(999) # => false # # @param target [Object] the value to query # @return [true, false] true if the value is present, false otherwise # def has_value?(target) # don't refactor this to any? -- Struct's #any is weird each_pair{|key, val| return true if (val == target) } false end # # Returns a value from the hashlike for the given key. If the key can't be # found, there are several options: # * With no other arguments, it will raise a +KeyError+ exception; # * if default is given, then that will be returned; # * if the optional code block is specified, then that will be run and its result returned. # # @example # hsh = { :a => 100, :b => 200 } # hsh.fetch(:a) # => 100 # hsh.fetch(:z, "go fish") # => "go fish" # hsh.fetch(:z){|el| "go fish, #{el}"} # => "go fish, z" # # @example An exception is raised if the key is not found and a default value is not supplied. # hsh = { :a => 100, :b => 200 } # hsh.fetch(:z) # # produces: # prog.rb:2:in `fetch': key not found (KeyError) from prog.rb:2 # # hsh.fetch(:z, 3) # # => 3 # # hsh.fetch(:z){|key| key.to_s * 5 } # # => "zzzzz" # # @param key [Object] the key to query # @param default [Object] the value to use if the key is missing # @raise [KeyError] raised if missing, and neither +default+ nor +block+ is supplied # @yield [key] if missing, block called with the key requested # @return [Object] the value; if missing, the default; if missing, the # block's return value # def fetch(key, default=nil, &block) key = convert_key(key) if respond_to?(:convert_key) warn "#{caller[0]}: warning: block supersedes default value argument" if default && block_given? if has_key?(key) then self[key] elsif block_given? then yield(key) elsif default then default else raise KeyError, "key not found: #{key.inspect}" end end # # Searches the hash for an entry whose value == +val+, returning the # corresponding key. If not found, returns +nil+. # # You are guaranteed that the first matching key in #keys will be the one # returned. # # @example # hsh = { :a => 100, :b => 200 } # hsh.key(200) # => :b # hsh.key(999) # => nil # # @param val [Object] the value to look up # @return [Object, nil] the key for the given val, or nil if missing # def key(val) keys.find{|key| self[key] == val } end # # Searches through the hashlike comparing obj with the key using ==. # Returns the key-value pair (two elements array) or nil if no match is # found. # # @see Array#assoc. # # @example # hsh = { "colors" => ["red", "blue", "green"], # "letters" => [:a, :b, :c ]} # hsh.assoc("letters") # => ["letters", [:a, :b, :c]] # hsh.assoc("foo") # => nil # # @return [Array, nil] the key-value pair (two elements array) or nil if no # match is found. # def assoc(key) key = convert_key(key) if respond_to?(:convert_key) return unless has_key?(key) [key, self[key]] end # # Searches through the hashlike comparing obj with the value using ==. # Returns the first key-value pair (two-element array) that matches, or nil # if no match is found. # # @see Array#rassoc. # # @example # hsh = { 1 => "one", 2 => "two", 3 => "three", "ii" => "two"} # hsh.rassoc("two") # => [2, "two"] # hsh.rassoc("four") # => nil # # @return [Array, nil] The first key-value pair (two-element array) that # matches, or nil if no match is found # def rassoc(val) key = key(val) or return [key, self[key]] end # # Returns true if the hashlike contains no key-value pairs, false otherwise. # # @example # {}.empty? # => true # # @return [true, false] true if +hsh+ contains no key-value pairs, false otherwise # def empty? keys.empty? end # # Adds the contents of +other_hash+ to +hsh+. If no block is # specified, entries with duplicate keys are overwritten with the values from # +other_hash+, otherwise the value of each duplicate key is determined by # calling the block with the key, its value in +hsh+ and its value in # +other_hash+. # # @example # h1 = { :a => 100, :b => 200 } # h2 = { :b => 254, :c => 300 } # h1.merge!(h2) # # => { :a => 100, :b => 254, :c => 300 } # # h1 = { :a => 100, :b => 200 } # h2 = { :b => 254, :c => 300 } # h1.merge!(h2){|key, v1, v2| v1 } # # => { :a => 100, :b => 200, :c => 300 } # # @overload hsh.update(other_hash) -> hsh # Adds the contents of +other_hash+ to +hsh+. Entries with duplicate keys are # overwritten with the values from +other_hash+ # @param other_hash [Hash, Hashlike] the hash to merge (it wins) # @return [Hashlike] this hashlike, updated # # @overload hsh.update(other_hash){|key, oldval, newval| block} -> hsh # Adds the contents of +other_hash+ to +hsh+. The value of each duplicate key # is determined by calling the block with the key, its value in +hsh+ and its # value in +other_hash+. # @param other_hash [Hash, Hashlike] the hash to merge (it wins) # @yield [Object, Object, Object] called if key exists in each +hsh+ # @return [Hashlike] this hashlike, updated # def update(other_hash) raise TypeError, "can't convert #{other_hash.nil? ? 'nil' : other_hash.class} into Hash" unless other_hash.respond_to?(:each_pair) other_hash.each_pair do |key, val| if block_given? && has_key?(key) val = yield(key, val, self[key]) end self[key] = val end self end # # Returns a new hashlike containing the contents of +other_hash+ and the # contents of +hsh+. If no block is specified, the value for entries with # duplicate keys will be that of +other_hash+. Otherwise the value for each # duplicate key is determined by calling the block with the key, its value in # +hsh+ and its value in +other_hash+. # # @example # h1 = { :a => 100, :b => 200 } # h2 = { :b => 254, :c => 300 } # h1.merge(h2) # # => { :a=>100, :b=>254, :c=>300 } # h1.merge(h2){|key, oldval, newval| newval - oldval} # # => { :a => 100, :b => 54, :c => 300 } # h1 # # => { :a => 100, :b => 200 } # # @overload hsh.merge(other_hash) -> hsh # Adds the contents of +other_hash+ to +hsh+. Entries with duplicate keys are # overwritten with the values from +other_hash+ # @param other_hash [Hash, Hashlike] the hash to merge (it wins) # @return [Hashlike] a new merged hashlike # # @overload hsh.merge(other_hash){|key, oldval, newval| block} -> hsh # Adds the contents of +other_hash+ to +hsh+. The value of each duplicate key # is determined by calling the block with the key, its value in +hsh+ and its # value in +other_hash+. # @param other_hash [Hash, Hashlike] the hash to merge (it wins) # @yield [Object, Object, Object] called if key exists in each +hsh+ # @return [Hashlike] a new merged hashlike # def merge(*args, &block) self.dup.update(*args, &block) end # # Deletes every key-value pair from +hsh+ for which +block+ evaluates truthy # (equivalent to Hashlike#delete_if), but returns nil if no changes were made. # # @example # hsh = { :a => 100, :b => 200, :c => 300 } # hsh.delete_if{|key, val| key.to_s >= "b" } # => { :a => 100 } # # hsh = { :a => 100, :b => 200, :c => 300 } # hsh.delete_if{|key, val| key.to_s >= "z" } # nil # # @overload hsh.reject!{|key, val| block } -> hsh or nil # Deletes every key-value pair from +hsh+ for which +block+ evaluates truthy. # @return [Hashlike, nil] # # @overload hsh.reject! -> an_enumerator # with no block, returns a raw enumerator # @return [Enumerator] # def reject!(&block) return enum_for(:reject!) unless block_given? changed = false each_pair do |key, val| if yield(*[key, val].take(block.arity)) changed = true delete(key) end end changed ? self : nil end # # Deletes every key-value pair from +hsh+ for which +block+ evaluates falsy # (equivalent to Hashlike#keep_if), but returns nil if no changes were made. # # @example # hsh = { :a => 100, :b => 200, :c => 300 } # hsh.select!{|key, val| key.to_s >= "b" } # => { :b => 200, :c => 300 } # # hsh = { :a => 100, :b => 200, :c => 300 } # hsh.select!{|key, val| key.to_s >= "a" } # => { :a => 100, :b => 200, :c => 300 } # # @overload hsh.select!{|key, val| block } -> hsh or nil # Deletes every key-value pair from +hsh+ for which +block+ evaluates falsy. # @return [Hashlike] # # @overload hsh.select! -> an_enumerator # with no block, returns a raw enumerator # @return [Enumerator] # def select!(&block) return enum_for(:select!) unless block_given? changed = false each_pair do |key, val| if not yield(*[key, val].take(block.arity)) changed = true delete(key) end end changed ? self : nil end # # Deletes every key-value pair from +hsh+ for which +block+ evaluates truthy. # # If no block is given, an enumerator is returned instead. # # @example # hsh = { :a => 100, :b => 200, :c => 300 } # hsh.delete_if{|key, val| key.to_s >= "b" } # => { :a => 100 } # # hsh = { :a => 100, :b => 200, :c => 300 } # hsh.delete_if{|key, val| key.to_s >= "z" } # => { :a => 100, :b => 200, :c => 300 } # # @overload hsh.delete_if{|key, val| block } -> hsh # Deletes every key-value pair from +hsh+ for which +block+ evaluates truthy. # @return [Hashlike] # # @overload hsh.delete_if -> an_enumerator # with no block, returns a raw enumerator # @return [Enumerator] # def delete_if(&block) return enum_for(:delete_if) unless block_given? reject!(&block) self end # # Deletes every key-value pair from +hsh+ for which +block+ evaluates falsy. # # If no block is given, an enumerator is returned instead. # # @example # hsh = { :a => 100, :b => 200, :c => 300 } # hsh.keep_if{|key, val| key.to_s >= "b" } # => { :b => 200, :c => 300 } # # hsh = { :a => 100, :b => 200, :c => 300 } # hsh.keep_if{|key, val| key.to_s >= "a" } # => { :a => 100, :b => 200, :c => 300 } # # @overload hsh.keep_if{|key, val| block } -> hsh # Deletes every key-value pair from +hsh+ for which +block+ evaluates falsy. # @return [Hashlike] # # @overload hsh.keep_if -> an_enumerator # with no block, returns a raw enumerator # @return [Enumerator] # def keep_if(&block) return enum_for(:keep_if) unless block_given? select!(&block) self end # # Overrides the implementation in Enumerable, which iterates on keys, not # key/value pairs # module OverrideEnumerable # # Deletes every key-value pair from +hsh+ for which +block+ evaluates to # true (similar to Hashlike#delete_if), but works on (and returns) a copy of # the +hsh+. Equivalent to hsh.dup.delete_if. # # @example # hsh = { :a => 100, :b => 200, :c => 300 } # hsh.reject{|key, val| key.to_s >= "b" } # => { :a => 100 } # hsh # => { :a => 100, :b => 200, :c => 300 } # # hsh = { :a => 100, :b => 200, :c => 300 } # hsh.reject{|key, val| key.to_s >= "z" } # => { :a => 100, :b => 200, :c => 300 } # hsh # => { :a => 100, :b => 200, :c => 300 } # # @overload hsh.reject{|key, val| block } -> new_hashlike # Deletes every key-value pair from +hsh+ for which +block+ evaluates truthy. # @return [Hashlike] # # @overload hsh.reject -> an_enumerator # with no block, returns a raw enumerator # @return [Enumerator] # # Overrides the implementation in Enumerable, which does the keys wrong. # def reject(&block) return enum_for(:reject) unless block_given? self.dup.delete_if(&block) end # # Deletes every key-value pair from +hsh+ for which +block+ evaluates to # false (similar to Hashlike#keep_if), but works on (and returns) a copy of # the +hsh+. Equivalent to hsh.dup.keep_if. # # @example # hsh = { :a => 100, :b => 200, :c => 300 } # hsh.select{|key, val| key.to_s >= "b" } # => { :b => 200, :c => 300 } # hsh # => { :a => 100, :b => 200, :c => 300 } # # hsh = { :a => 100, :b => 200, :c => 300 } # hsh.select{|key, val| key.to_s >= "z" } # => { } # hsh # => { :a => 100, :b => 200, :c => 300 } # # @overload hsh.select{|key, val| block } -> new_hashlike # Deletes every key-value pair from +hsh+ for which +block+ evaluates truthy. # @return [Hashlike] # # @overload hsh.select -> an_enumerator # with no block, returns a raw enumerator # @return [Enumerator] # # Overrides the implementation in Enumerable, which does the keys wrong. # def select(&block) return enum_for(:select) unless block_given? self.dup.keep_if(&block) end end # # Removes all key-value pairs from +hsh+. # # @example # hsh = { :a => 100, :b => 200 } # => { :a => 100, :b => 200 } # hsh.clear # => {} # # @return [Hashlike] this hashlike, emptied # def clear each_pair{|k,v| delete(k) } end # # Returns a hash with each key set to its associated value. # # @example # my_hshlike = MyHashlike.new # my_hshlike[:a] = 100; my_hshlike[:b] = 200 # my_hshlike.to_hash # => { :a => 100, :b => 200 } # # @return [Hash] a new Hash instance, with each key set to its associated value. # def to_hash {}.tap{|hsh| each_pair{|key, val| hsh[key] = val } } end # # Returns a new hash created by using +hsh+'s values as keys, and the keys as # values. If +hsh+ has duplicate values, the result will contain only one of # them as a key -- which one is not predictable. # # @example # hsh = { :n => 100, :m => 100, :y => 300, :d => 200, :a => 0 } # hsh.invert # => { 0 => :a, 100 => :m, 200 => :d, 300 => :y } # # @return [Hash] a new hash, with values for keys and vice-versa # def invert to_hash.invert end # # Returns a new array that is a one-dimensional flattening of this # hashlike. That is, for every key or value that is an array, extract its # elements into the new array. Unlike Array#flatten, this method does not # flatten recursively by default; pass +nil+ explicitly to flatten # recursively. The optional level argument determines the level of # recursion to flatten. # # @example # hsh = {1=> "one", 2 => [2,"two"], 3 => "three"} # hsh.flatten # => [1, "one", 2, [2, "two"], 3, "three"] # hsh.flatten(2) # => [1, "one", 2, 2, "two", 3, "three"] # # @example with deep nesting # hsh = { [1, 2, [3, 4]] => [1, [2, 3, [4, 5, 6]]] } # hsh.flatten # # => [[1, 2, [3, 4]], [1, [2, 3, [4, 5, 6]]]] # hsh.flatten(0) # # => [[[1, 2, [3, 4]], [1, [2, 3, [4, 5, 6]]]]] # hsh.flatten(1) # # => [[1, 2, [3, 4]], [1, [2, 3, [4, 5, 6]]]] # hsh.flatten(2) # # => [1, 2, [3, 4], 1, [2, 3, [4, 5, 6]]] # hsh.flatten(3) # # => [1, 2, 3, 4, 1, 2, 3, [4, 5, 6]] # hsh.flatten(4) # # => [1, 2, 3, 4, 1, 2, 3, 4, 5, 6] # hsh.flatten.flatten # # => [1, 2, 3, 4, 1, 2, 3, 4, 5, 6] # # @example nil level means complete flattening # hsh.flatten(nil) # # => [1, 2, 3, 4, 1, 2, 3, 4, 5, 6] # # @param level [Integer] the level of recursion to flatten, 0 by default. # @return [Array] the flattened key-value array. # def flatten(*args) to_hash.flatten(*args) end def self.included(base) base.class_eval do include EnumerateFromKeys unless method_defined?(:each_pair) unless include?(Enumerable) include Enumerable include OverrideEnumerable end # included here so they win out over Enumerable alias_method :include?, :has_key? alias_method :key?, :has_key? alias_method :member?, :has_key? alias_method :value?, :has_value? alias_method :merge!, :update alias_method :size, :length end end end end