Jaipo - Micro-blogging Client


Jaipo documentation Contained in the Jaipo distribution.

Index


Code Index:

NAME

Top

Jaipo - Micro-blogging Client

VERSION

Top

Version 0.02

DESCRIPTION

Top

Jaipo ( 宅噗 )

This project started for Jaiku.com, but now is going to support as-much-as-we-can micro-blogging sites.

"Jaiku" pronunced close to "宅窟" in Chinese, which means an area full of computer/internet users, and it really is one of the most popular sites recently. As jaiku is part of google and growing, there're still only few linux client.

it's writen in perl, so it can run on any platform that you can get perl on it. we got the first feedback that somebody use it on ARM embedded system at May 2008.

FUNCTIONS

Top

new

config

return Jaipo::Config

services

init CALLER_OBJECT

list_loaded_triggers

list_triggers

find_service_by_trigger TRIGGER_NAME [ SERVICES ]

_require ( module => MODULE , ... )

_already_required CLASS_NAME

_try_to_require CLASS_NAME

find_plugin CLASS_NAME

Find plugins by class name, which is full-qualified class name.

set_plugin_trigger PLUGIN_OBJECT CLASS

runtime_load_service CALLER SERVICE_NAME [TRIGGER_NAME]

if trigger name is specified, and it doesn't exist in config. Jaipo will create a new service object, and assign the trigger name to it.

if trigger name is specified, and Jaipo will try to search the service config by trigger name and load the service.

if trigger name is not specified. Jaipo will try to find service configs by service name. if there are two or more same service, Jaipo will load the default trigger name ( service name in lowcase )

dispatch_to_service SERVICE_TRIGGER , MESSAGE

command start with :[service] ( e.g. :twitter or :plurk ) something like that will call the servcie dispatch method, service plugin will decide what to do with.

action ACTION, PARAM

AUTHOR

Top

BlueT, <bluet at blue.org> Cornelius, cornelius.howl at gmail.com

BUGS

Top

Please report any bugs or feature requests to bug-jaipo at rt.cpan.org, or through the web interface at http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Jaipo. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.

SUPPORT

Top

You can find documentation for this module with the perldoc command.

	perldoc Jaipo

You can also look for information at:

* RT: CPAN's request tracker

http://rt.cpan.org/NoAuth/Bugs.html?Dist=Jaipo

* AnnoCPAN: Annotated CPAN documentation

http://annocpan.org/dist/Jaipo

* CPAN Ratings

http://cpanratings.perl.org/d/Jaipo

* Search CPAN

http://search.cpan.org/dist/Jaipo/

ACKNOWLEDGEMENTS

Top

COPYRIGHT & LICENSE

Top


Jaipo documentation Contained in the Jaipo distribution.
package Jaipo;
use warnings;
use strict;
use feature qw(:5.10);
use Jaipo::Config;
use Jaipo::Notify;
use Jaipo::Logger;
use base qw/Class::Accessor::Fast/;
__PACKAGE__->mk_accessors(qw/config/);

use vars qw/$NOTIFY $CONFIG $LOGGER $HANDLER $PUB_SUB @PLUGINS @SERVICES/;

my $debug = 0;

our $VERSION = '0.21';

sub new {
	my $class = shift;
	my %args  = @_;
	my $self  = {};

	bless $self, $class;
	return $self;
}

sub config {
	my $class = shift;
	$CONFIG = shift if (@_);
	$CONFIG ||= Jaipo::Config->new ();
	return $CONFIG;
}

sub notify {
	my $class = shift;
	$NOTIFY ||= Jaipo::Notify->new;
	return $NOTIFY;
}

sub services {
	my $class = shift;
	@SERVICES = @_ if @_;
	return @SERVICES;
}

sub logger {
	my $class = shift;
	$LOGGER = shift if (@_);
	return $LOGGER;
}

