File Coverage

blib/lib/Text/Lorem/More.pm
Criterion Covered Total %
statement 142 162 87.6
branch 37 62 59.6
condition 10 15 66.6
subroutine 21 23 91.3
pod 5 5 100.0
total 215 267 80.5


line stmt bran cond sub pod time code
1             package Text::Lorem::More;
2              
3 8     8   165317 use warnings;
  8         20  
  8         300  
4 8     8   46 use strict;
  8         17  
  8         526  
5              
6             =head1 NAME
7              
8             Text::Lorem::More - Generate formatted nonsense using random Latin words.
9              
10             =head1 VERSION
11              
12             Version 0.13
13              
14             =head1 SYNOPSIS
15              
16             Generate formatted nonsense using random Latin words.
17              
18             use Text::Lorem::More;
19              
20             my $lorem = Text::Lorem::More->new;
21            
22             # Greet a friend
23             print "Hello, ", $lorem->fullname, "\n";
24              
25             # You could also ...
26             print $lorem->process("Hello, +fullname\n");
27              
28             ... or you can use the singleton:
29              
30             use Text::Lorem::More qw(lorem);
31              
32             # Generate a random latin word
33             my $latinwordoftheday = lorem->word;
34              
35             # Produce paragaphs in the Text::Lorem compatible manner
36             my $content = lorem->paragraphs(3);
37              
38             # Print 4 paragraphs, each separated by a single newline and tab:
39             print "\t", scalar lorem->paragraph(4, "\n\t");
40              
41             =cut
42              
43             our $VERSION = '0.13';
44              
45 8     8   47 use base qw/Exporter/;
  8         28  
  8         848  
46              
47 8     8   45 use Carp;
  8         37  
  8         745  
48 8     8   22365 use Parse::RecDescent;
  8         500810  
  8         74  
49              
50 8     8   7384 use Text::Lorem::More::Source;
  8         21  
  8         6748  
