File Coverage

blib/lib/ZMQ/Declare/DSL.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1             package ZMQ::Declare::DSL;
2             {
3             $ZMQ::Declare::DSL::VERSION = '0.03';
4             }
5              
6 1     1   21533 use 5.008001;
  1         4  
  1         49  
7 1     1   5 use strict;
  1         2  
  1         33  
8 1     1   5 use warnings;
  1         2  
  1         32  
9              
10 1     1   5 use Carp ();
  1         2  
  1         20  
11 1     1   1619 use ZeroMQ ();
  0            
  0            
12              
13             use ZMQ::Declare;
14              
15             require Exporter;
16             our @ISA = qw(Exporter);
17              
18             our @EXPORT = qw(
19             declare_zdcf
20             app
21             context
22             iothreads
23             device
24             name
25             type
26             sock
27             bnd
28             conn
29             option
30             );
31             our %EXPORT_TAGS = (
32             'all' => \@EXPORT,
33             );
34              
35             our $ZDCF;
36             our $App;
37             our $Context;
38             our $Device;
39             our $Socket;
40             # Valid scopes: zdcf, app, context, device, socket
41             our $CurScope;
42              
43             sub _check_scope {
44             my $expected_scope = shift;
45             my $global_state_var_ref = shift;
46             (undef, undef, undef, my $function) = caller(1);
47             $function =~ s/^.*:://;
48              
49             Carp::croak("'$function' outside ZDCF found: Did you fail to call 'declare_zdcf'?")
50             if not defined $CurScope;
51             Carp::croak("Wrongly nested or out-of-place '$function' detected: You cannot nest ZDCF '${function}'s")
52             if ($global_state_var_ref && $$global_state_var_ref)
53             or $CurScope ne $expected_scope;
54             }
55              
56             sub declare_zdcf(&) {
57             local $ZDCF = {version => "1.0"};
58             local $CurScope = 'zdcf';
59             # Clear the various components in this dynamic scope
60             local $App;
61             local $Context;
62             local $Device;
63             local $Socket;
64             $_[0]->();
65             return ZMQ::Declare::ZDCF->new(tree => $ZDCF);
66             }
67              
68             sub app(&) {
69             _check_scope('zdcf', \$App);
70              
71             local $CurScope = 'app';
72             local $App = {};
73             $_[0]->();
74              
75             my $name = delete $App->{name};
76             Carp::croak("Missing app name!") if not defined $name;
77             $ZDCF->{apps}{$name} = $App;
78             }
79              
80             sub context(&) {
81             _check_scope('app' => \$Context);
82              
83             local $Context = {};
84             local $CurScope = 'context';
85             $_[0]->();
86             $App->{context} = $Context;
87             }
88              
89             sub iothreads($) {
90             _check_scope('context');
91             $Context->{iothreads} = $_[0];
92             }
93              
94             sub device(&) {
95             _check_scope('app' => \$Device);
96              
97             local $Device = {};
98             local $CurScope = 'device';
99             $_[0]->();
100              
101             my $name = delete $Device->{name};
102             if (not defined $name) {
103             $name = $App->{name};
104             }
105              
106             Carp::croak("Missing device name!") if not defined $name;
107             $App->{devices}{$name} = $Device;
108             }
109              
110             sub name($) {
111             if (not defined $CurScope) {
112             Carp::croak("Error 'name()' outside app, device, and socket. Did you fail to call 'declare_zdcf'?");
113             }
114             elsif ($CurScope eq 'device') { $Device->{name} = shift }
115             elsif ($CurScope eq 'socket') { $Socket->{name} = shift }
116             elsif ($CurScope eq 'app') { $App->{name} = shift }
117             else { Carp::croak("Error 'name()' outside app, device, and socket") }
118             }
119              
120             sub type($) {
121             if (not defined $CurScope) {
122             Carp::croak("Error 'type()' outside device, and socket. Did you fail to call 'declare_zdcf'?");
123             }
124             elsif ($CurScope eq 'device') { $Device->{type} = shift }
125             elsif ($CurScope eq 'socket') { $Socket->{type} = shift }
126             else { Carp::croak("Error 'type()' outside device, and socket") }
127             }
128              
129             sub sock(&) {
130             _check_scope('device' => \$Socket);
131              
132             local $Socket = {};
133             local $CurScope = 'socket';
134             $_[0]->();
135              
136             my $name = delete $Socket->{name};
137             Carp::croak("Missing socket name!") if not defined $name;
138             $Device->{sockets}{$name} = $Socket;
139             }
140              
141             sub bnd(@) {
142             Carp::croak("Error: bnd (bind) outside socket")
143             if not defined $CurScope or $CurScope ne 'socket';
144             push @{ $Socket->{bind} }, @_;
145             }
146              
147             sub conn(@) {
148             Carp::croak("Error: conn (connect) outside socket")
149             if not defined $CurScope or $CurScope ne 'socket';
150             push @{ $Socket->{connect} }, @_;
151             }
152              
153             sub option(%) {
154             Carp::croak("Error: option() outside socket")
155             if not defined $CurScope or $CurScope ne 'socket';
156             while (@_) {
157             my $k = shift;
158             $Socket->{option}->{$k} = shift;
159             }
160             }
161              
162             1;
163             __END__
164              
165             =head1 NAME
166              
167             ZMQ::Declare::DSL - DSL for declaring 0MQ infrastructure
168              
169             =head1 SYNOPSIS
170              
171             use ZMQ::Declare::DSL;
172            
173             my $zdcf = declare_zdcf {
174            
175             app {
176             name 'weather';
177            
178             context { iothreads 1 };
179            
180             device {
181             name 'client';
182             sock {
183             name 'weather_stream';
184             type 'sub';
185             conn qw(tcp://localhost:12345);
186             option subscribe => "70123"; # ZIP code in this example
187             };
188             };
189            
190             device {
191             name 'server';
192             sock {
193             name 'weather_publisher';
194             type 'pub';
195             bnd qw(tcp://*:12345);
196             };
197             };
198             };
199            
200             };
201            
202             # elsewhere
203             my $server = $zdcf->application("weather")->device('server');
204             $server->implementation(sub {
205             my ($runtime) = @_;
206             # server main loop here
207             return();
208             });
209             $server->run();
210            
211             # yet elsewhere
212             my $client = $zdcf->application("weather")->device('client');
213             $client->implementation(sub {
214             my ($runtime) = @_;
215             # client main loop here
216             return();
217             });
218             $client->run();
219              
220             =head1 DESCRIPTION
221              
222             B<This is experimental software. Interfaces and implementation are subject to
223             change. If you are interested in using this in production, please get in touch
224             to gauge the current state of stability.>
225              
226             This module defines a domain specific language (which just so happens to be valid
227             Perl that's slightly beaten into shape) for declaring 0MQ infrastructure.
228             Please read L<ZMQ::Declare> before you proceed with this document.
229              
230             This module is just a thin syntax-sugar layer on top of simply creating a regular
231             nested Perl data structure and passing it to C<ZMQ::Declare::ZDCF->new()>. It
232             adds no features beyond a different syntax. Unless you find the syntax very
233             attractive, consider using a simpler way to declare 0MQ infrastructure.
234              
235             Generally speaking, there are multiple kinds of functions in this module:
236             There are those that have a notion of scope, such as the outer C<declare_zdcf BLOCK>
237             and C<app BLOCK>, C<context BLOCK>, C<device BLOCK>, and C<socket BLOCK>.
238             And there are those that simply set a property of the enclosing object (well, scope):
239             C<name STRING>, C<type STRING>, C<bnd LIST>, C<conn LIST>, C<option LIST>,
240             and C<iothreads INTEGER>.
241              
242             Most of these can only occurr within certain scopes. For example, C<iothreads> can
243             only be set within a C<context> and a C<context> can only appear within an C<app>,
244             but a C<name> is valid in an C<app>, a C<device>, or a C<socket>. Etc.
245              
246             =head2 EXPORTS
247              
248             This module exports a plethora of functions (as of this writing, all functions
249             that are documented below) by default. That's the point.
250              
251             =head1 FUNCTIONS
252              
253             =head2 declare_zdcf
254              
255             The outermost function that starts the declaration of a new ZDCF specification
256             containing zero or more apps.
257              
258             =head2 app
259              
260             Defines a new app within a ZDCF specification. Only valid within the outermost
261             C<declare_zdcf> block. Must cointain at least a C<name> property.
262              
263             Can contain one or more devices.
264              
265             =head2 context
266              
267             Defines a threading context of an app. Can occur zero or one time per app,
268             but cannot be used outside an app or inside its substructures (like devices).
269              
270             =head2 iothreads
271              
272             Defines the number of iothreads in a threading context. Defaults to one.
273             This is the only property that is currently valid in a C<context>.
274              
275             =head2 device
276              
277             Defines a single device within an app. Can occur zero or more times
278             in each app. Not valid outside of an app definition or within its substructures.
279              
280             Can contain zero or more sockets. May have a type and a name property.
281             The name defaults to the app name, but that requires that the app name
282             declaration appears before the device declaration.
283              
284             =head2 sock
285              
286             Defines a single socket within a device. Can occur zero or more times in each device.
287             Not valid outside of a device definition or within its substructures.
288              
289             Requires at least a name and a type property and at least one bind or connect
290             property.
291              
292             =head2 name
293              
294             Defines the name of the enclosing object. Valid for apps, devices, and sockets.
295              
296             =head2 type
297              
298             Defines the type of the enclosing object. Valid for devices and sockets.
299              
300             =head2 bnd
301              
302             Given a list of endpoints (strings), adds to the set of endpoints that the
303             enclosing socket is to bind to.
304              
305             Valid any number of times within a socket. Either C<bnd> or C<conn> need
306             to appear at least once in a socket.
307              
308             =head2 conn
309              
310             Same as C<bnd>, but for connecting to sockets instead of binding.
311              
312             =head2 option
313              
314             Given a list of key/value pairs, sets socket options. Valid any number of
315             times within a socket.
316              
317             =head1 SEE ALSO
318              
319             L<ZMQ::Declare>
320              
321             L<ZeroMQ>
322              
323             =head1 AUTHOR
324              
325             Steffen Mueller E<lt>smueller@cpan.orgE<gt>
326              
327             =head1 COPYRIGHT AND LICENSE
328              
329             Copyright (C) 2012 by Steffen Mueller
330              
331             This library is free software; you can redistribute it and/or modify
332             it under the same terms as Perl itself, either Perl version 5.8.1 or,
333             at your option, any later version of Perl 5 you may have available.
334              
335             =cut