File Coverage

blib/lib/BusyBird/StatusStorage/Common.pm
Criterion Covered Total %
statement 119 131 90.8
branch 44 48 91.6
condition 8 11 72.7
subroutine 21 24 87.5
pod 3 3 100.0
total 195 217 89.8


line stmt bran cond sub pod time code
1             package BusyBird::StatusStorage::Common;
2 2     2   9 use strict;
  2         4  
  2         72  
3 2     2   10 use warnings;
  2         3  
  2         55  
4 2     2   10 use Carp;
  2         3  
  2         139  
5 2     2   35 use Exporter qw(import);
  2         3  
  2         93  
6 2     2   10 use BusyBird::Util qw(future_of);
  2         2  
  2         96  
7 2     2   11 use BusyBird::DateTime::Format;
  2         3  
  2         57  
8 2     2   10 use DateTime;
  2         2  
  2         46  
9 2     2   9 use Try::Tiny;
  2         8  
  2         116  
10 2     2   9 use Future::Q;
  2         3  
  2         2168  
11              
12             our @EXPORT_OK = qw(contains ack_statuses get_unacked_counts);
13              
14             sub ack_statuses {
15 32     32 1 119 my ($self, %args) = @_;
16 32 100       292 croak 'timeline arg is mandatory' if not defined $args{timeline};
17 31         58 my $ids;
18 31 100       103 if(defined($args{ids})) {
19 16 100       86 if(!ref($args{ids})) {
    50          
20 6         17 $ids = [$args{ids}];
21             }elsif(ref($args{ids}) eq 'ARRAY') {
22 10         21 $ids = $args{ids};
23 10 100       31 croak "ids arg array must not contain undef" if grep { !defined($_) } @$ids;
  33         162  
24             }else {
25 0         0 croak "ids arg must be either undef, status ID or array-ref of IDs";
26             }
27             }
28 30         58 my $max_id = $args{max_id};
29 30         58 my $timeline = $args{timeline};
30 30   50 0   123 my $callback = $args{callback} || sub {};
  0         0  
31 30         186 my $ack_str = BusyBird::DateTime::Format->format_datetime(
32             DateTime->now(time_zone => 'UTC')
33             );
34 30         17573 my @subfutures = (_get_unacked_statuses_by_ids_future($self, $timeline, $ids));
35 30 100 100     3859 if(!defined($ids) || defined($max_id)) {
36 21         748 push @subfutures, future_of(
37             $self, 'get_statuses',
38             timeline => $timeline,
39             max_id => $max_id, count => 'all',
40             ack_state => 'unacked'
41             );
42             }
43             Future::Q->needs_all(@subfutures)->then(sub {
44 30     30   6646 my @statuses_list = @_;
45 30         88 my @target_statuses = _uniq_statuses(map { @$_ } @statuses_list);
  51         158  
46 30 100       99 if(!@target_statuses) {
47 6         27 return 0;
48             }
49 24         211 $_->{busybird}{acked_at} = $ack_str foreach @target_statuses;
50 24         105 return future_of(
51             $self, 'put_statuses',
52             timeline => $timeline, mode => 'update',
53             statuses => \@target_statuses,
54             );
55             })->then(sub {
56             ## invocations of $callback should be at the same level of
57             ## then() chain, because $callback might throw exception and
58             ## we should not catch that exception.
59            
60 30     30   10216 my ($changed) = @_;
61 30         106 @_ = (undef, $changed);
62 30         152 goto $callback;
63             }, sub {
64 0     0   0 my ($error) = @_;
65 0         0 @_ = ($error);
66 0         0 goto $callback;
67 30         1771 });
68             }
69              
70             sub _get_unacked_statuses_by_ids_future {
71 30     30   135 my ($self, $timeline, $ids) = @_;
72 30 100 66     156 if(!defined($ids) || !@$ids) {
73 17         107 return Future::Q->new->fulfill([]);
74             }
75 36         736 my @status_futures = map {
76 13         31 my $id = $_;
77 36         135 future_of(
78             $self, 'get_statuses',
79             timeline => $timeline, max_id => $id, ack_state => 'unacked', count => 1
80             );
81             } @$ids;
82             return Future::Q->needs_all(@status_futures)->then(sub {
83 13     13   3221 my @statuses_list = @_;
84 13 100       29 return [ map { defined($_->[0]) ? ($_->[0]) : () } @statuses_list ];
  36         106  
85 13         472 });
86             }
87              
88             sub _uniq_statuses {
89 30     30   63 my (@statuses) = @_;
90 30         62 my %id_to_s = map { $_->{id} => $_ } @statuses;
  157         311  
91 30         171 return values %id_to_s;
92             }
93              
94             sub contains {
95 16     16 1 38 my ($self, %args) = @_;
96 16         23 my $timeline = $args{timeline};
97 16         22 my $query = $args{query};
98 16         18 my $callback = $args{callback};
99 16 100       117 croak 'timeline argument is mandatory' if not defined($timeline);
100 15 100       105 croak 'query argument is mandatory' if not defined($query);
101 14 100       102 croak 'callback argument is mandatory' if not defined($callback);
102 13 100 66     49 if(ref($query) eq 'ARRAY') {
    50          
103             ;
104             }elsif(ref($query) eq 'HASH' || !ref($query)) {
105 5         8 $query = [$query];
106             }else {
107 0         0 croak 'query argument must be either STATUS, ID or ARRAYREF_OF_STATUSES_OR_IDS';
108             }
109 13 100       24 if(grep { !defined($_) } @$query) {
  59         97  
110 1         83 croak 'query argument must not contain undef';
111             }
112 12 100       26 if(!@$query) {
113 1         3 @_ = (undef, [], []);
114 1         3 goto $callback;
115             }
116 56         1336 my @subfutures = map {
117 11         20 my $query_elem = $_;
118 56 100       106 my $id = ref($query_elem) ? $query_elem->{id} : $query_elem;
119 56 100       195 defined($id) ? future_of($self, "get_statuses", timeline => $timeline, count => 1, max_id => $id)
120             : Future::Q->new->fulfill([]); ## ID-less status is always 'not contained'.
121             } @$query;
122             Future::Q->needs_all(@subfutures)->then(sub {
123 11     11   2788 my (@statuses_list) = @_;
124 11 50       35 if(@statuses_list != @$query) {
125 0         0 confess("fatal error: number of statuses_list does not match the number of query");
126             }
127 11         14 my @contained = ();
128 11         15 my @not_contained = ();
129 11         27 foreach my $i (0 .. $#statuses_list) {
130 56 100       53 if(@{$statuses_list[$i]}) {
  56         92  
131 20         29 push @contained, $query->[$i];
132             }else {
133 36         62 push @not_contained, $query->[$i];
134             }
135             }
136 11         35 return (\@contained, \@not_contained);
137             })->then(sub {
138 11     11   2921 my ($contained, $not_contained) = @_;
139 11         29 @_ = (undef, $contained, $not_contained);
140 11         30 goto $callback;
141             }, sub {
142 0     0   0 my ($error) = @_;
143 0         0 @_ = ($error);
144 0         0 goto $callback;
145 11         355 });
146             }
147              
148             sub get_unacked_counts {
149 16     16 1 51 my ($self, %args) = @_;
150 16 100       142 croak 'timeline arg is mandatory' if not defined $args{timeline};
151 15 100       122 croak 'callback arg is mandatory' if not defined $args{callback};
152 14         28 my $timeline = $args{timeline};
153 14         25 my $callback = $args{callback};
154            
155             ## get_statuses() called plainly. its exception propagates to the caller.
156             $self->get_statuses(
157             timeline => $timeline, ack_state => "unacked", count => "all",
158             callback => sub {
159 14     14   27 my ($error, $statuses) = @_;
160 14 50       48 if(defined($error)) {
161 0         0 @_ = ("get error: $error");
162 0         0 goto $callback;
163             }
164 14         48 my %count = (total => int(@$statuses));
165 14         37 foreach my $status (@$statuses) {
166 39         37 my $level = do {
167 2     2   518 no autovivification;
  2         774  
  2         11  
168 39 100       141 $status->{busybird}{level} || 0;
169             };
170 39         71 $count{$level}++;
171             }
172 14         40 @_ = (undef, \%count);
173 14         54 goto $callback;
174             }
175 14         105 );
176             }
177              
178              
179             1;
180             __END__