File Coverage

blib/lib/Data/Taxonomy/Tags.pm
Criterion Covered Total %
statement 82 82 100.0
branch 13 16 81.2
condition 3 6 50.0
subroutine 21 21 100.0
pod 8 8 100.0
total 127 133 95.4


line stmt bran cond sub pod time code
1             package Data::Taxonomy::Tags;
2              
3 4     4   105577 use strict;
  4         9  
  4         161  
4 4     4   20 use warnings;
  4         10  
  4         137  
5              
6 4     4   21 use vars qw($VERSION $ERROR);
  4         13  
  4         575  
7             $VERSION = '0.05';
8              
9             use overload
10 3     3   408 '""' => sub { shift->as_string },
11 4     4   20412 fallback => 1;
  4         4605  
  4         45  
12              
13             # Constants for separator and category
14 4     4   300 use constant SPLIT => 0;
  4         9  
  4         359  
15 4     4   18 use constant JOIN => 1;
  4         8  
  4         153  
16              
17 4     4   2413 use Data::Taxonomy::Tags::Tag;
  4         9  
  4         1925  
18              
19             =head1 NAME
20              
21             Data::Taxonomy::Tags - Represents a set of tags for any item
22              
23             =head1 SYNOPSIS
24              
25             use Data::Taxonomy::Tags;
26            
27             my $tags = Data::Taxonomy::Tags->new('perl tags cpan module system:meta');
28            
29             print $_, "\n" for $tags->tags;
30            
31             print $_, "\n" for $tags->categories;
32              
33             =head1 DESCRIPTION
34              
35             Data::Taxonomy::Tags will basically take care of managing tags for an
36             item easier. You provide it with a string of tags and it'll allow you
37             to call methods to get all the tags and categories as well as add and
38             delete tags from the list.
39              
40             =head2 Methods
41              
42             =over 12
43              
44             =item new($string[,\%options])
45              
46             The first argument is a string of tags. This string is stripped of any
47             leading and trailing whitespace. The second argument, which is optional,
48             is a hashref of options.
49              
50             Returns a Data::Taxonomy::Tags object;
51              
52             =over 24
53              
54             =item C<< separator => ['\s+', ' '] >>
55              
56             Specifies the regex pattern (or compiled regex) which will be used to
57             C the tags apart and the character(s) used between tags when
58             converting the object back to a string. Make sure to escape any
59             special characters in the regex pattern.
60              
61             If the value is not an arrayref, then the same value is used for both
62             operations (and is escaped for the regex).
63              
64             Defaults to C<['\s+', ' ']>.
65              
66             =item C<< category => [':', ':'] >>
67              
68             Specifies the regex pattern (or compiled regex) which will be used to
69             C the tag name from it's optional category and the character(s)
70             used between the category and tag when converting to a string. Make
71             sure to escape any special characters in the regex pattern.
72              
73             If the value is not an arrayref, then the same value is used for both
74             operations (and is escaped for the regex).
75              
76             Defaults to C<[':', ':']>.
77              
78             =back
79              
80             =cut
81             sub new {
82 23     23 1 101 my ($class, $tags, $opt) = @_;
83            
84 23         146 my $self = bless {
85             _input => $tags,
86             separator => ['\s+', ' '],
87             category => [':', ':'],
88             }, $class;
89            
90 23 100       73 if (defined $opt) {
91 6         13 for (qw(separator category)) {
92 12 100       30 if (defined $opt->{$_}) {
93 7 50 33     104 $self->{$_} = ref $opt->{$_} eq 'ARRAY' && @{$opt->{$_}} == 2
94             ? $opt->{$_}
95             : [qr/\Q$opt->{$_}\E/, $opt->{$_}];
96             }
97             }
98             }
99            
100 23         56 $self->add_to_tags($tags);
101            
102 23         69 return $self;
103             }
104              
105             =item tags
106              
107             Returns an array or arrayref (depending on context) of L
108             objects.
109              
110             =cut
111             sub tags {
112 30         132 return wantarray && defined $_[0]->{tags}
113 59 100 66 59 1 351 ? @{$_[0]->{tags}}
114             : $_[0]->{tags};
115             }
116              
117             =item add_to_tags($tags)
118              
119             Processes the string and adds the tag(s) to the object.
120              
121             =cut
122             sub add_to_tags {
123 26     26 1 59 my ($self, $input) = @_;
124 26         223 my @tags = split /$self->{separator}[SPLIT]/, $self->_cleanup($input);
125            
126             $_ = Data::Taxonomy::Tags::Tag->new($_, { separator => $self->{category} })
127 26         164 for @tags;
128            
129 26         49 @tags = @{$self->_remove_from_tagset($self->as_string, \@tags)};
  26         61  
130            
131 26         50 push @{$self->{tags}}, @tags;
  26         85  
132             }
133              
134             =item remove_from_tags($tags)
135              
136             Processes the string and removes the tag(s) from the object.
137              
138             =cut
139             sub remove_from_tags {
140 2     2 1 19 my ($self, $input) = @_;
141 2         6 $self->{tags} = $self->_remove_from_tagset($input, [$self->tags]);
142             }
143              
144             sub _remove_from_tagset {
145 28     28   37 my ($self, $input, $tagset) = @_;
146            
147 28         93 my %tags = map { $_ => 1 }
  9         26  
148             split /$self->{separator}[SPLIT]/, $self->_cleanup($input);
149            
150 28         55 my @result = grep { !$tags{$_} } @$tagset;
  81         191  
151 28         94 return \@result;
152             }
153              
154             =item remove_category($category)
155              
156             Removes all tags with the specified category.
157              
158             =cut
159             sub remove_category {
160 2     2 1 15 my ($self, $category) = @_;
161            
162             {
163 4     4   23 no warnings 'uninitialized';
  4         7  
  4         785  
  2         2  
164 2         4 @{$self->{tags}} = grep { $_->category ne $category } $self->tags;
  2         7  
  8         17  
165             }
166             }
167              
168             =item categories
169              
170             Returns an array or arrayref (depending on context) of the unique categories.
171              
172             =cut
173             sub categories {
174 3     3 1 5 my $self = shift;
175              
176 3         12 my %seen;
177 6 100       22 my @cats = grep { defined $_ && !$seen{$_}++ }
  6         14  
178 3         6 map { $_->category }
179             $self->tags;
180              
181 3 50       20 return wantarray ? @cats : \@cats;
182             }
183              
184             =item tags_with_category($category)
185              
186             Returns an array or arrayref (depending on context) of the tags with the
187             specified category
188              
189             =cut
190             sub tags_with_category {
191 4     4 1 4 my ($self, $category) = @_;
192            
193 4         6 my @tags;
194             {
195 4     4   20 no warnings 'uninitialized';
  4         5  
  4         650  
  4         5  
196              
197 6         18 @tags = map { $_->[1]->name }
  16         35  
198 16         37 grep { $_->[0] eq $category }
199 4         22 map { [$_->category, $_] }
200             $self->tags;
201             }
202              
203 4 50       36 return wantarray ? @tags : \@tags;
204             }
205              
206             =item as_string
207              
208             Returns the tag list as a string (that is, what was given to the constructor).
209             Overloading is used as well to automatically call this method if the object
210             is used in a string context.
211              
212             =cut
213             sub as_string {
214 29     29 1 32 my $self = shift;
215            
216 29 100       48 return defined $self->tags
217             ? join $self->{separator}[JOIN], $self->tags
218             : undef;
219             }
220              
221             sub _cleanup {
222 54     54   73 my ($self, $str) = @_;
223             {
224 4     4   20 no warnings 'uninitialized';
  4         11  
  4         412  
  54         57  
225 54         240 $str =~ s/^\s*//g;
226 54         260 $str =~ s/\s*$//g;
227             }
228 54         180 return $str;
229             }
230              
231             =back
232              
233             =head1 BUGS
234              
235             All bugs, open and resolved, are handled by RT at
236             L.
237              
238             Please report all bugs via
239             L.
240              
241             =head1 LICENSE
242              
243             Copyright 2005, Thomas R. Sibley.
244              
245             You may use, modify, and distribute this package under the same terms as Perl itself.
246              
247             =head1 AUTHOR
248              
249             Thomas R. Sibley, L
250              
251             =cut
252              
253             42;