sub init {
	my $self   = shift;
	my $caller = shift;

	# Logger turn on
	Jaipo->logger ( Jaipo::Logger->new );

	# prereserve arguments for service plugin
	# my $args = {
	#
	# };
    Jaipo->notify;

	# we initialize service plugin class here
	# Set up plugins
    my @services;
    my @services_to_load = @{ Jaipo->config->app ('Services') };

    my @plugins;
    my @plugins_to_load;

	for ( my $i = 0; my $service = $services_to_load[$i]; $i++ ) {

		# Prepare to learn the plugin class name
		my ($service_name) = keys %{$service};
        say "Jaipo: Init " . $service_name;

		my $class;

		# Is the plugin name a fully-qualified class name?
		if ( $service_name =~ /^Jaipo::Service::/ ) {

            # app-specific plugins use fully qualified names, Jaipo service plugins may
			$class = $service_name;
		}

		# otherwise, assume it's a short name, qualify it
		else {
			$class = "Jaipo::Service::" . $service_name;
		}

		# Load the service plugin options
		my %options = ( %{ $service->{$service_name} } );

		if ( !$options{enable} ) {
			Jaipo->logger->info ( '%s is disabled', $service_name );
			next;
		}

		# Load the service plugin code
		$self->_try_to_require ($class);

        # XXX: if Service don't have trigger_name, we have to do something
        # 
		# Initialize the plugin and mark the prerequisites for loading too
		my $plugin_obj = $class->new (%options);
		$plugin_obj->init ($caller);

		push @services, $plugin_obj;
		foreach my $name ( $plugin_obj->prereq_plugins ) {
			next if grep { $_ eq $name } @plugins_to_load;
			push @plugins_to_load, { $name => {} };
		}

	}

	# All plugins loaded, save them for later reference
	Jaipo->services (@services);

	# XXX: need to implement plugin loader

	# warn "No supported service provider initialled!\n" if not $has_site;

	# when initialize jaipo, there are some new settings that we need to save.
	Jaipo->config->save;
	Jaipo->logger->info ('Configuration saved.');
}

sub list_loaded_triggers {
    my @services = Jaipo->services;
    for my $s (@services) {
        print $s->trigger_name, " => ", ref ($s), "\n";
    }
}

sub list_triggers {
	my @service_configs = @{ Jaipo->config->app ('Services') };
	for my $s (@service_configs) {
		my @v = values %$s;
		print $v[0]->{trigger_name}, " => ", join ( q||, keys (%$s) ), "\n";
	}
}

sub find_service_by_trigger {
	my ( $self, $tg, $services ) = @_;
	$services ||= [ Jaipo->services ];
	for my $s (@$services) {
		my $s_tg = $s->trigger_name;
		return $s if $s->trigger_name eq $tg;
	}
}

sub _require {
	my $self  = shift;
	my %args  = @_;
	my $class = $args{module};

	return 1 if $self->_already_required ($class);

	my $file = $class;
	$file .= '.pm' unless $file =~ /\.pm$/;
	$file =~ s|::|/|g;

	my $retval = eval { CORE::require "$file" };
	my $error = $@;
	if ( my $message = $error ) {
		$message =~ s/ at .*?\n$//;
		if ( $args{'quiet'} and $message =~ /^Can't locate $file/ ) {
			return 0;
		}
		elsif ( $error !~ /^Can't locate $file/ ) {
			die $error;
		}
		else {
		   #log->error(sprintf("$message at %s line %d\n", (caller(1))[1,2]));
			return 0;
		}
	}
}

sub _already_required {
	my $self  = shift;
	my $class = shift;
	my ($path) = ( $class =~ s|::|/|g );
	$path .= '.pm';
	return $INC{$path} ? 1 : 0;
}

sub _try_to_require {
	my $self   = shift;
	my $module = shift;
	$self->_require ( module => $module, quiet => 0 );
}

sub find_plugin {
	my $self    = shift;
	my $name    = shift;
	my @plugins = grep { $_->isa ($name) } Jaipo->plugins;
	return wantarray ? @plugins : $plugins[0];
}

