File Coverage

blib/lib/WWW/Ideone.pm
Criterion Covered Total %
statement 18 104 17.3
branch 0 20 0.0
condition n/a
subroutine 6 17 35.2
pod 4 11 36.3
total 28 152 18.4


line stmt bran cond sub pod time code
1             package WWW::Ideone;
2 1     1   33949 use warnings;
  1         3  
  1         32  
3 1     1   5 use strict;
  1         2  
  1         36  
4 1     1   4051 use Template;
  1         498601  
  1         27  
5 1     1   1185 use LWP::UserAgent;
  1         528991  
  1         33  
6 1     1   13 use Carp;
  1         2  
  1         85  
7 1     1   1740 use IO::Uncompress::Gunzip qw/gunzip $GunzipError/;
  1         91167  
  1         1576  
8              
9             our $VERSION = 0.04;
10              
11             # The version of soap which we must use to be understood by ideone.com.
12              
13             our $soap_version = 'xmlns:env="http://www.w3.org/2003/05/soap-envelope"';
14              
15             # The URL we send our input to
16              
17             our $ideone_api_url = 'http://ideone.com/api/1/service';
18              
19             # The template from which we create the XML request to ideone.com.
20              
21             my $ideone_request_tmpl = <<'EOF';
22            
23            
24            
25            
26             [%- FOR field IN fields.keys.sort %]
27             <[% field %]>[% fields.$field | xml %]
28             [%- END %]
29            
30            
31            
32             EOF
33              
34             # The fields which we can send to ideone.com
35              
36             my %fields = (
37             user
38             =>
39             {
40             type => 'string',
41             },
42             pass
43             =>
44             {
45             type => 'string',
46             },
47             sourceCode
48             =>
49             {
50             type => 'string',
51             },
52             language
53             =>
54             {
55             type => 'number',
56             },
57             input
58             =>
59             {
60             type => 'string',
61             default => '',
62             },
63             run
64             =>
65             {
66             type => 'boolean',
67             default => 'true',
68             },
69             private
70             =>
71             {
72             type => 'boolean',
73             default => 'false',
74             },
75             # "link" is a Perl keyword so this needs quotes
76             'link'
77             =>
78             {
79             type => 'string',
80             },
81             withSource
82             =>
83             {
84             type => 'boolean',
85             default => 'false',
86             },
87             withInput
88             =>
89             {
90             type => 'boolean',
91             default => 'false',
92             },
93             withOutput
94             =>
95             {
96             type => 'boolean',
97             default => 'false',
98             },
99             );
100              
101             # The commands which we know about.
102              
103             my %commands = (
104             'createSubmission' => {
105             params => [
106             qw/
107             sourceCode
108             language
109             input
110             run
111             private
112             /,
113             ],
114             },
115             'get_submissionStatus' => {
116             params => [
117             qw/
118             link
119             /,
120             ],
121             },
122             'get_submissionDetails' => {
123             params => [
124             qw/
125             withSource
126             withInput
127             withOutput
128             /,
129             ],
130             },
131             'getLanguages' => {
132             params => [],
133             },
134             'testFunction' => {
135             params => [],
136             },
137             );
138              
139             # All of these need a user and pass field.
140              
141             for my $i (values %commands) {
142             push @{$i->{params}}, qw/user pass/;
143             }
144              
145             sub new
146             {
147 0     0 1   return bless {};
148             }
149              
150             sub run_tt
151             {
152 0     0 0   my ($unused, $input_template, $tt_vars_ref) = @_;
153 0           my $tt = Template->new (
154             ABSOLUTE => 1,
155             INCLUDE_PATH => ["$FindBin::Bin/tmpl"],
156             );
157 0           $tt_vars_ref->{ideone}{soap_version} = $soap_version;
158 0           my $tt_out;
159              
160 0           $tt->process ($input_template, $tt_vars_ref, \$tt_out);
161 0           return $tt_out;
162             }
163              
164             # Validate the field C<$field>
165              
166             sub validate
167             {
168 0     0 0   my ($input_fields) = @_;
169 0           for my $field (keys %$input_fields) {
170 0 0         if (! $fields{$field}) {
171 0           croak "Unknown field '$field'";
172             }
173             }
174             }
175              
176             #
177              
178             sub check_inputs
179             {
180 0     0 0   my ($command, $input_fields) = @_;
181 0           my $params = $commands{$command}{params};
182 0           my %expect;
183 0           for my $field (@$params) {
184 0 0         if (! defined $input_fields->{$field}) {
185 0           my $default = $fields{$field}{default};
186 0 0         if (defined $default) {
187 0           $input_fields->{$field} = $default;
188             }
189             else {
190 0           croak "Required input '$field' for '$command' is undefined";
191             }
192             }
193 0           $expect{$field} = 1;
194             }
195 0           for my $field (keys %$input_fields) {
196 0 0         if (! defined $expect{$field}) {
197 0           carp "Method '$command' does not require a '$field' input";
198 0           delete $input_fields->{$field};
199             }
200             }
201             }
202              
203             sub make_tt
204             {
205 0     0 0   my ($unused, %input) = @_;
206 0           my $tt = Template->new ();
207 0           my $input_fields = $input{fields};
208 0 0         if (! defined $input_fields) {
209 0           $input_fields = {};
210             }
211 0           $input_fields->{user} = $unused->{ideone}->{user};
212 0           $input_fields->{pass} = $unused->{ideone}->{pass};
213 0           validate ($input_fields);
214 0           my $command = $input{command};
215 0 0         if (! defined $command) {
216              
217             }
218 0           check_inputs ($command, $input_fields);
219 0 0         if (! $commands{$command}) {
220 0           croak "Unknown command '$command'";
221             }
222 0           my %tt_vars;
223 0           $tt_vars{fields} = $input_fields;
224 0           undef $input_fields;
225 0           $tt_vars{ideone}{command} = $command;
226 0           undef $command;
227 0           $tt_vars{ideone}{soap_version} = $soap_version;
228 0           my $tt_out;
229              
230 0           $tt->process (\$ideone_request_tmpl, \%tt_vars, \$tt_out);
231 0           return $tt_out;
232             }
233              
234              
235             sub send_request
236             {
237 0     0 0   my ($unused, $content) = @_;
238 0           my $ua = LWP::UserAgent->new (agent => __PACKAGE__);
239 0           my $response = $ua->post (
240             $ideone_api_url,
241             'Content-Type' => 'application/soap+xml; charset=utf-8',
242             'Content-Length' => length $content,
243             'Accept-Encoding' => 'gzip',
244             content => $content,
245             );
246 0 0         if ($response->is_success ()) {
247 0           my $content = $response->content ();
248             # gunzip if necessary
249 0 0         if ($response->header ('Content-Encoding') eq 'gzip') {
250 0           my $unzipped_content;
251 0 0         gunzip \$content, \$unzipped_content
252             or die "gunzip failed: $GunzipError.\n";
253 0           $content = $unzipped_content;
254             }
255 0           return $content;
256             }
257             else {
258 0           croak "Request failed with the following message:\n" .
259             $response->as_string ();
260             }
261             }
262              
263             sub send
264             {
265 0     0 1   my $object = $_[0];
266 0           my $message = make_tt (@_);
267 0           my $reply = $object->send_request ($message);
268 0           return $reply;
269             }
270              
271             sub get_languages
272             {
273 0     0 1   my ($object) = @_;
274 0           my $lang_xml = $object->send (command => 'getLanguages');
275 0           my %languages;
276 0           while ($lang_xml =~ m!]+>(\d+)]+>([^<]+)!g) {
277 0           $languages{$1} = $2;
278             }
279 0           return %languages;
280             }
281              
282             sub user_pass
283             {
284 0     0 1   my ($object, $user, $pass) = @_;
285 0           $object->{ideone}->{user} = $user;
286 0           $object->{ideone}->{pass} = $pass;
287             }
288              
289             sub parse_xml
290             {
291 0     0 0   my ($unused, $xml) = @_;
292              
293 0           my @return = xml_contents ($xml, 'return');
294            
295 0           my %hash;
296 0           for (@return) {
297 0           my @items = xml_contents ($_, 'item');
298 0           for my $item (@items) {
299 0           my @key = xml_contents ($item, 'key');
300 0           my @value = xml_contents ($item, 'value');
301 0           $hash{$key[0]} = $value[0];
302             }
303             }
304 0           return %hash;
305             }
306              
307             sub xml_contents
308             {
309 0     0 0   my ($xml, $tag) = @_;
310 0           my @contents;
311              
312 0           while ($xml =~ m!<$tag[^>]*>(.*?)!smg) {
313 0           push @contents, $1;
314             }
315 0           return @contents;
316             }
317              
318              
319             1;
320