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 = '1018';
5 5     5   239852 use feature 'state';
  5         16  
  5         444  
6              
7 5     5   2049 use Moo;
  5         37641  
  5         27  
8 5     5   6976 use MooX::Cmd;
  5         17069  
  5         26  
9 5     5   885076 use MooX::Options;
  5         93481  
  5         32  
10 5     5   147405 use App::DuckPAN::Config;
  5         23  
  5         148  
11 5     5   31 use File::Which;
  5         10  
  5         225  
12 5     5   1515 use Class::Load ':all';
  5         8062  
  5         679  
13 5     5   1921 use HTTP::Request::Common qw( GET POST );
  5         82216  
  5         293  
14 5     5   1769 use HTTP::Status;
  5         14597  
  5         966  
15 5     5   34 use List::Util qw( first max );
  5         12  
  5         310  
16 5     5   2503 use LWP::UserAgent;
  5         61645  
  5         155  
17 5     5   1799 use LWP::Simple;
  5         28772  
  5         30  
18 5     5   2886 use Parse::CPAN::Packages::Fast;
  5         145930  
  5         169  
19 5     5   2311 use File::Temp qw/ :POSIX /;
  5         28897  
  5         480  
20 5     5   2395 use Term::ANSIColor;
  5         26003  
  5         260  
21 5     5   1951 use Term::UI;
  5         89541  
  5         176  
22 5     5   37 use Term::ReadLine;
  5         10  
  5         85  
23 5     5   23 use Carp;
  5         9  
  5         226  
24 5     5   29 use Encode;
  5         11  
  5         283  
25 5     5   2048 use Perl::Version;
  5         9835  
  5         166  
26 5     5   34 use Path::Tiny;
  5         35  
  5         231  
27 5     5   1760 use open qw/:std :utf8/;
  5         4250  
  5         32  
28 5     5   2419 use App::DuckPAN::Cmd::Help;
  5         15  
  5         140  
29 5     5   2031 use DDG::Meta::Data;
  5         2523989  
  5         737  
30              
31 5     5   73 no warnings 'uninitialized';
  5         12  
  5         18713  
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   3 my ($self) = @_;
106 1   33     4 my $class = ref $self || $self;
107 1   33     12 my $version = $class->VERSION || $self->dev_version;
108 1         7 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   1291 my ( $self ) = @_;
222 1         13 my $agent = LWP::UserAgent->new;
223 1         307 $agent->agent($self->_ua_string);
224 1         62 $agent->env_proxy;
225 1 50       129 $agent->proxy( http => $self->http_proxy ) if $self->has_http_proxy;
226 1         6 return $agent;
227             }
228              
229             has server_hostname => (
230             is => 'ro',
231             lazy => 1,
232             builder => 1,
233             );
234              
235 1 50   1   16 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   34 my ( $self ) = @_;
251 3 100       51 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   14 load_class('App::DuckPAN::Perl');
265 1         39 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   15 load_class('App::DuckPAN::DDG');
276 1         40 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 171 my ( $self, $args, $chain ) = @_;
292 1         3 my @arr_args = grep { $_ !~ /^-/} @{$args}; # Command line switches make it here, so we try to remove
  0         0  
  1         3  
