File Coverage

blib/lib/Resource/Dispose.pm
Criterion Covered Total %
statement 48 48 100.0
branch 9 10 90.0
condition 3 3 100.0
subroutine 13 13 100.0
pod 0 5 0.0
total 73 79 92.4


line stmt bran cond sub pod time code
1             #!/usr/bin/perl -c
2              
3             package Resource::Dispose;
4              
5             =head1 NAME
6              
7             Resource::Dispose - Syntax sugar for dispose pattern
8              
9             =head1 SYNOPSIS
10              
11             use Resource::Dispose;
12             {
13             resource my $obj = Some::Class->new;
14             }
15             # $obj->DISPOSE is called even if $obj can not be freed and destroyed
16              
17             =head1 DESCRIPTION
18              
19             The dispose pattern is a design pattern which is used to handle resource
20             cleanup in runtime environment that use automatic garbage collection. In Perl
21             there is possibility that the object will be destructed during global
22             destruction and it leads to memory leaking and other drawbacks like unclosed
23             file handles, etc.
24              
25             This module provides new keyword C as a syntax sugar for dispose
26             pattern. The C method of the resource object is called if the
27             resource is going out of scope.
28              
29             =for readme stop
30              
31             =cut
32              
33              
34 3     3   105537 use strict;
  3         9  
  3         104  
35 3     3   16 use warnings;
  3         6  
  3         122  
36              
37             our $VERSION = '0.01';
38              
39              
40 3     3   4448 use Devel::Declare ();
  3         39603  
  3         112  
41 3     3   2964 use Guard ();
  3         2126  
  3         77  
42 3     3   28 use Carp qw(croak);
  3         6  
  3         449  
43              
44              
45             our @CARP_NOT = qw(Devel::Declare);
46              
47              
48             sub import {
49 3     3   37 my ($class) = @_;
50              
51 3         10 my $caller = caller;
52              
53 3         48 Devel::Declare->setup_for(
54             $caller,
55             { resource => { const => \&parser } }
56             );
57              
58 3     3   16 no strict 'refs';
  3         5  
  3         3185  
59 3     9   101 *{$caller.'::resource'} = sub ($) {};
  3         20  
  9         1038  
60              
61 3         59 return 1;
62             };
63              
64              
65             our $Prefix = '';
66              
67             sub get_linestr {
68 48     48 0 200 return substr Devel::Declare::get_linestr, length $Prefix;
69             };
70              
71             sub set_linestr {
72 22     22 0 79 return Devel::Declare::set_linestr $Prefix . $_[0];
73             };
74              
75             sub parser {
76 12     12 0 11855 my ($keyword, $offset) = @_;
77              
78 12         31 local $Prefix = substr get_linestr, 0, $offset;
79 12         25 strip_keyword();
80              
81 12         21 my $linestr = get_linestr;
82              
83 12 100       501 $linestr =~ s/\s*(?:(local|my|our|state)\s*)?(\$\w+|\(.*?\))// or croak 'Syntax error';
84 10         28 my ($scope, $decl) = ($1, $2);
85              
86 10         16 my $before = '';
87 10 100       590 $before .= "$scope $decl; " if defined $scope;
88              
89 10 100       59 my @vars = $decl =~ /^\((.*)\)$/
90             ? split /\s*,\s*/, $1
91             : $decl;
92              
93 10         21 foreach my $var (@vars) {
94 12         36 $before .= "Guard::scope_guard { $var->DISPOSE if eval { $var->can('DISPOSE') } }; ";
95             };
96              
97 10 100 100     95 $before .= "$decl" if $linestr =~ /\s*=/ and $decl =~ /^\s*(\$\w+|\(\s*\$\w+\s*\))\s*$/;
98              
99 10         553 set_linestr($before . $linestr);
100              
101 10         73 return 1;
102             };
103              
104             sub strip_space {
105 12     12 0 32 my $skip = Devel::Declare::toke_skipspace length $Prefix;
106 12         23 set_linestr substr get_linestr, $skip;
107 12         25 return 1;
108             };
109              
110             sub strip_keyword {
111 12     12 0 25 strip_space;
112 12 50       20 get_linestr =~ /^(resource)(?:\b|$)/ or croak 'Could not match resource keyword ', get_linestr;
113 12         29 $Prefix .= $1;
114 12         12 return $1;
115             };
116              
117              
118             1;
119              
120              
121             =for readme continue
122              
123             =head1 SEE ALSO
124              
125             This C keyword is inspired by C keyword from C# language and
126             extended C keyword from Java 7 language.
127              
128             L, L, L.
129              
130             =head1 BUGS
131              
132             If you find the bug or want to implement new features, please report it at
133             L
134              
135             The code repository is available at
136             L
137              
138             =head1 AUTHOR
139              
140             Piotr Roszatycki
141              
142             =head1 LICENSE
143              
144             Copyright (c) 2012 Piotr Roszatycki .
145              
146             This is free software; you can redistribute it and/or modify it under
147             the same terms as perl itself.
148              
149             See L