File Coverage

blib/lib/BusyBird/StatusStorage/Common.pm
Criterion Covered Total %
statement 122 134 91.0
branch 44 48 91.6
condition 8 11 72.7
subroutine 22 25 88.0
pod 3 3 100.0
total 199 221 90.0


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