NAME
BusyBird::Timeline - a timeline object in BusyBird
SYNOPSIS
use BusyBird::Timeline;
use BusyBird::StatusStorage::SQLite;
my $storage = BusyBird::StatusStorage::SQLite->new(
path => ':memory:'
);
my $timeline = BusyBird::Timeline->new(
name => "sample", storage => $storage
);
$timeline->set_config(
time_zone => "+0900"
);
## Add some statuses
$timeline->add_statuses(
statuses => [{text => "foo"}, {text => "bar"}],
callback => sub {
my ($error, $num) = @_;
if($error) {
warn("error: $error");
return;
}
print "Added $num statuses.\n";
}
);
## Ack all statuses
$timeline->ack_statuses(callback => sub {
my ($error, $num) = @_;
if($error) {
warn("error: $error");
return;
}
print "Acked $num statuses.\n";
});
## Change acked statuses into unacked.
$timeline->get_statuses(
ack_state => 'acked', count => 10,
callback => sub {
my ($error, $statuses) = @_;
if($error) {
warn("error: $error");
return;
}
foreach my $s (@$statuses) {
$s->{busybird}{acked_at} = undef;
}
$timeline->put_statuses(
mode => "update", statuses => $statuses,
callback => sub {
my ($error, $num) = @_;
if($error) {
warn("error: $error");
return;
}
print "Updated $num statuses.\n";
}
);
}
);
## Delete all statuses
$timeline->delete_statuses(
ids => undef, callback => sub {
my ($error, $num) = @_;
if($error) {
warn("error: $error");
return;
}
print "Delete $num statuses.\n";
}
);
DESCRIPTION
BusyBird::Timeline stores and manages a timeline, which is an ordered sequence of statuses. You can add statuses to a timeline, and get statuses from the timeline.
This module uses BusyBird::Log for logging.
Filters
You can set status filters to a timeline. A status filter is a subroutine that is called when new statuses are added to the timeline via add_statuses()
method.
Using status filters, you can modify or even drop the added statuses before they are actually inserted to the timeline. Status filters are executed in the same order as they are added.
BusyBird comes with some pre-defined status filters. See BusyBird::Filter for detail.
Status Storage
A timeline's statuses are actually saved in a BusyBird::StatusStorage object. When you create a timeline via new()
method, you have to specify a BusyBird::StatusStorage object explicitly.
Callback-Style Methods
Some methods of BusyBird::Timeline are callback-style, that is, their results are not returned but given to the callback function you specify.
It depends on the underlying BusyBird::StatusStorage object whether the callback-style methods are synchronous or asynchronous. BusyBird::StatusStorage::SQLite, for example, is synchronous. If the status storage is synchronous, the callback is always called before the method returns. If the status storage is asynchronous, it is possible for the method to return without calling the callback. The callback will be called at a certain point later.
To handle callback-style methods, I recommend future_of()
function in BusyBird::Util. future_of()
function transforms callback-style methods into Future-style methods.
CLASS METHODS
$timeline = BusyBird::Timeline->new(%args)
Creates a new timeline.
You can also create a timeline via BusyBird::Main's timeline()
method.
Fields in %args
are as follows.
name
=> STRING (mandatory)-
Specifies the name of the timeline. If it includes Unicode characters, it must be a character string (decoded string), not a binary string (encoded string).
storage
=> STATUS_STORAGE (mandatory)-
Specifies a BusyBird::StatusStorage object. Statuses in
$timeline
is saved to thestorage
.
OBJECT METHODS
$name = $timeline->name()
Returns the $timeline
's name.
$timeline->add($statuses, [$callback])
$timeline->add_statuses(%args)
Adds new statuses to the $timeline
.
Note that statuses added by add_statuses()
method go through the $timeline
's filters. It is the filtered statuses that are actually inserted to the storage.
In addition to filtering, if statuses added to the $timeline
lack id
or created_at
field, it automatically generates and sets these fields. This auto-generation of IDs and timestamps are done after the filtering.
add()
method is a short-hand of add_statuses(statuses => $statuses, callback => $callback)
.
Fields in %args
are as follows.
statuses
=> {STATUS, ARRAYREF_OF_STATUSES} (mandatory)-
Specifies a status object or an array-ref of status objects to be added. See BusyBird::Manual::Status about what status objects look like.
callback
=> CODEREF($error, $added_num) (optional, default:undef
)-
Specifies a subroutine reference that is called when the operation has completed.
In success,
callback
is called with two arguments ($error
and$added_num
).$error
isundef
, and$added_num
is the number of statuses actually added to the$timeline
.In failure,
$error
is a truthy value describing the error.
$timeline->ack_statuses(%args)
Acknowledges statuses in the $timeline
, that is, changing 'unacked' statuses into 'acked'.
Acked status is a status whose $status->{busybird}{acked_at}
field evaluates to true. Otherwise, the status is unacked.
Fields in %args
are as follows.
ids
=> {ID, ARRAYREF_OF_IDS} (optional, default:undef
)-
Specifies the IDs of the statuses to be acked.
If it is a defined scalar, the status with the specified ID is acked. If it is an array-ref of IDs, the statuses with those IDs are acked.
If both
max_id
andids
are omitted or set toundef
, all unacked statuses are acked. If bothmax_id
andids
are specified, both statuses older than or equal tomax_id
and statuses specifed byids
are acked.If Status IDs include Unicode characters, they should be character strings (decoded strings), not binary strings (encoded strings).
max_id
=> ID (optional, default:undef
)-
Specifies the latest ID of the statuses to be acked.
If specified, unacked statuses with IDs older than or equal to the specified
max_id
are acked. If there is no unacked status with IDmax_id
, no status is acked.If both
max_id
andids
are omitted or set toundef
, all unacked statuses are acked. If bothmax_id
andids
are specified, both statuses older than or equal tomax_id
and statuses specifed byids
are acked.If the Status ID includes Unicode characters, it should be a character string (decoded string), not a binary string (encoded string).
callback
=> CODEREF($error, $acked_num) (optional, default:undef
)-
Specifies a subroutine reference that is called when the operation completes.
In success, the
callback
is called with two arguments ($error
and$acked_num
).$error
isundef
, and$acked_num
is the number of acked statuses.In failure,
$error
is a truthy value describing the error.
$timeline->get_statuses(%args)
Fetches statuses from the $timeline
. The fetched statuses are given to the callback
function.
Fields in %args
are as follows.
callback
=> CODEREF($error, $arrayref_of_statuses) (mandatory)-
Specifies a subroutine reference that is called upon completion of fetching statuses.
In success,
callback
is called with two arguments ($error
and$arrayref_of_statuses
).$error
isundef
, and$arrayref_of_statuses
is an array-ref of fetched status objects. The array-ref can be empty.In failure,
$error
is a truthy value describing the error. ack_state
=> {'any', 'unacked', 'acked'} (optional, default: 'any')-
Specifies the acked/unacked state of the statuses to be fetched.
By setting it to
'unacked'
, this method returns only unacked statuses from the storage. By setting it to'acked'
, it returns only acked statuses. By setting it to'any'
, it returns both acked and unacked statuses. max_id
=> STATUS_ID (optional, default:undef
)-
Specifies the latest ID of the statuses to be fetched. It fetches statuses with IDs older than or equal to the specified
max_id
.If there is no such status that has the ID equal to
max_id
in specifiedack_state
, the result is an empty array-ref.If this option is omitted or set to
undef
, statuses starting from the latest status are fetched.If the Status ID includes Unicode characters, it should be a character string (decoded string), not a binary string (encoded string).
count
=> {'all', NUMBER} (optional)-
Specifies the maximum number of statuses to be fetched.
If
'all'
is specified, all statuses starting frommax_id
in specifiedack_state
are fetched.The default value of this option is up to implementation of the status storage the
$timeline
uses.
$timeline->put_statuses(%args)
Inserts statuses to the $timeline
or updates statuses in the $timeline
.
Usually you should use add_statuses()
method to add new statuses to the $timeline
, because statuses inserted by put_statuses()
bypasses the $timeline
's filters.
Fields in %args
are as follows.
mode
=> {'insert', 'update', 'upsert'} (mandatory)-
Specifies the mode of operation.
If
mode
is"insert"
, the statuses are inserted (added) to the$timeline
. Ifmode
is"update"
, the statuses in the$timeline
are updated to the given statuses. Ifmode
is"upsert"
, statuses already in the$timeline
are updated while statuses not in the$timeline
are inserted.The statuses are identified by
$status->{id}
field. The$status->{id}
field must be unique in the$timeline
. So ifmode
is"insert"
, statuses whose ID is already in the$timeline
are ignored and not inserted.If
$status->{id}
includes Unicode characters, it should be a character string (decoded string), not a binary string (encoded string). statuses
=> {STATUS, ARRAYREF_OF_STATUSES} (mandatory)-
The statuses to be saved in the
$timeline
. It is either a status object or an array-ref of status objects.See BusyBird::Manual::Status for specification of status objects.
callback
=> CODEREF($error, $put_num) (optional, default:undef
)-
Specifies a subroutine reference that is called when the operation completes.
In success,
callback
is called with two arguments ($error
and$put_num
).$error
isundef
, and$put_num
is the number of statuses inserted or updated.In failure,
$error
is a truthy value describing the error.
$timeline->delete_statuses(%args)
Deletes statuses from the $timeline
.
Fields in %args
are as follows.
ids
=> {undef
, ID, ARRAYREF_OF_IDS} (mandatory)-
Specifies the IDs (value of
$status->{id}
field) of the statuses to be deleted.If it is a defined scalar, the status with the specified ID is deleted. If it is an array-ref of IDs, the statuses with those IDs are deleted. If it is explicitly set to
undef
, all statuses in the$timeline
are deleted.If Status IDs include Unicode characters, they should be character strings (decoded strings), not binary strings (encoded strings).
callback
=> CODEREF($error, $deleted_num) (optional, default:undef
)-
Specifies a subroutine reference that is called when the operation completes.
In success, the
callback
is called with two arguments ($error
and$deleted_num
).$error
isundef
, and$deleted_num
is the number of deleted statuses.In failure,
$error
is a truthy value describing the error.
$timeline->get_unacked_counts(%args)
Fetches numbers of unacked statuses in the $timeline
.
Fields in %args
are as follows.
callback
=> CODEREF($error, $unacked_counts) (mandatory)-
Specifies a subroutine reference that is called when the operation completes.
In success, the
callback
is called with two arguments ($error
and$unacked_counts
).$error
isundef
, and$unacked_counts
is a hash-ref describing numbers of unacked statuses in each level.In failure,
$error
is a truthy value describing the error.
Fields in %$unacked_counts
are as follows.
- LEVEL => COUNT_OF_UNACKED_STATUSES_IN_THE_LEVEL
-
LEVEL is an integer key that represents the status level. The value is the number of unacked statuses in the level.
A status's level is the
$status->{busybird}{level}
field. See BusyBird::Manual::Status for detail.LEVEL key-value pair is present for each level in which there are some unacked statuses.
total
=> COUNT_OF_ALL_UNACKED_STATUSES-
The key
"total"
represents the total number of unacked statuses in the$timeline
.
For example, $unacked_counts
is structured like:
$unacked_counts = {
total => 3,
0 => 1,
1 => 2,
};
This means there are 3 unacked statuses in total, one of which is in level 0, and the rest is in level 2.
$timeline->contains(%args)
Checks if the given statuses (or IDs) are contained in the $timeline
.
Fields in %args
are as follows.
query
=> {STATUS, ID, ARRAYREF_OF_STATUSES_OR_IDS} (mandatory)-
Specifies the statuses or IDs to be checked.
If it is a scalar, that value is treated as a status ID. If it is a hash-ref, that object is treated as a status object. If it is an array-ref, elements in the array-ref are treated as status objects or IDs. Status objects and IDs can be mixed in a single array-ref.
If some statuses in
query
don't have theirid
field, those statuses are always treated as "not contained" in the$timeline
.If Status IDs include Unicode characters, they should be character strings (decoded strings), not binary strings (encoded strings).
callback
=> CODEREF($error, $contained, $not_contained) (mandatory)-
Specifies a subroutine reference that is called when the check has completed.
In success,
callback
is called with three arguments ($error
,$contained
,$not_contained
).$error
isundef
.$contained
is an array-ref of given statuses or IDs that are contained in the$timeline
.$not_contained
is an array-ref of given statuses or IDs that are NOT contained in the$timeline
.In failure,
$error
is a truthy value describing the error.
$timeline->add_filter($filter, [$is_async])
Add a status filter to the $timeline
.
$filter
is a subroutine reference that is called upon added statuses. $is_async
specifies whether the $filter
is synchronous or asynchronous.
BusyBird::Filter may help you create common status filters.
Synchronous Filter
If $is_async
is false, $filter
is a synchronous filter. It is called like
$result_arrayref = $filter->($arrayref_of_statuses);
where $arrayref_of_statuses
is an array-ref of statuses that is injected to the filter.
$filter
must return an array-ref of statuses ($result_arrayref
), which is going to be passed to the next filter (or the status storage if there is no next filter). $result_arrayref
may be either $arrayref_of_statuses
or a new array-ref. If $filter
returns anything other than an array-ref, a warning is logged and $arrayref_of_statuses
is passed to the next.
Asynchronous Filter
If $is_async
is true, $filter
is a asynchronous filter. It is called like
$filter->($arrayref_of_statuses, $done);
where $done
is a subroutine reference that $filter
is supposed to call when it completes its task. $filter
must pass the result array-ref of statuses to the $done
callback.
$done->($result_arrayref)
Examples
## Synchronous filter
$timeline->add_filter(sub {
my ($statuses) = @_;
return [ grep { some_predicate($_) } @$statuses ];
});
## Asynchronous filter
$timeline->add_filter(sub {
my ($statuses, $done) = @_;
some_async_processing(
statuses => $statuses,
callback => sub {
my ($results) = @_;
$done->($results);
}
);
}, 1);
$timeline->add_filter_async($filter)
Add an asynchronous status filter. This is equivalent to $timeline->add_filter($filter, 1)
.
$timeline->set_config($key1 => $value1, $key2 => $value2, ...)
Sets config parameters to the $timeline
.
$key1
, $key2
, ... are the keys for the config parameters, and $value1
, $value2
, ... are the values for them.
See BusyBird::Manual::Config for the list of config parameters.
$value = $timeline->get_config($key)
Returns the value of config parameter whose key is $key
.
If there is no config parameter associated with $key
, it returns undef
.
$watcher = $timeline->watch_unacked_counts(%args)
Watch updates of unacked counts in the $timeline
.
Fields in %args
are as follows.
assumed
=> HASHREF (mandatory)-
Specifies the unacked counts that the caller assumes.
callback
=> CODEREF ($error, $w, $unacked_counts) (mandatory)-
Specifies the callback function that is called when the unacked counts given in
assumed
argument are different from the current unacked counts.
In assumed
argument, caller must describe numbers of unacked statuses (i.e. unacked counts) for each status level and/or in total. If the assumed unacked counts is different from the current unacked counts in $timeline
, callback
subroutine reference is called with the current unacked counts ($unacked_counts
). If the assumed unacked counts is the same as the current unacked counts, execution of callback
is delayed until there is some difference between them.
Format of assumed
argument and %$unacked_counts
is the same as %$unacked_counts
returned by get_unacked_counts()
method. See also the following example.
In success, the callback
is called with three arguments ($error
, $w
, $unacked_counts
). $error
is undef
. $w
is an BusyBird::Watcher object representing this watch. $unacked_counts
is a hash-ref describing the current unacked counts of the $timeline
.
In failure, $error
is a truthy value describing the error. $w
is an inactive BusyBird::Watcher.
For example,
use Data::Dumper;
my $watcher = $timeline->watch_unacked_counts(
assumed => { total => 4, 1 => 2 },
callback => sub {
my ($error, $w, $unacked_counts) = @_;
$w->cancel();
print Dumper $unacked_counts;
}
);
In the above example, the caller assumes that the $timeline
has 4 unacked statuses in total, and 2 of them are in level 1.
The callback is called when the assumption breaks. The $unacked_counts
describes the current unacked counts.
$unacked_counts = {
total => 4,
-1 => 1,
0 => 2,
1 => 1,
};
In the above, the counts in 'total' is the same as the assumption (4), but the unacked count for level 1 is 1 instead of 2. So the callback is executed.
The return value of this method ($watcher
) is an BusyBird::Watcher object. It is the same instance as $w
given in the callback
function. You can call $watcher->cancel()
or $w->cancel()
to cancel the watcher, like we did in the above example. Otherwise, the callback
function is called repeatedly.
Caller does not have to specify the complete set of unacked counts in assumed
argument. Updates are checked only for levels (or 'total') that are explicitly specified in assumed
. Therefore, if some updates happen in levels that are not in assumed
, callback
is never called.
If assumed
is an empty hash-ref, callback
is always called immediately.
Never apply future_of()
function from BusyBird::Util to this method. This is because this method can execute the callback more than once.
AUTHOR
Toshio Ito <toshioito [at] cpan.org>