summaryrefslogtreecommitdiff
path: root/vendor/oojs/oojs-ui/bin/doccomparer.rb
blob: cd3623dfdfda6a76829b45b03303a2f1f33a2f00 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
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
157
158
159
160
161
162
163
164
165
require 'pp'
require_relative 'docparser'

# convert [ {name: 'foo'}, … ] to { foo: {name: 'foo'}, … }
def reindex arg
	if arg.is_a?(Array) && arg.all?{|v| v.is_a? Hash }
		Hash[ arg.map{|v| [ v[:name], reindex(v) ] } ]
	elsif arg.is_a? Hash
		arg.each_pair{|k, v| arg[k] = reindex(v) }
	else
		arg
	end
end

def indent text, tabs
	text == '' ? text : text.gsub(/^/, '  ' * tabs)
end

# whitespace-insensitive strings
def canonicalize value
	if value.is_a? String
		value.strip.gsub(/\s+/, ' ')
	elsif value.is_a? Array
		value.map{|v| canonicalize v }
	elsif value.is_a? Hash
		value.each_pair{|k, v| value[k] = canonicalize v }
	else
		value
	end
end

def sanitize_description text
	cleanup_class_name(text)
		.gsub('null and undefined', 'null')
		.gsub('undefined and null', 'null')
		.gsub('array()', '[]')
		.gsub('jQuery|string|Function', 'string')
		.gsub('jQuery', 'Tag')
		.gsub('string|Function', 'string')
		.gsub('object', 'array')
		.gsub(/#(\w+)/, '\1()')
		.gsub(/Object\.<.+?>/, 'array')
end

def smart_compare_process val, type
	val[:description] = sanitize_description val[:description]

	case type
	when :class
		val = val.dup
		val[:mixins].delete 'OO.EventEmitter' # JS only
		val[:mixins].delete 'PendingElement' # JS only
		val.delete :parent if val[:parent] == 'ElementMixin' # PHP only
		val.delete :methods
		val.delete :properties
		val.delete :events

	when :method
		if val[:name] == '#constructor'
			val[:params].delete 'element' # PHP only
		end
		val[:config].each_pair{|_k, v|
			default = v.delete :default
			v[:description] << " (default: #{default})" if default
			v[:description] = sanitize_description v[:description]
			v[:type] = sanitize_description v[:type]
		}
		val[:params].each_pair{|_k, v|
			default = v.delete :default
			v[:description] << " (default: #{default})" if default
			v[:description] = sanitize_description v[:description]
			v[:type] = sanitize_description v[:type]
		}
		if val[:return]
			val[:return][:description] = sanitize_description val[:return][:description]
			val[:return][:type] = sanitize_description val[:return][:type]
		end

	when :property
		val[:description] = sanitize_description val[:description]
		val[:type] = sanitize_description val[:type]

	end
	val
end

def smart_compare a, b, a_name, b_name, type
	a = smart_compare_process a, type
	b = smart_compare_process b, type
	compare_hash a, b, a_name, b_name
end

def smart_compare_methods a, b, a_name, b_name
	smart_compare a, b, a_name, b_name, :method
end

def smart_compare_properties a, b, a_name, b_name
	smart_compare a, b, a_name, b_name, :property
end

def compare_hash a, b, a_name, b_name, nested=:compare_hash
	keys = (a ? a.keys : []) + (b ? b.keys : [])
	out = keys.to_a.sort.uniq.map do |key|
		a_val = a ? canonicalize(a[key]) : nil
		b_val = b ? canonicalize(b[key]) : nil

		if [a_val, b_val] == [{}, []] || [a_val, b_val] == [[], {}]
			a_val, b_val = {}, {}
		end

		if a_val.is_a?(Hash) && b_val.is_a?(Hash)
			comparison_result = indent method(nested).call(a_val, b_val, a_name, b_name), 2
			if comparison_result.strip == ''
				"#{key}: match" if $VERBOSE
			else
				"#{key}: MISMATCH\n#{comparison_result}"
			end
		else
			if a_val == b_val
				"#{key}: match" if $VERBOSE
			elsif a_val.nil?
				"#{key}: #{a_name} missing"
			elsif b_val.nil?
				"#{key}: #{b_name} missing"
			else
				"#{key}: MISMATCH\n  #{a_name}: #{a_val.inspect}\n  #{b_name}: #{b_val.inspect}"
			end
		end
	end
	out.compact.join "\n"
end

if ARGV.empty? || ARGV == ['-h'] || ARGV == ['--help']
	$stderr.puts "usage: ruby [-v] #{$PROGRAM_NAME} <dirA> <dirB> <nameA> <nameB>"
	$stderr.puts "       ruby #{$PROGRAM_NAME} src php JS PHP > compare.txt"
else
	dir_a, dir_b, name_a, name_b = ARGV

	js = parse_any_path dir_a
	php = parse_any_path dir_b

	js = reindex js
	php = reindex php

	(js.keys + php.keys).sort.uniq.each do |class_name|
		where = [js.key?(class_name) ? name_a : nil, php.key?(class_name) ? name_b : nil].compact
		puts "\n#{class_name}: #{where.join '/'}" if $VERBOSE || where.length == 2

		if where.length == 2
			data = {
				'Basic:' =>
					smart_compare(js[class_name], php[class_name], name_a, name_b, :class),
				'Methods:' =>
					compare_hash(js[class_name][:methods], php[class_name][:methods], name_a, name_b, :smart_compare_methods),
				'Properties:' =>
					compare_hash(js[class_name][:properties], php[class_name][:properties], name_a, name_b, :smart_compare_properties),
			}
			data = data
				.select{|_k, v| v != ''}
				.map{|k, v| "#{k}\n#{indent v, 2}" }
				.join("\n")
			puts indent data, 2
		end
	end
end