File Coverage

blib/lib/App/DuckPAN/DDG.pm
Criterion Covered Total %
statement 15 85 17.6
branch 0 28 0.0
condition 0 3 0.0
subroutine 5 8 62.5
pod 0 2 0.0
total 20 126 15.8


line stmt bran cond sub pod time code
1             package App::DuckPAN::DDG;
2             our $AUTHORITY = 'cpan:DDG';
3             # ABSTRACT: DDG related functionality of duckpan
4             $App::DuckPAN::DDG::VERSION = '1017';
5 2     2   2013 use Moo;
  2         4  
  2         11  
6             with 'App::DuckPAN::HasApp';
7              
8 2     2   440 use Module::Pluggable::Object;
  2         2  
  2         49  
9 2     2   7 use Class::Load ':all';
  2         5  
  2         279  
10 2     2   1308 use Data::Printer return_value => 'dump';
  2         22978  
  2         14  
11 2     2   1751 use List::Util qw (first);
  2         2  
  2         909  
12              
13             # This function tells the user which modules / instant answers failed to load.
14             sub show_failed_modules {
15 0     0 0   my ($self, $failed_to_load) = @_;
16              
17 0 0         if (%$failed_to_load) {
18 0           $self->app->emit_notice("These instant answers were not loaded:");
19 0           $self->app->emit_notice(p($failed_to_load, colored => $self->app->colors));
20             $self->app->emit_notice(
21             "To learn more about installing Perl dependencies, please read http://docs.duckduckhack.com/resources/other-dev-environments.html#dealing-with-installation-issues.",
22             "Note: You can ignore these errors if you're not working on these instant answers."
23 0 0   0     ) if first { /dependencies/ } values %$failed_to_load;
  0            
24             }
25             }
26              
27             sub get_blocks_from_current_dir {
28 0     0 0   my ($self, @args) = @_;
29              
30 0 0         $self->emit_and_exit(1, 'You need to have the DDG distribution installed', 'To get the installation command, please run: duckpan check')
31             unless ($self->app->get_local_ddg_version);
32              
33 0           my $type = $self->app->get_ia_type;
34             my $finder = Module::Pluggable::Object->new(
35 0           search_path => [$type->{dir}],
36             );
37 0 0         if (scalar @args == 0) {
38 0 0         $self->app->emit_and_exit(1, "No Fathead ID passed as argument.", "Please specify a Fathead ID e.g. 'duckpan server mdn_css'") if $type->{name} eq "Fathead";
39 0           my @plugins = $finder->plugins;
40 0           push @args, sort { $a cmp $b } @plugins;
  0            
41             @args = map {
42 0           $_ =~ s!/!::!g;
  0            
43 0           my @parts = split('::', $_);
44 0           shift @parts;
45 0           join('::', @parts);
46             } @args;
47             }
48             else {
49             my @fatheads = grep {
50 0           $type->{name} eq "Fathead"
  0            
51             } @args;
52 0           $self->app->fathead->selected(@fatheads);
53              
54             @args = grep {
55 0 0         $type->{name} eq "Spice" || $type->{name} eq "Goodie"
  0            
56             } @args;
57             }
58 0           require lib;
59 0           lib->import('lib');
60 0           $self->app->emit_info("Loading Instant Answers...");
61              
62             # This list contains all of the classes that loaded successfully.
63 0           my @successfully_loaded;
64              
65             # This hash contains all of the modules that failed.
66             # The key contains the module name and the value contains the dependency that wasn't met.
67             my %failed_to_load;
68              
69 0           my (%blocks_plugins, @UC_TRIGGERS);
70             # This loop goes through each Goodie / Spice, and it tries to load it.
71 0           foreach my $arg (@args) {
72             # Let's try to load each Goodie / Spice module
73             # and see if they load successfully.
74 0           my $class;
75 0 0         if (my $ia = $self->app->get_ia_by_name($arg)) {
76 0           $class = $ia->{perl_module};
77             }
78             else {
79 0           $failed_to_load{$arg} =
80             'Could not retrieve metadata - please ensure the Instant Answer page ' .
81             'is in development status or later.';
82 0           next;
83             }
84 0           my ($load_success, $load_error_message) = try_load_class($class);
85              
86             # If they load successfully, $load_success would be a 1.
87             # Otherwise, it would be a 0.
88 0 0         if ($load_success) {
89             # Since we only want the successful classes to trigger, we
90             # collect all of the ones that triggered successfully in a temporary list.
91 0           push @successfully_loaded, $class;
92              
93             # Display to the user when a class has been successfully loaded.
94 0           $self->app->emit_debug(" - $class (" . $class->triggers_block_type . ")");
95              
96 0 0         unless ($blocks_plugins{$class->triggers_block_type}) {
97 0           $blocks_plugins{$class->triggers_block_type} = [];
98             }
99              
100 0           my $triggers_block_type = $class->triggers_block_type;
101 0           push @{$blocks_plugins{$triggers_block_type}}, $class;
  0            
102              
103             # We could potentially do other IA-specific checks here
104             # Check for useless uppercase Words triggers so we can warn
105 0 0         if($triggers_block_type eq 'Words'){
106 0           my $trigger_types = $class->get_triggers;
107              
108 0           UC_TRIGGER: for my $triggers (values %$trigger_types){
109 0           for my $t (@$triggers){
110 0 0         if($t =~ /\p{Uppercase}/){
111 0           push @UC_TRIGGERS, $class;
112             #warn "is $t uppercase?\n";
113             # the first one found should be sufficient.
114 0           last UC_TRIGGER;
115             }
116             }
117             }
118             }
119             }
120             else {
121             # Get the module name that needs to be installed by the user.
122 0 0         if ($load_error_message =~ /Can't locate ([^\.]+).pm in \@INC/) {
123 0           $load_error_message = $1;
124 0           $load_error_message =~ s/\//::/g;
125              
126 0           $failed_to_load{$class} = "Please install $load_error_message and any other required dependencies to use this instant answer.";
127             }
128             else {
129             # We just set the value to whatever the error message was if it failed for some other reason.
130 0           $failed_to_load{$class} = $load_error_message;
131             }
132             }
133             }
134              
135             # Since @args can contain modules that we don't want to trigger (since they didn't load in the first place),
136             # and @successfully_loaded does, we just use what's in @successfully_loaded.
137 0           @args = @successfully_loaded;
138              
139             # Now let's tell the user why some of the modules failed.
140 0           $self->show_failed_modules(\%failed_to_load);
141              
142             # Always bail if we have no Instant Answers to work with.
143 0 0 0       unless (@successfully_loaded || $self->app->fathead->selected) {
144 0           $self->app->emit_and_exit(1, "No Instant Answers loaded.");
145             }
146              
147 0 0         if(@UC_TRIGGERS){
148 0           $self->app->emit_notice('Detected potential UPPERCASE triggers in the following instant answers. If yours is listed, check it out! Only lowercase will work.' . "\n"
149             . p(@UC_TRIGGERS, colored => $self->app->colors));
150             }
151              
152 0           my @blocks;
153 0           for (keys %blocks_plugins) {
154 0           my $block_class = 'DDG::Block::' . $_;
155 0           load_class($block_class);
156             push @blocks,
157             $block_class->new(
158 0           plugins => $blocks_plugins{$_},
159             return_one => 0
160             );
161             }
162 0           load_class('DDG::Request');
163 0           return \@blocks;
164             }
165              
166             1;
167              
168             __END__
169              
170             =pod
171              
172             =head1 NAME
173              
174             App::DuckPAN::DDG - DDG related functionality of duckpan
175              
176             =head1 VERSION
177              
178             version 1017
179              
180             =head1 AUTHOR
181              
182             DuckDuckGo <open@duckduckgo.com>, Zach Thompson <zach@duckduckgo.com>, Zaahir Moolla <moollaza@duckduckgo.com>, Torsten Raudssus <torsten@raudss.us> L<https://raudss.us/>
183              
184             =head1 COPYRIGHT AND LICENSE
185              
186             This software is Copyright (c) 2013 by DuckDuckGo, Inc. L<https://duckduckgo.com/>.
187              
188             This is free software, licensed under:
189              
190             The Apache License, Version 2.0, January 2004
191              
192             =cut