Module: ActiveRecord::Acts::Diffable::InstanceMethods
- Defined in:
- lib/acts_as_diffable.rb
Overview
Adds instance methods.
Instance Method Summary collapse
-
#association_ids(*instances) ⇒ Object
Helper for collecting ids to ignore.
-
#attributes_diff(left, right, ignore = [:id]) ⇒ Object
Helper for handing objects with an attributes hash (a la ARec).
-
#diff(other) ⇒ Object
Return a hash of the different attributes between two hashes, such as attributes of an ActiveRecord class.
-
#generate_diff_hash(left, right, *ignore) ⇒ Object
Accepts a left & right hash, and an array of keys to ignore, returns a hash of the differences.
-
#keyify(keyset) ⇒ Object
reduce the key out of an array to a single string if only one element.
-
#plural_association_diff(left_parent, right_parent, association, key_pattern) ⇒ Object
Helper for processing a collection of associated objects, such as has_many (or habtm) association.
-
#remove_unchanged_entries(diff_hash) ⇒ Object
Helper for thinning the herd.
-
#singular_association_diff(left_parent, right_parent, association) ⇒ Object
Helper for processing a single associated object, such as has_one (or belongs_to) associations.
- #timed_log(start_time, msg) ⇒ Object
Instance Method Details
#association_ids(*instances) ⇒ Object
Helper for collecting ids to ignore.
128 129 130 |
# File 'lib/acts_as_diffable.rb', line 128 def association_ids(*instances) ['id'] + instances.collect{|i| i.class.to_s.underscore + '_id'} end |
#attributes_diff(left, right, ignore = [:id]) ⇒ Object
Helper for handing objects with an attributes hash (a la ARec).
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 |
# File 'lib/acts_as_diffable.rb', line 133 def attributes_diff(left, right, ignore = [:id]) left_attributes = case when left.is_a?(Hash) then left when left.respond_to?(:attributes) then left.attributes else left.instance_values end right_attributes = case when right.is_a?(Hash) then right when right.respond_to?(:attributes) then right.attributes else right.instance_values end if left_attributes == right_attributes return nil else generate_diff_hash(left_attributes, right_attributes, *ignore) end end |
#diff(other) ⇒ Object
Return a hash of the different attributes between two hashes, such as attributes of an ActiveRecord class.
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
# File 'lib/acts_as_diffable.rb', line 40 def diff(other) # is other an instance or just an id? case other.class when self.class other else other = self.class.find(other) end # diff the top-level attributes differences = attributes_diff(self, other) || {} # has_one and belongs_to associations ActiveRecord::Acts::Diffable::SINGULAR_MACROS.each do |macro| self.class.reflect_on_all_associations(macro).each do |a| differences[a.name.to_s] = singular_association_diff(self, other, a.name) if a.[:diff] differences.delete(a.[:foreign_key] || "#{a.name}_id") end end # has_many and habtm associations ActiveRecord::Acts::Diffable::PLURAL_MACROS.each do |macro| self.class.reflect_on_all_associations(macro).each do |a| differences[a.name.to_s] = plural_association_diff(self, other, a.name, a.[:diff_key]) if a.[:diff_key] end end # manually defined diffs self.class.manual_diff_definitions.each{|d_name, d_props| if d_props[:diff_key] differences[d_name] = plural_association_diff(self, other, d_props[:eval], d_props[:diff_key]) else differences[d_name] = singular_association_diff(self, other, d_props[:eval]) end } remove_unchanged_entries differences end |
#generate_diff_hash(left, right, *ignore) ⇒ Object
Accepts a left & right hash, and an array of keys to ignore, returns a hash of the differences.
This here is the meat & potatoes!
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 |
# File 'lib/acts_as_diffable.rb', line 173 def generate_diff_hash(left, right, *ignore) case [left.blank?, right.blank?] when [false, true] # the represented object was deleted { '_delete' => true } # inspired by nested_attributes when [true, false] # the represented object was added (ignore + %w(created_at updated_at)).each{|k| right.delete(k.to_s) } return right # just return the attributes to add when [false, false] # the represented object changed # generate the attribute diffs from each side and # merge them together as attribute => [left_value, right_value] if left == right return nil else diff_hash = left.diff(right).merge(right.diff(left)){|k, lv, rv| [lv, rv] } # remove any ignored attributes ignore.each {|k| diff_hash.delete(k.to_s) } # compress created_at/updated_at duplication diff_hash.delete('updated_at') if diff_hash['created_at'] == diff_hash['updated_at'] remove_unchanged_entries diff_hash end end end |
#keyify(keyset) ⇒ Object
reduce the key out of an array to a single string if only one element
118 119 120 121 122 123 124 125 |
# File 'lib/acts_as_diffable.rb', line 118 def keyify(keyset) case keyset.size when 1 keyset[0] else keyset end end |
#plural_association_diff(left_parent, right_parent, association, key_pattern) ⇒ Object
Helper for processing a collection of associated objects, such as has_many (or habtm) association.
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 |
# File 'lib/acts_as_diffable.rb', line 90 def plural_association_diff(left_parent, right_parent, association, key_pattern) key_pattern = Array(key_pattern) association = association.to_s #instance_eval doesn't like symbols. What'ev. left_association_set = Array(left_parent.instance_eval(association)) right_associaton_set = Array(right_parent.instance_eval(association)) # construct a set of values (key_set) from the attributes defined in key_pattern key_sets = ( left_association_set.collect{|i| key_pattern.collect{|k| i.send(k) } } + right_associaton_set.collect{|i| key_pattern.collect{|k| i.send(k) } } ).uniq # for each key_set, compare instances in each collection diff_set = {} key_sets.each do |key_set| conditions = {} key_pattern.each_with_index{|k, i| conditions[k] = key_set[i] } left_instance = left_association_set.find{|i| conditions.collect{|cf,cv| i.send(cf) == cv}.all? } right_instance = right_associaton_set.find{|i| conditions.collect{|cf,cv| i.send(cf) == cv}.all? } diff_set[keyify(key_set)] = attributes_diff(left_instance, right_instance, association_ids(left_parent) ) end # clean up unchanged pairs remove_unchanged_entries diff_set end |
#remove_unchanged_entries(diff_hash) ⇒ Object
Helper for thinning the herd
159 160 161 162 163 164 165 166 167 |
# File 'lib/acts_as_diffable.rb', line 159 def remove_unchanged_entries(diff_hash) return nil if !diff_hash diff_hash.delete_if{|k,v| v.nil? } if diff_hash.empty? return nil else return diff_hash end end |
#singular_association_diff(left_parent, right_parent, association) ⇒ Object
Helper for processing a single associated object, such as has_one (or belongs_to) associations.
80 81 82 83 84 85 86 |
# File 'lib/acts_as_diffable.rb', line 80 def singular_association_diff(left_parent, right_parent, association) association = association.to_s #instance_eval doesn't like symbols. What'ev. attributes_diff( left_parent.instance_eval(association), right_parent.instance_eval(association), association_ids(left_parent) ) end |
#timed_log(start_time, msg) ⇒ Object
74 75 76 |
# File 'lib/acts_as_diffable.rb', line 74 def timed_log(start_time, msg) puts "%04.2fs %s" % [(Time.now - start_time), msg] end |