svn_record 1.2.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.
Files changed (50) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +72 -0
  3. data/Rakefile +7 -0
  4. data/app/assets/images/svn_record/file_icon/bullet_add.png +0 -0
  5. data/app/assets/images/svn_record/file_icon/bullet_black.png +0 -0
  6. data/app/assets/images/svn_record/file_icon/bullet_blue.png +0 -0
  7. data/app/assets/images/svn_record/file_icon/bullet_delete.png +0 -0
  8. data/app/assets/images/svn_record/file_icon/bullet_diamond.png +0 -0
  9. data/app/assets/images/svn_record/file_icon/bullet_end.png +0 -0
  10. data/app/assets/images/svn_record/file_icon/bullet_go.png +0 -0
  11. data/app/assets/images/svn_record/file_icon/bullet_orange.png +0 -0
  12. data/app/assets/images/svn_record/file_icon/bullet_purple.png +0 -0
  13. data/app/assets/images/svn_record/file_icon/bullet_toggle_minus.png +0 -0
  14. data/app/assets/images/svn_record/file_icon/bullet_toggle_plus.png +0 -0
  15. data/app/assets/images/svn_record/file_icon/dir.png +0 -0
  16. data/app/assets/images/svn_record/file_icon/file.png +0 -0
  17. data/app/assets/images/svn_record/file_icon/folder.png +0 -0
  18. data/app/assets/images/svn_record/file_icon/folder_open.png +0 -0
  19. data/app/assets/images/svn_record/file_icon/folder_open_add.png +0 -0
  20. data/app/assets/images/svn_record/file_icon/folder_open_orange.png +0 -0
  21. data/app/assets/javascripts/svn_record/application.js +16 -0
  22. data/app/assets/javascripts/svn_record/change.js.coffee +4 -0
  23. data/app/assets/stylesheets/svn_record/application.css +14 -0
  24. data/app/assets/stylesheets/svn_record/site.css +396 -0
  25. data/app/controllers/svn_record/application_controller.rb +3 -0
  26. data/app/controllers/svn_record/repository/application_controller.rb +3 -0
  27. data/app/controllers/svn_record/repository/changes_controller.rb +38 -0
  28. data/app/models/svn_record/repository/change.rb +39 -0
  29. data/app/models/svn_record/repository/file.rb +20 -0
  30. data/app/models/svn_record/repository/user.rb +14 -0
  31. data/app/views/layouts/svn_record/subversion.html.slim +22 -0
  32. data/app/views/svn_record/repository/changes/_changesets_list.slim +29 -0
  33. data/app/views/svn_record/repository/changes/diff.html.slim +30 -0
  34. data/app/views/svn_record/repository/changes/entry.html.slim +18 -0
  35. data/app/views/svn_record/repository/changes/index.html.slim +34 -0
  36. data/app/views/svn_record/repository/changes/list.html.slim +2 -0
  37. data/app/views/svn_record/repository/changes/revisions.html.slim +18 -0
  38. data/config/configuration.yml +5 -0
  39. data/config/routes.rb +17 -0
  40. data/lib/Svn/helper.rb +108 -0
  41. data/lib/Svn/mime_type.rb +492 -0
  42. data/lib/Svn/scm/adapters/subversion.rb +303 -0
  43. data/lib/generators/svn_record/install_generator.rb +16 -0
  44. data/lib/generators/svn_record/templates/create_repository_changes.rb +17 -0
  45. data/lib/generators/svn_record/templates/create_repository_files.rb +21 -0
  46. data/lib/generators/svn_record/templates/create_repository_user.rb +8 -0
  47. data/lib/svn_record/engine.rb +11 -0
  48. data/lib/svn_record/version.rb +3 -0
  49. data/lib/svn_record.rb +4 -0
  50. metadata +161 -0
