File Coverage

blib/lib/JMAP/Tester/Role/SentenceCollection.pm
Criterion Covered Total %
statement 92 100 92.0
branch 22 32 68.7
condition 6 6 100.0
subroutine 18 19 94.7
pod 13 14 92.8
total 151 171 88.3


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