coopy 0.6.4.1 → 1.0.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.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.rspec +2 -0
- data/CHANGELOG.md +7 -0
- data/Gemfile +7 -0
- data/LICENSE.md +22 -0
- data/README.md +59 -0
- data/Rakefile +4 -6
- data/coopy.gemspec +26 -0
- data/lib/coopy.rb +32 -175
- data/lib/coopy/alignment.rb +260 -0
- data/lib/coopy/bag.rb +17 -0
- data/lib/coopy/cell_info.rb +24 -0
- data/lib/coopy/change_type.rb +10 -0
- data/lib/coopy/compare_flags.rb +62 -0
- data/lib/coopy/compare_table.rb +327 -0
- data/lib/coopy/coopy.rb +22 -0
- data/lib/coopy/cross_match.rb +10 -0
- data/lib/coopy/csv_table.rb +51 -0
- data/lib/coopy/diff_render.rb +307 -0
- data/lib/coopy/index.rb +73 -0
- data/lib/coopy/index_item.rb +17 -0
- data/lib/coopy/index_pair.rb +72 -0
- data/lib/coopy/mover.rb +123 -0
- data/lib/coopy/ordering.rb +27 -0
- data/lib/coopy/row.rb +9 -0
- data/lib/coopy/simple_cell.rb +15 -0
- data/lib/coopy/simple_table.rb +144 -0
- data/lib/coopy/simple_view.rb +36 -0
- data/lib/coopy/table.rb +44 -0
- data/lib/coopy/table_comparison_state.rb +33 -0
- data/lib/coopy/table_diff.rb +634 -0
- data/lib/coopy/table_text.rb +14 -0
- data/lib/coopy/table_view.rb +31 -0
- data/lib/coopy/unit.rb +53 -0
- data/lib/coopy/version.rb +3 -0
- data/lib/coopy/view.rb +34 -0
- data/spec/fixtures/bridges.html +10 -0
- data/spec/fixtures/bridges_diff.csv +8 -0
- data/spec/fixtures/bridges_new.csv +9 -0
- data/spec/fixtures/bridges_old.csv +9 -0
- data/spec/fixtures/planetary_bodies.html +22 -0
- data/spec/fixtures/planetary_bodies_diff.csv +19 -0
- data/spec/fixtures/planetary_bodies_new.csv +20 -0
- data/spec/fixtures/planetary_bodies_old.csv +19 -0
- data/spec/fixtures/quote_me.csv +10 -0
- data/spec/fixtures/quote_me2.csv +11 -0
- data/spec/integration/table_diff_spec.rb +57 -0
- data/spec/libs/compare_flags_spec.rb +40 -0
- data/spec/libs/coopy_spec.rb +14 -0
- data/spec/libs/ordering_spec.rb +28 -0
- data/spec/libs/unit_spec.rb +31 -0
- data/spec/spec_helper.rb +29 -0
- metadata +153 -46
- data/bin/sqlite_diff +0 -4
- data/bin/sqlite_patch +0 -4
- data/bin/sqlite_rediff +0 -4
- data/lib/coopy/dbi_sql_wrapper.rb +0 -89
- data/lib/coopy/diff_apply_sql.rb +0 -35
- data/lib/coopy/diff_columns.rb +0 -33
- data/lib/coopy/diff_output.rb +0 -21
- data/lib/coopy/diff_output_action.rb +0 -34
- data/lib/coopy/diff_output_group.rb +0 -40
- data/lib/coopy/diff_output_raw.rb +0 -17
- data/lib/coopy/diff_output_stats.rb +0 -45
- data/lib/coopy/diff_output_table.rb +0 -49
- data/lib/coopy/diff_output_tdiff.rb +0 -48
- data/lib/coopy/diff_parser.rb +0 -92
- data/lib/coopy/diff_render_csv.rb +0 -29
- data/lib/coopy/diff_render_html.rb +0 -74
- data/lib/coopy/diff_render_log.rb +0 -52
- data/lib/coopy/row_change.rb +0 -25
- data/lib/coopy/scraperwiki_sql_wrapper.rb +0 -8
- data/lib/coopy/scraperwiki_utils.rb +0 -23
- data/lib/coopy/sequel_sql_wrapper.rb +0 -73
- data/lib/coopy/sql_compare.rb +0 -222
- data/lib/coopy/sql_wrapper.rb +0 -34
- data/lib/coopy/sqlite_sql_wrapper.rb +0 -143
- data/test/test_coopy.rb +0 -126
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'csv'
|
2
|
+
|
3
|
+
module Coopy
|
4
|
+
class CsvTable
|
5
|
+
|
6
|
+
include Coopy::Table
|
7
|
+
|
8
|
+
def initialize(csv)
|
9
|
+
@csv = csv
|
10
|
+
@height = csv.size
|
11
|
+
@width = csv[0].size
|
12
|
+
end
|
13
|
+
|
14
|
+
def get_cell(x, y)
|
15
|
+
@csv[y][x]
|
16
|
+
end
|
17
|
+
|
18
|
+
def set_cell(x, y, cell)
|
19
|
+
@csv[y][x] = cell
|
20
|
+
end
|
21
|
+
|
22
|
+
def get_cell_view
|
23
|
+
Coopy::SimpleView.new
|
24
|
+
end
|
25
|
+
|
26
|
+
def is_resizable?
|
27
|
+
false
|
28
|
+
end
|
29
|
+
|
30
|
+
def resize(w, h)
|
31
|
+
raise NotImplementedError
|
32
|
+
end
|
33
|
+
|
34
|
+
def clear
|
35
|
+
@csv = []
|
36
|
+
end
|
37
|
+
|
38
|
+
def insert_or_delete_rows(fate, hfate)
|
39
|
+
raise NotImplementedError
|
40
|
+
end
|
41
|
+
|
42
|
+
def insert_or_delete_columns(fate, wfate)
|
43
|
+
raise NotImplementedError
|
44
|
+
end
|
45
|
+
|
46
|
+
def trim_blank
|
47
|
+
raise NotImplementedError
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,307 @@
|
|
1
|
+
module Coopy
|
2
|
+
class DiffRender
|
3
|
+
|
4
|
+
def initialize
|
5
|
+
@text_to_insert = []
|
6
|
+
@open = false
|
7
|
+
@pretty_arrows = true
|
8
|
+
end
|
9
|
+
|
10
|
+
def use_pretty_arrows(flag)
|
11
|
+
@pretty_arrows = flag
|
12
|
+
end
|
13
|
+
|
14
|
+
def insert(str)
|
15
|
+
@text_to_insert.push(str)
|
16
|
+
end
|
17
|
+
|
18
|
+
def begin_table()
|
19
|
+
insert("<table>\n")
|
20
|
+
end
|
21
|
+
|
22
|
+
def begin_row(mode)
|
23
|
+
@td_open = '<td'
|
24
|
+
@td_close = '</td>'
|
25
|
+
row_class = ""
|
26
|
+
if (mode=="header")
|
27
|
+
@td_open = "<th"
|
28
|
+
@td_close = "</th>"
|
29
|
+
else
|
30
|
+
row_class = mode
|
31
|
+
end
|
32
|
+
tr = "<tr>"
|
33
|
+
if (row_class!="")
|
34
|
+
tr = "<tr class=\"" + row_class + "\">"
|
35
|
+
end
|
36
|
+
insert(tr)
|
37
|
+
end
|
38
|
+
|
39
|
+
def insert_cell(txt, mode)
|
40
|
+
cell_decorate = ""
|
41
|
+
if (mode!="")
|
42
|
+
cell_decorate = " class=\"" + mode + "\""
|
43
|
+
end
|
44
|
+
insert(@td_open+cell_decorate+">")
|
45
|
+
insert(txt)
|
46
|
+
insert(@td_close)
|
47
|
+
end
|
48
|
+
|
49
|
+
def end_row()
|
50
|
+
insert("</tr>\n")
|
51
|
+
end
|
52
|
+
|
53
|
+
def end_table()
|
54
|
+
insert("</table>\n")
|
55
|
+
end
|
56
|
+
|
57
|
+
def html()
|
58
|
+
return @text_to_insert.join('')
|
59
|
+
end
|
60
|
+
|
61
|
+
def to_s()
|
62
|
+
return html()
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
def self.examine_cell(x, y, value, vcol, vrow, vcorner, cell)
|
67
|
+
cell.category = ""
|
68
|
+
cell.category_given_tr = ""
|
69
|
+
cell.separator = ""
|
70
|
+
cell.conflicted = false
|
71
|
+
cell.updated = false
|
72
|
+
cell.pvalue = cell.lvalue = cell.rvalue = nil
|
73
|
+
cell.value = value
|
74
|
+
cell.value = "" if (cell.value.nil?)
|
75
|
+
cell.pretty_value = cell.value
|
76
|
+
vrow = "" if (vrow.nil?)
|
77
|
+
vcol = "" if (vcol.nil?)
|
78
|
+
removed_column = false
|
79
|
+
if (vrow == ":")
|
80
|
+
cell.category = 'move'
|
81
|
+
end
|
82
|
+
if (vcol.index("+++"))
|
83
|
+
cell.category_given_tr = cell.category = 'add'
|
84
|
+
elsif (vcol.index("---"))
|
85
|
+
cell.category_given_tr = cell.category = 'remove'
|
86
|
+
removed_column = true
|
87
|
+
end
|
88
|
+
if (vrow == "!")
|
89
|
+
cell.category = 'spec'
|
90
|
+
elsif (vrow == "@@")
|
91
|
+
cell.category = 'header'
|
92
|
+
elsif (vrow == "+++")
|
93
|
+
if (!removed_column)
|
94
|
+
cell.category = 'add'
|
95
|
+
end
|
96
|
+
elsif (vrow == "---")
|
97
|
+
cell.category = "remove"
|
98
|
+
elsif (vrow.index("->"))
|
99
|
+
if (!removed_column)
|
100
|
+
tokens = vrow.split("!")
|
101
|
+
full = vrow
|
102
|
+
part = tokens[1]
|
103
|
+
part = full if (part.nil?)
|
104
|
+
if (cell.value.index(part))
|
105
|
+
cat = "modify"
|
106
|
+
div = part
|
107
|
+
# render with utf8 -> symbol
|
108
|
+
if (part!=full)
|
109
|
+
if (cell.value.index(full))
|
110
|
+
div = full
|
111
|
+
cat = "conflict"
|
112
|
+
cell.conflicted = true
|
113
|
+
end
|
114
|
+
end
|
115
|
+
cell.updated = true
|
116
|
+
cell.separator = div
|
117
|
+
tokens = cell.pretty_value.split(div)
|
118
|
+
pretty_tokens = tokens
|
119
|
+
if (tokens.length>=2)
|
120
|
+
pretty_tokens[0] = mark_spaces(tokens[0],tokens[1])
|
121
|
+
pretty_tokens[1] = mark_spaces(tokens[1],tokens[0])
|
122
|
+
end
|
123
|
+
if (tokens.length>=3)
|
124
|
+
ref = pretty_tokens[0]
|
125
|
+
pretty_tokens[0] = mark_spaces(ref,tokens[2])
|
126
|
+
pretty_tokens[2] = mark_spaces(tokens[2],ref)
|
127
|
+
end
|
128
|
+
if (tokens.length == 0)
|
129
|
+
pretty_tokens = ['','']
|
130
|
+
end
|
131
|
+
cell.pretty_value = pretty_tokens.join("→")
|
132
|
+
cell.category_given_tr = cell.category = cat
|
133
|
+
offset = cell.conflicted ? 1 : 0
|
134
|
+
cell.lvalue = tokens[offset]
|
135
|
+
cell.rvalue = tokens[offset+1]
|
136
|
+
cell.pvalue = tokens[0] if (cell.conflicted)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def self.mark_spaces(sl, sr)
|
143
|
+
return sl if (sl==sr)
|
144
|
+
return sl if (sl.nil? || sr.nil?)
|
145
|
+
slc = sl.gsub(" ","")
|
146
|
+
src = sr.gsub(" ","")
|
147
|
+
return sl if (slc!=src)
|
148
|
+
slo = ""
|
149
|
+
il = 0
|
150
|
+
ir = 0
|
151
|
+
while (il<sl.length)
|
152
|
+
cl = sl[il]
|
153
|
+
cr = ""
|
154
|
+
if (ir<sr.length)
|
155
|
+
cr = sr[ir]
|
156
|
+
end
|
157
|
+
if (cl==cr)
|
158
|
+
slo += cl
|
159
|
+
il+=1
|
160
|
+
ir+=1
|
161
|
+
elsif (cr==" ")
|
162
|
+
ir+=1
|
163
|
+
else
|
164
|
+
slo += " " # this is U+2423, open box
|
165
|
+
il+=1
|
166
|
+
end
|
167
|
+
end
|
168
|
+
return slo
|
169
|
+
end
|
170
|
+
|
171
|
+
def self.render_cell(tt, x, y)
|
172
|
+
cell = Coopy::CellInfo.new
|
173
|
+
corner = tt.get_cell_text(0,0)
|
174
|
+
off = (corner=="@:@") ? 1 : 0
|
175
|
+
|
176
|
+
examine_cell(x,
|
177
|
+
y,
|
178
|
+
tt.get_cell_text(x,y),
|
179
|
+
tt.get_cell_text(x,off),
|
180
|
+
tt.get_cell_text(off,y),
|
181
|
+
corner,
|
182
|
+
cell)
|
183
|
+
return cell
|
184
|
+
end
|
185
|
+
|
186
|
+
def render(rows)
|
187
|
+
return if (rows.width==0||rows.height==0)
|
188
|
+
render = self
|
189
|
+
render.begin_table()
|
190
|
+
change_row = -1
|
191
|
+
tt = Coopy::TableText.new(rows)
|
192
|
+
cell = CellInfo.new
|
193
|
+
corner = tt.get_cell_text(0,0)
|
194
|
+
off = (corner=="@:@") ? 1 : 0
|
195
|
+
if (off>0)
|
196
|
+
return if (rows.width<=1||rows.height<=1)
|
197
|
+
end
|
198
|
+
(0...rows.height).each do |row|
|
199
|
+
|
200
|
+
@open = false
|
201
|
+
|
202
|
+
txt = tt.get_cell_text(off,row)
|
203
|
+
txt = "" if (txt.nil?)
|
204
|
+
DiffRender.examine_cell(0,row,txt,"",txt,corner,cell)
|
205
|
+
row_mode = cell.category
|
206
|
+
if (row_mode == "spec")
|
207
|
+
change_row = row
|
208
|
+
end
|
209
|
+
|
210
|
+
render.begin_row(row_mode)
|
211
|
+
|
212
|
+
(0...rows.width).each do |c|
|
213
|
+
DiffRender.examine_cell(c,
|
214
|
+
row,
|
215
|
+
tt.get_cell_text(c,row),
|
216
|
+
(change_row>=0)?tt.get_cell_text(c,change_row):"",
|
217
|
+
txt,
|
218
|
+
corner,
|
219
|
+
cell)
|
220
|
+
render.insert_cell(@pretty_arrows ? cell.pretty_value : cell.value,
|
221
|
+
cell.category_given_tr)
|
222
|
+
end
|
223
|
+
render.end_row()
|
224
|
+
end
|
225
|
+
render.end_table()
|
226
|
+
end
|
227
|
+
|
228
|
+
def sample_css()
|
229
|
+
return ".highlighter .add
|
230
|
+
background-color: #7fff7f
|
231
|
+
end
|
232
|
+
|
233
|
+
.highlighter .remove
|
234
|
+
background-color: #ff7f7f
|
235
|
+
end
|
236
|
+
|
237
|
+
.highlighter td.modify
|
238
|
+
background-color: #7f7fff
|
239
|
+
end
|
240
|
+
|
241
|
+
.highlighter td.conflict
|
242
|
+
background-color: #f00
|
243
|
+
end
|
244
|
+
|
245
|
+
.highlighter .spec
|
246
|
+
background-color: #aaa
|
247
|
+
end
|
248
|
+
|
249
|
+
.highlighter .move
|
250
|
+
background-color: #ffa
|
251
|
+
end
|
252
|
+
|
253
|
+
.highlighter .nil
|
254
|
+
color: #888
|
255
|
+
end
|
256
|
+
|
257
|
+
.highlighter table
|
258
|
+
border-collapse:collapse
|
259
|
+
end
|
260
|
+
|
261
|
+
.highlighter td, .highlighter th
|
262
|
+
border: 1px solid #2D4068
|
263
|
+
padding: 3px 7px 2px
|
264
|
+
end
|
265
|
+
|
266
|
+
.highlighter th, .highlighter .header
|
267
|
+
background-color: #aaf
|
268
|
+
font-weight: bold
|
269
|
+
padding-bottom: 4px
|
270
|
+
padding-top: 5px
|
271
|
+
text-align:left
|
272
|
+
end
|
273
|
+
|
274
|
+
.highlighter tr:first-child td
|
275
|
+
border-top: 1px solid #2D4068
|
276
|
+
end
|
277
|
+
|
278
|
+
.highlighter td:first-child
|
279
|
+
border-left: 1px solid #2D4068
|
280
|
+
end
|
281
|
+
|
282
|
+
.highlighter td
|
283
|
+
empty-cells: show
|
284
|
+
end
|
285
|
+
"
|
286
|
+
end
|
287
|
+
|
288
|
+
def completeHtml()
|
289
|
+
@text_to_insert.insert(0,"<html>
|
290
|
+
<meta charset='utf-8'>
|
291
|
+
<head>
|
292
|
+
<style TYPE='text/css'>
|
293
|
+
")
|
294
|
+
@text_to_insert.insert(1,sample_css())
|
295
|
+
@text_to_insert.insert(2,"</style>
|
296
|
+
</head>
|
297
|
+
<body>
|
298
|
+
<div class='highlighter'>
|
299
|
+
")
|
300
|
+
@text_to_insert.push("</div>
|
301
|
+
</body>
|
302
|
+
</html>
|
303
|
+
")
|
304
|
+
end
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
data/lib/coopy/index.rb
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
module Coopy
|
2
|
+
class Index
|
3
|
+
|
4
|
+
attr_accessor :items # Hash<String,IndexItem>
|
5
|
+
attr_accessor :keys # Array<String>
|
6
|
+
attr_accessor :top_freq # integer
|
7
|
+
attr_accessor :height # integer
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@items = {}
|
11
|
+
@cols = [] # Array<integer>
|
12
|
+
@keys = []
|
13
|
+
@top_freq = 0
|
14
|
+
@height = 0
|
15
|
+
@v = nil # View
|
16
|
+
@indexed_table = nil # Table
|
17
|
+
end
|
18
|
+
|
19
|
+
def add_column(i)
|
20
|
+
@cols << i
|
21
|
+
end
|
22
|
+
|
23
|
+
def index_table(t)
|
24
|
+
@indexed_table = t
|
25
|
+
(0...t.height).each do |i|
|
26
|
+
key = ""
|
27
|
+
if @keys.length > i
|
28
|
+
key = @keys[i]
|
29
|
+
else
|
30
|
+
key = to_key(t,i)
|
31
|
+
@keys << key
|
32
|
+
end
|
33
|
+
item = @items[key]
|
34
|
+
if item.nil?
|
35
|
+
item = IndexItem.new
|
36
|
+
@items[key] = item
|
37
|
+
end
|
38
|
+
ct = item.add(i)
|
39
|
+
@top_freq = ct if ct>@top_freq
|
40
|
+
end
|
41
|
+
@height = t.height
|
42
|
+
end
|
43
|
+
|
44
|
+
def to_key(table, i)
|
45
|
+
wide = ""
|
46
|
+
@v = table.get_cell_view if @v.nil?
|
47
|
+
@cols.each_with_index do |col, k|
|
48
|
+
d = table.get_cell(col,i)
|
49
|
+
txt = @v.to_s(d)
|
50
|
+
next if (txt=="" || txt=="null" || txt=="undefined")
|
51
|
+
wide += " // " if (k>0)
|
52
|
+
wide += txt
|
53
|
+
end
|
54
|
+
wide
|
55
|
+
end
|
56
|
+
|
57
|
+
def to_key_by_content(row)
|
58
|
+
wide = ""
|
59
|
+
@cols.each_with_index do |col, k|
|
60
|
+
txt = row.get_row_string(col)
|
61
|
+
next if (txt=="" || txt=="null" || txt=="undefined")
|
62
|
+
wide += " // " if (k>0)
|
63
|
+
wide += txt
|
64
|
+
end
|
65
|
+
wide
|
66
|
+
end
|
67
|
+
|
68
|
+
def get_table
|
69
|
+
@indexed_table
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
end
|