File Coverage

blib/lib/App/DuckPAN.pm
Criterion Covered Total %
statement 108 305 35.4
branch 9 98 9.1
condition 2 78 2.5
subroutine 35 62 56.4
pod 0 23 0.0
total 154 566 27.2


line stmt bran cond sub pod time code
1             package App::DuckPAN;
2             our $AUTHORITY = 'cpan:DDG';
3             # ABSTRACT: The DuckDuckGo DuckPAN client
4             $App::DuckPAN::VERSION = '1017';
5 5     5   241380 use feature 'state';
  5         5  
  5         402  
6              
7 5     5   2412 use Moo;
  5         39471  
  5         29  
8 5     5   7723 use MooX::Cmd;
  5         20131  
  5         27  
9 5     5   825626 use MooX::Options;
  5         107411  
  5         38  
10 5     5   156107 use App::DuckPAN::Config;
  5         11  
  5         139  
11 5     5   25 use File::Which;
  5         6  
  5         219  
12 5     5   1602 use Class::Load ':all';
  5         12472  
  5         825  
13 5     5   2534 use HTTP::Request::Common qw( GET POST );
  5         96526  
  5         367  
14 5     5   2663 use HTTP::Status;
  5         16420  
  5         1267  
15 5     5   30 use List::Util qw( first max );
  5         8  
  5         308  
16 5     5   3327 use LWP::UserAgent;
  5         72333  
  5         169  
17 5     5   2524 use LWP::Simple;
  5         32869  
  5         35  
18 5     5   3649 use Parse::CPAN::Packages::Fast;
  5         172465  
  5         179  
19 5     5   2578 use File::Temp qw/ :POSIX /;
  5         28534  
  5         680  
20 5     5   3155 use Term::ANSIColor;
  5         28195  
  5         367  
21 5     5   2984 use Term::UI;
  5         106277  
  5         210  
22 5     5   41 use Term::ReadLine;
  5         9  
  5         97  
23 5     5   20 use Carp;
  5         7  
  5         268  
24 5     5   22 use Encode;
  5         7  
  5         372  
25 5     5   2856 use Perl::Version;
  5         9508  
  5         129  
26 5     5   26 use Path::Tiny;
  5         7  
  5         238  
27 5     5   2256 use open qw/:std :utf8/;
  5         4164  
  5         25  
28 5     5   2666 use App::DuckPAN::Cmd::Help;
  5         14  
  5         148  
29 5     5   2551 use DDG::Meta::Data;
  5         2091840  
  5         689  
30              
31 5     5   66 no warnings 'uninitialized';
  5         11  
  5         18348  
