line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package App::DuckPAN::Cmd::New; |
2
|
|
|
|
|
|
|
our $AUTHORITY = 'cpan:DDG'; |
3
|
|
|
|
|
|
|
# ABSTRACT: Take a name as input and generates a new, named Goodie or Spice instant answer skeleton |
4
|
|
|
|
|
|
|
$App::DuckPAN::Cmd::New::VERSION = '1018'; |
5
|
|
|
|
|
|
|
# See the template/templates.yml file in the Goodie or Spice repository for the |
6
|
|
|
|
|
|
|
# list of template-sets and files generated for them |
7
|
|
|
|
|
|
|
|
8
|
1
|
|
|
1
|
|
943
|
use Moo; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
4
|
|
9
|
|
|
|
|
|
|
with qw( App::DuckPAN::Cmd ); |
10
|
|
|
|
|
|
|
|
11
|
1
|
|
|
1
|
|
350
|
use MooX::Options protect_argv => 0; |
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
5
|
|
12
|
1
|
|
|
1
|
|
1362
|
use Try::Tiny; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
75
|
|
13
|
1
|
|
|
1
|
|
6
|
use List::MoreUtils 'any'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
19
|
|
14
|
|
|
|
|
|
|
|
15
|
1
|
|
|
1
|
|
915
|
use App::DuckPAN::TemplateDefinitions; |
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
32
|
|
16
|
|
|
|
|
|
|
|
17
|
1
|
|
|
1
|
|
6
|
no warnings 'uninitialized'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
1336
|
|
18
|
|
|
|
|
|
|
########################## |
19
|
|
|
|
|
|
|
# Command line arguments # |
20
|
|
|
|
|
|
|
########################## |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
# A 'template' for the user is equivalent to a 'template-set' for the program |
23
|
|
|
|
|
|
|
option template => ( |
24
|
|
|
|
|
|
|
is => 'rwp', |
25
|
|
|
|
|
|
|
format => 's', |
26
|
|
|
|
|
|
|
default => 'default', |
27
|
|
|
|
|
|
|
short => 't', |
28
|
|
|
|
|
|
|
doc => 'template used to generate the instant answer skeleton (default: default)', |
29
|
|
|
|
|
|
|
); |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
option list_templates => ( |
32
|
|
|
|
|
|
|
is => 'ro', |
33
|
|
|
|
|
|
|
short => 'l', |
34
|
|
|
|
|
|
|
doc => 'list the available instant answer templates and exit', |
35
|
|
|
|
|
|
|
); |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
option cheatsheet => ( |
38
|
|
|
|
|
|
|
is => 'ro', |
39
|
|
|
|
|
|
|
short => 'c', |
40
|
|
|
|
|
|
|
doc => "create a Cheat Sheet (short for `--template cheatsheet'; valid only for Goodies)", |
41
|
|
|
|
|
|
|
); |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
option no_optionals => ( |
44
|
|
|
|
|
|
|
is => 'ro', |
45
|
|
|
|
|
|
|
short => 'N', |
46
|
|
|
|
|
|
|
doc => 'do not create any optional files from the chosen template', |
47
|
|
|
|
|
|
|
); |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
############## |
50
|
|
|
|
|
|
|
# Attributes # |
51
|
|
|
|
|
|
|
############## |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
has _template_defs => ( |
54
|
|
|
|
|
|
|
is => 'ro', |
55
|
|
|
|
|
|
|
init_arg => undef, |
56
|
|
|
|
|
|
|
lazy => 1, |
57
|
|
|
|
|
|
|
builder => 1, |
58
|
|
|
|
|
|
|
doc => 'Template definitions for the templates for the current IA type', |
59
|
|
|
|
|
|
|
); |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
sub _build__template_defs { |
62
|
0
|
|
|
0
|
|
|
my $self = shift; |
63
|
0
|
|
|
|
|
|
my $template_defs; |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
# Read the templates.yml file |
66
|
|
|
|
|
|
|
try { |
67
|
0
|
|
|
0
|
|
|
$template_defs = App::DuckPAN::TemplateDefinitions->new; |
68
|
|
|
|
|
|
|
} catch { |
69
|
0
|
|
|
0
|
|
|
my $error = $_; |
70
|
|
|
|
|
|
|
|
71
|
0
|
0
|
|
|
|
|
if ($error =~ /no such file/i) { |
72
|
|
|
|
|
|
|
# Handle the 'no such file or directory' exception |
73
|
|
|
|
|
|
|
# specially to show more information since it can be a |
74
|
|
|
|
|
|
|
# common error for users with an older IA repository |
75
|
0
|
|
|
|
|
|
my $type = $self->app->get_ia_type(); |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
$self->app->emit_and_exit(1, |
78
|
|
|
|
|
|
|
"Template definitions file not found for " . $type->{name} . |
79
|
0
|
|
|
|
|
|
" Instant Answers. You may need to pull the latest version " . |
80
|
|
|
|
|
|
|
"of this repository."); |
81
|
|
|
|
|
|
|
} |
82
|
|
|
|
|
|
|
else { |
83
|
0
|
|
|
|
|
|
$self->app->emit_and_exit(1, $error); |
84
|
|
|
|
|
|
|
} |
85
|
0
|
|
|
|
|
|
}; |
86
|
|
|
|
|
|
|
|
87
|
0
|
|
|
|
|
|
return $template_defs; |
88
|
|
|
|
|
|
|
} |
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
has _template_set => ( |
91
|
|
|
|
|
|
|
is => 'ro', |
92
|
|
|
|
|
|
|
init_arg => undef, |
93
|
|
|
|
|
|
|
lazy => 1, |
94
|
|
|
|
|
|
|
builder => 1, |
95
|
|
|
|
|
|
|
doc => 'The template set chosen by the user', |
96
|
|
|
|
|
|
|
); |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
sub _build__template_set { |
99
|
0
|
|
|
0
|
|
|
my $self = shift; |
100
|
0
|
|
|
|
|
|
my $type = $self->app->get_ia_type(); |
101
|
0
|
|
|
|
|
|
my $template_defs = $self->_template_defs; |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
# Get the template chosen by the user |
104
|
0
|
|
|
|
|
|
my $template_set = $template_defs->get_template_set($self->template); |
105
|
|
|
|
|
|
|
|
106
|
0
|
0
|
|
|
|
|
unless ($template_set) { |
107
|
|
|
|
|
|
|
# We didn't find the template-set by the name. This could mean |
108
|
|
|
|
|
|
|
# that there was a typo in the name or the user has an older IA |
109
|
|
|
|
|
|
|
# repo and it not present in that version. |
110
|
|
|
|
|
|
|
$self->app->emit_and_exit(1, |
111
|
|
|
|
|
|
|
"'" . $self->template . "' is not a valid template for a " . |
112
|
0
|
|
|
|
|
|
$type->{name} . " Instant Answer. You may need to update " . |
113
|
|
|
|
|
|
|
"your repository to get the latest templates.\n" . |
114
|
|
|
|
|
|
|
$self->_available_templates_message); |
115
|
|
|
|
|
|
|
} |
116
|
|
|
|
|
|
|
|
117
|
0
|
|
|
|
|
|
return $template_set; |
118
|
|
|
|
|
|
|
} |
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
########### |
121
|
|
|
|
|
|
|
# Methods # |
122
|
|
|
|
|
|
|
########### |
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
# Copy of @ARGV before MooX::Options processes it |
125
|
|
|
|
|
|
|
my @ORIG_ARGV; |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
before new_with_options => sub { @ORIG_ARGV = @ARGV }; |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
sub run { |
130
|
0
|
|
|
0
|
0
|
|
my ($self, @args) = @_; |
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
# Check which IA repo we're in... |
133
|
0
|
|
|
|
|
|
my $type = $self->app->get_ia_type(); |
134
|
|
|
|
|
|
|
|
135
|
0
|
|
|
|
|
|
my $no_handler = 0; |
136
|
|
|
|
|
|
|
# Process the --cheatsheet option |
137
|
0
|
0
|
0
|
|
|
|
if ($self->cheatsheet || $self->template eq 'cheatsheet') { |
138
|
0
|
0
|
|
|
|
|
if ($type->{name} ne 'Goodie') { |
139
|
0
|
|
|
|
|
|
$self->app->emit_and_exit(1, |
140
|
|
|
|
|
|
|
"Cheat Sheets can be created only in the Goodie " . |
141
|
|
|
|
|
|
|
"Instant Answer repository."); |
142
|
|
|
|
|
|
|
} |
143
|
|
|
|
|
|
|
|
144
|
0
|
|
|
|
|
|
$self->_set_template('cheatsheet'); |
145
|
0
|
|
|
|
|
|
$no_handler = 1; |
146
|
|
|
|
|
|
|
} |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
# Process the --list-templates option: List the template-set names and exit with success |
149
|
0
|
0
|
|
|
|
|
$self->app->emit_and_exit(0, $self->_available_templates_message) |
150
|
|
|
|
|
|
|
if $self->list_templates; |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
# Gracefully handle the case where '--template' is the last argument |
153
|
0
|
0
|
0
|
|
|
|
$self->app->emit_and_exit( |
154
|
|
|
|
|
|
|
1, |
155
|
|
|
|
|
|
|
"Please specify the template for your Instant Answer.\n" . |
156
|
|
|
|
|
|
|
$self->_available_templates_message |
157
|
|
|
|
|
|
|
) if ($ORIG_ARGV[$#ORIG_ARGV] // '') eq '--template'; |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
# Get the template-set instance based on the command line arguments. |
160
|
0
|
|
|
|
|
|
my $template_set = $self->_template_set(); |
161
|
|
|
|
|
|
|
|
162
|
0
|
|
|
|
|
|
$self->app->emit_info('Creating a new ' . $template_set->description . '...'); |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
# Instant Answer name as parameter |
165
|
0
|
0
|
|
|
|
|
my $entered_name = (@args) ? join(' ', @args) : $self->app->get_reply('Please enter a name for your Instant Answer: '); |
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
# Validate the entered name |
168
|
0
|
0
|
|
|
|
|
$self->app->emit_and_exit(1, 'Must supply a name for your Instant Answer.') unless $entered_name; |
169
|
0
|
0
|
|
|
|
|
$self->app->emit_and_exit(1, |
170
|
|
|
|
|
|
|
"'$entered_name' is not a valid name for an Instant Answer. " . |
171
|
|
|
|
|
|
|
'Please run the program again and provide a valid name.' |
172
|
|
|
|
|
|
|
) unless $entered_name =~ m@^( [a-zA-Z0-9\s] | (?<![:/])(::|/)(?![:/]) )+$@x; |
173
|
0
|
0
|
0
|
|
|
|
$self->app->emit_and_exit(1, |
174
|
|
|
|
|
|
|
'The name for this type of Instant Answer cannot contain package or path separators. ' . |
175
|
|
|
|
|
|
|
'Please run the program again and provide a valid name.' |
176
|
|
|
|
|
|
|
) if !$template_set->subdir_support && $entered_name =~ m![/:]!; |
177
|
|
|
|
|
|
|
|
178
|
0
|
|
|
|
|
|
$entered_name =~ s/\//::/g; #change "/" to "::" for easier handling |
179
|
|
|
|
|
|
|
|
180
|
0
|
|
|
|
|
|
my $package_name = $self->app->phrase_to_camel($entered_name); |
181
|
0
|
|
|
|
|
|
my ($name, $separated_name, $path, $lc_path) = ($package_name, $package_name, '', ''); |
182
|
|
|
|
|
|
|
|
183
|
0
|
|
|
|
|
|
$separated_name =~ s/::/ /g; |
184
|
|
|
|
|
|
|
|
185
|
0
|
0
|
|
|
|
|
if ($package_name =~ m/::/) { |
186
|
0
|
|
|
|
|
|
my @path_parts = split(/::/, $package_name); |
187
|
0
|
0
|
|
|
|
|
if (scalar @path_parts > 1) { |
188
|
0
|
|
|
|
|
|
$name = pop @path_parts; |
189
|
0
|
|
|
|
|
|
$path = join("/", @path_parts); |
190
|
0
|
|
|
|
|
|
$lc_path = join("/", map { $self->app->camel_to_underscore($_) } @path_parts); |
|
0
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
} |
192
|
|
|
|
|
|
|
else { |
193
|
0
|
|
|
|
|
|
$self->app->emit_and_exit(1, "Malformed input. Please provide a properly formatted package name for your Instant Answer."); |
194
|
|
|
|
|
|
|
} |
195
|
|
|
|
|
|
|
} |
196
|
|
|
|
|
|
|
|
197
|
0
|
|
|
|
|
|
my $lc_name = $self->app->camel_to_underscore($name); |
198
|
0
|
0
|
|
|
|
|
my $filepath = $path ? "$path/$name" : $name; |
199
|
0
|
0
|
|
|
|
|
my $lc_filepath = $lc_path ? "$lc_path/$lc_name" : $lc_name; |
200
|
0
|
0
|
|
|
|
|
if (scalar $lc_path) { |
201
|
0
|
|
|
|
|
|
$lc_path =~ s/\//_/g; #safe to modify, we already used this in $lc_filepath |
202
|
0
|
|
|
|
|
|
$lc_name = $lc_path . '_' . $lc_name; |
203
|
|
|
|
|
|
|
} |
204
|
|
|
|
|
|
|
|
205
|
0
|
0
|
|
|
|
|
my @optional_templates = $self->_ask_optional_templates |
206
|
|
|
|
|
|
|
unless $self->no_optionals; |
207
|
|
|
|
|
|
|
|
208
|
0
|
|
|
|
|
|
my %vars = ( |
209
|
|
|
|
|
|
|
ia_package_name => $package_name, |
210
|
|
|
|
|
|
|
ia_name_separated => $separated_name, |
211
|
|
|
|
|
|
|
ia_id => $lc_name, |
212
|
|
|
|
|
|
|
ia_path => $filepath, |
213
|
|
|
|
|
|
|
ia_path_lc => $lc_filepath, |
214
|
|
|
|
|
|
|
); |
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
# Cheat sheets use hyphenated file names. |
217
|
0
|
0
|
|
|
|
|
if ($self->template eq 'cheatsheet') { |
218
|
0
|
|
|
|
|
|
my $underscored = $vars{ia_id} =~ s/_cheat_sheet//r; |
219
|
0
|
|
|
|
|
|
$vars{cheat_sheet_hyphenated} = $underscored =~ s/_/-/gr; |
220
|
|
|
|
|
|
|
} |
221
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
# If the Perl module every becomes optional, this should only run if the user |
223
|
|
|
|
|
|
|
# requests one |
224
|
0
|
0
|
|
|
|
|
unless($no_handler){ |
225
|
0
|
|
|
|
|
|
%vars = (%vars, %{$self->_config_handler}); |
|
0
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
} |
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
# Generate the instant answer files. The return value is a hash with |
229
|
|
|
|
|
|
|
# information about the created files and any error that was encountered. |
230
|
0
|
|
|
|
|
|
my %generate_result = $template_set->generate(\%vars, \@optional_templates); |
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
# Show the list of files that were successfully created |
233
|
0
|
|
|
|
|
|
my @created_files = @{$generate_result{created_files}}; |
|
0
|
|
|
|
|
|
|
234
|
0
|
|
|
|
|
|
$self->app->emit_info('Created files:'); |
235
|
0
|
|
|
|
|
|
$self->app->emit_info(" $_") for @created_files; |
236
|
0
|
0
|
|
|
|
|
$self->app->emit_info(' (none)') unless @created_files; # possible on error |
237
|
|
|
|
|
|
|
|
238
|
0
|
0
|
|
|
|
|
if (my $error = $generate_result{error}) { |
239
|
|
|
|
|
|
|
# Remove the line number information if not in verbose mode. |
240
|
|
|
|
|
|
|
# This error message would be seen mostly by users writing IAs |
241
|
|
|
|
|
|
|
# for whom the line numbers don't add much value. |
242
|
0
|
0
|
|
|
|
|
$error =~ s/.*\K at .* line \d+\.$// |
243
|
|
|
|
|
|
|
unless $self->app->verbose; |
244
|
|
|
|
|
|
|
|
245
|
0
|
|
|
|
|
|
$self->app->emit_and_exit(1, $error) |
246
|
|
|
|
|
|
|
} |
247
|
|
|
|
|
|
|
|
248
|
0
|
|
|
|
|
|
$self->app->emit_info('Success!'); |
249
|
|
|
|
|
|
|
} |
250
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
# Allow user to choose a handler |
252
|
|
|
|
|
|
|
sub _config_handler { |
253
|
0
|
|
|
0
|
|
|
my $self = shift; |
254
|
|
|
|
|
|
|
|
255
|
0
|
|
|
|
|
|
my @handlers = ( |
256
|
|
|
|
|
|
|
# Scalar-based |
257
|
|
|
|
|
|
|
'remainder: (default) The query without the trigger words, spacing and case are preserved.', |
258
|
|
|
|
|
|
|
'query_raw: Like remainder but with trigger words intact', |
259
|
|
|
|
|
|
|
'query: Full query normalized with a single space between terms', |
260
|
|
|
|
|
|
|
'query_lc: Like query but in lowercase', |
261
|
|
|
|
|
|
|
'query_clean: Like query_lc but with non-alphanumeric characters removed', |
262
|
|
|
|
|
|
|
'query_nowhitespace: All whitespace removed', |
263
|
|
|
|
|
|
|
'query_nowhitespace_nodash: All whitespace and hyphens removed', |
264
|
|
|
|
|
|
|
# Array-based |
265
|
|
|
|
|
|
|
'matches: Returns an array of captured expression from a regular expression trigger', |
266
|
|
|
|
|
|
|
'words: Like query_clean but returns an array of the terms split on whitespace', |
267
|
|
|
|
|
|
|
'query_parts: Like query but returns an array of the terms split on whitespace', |
268
|
|
|
|
|
|
|
'query_raw_parts: Like query_parts but array contains original whitespace elements' |
269
|
|
|
|
|
|
|
); |
270
|
|
|
|
|
|
|
|
271
|
0
|
|
|
|
|
|
my $res = $self->app->get_reply( |
272
|
|
|
|
|
|
|
'Which handler would you like to use to process the query?', |
273
|
|
|
|
|
|
|
choices => \@handlers, |
274
|
|
|
|
|
|
|
default => $handlers[0] |
275
|
|
|
|
|
|
|
); |
276
|
|
|
|
|
|
|
|
277
|
0
|
0
|
|
|
|
|
unless($res =~ /^([^:]+)/){ |
278
|
0
|
|
|
|
|
|
$self->app->emit_and_exit(1, "Failed to extract handler from response: $res"); |
279
|
|
|
|
|
|
|
} |
280
|
0
|
|
|
|
|
|
my $handler = $1; |
281
|
0
|
0
|
|
0
|
|
|
my $var = (any {$handler eq $_} qw(words query_parts query_raw_parts matches)) ? '@' : '$'; |
|
0
|
|
|
|
|
|
|
282
|
0
|
0
|
|
|
|
|
my $trigger = $handler eq 'matches' |
283
|
|
|
|
|
|
|
? q{query => qr/trigger regex/} |
284
|
|
|
|
|
|
|
: q{any => 'triggerword', 'trigger phrase'}; |
285
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
return { |
287
|
0
|
|
|
|
|
|
ia_handler => $handler, |
288
|
|
|
|
|
|
|
ia_handler_var => $var, |
289
|
|
|
|
|
|
|
ia_trigger => $trigger |
290
|
|
|
|
|
|
|
}; |
291
|
|
|
|
|
|
|
} |
292
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
# Ask the user for which optional templates they want to use and return a list |
294
|
|
|
|
|
|
|
# of the chosen templates |
295
|
|
|
|
|
|
|
sub _ask_optional_templates { |
296
|
0
|
|
|
0
|
|
|
my $self = shift; |
297
|
0
|
|
|
|
|
|
my $template_set = $self->_template_set; |
298
|
0
|
|
|
|
|
|
my $combinations = $template_set->optional_template_combinations; |
299
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
# no optional templates; nothing to do |
301
|
0
|
0
|
|
|
|
|
return unless @$combinations; |
302
|
|
|
|
|
|
|
|
303
|
0
|
|
|
|
|
|
my $show_optional_templates = $self->app->ask_yn( |
304
|
|
|
|
|
|
|
'Would you like to configure optional templates?', |
305
|
|
|
|
|
|
|
default => 0, |
306
|
|
|
|
|
|
|
); |
307
|
|
|
|
|
|
|
|
308
|
0
|
0
|
|
|
|
|
if ($show_optional_templates) { |
309
|
|
|
|
|
|
|
# The choice strings to show to the user |
310
|
0
|
|
|
|
|
|
my @choices; |
311
|
|
|
|
|
|
|
# Mapping from a choice string to the corresponding template combination |
312
|
|
|
|
|
|
|
my %choice_combinations; |
313
|
|
|
|
|
|
|
|
314
|
0
|
|
|
|
|
|
for my $combination (@$combinations) { |
315
|
|
|
|
|
|
|
# Label of every template in the combination |
316
|
0
|
|
|
|
|
|
my @labels = map { $_->label } @$combination; |
|
0
|
|
|
|
|
|
|
317
|
0
|
|
|
|
|
|
my $choice = join(', ', @labels); |
318
|
|
|
|
|
|
|
|
319
|
0
|
|
|
|
|
|
push @choices, $choice; |
320
|
0
|
|
|
|
|
|
$choice_combinations{$choice} = $combination; |
321
|
|
|
|
|
|
|
} |
322
|
|
|
|
|
|
|
|
323
|
0
|
|
|
|
|
|
my $reply = $self->app->get_reply( |
324
|
|
|
|
|
|
|
'Choose configuration', |
325
|
|
|
|
|
|
|
choices => \@choices, |
326
|
|
|
|
|
|
|
default => $choices[0], |
327
|
|
|
|
|
|
|
); |
328
|
|
|
|
|
|
|
|
329
|
0
|
|
|
|
|
|
return @{$choice_combinations{$reply}}; |
|
0
|
|
|
|
|
|
|
330
|
|
|
|
|
|
|
} |
331
|
|
|
|
|
|
|
|
332
|
0
|
|
|
|
|
|
return; |
333
|
|
|
|
|
|
|
} |
334
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
# Create a message with the list of available template-sets for this IA type |
336
|
|
|
|
|
|
|
sub _available_templates_message { |
337
|
0
|
|
|
0
|
|
|
my $self = shift; |
338
|
0
|
|
|
|
|
|
my $template_defs = $self->_template_defs; |
339
|
|
|
|
|
|
|
# template-sets, sorted by name |
340
|
|
|
|
|
|
|
my @template_sets = |
341
|
0
|
|
|
|
|
|
sort { $a->name cmp $b->name } $template_defs->get_template_sets; |
|
0
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
|
343
|
0
|
|
|
|
|
|
my $message = "Available templates:"; |
344
|
|
|
|
|
|
|
|
345
|
0
|
|
|
|
|
|
for my $template_set (@template_sets) { |
346
|
0
|
|
|
|
|
|
$message .= sprintf("\n %10s - %s", |
347
|
|
|
|
|
|
|
$template_set->name, |
348
|
|
|
|
|
|
|
$template_set->description, |
349
|
|
|
|
|
|
|
); |
350
|
|
|
|
|
|
|
} |
351
|
|
|
|
|
|
|
|
352
|
0
|
|
|
|
|
|
return $message; |
353
|
|
|
|
|
|
|
} |
354
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
1; |
356
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
__END__ |
358
|
|
|
|
|
|
|
|
359
|
|
|
|
|
|
|
=pod |
360
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
=head1 NAME |
362
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
App::DuckPAN::Cmd::New - Take a name as input and generates a new, named Goodie or Spice instant answer skeleton |
364
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
=head1 VERSION |
366
|
|
|
|
|
|
|
|
367
|
|
|
|
|
|
|
version 1018 |
368
|
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
=head1 AUTHOR |
370
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
DuckDuckGo <open@duckduckgo.com>, Zach Thompson <zach@duckduckgo.com>, Zaahir Moolla <moollaza@duckduckgo.com>, Torsten Raudssus <torsten@raudss.us> L<https://raudss.us/> |
372
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
374
|
|
|
|
|
|
|
|
375
|
|
|
|
|
|
|
This software is Copyright (c) 2013 by DuckDuckGo, Inc. L<https://duckduckgo.com/>. |
376
|
|
|
|
|
|
|
|
377
|
|
|
|
|
|
|
This is free software, licensed under: |
378
|
|
|
|
|
|
|
|
379
|
|
|
|
|
|
|
The Apache License, Version 2.0, January 2004 |
380
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
=cut |