File Coverage

blib/lib/JMAP/Tester/Role/SentenceCollection.pm
Criterion Covered Total %
statement 132 143 92.3
branch 22 32 68.7
condition 6 6 100.0
subroutine 20 21 95.2
pod 13 14 92.8
total 193 216 89.3


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