File Coverage

blib/lib/Crixa.pm
Criterion Covered Total %
statement 19 21 90.4
branch n/a
condition n/a
subroutine 7 7 100.0
pod n/a
total 26 28 92.8


line stmt bran cond sub pod time code
1             package Crixa;
2              
3 1     1   8350 use v5.10;
  1         3  
  1         41  
4              
5 1     1   3 use strict;
  1         1  
  1         22  
6 1     1   3 use warnings;
  1         1  
  1         23  
7 1     1   4 use namespace::autoclean;
  1         1  
  1         6  
8              
9             our $VERSION = '0.13';
10              
11 1     1   270 use Moose;
  1         1  
  1         7  
12              
13 1     1   5992 use Crixa::Channel;
  1         3  
  1         43  
14 1     1   205 use Net::AMQP::RabbitMQ 0.310000;
  0            
  0            
15              
16             with qw(Crixa::HasMQ);
17              
18             has '+_mq' => (
19             init_arg => 'mq',
20             required => 0,
21             lazy => 1,
22             builder => '_build_mq',
23             handles => [qw( disconnect )],
24             );
25              
26             has host => (
27             isa => 'Str',
28             is => 'ro',
29             required => 1,
30             );
31              
32             has [qw(user password)] => (
33             isa => 'Str',
34             is => 'ro',
35             );
36              
37             has port => (
38             isa => 'Int',
39             is => 'ro',
40             );
41              
42             has _channel_id => (
43             isa => 'Int',
44             default => 0,
45             traits => ['Counter'],
46             handles => {
47             _next_channel_id => 'inc',
48             release_channel_id => 'dec',
49             reset_channel_id => 'reset',
50             },
51             );
52              
53             sub _build_mq { Net::AMQP::RabbitMQ->new; }
54              
55             ## no critic (Subroutines::ProhibitBuiltinHomonyms)
56             sub connect {
57             my $self = shift->new(@_);
58             $self->_connect_mq($self);
59             return $self;
60             }
61             ## use critic
62              
63             sub _connect_mq {
64             my ( $self, $crixa ) = @_;
65              
66             my %args;
67             for (qw( user password port )) {
68             $args{$_} = $crixa->$_ if defined $crixa->$_;
69             }
70             $self->_mq->connect( $crixa->host, \%args );
71             }
72              
73             sub new_channel {
74             my $self = shift;
75              
76             return Crixa::Channel->new(
77             id => $self->_next_channel_id,
78             _mq => $self->_mq,
79             );
80             }
81              
82             sub is_connected {
83             my $self = shift;
84              
85             return
86             $self->_mq->can('is_connected') ? $self->_mq->is_connected
87             : $self->_mq->can('connected') ? $self->_mq->connected
88             : die
89             'The underlying mq object does not have an is_connected or connected method!';
90             }
91              
92             sub DEMOLISH {
93             my $self = shift;
94             $self->disconnect if $self->_mq && $self->is_connected;
95             }
96              
97             __PACKAGE__->meta->make_immutable;
98              
99             1;
100              
101             # ABSTRACT: A Cleaner API for Net::AMQP::RabbitMQ
102              
103             __END__
104              
105             =pod
106              
107             =head1 NAME
108              
109             Crixa - A Cleaner API for Net::AMQP::RabbitMQ
110              
111             =head1 VERSION
112              
113             version 0.13
114              
115             =head1 SYNOPSIS
116              
117             use Crixa;
118              
119             my $mq = Crixa->connect( host => 'localhost' );
120             my $channel = $mq->new_channel;
121             my $exchange = $channel->exchange( name => 'hello' );
122              
123             $exchange->publish('Hello World');
124              
125             my $queue = $exchange->queue( name => 'hello' );
126              
127             $queue->handle_message( sub { say $_->body } );
128              
129             =head1 DESCRIPTION
130              
131             All the world will be your enemy, Prince of a Thousand enemies. And when
132             they catch you, they will kill you. But first they must catch you; digger,
133             listener, runner, Prince with the swift warning. Be cunning, and full of
134             tricks, and your people will never be destroyed. -- Richard Adams
135              
136             This module provides a more natural API over L<Net::AMQP::RabbitMQ>, with
137             separate objects for channels, exchanges, and queues.
138              
139             =encoding UTF-8
140              
141             =head1 WARNING
142              
143             B<Crixa is still in development and the API may change in the future!>
144              
145             =head1 METHODS
146              
147             This class provides the following methods:
148              
149             =head2 Crixa->connect(...)
150              
151             Creates a new connection to a RabbitMQ server. It takes a hash or hashref of
152             named parameters.
153              
154             =over 4
155              
156             =item host => $hostname
157              
158             The hostname to connect to. Required.
159              
160             =item port => $post
161              
162             An optional port.
163              
164             =item user => $user
165              
166             An optional username.
167              
168             =item password => $password
169              
170             An optional password.
171              
172             =item mq => $mq
173              
174             This is an optional parameter which can contain an object which implements the
175             C<Net::AMQP::RabbitMQ> interface.
176              
177             Normally this will be created as needed but you can pass a
178             L<Test::Net::RabbitMQ> object instead so you can write tests for code that
179             uses Crixa without actually having a rabbitmq server running.
180              
181             Note that L<Test::Net::RabbitMQ> does not (as of version 0.10) implement the
182             entire L<Net::AMQP::RabbitMQ> interface so some Crixa methods may blow up with
183             L<Test::Net::RabbitMQ>.
184              
185             See the section on L</MOCKING> for more details.
186              
187             =back
188              
189             =head2 $crixa->new_channel
190              
191             Returns a new L<Crixa::Channel> object.
192              
193             You can use the channel to create exchanges and queues.
194              
195             =head2 $crixa->disconnect
196              
197             Disconnect from the server. This is called implicitly by C<DEMOLISH> so
198             normally there should be no need to do this explicitly.
199              
200             =head2 $crixa->host
201              
202             Returns the port passed to the constructor, if nay.
203              
204             =head2 $crixa->user
205              
206             Returns the user passed to the constructor, if any.
207              
208             =head2 $crixa->password
209              
210             Returns the password passed to the constructor, if any.
211              
212             =head2 $crixa->is_connected
213              
214             This returns true if the underlying mq object thinks it is connected.
215              
216             =head1 MOCKING
217              
218             If you are testing code that uses Crixa, you may want to mock out the use of
219             an actual rabbitmq server with something that is a little simpler to test. In
220             that case, you can pass a L<Test::Net::RabbitMQ> object to the C<<
221             Crixa->connect >> method:
222              
223             my $test_mq = Test::Net::RabbitMQ->new;
224             my $crixa = Crixa->connect(
225             host => 'irrelevant',
226             mq => $test_mq,
227             );
228              
229             Note that if you are publishing and consuming messages, this all must happen
230             in a single process B<and a single L<Test::Net::RabbitMQ> object> in order for
231             this mocking to work.
232              
233             If the code that publishes messages makes a separate Crixa object from the one
234             you use to test those messages, you need to be careful to share the same
235             L<Test::Net::RabbitMQ> object. Also, since the Crixa object calls its
236             C<disconnect()> method when it goes out of scope, you may need to reconnect
237             the L<Test::Net::RabbitMQ> object or it will die when you call methods on it.
238              
239             Here's an example:
240              
241             my $test_mq = Test::Net::RabbitMQ->new;
242             test_messages($test_mq) :;
243              
244             sub test_messages {
245             my $mq = shift;
246             my $crixa = Crixa->connect(
247             host => 'irrelevant',
248             mq => $test_mq,
249             );
250              
251             publish($test_mq);
252              
253             # This will die!
254             my @messages = $crixa->channel->queue(...)->check_for_messages;
255             }
256              
257             sub publish {
258             my $mq = shift;
259             my $crixa = Crixa->connect(
260             host => 'irrelevant',
261             mq => $test_mq,
262             );
263              
264             # publish some messages
265              
266             # When the sub exits the $crixa object calls disconnect() on itself.
267             }
268              
269             We can fix this by adding an extra "safety" call to connect the C<$test_mq>
270             object in the C<test_messages()> sub:
271              
272             sub test_messages {
273             my $mq = shift;
274             my $crixa = Crixa->connect(
275             host => 'irrelevant',
276             mq => $test_mq,
277             );
278              
279             publish($test_mq);
280              
281             $test_mq->connect unless $test_mq->connected;
282              
283             # This will die!
284             my @messages = $crixa->channel->queue(...)->check_for_messages;
285             }
286              
287             Of course, this is a very artificial example, but in real code you may come
288             across this problem.
289              
290             =head1 SUPPORT
291              
292             Please report all issues with this code using the GitHub issue tracker at
293             L<https://github.com/Tamarou/Crixa/issues>.
294              
295             =head1 SEE ALSO
296              
297             This module uses L<Net::AMQP::RabbitMQ> under the hood, though it does not
298             expose everything provided by its API.
299              
300             The best documentation we've found on RabbitMQ (and AMQP) concepts is the
301             Bunny documentation at http://rubybunny.info/articles/guides.html. We strongly
302             recommend browsing this to get a better understanding of how RabbitMQ works,
303             what different options for exchanges, queues, and messages mean, and more.
304              
305             =head1 AUTHORS
306              
307             =over 4
308              
309             =item *
310              
311             Chris Prather <chris@prather.org>
312              
313             =item *
314              
315             Dave Rolsky <autarch@urth.org>
316              
317             =back
318              
319             =head1 CONTRIBUTORS
320              
321             =for stopwords Gregory Oschwald Ran Eilam Torsten Raudssus
322              
323             =over 4
324              
325             =item *
326              
327             Gregory Oschwald <goschwald@maxmind.com>
328              
329             =item *
330              
331             Gregory Oschwald <oschwald@gmail.com>
332              
333             =item *
334              
335             Ran Eilam <ran.eilam@gmail.com>
336              
337             =item *
338              
339             Torsten Raudssus <torsten@raudss.us>
340              
341             =back
342              
343             =head1 COPYRIGHT AND LICENSE
344              
345             This software is copyright (c) 2012 - 2015 by Chris Prather.
346              
347             This is free software; you can redistribute it and/or modify it under
348             the same terms as the Perl 5 programming language system itself.
349              
350             =cut