File Coverage

blib/lib/Buffer/Transactional.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


line stmt bran cond sub pod time code
1             package Buffer::Transactional;
2 10     10   562614 use Moose;
  0            
  0            
3             use Moose::Util::TypeConstraints;
4              
5             use Buffer::Transactional::Buffer::String;
6              
7             our $VERSION = '0.02';
8             our $AUTHORITY = 'cpan:STEVAN';
9              
10             has 'out' => (
11             is => 'ro',
12             isa => duck_type( [ 'print' ] ),
13             required => 1,
14             );
15              
16             has '_buffers' => (
17             traits => [ 'Array' ],
18             is => 'ro',
19             isa => 'ArrayRef[ Buffer::Transactional::Buffer ]',
20             lazy => 1,
21             default => sub { [] },
22             handles => {
23             '_add_buffer' => 'push',
24             'clear_current_buffer' => 'pop',
25             'has_current_buffer' => 'count',
26             'current_buffer' => [ 'get', -1 ]
27             }
28             );
29              
30             has 'buffer_class' => (
31             is => 'ro',
32             isa => 'ClassName',
33             lazy => 1,
34             default => sub { 'Buffer::Transactional::Buffer::String' },
35             );
36              
37             sub begin_work {
38             my $self = shift;
39             $self->_add_buffer( $self->buffer_class->new );
40             }
41              
42             sub commit {
43             my $self = shift;
44             ($self->has_current_buffer)
45             || confess "Not within transaction scope";
46              
47             my $current = $self->clear_current_buffer;
48              
49             if ($self->has_current_buffer) {
50             $self->current_buffer->subsume( $current );
51             }
52             else {
53             $self->out->print( $current->as_string );
54             }
55             }
56              
57             sub rollback {
58             my $self = shift;
59             ($self->has_current_buffer)
60             || confess "Not within transaction scope";
61             $self->clear_current_buffer;
62             }
63              
64             sub _write_to_buffer {
65             my $self = shift;
66             ($self->has_current_buffer)
67             || confess "Not within transaction scope";
68             $self->current_buffer->put( @_ );
69             }
70              
71             sub print {
72             my ($self, @data) = @_;
73             $self->_write_to_buffer( @data );
74             }
75              
76             sub txn_do {
77             my ($self, $body) = @_;
78             $self->begin_work;
79             $body->();
80             $self->commit;
81             }
82              
83             __PACKAGE__->meta->make_immutable;
84              
85             no Moose; 1;
86              
87             __END__
88              
89             =pod
90              
91             =head1 NAME
92              
93             Buffer::Transactional - A transactional buffer for writing data
94              
95             =head1 SYNOPSIS
96              
97             use IO::File;
98             use Try::Tiny;
99             use Buffer::Transactional;
100              
101             my $b = Buffer::Transactional->new( out => IO::File->new('my_novel.txt', 'w') );
102             try {
103             $b->begin_work;
104             $b->print('It was the best of times, it was the worst of times ...');
105             # ...
106             die "Whoops!";
107             # ...
108             $b->commit;
109             } catch {
110             $b->rollback;
111             warn "Transaction aborted because : $_";
112             };
113              
114             =head1 DESCRIPTION
115              
116             Allow me to take you on a journey, into the distant past ...
117              
118             So a year or so ago I got really into the O'Caml language and in exploring
119             the available libraries I stumbled onto OCamlnet. Ocamlnet is basically an
120             all things internet related module for O'Caml. Of particular interest to me
121             was their CGI module and one nice feature jumped out at me, which was the
122             fact they used a transactional buffer to print the output of the CGI. Now,
123             in a Modern Perl world few people probably call C<print> inside a CGI script
124             anymore, but instead use templating systems and the like. However I still
125             thought that a nice transactional buffer would be useful for other things
126             such as in the internals of such a templating system or the bowels of a web
127             framework.
128              
129             Fast forward to 2009 ... and here you have it! We support several different
130             kind of buffer types as well as nested transactions. As this is the first
131             release, no doubt there is much room for improvement so feel free to suggest
132             away.
133              
134             Use only as directed, be sure to check with your doctor to make sure
135             your healthy enough for transactional activity. If your transaction lasts
136             for more then 4 hours, consult a physician immediately.
137              
138             =head1 ATTRIBUTES
139              
140             =over 4
141              
142             =item B<out>
143              
144             This is an object which responds to the method C<print>, most often
145             it will be some kind of L<IO::File>, L<IO::String> or L<IO::Scalar>
146             varient. This attribute is required.
147              
148             =item B<buffer_class>
149              
150             This is a class name for the buffer subclass you wish to use, it
151             currently defaults to L<Buffer::Transactional::Buffer::String>.
152              
153             =back
154              
155             =head1 METHODS
156              
157             =over 4
158              
159             =item B<begin_work>
160              
161             Declares the start of a transaction.
162              
163             =item B<commit>
164              
165             Commits the current transaction.
166              
167             =item B<rollback>
168              
169             Rollsback the current transaction.
170              
171             =item B<print ( @strings )>
172              
173             Print to the current buffer.
174              
175             =item B<txn_do ( \&body )>
176              
177             This is a convience wrapper around the C<begin_work> and C<commit>
178             methods. It takes a CODE ref and will execute it within the context
179             of a transaction. It does B<not> attempt to handle exceptions,
180             rollbacks or anything of the like, it simply wraps the transaction.
181              
182             =back
183              
184             =head1 SEE ALSO
185              
186             OCamlnet - L<http://projects.camlcity.org/projects/ocamlnet.html>
187              
188             =head1 BUGS
189              
190             All complex software has bugs lurking in it, and this module is no
191             exception. If you find a bug please either email me, or add the bug
192             to cpan-RT.
193              
194             =head1 AUTHOR
195              
196             Stevan Little E<lt>stevan.little@iinteractive.comE<gt>
197              
198             =head1 COPYRIGHT AND LICENSE
199              
200             Copyright 2009, 2010 Infinity Interactive, Inc.
201              
202             L<http://www.iinteractive.com>
203              
204             This library is free software; you can redistribute it and/or modify
205             it under the same terms as Perl itself.
206              
207             =cut