File Coverage

blib/lib/RosettaCode.pm
Criterion Covered Total %
statement 63 162 38.8
branch 0 26 0.0
condition 0 8 0.0
subroutine 21 31 67.7
pod 0 10 0.0
total 84 237 35.4


line stmt bran cond sub pod time code
1 1     1   1024 use strict; use warnings;
  1     1   2  
  1         28  
  1         5  
  1         2  
  1         43  
2             package RosettaCode;
3              
4             our $VERSION = '0.1.1';
5              
6 1     1   400 use RosettaCode::Lang;
  1         4  
  1         31  
7 1     1   436 use RosettaCode::Task;
  1         3  
  1         32  
8              
9 1     1   607 use utf8;
  1         16  
  1         5  
10 1     1   835 use MediaWiki::Bot;
  1         128741  
  1         53  
11 1     1   609 use YAML::PP;
  1         62052  
  1         62  
12 1     1   7 use Carp 'confess';
  1         5  
  1         46  
13              
14             use Module::Pluggable
15 1         11 require => 1,
16 1     1   7 search_path => [ 'RosettaCode::Command' ];
  1         2  
17             RosettaCode->plugins;
18              
19 1     1   116 use Mo qw'build builder default xxx';
  1         2  
  1         8  
20             extends 'RosettaCode::Command';
21              
22             has text => '';
23             has log_io => '';
24              
25 1     1   1266 use File::Path 'rmtree';
  1         2  
  1         99  
26 1     1   7 use IO::All;
  1         1  
  1         12  
27              
28 1     1   71 use constant abstract => 'Sync local repository with remote RosettaCode wiki';
  1         4  
  1         54  
29 1     1   8 use constant usage_desc => 'rosettacode sync <target_directory> [<options>]';
  1         8  
  1         45  
30 1     1   6 use constant options => [qw( target )];
  1         3  
  1         71  
31              
32 1     1   7 use constant ROSETTACODE_API_URL => 'http://rosettacode.org/w/api.php';
  1         2  
  1         70  
33 1     1   6 use constant TASKS_FILE => 'Cache/tasks.txt';
  1         4  
  1         55  
34 1     1   9 use constant LANGS_FILE => 'Cache/langs.txt';
  1         2  
  1         51  
35 1     1   7 use constant TASKS_CATEGORY => 'Category:Programming_Tasks';
  1         1  
  1         42  
36 1     1   6 use constant LANGS_CATEGORY => 'Category:Programming_Languages';
  1         2  
  1         57  
37 1     1   7 use constant CACHE_TIME => 7 * 24 * 60 * 60;
  1         3  
  1         1764  
