Module: Xcodeproj::Differ

Defined in:
lib/xcodeproj/differ.rb

Overview

Computes the recursive diff of Hashes, Array and other objects.

Useful to compare two projects. Inspired from 'active_support/core_ext/hash/diff'.

Examples:

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

Type specific handlers collapse

Cleaning collapse

Class Method Summary collapse

Class Method Details

.array_diff(value_1, value_2, options) ⇒ Object

Returns the recursive diff of two arrays.

See Also:


111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
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/xcodeproj/differ.rb', line 111

def self.array_diff(value_1, value_2, options)
  ensure_class(value_1, Array)
  ensure_class(value_2, Array)
  return nil if value_1 == value_2

  new_objects_value_1 = array_non_unique_diff(value_1, value_2)
  new_objects_value_2 = array_non_unique_diff(value_2, value_1)
  return nil if value_1.empty? && value_2.empty?

  matched_diff = {}
  if id_key = options[:id_key]
    matched_value_1 = []
    matched_value_2 = []
    new_objects_value_1.each do |entry_value_1|
      if entry_value_1.is_a?(Hash)
        id_value = entry_value_1[id_key]
        entry_value_2 = new_objects_value_2.find do |entry|
          entry[id_key] == id_value
        end
        if entry_value_2
          matched_value_1 << entry_value_1
          matched_value_2 << entry_value_2
          diff = diff(entry_value_1, entry_value_2, options)
          matched_diff[id_value] = diff if diff
        end
      end
    end

    new_objects_value_1 -= matched_value_1
    new_objects_value_2 -= matched_value_2
  end

  if new_objects_value_1.empty? && new_objects_value_2.empty?
    if matched_diff.empty?
      nil
    else
      matched_diff
    end
  else
    result = {}
    result[options[:key_1]] = new_objects_value_1 unless new_objects_value_1.empty?
    result[options[:key_2]] = new_objects_value_2 unless new_objects_value_2.empty?
    result[:diff] = matched_diff unless matched_diff.empty?
    result
  end
end

.clean_hash(hash, key) ⇒ Hash

Returns a copy of the hash where the given key is removed recursively.

Parameters:

  • hash (Hash)

    The hash to clean

  • key (Object)

    The key to remove.

Returns:

  • (Hash)

    A copy of the hash without the key.


187
188
189
190
191
# File 'lib/xcodeproj/differ.rb', line 187

def self.clean_hash(hash, key)
  new_hash = hash.dup
  self.clean_hash!(new_hash, key)
  new_hash
end

.clean_hash!(hash, key) ⇒ void

This method returns an undefined value.

Recursively cleans a key from the given hash.

Parameters:

  • hash (Hash)

    The hash to clean

  • key (Object)

    The key to remove.


203
204
205
206
207
208
209
210
211
212
213
# File 'lib/xcodeproj/differ.rb', line 203

def self.clean_hash!(hash, key)
  hash.delete(key)
  hash.each do |_, value|
    case value
    when Hash
      clean_hash!(value, key)
    when Array
      value.each { |entry| clean_hash!(entry, key) if entry.is_a?(Hash) }
    end
  end
end

.diff(value_1, value_2, options = {}) ⇒ Hash, Nil

Computes the recursive difference of two given values.

Parameters:

  • value_1 (Object)

    The first value to compare.

  • value_2 (Object)

    The second value to compare.

  • key_1 (Object)

    The key for the diff of value_1.

  • key_2 (Object)

    The key for the diff of value_2.

  • id_key (Object)

    The key used to identify correspondent hashes in an array.

Returns:

  • (Hash)

    The diff

  • (Nil)

    if the given values are equal.


45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/xcodeproj/differ.rb', line 45

def self.diff(value_1, value_2, options = {})
  options[:key_1] ||= 'value_1'
  options[:key_2] ||= 'value_2'
  options[:id_key] ||= nil

  method = if value_1.class == value_2.class
             case value_1
             when Hash  then :hash_diff
             when Array then :array_diff
             else :generic_diff
             end
           else
             :generic_diff
           end
  send(method, value_1, value_2, options)
end

.generic_diff(value_1, value_2, options) ⇒ Object

Returns the diff of two generic objects.

See Also:


162
163
164
165
166
167
168
169
# File 'lib/xcodeproj/differ.rb', line 162

def self.generic_diff(value_1, value_2, options)
  return nil if value_1 == value_2

  {
    options[:key_1] => value_1,
    options[:key_2] => value_2,
  }
end

.hash_diff(value_1, value_2, options) ⇒ Object

Computes the recursive difference of two hashes.

See Also:


85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/xcodeproj/differ.rb', line 85

def self.hash_diff(value_1, value_2, options)
  ensure_class(value_1, Hash)
  ensure_class(value_2, Hash)
  return nil if value_1 == value_2

  result = {}
  all_keys = (value_1.keys + value_2.keys).uniq
  all_keys.each do |key|
    key_value_1 = value_1[key]
    key_value_2 = value_2[key]
    diff = diff(key_value_1, key_value_2, options)
    if diff
      result[key] = diff if diff
    end
  end
  if result.empty?
    nil
  else
    result
  end
end

.project_diff(project_1, project_2, key_1 = 'project_1', key_2 = 'project_2') ⇒ Object

Optimized for reducing the noise from the tree hash of projects


64
65
66
67
68
69
70
71
72
73
# File 'lib/xcodeproj/differ.rb', line 64

def self.project_diff(project_1, project_2, key_1 = 'project_1', key_2 = 'project_2')
  project_1 = project_1.to_tree_hash unless project_1.is_a?(Hash)
  project_2 = project_2.to_tree_hash unless project_2.is_a?(Hash)
  options = {
    :key_1  => key_1,
    :key_2  => key_2,
    :id_key => 'displayName',
  }
  diff(project_1, project_2, options)
end