/[Frey]/branches/zimbardo/lib/Frey/SVN.pm
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Annotation of /branches/zimbardo/lib/Frey/SVN.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1013 - (hide annotations)
Sat Jan 24 19:35:41 2009 UTC (15 years, 3 months ago) by dpavlin
Original Path: trunk/lib/Frey/SVN.pm
File size: 5302 byte(s)
set width of files to longest one in ex to align it to left,
count path usage and display as it title with fixed
link to all revisions of that file
1 dpavlin 769 package Frey::SVN;
2     use Moose;
3    
4     # Convert output from svn log to html page (with some formatting of
5     # commit messages)
6     #
7     # 2004-04-28 Dobrica Pavlinusic <dpavlin@rot13.org>
8    
9     extends 'Frey';
10     with 'Frey::Web';
11     #with 'Frey::Storage';
12    
13     use XML::Simple;
14 dpavlin 807 use DateTimeX::Easy;
15 dpavlin 769
16     has repository => (
17     is => 'rw',
18     isa => 'Str',
19     required => 1,
20     default => 'file:///home/dpavlin/private/svn/Frey',
21     );
22    
23 dpavlin 987 has path => (
24     is => 'rw',
25     isa => 'Str'
26     );
27    
28 dpavlin 770 has limit => (
29     is => 'rw',
30     isa => 'Int',
31     default => 50,
32     );
33    
34 dpavlin 804 sub iterator {
35     my ($self,$coderef) = @_;
36    
37 dpavlin 769 sub sh_regex($$) {
38     my ($cmd,$regex) = @_;
39     open(my $sh, $cmd . ' |') || die "sh_regex failed on $cmd: $!";
40     while(my $l = <$sh>) {
41     chomp($l);
42     if ($l =~ $regex) {
43     if ($1 && $2) {
44     return ($1,$2);
45     } elsif ($1) {
46     return $1;
47     } else {
48     return $l;
49     }
50     }
51     }
52     #warn "can't find $regex in output of $cmd\n";
53     return;
54     }
55    
56 dpavlin 987 my $path = $self->repository . $self->path;
57 dpavlin 769 warn "# path $path\n";
58    
59     my $cmd;
60     if ($path =~ m#file://# || -e "$path/.svn") {
61     $cmd = "svn log -v --xml $path";
62     } else {
63     my $svn_path = sh_regex('svk info', qr#Mirrored From:\s+([^,]+)#i);
64    
65     if (! $svn_path) {
66    
67     my $svk_depot = sh_regex('svk info', qr#Depot Path: (/.+)#i);
68    
69     my $depot = $svk_depot;
70     my $rel_path;
71    
72     my $path = sh_regex('svk depot --list', qr/^$depot\s+(\S+)/i);
73    
74     while (! $path && $depot =~ s{^(/.*/)([^/]+)/?$}{$1} ) {
75     $rel_path = "$2/$rel_path";
76     $path = sh_regex('svk depot --list', qr/^$depot\s+(\S+)/i);
77     }
78    
79     die "can't find depot path '$svk_depot' in svk depot --list\n" unless ($path);
80     $svn_path = "file:///$path/$rel_path";
81     }
82    
83     $cmd = "svn log -v --xml $svn_path";
84     }
85    
86 dpavlin 770 $cmd .= " --limit " . $self->limit if $self->limit;
87    
88 dpavlin 769 warn "# $cmd\n";
89     open(my $fh, $cmd .' |') || die "failed $cmd: $!";
90     my $log;
91     while(<$fh>) {
92     $log .= $_;
93     }
94     close($fh);
95    
96     my $xml = XMLin($log, ForceArray => [ 'logentry', 'path' ]);
97    
98 dpavlin 804 foreach my $e (@{$xml->{'logentry'}}) {
99 dpavlin 875 warn "# e = ",$self->dump( $e ) if $self->debug;
100 dpavlin 804 $coderef->($e);
101     }
102     }
103    
104     sub as_markup {
105     my ($self) = @_;
106    
107     # extract svk revision: r113@athlon (orig r999): dpavlin | 2005-09-01 20:38:07 +0200
108     our $svk_rev_re = '\s+(r\d+@\w+(\s+\(orig\s+r\d+\))*:\s+\w+\s+\|\s+\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2}\s+\+\d+)\s*';
109    
110     sub encode {
111     my $foo = shift;
112     $foo =~ s/$svk_rev_re//gsm;
113     $foo =~ s/</&lt;/g;
114     $foo =~ s/>/&gt;/g;
115     $foo =~ s/"/&quot;/g;
116 dpavlin 909 # $foo =~ s/([\n\r][\n\r]+)/$1<br\/>/gis;
117 dpavlin 804 $foo =~ s/([\n\r]+)([\-\*]\s+)/$1<br\/>$2/gis;
118     $foo =~ s/([\n\r]+)(r\d+:\s+)/$1<br\/>$2/gis;
119     $foo =~ s/([\n\r]+)(\s+r\d+@)/$1<br\/>$2/gis; # svk
120     return $foo;
121     }
122    
123 dpavlin 987 my $repository = $self->repository;
124     my $path = $self->path;
125 dpavlin 769
126 dpavlin 987 our $html = qq|
127     <h1><a href="?repository=$repository">$repository</a></h1>
128     <h2>$path</h2>
129     |;
130    
131 dpavlin 852 $self->add_css(qq|
132 dpavlin 909 .commit {
133     clear: both;
134     padding-top: 1em;
135     padding-bottom: 1em;
136     border-top: 1px dashed #ccc;
137     }
138     .files {
139     color: #888;
140     font-family: monospace;
141     font-size: 80%;
142     float: right;
143     padding-bottom: 1.2em; /* fix 80% back to original 1em */
144     }
145 dpavlin 987 .files a {
146     text-decoration: none;
147     color: #888;
148     }
149 dpavlin 852 .date, .revision { color: #666; }
150 dpavlin 909 .message {
151     padding-top: 0.5em;
152     padding-left: 2em; /* like blockquote */
153     white-space: pre-wrap;
154     }
155 dpavlin 852
156     ins { color: #8c8 }
157     del { color: #c88 }
158     |);
159    
160 dpavlin 1013 my $max_path_len = 0;
161     my $path_count;
162    
163 dpavlin 804 $self->iterator( sub {
164     my $e = shift;
165    
166 dpavlin 769 my $rev = $e->{'revision'};
167     my $date = $e->{'date'};
168    
169     $date =~ s/T/ /;
170     $date =~ s/\.\d+Z$//;
171    
172 dpavlin 852 my $msg = $e->{'msg'};
173     $msg = '' if ref($msg); # FIXME why do I need this, dammit?
174     if ( $msg ) {
175     $msg = encode( $msg );
176     $msg = qq|<div class="message">$msg</div>|;
177     }
178    
179 dpavlin 769 my @files;
180    
181     foreach my $p (@{$e->{'paths'}->{'path'}}) {
182     my ($action,$path) = ($p->{'action'},$p->{'content'});
183    
184     if ($action eq "A") {
185     push @files, "<ins>$path</ins>";
186     } elsif ($action eq "D") {
187     push @files, "<del>$path</del>";
188 dpavlin 851 } else {
189 dpavlin 769 push @files, $path;
190     }
191 dpavlin 1013
192     $max_path_len = length $path if length $path > $max_path_len;
193     $path_count->{$path}++;
194 dpavlin 769 }
195    
196 dpavlin 909 $html .= qq|
197     <div class="commit">
198 dpavlin 987 <span class="date">$date</span>
199     <em>$e->{author}</em>
200     <span class="revision">$e->{revision}</span>
201     <div class="files">\n
202     |
203     . join("<br>\n",
204     map {
205 dpavlin 1013 my $path = $_;
206     $path =~ s{<[^>]+>}{}g;
207     qq|<a href="?repository=$repository;path=$path" title="$path ##">$_</a>|
208 dpavlin 987 } @files
209     )
210     . qq|
211     </div>
212     $msg
213 dpavlin 909 </div>
214     |;
215 dpavlin 769
216 dpavlin 804 });
217 dpavlin 769
218 dpavlin 1013 $self->add_css(qq|
219     .files {
220     width: ${max_path_len}ex;
221     }
222     |);
223    
224     $html =~ s[title="(\S+) ##"]['title="' . $path_count->{$1} . '"']gse;
225    
226 dpavlin 769 return $html;
227     }
228    
229 dpavlin 805 sub codeswarm_as_markup {
230     my ($self) = @_;
231    
232     $self->content_type('text/xml');
233    
234     my $file_events = '';
235    
236     $self->iterator( sub {
237     my $e = shift;
238    
239     my $rev = $e->{'revision'};
240 dpavlin 807 my $date = DateTimeX::Easy->new( $e->{'date'} )->epoch . '000'; # ms
241 dpavlin 805 my $author = $e->{'author'};
242    
243     foreach my $p (@{$e->{'paths'}->{'path'}}) {
244     my ($action,$path) = ($p->{'action'},$p->{'content'});
245     $file_events .= qq|\t<event filename="$path" date="$date" author="$author" />\n|;
246     }
247    
248     });
249    
250     return qq|<?xml version="1.0"?>
251     <!-- One commit per day for one month by a documenter and programmer. -->
252     <file_events>
253     $file_events
254     </file_events>
255     |;
256    
257     }
258    
259 dpavlin 769 1;

  ViewVC Help
Powered by ViewVC 1.1.26