xibdiff 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
|