@@ -0,0 +1,492 @@
1
+ module Svn
2
+ module MimeType
3
+
4
+ MIME_TYPES = {
5
+ 'text/css' => 'css',
6
+ 'text/html' => 'html,htm,xhtml',
7
+ 'text/x-html-template' => 'rhtml',
8
+ 'text/x-ruby' => 'rb,rbw,ruby,rake,erb',
9
+ 'text/x-sh' => 'sh',
10
+ 'text/csv' => 'csv',
11
+ 'image/gif' => 'gif',
12
+ 'image/jpeg' => 'jpg,jpeg,jpe',
13
+ 'image/png' => 'png',
14
+ 'image/tiff' => 'tiff,tif',
15
+ 'image/x-ms-bmp' => 'bmp',
16
+ 'image/x-xpixmap' => 'xpm',
17
+ 'image/svg+xml'=> 'svg',
18
+ 'application/javascript' => 'js',
19
+ 'application/vnd.ms-powerpoint' => 'ppt,pps',
20
+ 'application/x-tar' => 'tar',
21
+ 'application/zip' => 'zip',
22
+ 'application/x-gzip' => 'gz',
23
+ }.freeze
24
+
25
+ EXTENSIONS = MIME_TYPES.inject({}) do |map, (type, exts)|
26
+ exts.split(',').each {|ext| map[ext.strip] = type}
27
+ map
28
+ end
29
+
30
+ # returns mime type for name or nil if unknown
31
+ def self.of(name)
32
+ return nil unless name
33
+ m = name.to_s.match(/(^|\.)([^\.]+)$/)
34
+ EXTENSIONS[m[2].downcase] if m
35
+ end
36
+
37
+ # Returns the css class associated to
38
+ # the mime type of name
39
+ def self.css_class_of(name)
40
+ mime = of(name)
41
+ mime && mime.gsub('/', '-')
42
+ end
43
+
44
+ def self.main_mimetype_of(name)
45
+ mimetype = of(name)
46
+ mimetype.split('/').first if mimetype
47
+ end
48
+
49
+ # return true if mime-type for name is type/*
50
+ # otherwise false
51
+ def self.is_type?(type, name)
52
+ main_mimetype = main_mimetype_of(name)
53
+ type.to_s == main_mimetype
54
+ end
55
+ end
56
+
57
+ module SyntaxHighlighting
58
+
59
+ class << self
60
+ attr_reader :highlighter
61
+ delegate :highlight_by_filename, :highlight_by_language, :to => :highlighter
62
+
63
+ def highlighter=(name)
64
+ if name.is_a?(Module)
65
+ @highlighter = name
66
+ else
67
+ @highlighter = const_get(name)
68
+ end
69
+ end
70
+ end
71
+ module CodeRay
72
+ require 'coderay'
73
+ require 'coderay/helpers/file_type'
74
+ class << self
75
+ # Highlights +text+ as the content of +filename+
76
+ # Should not return line numbers nor outer pre tag
77
+ def highlight_by_filename(text, filename)
78
+ language = ::CodeRay::FileType[filename]
79
+ language ? ::CodeRay.scan(text, language).html(:break_lines => true) : ERB::Util.h(text)
80
+ end
81
+ # Highlights +text+ using +language+ syntax
82
+ # Should not return outer pre tag
83
+ def highlight_by_language(text, language)
84
+ ::CodeRay.scan(text, language).html(:wrap => :span)
85
+ end
86
+ end
87
+ end
88
+ end
89
+ SyntaxHighlighting.highlighter = 'CodeRay'
90
+
91
+ class UnifiedDiff < Array
92
+ attr_reader :diff_type, :diff_style
93
+
94
+ def initialize(diff, options={})
95
+ options.assert_valid_keys(:type, :style, :max_lines)
96
+ diff = diff.split("\n") if diff.is_a?(String)
97
+ @diff_type = options[:type] || 'inline'
98
+ @diff_style = options[:style]
99
+ lines = 0
100
+ @truncated = false
101
+ diff_table = DiffTable.new(diff_type, diff_style)
102
+ diff.each do |line_raw|
103
+ line = Svn::CodesetUtil.to_utf8_by_setting(line_raw)
104
+ unless diff_table.add_line(line)
105
+ self << diff_table if diff_table.length > 0
106
+ diff_table = DiffTable.new(diff_type, diff_style)
107
+ end
108
+ lines += 1
109
+ if options[:max_lines] && lines > options[:max_lines]
110
+ @truncated = true
111
+ break
112
+ end
113
+ end
114
+ self << diff_table unless diff_table.empty?
115
+ self
116
+ end
117
+ def truncated?; @truncated; end
118
+ end
119
+
120
+ # Class that represents a file diff
121
+ class DiffTable < Array
122
+ attr_reader :file_name
123
+ def initialize(type="inline", style=nil)
124
+ @parsing = false
125
+ @added = 0
126
+ @removed = 0
127
+ @type = type
128
+ @style = style
129
+ @file_name = nil
130
+ @git_diff = false
131
+ end
132
+
133
+ def add_line(line)
134
+ unless @parsing
135
+ if line =~ /^(---|\+\+\+) (.*)$/
136
+ self.file_name = $2
137
+ elsif line =~ /^@@ (\+|\-)(\d+)(,\d+)? (\+|\-)(\d+)(,\d+)? @@/
138
+ @line_num_l = $2.to_i
139
+ @line_num_r = $5.to_i
140
+ @parsing = true
141
+ end
142
+ else
143
+ if line =~ %r{^[^\+\-\s@\\]}
144
+ @parsing = false
145
+ return false
146
+ elsif line =~ /^@@ (\+|\-)(\d+)(,\d+)? (\+|\-)(\d+)(,\d+)? @@/
147
+ @line_num_l = $2.to_i
148
+ @line_num_r = $5.to_i
149
+ else
150
+ parse_line(line, @type)
151
+ end
152
+ end
153
+ return true
154
+ end
155
+ def each_line
156
+ prev_line_left, prev_line_right = nil, nil
157
+ each do |line|
158
+ spacing = prev_line_left && prev_line_right && (line.nb_line_left != prev_line_left+1) && (line.nb_line_right != prev_line_right+1)
159
+ yield spacing, line
160
+ prev_line_left = line.nb_line_left.to_i if line.nb_line_left.to_i > 0
161
+ prev_line_right = line.nb_line_right.to_i if line.nb_line_right.to_i > 0
162
+ end
163
+ end
164
+
165
+ def inspect
166
+ puts '### DIFF TABLE ###'
167
+ puts "file : #{file_name}"
168
+ self.each do |d|
169
+ d.inspect
170
+ end
171
+ end
172
+
173
+ private
174
+
175
+ def file_name=(arg)
176
+ both_git_diff = false
177
+ if file_name.nil?
178
+ @git_diff = true if arg =~ %r{^(a/|/dev/null)}
179
+ else
180
+ both_git_diff = (@git_diff && arg =~ %r{^(b/|/dev/null)})
181
+ end
182
+ if both_git_diff
183
+ if file_name && arg == "/dev/null"
184
+ # keep the original file name
185
+ @file_name = file_name.sub(%r{^a/}, '')
186
+ else
187
+ # remove leading b/
188
+ @file_name = arg.sub(%r{^b/}, '')
189
+ end
190
+ elsif @style == "Subversion"
191
+ # removing trailing "(revision nn)"
192
+ @file_name = arg.sub(%r{\t+\(.*\)$}, '')
193
+ else
194
+ @file_name = arg
195
+ end
196
+ end
197
+
198
+ def diff_for_added_line
199
+ if @type == 'sbs' && @removed > 0 && @added < @removed
200
+ self[-(@removed - @added)]
201
+ else
202
+ diff = Diff.new
203
+ self << diff
204
+ diff
205
+ end
206
+ end
207
+
208
+ def parse_line(line, type="inline")
209
+ if line[0, 1] == "+"
210
+ diff = diff_for_added_line
211
+ diff.line_right = line[1..-1]
212
+ diff.nb_line_right = @line_num_r
213
+ diff.type_diff_right = 'diff_in'
214
+ @line_num_r += 1
215
+ @added += 1
216
+ true
217
+ elsif line[0, 1] == "-"
218
+ diff = Diff.new
219
+ diff.line_left = line[1..-1]
220
+ diff.nb_line_left = @line_num_l
221
+ diff.type_diff_left = 'diff_out'
222
+ self << diff
223
+ @line_num_l += 1
224
+ @removed += 1
225
+ true
226
+ else
227
+ write_offsets
228
+ if line[0, 1] =~ /\s/
229
+ diff = Diff.new
230
+ diff.line_right = line[1..-1]
231
+ diff.nb_line_right = @line_num_r
232
+ diff.line_left = line[1..-1]
233
+ diff.nb_line_left = @line_num_l
234
+ self << diff
235
+ @line_num_l += 1
236
+ @line_num_r += 1
237
+ true
238
+ elsif line[0, 1] = "\\"
239
+ true
240
+ else
241
+ false
242
+ end
243
+ end
244
+ end
245
+
246
+ def write_offsets
247
+ if @added > 0 && @added == @removed
248
+ @added.times do |i|
249
+ line = self[-(1 + i)]
250
+ removed = (@type == 'sbs') ? line : self[-(1 + @added + i)]
251
+ offsets = offsets(removed.line_left, line.line_right)
252
+ removed.offsets = line.offsets = offsets
253
+ end
254
+ end
255
+ @added = 0
256
+ @removed = 0
257
+ end
258
+
259
+ def offsets(line_left, line_right)
260
+ if line_left.present? && line_right.present? && line_left != line_right
261
+ max = [line_left.size, line_right.size].min
262
+ starting = 0
263
+ while starting < max && line_left[starting] == line_right[starting]
264
+ starting += 1
265
+ end
266
+ while line_left[starting].ord.between?(128, 191) && starting > 0
267
+ starting -= 1
268
+ end
269
+ ending = -1
270
+ while ending >= -(max - starting) && line_left[ending] == line_right[ending]
271
+ ending -= 1
272
+ end
273
+ while line_left[ending].ord.between?(128, 191) && ending > -1
274
+ ending -= 1
275
+ end
276
+ unless starting == 0 && ending == -1
277
+ [starting, ending]
278
+ end
279
+ end
280
+ end
281
+ end
282
+
283
+ # A line of diff
284
+ class Diff
285
+ attr_accessor :nb_line_left
286
+ attr_accessor :line_left
287
+ attr_accessor :nb_line_right
288
+ attr_accessor :line_right
289
+ attr_accessor :type_diff_right
290
+ attr_accessor :type_diff_left
291
+ attr_accessor :offsets
292
+
293
+ def initialize()
294
+ self.nb_line_left = ''
295
+ self.nb_line_right = ''
296
+ self.line_left = ''
297
+ self.line_right = ''
298
+ self.type_diff_right = ''
299
+ self.type_diff_left = ''
300
+ end
301
+
302
+ def type_diff
303
+ type_diff_right == 'diff_in' ? type_diff_right : type_diff_left
304
+ end
305
+
306
+ def line
307
+ type_diff_right == 'diff_in' ? line_right : line_left
308
+ end
309
+
310
+ def html_line_left
311
+ line_to_html(line_left, offsets)
312
+ end
313
+
314
+ def html_line_right
315
+ line_to_html(line_right, offsets)
316
+ end
317
+
318
+ def html_line
319
+ line_to_html(line, offsets)
320
+ end
321
+
322
+ def inspect
323
+ puts '### Start Line Diff ###'
324
+ puts self.nb_line_left
325
+ puts self.line_left
326
+ puts self.nb_line_right
327
+ puts self.line_right
328
+ end
329
+
330
+ private
331
+
332
+ def line_to_html(line, offsets)
333
+ html = line_to_html_raw(line, offsets)
334
+ html.force_encoding('UTF-8') if html.respond_to?(:force_encoding)
335
+ html
336
+ end
337
+
338
+ def line_to_html_raw(line, offsets)
339
+ if offsets
340
+ s = ''
341
+ unless offsets.first == 0
342
+ s << CGI.escapeHTML(line[0..offsets.first-1])
343
+ end
344
+ s << '<span>' + CGI.escapeHTML(line[offsets.first..offsets.last]) + '</span>'
345
+ unless offsets.last == -1
346
+ s << CGI.escapeHTML(line[offsets.last+1..-1])
347
+ end
348
+ s
349
+ else
350
+ CGI.escapeHTML(line)
351
+ end
352
+ end
353
+ end
354
+
355
+ module CodesetUtil
356
+
357
+ def self.replace_invalid_utf8(str)
358
+ return str if str.nil?
359
+ if str.respond_to?(:force_encoding)
360
+ str.force_encoding('UTF-8')
361
+ if ! str.valid_encoding?
362
+ str = str.encode("US-ASCII", :invalid => :replace,
363
+ :undef => :replace, :replace => '?').encode("UTF-8")
364
+ end
365
+ elsif RUBY_PLATFORM == 'java'
366
+ begin
367
+ ic = Iconv.new('UTF-8', 'UTF-8')
368
+ str = ic.iconv(str)
369
+ rescue
370
+ str = str.gsub(%r{[^\r\n\t\x20-\x7e]}, '?')
371
+ end
372
+ else
373
+ ic = Iconv.new('UTF-8', 'UTF-8')
374
+ txtar = ""
375
+ begin
376
+ txtar += ic.iconv(str)
377
+ rescue Iconv::IllegalSequence
378
+ txtar += $!.success
379
+ str = '?' + $!.failed[1,$!.failed.length]
380
+ retry
381
+ rescue
382
+ txtar += $!.success
383
+ end
384
+ str = txtar
385
+ end
386
+ str
387
+ end
388
+
389
+ def self.to_utf8(str, encoding)
390
+ return str if str.nil?
391
+ str.force_encoding("ASCII-8BIT") if str.respond_to?(:force_encoding)
392
+ if str.empty?
393
+ str.force_encoding("UTF-8") if str.respond_to?(:force_encoding)
394
+ return str
395
+ end
396
+ enc = encoding.blank? ? "UTF-8" : encoding
397
+ if str.respond_to?(:force_encoding)
398
+ if enc.upcase != "UTF-8"
399
+ str.force_encoding(enc)
400
+ str = str.encode("UTF-8", :invalid => :replace,
401
+ :undef => :replace, :replace => '?')
402
+ else
403
+ str.force_encoding("UTF-8")
404
+ if ! str.valid_encoding?
405
+ str = str.encode("US-ASCII", :invalid => :replace,
406
+ :undef => :replace, :replace => '?').encode("UTF-8")
407
+ end
408
+ end
409
+ elsif RUBY_PLATFORM == 'java'
410
+ begin
411
+ ic = Iconv.new('UTF-8', enc)
412
+ str = ic.iconv(str)
413
+ rescue
414
+ str = str.gsub(%r{[^\r\n\t\x20-\x7e]}, '?')
415
+ end
416
+ else
417
+ ic = Iconv.new('UTF-8', enc)
418
+ txtar = ""
419
+ begin
420
+ txtar += ic.iconv(str)
421
+ rescue Iconv::IllegalSequence
422
+ txtar += $!.success
423
+ str = '?' + $!.failed[1,$!.failed.length]
424
+ retry
425
+ rescue
426
+ txtar += $!.success
427
+ end
428
+ str = txtar
429
+ end
430
+ str
431
+ end
432
+
433
+ def self.to_utf8_by_setting(str)
434
+ return str if str.nil?
435
+ str = self.to_utf8_by_setting_internal(str)
436
+ if str.respond_to?(:force_encoding)
437
+ str.force_encoding('UTF-8')
438
+ end
439
+ str
440
+ end
441
+
442
+ def self.to_utf8_by_setting_internal(str)
443
+ return str if str.nil?
444
+ if str.respond_to?(:force_encoding)
445
+ str.force_encoding('ASCII-8BIT')
446
+ end
447
+ return str if str.empty?
448
+ return str if /\A[\r\n\t\x20-\x7e]*\Z/n.match(str) # for us-ascii
449
+ if str.respond_to?(:force_encoding)
450
+ str.force_encoding('UTF-8')
451
+ end
452
+ str = self.replace_invalid_utf8(str)
453
+ if str.respond_to?(:force_encoding)
454
+ str.force_encoding('UTF-8')
455
+ end
456
+ str
457
+ end
458
+
459
+ def self.from_utf8(str, encoding)
460
+ str ||= ''
461
+ if str.respond_to?(:force_encoding)
462
+ str.force_encoding('UTF-8')
463
+ if encoding.upcase != 'UTF-8'
464
+ str = str.encode(encoding, :invalid => :replace,
465
+ :undef => :replace, :replace => '?')
466
+ else
467
+ str = self.replace_invalid_utf8(str)
468
+ end
469
+ elsif RUBY_PLATFORM == 'java'
470
+ begin
471
+ ic = Iconv.new(encoding, 'UTF-8')
472
+ str = ic.iconv(str)
473
+ rescue
474
+ str = str.gsub(%r{[^\r\n\t\x20-\x7e]}, '?')
475
+ end
476
+ else
477
+ ic = Iconv.new(encoding, 'UTF-8')
478
+ txtar = ""
479
+ begin
480
+ txtar += ic.iconv(str)
481
+ rescue Iconv::IllegalSequence
482
+ txtar += $!.success
483
+ str = '?' + $!.failed[1, $!.failed.length]
484
+ retry
485
+ rescue
486
+ txtar += $!.success
487
+ end
488
+ str = txtar
489
+ end
490
+ end
491
+ end
492
+ end