xibdiff 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1 @@
1
+ xibdiff - a tool for diffing XIB/NIB files created in Interface Builder
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+ #$:.unshift(File.expand_path(File.join(File.dirname(__FILE__), 'lib')))
3
+ require 'rubygems'
4
+
5
+ require 'xibdiff.gemspec'
@@ -0,0 +1,25 @@
1
+ class XibDiffLogger
2
+ @@obj_path = []
3
+
4
+ @@separator = '/'
5
+
6
+ def self.obj_path
7
+ @@obj_path.join(@@separator)
8
+ end
9
+
10
+ def self.msg(msg)
11
+ "#{obj_path}: #{msg}"
12
+ end
13
+
14
+ def self.log(msg)
15
+ puts msg(msg)
16
+ end
17
+
18
+ def self.push(e)
19
+ @@obj_path << e
20
+ end
21
+
22
+ def self.pop
23
+ @@obj_path.pop
24
+ end
25
+ end
@@ -0,0 +1,83 @@
1
+ module ArrayPrettyPrint
2
+ def to_pretty_str(indent,var=nil)
3
+ res = ""
4
+ self.each_with_index do |e,i|
5
+ preamble = "#{i} = "
6
+ res += preamble
7
+ if e.respond_to? 'to_pretty_str'
8
+ res += e.to_pretty_str(indent + " " * preamble.length,var)
9
+ else
10
+ res += e.to_s
11
+ end
12
+ res += "\n" + indent
13
+ end
14
+ res
15
+ end
16
+ end
17
+
18
+ module HashPrettyPrint
19
+ def to_pretty_str(indent,var=nil)
20
+ res = ""
21
+ each_pair do |k,v|
22
+ # ignore certain keys
23
+ next if var and var[:ignore_keys] and var[:ignore_keys].include? k
24
+
25
+ preamble = "#{k} = "
26
+ res += preamble
27
+ if v.respond_to? 'to_pretty_str'
28
+ res += v.to_pretty_str(indent + " " * preamble.length,var)
29
+ else
30
+ res += v.to_s
31
+ end
32
+ res += "\n" + indent
33
+ end
34
+ res
35
+ end
36
+ end
37
+
38
+ module DistanceTo
39
+ def distance_to(other,var=nil)
40
+ d = 0 # assume they're equal
41
+ if self.class != other.class
42
+ d += 1000
43
+ elsif self.class == Array
44
+ if self.length != other.length
45
+ d += 100
46
+ else
47
+ self.each_with_index do |e,i|
48
+ d += e.distance_to(other[i],var)
49
+ end
50
+ end
51
+ elsif self.class == Hash
52
+ if self.keys.length != other.keys.length
53
+ d += 50
54
+ elsif self.keys != other.keys
55
+ d += 50
56
+ else
57
+ self.each_pair do |k,v|
58
+ # ignore certain keys
59
+ next if var and var[:ignore_keys] and var[:ignore_keys].include? k
60
+
61
+ d += v.distance_to(other[k],var)
62
+ end
63
+ end
64
+ else
65
+ # At this point, self and other should be of the same
66
+ # class, and not an Array nor a Hash
67
+ d += 1 if self.eql? other
68
+ end
69
+ d
70
+ end
71
+ end
72
+
73
+ Object.class_eval do
74
+ include DistanceTo
75
+ end
76
+
77
+ Array.class_eval do
78
+ include ArrayPrettyPrint
79
+ end
80
+
81
+ Hash.class_eval do
82
+ include HashPrettyPrint
83
+ end
@@ -0,0 +1,91 @@
1
+ module XibDiffObjectComparison
2
+ def compare_generic(a,b,var=nil)
3
+ if a.class != b.class
4
+ XibDiffLogger.log "items have different classes: a:#{a.class} b:#{b.class}"
5
+ elsif a.class == Hash
6
+ compare_generic_dicts(a,b,var)
7
+ elsif a.class == Array
8
+ compare_generic_arrays(a,b,var)
9
+ else
10
+ XibDiffLogger.log "items differ: a:#{a} b:#{b}" unless a == b
11
+ end
12
+ end
13
+
14
+ def compare_generic_arrays(a,b,var=nil)
15
+ a_idxs = (0..a.length-1).to_a
16
+ b_idxs = (0..b.length-1).to_a
17
+
18
+ a_idxs.each do |a_idx|
19
+ obj_name = a_idx.to_s
20
+ obj_name += ":#{a[a_idx][var[:name_key]]}" if var[:name_key] and not a[a_idx][var[:name_key]].nil?
21
+ XibDiffLogger.push obj_name
22
+ min_dist = 0
23
+ best_match = -1
24
+
25
+ b_idxs.each do |b_idx|
26
+ dist = a[a_idx].distance_to(b[b_idx],var)
27
+ if dist == 0 # a[a_idx] == b[b_idx]
28
+ min_dist = 0
29
+ best_match = b_idx
30
+ break
31
+ elsif best_match == -1 || dist < min_dist
32
+ best_match = b_idx
33
+ min_dist = dist
34
+ end
35
+ end
36
+
37
+ if best_match == -1
38
+ XibDiffLogger.log "removed"
39
+ else
40
+ b_idxs.delete best_match
41
+
42
+ if a_idx != best_match
43
+ XibDiffLogger.log "moved to position #{best_match}"
44
+ end
45
+
46
+ if min_dist > 0
47
+ compare_generic(a[a_idx],b[best_match],var)
48
+ end
49
+ end
50
+ XibDiffLogger.pop
51
+ end
52
+
53
+ b_idxs.each do |b_idx|
54
+ preamble = "added => "
55
+ XibDiffLogger.log "#{preamble}#{b[b_idx].to_pretty_str(" " * XibDiffLogger.XibDiffLogger.msg(preamble).length,var)}"
56
+ end
57
+ end
58
+
59
+ def compare_generic_dicts(a,b,var=nil)
60
+ # Pre: a.class == Hash, b.class == Hash
61
+ in_a_and_b = []
62
+ # for each key on dict a
63
+ a.each_pair do |k,v|
64
+ # skip items on ignored list
65
+ next if var and var[:ignore_keys] and var[:ignore_keys].include? k
66
+
67
+ # check if it's in b
68
+ if b[k].nil?
69
+ XibDiffLogger.log "removed '#{k}'"
70
+ else
71
+ in_a_and_b << b[k] # add to intersection list
72
+
73
+ XibDiffLogger.push(k)
74
+ compare_generic(v,b[k],var) # recursive compare
75
+ XibDiffLogger.pop
76
+ end
77
+ end
78
+
79
+ # check if missing key in b
80
+ b.each_pair do |k,v|
81
+ # skip items on ignored list
82
+ next if var and var[:ignore_keys] and var[:ignore_keys].include? k
83
+
84
+ if !in_a_and_b.include? v
85
+ preamble = "added '#{k}' => "
86
+ XibDiffLogger.log "#{preamble}#{v.to_pretty_str(" " * XibDiffLogger.msg(preamble).length,var)}" if v.class == Hash
87
+ XibDiffLogger.log "#{preamble}#{v}#{" " * XibDiffLogger.msg(preamble).length}" if v.class != Hash
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,192 @@
1
+ require 'XibDiffObjectComparison'
2
+
3
+ include XibDiffObjectComparison
4
+
5
+ module XibDiffXibComparison
6
+ def compare_classes(*c)
7
+ XibDiffLogger.push(c[0]['class'])
8
+ compare_generic( c[0], c[1], nil )
9
+ XibDiffLogger.pop
10
+ end
11
+
12
+ def compare_class_dicts(*class_dict)
13
+ in_a_and_b = []
14
+ class_dict[0].each_pair do |key,value|
15
+ # search for class 'key' in the second hash
16
+ if class_dict[1][key].nil?
17
+ XibDiffLogger.push key
18
+ XibDiffLogger.log "deleted."
19
+ XibDiffLogger.pop
20
+ else
21
+ in_a_and_b << key
22
+ compare_classes(value, class_dict[1][key])
23
+ end
24
+ end
25
+
26
+ class_dict[1].each_pair do |k,v|
27
+ if !(in_a_and_b.include? k)
28
+ XibDiffLogger.push k
29
+ preamble = "added => "
30
+ XibDiffLogger.log "#{preamble}#{v.to_pretty_str(" " * XibDiffLogger.msg(preamble).length)}"
31
+ XibDiffLogger.pop
32
+ end
33
+ end
34
+ end
35
+
36
+ def compare_hierarchy_arrays(*hierarchy_array)
37
+ in_a_and_b = []
38
+ hierarchy_array[0].each do |item|
39
+ XibDiffLogger.push item['label']
40
+ # search for object named the same in the other dict
41
+ hierarchy_array[1].each do |other_item|
42
+ if other_item['label'] == item['label']
43
+ in_a_and_b << item['label']
44
+
45
+ # now compare the two
46
+ compare_generic(item, other_item, :name_key => 'label', :ignore_keys => ['object-id'])
47
+ end
48
+ end
49
+
50
+ # Check if not found
51
+ if in_a_and_b.last != item['label']
52
+ XibDiffLogger.log "deleted."
53
+ end
54
+ XibDiffLogger.pop
55
+ end
56
+
57
+ # check for the missing ones
58
+ hierarchy_array[1].each do |item|
59
+ if !(in_a_and_b.include? item['label'])
60
+ XibDiffLogger.push item['label']
61
+ preamble = "added => "
62
+ XibDiffLogger.log "#{preamble}#{item.to_pretty_str(" " * XibDiffLogger.msg(preamble).length, :ignore_keys => ['object-id'])}"
63
+ XibDiffLogger.pop
64
+ end
65
+ end
66
+ end
67
+
68
+ def dict_with_path_to_object_id(hierarchy_array)
69
+ dict = {}
70
+ stack = [ { :obj => hierarchy_array, :path => [] } ]
71
+
72
+ while not stack.empty?
73
+ top = stack.pop
74
+ obj = top[:obj]
75
+ path = top[:path]
76
+ if obj.class == Array
77
+ obj.each_with_index do |e,i|
78
+ obj_name = i.to_s
79
+ obj_name += ":#{e['label']}" if e['label']
80
+ stack << { :obj => e, :path => (Array.new(path) << obj_name) }
81
+ end
82
+ elsif obj.class == Hash
83
+ obj_path = path.join('/')
84
+ obj_path += ":#{obj['label']}" if obj['label']
85
+ dict[obj_path] = obj['object-id'] if obj['object-id']
86
+ obj.each_pair do |k,v|
87
+ stack << { :obj => v, :path => Array.new(path) } if v.class == Array and k == 'children'
88
+ stack << { :obj => v, :path => (Array.new(path) << k) } if k != 'children' and ( v.class == Array or v.class == Hash )
89
+ end
90
+ end
91
+ end
92
+
93
+ dict
94
+ end
95
+
96
+ def compare_object_arrays(*params)
97
+ a_dict = params[0]
98
+ a_objs = params[1]
99
+ b_dict = params[2]
100
+ b_objs = params[3]
101
+
102
+ a_keys = a_dict.keys.sort
103
+ b_keys = b_dict.keys.sort
104
+
105
+ a_idx = b_idx = 0
106
+ while a_idx < a_keys.length or b_idx < b_keys.length
107
+ a_key = a_keys[a_idx]
108
+ b_key = b_keys[b_idx]
109
+
110
+ if ( a_key and b_key and (a_key < b_key) ) or ( a_key and b_key.nil? )
111
+ # Removed object
112
+ XibDiffLogger.push a_key
113
+ XibDiffLogger.log "deleted."
114
+ XibDiffLogger.pop
115
+
116
+ a_idx += 1
117
+ elsif ( a_key and b_key and (a_key > b_key) ) or ( a_key.nil? and b_key )
118
+ # Added object
119
+ XibDiffLogger.push b_key
120
+ preamble = "added => "
121
+ XibDiffLogger.log "#{preamble}#{b_objs[b_dict[b_key].to_s].to_pretty_str(" " * XibDiffLogger.msg(preamble).length )}"
122
+ XibDiffLogger.pop
123
+
124
+ b_idx += 1
125
+ else # a_key and b_key and a_key == b_key
126
+ XibDiffLogger.push a_key # or b_key, doesn't matter
127
+ compare_generic(a_objs[a_dict[a_key].to_s], b_objs[b_dict[b_key].to_s])
128
+ XibDiffLogger.pop
129
+
130
+ a_idx += 1
131
+ b_idx += 1
132
+ end
133
+ end
134
+ end
135
+
136
+ def connection_map_for_connection_array(dict, conns)
137
+ map = {}
138
+ conns.each_pair do |k,v|
139
+ key = "#{dict[v['source-id']]} => #{dict[v['destination-id']]} #{v['type']}"
140
+ key += " #{v['label']}" if v['type'] == 'IBCocoaOutletConnection' or v['type'] == 'IBCocoaActionConnection'
141
+ key += " #{v['binding']}:#{v['keypath']}" if v['type'] == 'IBBindingConnection'
142
+ value = k
143
+
144
+ map[key] = value
145
+ end
146
+ map
147
+ end
148
+
149
+ def compare_connection_arrays(*params)
150
+ a_dict = params[0]
151
+ a_conns = params[1]
152
+ b_dict = params[2]
153
+ b_conns = params[3]
154
+
155
+ # build connection map array
156
+ a_map = connection_map_for_connection_array(a_dict, a_conns)
157
+ b_map = connection_map_for_connection_array(b_dict, b_conns)
158
+
159
+ a_keys = a_map.keys.sort
160
+ b_keys = b_map.keys.sort
161
+
162
+ a_idx = b_idx = 0
163
+ while a_idx < a_keys.length or b_idx < b_keys.length
164
+ a_key = a_keys[a_idx]
165
+ b_key = b_keys[b_idx]
166
+
167
+ if ( a_key and b_key and (a_key < b_key) ) or ( a_key and b_key.nil? )
168
+ # Removed object
169
+ XibDiffLogger.push a_key
170
+ XibDiffLogger.log "deleted."
171
+ XibDiffLogger.pop
172
+
173
+ a_idx += 1
174
+ elsif ( a_key and b_key and (a_key > b_key) ) or ( a_key.nil? and b_key )
175
+ # Added object
176
+ XibDiffLogger.push b_key
177
+ preamble = "added => "
178
+ XibDiffLogger.log "#{preamble}#{b_conns[b_map[b_key].to_s].to_pretty_str(" " * XibDiffLogger.msg(preamble).length )}"
179
+ XibDiffLogger.pop
180
+
181
+ b_idx += 1
182
+ else # a_key and b_key and a_key == b_key
183
+ XibDiffLogger.push a_key # or b_key, doesn't matter
184
+ compare_generic(a_conns[a_map[a_key].to_s], b_conns[b_map[b_key].to_s])
185
+ XibDiffLogger.pop
186
+
187
+ a_idx += 1
188
+ b_idx += 1
189
+ end
190
+ end
191
+ end
192
+ end
metadata ADDED
@@ -0,0 +1,69 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: xibdiff
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 0
9
+ version: 0.1.0
10
+ platform: ruby
11
+ authors:
12
+ - Igor Sales
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-07-04 00:00:00 -04:00
18
+ default_executable:
19
+ dependencies: []
20
+
21
+ description: xibdiff is a ruby command-line tool to diff two nib or xib files.
22
+ email:
23
+ - self@igorsales.ca
24
+ executables: []
25
+
26
+ extensions: []
27
+
28
+ extra_rdoc_files:
29
+ - README.txt
30
+ files:
31
+ - README.txt
32
+ - Rakefile
33
+ - lib/XibDiffLogger.rb
34
+ - lib/XibDiffObjectAdditions.rb
35
+ - lib/XibDiffObjectComparison.rb
36
+ - lib/XibDiffXibComparison.rb
37
+ has_rdoc: true
38
+ homepage: http://github.com/igorsales/xibdiff
39
+ licenses: []
40
+
41
+ post_install_message:
42
+ rdoc_options:
43
+ - --main
44
+ - README.txt
45
+ require_paths:
46
+ - lib
47
+ required_ruby_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ segments:
52
+ - 0
53
+ version: "0"
54
+ required_rubygems_version: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ segments:
59
+ - 0
60
+ version: "0"
61
+ requirements: []
62
+
63
+ rubyforge_project: xibdiff
64
+ rubygems_version: 1.3.6
65
+ signing_key:
66
+ specification_version: 3
67
+ summary: XIB/NIB diff tool for Mac OS X
68
+ test_files: []
69
+