File Coverage

blib/lib/Rubric/Entry.pm
Criterion Covered Total %
statement 62 62 100.0
branch 14 14 100.0
condition 4 6 66.6
subroutine 20 20 100.0
pod 8 9 88.8
total 108 111 97.3


line stmt bran cond sub pod time code
1 12     12   17574 use strict;
  12         24  
  12         461  
2 12     12   58 use warnings;
  12         18  
  12         657  
3             package Rubric::Entry;
4             # ABSTRACT: a single entry made by a user
5             $Rubric::Entry::VERSION = '0.155';
6 12     12   57 use parent qw(Rubric::DBI);
  12         19  
  12         81  
7              
8 12     12   12728 use Class::DBI::utf8;
  12         164568  
  12         61  
9              
10             # =head1 DESCRIPTION
11             #
12             # This class provides an interface to Rubric entries. It inherits from
13             # Rubric::DBI, which is a Class::DBI class.
14             #
15             # =cut
16              
17 12     12   1875 use Encode 2 qw(_utf8_on);
  12         233  
  12         502  
18 12     12   7263 use Rubric::Entry::Formatter;
  12         29  
  12         319  
19 12     12   9310 use String::TagString;
  12         315416  
  12         568  
20 12     12   10385 use Time::Piece;
  12         112674  
  12         82  
21              
22             __PACKAGE__->table('entries');
23              
24             # =head1 COLUMNS
25             #
26             # id - a unique identifier
27             # link - the link to which the entry refers
28             # username - the user who made the entry
29             # title - the title of the link's destination
30             # description - a short description of the entry
31             # body - a long body of text for the entry
32             # created - the time when the entry was first created
33             # modified - the time when the entry was last modified
34             #
35             # =cut
36              
37             __PACKAGE__->columns(
38             All => qw(id link username title description body created modified)
39             );
40              
41             __PACKAGE__->utf8_columns(qw( title description body ));
42              
43             # =head1 RELATIONSHIPS
44             #
45             # =head2 link
46             #
47             # The link attribute returns a Rubric::Link.
48             #
49             # =cut
50              
51             __PACKAGE__->has_a(link => 'Rubric::Link');
52              
53             # =head2 uri
54             #
55             # The uri attribute returns the URI of the entry's link.
56             #
57             # =cut
58              
59 4 100   4 1 2328 sub uri { my ($self) = @_; return unless $self->link; $self->link->uri; }
  4         20  
  2         273  
