File Coverage

blib/lib/SMS/Send/Driver/WebService.pm
Criterion Covered Total %
statement 136 145 93.7
branch 56 78 71.7
condition 2 6 33.3
subroutine 34 34 100.0
pod 16 16 100.0
total 244 279 87.4


line stmt bran cond sub pod time code
1             package SMS::Send::Driver::WebService;
2 9     9   553976 use strict;
  9         78  
  9         220  
3 9     9   39 use warnings;
  9         11  
  9         229  
4 9     9   33 use base qw{SMS::Send::Driver};
  9         14  
  9         4055  
5 9     9   6769 use URI qw{};
  9         57569  
  9         233  
6 9     9   3768 use Path::Class qw{};
  9         300302  
  9         209  
7 9     9   6075 use Config::IniFiles qw{};
  9         83839  
  9         199  
8 9     9   5789 use HTTP::Tiny qw{};
  9         304611  
  9         12068  
9              
10             our $VERSION = '0.06';
11             our $PACKAGE = __PACKAGE__;
12              
13             =head1 NAME
14              
15             SMS::Send::Driver::WebService - SMS::Send driver base class for web services
16              
17             =head1 SYNOPSIS
18              
19             package SMS::Send::My::Driver;
20             use base qw{SMS::Send::Driver::WebService};
21             sub send_sms {
22             my $self = shift;
23             my $ua = $self->ua; #isa LWP::UserAgent
24             my $uat = $self->uat; #isa HTTP::Tiny
25             my $cfg = self->cfg; #isa Config::IniFiles
26             #call web service die on critical error
27             #parse return with a package like XML::Simple or JSON::XS
28             #return 1 for successful or 0 for unsuccessful
29             }
30              
31             =head1 DESCRIPTION
32              
33             The SMS::Send::Driver::WebService package provides an L driver base class to support two common needs. The first need is a base class that provides L as a simple method. The second need is a way to configure various setting for multiple SMS providers without having to rebuild the SMS::Send driver concept.
34              
35             =head1 USAGE
36              
37             use base qw{SMS::Send::Driver::WebService};
38              
39             =head1 METHODS
40              
41             =head2 new
42              
43             SMS::Send API; Note: $service isa SMS::Send object in this syntax
44              
45             my $service = SMS::Send->new("My::Driver",
46             _username => $username,
47             _password => $password,
48             );
49              
50             Driver API; Note: $service isa SMS::Send::My::Driver object in this syntax
51              
52             my $service = SMS::Send::My::Driver->new(
53             username => $username,
54             password => $password,
55             );
56              
57             SMS::Send API with SMS-Send.ini file
58              
59             SMS-Send.ini
60             [My::Driver1]
61             username=user1
62             password=pass1
63              
64             [My::Driver2]
65             username=user2
66             password=pass2
67              
68             my $service1 = SMS::Send->new("My::Driver1"); #username and password read from SMS-Send.ini
69             my $service2 = SMS::Send->new("My::Driver2"); #username and password read from SMS-Send.ini
70              
71             Driver API with SMS-Send.ini file
72              
73             my $service = SMS::Send::My::Driver1->new;
74              
75             =cut
76              
77             sub new {
78 9     9 1 1038 my $this=shift;
79 9   33     62 my $class=ref($this) || $this;
80 9         23 my $self={};
81 9         20 bless $self, $class;
82 9         48 $self->initialize(@_);
83 9         22 return $self;
84             }
85              
86             =head2 initialize
87              
88             Initializes data to compensate for the API deltas between SMS::Send and this package (i.e. removes underscore "_" from all parameters passed)
89              
90             In this example
91              
92             SMS::Send->new("My::Driver", _password => "mypassword");
93              
94             _password would be available to the driver as password=>"mypassword";
95              
96             =cut
97              
98             sub initialize {
99 9     9 1 17 my $self=shift;
100 9         43 my %hash=@_;
101             #SMS::Send API deltas
102 9         34 foreach my $key (keys %hash) {
103 23 50       47 if ($key =~ m/^_(.*)/) {
104 0         0 my $newkey=$1;
105 0         0 $hash{$newkey}=delete($hash{$key});
106             }
107             }
108 9         59 %$self=%hash;
109 9         24 return $self;
110             }
111              
112             =head2 send_sms
113              
114             You will need to overload this method in your sub class.
115              
116             Override in sub class (Example from Kannel SMSBox implementation)
117              
118             sub send_sms {
119             my $self = shift;
120             my %argv = @_;
121             my $to = $argv{"to"} or die("Error: to address required");
122             my $text = defined($argv{"text"}) ? $argv{"text"} : ''; #use < 5.10 syntax to support older Perls
123             my $url = $self->url; #isa URI
124             my @form = (
125             username => $self->username,
126             password => $self->password,
127             to => $to,
128             text => $text,
129             );
130             $url->query_form(\@form); #isa URI
131             my $response = $self->ua->get($url); #isa HTTP::Response see LWP::UserAgent->get
132             die(sprintf("HTTP Error: %s", $response->status_line)) unless $response->is_success;
133             my $content = $response->decoded_content;
134             return $content =~ m/^0:/ ? 1 : 0; #0: Accepted for delivery
135             }
136              
137             =head1 PROPERTIES
138              
139             =head2 username
140              
141             Sets and returns the username string value
142              
143             Override in sub class
144              
145             sub _username_default {"myusername"};
146              
147             Override in configuration
148              
149             [My::Driver]
150             username=myusername
151              
152             =cut
153              
154             sub username {
155 6     6 1 3794 my $self=shift;
156 6 100       47 $self->{'username'}=shift if @_;
157 6 100       34 $self->{'username'}=$self->cfg_property('username', $self->_username_default) unless defined $self->{'username'};
158 6 50       131 die('Error: username property required') unless defined $self->{'username'};
159 6         26 return $self->{'username'};
160             }
161              
162 2     2   10 sub _username_default {undef};
163              
164             =head2 password
165              
166             Sets and returns the password string value (passed to the web service as PWD)
167              
168             Override in sub class
169              
170             sub _password_default {"mypassword"};
171              
172             Override in configuration
173              
174             [My::Driver]
175             password=mypassword
176              
177             =cut
178              
179             sub password {
180 4     4 1 12 my $self=shift;
181 4 50       14 $self->{'password'}=shift if @_;
182 4 50       26 $self->{'password'}=$self->cfg_property('password', $self->_password_default) unless defined $self->{'password'};
183 4 50       98 die('Error: password property required') unless defined $self->{'password'};
184 4         16 return $self->{'password'};
185             }
186              
187 2     2   7 sub _password_default {undef};
188              
189             =head2 host
190              
191             Default: 127.0.0.1
192              
193             Override in sub class
194              
195             sub _host_default {"myhost.domain.tld"};
196              
197             Override in configuration
198              
199             [My::Driver]
200             host=myhost.domain.tld
201              
202             =cut
203              
204             sub host {
205 9     9 1 1366 my $self=shift;
206 9 100       51 $self->{'host'}=shift if @_;
207 9 100       44 $self->{'host'}=$self->cfg_property('host', $self->_host_default) unless defined $self->{'host'};
208 9         125 return $self->{'host'};
209             }
210              
211 2     2   6 sub _host_default {'127.0.0.1'};
212              
213             =head2 protocol
214              
215             Default: http
216              
217             Override in sub class
218              
219             sub _protocol_default {"https"};
220              
221             Override in configuration
222              
223             [My::Driver]
224             protocol=https
225              
226             =cut
227              
228             sub protocol {
229 9     9 1 26 my $self=shift;
230 9 100       31 $self->{'protocol'}=shift if @_;
231 9 100       52 $self->{'protocol'}=$self->cfg_property('protocol', $self->_protocol_default) unless defined $self->{'protocol'};
232 9         125 return $self->{'protocol'};
233             }
234              
235 2     2   6 sub _protocol_default {'http'};
236              
237             =head2 port
238              
239             Default: 80
240              
241             Override in sub class
242              
243             sub _port_default {443};
244              
245             Override in configuration
246              
247             [My::Driver]
248             port=443
249              
250             =cut
251              
252             sub port {
253 9     9 1 23 my $self=shift;
254 9 100       38 $self->{'port'}=shift if @_;
255 9 100       42 $self->{'port'}=$self->cfg_property('port', $self->_port_default) unless defined $self->{'port'};
256 9         120 return $self->{'port'};
257             }
258              
259 2     2   15 sub _port_default {'80'};
260              
261             =head2 script_name
262              
263             Default: /cgi-bin/sendsms
264              
265             Override in sub class
266              
267             sub _script_name_default {"/path/file"};
268              
269             Override in configuration
270              
271             [My::Driver]
272             script_name=/path/file
273              
274             =cut
275              
276             sub script_name {
277 10     10 1 24 my $self=shift;
278 10 100       36 $self->{'script_name'}=shift if @_;
279 10 100       39 $self->{'script_name'}=$self->cfg_property('script_name', $self->_script_name_default) unless defined $self->{'script_name'};
280 10         119 return $self->{'script_name'};
281             }
282              
283 2     2   7 sub _script_name_default {'/cgi-bin/sendsms'};
284              
285             =head2 url
286              
287             Returns a L object based on above properties OR returns a string from sub class or configuration file.
288              
289             Override in sub class (Can be a string or any object that stringifies to a URL)
290              
291             sub _url_default {"http://myservice.domain.tld/path/file"};
292              
293             Override in configuration
294              
295             [My::Driver]
296             url=http://myservice.domain.tld/path/file
297              
298             Overriding the url method in the sub class or the configuration makes the protocol, host, port, and script_name methods inoperable.
299              
300             =cut
301              
302             sub url {
303 6     6 1 13 my $self=shift;
304 6 100       20 $self->{'url'}=shift if @_;
305 6 100       25 $self->{'url'}=$self->cfg_property('url', $self->_url_default) unless defined $self->{'url'};
306 6 100       53 unless (defined $self->{'url'}) {
307 1         8 my $url=URI->new();
308 1         3781 $url->scheme($self->protocol);
309 1         2800 $url->host($self->host);
310 1         116 $url->port($self->port);
311 1         51 $url->path($self->script_name);
312 1         39 $self->{'url'}=$url; #object assignment
313             }
314             #print $self->{'url'}, "\n";
315 6         27 return $self->{'url'};
316             }
317              
318 3     3   11 sub _url_default {undef};
319              
320             =head1 OBJECT ACCESSORS
321              
322             =head2 uat
323              
324             Returns a lazy loaded L object
325              
326             =cut
327              
328             sub uat {
329 1     1 1 3 my $self = shift;
330 1 50       4 unless ($self->{'uat'}) {
331 1         5 $self->{'uat'} = HTTP::Tiny->new(
332             keep_alive => 0, #override bad default
333             agent => $self->_http_agent,
334             );
335             }
336 1         83 return $self->{'uat'};
337             }
338              
339             =head2 ua
340              
341             Returns a lazy loaded L object
342              
343             =cut
344              
345             sub ua {
346 1     1 1 3 my $self = shift;
347 1 50       15 unless ($self->{'ua'}) {
348 1         2 local $@;
349 1     1   674 eval 'use LWP::UserAgent'; #Lazy Load Package
  1         35006  
  1         25  
  1         141  
350 1         5 my $error = $@;
351 1 50       5 die($error) if $error;
352 1         6 $self->{'ua'} = LWP::UserAgent->new(
353             env_proxy => 1, #override bad default
354             agent => $self->_http_agent,
355             );
356             }
357 1         15184 return $self->{'ua'};
358             }
359              
360             sub _http_agent {
361 2     2   5 my $self = shift;
362 2 50       7 $self->{'_http_agent'} = shift if @_;
363             $self->{'_http_agent'} = "Mozilla/5.0 (compatible; $PACKAGE/$VERSION; See rt.cpan.org 35173)"
364 2 100       19 unless defined $self->{'_http_agent'};
365 2         16 return $self->{'_http_agent'};
366             }
367              
368             =head2 cfg
369              
370             Returns a lazy loaded L object so that you can read settings from the INI file.
371              
372             my $cfg=$driver->cfg; #isa Config::IniFiles
373              
374             =cut
375              
376             sub cfg {
377 58     58 1 4634 my $self=shift;
378 58 100       106 unless (exists $self->{'cfg'}) {
379 5         88 my $file=$self->cfg_file;
380 5 50 33     22 if ($file and -r $file) {
381 5         225 $self->{'cfg'}=Config::IniFiles->new(-file=>"$file")
382             } else {
383 0         0 $self->{'cfg'}=undef;
384             }
385             }
386 58         35258 return $self->{'cfg'};
387             }
388              
389              
390             =head2 cfg_file
391              
392             Sets or returns the profile INI filename
393              
394             my $file=$driver->cfg_file;
395             my $file=$driver->cfg_file("./my.ini");
396              
397             Set on construction
398              
399             my $driver=SMS::Send::My::Driver->new(cfg_file=>"./my.ini");
400              
401             Default: SMS-Send.ini
402              
403             =cut
404              
405             sub cfg_file {
406 9     9 1 22 my $self=shift;
407 9 50       30 if (@_) {
408 0         0 $self->{'cfg_file'}=shift;
409 0 0       0 die(sprintf(qq{Error: Cannot read file "%s".}, $self->{'cfg_file'})) unless -r $self->{'cfg_file'};
410             }
411 9 100       30 unless (defined $self->{'cfg_file'}) {
412 5 50       21 die(sprintf(qq{Error: path method returned a "%s"; expecting an array reference.}, ref($self->cfg_path)))
413             unless ref($self->cfg_path) eq 'ARRAY';
414 5         11 foreach my $path (@{$self->cfg_path}) {
  5         18  
415 5         31 $self->{'cfg_file'}=Path::Class::file($path, $self->_cfg_file_default);
416 5 50       937 last if -r $self->{'cfg_file'};
417             }
418             }
419             #We may not have a vaild file here? We'll let Config::IniFiles catch the error.
420 9         336 return $self->{'cfg_file'};
421             }
422              
423 5     5   28 sub _cfg_file_default {'SMS-Send.ini'};
424              
425             =head2 cfg_path
426              
427             Sets and returns a list of search paths for the INI file.
428              
429             my $path=$driver->cfg_path; # []
430             my $path=$driver->cfg_path(".", ".."); # []
431              
432             Default: ["."]
433             Default: [".", 'C:\Windows'] on Windows-like systems that have Win32 installed
434             Default: [".", "/etc"] on other systems that have Sys::Path installed
435              
436             override in sub class
437              
438             sub cfg_path {["/my/path"]};
439              
440             =cut
441              
442             sub cfg_path {
443 18     18 1 37 my $self=shift;
444 18 50       44 $self->{'path'}=[@_] if @_;
445 18 100       51 unless (ref($self->{'path'}) eq 'ARRAY') {
446 5         14 my @path=('.');
447 5 50       33 if ($^O eq 'MSWin32') {
448 0         0 eval('use Win32');
449 0 0       0 push @path, eval('Win32::GetFolderPath(Win32::CSIDL_WINDOWS)') unless $@;
450             } else {
451 5     5   773 eval('use Sys::Path');
  0         0  
  0         0  
  5         384  
452 5 50       35 push @path, eval('Sys::Path->sysconfdir') unless $@;
453             }
454 5         25 $self->{'path'}=\@path;
455             }
456 18         62 return $self->{'path'};
457             }
458              
459             =head2 cfg_section
460              
461             Returns driver name as specified by package namespace
462              
463             Example
464             package SMS::Send::My::Driver;
465              
466             Configuration in SMS-Send.ini file
467              
468             [My::Driver]
469             username=myuser
470             password=mypass
471             host=myserver
472              
473             =cut
474              
475             sub cfg_section {
476 30     30 1 40 my $self=shift;
477 30 50       62 $self->{'cfg_section'}=shift if @_;
478 30 100       56 unless ($self->{'cfg_section'}) {
479 4         9 my $section=ref($self);
480 4         23 $section =~ s/\ASMS::Send:://;
481 4         12 $self->{'cfg_section'}=$section;
482             }
483 30         96 return $self->{'cfg_section'};
484             }
485              
486             =head2 cfg_property
487              
488             my $property=$self->cfg_property("username");
489             my $property=$self->cfg_property("host", "mydefault");
490              
491             =cut
492              
493             sub cfg_property {
494 27     27 1 71 my $self = shift;
495 27 50       56 my $property = shift or die('Error: property name required');
496 27         40 my $default = shift; #|| undef
497 27         53 my $cfg = $self->cfg;
498 27 100       58 if ($cfg) { #if config object
499 26         48 return $self->cfg->val($self->cfg_section, $property, $default);
500             } else {
501 1         3 return $default;
502             }
503             }
504              
505             =head1 BUGS
506              
507             Please log on RT and send an email to the author.
508              
509             =head1 SUPPORT
510              
511             DavisNetworks.com supports all Perl applications including this package.
512              
513             =head1 AUTHOR
514              
515             Michael R. Davis
516             CPAN ID: MRDVT
517             Satellite Tracking of People, LLC
518             mdavis@stopllc.com
519             http://www.stopllc.com/
520              
521             =head1 COPYRIGHT
522              
523             This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
524              
525             The full text of the license can be found in the LICENSE file included with this module.
526              
527             =head1 SEE ALSO
528              
529             L
530              
531             =cut
532              
533             1;