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