60              
61             # =head2 username
62             #
63             # The user attribute returns a Rubric::User.
64             #
65             # =cut
66              
67             __PACKAGE__->has_a(username => 'Rubric::User');
68              
69             # =head2 tags
70             #
71             # Every entry has_many tags that describe it. The C method will return the
72             # tags, and the C method will return the Rubric::EntryTag objects that
73             # represent them.
74             #
75             # =cut
76              
77             __PACKAGE__->has_many(entrytags => 'Rubric::EntryTag');
78             __PACKAGE__->has_many(tags => [ 'Rubric::EntryTag' => 'tag' ]);
79              
80             # =head3 recent_tags_counted
81             #
82             # This method returns a reference to an array of arrayrefs, each a (tag, count)
83             # pair for tags used on the week's 50 most recent entries.
84             #
85             # =cut
86              
87             __PACKAGE__->set_sql(recent_tags_counted => <<'');
88             SELECT tag, COUNT(*) as count
89             FROM entrytags
90             WHERE entry IN (SELECT id FROM entries WHERE created > ? LIMIT 100)
91             AND tag NOT LIKE '@%%'
92             AND entry NOT IN (SELECT entry FROM entrytags WHERE tag = '@private')
93             GROUP BY tag
94             ORDER BY count DESC
95             LIMIT 50
96              
97             sub recent_tags_counted {
98 11     11 1 1821 my ($class) = @_;
99 11         166 my $sth = $class->sql_recent_tags_counted;
100 11         28441 $sth->execute(time - (86400 * 7));
101 11         3872 my $result = $sth->fetchall_arrayref;
102 11         67 return $result;
103             }
104              
105             # =head1 INFLATIONS
106             #
107             # =head2 created
108             #
109             # =head2 modified
110             #
111             # The created and modified columns are stored as seconds since epoch, but
112             # inflated to Time::Piece objects.
113             #
114             # =cut
115              
116             __PACKAGE__->has_a(
117             $_ => 'Time::Piece',
118             deflate => 'epoch',
119             inflate => Rubric::Config->display_localtime ? sub { localtime($_[0]) }
120             : sub { gmtime($_[0]) }
121             ) for qw(created modified);
122              
123             __PACKAGE__->add_trigger(before_create => \&_default_title);
124              
125             __PACKAGE__->add_trigger(before_create => \&_create_times);
126             __PACKAGE__->add_trigger(before_update => \&_update_times);
127              
128             sub _default_title {
129 5     5   15808 my $self = shift;
130 5 100       52 $self->title('(default)') unless $self->{title}
131             }
132              
133             sub _create_times {
134 5     5   1340 my $self = shift;
135 5 100       45 $self->created(scalar gmtime) unless defined $self->{created};
136 5 100       1725 $self->modified(scalar gmtime) unless defined $self->{modified};
137             }
138              
139             sub _update_times {
140 12     12   2546 my $self = shift;
141 12         82 $self->modified(scalar gmtime);
142             }
143              
144             # =head1 METHODS
145             #
146             # =head2 query(\%arg)
147             #
148             # The arguments to C provide a set of abstract constraints for the query.
149             # These are sent to Rubric::Entry::Query, which builds an SQL query and returns
150             # the result of running it. (Either a list or an Iterator is returned.)
151             #
152             # (The built-in Class::DBI search method can't handle this kind of search.)
153             #
154             # user - entries for this User
155             # tags - entries with these tags (arrayref)
156             # link - entries for this Link
157             # urimd5 - entries for the Link with this md5 sum
158             # has_body - whether entries must have bodies (T, F, or undef)
159             # has_link - whether entries must have a link (T, F, or undef)
160             # (time spec) - {created,modified}_{before,after,on}
161             # limits entries by time; given as a complete or partial
162             # time and date string in the form "YYYY-MM-DD HH:MM"
163             #
164             # =cut
165              
166             sub query {
167 47     47 1 55193 my $self = shift;
168 47         1859 require Rubric::Entry::Query;
169 47         454 Rubric::Entry::Query->query(@_);
170             }
171              
172             # =head2 set_new_tags(\%tags)
173             #
174             # This method replaces all entry's current tags with the new set of tags.
175             #
176             # =cut
177              
178             sub set_new_tags {
179 6     6 1 5280 my ($self, $tags) = @_;
180 6         61 $self->entrytags->delete_all;
181 6         13802 $self->update;
182              
183 6         563933 while (my ($tag, $value) = each %$tags) {
184 6         153607 $self->add_to_tags({ tag => $tag, tag_value => $value });
185             }
186             }
187              
188             # =head2 tags_from_string
189             #
190             # my $tags = Rubric::Entry->tags_from_string($string);
191             #
192             # This (class) method takes a string of tags, delimited by whitespace, and
193             # returns an array of the tags, throwing an exception if it finds invalid tags.
194             #
195             # Valid tags (shouldn't this be documented somewhere else instead?) may contain
196             # letters, numbers, underscores, colons, dots, and asterisks. Hyphens me be
197             # used, but not as the first character.
198             #
199             # =cut
200              
201             sub tags_from_string {
202 28     28 1 2426252 my ($class, $tagstring) = @_;
203              
204 28 100 66     291 return {} unless $tagstring and $tagstring =~ /\S/;
205              
206 22         213 String::TagString->tags_from_string($tagstring);
207             }
208              
209             # =head2 C< markup >
210             #
211             # This method returns the value of the entry's @markup tag, or C<_default> if
212             # there is no such tag.
213             #
214             # =cut
215              
216             sub markup {
217 20     20 1 42 my ($self) = @_;
218              
219 20         84 my ($tag)
220             = Rubric::EntryTag->search({ entry => $self->id, tag => '@markup' });
221              
222 20 100 66     17230 return ($tag and $tag->tag_value) ? $tag->tag_value : '_default';
223             }
224              
225              
226             # =head2 C< body_as >
227             #
228             # my $formatted_body = $entry->body_as("html");
229             #
230             # This method returns the body of the entry, formatted into the given format. If
231             # the entry cannot be rendered into the given format, an exception is thrown.
232             #
233             # =cut
234              
235             sub body_as {
236 18     18 1 25031 my ($self, $format) = @_;
237              
238 18         67 my $markup = $self->markup;
239              
240 18         2534 Rubric::Entry::Formatter->format({
241             text => $self->body,
242             markup => $markup,
243             format => $format
244             });
245             }
246              
247             sub accessor_name_for {
248 96     96 1 19282 my ($class, $field) = @_;
249              
250 96 100       266 return 'user' if $field eq 'username';
251              
252 84         767 return $field;
253             }
254              
255             ## return retrieve_all'd objects in recent-to-older order
256              
257             __PACKAGE__->set_sql(RetrieveAll => <<'');
258             SELECT __ESSENTIAL__
259             FROM __TABLE__
260             ORDER BY created DESC
261              
262             sub tagstring {
263 1     1 0 31 my ($self) = @_;
264             String::TagString->string_from_tags({
265 1         8 map {; $_->tag => $_->tag_value } $self->entrytags
  1         1034  
266             });
267             }
268              
269             1;
270              
271             __END__