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   8 use strict;
  2         2  
  2         57  
3 2     2   7 use warnings;
  2         3  
  2         35  
4 2     2   7 use Carp;
  2         2  
  2         118  
5 2     2   24 use Exporter qw(import);
  2         2  
  2         52  
6 2     2   7 use BusyBird::Util qw(future_of);
  2         2  
  2         69  
7 2     2   7 use BusyBird::DateTime::Format;
  2         3  
  2         36  
8 2     2   6 use DateTime;
  2         3  
  2         33  
9 2     2   6 use Try::Tiny;
  2         5  
  2         90  
10 2     2   7 use Future::Q;
  2         3  
  2         2178  
11              
12             our @EXPORT_OK = qw(contains ack_statuses get_unacked_counts);
13              
14             sub ack_statuses {
15 32     32 1 112 my ($self, %args) = @_;
16 32 100       249 croak 'timeline arg is mandatory' if not defined $args{timeline};
17 31         36 my $ids;
18 31 100       89 if(defined($args{ids})) {
19 16 100       111 if(!ref($args{ids})) {
    50          
20 6         19 $ids = [$args{ids}];
21             }elsif(ref($args{ids}) eq 'ARRAY') {
22 10         21 $ids = $args{ids};
23 10 100       28 croak "ids arg array must not contain undef" if grep { !defined($_) } @$ids;
  33         138  
24             }else {
25 0         0 croak "ids arg must be either undef, status ID or array-ref of IDs";
26             }
27             }
28 30         65 my $max_id = $args{max_id};
29 30         63 my $timeline = $args{timeline};
30 30   50 0   111 my $callback = $args{callback} || sub {};
  0         0  
31 30         179 my $ack_str = BusyBird::DateTime::Format->format_datetime(
32             DateTime->now(time_zone => 'UTC')
33             );
34 30         14949 my @subfutures = (_get_unacked_statuses_by_ids_future($self, $timeline, $ids));
35 30 100 100     3653 if(!defined($ids) || defined($max_id)) {
36 21         663 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   6416 my @statuses_list = @_;
45 30         70 my @target_statuses = _uniq_statuses(map { @$_ } @statuses_list);
  51         134  
46 30 100       85 if(!@target_statuses) {
47 6         21 return 0;
48             }
49 24         206 $_->{busybird}{acked_at} = $ack_str foreach @target_statuses;
50 24         90 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   8843 my ($changed) = @_;
61 30         81 @_ = (undef, $changed);
62 30         105 goto $callback;
63             }, sub {
64 0     0   0 my ($error) = @_;
65 0         0 @_ = ($error);
66 0         0 goto $callback;
67 30         1576 });
68             }
69              
70             sub _get_unacked_statuses_by_ids_future {
71 30     30   122 my ($self, $timeline, $ids) = @_;
72 30 100 66     154 if(!defined($ids) || !@$ids) {
73 17         102 return Future::Q->new->fulfill([]);
74             }
75 36         605 my @status_futures = map {
76 13         38 my $id = $_;
77 36         121 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   3149 my @statuses_list = @_;
84 13 100       30 return [ map { defined($_->[0]) ? ($_->[0]) : () } @statuses_list ];
  36         105  
85 13         419 });
86             }
87              
88             sub _uniq_statuses {
89 30     30   73 my (@statuses) = @_;
90 30         62 my %id_to_s = map { $_->{id} => $_ } @statuses;
  157         297  
91 30         137 return values %id_to_s;
92             }
93              
94             sub contains {
95 16     16 1 36 my ($self, %args) = @_;
96 16         22 my $timeline = $args{timeline};
97 16         20 my $query = $args{query};
98 16         18 my $callback = $args{callback};
99 16 100       111 croak 'timeline argument is mandatory' if not defined($timeline);
100 15 100       98 croak 'query argument is mandatory' if not defined($query);
101 14 100       100 croak 'callback argument is mandatory' if not defined($callback);
102 13 100 66     48 if(ref($query) eq 'ARRAY') {
    50          
103             ;
104             }elsif(ref($query) eq 'HASH' || !ref($query)) {
105 5         9 $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       25 if(grep { !defined($_) } @$query) {
  59         80  
110 1         73 croak 'query argument must not contain undef';
111             }
112 12 100       28 if(!@$query) {
113 1         2 @_ = (undef, [], []);
114 1         4 goto $callback;
115             }
116 56         1053 my @subfutures = map {
117 11         17 my $query_elem = $_;
118 56 100       90 my $id = ref($query_elem) ? $query_elem->{id} : $query_elem;
119 56 100       160 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   2618 my (@statuses_list) = @_;
124 11 50       31 if(@statuses_list != @$query) {
125 0         0 confess("fatal error: number of statuses_list does not match the number of query");
126             }
127 11         16 my @contained = ();
128 11         11 my @not_contained = ();
129 11         26 foreach my $i (0 .. $#statuses_list) {
130 56 100       41 if(@{$statuses_list[$i]}) {
  56         75  
131 20         27 push @contained, $query->[$i];
132             }else {
133 36         47 push @not_contained, $query->[$i];
134             }
135             }
136 11         37 return (\@contained, \@not_contained);
137             })->then(sub {
138 11     11   2830 my ($contained, $not_contained) = @_;
139 11         29 @_ = (undef, $contained, $not_contained);
140 11         28 goto $callback;
141             }, sub {
142 0     0   0 my ($error) = @_;
143 0         0 @_ = ($error);
144 0         0 goto $callback;
145 11         315 });
146             }
147              
148             sub get_unacked_counts {
149 16     16 1 47 my ($self, %args) = @_;
150 16 100       131 croak 'timeline arg is mandatory' if not defined $args{timeline};
151 15 100       137 croak 'callback arg is mandatory' if not defined $args{callback};
152 14         21 my $timeline = $args{timeline};
153 14         20 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   24 my ($error, $statuses) = @_;
160 14 50       41 if(defined($error)) {
161 0         0 @_ = ("get error: $error");
162 0         0 goto $callback;
163             }
164 14         41 my %count = (total => int(@$statuses));
165 14         28 foreach my $status (@$statuses) {
166 39         33 my $level = do {
167 2     2   448 no autovivification;
  2         762  
  2         12  
168 39 100       131 $status->{busybird}{level} || 0;
169             };
170 39         68 $count{$level}++;
171             }
172 14         37 @_ = (undef, \%count);
173 14         53 goto $callback;
174             }
175 14         88 );
176             }
177              
178              
179             1;
180             __END__