—package
Exobrain;
use
v5.010;
use
strict;
use
warnings;
use
feature ();
use
autodie;
use
Moose;
use
Method::Signatures;
# ABSTRACT: Automate your life with Exobrain
our
$VERSION
=
'1.08'
;
# VERSION: Generated by DZP::OurPkg:Version
# This forms the top-level Exobrain dispatch class. It's mostly
# lazy attributes, and nice sugar for doing common operations.
use
Exobrain::Bus;
use
Exobrain::Config;
use
Exobrain::Message;
has
'config'
=> (
is
=>
'ro'
,
isa
=>
'Exobrain::Config'
,
builder
=>
'_build_config'
,
);
# Pub/Sub interfaces to our bus. These don't get generated unless
# our end code actually asks for them. Many things will only require
# one, or will use higher-level functions to do their work.
has
'pub'
=> (
is
=>
'ro'
,
isa
=>
'Exobrain::Bus'
,
builder
=>
'_build_pub'
,
lazy
=> 1,
);
has
'sub'
=> (
is
=>
'ro'
,
isa
=>
'Exobrain::Bus'
,
builder
=>
'_build_sub'
,
lazy
=> 1,
);
# import is called when we load the Exobrain class, and lets us
# automatically set strict, warnings, 5.10 features and Method::Signatures
# by default.
sub
import
{
strict->
import
();
warnings->
import
();
feature->
import
(
':5.10'
);
# Method::Signatures needs a little more magic.
my
$caller
=
caller
;
## no critic (ProhibitStringyEval)
eval
qq{
package $caller;
Method::Signatures->import;
1;
}
or
die
$@;
## use critic
return
;
}
# Right now we make sure anything using Exobrain is going to use
# the user's timezone (if set).
method BUILD(...) {
$self
->_set_timezone();
return
;
}
# This sets our timezone based upon what's in our config file,
# so any use of localtime() should use the user's local timezone.
method _set_timezone(
$tz
?) {
$tz
//=
$self
->config->{General}{timezone};
if
(
$tz
) {
$ENV
{TZ} =
$tz
;
## no critic RequireLocalizedPunctuationVars
tzset();
}
return
;
}
sub
_build_config {
return
Exobrain::Config->new; };
sub
_build_pub {
return
Exobrain::Bus->new(
type
=>
'PUB'
,
exobrain
=>
shift
) }
sub
_build_sub {
return
Exobrain::Bus->new(
type
=>
'SUB'
,
exobrain
=>
shift
) }
method watch_loop(
Str :
$class
!,
CodeRef :
$filter
,
CodeRef :
$then
!,
CodeRef :
$debug
?,
) {
# Load our component, because that means we immediately get
# an error if that class doesn't exist.
$self
->_load_component(
$class
);
while
(
my
$event
=
$self
->
sub
->get) {
my
$namespace
=
$event
->namespace;
if
(
grep
{
$_
eq
$class
} (
$namespace
, @{
$event
->roles })) {
# Note that we have to cast it to the namespace on the
# packet (which is a class), and not the $class argument
# (which could be a role!)
#
# Yes, this code should be simplified/improved!
$event
=
$event
->to_class(
$namespace
);
$debug
->(
$event
)
if
$debug
;
if
(
$filter
) {
# Check our filter, and skip if required
local
$_
=
$event
;
next
unless
$filter
->(
$event
);
}
# Everything passes! Trigger our callback
{
local
$_
=
$event
;
$then
->(
$event
); }
}
}
}
method notify(
$message
,
@args
) {
return
$self
->intent( NOTIFY,
message
=>
$message
,
@args
,
);
}
method message(
@args
) {
return
Exobrain::Message::Raw->new(
exobrain
=>
$self
,
@args
,
);
}
method message_class(
$class
,
@args
) {
$class
=
$self
->_load_component(
$class
);
return
$class
->new(
exobrain
=>
$self
,
@args
,
);
}
method measure(
$type
,
@args
) {
my
$class
=
$self
->_load_component( MEASURE_PREFIX .
$type
);
return
$class
->new(
exobrain
=>
$self
,
@args
,
);
}
method intent(
$type
,
@args
) {
my
$class
=
$self
->_load_component( INTENT_PREFIX .
$type
);
return
$class
->new(
exobrain
=>
$self
,
@args
,
);
}
method run(Str
$class
) {
my
$agent
=
$self
->_load_component( AGENT_PREFIX .
$class
)->new;
return
$agent
->start;
}
method can_run(Str
$class
) {
my
$agent
=
$self
->_load_component( AGENT_PREFIX .
$class
);
if
(
$agent
->DOES(
'Exobrain::Agent::Depends'
)) {
return
$agent
->check_dependencies;
}
return
1;
}
# Loads a class, automatically adding Exobrain if
# required. Returns the class loaded.
method _load_component(Str
$class
) {
$class
= CLASS_PREFIX .
$class
;
eval
"require $class"
;
croak $@
if
$@;
return
$class
;
}
__PACKAGE__->meta->make_immutable;
1;
__END__
=pod
=head1 NAME
Exobrain - Automate your life with Exobrain
=head1 VERSION
version 1.08
=head1 SYNOPSIS
use Exobrain;
my $exobrain = Exobrain->new;
$exobrain->notify("Hello World");
=head1 DESCRIPTION
Exobrain is a collection of agents which collect, classify, and act
upon data. They share a common bus for communication. Think of it as
IFTTT, but free, open source, and *you* keep control of your privacy.
Examples of things that exobrain can currently do:
=over
=item *
Give you XP in HabitRPG when you update your beeminder goals
=item *
Let others add items to your RememberTheMilk ToDo lists via twitter
=item *
Update beeminder by checking your inbox sizes using IMAP
=item *
Reward with HabitRPG XP you for responding to email
=item *
Automatically log life events to idonethis
=back
You can find out more information about Exobrain, including the
full README, install instructions, current development, and more
L<on github|https://github.com/pjf/exobrain>. In particular,
L<the README|https://github.com/pjf/exobrain/blob/master/README.md>
is strongly suggested reading.
Using C<Exobrain> will automatically enable both C<strict>, C<warnings>,
and Perl 5.10 features (such as C<say>).
The following are methods provided by the top-level Exobrain object:
=head1 METHODS
=head2 watch_loop
$exobrain->watch_loop(
class => 'Measurement::Geo',
filter => sub { $_->is_me },
then => sub { ... },
);
When we see packets of a particular class, do a particular thing.
The C<class> need not strictly be a class, but may also be a
C<role>.
The 'Exobrain::' prefix should not be supplied to the class/roles
you are searching for.
If the optional C<debug> option is passed with a coderef, that will be run for
every event in the desired class, before the filter is evaluated.
The event is passed as the first argument to all coderefs. As a
convenience, it is also placed inside C<$_>.
Never returns, just runs the loop forever.
=head2 notify
$exobrain->notify($msg
priority => -1,
);
Takes a mandatory message, and any arguments that can be passeed to
L<Exobrain::Intent::Notify>, and notifies the user. At the time of
writing, notifications are done by the pushover end-point by default.
This is a thin wrapper around C< $exobrain->intent('Notify', ... >.
=head2 message
$exobrain->message( ... );
Shortcut to create a 'raw' message. The exobrain parameter will be passed
to the class constructor automatically.
The message I<will> be sent automatically, unless the C<nosend> parameter
is set to a true value.
=head2 measure
$exobrain->measure( 'Mailbox',
count => 42,
user => 'pjf',
server => 'imap.example.com',
fodler => 'INBOX',
)->send;
Preferred shortcut for creating a measurement of the desired class. The
C<exobrain> parameter will be passed to the measurement class constructor
automatically.
=head2 intent
my $intent = $exobrain->intent( 'Tweet',
tweet => 'Hello World',
);
Preferred shortcut for making an intent of the desired class. The
C<exobrain> parameter will be passed to the intent class constructor
automatically.
=head2 run
$exobrain->run($agent);
Runs the agent of the class specified. The agent name is
automatically prepended with "Exobrain::Agent::" and loaded
first. This method never returns.
This is usually called from the C<exobrain> cmdline program.
=head2 can_run
if ($exobrain->can_run('Action::Foo') { ... }
Checks to see if a given agent I<could> be run. (Ie, it can be loaded, and
all its dependencies are satisified.)
=for Pod::Coverage BUILD DEMOLISH CLASS_PREFIX INTENT_PREFIX MEASURE_PREFIX NOTIFY AGENT_PREFIX import
=head1 AUTHOR
Paul Fenwick <pjf@cpan.org>
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2014 by Paul Fenwick.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut