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.101;
2              
3 1     1   823 use Moo::Role;
  1         3  
  1         8  
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   249 my $self = shift;
19 96         2392 $self->sentence_broker->$m(@_);
20 5         19 };
21 1     1   506 no strict 'refs';
  1         3  
  1         40  
22 5         1555 *$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 214 $_[0]->_index_setup;
33             }
34              
35             sub _index_setup {
36 12     12   36 my ($self) = @_;
37              
38 12         55 my @cids = $self->client_ids_for_items([ $self->items ]);
39              
40 12         41 my $prev_cid;
41 12         26 my $next_para_idx = 0;
42              
43 12         31 my %cid_indices;
44             my @para_indices;
45              
46 12         75 for my $i (0 .. $#cids) {
47 18         44 my $cid = $cids[$i];
48 18 50       74 unless (defined $cid) {
49 0         0 Carp::cluck("undefined client_id in position $i");
50 0         0 next;
51             }
52              
53 18 100 100     82 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       42 if $cid_indices{$cid};
57              
58 3         26 $next_para_idx++;
59             }
60              
61 18         73 push @{ $cid_indices{$cid} }, $i;
  18         149  
62 18         122 push @{ $para_indices[ $next_para_idx ] }, $i;
  18         54  
63              
64 18         62 $prev_cid = $cid;
65             }
66              
67 12         72 $self->_cid_indices(\%cid_indices);
68 12         93 $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 38129 my ($self, $n) = @_;
88              
89 15         77 my @items = $self->items;
90 15 100       73 $self->abort("there is no sentence for index $n")
91             unless my $item = $items[$n];
92              
93 14         47 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 4989 my ($self) = @_;
106              
107 4         21 my @sentences = map {; $self->sentence_for_item($_) }
  17         352  
108             $self->items;
109              
110 4         103 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 5460 my ($self, $name) = @_;
127              
128 8         35 my @items = $self->items;
129 8 100       40 unless (@items == 1) {
130 1         10 $self->abort(
131             sprintf("single_sentence called but there are %i sentences", 0+@items)
132             );
133             }
134              
135 7         32 my $sentence = $self->sentence_for_item($items[0]);
136              
137 7         276 my $have = $sentence->name;
138 7 100 100     121 if (defined $name && $have ne $name) {
139 2         14 $self->abort(qq{single sentence has name "$have" not "$name"});
140             }
141              
142 5         41 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 1133 my ($self, $name) = @_;
156              
157 3 50       13 Carp::confess("no name given") unless defined $name;
158              
159 3         12 my @sentences = grep {; $_->name eq $name } $self->sentences;
  12         88  
160              
161 3 100       44 unless (@sentences) {
162 1         7 $self->abort(qq{no sentence found with name "$name"});
163             }
164              
165 2 100       9 if (@sentences > 1) {
166 1         7 $self->abort(qq{found more than one sentence with name "$name"});
167             }
168              
169 1         8 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 26595 my ($self, $n) = @_;
206              
207 7 100       147 $self->abort("there is no paragraph for index $n")
208             unless my $indices = $self->_para_indices->[$n];
209              
210 6         53 my @items = $self->items;
211 6         28 my @selected = @items[ @$indices ];
212              
213 6         29 $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 7 my ($self) = @_;
226              
227 2         5 my @para_indices = @{ $self->_para_indices };
  2         12  
228 2         9 my @items = $self->items;
229              
230 2         6 my @paragraphs;
231 2         7 for my $i_set (@para_indices) {
232 6         87 push @paragraphs, $self->paragraph_for_items(
233             [ @items[ @$i_set ] ]
234             );
235             }
236              
237 2         43 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 4987 my ($self, $n) = @_;
251              
252 2 50       11 Carp::confess("no paragraph count given") unless defined $n;
253              
254 2         5 my @para_indices = @{ $self->_para_indices };
  2         24  
255 2 100       12 unless ($n == @para_indices) {
256 1         9 $self->abort("expected $n paragraphs but got " . @para_indices)
257             }
258              
259 1         5 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 4898 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       13 unless my $indices = $self->_cid_indices->{$cid};
278              
279 2         10 my @items = $self->items;
280 2         6 my @selected = @items[ @$indices ];
281              
282 2         8 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 1128 my ($self) = @_;
298              
299             return [
300 2         15 map {; $self->sentence_for_item($_)->as_triple }
  10         27  
301             $self->items
302             ];
303             }
304              
305             sub as_stripped_triples {
306 1     1 1 4516 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 21139 my ($self) = @_;
322              
323             return [
324 4         23 map {; $self->sentence_for_item($_)->as_pair }
  12         37  
325             $self->items
326             ];
327             }
328              
329             sub as_stripped_pairs {
330 1     1 1 3311 my ($self) = @_;
331              
332 1         7 return $self->strip_json_types($self->as_pairs);
333             }
334              
335             1;
336              
337             __END__