38              
39             has bot => (builder => 'build_bot');
40             has tasks => (builder => 'build_tasks');
41             has langs => (builder => 'build_langs');
42             has log_io => io->file('rosettacode.log')->utf8;
43              
44             sub run {
45 0     0 0   my ($self) = @_;
46              
47 0 0         die "Current directory does not look like a RosettaCodeData directory"
48             unless -f 'Conf/task.yaml';
49              
50 0           for my $dir (qw( Meta Lang Task )) {
51 0 0         if (-d $dir) {
52 0           print "Removing '$dir'\n";
53 0 0         rmtree $dir
54             or die "Failed to rm -r Meta Lang Task";
55             }
56             }
57              
58 0           $self->log("START RosettaCode Sync");
59 0           for my $lang (sort keys %{$self->langs}) {
  0            
60 0           my $langs = $self->langs;
61 0 0         my $info = $langs->{$lang} or next;
62              
63 0           my $rcl = RosettaCode::Lang->new(
64             %$info,
65             langs => $self->langs,
66             tasks => $self->tasks,
67             bot => $self->bot,
68             log_io => $self->log_io,
69             );
70              
71 0           print "* LANG: $info->{name}\n";
72              
73 0           $rcl->fetch_lang;
74              
75 0 0         next if $ENV{RCD_FETCHONLY};
76              
77 0           $rcl->build_lang;
78             }
79              
80 0           for my $task (sort keys %{$self->tasks}) {
  0            
81             # exit if $a++ > 100;
82 0           my $tasks = $self->tasks;
83 0 0         my $info = $tasks->{$task} or next;
84              
85 0           my $rct = RosettaCode::Task->new(
86             %$info,
87             langs => $self->langs,
88             tasks => $self->tasks,
89             bot => $self->bot,
90             log_io => $self->log_io,
91             );
92              
93 0           print "* TASK: $rct->{name}\n";
94              
95 0           $rct->fetch_task;
96              
97 0 0         next if $ENV{RCD_FETCHONLY};
98              
99 0           $rct->build_task;
100             }
101              
102 0           $self->log("COMPLETE RosettaCode Sync");
103             }
104              
105             sub build_tasks {
106 0     0 0   my ($self) = @_;
107 0           my $io = io->file(TASKS_FILE)->utf8;
108 0           my @task_list;
109 0 0 0       if ($io->exists and time - $io->mtime < CACHE_TIME) {
110 0           @task_list = $io->chomp->slurp;
111             }
112             else {
113 0           @task_list = $self->get_category(TASKS_CATEGORY);
114 0           $io->assert->println($_) for @task_list;
115             }
116 0           my $tasks = YAML::PP::LoadFile('Conf/task.yaml');
117 0           for my $name (keys %$tasks) {
118 0   0       my $info = $tasks->{$name} ||= {};
119 0           $info->{name} = $name;
120 0           $info->{url} = $name;
121 0           $info->{url} =~ s/ /_/g;
122 0           $info->{path} = $name;
123 0           $info->{path} =~ s/[\'\"]//g;
124 0           $info->{path} =~ s/[\,\ \/\*\!\(\)\x{7f}-\x{ffff}]+/-/g;
125 0 0         die unless $info->{path};
126 0 0         die if $info->{path} eq '-';
127             }
128 0           return $tasks;
129             }
130              
131             sub build_langs {
132 0     0 0   my ($self) = @_;
133 0           my $io = io->file(LANGS_FILE)->utf8;
134 0           my @lang_list;
135 0 0 0       if ($io->exists and time - $io->mtime < CACHE_TIME) {
136 0           @lang_list = $io->chomp->slurp;
137             }
138             else {
139             @lang_list = map {
140 0           s/^Category://;
  0            
141 0           $_;
142             } $self->get_category(LANGS_CATEGORY);
143 0           $io->assert->println($_) for @lang_list;
144             }
145 0           my $langs = YAML::PP::LoadFile('Conf/lang.yaml');
146 0           my $meta_langs = {};
147 0           for my $name (keys %$langs) {
148 0           my $text = $langs->{$name};
149 0           my $info = {};
150 0           $info->{name} = $name;
151 0           $info->{url} = $name;
152 0           $info->{url} =~ s/ /_/g;
153 0           $info->{path} = $name;
154 0           $info->{path} =~ s/[\ \/\*\!]/-/g;
155 0 0         $text =~ s/^\.(\S+)\ ?// or die "ERROR: '$name: $text'";
156 0           $info->{ext} = $1;
157 0           $meta_langs->{lc $name} = $info;
158             }
159              
160             # Common mutations:
161 0           my $m = $meta_langs;
162 0           my $alias = YAML::PP::LoadFile('Conf/alias.yaml');
163              
164 0           for (sort keys %{$alias->{lang}}) {
  0            
165 0           $m->{$_} = $m->{$alias->{lang}{$_}};
166             }
167              
168 0           $self->log("Dump Meta/Lang.yaml");
169 0           my $yaml = YAML::PP::Dump($meta_langs);
170 0           $yaml =~ s/FALSE/'FALSE'/g; # Fix YAML for Ruby
171 0           io->file("Meta/Lang.yaml")->assert->print($yaml);
172 0           return $meta_langs;
173             }
174              
175             sub build_bot {
176 0     0 0   my ($self) = @_;
177 0 0         ROSETTACODE_API_URL =~ m!^(https?)://([^/]+)/(.*)/api.php$! or die;
178 0           my ($protocol, $host, $path) = ($1, $2, $3);
179 0           MediaWiki::Bot->new({
180             agent =>
181             'rosettacodedata/' . $VERSION
182             . ' (https://github.com/ingydotnet/rosettacode-pm) MediaWiki::Bot/'
183             . MediaWiki::Bot->VERSION,
184             assert => 'bot',
185             protocol => $protocol,
186             host => $host,
187             path => $path,
188             });
189             }
190              
191             sub get_text {
192 0     0 0   my ($self, $name) = @_;
193 0           $self->log("Fetch MediaWiki text for '$name'");
194 0           return $self->bot->get_text($name);
195             }
196              
197             sub get_category {
198 0     0 0   my ($self, $category) = @_;
199 0           $self->log("Fetch MediaWiki category '$category'");
200 0           $self->bot->get_pages_in_category($category, {max => 0});
201             }
202              
203             sub write_file {
204 0     0 0   my ($self, $file, $content, $indent) = @_;
205 0           $self->log(' ' x $indent . "Write $file");
206 0           io->file($file)->assert->utf8->print($content);
207             }
208              
209             sub write_symlink {
210 0     0 0   my ($self, $source, $target, $indent) = @_;
211 0           $self->log(' ' x $indent . "Symlink $source -> $target");
212 0           io->link($source)->assert->symlink($target);
213             }
214              
215             sub dump_file {
216 0     0 0   my ($self, $file, $object, $indent) = @_;
217 0           $self->log(' ' x $indent . "Dump $file");
218 0           YAML::PP::DumpFile($file, $object);
219             }
220              
221             sub log {
222 0     0 0   my ($self, $string, @args) = @_;
223 0           my $time = gmtime();
224 0           $self->log_io->append(sprintf "<$time> $string\n", @args);
225             }
226              
227             1;