require 'enumerator' module Gorillib module Hashlike # # Makes a Receiver thingie behave mostly like a hash. # # By default, the hashlike methods iterate over the receiver attributes: # instance #keys delegates to self.class.keys which calls # receiver_attr_names. If you want to filter our add to the keys list, you # can just override the class-level keys method (and call super, or not): # # def self.keys # super + [:firstname, :lastname] - [:fullname] # end # # in addition to the below, by including Enumerable, this also adds # # :each_cons, :each_entry, :each_slice, :each_with_index, :each_with_object, # :map, :collect, :collect_concat, :entries, :to_a, :flat_map, :inject, :reduce, # :group_by, :chunk, :cycle, :partition, :reverse_each, :slice_before, :drop, # :drop_while, :take, :take_while, :detect, :find, :find_all, :find_index, :grep, # :all?, :any?, :none?, :one?, :first, :count, :zip :max, :max_by, :min, :min_by, # :minmax, :minmax_by, :sort, :sort_by # # As opposed to hash, does *not* define # # default, default=, default_proc, default_proc=, shift, flatten, compare_by_identity # compare_by_identity? rehash # module HashlikeViaAccessors # Hashlike#[] # # Element Reference -- Retrieves the value stored for +key+. # # In a normal hash, a default value can be set; none is provided here. # # Delegates to self.send(key) # # @example # hsh = { :a => 100, :b => 200 } # hsh[:a] # => 100 # hsh[:c] # => nil # # @param key [Object] key to retrieve # @return [Object] the value stored for key, nil if missing # def [](key) key = convert_key(key) self.send(key) end # Hashlike#[]= # Hashlike#store # # Element Assignment -- Associates the value given by +val+ with the key # given by +key+. # # key should not have its value changed while it is in use as a key. In a # normal hash, a String passed as a key will be duplicated and frozen. No such # guarantee is provided here # # Delegates to self.send("key=", val) # # @example # hsh = { :a => 100, :b => 200 } # hsh[:a] = 9 # hsh[:c] = 4 # hsh # => { :a => 9, :b => 200, :c => 4 } # # hsh[key] = val -> val # hsh.store(key, val) -> val # # @param key [Object] key to associate # @param val [Object] value to associate it with # @return [Object] # def []=(key, val) key = convert_key(key) self.send("#{key}=", val) end # Hashlike#delete # # Deletes and returns the value from +hsh+ whose key is equal to +key+. If the # optional code block is given and the key is not found, pass in the key and # return the result of +block+. # # In a normal hash, a default value can be set; none is provided here. # # @example # hsh = { :a => 100, :b => 200 } # hsh.delete(:a) # => 100 # hsh.delete(:z) # => nil # hsh.delete(:z){|el| "#{el} not found" } # => "z not found" # # @overload hsh.delete(key) -> val # @param key [Object] key to remove # @return [Object, Nil] the removed object, nil if missing # # @overload hsh.delete(key){|key| block } -> val # @param key [Object] key to remove # @yield [Object] called (with key) if key is missing # @yieldparam key # @return [Object, Nil] the removed object, or if missing, the return value # of the block # def delete(key, &block) key = convert_key(key) if has_key?(key) val = self[key] self.send(:remove_instance_variable, "@#{key}") val elsif block_given? block.call(key) else nil end end # # Hashlike#== # # # # Equality -- Two hashes are equal if they contain the same number of keys, # # and the value corresponding to each key in the first hash is equal (using # # ==) to the value for the same key in the second. If +obj+ is not a # # Hashlike, attempt to convert it using +to_hash+ and return obj == # # hsh. # # # # Does not take a default value comparion into account. # # # # @example # # h1 = { :a => 1, :c => 2 } # # h2 = { 7 => 35, :c => 2, :a => 1 } # # h3 = { :a => 1, :c => 2, 7 => 35 } # # h4 = { :a => 1, :d => 2, :f => 35 } # # h1 == h2 # => false # # h2 == h3 # => true # # h3 == h4 # => false # # # def ==(other_hash) # (length == other_hash.length) && # all?{|k,v| v == other_hash[k] } # end # Hashlike#keys # # Returns a new array populated with the keys from this hashlike. # # @see Hashlike#values. # # @example # hsh = { :a => 100, :b => 200, :c => 300, :d => 400 } # hsh.keys # => [:a, :b, :c, :d] # # @return [Array] list of keys # def keys instance_variables.map{|s| convert_key(s[1..-1]) } end protected def convert_key(key) raise ArgumentError, "Keys for #{self.class} must be symbols, strings or respond to #to_sym" unless key.respond_to?(:to_sym) key.to_sym end end end end