# this may used by runtime_load_service
sub set_plugin_trigger {
	my ( $self, $plugin_obj, $options, $class, $services ) = @_;

	# give a trigger to plugin obj , take a look.  :p
	my $trigger_name;
	if ( defined $options->{trigger_name} ) {
		$trigger_name = $options->{trigger_name};
	}

	else {
		($trigger_name) = ( $class =~ m/(?<=Service::)(\w+)$/ );
		$trigger_name = lc $trigger_name;    # lower case
	}

	# repeat service trigger name
	while ( my $s
		= $self->find_service_by_trigger ( $trigger_name, $services ) )
	{

		# give an another trigger name for it or ask user
		# TODO: provide a config option to let user set jaipo to ask
		$trigger_name .= '_';
	}

	# set trigger name
	$plugin_obj->trigger_name ($trigger_name);
	print "set trigger: ", $trigger_name, ' for ', $class, "\n";
}

# XXX: need to re-check logic
sub runtime_load_service {
	my ( $self, $caller, $service_name, $trigger_name ) = @_;

	$trigger_name ||= lc $service_name;
	my $class = "Jaipo::Service::" . ucfirst $service_name;

	my $options = {};
	my @sp_options
		= Jaipo->config->find_service_option_by_trigger ($trigger_name);

	# can not find option , set default trigger name and sp_id
	if ( !@sp_options ) {
		$options->{trigger_name} = $trigger_name;
		my $num_rec = Number::RecordLocator->new;
		$options->{sp_id} = $num_rec->encode ( Jaipo->config->last_sp_cnt );
	}

	elsif ( scalar @sp_options == 1 ) {
		$options = $sp_options[0];
	}

	# XXX:
	# actually won't happen, config loader will canonicalize the config
	# service plugin will get it's default trigger namd from service name.
	else {
	 # find by service name
	 #       elsif ( scalar @sp_options > 1 ) {
	 #           # find service by trigger name
	 #           for my $s (@sp_options) {
	 #               $options = $s if ( $s->{trigger_name} eq $trigger_name );
	 #           }
	 #       }
	}

	# Load the service plugin code
	$self->_try_to_require ($class);

	# Jaipo::ClassLoader->new(base => $class)->require;

	my $plugin_obj = $class->new (%$options);
	$plugin_obj->init ($caller);

	# $self->set_plugin_trigger( $plugin_obj , $class );

	my @services = Jaipo->services;
	push @services, $plugin_obj;
	foreach my $name ( $plugin_obj->prereq_plugins ) {

		# next if grep { $_ eq $name } @plugins_to_load;
		#push @plugins_to_load, {$name => {}};
	}
	Jaipo->services (@services);

   # call save configuration here
   # TODO: this may overwrites other plugins afterload options
   # make sure that user did config jaipo , or we don't need to rewrite config
	Jaipo->config->save;
}

sub dispatch_to_service {
	my ( $self, $service_tg, $line ) = @_;
	my $s = $self->find_service_by_trigger ($service_tg);
    my ($sub_command) = ($line =~ m[^(\w+)] );
    $s->dispatch_sub_command( $sub_command , $line );

}

sub cache_clear {
	my @services = Jaipo->services;
	foreach my $service (@services) {
        if( UNIVERSAL::can( $service , 'get_cache' ) ) {
            my $c = $service->get_cache;
            $c->clear;
        }
	}
}

sub action {
	my ( $self, $action, $param ) = @_;
	my @services = Jaipo->services;
    foreach my $service (@services) {
        if ( UNIVERSAL::can( $service, $action ) ) {
            my $ret = $service->$action($param);
            if ($debug) {
				use Data::Dumper::Simple;
            	warn Dumper( $ret );
			}

            # XXX:
            #  - we should check ret->{type} eq 'notification'
            #  - and call Notify::init
            if ( ref $ret 
                and $ret->{type} eq 'notification' 
                and $ret->{updates} > 0 ) 
            {
                Jaipo->notify->create($ret);
            }
        }
        else {
            # service plugin doesn't support this kind of action
        }
    }

}

1;    # End of Jaipo