293 1 50       13 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 362 my ($self, $phrase) = @_;
416 7         13 my $camel = $phrase;
417              
418 7         72 $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         23 $camel =~ s/\s+$//;
428              
429 7         43 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              
438 0 0       0 if ($name =~ /^DDG::/) {
439 0         0 $ia = DDG::Meta::Data->get_ia(module => $name);
440 0 0       0 $ia = $ia->[0] if $ia;
441             }
442             else {
443 0 0       0 $ia = $name =~ /_/
444             ? DDG::Meta::Data->get_ia(id => $name)
445             : DDG::Meta::Data->get_ia(id => $self->camel_to_underscore($name));
446             }
447              
448 0         0 return $ia;
449             }
450              
451             sub check_requirements {
452 0     0 0 0 my ($self) = @_;
453              
454 0 0       0 if (!$self->check) {
455 0         0 $self->emit_notice("Requirements checking was disabled...");
456 0         0 return 1;
457             }
458 0         0 my $signal_file = $self->cfg->cache_path->child('perl_checked');
459 0 0       0 my $last_checked_perl = ($signal_file->exists) ? $signal_file->stat->mtime : 0;
460 0 0       0 if ((time - $last_checked_perl) <= $self->cachesec) {
461 0         0 $self->emit_debug("Perl module versions recently checked, skipping requirements check...");
462             }
463             else {
464 0         0 $self->emit_info("Checking for DuckPAN requirements...");
465              
466 0 0 0     0 $self->emit_and_exit(1, 'Requirements check failed')
      0        
      0        
      0        
467             unless (
468             $self->check_perl &&
469             $self->check_app_duckpan &&
470             $self->check_ddg &&
471             $self->check_ssh &&
472             $self->check_git);
473             }
474 0         0 $signal_file->touch;
475              
476 0         0 return 1;
477             }
478              
479             sub get_local_ddg_version {
480 1     1 0 1407 my ( $self ) = @_;
481 1         24 return $self->perl->get_local_version('DDG');
482             }
483              
484             sub get_local_app_duckpan_version {
485 0     0 0 0 my ( $self ) = @_;
486 0         0 return $self->perl->get_local_version('App::DuckPAN');
487             }
488              
489              
490             sub check_git {
491 0     0 0 0 my ( $self ) = @_;
492 0         0 my $ok = 0;
493 0         0 $self->emit_info("Checking for git...");
494 0 0       0 if (my $git = which('git')) {
495 0         0 my $version_string = `$git --version`;
496 0 0       0 if ($version_string =~ m/git version (\d+)\.(\d+)/) {
497 0 0 0     0 if ($1 <= 1 && $2 < 7) {
498 0         0 $self->emit_error("require minimum git 1.7");
499             }
500             else {
501 0         0 $self->emit_debug($git);
502 0         0 $ok = 1;
503             }
504             }
505             else {
506 0         0 $self->emit_error("Unknown git version!");
507             }
508             }
509             else {
510 0         0 $self->emit_error("git not found");
511             }
512 0         0 return $ok;
513             }
514              
515             sub check_ssh {
516 0     0 0 0 my ( $self ) = @_;
517 0         0 my $ok = 0;
518 0         0 $self->emit_info("Checking for ssh...");
519 0 0       0 if (my $ssh = which('ssh')) {
520 0         0 $self->emit_debug($ssh);
521 0         0 $ok = 1;
522             }
523             else {
524 0         0 $self->emit_error('ssh not found');
525             }
526 0         0 return $ok;
527             }
528              
529             my %perl_versions = (
530             required => Perl::Version->new('v5.14'),
531             recommended => Perl::Version->new('v5.16'),
532             );
533              
534             sub check_perl {
535 0     0 0 0 my ($self) = @_;
536              
537 0         0 $self->emit_info("Checking perl version... ");
538 0         0 my $installed_version = Perl::Version->new($]);
539              
540 0         0 my $ok = 1;
541 0 0       0 if ($installed_version->vcmp($perl_versions{required}) < 0) {
    0          
542 0         0 $self->emit_error('perl ' . $perl_versions{required}->normal . ' or higher is required. ', $installed_version->normal . ' is installed.');
543 0         0 $ok = 0;
544             }
545             elsif ($installed_version->vcmp($perl_versions{recommended}) < 0) {
546 0         0 $self->emit_notice('perl ' . $perl_versions{recommended}->normal . ' or higher is recommended. ',
547             $installed_version->normal . " is installed.");
548             }
549             else {
550 0         0 $self->emit_debug($installed_version->normal);
551             }
552              
553 0         0 return $ok;
554             }
555              
556             sub check_app_duckpan {
557 0     0 0 0 my ($self) = @_;
558 0         0 my $ok = 1;
559 0   0     0 my $pin_version = $ENV{"DuckPAN"} || undef;
560 0         0 my $installed_version = $self->get_local_app_duckpan_version;
561 0 0 0     0 return $ok if $installed_version && $installed_version == $self->dev_version;
562 0         0 $self->emit_info("Checking for latest App::DuckPAN... ");
563 0         0 my $packages = $self->duckpan_packages;
564 0         0 my $module = $packages->package('App::DuckPAN');
565 0         0 my $latest = $self->duckpan . 'authors/id/' . $module->distribution->pathname;
566 0         0 my $latest_version = version->parse($module->version);
567              
568 0 0       0 if ($installed_version >= $latest_version) {
569 0         0 my $msg = "App::DuckPAN version: $installed_version";
570 0 0       0 $msg .= " (duckpan has " . $module->version . ")" if $installed_version ne $module->version;
571 0         0 $self->emit_debug($msg);
572             }
573             else {
574 0         0 my @msg = (
575             "You have version $installed_version, latest is " . $module->version . "!",
576             "Please install the latest App::DuckPAN package with: duckpan upgrade"
577             );
578 0         0 $self->emit_notice(@msg);
579             }
580 0         0 return $ok;
581             }
582              
583             sub check_ddg {
584 0     0 0 0 my ($self) = @_;
585 0         0 my $ok = 1;
586 0   0     0 my $pin_version = $ENV{"DDG"} || undef;
587 0         0 my $installed_version = $self->get_local_ddg_version;
588 0 0 0     0 return $ok if $installed_version && $installed_version == $self->dev_version;
589 0         0 warn "installing DDG";
590 0         0 $self->emit_info("Checking for latest DDG Perl package...");
591 0         0 my $packages = $self->duckpan_packages;
592 0         0 my $module = $packages->package('DDG');
593 0         0 my $latest = $self->duckpan . 'authors/id/' . $module->distribution->pathname;
594 0         0 my $latest_version = version->parse($module->version);
595              
596 0 0 0     0 if ($installed_version >= $latest_version) {
    0          
597 0         0 my $msg = "DDG version: $installed_version";
598 0 0       0 $msg .= " (duckpan has $latest_version )" if $installed_version ne $latest_version;
599 0         0 $self->emit_debug($msg);
600             }
601             elsif ($pin_version && $pin_version < $latest_version){
602 0         0 my @msg = (
603             "A newer version of DDG exists: $latest_version.",
604             "You have the version pinned to: $pin_version. Please update your version pin!"
605             );
606 0         0 $self->emit_notice(@msg);
607             }
608             else {
609 0 0       0 if ($installed_version) {
610 0         0 my @msg = (
611             "You have version $installed_version, latest is " . $module->version . "!",
612             "Please install the latest DDG package with: duckpan DDG"
613             );
614 0         0 $self->emit_notice(@msg);
615             }
616             else {
617 0         0 $self->perl->duckpan_install('DDG');
618             }
619             }
620 0         0 return $ok;
621             }
622              
623             sub get_ia_type {
624 0     0 0 0 my ($self) = @_;
625              
626 0         0 my $repo = $self->repository;
627 0 0       0 unless ($repo) {
628 0         0 $self->emit_and_exit(-1,
629             'Must be run from within an Instant Answer repository'
630             );
631             }
632              
633 0         0 return $repo;
634             }
635              
636             sub empty_cache {
637 0     0 0 0 my ($self) = @_;
638             # Clear cache so share files are written into cache
639 0         0 my $cache = $self->cfg->cache_path;
640 0         0 $self->emit_info("Emptying DuckPAN cache...");
641 0         0 $cache->remove_tree({keep_root => 1});
642 0         0 $self->emit_info("DuckPAN cache emptied");
643             }
644              
645             has repository => (
646             is => 'rwp',
647             doc => 'Instant Answer repository from which DuckPAN was run.',
648             trigger => \&_check_repository,
649             );
650              
651             sub _get_repository_config {
652 0     0   0 my ($self, $by, $lookup, $single) = @_;
653 0   0     0 $single //= 0;
654 0         0 my @repos = grep { $_->{$by} eq $lookup } @{$self->ia_types};
  0         0  
  0         0  
655 0 0       0 $single ? (@repos > 1 ? undef : $repos[0]) : @repos;
    0          
656             }
657              
658             sub _check_repository {
659 0     0   0 my ($self, $repo) = @_;
660 0         0 my $path_basename = $repo->{path_basename};
661             $self->emit_and_exit(-1,
662             "'$path_basename' is currently not supported by DuckPAN."
663 0 0       0 ) unless $repo->{supported};
664             }
665              
666             # Ensure further commands are run from the 'root' of the Instant
667             # Answer repository.
668             sub initialize_working_directory {
669 0     0 0 0 my $self = shift;
670 0         0 my $check_path = Path::Tiny::cwd;
671 0         0 while (!$check_path->is_rootdir()) {
672 0 0       0 if (my $repo = $self->_get_repository_config(
673             path_basename => $check_path->basename, 1
674             )) {
675 0         0 $self->_set_repository($repo);
676 0         0 chdir $check_path->stringify;
677 0         0 last;
678             }
679             }
680             continue {
681 0         0 $check_path = $check_path->parent;
682             }
683             }
684              
685             sub BUILD {
686 3     3 0 24 my ($self) = @_;
687              
688 3 50       15 $self->emit_and_exit(1, 'We dont support Win32') if ($^O eq 'MSWin32');
689 3         51 my $env_config = $self->cfg->config_path->child('env.ini');
690 3 50       1144 $self->empty_cache if $self->empty;
691 3 50       13 if ($env_config->exists) {
692 0           my $env = Config::INI::Reader->read_file($env_config);
693 0 0         map { $ENV{$_} = $env->{'_'}{$_}; } keys %{$env->{'_'}} if $env->{'_'};
  0            
  0            
694             }
695             }
696              
697             1;
698              
699             __END__
700              
701             =pod
702              
703             =head1 NAME
704              
705             App::DuckPAN - The DuckDuckGo DuckPAN client
706              
707             =head1 VERSION
708              
709             version 1018
710              
711             =head1 DuckPAN
712              
713             The DuckDuckHack Testing Tool
714              
715             =head2 SYNPOSIS
716              
717             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.
718              
719             C<duckpan help> provides more detailed information.
720              
721             =head1 SEE ALSO
722              
723             =over 4
724              
725             =item L<https://github.com/duckduckgo/p5-app-duckpan/>
726              
727             =item L<https://github.com/duckduckgo/>
728              
729             =item L<https://duckduckgo.com/>
730              
731             =item L<https://duck.co/>
732              
733             =item L<http://duckpan.org/>
734              
735             =back
736              
737             =head1 CONTRIBUTION
738              
739             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.
740              
741             =head1 SUPPORT
742              
743             B<IRC>:
744              
745             We invite you to join us at B<#duckduckgo> on B<irc.freenode.net> for any queries and lively discussion.
746              
747             B<Repository>:
748              
749             L<https://github.com/duckduckgo/p5-app-duckpan>
750              
751             B<Issue Tracker>:
752              
753             L<https://github.com/duckduckgo/p5-app-duckpan/issues>
754              
755             =head1 AUTHOR
756              
757             DuckDuckGo <open@duckduckgo.com>, Zach Thompson <zach@duckduckgo.com>, Zaahir Moolla <moollaza@duckduckgo.com>, Torsten Raudssus <torsten@raudss.us> L<https://raudss.us/>
758              
759             =head1 COPYRIGHT AND LICENSE
760              
761             This software is Copyright (c) 2013 by DuckDuckGo, Inc. L<https://duckduckgo.com/>.
762              
763             This is free software, licensed under:
764              
765             The Apache License, Version 2.0, January 2004
766              
767             =cut