File Coverage

lib/Kwiki/Purple/Sequence.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


line stmt bran cond sub pod time code
1             package Kwiki::Purple::Sequence;
2 1     1   27662 use Kwiki::Plugin '-Base';
  0            
  0            
3             use Kwiki::Installer '-base';
4             use Carp;
5              
6             const class_id => 'purple_sequence';
7             const class_title => 'Purple Sequence';
8             const css_file => 'purple.css';
9             const config_file => 'purple_sequence.yaml';
10             const cgi_class => 'Kwiki::Purple::Sequence::CGI';
11              
12             field remote_sequence => -init =>
13             '$self->config->can("purple_sequence_remote") ? $self->config->purple_sequence_remote : ""';
14              
15             our $VERSION = '0.03';
16              
17             sub register {
18             my $registry = shift;
19             $registry->add(action => 'purple_post');
20             $registry->add(action => 'purple_query');
21             }
22              
23             sub purple_post {
24             my $url = $self->cgi->url;
25             my $nid = $self->cgi->nid;
26              
27             warn "URL: $url\n";
28             warn "NID: $nid\n";
29              
30             if ($nid && $url) {
31             $self->_local_update_index($url, $nid);
32             } elsif ($url) {
33             $nid = $self->_local_get_next_and_update($url);
34             } else {
35             $nid = $self->_local_get_next;
36             }
37              
38             $self->hub->headers->content_type('text/plain');
39             return $nid;
40             }
41              
42             sub purple_query {
43             my $nid = $self->cgi->nid;
44             my $url = $self->_local_query_index($nid);
45             $self->hub->headers->content_type('text/plain');
46             return $url;
47             }
48              
49             sub query_index {
50             my $nid = shift;
51             return $self->_remote_query_index($nid) if
52             $self->remote_sequence;
53             return $self->_local_query_index($nid);
54             }
55              
56             # XXX do permissions checking a la PurpleWiki 0.9[56]
57             # XXX Error Handling!!!??
58             sub update_index {
59             my $url = shift or croak "must supply url";
60             my $nid = shift;
61              
62             return $self->_remote_update_index($url, $nid) if
63             $self->remote_sequence;
64             return $self->_local_update_index($url, $nid);
65             }
66              
67             sub get_next {
68             return $self->_remote_get_next if
69             $self->remote_sequence;
70             return $self->_local_get_next;
71             }
72              
73             sub get_next_and_update {
74             my $url = shift;
75              
76             return $self->_remote_get_next_and_update($url) if
77             $self->remote_sequence;
78              
79             return $self->_local_get_next_and_update($url);
80             }
81              
82             #### PRIVATE
83              
84             sub _local_get_next {
85             $self->_lock;
86             my $nid = $self->_update_value($self->_increment_value($self->_get_value));
87             $self->_unlock;
88             return $nid;
89             }
90              
91              
92             sub _remote_get_next {
93             return $self->_remote_get_next_and_update;
94             }
95              
96             sub _remote_update_index {
97             $self->_remote_get_next_and_update(@_);
98             }
99              
100             sub _remote_get_next_and_update {
101             my $url = shift;
102             my $nid = shift;
103              
104             my $request_url = $self->remote_sequence;
105              
106             my $new_nid = $self->hub->purple->web_request(
107             method => 'POST',
108             request_url => $request_url,
109             post_data => [
110             action => 'purple_post',
111             $url ? (url => $url) : (),
112             $nid ? (nid => $nid) : (),
113             ],
114             );
115              
116             return $new_nid;
117             }
118              
119             sub _local_update_index {
120             my $url = shift or croak "must supply url";
121             my $nid = shift;
122             my $index = $self->_sequence_index_rdwr;
123             $index->{$nid} = $url;
124             }
125              
126              
127             sub _local_get_next_and_update {
128             my $url = shift;
129             my $nid = $self->_local_get_next;
130             $self->_local_update_index($url, $nid);
131             return $nid;
132             }
133              
134             sub _local_query_index {
135             my $nid = shift;
136             my $index = $self->_sequence_index_rdonly;
137             return $index->{$nid};
138             }
139              
140             sub _remote_query_index {
141             my $nid = shift;
142             my $request_url = $self->remote_sequence .
143             "?action=purple_query;nid=$nid";
144             return $self->hub->purple->web_request(
145             method => 'GET',
146             request_url => $request_url,
147             );
148             }
149              
150             sub _sequence_index_rdwr {
151             return io($self->_sequence_index_file)->dbm('DB_File::Lock')->rdwr;
152             }
153              
154             sub _sequence_index_rdonly {
155             return io($self->_sequence_index_file)->dbm('DB_File::Lock')->rdonly;
156             }
157              
158             # XXX can assists testing
159             sub _sequence_index_file {
160             my $index = ($self->config->can('purple_sequence_index') &&
161             $self->config->purple_sequence_index)
162             ? $self->config->purple_sequence_index
163             : $self->plugin_directory . '/' . 'sequence.index';
164             return $index;
165             }
166              
167              
168             # taken from PurpleWiki
169             sub _lock {
170             my $tries = 0;
171             while (mkdir($self->_lock_directory, 0555) == 0) {
172             die "unable to create sequence locking directory"
173             if ($! != 17);
174             $tries++;
175             die "timeout attempting to lock sequence"
176             if ($tries > $self->config->purple_sequence_lock_count);
177             sleep 1;
178             }
179             }
180              
181             sub _unlock {
182             rmdir($self->_lock_directory) or
183             die "unable to remove sequence locking directory";
184             }
185              
186             sub _lock_directory {
187             $self->_sequence_file . '.lck';
188             }
189              
190             sub _get_value {
191             io($self->_sequence_file)->print(0)
192             unless io($self->_sequence_file)->exists;
193             io($self->_sequence_file)->all;
194             }
195              
196             sub _update_value {
197             my $value = shift;
198             io($self->_sequence_file)->print($value);
199             return $value;
200             }
201              
202             sub _sequence_file {
203             ($self->config->can('purple_sequence_file') &&
204             $self->config->purple_sequence_file)
205             ? $self->config->purple_sequence_file
206             : $self->plugin_directory . '/' . 'sequence';
207             }
208              
209              
210             # XXX taken right out of purplewiki, i'm quite sure this can
211             # be made more smarter. might make sense to just go with ints
212             sub _increment_value {
213             my $value = shift;
214             $value ||= 0;
215              
216             my @oldValues = split('', $value);
217             my @newValues;
218             my $carryBit = 1;
219              
220             foreach my $char (reverse(@oldValues)) {
221             if ($carryBit) {
222             my $newChar;
223             ($newChar, $carryBit) = $self->_inc_char($char);
224             push(@newValues, $newChar);
225             } else {
226             push(@newValues, $char);
227             }
228             }
229             push(@newValues, '1') if ($carryBit);
230             return join('', (reverse(@newValues)));
231             }
232              
233             sub _inc_char {
234             my $char = shift;
235              
236             if ($char eq 'Z') {
237             return '0', 1;
238             }
239             if ($char eq '9') {
240             return 'A', 0;
241             }
242             if ($char =~ /[A-Z0-9]/) {
243             return chr(ord($char) + 1), 0;
244             }
245             }
246              
247             package Kwiki::Purple::Sequence::CGI;
248             use Kwiki::CGI -base;
249              
250             cgi 'nid';
251             cgi 'url';
252              
253             package Kwiki::Purple::Sequence;
254              
255             __DATA__