xibdiff 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.txt +1 -0
- data/Rakefile +5 -0
- data/lib/XibDiffLogger.rb +25 -0
- data/lib/XibDiffObjectAdditions.rb +83 -0
- data/lib/XibDiffObjectComparison.rb +91 -0
- data/lib/XibDiffXibComparison.rb +192 -0
- metadata +69 -0
data/README.txt
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
xibdiff - a tool for diffing XIB/NIB files created in Interface Builder
|
data/Rakefile
ADDED
@@ -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
|
+
|