51            
52             our $PARSER = Parse::RecDescent->new(<<'_END_');
53             content:
54             content: pattern(s) { \@content }
55             pattern: escape | variable | text
56             escape: '++' { push @content, \"+" }
57             variable: '+{' identifier '}' | '+' identifier
58             identifier: /[A-Za-z0-9_]+/ { push @content, $item[1] }
59             text: m/[^\+]+/ { push @content, \$item[1] }
60             _END_
61              
62             =head1 GENERATORS
63              
64             To use a generator, call the method with the same name as the generator,
65             To use the name generator, for example:
66              
67             my $name = $lorem->name;
68              
69             Alternatively, you can use the C or C functions.
70             To use the hostname generator, for example:
71              
72             my $hostname = $lorem->hostname;
73              
74             # This will do the same thing ...
75             my $otherhostname = $lorem->generate("+hostname");
76              
77             =head2 word
78              
79             Generates random latin word.
80              
81             dicta
82             sed
83             repellat
84              
85             =head2 sentence
86              
87             Generates between 4 and 9 words, with the first letter of the first word capitalized and a period following
88             the last word.
89              
90             =head2 paragraph
91              
92             Generates between 3 and 6 sentences,
93              
94             =head2 words $count
95              
96             A Text::Lorem compatible words generator.
97             Will generate $count words joined by " ".
98              
99             =head2 sentences $count
100              
101             A Text::Lorem compatible sentences generator.
102             Will generate $count sentences joined by " ".
103             Each sentence contains between 4 and 9 words and ends with a period.
104             B
105              
106             =head2 paragraphs $count
107              
108             A Text::Lorem compatible sentences generator.
109             Will generate $count paragraphs joined by "\n\n".
110             Each paragraph contains between 3 and 6 sentences.
111              
112             =head2 name / firstname / lastname
113              
114             Generates a random latin word with the first letter capitalized
115              
116             Repellat
117             Sed
118             Ipsum
119              
120             =head2 fullname
121              
122             Generates a firstname and lastname separated by a space
123              
124             Lorem Dicta
125              
126             =head2 username
127              
128             Generates a random latin word
129              
130             =head2 title
131              
132             Generates between 1 and 3 words with the first letter of the first word capitalized
133              
134             =head2 description
135              
136             Generates between 1 and 3 sentences.
137              
138             =head2 tld / topleveldomain
139              
140             Generates a top level domain.
141             Currently, this will either be "com", "org", or "net".
142              
143             =head2 domain / domainname
144              
145             Generates a domainname.
146             Currently, this will attach "example" to a tld generator result.
147              
148             example.com
149             example.net
150              
151             =head2 host / hostname
152              
153             Generates a hostname.
154             Currently, this will either return a plain domainname, as above, or attach a latin word to a domainname result.
155              
156             et.example.com
157             example.org
158              
159             =head2 email / mail
160              
161             =head2 path
162              
163             =head2 httpurl
164              
165             =head2 mailto
166              
167             =cut
168              
169             our %GENERATOR = (
170              
171 104         510 name => sub { [ sub { ucfirst lc $_ }, "+word" ] },
  104         277  
172             firstname => "name",
173             lastname => "name",
174              
175 23         58 fullname => sub { ["+firstname +lastname"] },
176              
177             username => "word",
178              
179             word => [ grep { length $_ } map { s/\W//g; lc } split m/\s/, <<_END_ ],
180             alias consequatur aut perferendis sit voluptatem accusantium doloremque aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis Nemo enim ipsam voluptatem quia voluptas sit suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae et iusto odio dignissimos ducimus qui blanditiis praesentium laudantium, totam rem voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi sint occaecati cupiditate non provident, Sed ut perspiciatis unde omnis iste natus error similique sunt in culpa qui officia deserunt mollitia animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio. Nam libero tempore, cum soluta nobis est eligendi optio cumque nihil impedit quo porro quisquam est, qui minus id quod maxime placeat facere possimus, omnis voluptas assumenda est, omnis dolor repellendus. Temporibus autem quibusdam et aut consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur? At vero eos et accusamus officiis debitis aut rerum necessitatibus saepe eveniet ut et voluptates repudiandae sint et molestiae non recusandae. Itaque earum rerum hic tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores doloribus asperiores repellat.
181             _END_
182              
183 0         0 title => sub { [ sub { ucfirst($_) }, "+word", 1 + int rand 3 ] },
  0         0  
184              
185 0         0 description => sub { [ "+sentence", 1 + int rand 3 ] },
186              
187 2         16 sentence => sub { [ sub { ucfirst($_) . "." }, "+word", 4 + int rand 6 ] },
  2         8  
188              
189 0         0 paragraph => sub { [ "+sentence", 3 + int rand 4 ] },
190              
191             words => sub {
192 24         29 $Text::Lorem::More::PRUNE = 1;
193 24         74 return [ "+word", $Text::Lorem::More::COUNT, " " ];
194             },
195              
196             sentences => sub {
197 5         9 my $lorem = shift;
198 5         10 $Text::Lorem::More::PRUNE = 1;
199 5         7 my $count = $Text::Lorem::More::COUNT;
200 5         6 my @sentence;
201 5         37 while ($count > 0) {
202 21         65 push @sentence, ucfirst $lorem->words(4 + int rand 6);
203 21         65 $count--;
204             }
205 5         63 return join(". ", @sentence) . ".";
206             },
207              
208             paragraphs => sub {
209 1         3 my $lorem = shift;
210 1         2 $Text::Lorem::More::PRUNE = 1;
211 1         2 my $count = $Text::Lorem::More::COUNT;
212 1         3 my @paragraph;
213 1         4 while ($count > 0) {
214 4         19 push @paragraph, $lorem->sentences(3 + int rand 4);
215 4         15 $count--;
216             }
217 1         7 return join("\n\n", @paragraph);
218             },
219              
220             email => [ map { [ $_ ] } split m/\n/, <<_END_ ],
221             +word\@+hostname
222             +word\@+domainname
223             _END_
224              
225             mail => "email",
226              
227             relativepath => '',
228             absolutepath => '',
229              
230 0         0 path => sub { [ "+word", 1 + int rand 6, "/" ] },
231              
232             httpurl => [ map { [ $_ ] } split m/\n/, <<_END_ ],
233             http://+hostname+path
234             http://+hostname:+port+path
235             _END_
236 0         0 port => sub { int rand(1 + (2 ** 15)) },
237              
238             mailto => \"mailto:+email",
239              
240             tld => "topleveldomain",
241              
242             topleveldomain => [ split m/\s/, <<_END_ ],
243             com org net
244             _END_
245              
246             domain => "domainname",
247              
248             domainname => [ map { [ $_ ] } split m/\n/, <<_END_ ],
249             example.+tld
250             _END_
251              
252             host => "hostname",
253              
254             hostname => [ map { [ $_ ] } split m/\n/, <<_END_ ],
255             +word.+domainname
256             +domainname
257             _END_
258              
259             );
260              
261 8     8   69 use constant MAXIMUM_RECURSION => 2 ** 12;
  8         15  
  8         498  
262 8     8   42 use constant GENERATOR => \%GENERATOR;
  8         15  
  8         5086  
263              
264             =head1 EXPORT
265              
266             =cut
267              
268             our @EXPORT_OK = qw(lorem);
269              
270             =head1 FUNCTIONS
271              
272             =head2 new [$source]
273              
274             Construct a new Text::Lorem::More object
275              
276             =cut
277             sub new {
278 9     9 1 3235 my $self = bless {}, shift;
279 9         26 my $source;
280 9 50       151 if (@_) {
281 0 0       0 if (ref $_[0] eq "HASH") {
    0          
    0          
282 0         0 my $generator = shift;
283 0         0 my $priority = shift;
284 0 0       0 $source = new Text::Lorem::More::Source($source, $priority) if ref $source eq "HASH";
285             }
286             elsif (UNIVERSAL::isa($_[0], "Text::Lorem::More::Source")) {
287 0         0 $source = shift;
288             }
289             elsif (! defined $_[0]) {
290 0         0 $source = new Text::Lorem::More::Source;
291             }
292             }
293             else {
294 9         46 $source = $self->_DEFAULT_SOURCE->copy;
295             }
296 9         48 $self->{source} = $source;
297 9         67 return $self;
298             }
299              
300             =head2 generate $pattern [, $count, $separator]
301              
302             Generate some text using the specified pattern.
303              
304             C is faster than C, as C uses regex to perform substitution.
305              
306             In list context, return a list with C<$count> number of "words"
307              
308             In scalar context, return C<$pattern> repeated C<$count> times and joined by C<$separator>.
309              
310             B
311             B
312              
313             The default for C<$count> is 1.
314              
315             The default for C<$separator> is " ".
316              
317             A pattern will usually contain one or more generator tokens. For example:
318              
319             +name
320             +fullname
321             +word+word+word+word
322              
323             The pattern can also contain other text:
324              
325             +name@+domainname
326             "+firstname +lastname"
327              
328             Sometimes you might need to enclose the token identifier between C<+{> and C<}>
329              
330             For example, the following pattern won't work right:
331              
332             prefix+namesuffix
333              
334             But this will:
335              
336             prefix+{name}suffix
337              
338             If you need to include a '+' in your pattern, you'll have to use C instead.
339              
340             =cut
341             sub generate {
342 294     294 1 365 my $self = shift;
343             # _generate may recurse any number of times.
344             # the RECURSION counter makes sure it doesn't get out of hand.
345 294         349 local $Text::Lorem::More::RECURSION = 0;
346 294         592 return $self->_generate(@_);
347             }
348              
349             =head2 process $text
350              
351             Process a block of text, performing pattern substitutions as they're found.
352              
353             process is slower than generate, as process uses L.
354              
355             To escape '+', simply repeat it. For example, to produce "2 + 2" you would submit:
356              
357             2 ++ 2
358              
359             Please see C for more information.
360              
361             =cut
362             sub process {
363 5     5 1 8 my $self = shift;
364             # _process may recurse any number of times.
365             # the RECURSION counter makes sure it doesn't get out of hand.
366 5         8 local $Text::Lorem::More::RECURSION = 0;
367 5         22 return $self->_process(@_);
368             }
369              
370             =head2 source
371              
372             Return the generator source for this instance.
373              
374             =cut
375             sub source {
376 0     0 1 0 my $self = shift;
377 0         0 return $self->{source};
378             }
379              
380             =head2 lorem
381              
382             A L singleton.
383              
384             =cut
385 272     272 1 138913 sub lorem() { __PACKAGE__->_singleton }
386              
387             sub _DEFAULT_SOURCE {
388 9   66 9   82 return our $SOURCE ||= do {
389 7         177 $SOURCE = new Text::Lorem::More::Source;
390 7         49 $SOURCE->push(GENERATOR);
391 7         59 $SOURCE;
392             }
393             }
394              
395             sub AUTOLOAD {
396 9     9   104 my $self = shift->_self;
397 9         22 my $method = our $AUTOLOAD;
398 9         71 $method =~ s/.*:://;
399              
400 9 50       51 if ($self->{source}->find($method)) {
401 8     8   52 no strict 'refs';
  8         24  
  8         9885  
402 9     292   82 *$AUTOLOAD = sub { my $self = shift; $self->generate("+$method", @_) };
  292         359  
  292         986  
403 9         25 unshift @_, $self;
404 9         48 goto &$AUTOLOAD;
405             }
406             else {
407 0         0 carp "no such generatelet for \"$method\" found";
408 0         0 return "";
409             }
410             }
411              
412             our ($RECURSION, $COUNT, $PRUNE);
413             sub _process {
414 5     5   6 my $self = shift;
415 5         7 my $content = shift;
416 5         5 my $count = shift;
417 5         6 my $separator = shift;
418              
419 5         8 $RECURSION += 1;
420 5 50       13 croak "Too much recursion ($RECURSION) on \"$content\"" if $RECURSION >= MAXIMUM_RECURSION;
421              
422 5 50       13 $count = 1 unless defined $count;
423 5 50       28 croak "\$count ($count) should be a number" unless $count =~ m/^\d+$/;
424              
425 5 50       22 $separator = " " unless defined $separator;
426              
427 5         6 local $Text::Lorem::More::COUNT = $count; $COUNT = $COUNT;
  5         6  
428 5         7 local $Text::Lorem::More::PRUNE = 0;
429              
430 5         6 my @content;
431 5         13 while ($count >= 0) {
432 10         21 my $yield = "";
433 10         83 my $parseryield = $PARSER->content($content);
434 10         23314 for (@$parseryield) {
435 20 100       69 $yield .= ref $_ ? $$_ : $self->_replace_pattern($_);
436             }
437 10 50       23 push @content, $yield if $Text::Lorem::More::PRUNE;
438 10 100 66     119 last if 0 == $count || $Text::Lorem::More::PRUNE;
439 5         9 push @content, $yield;
440 5         18 $count--;
441             }
442              
443 5 50       52 return $content[0] if 1 == @content;
444 0 0       0 return wantarray ? @content : join $separator, @content;
445             }
446              
447             sub _generate {
448 2955     2955   3196 my $self = shift;
449 2955         3198 my $pattern = shift;
450 2955         2698 my $count = shift;
451 2955         2489 my $separator = shift;
452 2955         3009 my $fast = shift;
453              
454 2955         3813 $RECURSION += 1;
455 2955 50       5082 croak "Too much recursion ($RECURSION) on \"$pattern\"" if $RECURSION >= MAXIMUM_RECURSION;
456              
457 2955 100       5662 $count = 1 unless defined $count;
458 2955 50       9452 croak "\$count ($count) should be a number" unless $count =~ m/^\d+$/;
459              
460 2955 100       5050 $separator = " " unless defined $separator;
461              
462 2955         3386 local $Text::Lorem::More::COUNT = $count; $COUNT = $COUNT;
  2955         2812  
463 2955         3055 local $Text::Lorem::More::PRUNE = 0;
464              
465 2955         2909 my @content;
466 2955         5260 while ($count >= 0) {
467 6027         6388 my $pattern = $pattern;
468 6027   66     24460 $pattern =~ s/\+\{(\w+)\}|\+(\w+)/$self->_replace_pattern($1 || $2)/eg;
  7611         27073  
469 6026 100       12387 push @content, $pattern if $Text::Lorem::More::PRUNE;
470 6026 100 66     15805 last if 0 == $count || $Text::Lorem::More::PRUNE;
471 3072         4338 push @content, $pattern;
472 3072         7564 $count--;
473             }
474              
475 2954 100       11775 return $content[0] if 1 == @content;
476 27 50       180 return wantarray ? @content : join $separator, @content;
477             }
478              
479             sub _replace_pattern {
480 7619     7619   7928 my $self = shift;
481 7619         9861 my $pattern = shift;
482              
483 7619         30170 my $generatelet = $self->{source}->find($pattern);
484              
485 7618 50       15712 return $pattern unless $generatelet;
486              
487 7618         6934 my $content;
488 7618 100       17424 if (ref $generatelet eq "ARRAY") {
    50          
    50          
489 7459         14448 $content = $generatelet->[int rand @$generatelet];
490             }
491             elsif (ref $generatelet eq "SCALAR") {
492 0         0 $content = $$generatelet;
493             }
494             elsif (ref $generatelet eq "CODE") {
495 159         301 $content = $generatelet->($self);
496             }
497             else {
498 0         0 croak "Don't know how to run/handle generatelet \"$generatelet\"";
499             }
500              
501 7618 100       13871 if (ref $content eq "ARRAY") {
502 2661         2476 my $filter;
503 2661 100       5340 $filter = shift @$content if ref $content->[0] eq "CODE";
504 2661         4067 my ($pattern, $count, $separator) = @$content;
505 2661         4926 local $_ = $self->_generate($pattern, $count, $separator);
506 2661 100       4687 $_ = $filter->($_) if $filter;
507 2661         5387 $content = $_;
508             }
509              
510 7618         23333 return $content;
511             }
512              
513 9 50   9   39 sub _self($) { return ref $_[0] ? $_[0] : $_[0]->_singleton }
514              
515              
516             sub _singleton {
517 272     272   453 my $class = shift;
518 272   66     1081 return our $singleton ||= $class->new;
519             }
520              
521 0     0     sub DESTROY {
522             }
523              
524             =head1 AUTHOR
525              
526             Robert Krimen, C<< >>
527              
528             =head1 SEE ALSO
529              
530             L
531              
532             L
533              
534             L
535              
536             =head1 SOURCE
537              
538             You can contribute or fork this project via GitHub:
539              
540             L
541              
542             git clone git://github.com/robertkrimen/text-lorem-more.git Text-Lorem-More
543              
544             =head1 ACKNOWLEDGEMENTS
545              
546             Thanks to Adeola Awoyemi for writing L
547              
548             =head1 COPYRIGHT & LICENSE
549              
550             Copyright 2006 Robert Krimen, all rights reserved.
551              
552             This program is free software; you can redistribute it and/or modify it
553             under the same terms as Perl itself.
554              
555             =head1 BUGS
556              
557             Probaby a lot. Please report them (as below) and I'll take a look.
558              
559             Please report any bugs or feature requests to
560             C, or through the web interface at
561             L.
562             I will be notified, and then you'll automatically be notified of progress on
563             your bug as I make changes.
564              
565             =head1 SUPPORT
566              
567             You can find documentation for this module with the perldoc command.
568              
569             perldoc Text::Lorem::More
570              
571             You can also look for information at:
572              
573             =over 4
574              
575             =item * AnnoCPAN: Annotated CPAN documentation
576              
577             L
578              
579             =item * CPAN Ratings
580              
581             L
582              
583             =item * RT: CPAN's request tracker
584              
585             L
586              
587             =item * Search CPAN
588              
589             L
590              
591             =back
592              
593             =cut
594              
595             1; # End of Text::Lorem::More