moduleXcodeproj# Computes the recursive diff of Hashes, Array and other objects.## Useful to compare two projects. Inspired from# 'active_support/core_ext/hash/diff'.## @example# h1 = { :common => 'value', :changed => 'v1' }# h2 = { :common => 'value', :changed => 'v2', :addition => 'new_value' }# h1.recursive_diff(h2) == {# :changed => {# :self => 'v1',# :other => 'v2'# },# :addition => {# :self => nil,# :other => 'new_value'# }# } #=> true####moduleDiffer# Computes the recursive difference of two given values.## @param [Object] value_1# The first value to compare.## @param [Object] value_2# The second value to compare.## @param [Object] key_1# The key for the diff of value_1.## @param [Object] key_2# The key for the diff of value_2.## @param [Object] id_key# The key used to identify correspondent hashes in an array.## @return [Hash] The diff# @return [Nil] if the given values are equal.#defself.diff(value_1,value_2,options={})options[:key_1]||='value_1'options[:key_2]||='value_2'options[:id_key]||=nilmethod=ifvalue_1.class==value_2.classcasevalue_1whenHashthen:hash_diffwhenArraythen:array_diffelse:generic_diffendelse:generic_diffendsend(method,value_1,value_2,options)end# Optimized for reducing the noise from the tree hash of projects#defself.project_diff(project_1,project_2,key_1='project_1',key_2='project_2')project_1=project_1.to_tree_hashunlessproject_1.is_a?(Hash)project_2=project_2.to_tree_hashunlessproject_2.is_a?(Hash)options={:key_1=>key_1,:key_2=>key_2,:id_key=>'displayName',}diff(project_1,project_2,options)end#-------------------------------------------------------------------------#public# @!group Type specific handlers# Computes the recursive difference of two hashes.## @see diff#defself.hash_diff(value_1,value_2,options)ensure_class(value_1,Hash)ensure_class(value_2,Hash)returnnilifvalue_1==value_2result={}all_keys=(value_1.keys+value_2.keys).uniqall_keys.eachdo|key|key_value_1=value_1[key]key_value_2=value_2[key]diff=diff(key_value_1,key_value_2,options)ifdiffresult[key]=diffifdiffendendifresult.empty?nilelseresultendend# Returns the recursive diff of two arrays.## @see diff#defself.array_diff(value_1,value_2,options)ensure_class(value_1,Array)ensure_class(value_2,Array)returnnilifvalue_1==value_2new_objects_value_1=(value_1-value_2)new_objects_value_2=(value_2-value_1)returnnilifvalue_1.empty?&&value_2.empty?matched_diff={}ifid_key=options[:id_key]matched_value_1=[]matched_value_2=[]new_objects_value_1.eachdo|entry_value_1|ifentry_value_1.is_a?(Hash)id_value=entry_value_1[id_key]entry_value_2=new_objects_value_2.finddo|entry|entry[id_key]==id_valueendifentry_value_2matched_value_1<<entry_value_1matched_value_2<<entry_value_2diff=diff(entry_value_1,entry_value_2,options)matched_diff[id_value]=diffifdiffendendendnew_objects_value_1-=matched_value_1new_objects_value_2-=matched_value_2endifnew_objects_value_1.empty?&&new_objects_value_2.empty?ifmatched_diff.empty?nilelsematched_diffendelseresult={}result[options[:key_1]]=new_objects_value_1unlessnew_objects_value_1.empty?result[options[:key_2]]=new_objects_value_2unlessnew_objects_value_2.empty?result[:diff]=matched_diffunlessmatched_diff.empty?resultendend# Returns the diff of two generic objects.## @see diff#defself.generic_diff(value_1,value_2,options)returnnilifvalue_1==value_2{options[:key_1]=>value_1,options[:key_2]=>value_2,}end#-------------------------------------------------------------------------#public# @!group Cleaning# Returns a copy of the hash where the given key is removed recursively.## @param [Hash] hash# The hash to clean## @param [Object] key# The key to remove.## @return [Hash] A copy of the hash without the key.#defself.clean_hash(hash,key)new_hash=hash.dupself.clean_hash!(new_hash,key)new_hashend# Recursively cleans a key from the given hash.## @param [Hash] hash# The hash to clean## @param [Object] key# The key to remove.## @return [void]#defself.clean_hash!(hash,key)hash.delete(key)hash.eachdo|_,value|casevaluewhenHashclean_hash!(value,key)whenArrayvalue.each{|entry|clean_hash!(entry,key)ifentry.is_a?(Hash)}endendend#-------------------------------------------------------------------------#private# @! Helpers# Ensures that the given object belongs to the given class.## @param [Object] object# The object to check.## @param [Class] klass# the expected class of the object.## @raise If the object doesn't belong to the given class.## @return [void]#defself.ensure_class(object,klass)raise"Wrong type `#{object.inspect}`"unlessobject.is_a?(klass)end#-------------------------------------------------------------------------#endend