• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /macosx-10.9.5/CPANInternal-140/DateTime-Format-Builder/lib/DateTime/Format/Builder/
1=head1 NAME
2
3DateTime::Format::Builder::Tutorial - Quick class on using Builder
4
5=head1 CREATING A CLASS
6
7As most people who are writing modules know, you start a
8package with a package declaration and some indication of
9module version:
10
11    package DateTime::Format::ICal;
12    our $VERSION = '0.04';
13
14After that, you call Builder with some options. There are
15only a few (detailed later). Right now, we're only interested
16in I<parsers>.
17
18    use DateTime::Format::Builder
19    (
20        parsers => {
21	...
22	}
23    );
24
25The I<parsers> option takes a reference to a hash of method
26names and specifications:
27
28        parsers => {
29	    parse_datetime => ... ,
30	    parse_datetime_with_timezone => ... ,
31	    ...
32	}
33
34Builder will create methods in your class, each method being
35a parser that follows the given specifications. It is
36B<strongly> recommended that one method is called
37I<parse_datetime>, be it a Builder created method or one of
38your own.
39
40In addition to creating any of the parser methods it also
41creates a C<new()> method that can instantiate (or clone)
42objects of this class. This behaviour can be modified with
43the I<constructor> option, but we don't need to know that
44yet.
45
46Each value corresponding to a method name in the parsers
47list is either a single specification, or a list of
48specifications. We'll start with the simple case.
49
50        parse_briefdate => {
51	    params => [ qw( year month day ) ],
52	    regex  => qr/^(\d\d\d\d)(\d\d)(\d\d)$/,
53	},
54
55This will result in a method named I<parse_briefdate> which
56will take strings in the form C<20040716> and return
57DateTime objects representing that date. A user of the class
58might write:
59
60    use DateTime::Format::ICal;
61    my $date = "19790716";
62    my $dt = DateTime::Format::ICal->parse_briefdate( $date );
63    print "My birth month is ", $dt->month_name, "\n";
64
65The C<regex> is applied to the input string, and if it
66matches, then C<$1>, C<$2>, ... are mapped to the I<params>
67given and handed to C<< DateTime->new() >>. Essentially:
68
69    my $rv = DateTime->new( year => $1, month => $2, day => $3 );
70
71There are more complicated things one can do within a single
72specification, but we'll cover those later.
73
74Often, you'll want a method to be able to take one string,
75and run it against multiple parser specifications. It would
76be very irritating if the user had to work out what format
77the datetime string was in and then which method was most
78appropriate.
79
80So, Builder lets you specify multiple specifications:
81
82    parse_datetime => [
83	{
84	    params => [ qw( year month day hour minute second ) ],
85	    regex  => qr/^(\d\d\d\d)(\d\d)(\d\d)T(\d\d)(\d\d)(\d\d)$/,
86	},
87	{
88	    params => [ qw( year month day hour minute ) ],
89	    regex  => qr/^(\d\d\d\d)(\d\d)(\d\d)T(\d\d)(\d\d)$/,
90	},
91	{
92	    params => [ qw( year month day hour ) ],
93	    regex  => qr/^(\d\d\d\d)(\d\d)(\d\d)T(\d\d)$/,
94	},
95	{
96	    params => [ qw( year month day ) ],
97	    regex  => qr/^(\d\d\d\d)(\d\d)(\d\d)$/,
98	},
99    ],
100
101It's an arrayref of specifications. A parser will be created
102that will try each of these specifications sequentially, in
103the order you specified.
104
105There's a flaw with this though. In this example, we're
106building a parser for ICal datetimes. One can place a
107timezone id at the start of an ICal datetime. You might
108extract such an id with the following code:
109
110    if ( $date =~ s/^TZID=([^:]+):// )
111    {
112	$time_zone = $1;
113    }
114    # Z at end means UTC
115    elsif ( $date =~ s/Z$// )
116    {
117	$time_zone = 'UTC';
118    }
119    else
120    {
121	$time_zone = 'floating';
122    }
123
124C<$date> would end up without the id, and C<$time_zone> would
125contain something appropriate to give to DateTime's
126I<set_time_zone> method, or I<time_zone> argument.
127
128But how to get this scrap of code into your parser? You
129might be tempted to call the parser something else and build
130a small wrapper. There's no need though because an option is
131provided for preprocesing dates:
132
133    parse_datetime => [
134        [ preprocess => \&_parse_tz ], # Only changed line!
135	{
136	    params => [ qw( year month day hour minute second ) ],
137	    regex  => qr/^(\d\d\d\d)(\d\d)(\d\d)T(\d\d)(\d\d)(\d\d)$/,
138	},
139	{
140	    params => [ qw( year month day hour minute ) ],
141	    regex  => qr/^(\d\d\d\d)(\d\d)(\d\d)T(\d\d)(\d\d)$/,
142	},
143	{
144	    params => [ qw( year month day hour ) ],
145	    regex  => qr/^(\d\d\d\d)(\d\d)(\d\d)T(\d\d)$/,
146	},
147	{
148	    params => [ qw( year month day ) ],
149	    regex  => qr/^(\d\d\d\d)(\d\d)(\d\d)$/,
150	},
151    ],
152
153It will necessitate I<_parse_tz> to be written, and that
154routine looks like this:
155
156    sub _parse_tz
157    {
158	my %args = @_;
159	my ($date, $p) = @args{qw( input parsed )};
160	if ( $date =~ s/^TZID=([^:]+):// )
161	{
162	    $p->{time_zone} = $1;
163	}
164	# Z at end means UTC
165	elsif ( $date =~ s/Z$// )
166	{
167	    $p->{time_zone} = 'UTC';
168	}
169	else
170	{
171	    $p->{time_zone} = 'floating';
172	}
173	return $date;
174    }
175
176On input it is given a hash containing two items: the input
177date and a hashref that will be used in the parsing. The
178return value from the routine is what the parser
179specifications will run against, and anything in the
180I<parsed> hash (C<$p> in the example) will be put in the
181call to C<< DateTime->new(...) >>.
182
183So, we now have a happily working ICal parser. It parses the
184assorted formats, and can also handle timezones. Is there
185anything else it needs to do? No. But we can make it work
186more efficiently.
187
188At present, the specifications are tested sequentially.
189However, each one applies to strings of particular lengths.
190Thus we could be efficient and have the parser only test the
191given strings against a parser that handles that string
192length. Again, Builder makes it easy:
193
194    parse_datetime => [
195	[ preprocess => \&_parse_tz ],
196	{
197	    length => 15, # We handle strings of exactly 15 chars
198	    params => [ qw( year month day hour minute second ) ],
199	    regex  => qr/^(\d\d\d\d)(\d\d)(\d\d)T(\d\d)(\d\d)(\d\d)$/,
200	},
201	{
202	    length => 13, # exactly 13 chars...
203	    params => [ qw( year month day hour minute ) ],
204	    regex  => qr/^(\d\d\d\d)(\d\d)(\d\d)T(\d\d)(\d\d)$/,
205	},
206	{
207	    length => 11, # 11..
208	    params => [ qw( year month day hour ) ],
209	    regex  => qr/^(\d\d\d\d)(\d\d)(\d\d)T(\d\d)$/,
210	},
211	{
212	    length => 8, # yes.
213	    params => [ qw( year month day ) ],
214	    regex  => qr/^(\d\d\d\d)(\d\d)(\d\d)$/,
215	},
216	],
217
218Now the created parser will create a parser that only runs
219specifications against appropriate strings.
220
221So our complete code looks like:
222
223    package DateTime::Format::ICal;
224    use strict;
225    our $VERSION = '0.04';
226
227    use DateTime::Format::Builder
228    (
229	parsers => {
230	    parse_datetime => [
231	    [ preprocess => \&_parse_tz ],
232	    {
233		length => 15,
234		params => [ qw( year month day hour minute second ) ],
235		regex  => qr/^(\d\d\d\d)(\d\d)(\d\d)T(\d\d)(\d\d)(\d\d)$/,
236	    },
237	    {
238		length => 13,
239		params => [ qw( year month day hour minute ) ],
240		regex  => qr/^(\d\d\d\d)(\d\d)(\d\d)T(\d\d)(\d\d)$/,
241	    },
242	    {
243		length => 11,
244		params => [ qw( year month day hour ) ],
245		regex  => qr/^(\d\d\d\d)(\d\d)(\d\d)T(\d\d)$/,
246	    },
247	    {
248		length => 8,
249		params => [ qw( year month day ) ],
250		regex  => qr/^(\d\d\d\d)(\d\d)(\d\d)$/,
251	    },
252	    ],
253	},
254    );
255
256    sub _parse_tz
257    {
258	my %args = @_;
259	my ($date, $p) = @args{qw( input parsed )};
260	if ( $date =~ s/^TZID=([^:]+):// )
261	{
262	    $p->{time_zone} = $1;
263	}
264	# Z at end means UTC
265	elsif ( $date =~ s/Z$// )
266	{
267	    $p->{time_zone} = 'UTC';
268	}
269	else
270	{
271	    $p->{time_zone} = 'floating';
272	}
273	return $date;
274    }
275
276    1;
277
278And that's an ICal parser. The actual
279L<DateTime::Format::ICal> module also includes formatting
280methods and parsing for durations, but Builder doesn't
281support those yet. A drop in replacement (at the time of
282writing the replacement) can be found in the F<examples>
283directory of the Builder distribution, along with similar
284variants of other common modules.
285
286=head1 SUPPORT
287
288Any errors you see in this document, please log them with
289CPAN RT system via the web or email:
290
291    http://perl.dellah.org/rt/dtbuilder
292    bug-datetime-format-builder@rt.cpan.org
293
294This makes it much easier for me to track things and thus means
295your problem is less likely to be neglected.
296
297=head1 LICENSE AND COPYRIGHT
298
299Copyright E<copy> Iain Truskett, 2003. All rights reserved.
300
301You can redistribute this document and/or modify
302it under the same terms as Perl itself.
303
304The full text of the licenses can be found in the F<Artistic> and
305F<COPYING> files included with this document.
306
307=head1 AUTHOR
308
309Iain Truskett <spoon@cpan.org>
310
311=head1 SEE ALSO
312
313C<datetime@perl.org> mailing list.
314
315http://datetime.perl.org/
316
317L<perl>, L<DateTime>, L<DateTime::Format::Builder>
318
319=cut
320
321