File Coverage

blib/lib/JMAP/Tester/Role/SentenceCollection.pm
Criterion Covered Total %
statement 94 102 92.1
branch 22 32 68.7
condition 6 6 100.0
subroutine 19 20 95.0
pod 13 14 92.8
total 154 174 88.5


line stmt bran cond sub pod time code
1 1     1   563 use v5.14.0;
  1         4  
2             package JMAP::Tester::Role::SentenceCollection 0.103;
3              
4 1     1   7 use Moo::Role;
  1         2  
  1         6  
5              
6             requires 'sentence_broker';
7              
8             BEGIN {
9 1     1   4 for my $m (qw(
10             client_ids_for_items
11             sentence_for_item
12             paragraph_for_items
13              
14             strip_json_types
15              
16             abort
17             )) {
18             my $sub = sub {
19 100     100   271 my $self = shift;
20 100         2110 $self->sentence_broker->$m(@_);
21 5         23 };
22 1     1   430 no strict 'refs';
  1         10  
  1         43  
23 5         1380 *$m = $sub;
24             }
25             }
26              
27             requires 'items';
28             requires 'add_items';
29              
30             after add_items => sub { $_[0]->_index_setup };
31              
32             sub BUILD {
33 14     14 0 220 $_[0]->_index_setup;
34             }
35              
36             sub _index_setup {
37 14     14   36 my ($self) = @_;
38              
39 14         58 my @cids = $self->client_ids_for_items([ $self->items ]);
40              
41 14         41 my $prev_cid;
42 14         28 my $next_para_idx = 0;
43              
44 14         43 my %cid_indices;
45             my @para_indices;
46              
47 14         56 for my $i (0 .. $#cids) {
48 20         35 my $cid = $cids[$i];
49 20 50       74 unless (defined $cid) {
50 0         0 Carp::cluck("undefined client_id in position $i");
51 0         0 next;
52             }
53              
54 20 100 100     81 if (defined $prev_cid && $prev_cid ne $cid) {
55             # We're transition from cid1 to cid2. -- rjbs, 2016-04-08
56             $self->abort("client_id <$cid> appears in non-contiguous positions")
57 3 50       42 if $cid_indices{$cid};
58              
59 3         25 $next_para_idx++;
60             }
61              
62 20         81 push @{ $cid_indices{$cid} }, $i;
  20         141  
63 20         135 push @{ $para_indices[ $next_para_idx ] }, $i;
  20         53  
64              
65 20         53 $prev_cid = $cid;
66             }
67              
68 14         68 $self->_cid_indices(\%cid_indices);
69 14         90 $self->_para_indices(\@para_indices);
70             }
71              
72             # The reason we don't have cid-to-para and para-to-lines is that in the event
73             # that one cid appears in non-contiguous positions, we want to allow it, even
74             # though it's garbage. -- rjbs, 2016-04-11
75             has cid_indices => (is => 'bare', accessor => '_cid_indices');
76             has para_indices => (is => 'bare', accessor => '_para_indices');
77              
78             #pod =method sentence
79             #pod
80             #pod my $sentence = $response->sentence($n);
81             #pod
82             #pod This method returns the Ith L of
83             #pod the response.
84             #pod
85             #pod =cut
86              
87             sub sentence {
88 15     15 1 35729 my ($self, $n) = @_;
89              
90 15         53 my @items = $self->items;
91 15 100       59 $self->abort("there is no sentence for index $n")
92             unless my $item = $items[$n];
93              
94 14         40 return $self->sentence_for_item($item);
95             }
96              
97             #pod =method sentences
98             #pod
99             #pod my @sentences = $response->sentences;
100             #pod
101             #pod This method returns a list of all sentences in the response.
102             #pod
103             #pod =cut
104              
105             sub sentences {
106 4     4 1 4486 my ($self) = @_;
107              
108 4         17 my @sentences = map {; $self->sentence_for_item($_) }
  17         336  
109             $self->items;
110              
111 4         104 return @sentences;
112             }
113              
114             #pod =method single_sentence
115             #pod
116             #pod my $sentence = $response->single_sentence;
117             #pod my $sentence = $response->single_sentence($name);
118             #pod
119             #pod This method returns the only L of
120             #pod the response, raising an exception if there's more than one Sentence. If
121             #pod C<$name> is given, an exception is raised if the Sentence's name doesn't match
122             #pod the given name.
123             #pod
124             #pod =cut
125              
126             sub single_sentence {
127 8     8 1 5065 my ($self, $name) = @_;
128              
129 8         31 my @items = $self->items;
130 8 100       33 unless (@items == 1) {
131 1         9 $self->abort(
132             sprintf("single_sentence called but there are %i sentences", 0+@items)
133             );
134             }
135              
136 7         26 my $sentence = $self->sentence_for_item($items[0]);
137              
138 7         257 my $have = $sentence->name;
139 7 100 100     53 if (defined $name && $have ne $name) {
140 2         14 $self->abort(qq{single sentence has name "$have" not "$name"});
141             }
142              
143 5         32 return $sentence;
144             }
145              
146             #pod =method sentence_named
147             #pod
148             #pod my $sentence = $response->sentence_named($name);
149             #pod
150             #pod This method returns the sentence with the given name. If no such sentence
151             #pod exists, or if two sentences with the name exist, the tester will abort.
152             #pod
153             #pod =cut
154              
155             sub sentence_named {
156 3     3 1 949 my ($self, $name) = @_;
157              
158 3 50       12 Carp::confess("no name given") unless defined $name;
159              
160 3         12 my @sentences = grep {; $_->name eq $name } $self->sentences;
  12         82  
161              
162 3 100       34 unless (@sentences) {
163 1         6 $self->abort(qq{no sentence found with name "$name"});
164             }
165              
166 2 100       10 if (@sentences > 1) {
167 1         6 $self->abort(qq{found more than one sentence with name "$name"});
168             }
169              
170 1         7 return $sentences[0];
171             }
172              
173             #pod =method assert_n_sentences
174             #pod
175             #pod my ($s1, $s2, ...) = $response->assert_n_sentences($n);
176             #pod
177             #pod This method returns all the sentences in the response, as long as there are
178             #pod exactly C<$n>. Otherwise, it aborts.
179             #pod
180             #pod =cut
181              
182             sub assert_n_sentences {
183 0     0 1 0 my ($self, $n) = @_;
184              
185 0 0       0 Carp::confess("no sentence count given") unless defined $n;
186              
187 0         0 my @sentences = $self->sentences;
188              
189 0 0       0 unless (@sentences == $n) {
190 0         0 $self->abort("expected $n sentences but got " . @sentences)
191             }
192              
193 0         0 return @sentences;
194             }
195              
196             #pod =method paragraph
197             #pod
198             #pod my $para = $response->paragraph($n);
199             #pod
200             #pod This method returns the Ith L
201             #pod of the response.
202             #pod
203             #pod =cut
204              
205             sub paragraph {
206 7     7 1 23716 my ($self, $n) = @_;
207              
208 7 100       54 $self->abort("there is no paragraph for index $n")
209             unless my $indices = $self->_para_indices->[$n];
210              
211 6         25 my @items = $self->items;
212 6         22 my @selected = @items[ @$indices ];
213              
214 6         26 $self->paragraph_for_items(\@selected);
215             }
216              
217             #pod =method paragraphs
218             #pod
219             #pod my @paragraphs = $response->paragraphs;
220             #pod
221             #pod This method returns a list of all paragraphs in the response.
222             #pod
223             #pod =cut
224              
225             sub paragraphs {
226 2     2 1 7 my ($self) = @_;
227              
228 2         5 my @para_indices = @{ $self->_para_indices };
  2         12  
229 2         9 my @items = $self->items;
230              
231 2         5 my @paragraphs;
232 2         6 for my $i_set (@para_indices) {
233 6         86 push @paragraphs, $self->paragraph_for_items(
234             [ @items[ @$i_set ] ]
235             );
236             }
237              
238 2         42 return @paragraphs;
239             }
240              
241             #pod =method assert_n_paragraphs
242             #pod
243             #pod my ($p1, $p2, ...) = $response->assert_n_paragraphs($n);
244             #pod
245             #pod This method returns all the paragraphs in the response, as long as there are
246             #pod exactly C<$n>. Otherwise, it aborts.
247             #pod
248             #pod =cut
249              
250             sub assert_n_paragraphs {
251 2     2 1 4323 my ($self, $n) = @_;
252              
253 2 50       10 Carp::confess("no paragraph count given") unless defined $n;
254              
255 2         6 my @para_indices = @{ $self->_para_indices };
  2         14  
256 2 100       9 unless ($n == @para_indices) {
257 1         6 $self->abort("expected $n paragraphs but got " . @para_indices)
258             }
259              
260 1         4 return $self->paragraphs;
261             }
262              
263             #pod =method paragraph_by_client_id
264             #pod
265             #pod my $para = $response->paragraph_by_client_id($cid);
266             #pod
267             #pod This returns the paragraph for the given client id. If there is no paragraph
268             #pod for that client id, an empty list is returned.
269             #pod
270             #pod =cut
271              
272             sub paragraph_by_client_id {
273 2     2 1 4772 my ($self, $cid) = @_;
274              
275 2 50       7 Carp::confess("no client id given") unless defined $cid;
276              
277             $self->abort("there is no paragraph for client_id $cid")
278 2 50       13 unless my $indices = $self->_cid_indices->{$cid};
279              
280 2         9 my @items = $self->items;
281 2         6 my @selected = @items[ @$indices ];
282              
283 2         7 return $self->paragraph_for_items(\@selected);
284             }
285              
286             #pod =method as_triples
287             #pod
288             #pod =method as_stripped_triples
289             #pod
290             #pod This method returns an arrayref of arrayrefs, holding the data returned by the
291             #pod JMAP server. With C, some of the JSON data may be in objects
292             #pod provided by L. If you'd prefer raw data, use the
293             #pod C form.
294             #pod
295             #pod =cut
296              
297             sub as_triples {
298 2     2 1 966 my ($self) = @_;
299              
300             return [
301 2         8 map {; $self->sentence_for_item($_)->as_triple }
  10         28  
302             $self->items
303             ];
304             }
305              
306             sub as_stripped_triples {
307 1     1 1 4168 my ($self) = @_;
308              
309 1         7 return $self->strip_json_types($self->as_triples);
310             }
311              
312             #pod =method as_pairs
313             #pod
314             #pod =method as_stripped_pairs
315             #pod
316             #pod These methods do the same thing as C and ,
317             #pod but omit client ids.
318             #pod
319             #pod =cut
320              
321             sub as_pairs {
322 6     6 1 19419 my ($self) = @_;
323              
324             return [
325 6         24 map {; $self->sentence_for_item($_)->as_pair }
  14         45  
326             $self->items
327             ];
328             }
329              
330             sub as_stripped_pairs {
331 1     1 1 2788 my ($self) = @_;
332              
333 1         5 return $self->strip_json_types($self->as_pairs);
334             }
335              
336             1;
337              
338             __END__