32              
33             option dev_version => (
34             is => 'ro',
35             default => 9.999,
36             doc => 'Version used when using unreleased code, e.g. git repos'
37             );
38              
39             option check => (
40             is => 'ro',
41             lazy => 1,
42             negativable => 1,
43             default => sub { 1 },
44             doc => 'perform requirements checks. turn off with --no-check',
45             );
46              
47             option empty => (
48             is => 'ro',
49             lazy => 1,
50             short => 'e',
51             default => sub { 0 },
52             doc => 'empty duckpan cache at start-up',
53             );
54              
55             has cachesec => (
56             is => 'ro',
57             lazy => 1,
58             default => sub { 60 * 60 * 4 }, # 4 hours by default
59             );
60              
61             option colors => (
62             is => 'ro',
63             lazy => 1,
64             negativable => 1,
65             default => sub { 1 },
66             doc => 'use color output. turn off with --no-colors',
67             );
68              
69             option verbose => (
70             is => 'ro',
71             lazy => 1,
72             short => 'v|debug',
73             default => sub { 0 },
74             doc => 'provide expanded output during operation',
75             );
76              
77             has duckpan_packages => (
78             is => 'ro',
79             lazy => 1,
80             builder => 1,
81             );
82              
83             sub _build_duckpan_packages {
84 0     0   0 my $self = shift;
85              
86 0         0 my $gz = '02packages.details.txt.gz';
87 0         0 my $package_url = join('/', $self->duckpan, 'modules', $gz);
88 0         0 my $mirror_to = $self->cfg->cache_path->child($gz);
89              
90 0 0       0 if (is_error(mirror($package_url, $mirror_to))) {
91 0         0 $self->emit_and_exit(1, "Cannot download $package_url");
92             }
93              
94 0         0 return Parse::CPAN::Packages::Fast->new($mirror_to->stringify);
95             }
96              
97             option duckpan => (
98             is => 'ro',
99             lazy => 1,
100             default => sub { 'http://duckpan.org/' },
101             doc => 'URI for the duckpan package server. defaults to "https://duckpan.org/"',
102             );
103              
104             sub _ua_string {
105 1     1   2 my ($self) = @_;
106 1   33     4 my $class = ref $self || $self;
107 1   33     11 my $version = $class->VERSION || $self->dev_version;
108 1         5 return "$class/$version";
109             }
110              
111             option http_proxy => (
112             is => 'ro',
113             predicate => 1,
114             doc => 'proxy to use for outbound HTTP requests',
115             );
116              
117             option config => (
118             is => 'ro',
119             predicate => 1,
120             doc => 'path to config directory. defaults to "~/.duckpan/config"',
121             );
122              
123             option cache => (
124             is => 'ro',
125             predicate => 1,
126             doc => 'path to cache directory. defaults to "~/.duckpan/cache"',
127             );
128              
129             has term => (
130             is => 'ro',
131             lazy => 1,
132             builder => 1,
133             );
134              
135 0     0   0 sub _build_term { Term::ReadLine->new('duckpan') }
136              
137             has ia_types => (
138             is => 'ro',
139             lazy => 1,
140             builder => 1,
141             );
142              
143             sub _build_ia_types {
144 0     0   0 my $ddg_path = path('lib', 'DDG');
145 0         0 my $t_dir = path('t');
146             return [{
147 0         0 name => 'Goodie',
148             dir => $ddg_path->child('Goodie'),
149             supported => 1,
150             path_basename => 'zeroclickinfo-goodies',
151             templates => {
152             code => {
153             in => path('template', 'lib', 'DDG', 'Goodie', 'Example.pm'),
154             out => $ddg_path->child('Goodie')
155             },
156             test => {
157             in => path('template', 't', 'Example.t'),
158             out => $t_dir
159             },
160             },
161             },
162             {
163             name => 'Spice',
164             dir => $ddg_path->child('Spice'),
165             supported => 1,
166             path_basename => 'zeroclickinfo-spice',
167             templates => {
168             code => {
169             in => path('template', 'lib', 'DDG', 'Spice', 'Example.pm'),
170             out => $ddg_path->child('Spice')
171             },
172             test => {
173             in => path('template', 't', 'Example.t'),
174             out => $t_dir
175             },
176             handlebars => {
177             in => path('template', 'share', 'spice', 'example', 'example.handlebars'),
178             out => path('share', 'spice')
179             },
180             js => {
181             in => path('template', 'share', 'spice', 'example', 'example.js'),
182             out => path('share', 'spice')
183             },
184             },
185             },
186             {
187             name => 'Fathead',
188             dir => $ddg_path->child('Fathead'),
189             supported => 1,
190             path_basename => 'zeroclickinfo-fathead',
191             },
192             {
193             name => 'Longtail',
194             dir => $ddg_path->child('Longtail'),
195             supported => 0,
196             path_basename => 'zeroclickinfo-longtail',
197             },
198             ];
199             }
200              
201             sub get_reply {
202 0     0 0 0 my ( $self, $prompt, %params ) = @_;
203 0         0 my $return = $self->term->get_reply( prompt => $prompt, %params );
204 0         0 Encode::_utf8_on($return);
205 0         0 return $return;
206             }
207              
208             sub ask_yn {
209 0     0 0 0 my ( $self, $prompt, %params ) = @_;
210              
211 0         0 return $self->term->ask_yn( prompt => $prompt, %params );
212             }
213              
214             has http => (
215             is => 'ro',
216             builder => 1,
217             lazy => 1,
218             );
219              
220             sub _build_http {
221 1     1   1230 my ( $self ) = @_;
222 1         15 my $agent = LWP::UserAgent->new;
223 1         231 $agent->agent($self->_ua_string);
224 1         37 $agent->env_proxy;
225 1 50       80 $agent->proxy( http => $self->http_proxy ) if $self->has_http_proxy;
226 1         5 return $agent;
227             }
228              
229             has server_hostname => (
230             is => 'ro',
231             lazy => 1,
232             builder => 1,
233             );
234              
235 1 50   1   311 sub _build_server_hostname { defined $ENV{APP_DUCKPAN_SERVER_HOSTNAME} ? $ENV{APP_DUCKPAN_SERVER_HOSTNAME} : 'duckduckgo.com' }
236              
237             has cfg => (
238             is => 'ro',
239             lazy => 1,
240             builder => 1,
241             handles => [qw(
242             config_path
243             config_file
244             set_config
245             get_config
246             )]
247             );
248              
249             sub _build_cfg {
250 3     3   897 my ( $self ) = @_;
251 3 100       49 App::DuckPAN::Config->new(
    50          
252             $self->has_config ? ( config_path => $self->config ) : (),
253             $self->has_cache ? ( cache_path => $self->cache ) : (),
254             );
255             }
256              
257             has perl => (
258             is => 'ro',
259             builder => 1,
260             lazy => 1,
261             );
262              
263             sub _build_perl {
264 1     1   359 load_class('App::DuckPAN::Perl');
265 1         33 App::DuckPAN::Perl->new( app => shift );
266             }
267              
268             has ddg => (
269             is => 'ro',
270             builder => 1,
271             lazy => 1,
272             );
273              
274             sub _build_ddg {
275 1     1   318 load_class('App::DuckPAN::DDG');
276 1         39 App::DuckPAN::DDG->new( app => shift );
277             }
278              
279             has fathead => (
280             is => 'ro',
281             builder => 1,
282             lazy => 1,
283             );
284              
285             sub _build_fathead {
286 0     0   0 load_class('App::DuckPAN::Fathead');
287 0         0 App::DuckPAN::Fathead->new( app => shift );
288             }
289              
290             sub execute {
291 1     1 0 691 my ( $self, $args, $chain ) = @_;
292 1         2 my @arr_args = grep { $_ !~ /^-/} @{$args}; # Command line switches make it here, so we try to remove
  0         0  
  1         2  
293 1 50       11 App::DuckPAN::Cmd::Help->run(1) if scalar @arr_args == 0;
294 0 0       0 if (@arr_args) {
295 0         0 my (@modules, @left_args, $ddg);
296 0         0 for (@arr_args) {
297 0 0 0     0 if (/^www/i ||
    0 0        
      0        
      0        
      0        
      0        
      0        
      0        
      0        
298             /^dist/i ||
299             /^(ddg)$/i ||
300             /(opensourceduckduckgo)$/i ||
301             /^(goodie)$/i || /^(spice)$/i || /^(fathead)$/i || /^(longtail)$/i ||
302             /^ddgc?::/i ||
303             /^app/i) {
304 0         0 my $m = lc $1;
305              
306 0 0 0     0 if($m eq 'goodie' or $m eq 'spice' or $m eq 'fathead' or $m eq 'longtail'){
      0        
      0        
307 0         0 $_ = 'DDG::' . ucfirst($m) . 'Bundle::OpenSourceDuckDuckGo';
308 0         0 $m = 'opensourceduckduckgo';
309             }
310              
311 0 0 0     0 if($m eq 'opensourceduckduckgo' && !$ddg){
    0 0        
312 0         0 unshift @modules, 'DDG';
313 0         0 $ddg = 1;
314             }
315 0         0 elsif($m eq 'ddg' && $ddg){ next }
316 0         0 push @modules, $_;
317             }
318             elsif (m/^duckpan|update|(upgrade|(reinstall|latest))$/i) {
319 0         0 my ($all_modules, $reinstall_latest) = map { lc } ($1, $2);
  0         0  
320 0 0       0 $self->empty_cache unless $self->empty;
321 0         0 push @modules, 'App::DuckPAN';
322 0 0       0 if($all_modules){
323 0         0 push @modules, 'DDG';
324 0 0       0 unshift @modules, $reinstall_latest if $reinstall_latest;
325             }
326             }
327             else {
328 0         0 push @left_args, $_;
329             }
330             }
331 0 0       0 exit $self->perl->duckpan_install(@modules) unless @left_args;
332             }
333 0         0 $self->emit_and_exit(0, "Unknown command. Use `duckpan help` to see the list of available DuckPAN commands.");
334             }
335              
336             has standard_prefix_width => (
337             is => 'ro',
338             default => sub { 9 },
339             );
340              
341             sub _output_prefix {
342 0     0   0 my ($self, $word, $color) = @_;
343              
344 0         0 my $extra_spaces = max(0, $self->standard_prefix_width - length($word) - 2 ); # 2 []s to be added.
345              
346 0         0 my $full_prefix = '[' . uc $word . ']' . (' ' x $extra_spaces);
347              
348 0 0       0 return ($self->colors) ? colored($full_prefix, $color) : $full_prefix;
349             }
350              
351             sub emit_info {
352 0     0 0 0 my ($self, @msg) = @_;
353              
354 0         0 $self->_print_msg(*STDOUT, '', @msg);
355             }
356              
357             sub emit_error {
358 0     0 0 0 my ($self, @msg) = @_;
359              
360 0         0 state $prefix = $self->_output_prefix('ERROR', 'red bold');
361              
362 0         0 $self->_print_msg(*STDERR, $prefix, @msg);
363             }
364              
365             sub emit_and_exit {
366 0     0 0 0 my ($self, $exit_code, @msg) = @_;
367              
368 0         0 state $prefix = $self->_output_prefix('FATAL', 'bright_red bold');
369              
370 0 0       0 if ($exit_code == 0) { # This is just an info message.
371 0         0 $self->emit_info(@msg);
372             }
373             else { # But if it's an unhappy exit
374 0         0 $self->_print_msg(*STDERR, $prefix, @msg);
375             }
376              
377 0         0 exit $exit_code;
378             }
379              
380             sub emit_debug {
381 0     0 0 0 my ($self, @msg) = @_;
382              
383 0 0       0 return unless $self->verbose; # only show messages in verbose mode.
384              
385 0         0 return $self->_print_msg(*STDOUT, '', @msg);
386             }
387              
388             sub emit_notice {
389 0     0 0 0 my ($self, @msg) = @_;
390              
391 0         0 state $prefix = $self->_output_prefix('NOTICE', 'yellow bold');
392              
393 0         0 $self->_print_msg(*STDOUT, $prefix, @msg);
394             }
395              
396             sub _print_msg {
397 0     0   0 my ($self, $fh, $prefix, @lines) = @_;
398              
399 0         0 foreach my $line (map { $prefix . $_ } grep { $_ } @lines) {
  0         0  
  0         0  
400 0         0 print $fh $line . "\n";
401             }
402             }
403              
404             sub camel_to_underscore {
405 0     0 0 0 my ($self, $name) = @_;
406             # Replace first capital by lowercase
407             # if followed my lowercase.
408 0         0 $name =~ s/^([A-Z])([a-z])/lc($1).$2/ge;
  0         0  
409             # Substitute camelCase to camel_case
410 0         0 $name =~ s/([a-z])([A-Z])/$1.'_'.lc($2)/ge;
  0         0  
411 0         0 return lc $name;
412             }
413              
414             sub phrase_to_camel {
415 7     7 0 247 my ($self, $phrase) = @_;
416 7         8 my $camel = $phrase;
417              
418 7         69 $camel =~ s/
419             (?: # if a character:
420             \s+ # - follows spaces
421             | (?<=::) # - or follows ::
422             | ^ # - or is the first character
423             )(.) # (the character)
424             /\U$1/gx; # then uppercase it (preceding spaces are removed)
425              
426             # remove trailing spaces
427 7         17 $camel =~ s/\s+$//;
428              
429 7         36 return $camel;
430             }
431              
432             # Normalize an Instant Answer name to a standard form.
433             # Returns undef if an IA matching the given name cannot be found.
434             sub get_ia_by_name {
435 0     0 0 0 my ($self, $name) = @_;
436 0         0 my $ia;
437 0 0       0 if ($name =~ /^DDG::/) {
438 0         0 $ia = DDG::Meta::Data->get_ia(module => $name);
439 0 0       0 $ia = $ia->[0] if $ia;
440             }
441             else {
442 0 0       0 $ia = $name =~ /_/
443             ? DDG::Meta::Data->get_ia(id => $name)
444             : DDG::Meta::Data->get_ia(id => $self->camel_to_underscore($name));
445             }
446 0         0 return $ia;
447             }
448              
449             sub check_requirements {
450 0     0 0 0 my ($self) = @_;
451              
452 0 0       0 if (!$self->check) {
453 0         0 $self->emit_notice("Requirements checking was disabled...");
454 0         0 return 1;
455             }
456 0         0 my $signal_file = $self->cfg->cache_path->child('perl_checked');
457 0 0       0 my $last_checked_perl = ($signal_file->exists) ? $signal_file->stat->mtime : 0;
458 0 0       0 if ((time - $last_checked_perl) <= $self->cachesec) {
459 0         0 $self->emit_debug("Perl module versions recently checked, skipping requirements check...");
460             }
461             else {
462 0         0 $self->emit_info("Checking for DuckPAN requirements...");
463              
464 0 0 0     0 $self->emit_and_exit(1, 'Requirements check failed')
      0        
      0        
      0        
465             unless (
466             $self->check_perl &&
467             $self->check_app_duckpan &&
468             $self->check_ddg &&
469             $self->check_ssh &&
470             $self->check_git);
471             }
472 0         0 $signal_file->touch;
473              
474 0         0 return 1;
475             }
476              
477             sub get_local_ddg_version {
478 1     1 0 1252 my ( $self ) = @_;
479 1         24 return $self->perl->get_local_version('DDG');
480             }
481              
482             sub get_local_app_duckpan_version {
483 0     0 0 0 my ( $self ) = @_;
484 0         0 return $self->perl->get_local_version('App::DuckPAN');
485             }
486              
487              
488             sub check_git {
489 0     0 0 0 my ( $self ) = @_;
490 0         0 my $ok = 0;
491 0         0 $self->emit_info("Checking for git...");
492 0 0       0 if (my $git = which('git')) {
493 0         0 my $version_string = `$git --version`;
494 0 0       0 if ($version_string =~ m/git version (\d+)\.(\d+)/) {
495 0 0 0     0 if ($1 <= 1 && $2 < 7) {
496 0         0 $self->emit_error("require minimum git 1.7");
497             }
498             else {
499 0         0 $self->emit_debug($git);
500 0         0 $ok = 1;
501             }
502             }
503             else {
504 0         0 $self->emit_error("Unknown git version!");
505             }
506             }
507             else {
508 0         0 $self->emit_error("git not found");
509             }
510 0         0 return $ok;
511             }
512              
513             sub check_ssh {
514 0     0 0 0 my ( $self ) = @_;
515 0         0 my $ok = 0;
516 0         0 $self->emit_info("Checking for ssh...");
517 0 0       0 if (my $ssh = which('ssh')) {
518 0         0 $self->emit_debug($ssh);
519 0         0 $ok = 1;
520             }
521             else {
522 0         0 $self->emit_error('ssh not found');
523             }
524 0         0 return $ok;
525             }
526              
527             my %perl_versions = (
528             required => Perl::Version->new('v5.14'),
529             recommended => Perl::Version->new('v5.16'),
530             );
531              
532             sub check_perl {
533 0     0 0 0 my ($self) = @_;
534              
535 0         0 $self->emit_info("Checking perl version... ");
536 0         0 my $installed_version = Perl::Version->new($]);
537              
538 0         0 my $ok = 1;
539 0 0       0 if ($installed_version->vcmp($perl_versions{required}) < 0) {
    0          
540 0         0 $self->emit_error('perl ' . $perl_versions{required}->normal . ' or higher is required. ', $installed_version->normal . ' is installed.');
541 0         0 $ok = 0;
542             }
543             elsif ($installed_version->vcmp($perl_versions{recommended}) < 0) {
544 0         0 $self->emit_notice('perl ' . $perl_versions{recommended}->normal . ' or higher is recommended. ',
545             $installed_version->normal . " is installed.");
546             }
547             else {
548 0         0 $self->emit_debug($installed_version->normal);
549             }
550              
551 0         0 return $ok;
552             }
553              
554             sub check_app_duckpan {
555 0     0 0 0 my ($self) = @_;
556 0         0 my $ok = 1;
557 0   0     0 my $pin_version = $ENV{"DuckPAN"} || undef;
558 0         0 my $installed_version = $self->get_local_app_duckpan_version;
559 0 0 0     0 return $ok if $installed_version && $installed_version == $self->dev_version;
560 0         0 $self->emit_info("Checking for latest App::DuckPAN... ");
561 0         0 my $packages = $self->duckpan_packages;
562 0         0 my $module = $packages->package('App::DuckPAN');
563 0         0 my $latest = $self->duckpan . 'authors/id/' . $module->distribution->pathname;
564 0         0 my $latest_version = version->parse($module->version);
565              
566 0 0       0 if ($installed_version >= $latest_version) {
567 0         0 my $msg = "App::DuckPAN version: $installed_version";
568 0 0       0 $msg .= " (duckpan has " . $module->version . ")" if $installed_version ne $module->version;
569 0         0 $self->emit_debug($msg);
570             }
571             else {
572 0         0 my @msg = (
573             "You have version $installed_version, latest is " . $module->version . "!",
574             "Please install the latest App::DuckPAN package with: duckpan upgrade"
575             );
576 0         0 $self->emit_notice(@msg);
577             }
578 0         0 return $ok;
579             }
580              
581             sub check_ddg {
582 0     0 0 0 my ($self) = @_;
583 0         0 my $ok = 1;
584 0   0     0 my $pin_version = $ENV{"DDG"} || undef;
585 0         0 my $installed_version = $self->get_local_ddg_version;
586 0 0 0     0 return $ok if $installed_version && $installed_version == $self->dev_version;
587 0         0 warn "installing DDG";
588 0         0 $self->emit_info("Checking for latest DDG Perl package...");
589 0         0 my $packages = $self->duckpan_packages;
590 0         0 my $module = $packages->package('DDG');
591 0         0 my $latest = $self->duckpan . 'authors/id/' . $module->distribution->pathname;
592 0         0 my $latest_version = version->parse($module->version);
593              
594 0 0 0     0 if ($installed_version >= $latest_version) {
    0          
595 0         0 my $msg = "DDG version: $installed_version";
596 0 0       0 $msg .= " (duckpan has $latest_version )" if $installed_version ne $latest_version;
597 0         0 $self->emit_debug($msg);
598             }
599             elsif ($pin_version && $pin_version < $latest_version){
600 0         0 my @msg = (
601             "A newer version of DDG exists: $latest_version.",
602             "You have the version pinned to: $pin_version. Please update your version pin!"
603             );
604 0         0 $self->emit_notice(@msg);
605             }
606             else {
607 0 0       0 if ($installed_version) {
608 0         0 my @msg = (
609             "You have version $installed_version, latest is " . $module->version . "!",
610             "Please install the latest DDG package with: duckpan DDG"
611             );
612 0         0 $self->emit_notice(@msg);
613             }
614             else {
615 0         0 $self->perl->duckpan_install('DDG');
616             }
617             }
618 0         0 return $ok;
619             }
620              
621             sub get_ia_type {
622 0     0 0 0 my ($self) = @_;
623              
624 0         0 my $repo = $self->repository;
625 0 0       0 unless ($repo) {
626 0         0 $self->emit_and_exit(-1,
627             'Must be run from within an Instant Answer repository'
628             );
629             }
630              
631 0         0 return $repo;
632             }
633              
634             sub empty_cache {
635 0     0 0 0 my ($self) = @_;
636             # Clear cache so share files are written into cache
637 0         0 my $cache = $self->cfg->cache_path;
638 0         0 $self->emit_info("Emptying DuckPAN cache...");
639 0         0 $cache->remove_tree({keep_root => 1});
640 0         0 $self->emit_info("DuckPAN cache emptied");
641             }
642              
643             has repository => (
644             is => 'rwp',
645             doc => 'Instant Answer repository from which DuckPAN was run.',
646             trigger => \&_check_repository,
647             );
648              
649             sub _get_repository_config {
650 0     0   0 my ($self, $by, $lookup, $single) = @_;
651 0   0     0 $single //= 0;
652 0         0 my @repos = grep { $_->{$by} eq $lookup } @{$self->ia_types};
  0         0  
  0         0  
653 0 0       0 $single ? (@repos > 1 ? undef : $repos[0]) : @repos;
    0          
654             }
655              
656             sub _check_repository {
657 0     0   0 my ($self, $repo) = @_;
658 0         0 my $path_basename = $repo->{path_basename};
659             $self->emit_and_exit(-1,
660             "'$path_basename' is currently not supported by DuckPAN."
661 0 0       0 ) unless $repo->{supported};
662             }
663              
664             # Ensure further commands are run from the 'root' of the Instant
665             # Answer repository.
666             sub initialize_working_directory {
667 0     0 0 0 my $self = shift;
668 0         0 my $check_path = Path::Tiny::cwd;
669 0         0 while (!$check_path->is_rootdir()) {
670 0 0       0 if (my $repo = $self->_get_repository_config(
671             path_basename => $check_path->basename, 1
672             )) {
673 0         0 $self->_set_repository($repo);
674 0         0 chdir $check_path->stringify;
675 0         0 last;
676             }
677             }
678             continue {
679 0         0 $check_path = $check_path->parent;
680             }
681             }
682              
683             sub BUILD {
684 3     3 0 23 my ($self) = @_;
685              
686 3 50       17 $self->emit_and_exit(1, 'We dont support Win32') if ($^O eq 'MSWin32');
687 3         13 my $env_config = $self->cfg->config_path->child('env.ini');
688 3 50       1120 $self->empty_cache if $self->empty;
689 3 50       13 if ($env_config->exists) {
690 0           my $env = Config::INI::Reader->read_file($env_config);
691 0 0         map { $ENV{$_} = $env->{'_'}{$_}; } keys %{$env->{'_'}} if $env->{'_'};
  0            
  0            
692             }
693             }
694              
695             1;
696              
697             __END__
698              
699             =pod
700              
701             =head1 NAME
702              
703             App::DuckPAN - The DuckDuckGo DuckPAN client
704              
705             =head1 VERSION
706              
707             version 1017
708              
709             =head1 DuckPAN
710              
711             The DuckDuckHack Testing Tool
712              
713             =head2 SYNPOSIS
714              
715             DuckPAN is an application built to provide developers a testing environment for DuckDuckHack Instant Answers. It allows you to test instant answer triggers and preview their visual design and output.
716              
717             C<duckpan help> provides more detailed information.
718              
719             =head1 SEE ALSO
720              
721             =over 4
722              
723             =item L<https://github.com/duckduckgo/p5-app-duckpan/>
724              
725             =item L<https://github.com/duckduckgo/>
726              
727             =item L<https://duckduckgo.com/>
728              
729             =item L<https://duck.co/>
730              
731             =item L<http://duckpan.org/>
732              
733             =back
734              
735             =head1 CONTRIBUTION
736              
737             To contribute to DuckPAN, please visit L<https://github.com/duckduckgo/p5-app-duckpan>. We also welcome and encourage contributions from our community. Please visit L<http://duckduckhack.com/> to contribute new instant answers, or visit L<https://duck.co/ideas> to share your ideas and instant answer source suggestions.
738              
739             =head1 SUPPORT
740              
741             B<IRC>:
742              
743             We invite you to join us at B<#duckduckgo> on B<irc.freenode.net> for any queries and lively discussion.
744              
745             B<Repository>:
746              
747             L<https://github.com/duckduckgo/p5-app-duckpan>
748              
749             B<Issue Tracker>:
750              
751             L<https://github.com/duckduckgo/p5-app-duckpan/issues>
752              
753             =head1 AUTHOR
754              
755             DuckDuckGo <open@duckduckgo.com>, Zach Thompson <zach@duckduckgo.com>, Zaahir Moolla <moollaza@duckduckgo.com>, Torsten Raudssus <torsten@raudss.us> L<https://raudss.us/>
756              
757             =head1 COPYRIGHT AND LICENSE
758              
759             This software is Copyright (c) 2013 by DuckDuckGo, Inc. L<https://duckduckgo.com/>.
760              
761             This is free software, licensed under:
762              
763             The Apache License, Version 2.0, January 2004
764              
765             =cut