class AttributeKit::AttributeHash
AttributeHash
inherits from and extends Hash, to provide tracking of attribute status (changed/deleted keys).
@example Basic usage
attributes = AttributeKit::AttributeHash.new #=> {} attributes.empty? #=> true attributes.dirty? #=> false attributes[:foo] = 'bar' #=> 'bar' attributes.dirty? #=> true attributes.dirty_keys #=> [:foo] attributes #=> {:foo=>"bar"} attributes[:bar] = 5 #=> 5 attributes #=> {:foo=>"bar", :bar=>5} attributes.dirty_keys #=> [:foo, :bar] attributes.deleted_keys #=> [] attributes.delete(:foo) #=> "bar" attributes.dirty_keys #=> [:bar, :foo] attributes.deleted_keys #=> [:foo] attributes.clean_attributes { |dirty_attrs| # Deleted: foo Nil value: true dirty_attrs.each_pair do |k,v| # Changed: bar New value: 5 case v[0] when :changed #=> {:foo=>[:deleted, nil], :bar=>[:changed, 5]} puts "Changed: #{k} New value: #{v[1]}" when :deleted # NOTE: The lack of a return value in this block puts "Deleted: #{k} Nil value: #{v[1].nil?}" # means that dirty_attrs was returned by both end # the block and the method itself. You may end # want to write a block to return true if it } # succeeds, and the dirty_attrs hash if it # fails, so you can pass the hash to a # method that can retry later.
Public Class Methods
Creates a new instance, using identical syntax to Hash @return [AttributeHash] new instance @see Hash#new
# File lib/attribute-kit/attribute_hash.rb, line 70 def initialize(*args) super(*args) @dirty_keys = [] @deleted_keys = [] end
Private Class Methods
@visibility private
# File lib/attribute-kit/attribute_hash.rb, line 52 def self.cond_deletion_method(method_name) define_method(method_name) { |&block| unless block.nil? keys = self.keys r = super(&block) @deleted_keys += keys - self.keys r else super() end } end
Public Instance Methods
Assigns a value to a key @param [Object] k key of key-value pair to insert or change in instance @param [Object] v value of key-value pair to insert or change in instance @return [Object] value of key-value pair inserted @see Hash#[]=
# File lib/attribute-kit/attribute_hash.rb, line 81 def []=(k,v) if self[k].eql? v v else @dirty_keys << k super end end
Calls a block with a hash of all keys, actions (:changed or :deleted), and current values (if :changed) of keys that have changed since the object was last marked clean. Marks the object as clean when it compiles the list of keys that have been modified. @param [Block] block to execute with hash of modified keys, actions, and values @yield [dirty_attrs] block is executed once, with a hash of dirty attributes @yieldparam [Hash] dirty_attrs the hash of changed/deleted attributes with the modified key as the key and a value
of an array in the format: [ACTION, VALUE] where ACTION is either :changed or :deleted, and VALUE is either the new value or nil if the attribute is deleted. A nil value does NOT mean the attribute is deleted if the ACTION is :changed, it means the value was actually set to nil
@yieldreturn [Object] any value the block returns bubbles up, otherwise @return [Object] the return value of the block is returned
# File lib/attribute-kit/attribute_hash.rb, line 232 def clean_attributes(&block) if !@dirty_keys.empty? dirty_attrs = {} @dirty_keys.uniq! dirty = @dirty_keys.dup @dirty_keys.clear deleted = @deleted_keys.dup @deleted_keys.clear while dirty.length > 0 do key = dirty.shift dirty_attrs[key] = [:changed, self[key]] end while deleted.length > 0 do key = deleted.shift dirty_attrs[key] = [:deleted, nil] end block.call(dirty_attrs) end end
Clear all contents of the object and mark it as dirty. An array of all removed keys is available via deleted_keys
. @return [AttributeHash] an empty AttributeHash
@see Hash#clear
# File lib/attribute-kit/attribute_hash.rb, line 195 def clear @deleted_keys += self.keys @dirty_keys.clear super end
Delete a key-value pair @param [Object] key key of key-value pair to delete from instance @return [Object] value of key-value pair deleted @see Hash#delete
# File lib/attribute-kit/attribute_hash.rb, line 96 def delete(k) @deleted_keys << k @dirty_keys.delete(k) super end
Returns the set of keys that have been deleted since the AttributeHash
was last marked clean. @return [Array] all of the deleted keys
# File lib/attribute-kit/attribute_hash.rb, line 216 def deleted_keys @deleted_keys.uniq! @deleted_keys end
Check whether the contents of the object have changed since the last time clean_attributes
was run. @return [Boolean] value indicating whether or not key-value pairs have been added, changed, or deleted
# File lib/attribute-kit/attribute_hash.rb, line 203 def dirty? !(@dirty_keys.empty? && @deleted_keys.empty?) end
Returns the set of keys that have been modified since the AttributeHash
was last marked clean. @return [Array] all of the changed keys
# File lib/attribute-kit/attribute_hash.rb, line 209 def dirty_keys @dirty_keys.uniq! @dirty_keys + self.deleted_keys end
Combine the contents of this object with the contents of the supplied hash, calling an optional supplied block to determine what value is used when there are duplicate keys. Without the block, values from the supplied hash will be used in the case of duplicate keys @param [Hash] other_hash hash of values to merge in to the instance @yield [key, oldval, newval] block is executed for every duplicate key between the instance and other_hash @yieldparam [Object] key the key being evaluated @yieldparam [Object] oldval the value from the value from the instance @yieldparam [Object] newval the value from the value from other_hash @yieldreturn [Object] the value to store for the key in question @return [AttributeHash] self with changes applied @see Hash#merge!
# File lib/attribute-kit/attribute_hash.rb, line 168 def merge!(other_hash, &block) old_keys = self.keys overlapping_keys = old_keys.dup.keep_if {|v| other_hash.keys.include?(v)} r = super if block.nil? @dirty_keys += (self.keys - old_keys) + overlapping_keys else new_values = other_hash.keep_if {|k,v| overlapping_keys.include?(k)} @dirty_keys += (self.keys - old_keys) + (new_values.keep_if {|k,v| !self[k].eql?(v) }).keys end r end
@overload KEY_dirty?()
Check whether a particular key is dirty - KEY is a string representation of the key name for the key-value pair being queried @note There can be conflicts if you have multiple keys that are similar, i.e. :blue and 'blue', so only use this when you can guarantee homogenous keys and are using either strings or symbols for the key (the only cases where it will work) @return [Boolean] value indicating key-value pair state @note Uses method_missing to implement the check
@overload KEY_deleted?()
Check whether a particular key has been deleted - KEY is a string representation of the key name for the key-value pair being queried @note There can be conflicts if you have multiple keys that are similar, i.e. :blue and 'blue', so only use this when you can guarantee homogenous keys and are using either strings or symbols for the key (the only cases where it will work) @return [Boolean] value indicating key-value pair state @note Uses method_missing to implement the check
# File lib/attribute-kit/attribute_hash.rb, line 270 def method_missing(method, *args, &block) method_name = method.to_s case method_name when /(.*)_dirty?/ return @dirty_keys.include?($1) if self.has_key?($1) return @dirty_keys.include?($1.to_sym) if self.has_key?($1.to_sym) @deleted_keys.include?($1) || @deleted_keys.include?($1.to_sym) when /(.*)_deleted?/ @deleted_keys.include?($1) || @deleted_keys.include?($1.to_sym) else if self.class.superclass.instance_methods.map(&:to_sym).include?(:method_mising) super(method, *args, &block) else raise NoMethodError.new("undefined method '#{method_name}' for #{self}") end end end
Replace the contents of this object with the contents of the supplied hash @param [Hash] other_hash hash of values to replace instance contents with @return [AttributeHash] self with changes applied @see Hash#replace
# File lib/attribute-kit/attribute_hash.rb, line 148 def replace(other_hash) old_keys = self.keys r = super new_keys = self.keys @dirty_keys = new_keys @deleted_keys += (old_keys - new_keys) r end
Returns a key-value pair from the instance and deletes it. @return [Array] key-value pair @see Hash#shift
# File lib/attribute-kit/attribute_hash.rb, line 186 def shift (k,v) = super @deleted_keys << k [k,v] end