#!/usr/bin/env perl use feature qw'say state' ; use strict ; use warnings ; use utf8 ; my $w = Wit->new( @ARGV ) ; $w->run() ; package Wit ; use lib '/home/jacoby/lib' ; use base 'Witter' ; use Witter::Twitter ; 1;
package Witter ; # highly adapted from perlbrew. use feature qw{ say } ; use strict ; use warnings ; sub new { my ( $class, @argv ) = @_ ; my $self ; $self->{foo} = 'bar' ; $self->{args} = [] ; if (@argv) { $self->{args} = \@argv ; } return bless $self, $class ; } sub run { my ($self) = @_ ; $self->run_command( $self->{args} ) ; } sub run_command { my ( $self, $args ) = @_ ; if ( scalar @$args == 0 || lc $args->[0] eq 'help' || $self->{help} ) { $self->help(@$args) ; exit ; } if ( lc $args->[0] eq 'commands' ) { say join "\n\t", '', $self->commands() ; exit ; } my $command = $args->[0] ; my $s = $self->can("twitter_$command") ; unless ($s) { $command =~ y/-/_/ ; $s = $self->can("twitter_$command") ; } unless ($s) { my @commands = $self->find_similar_commands($command) ; if ( @commands > 1 ) { @commands = map { ' ' . $_ } @commands ; die "Unknown command: `$command`. Did you mean one of the following?\n" . join( "\n", @commands ) . "\n" ; } elsif ( @commands == 1 ) { die "Unknown command: `$command`. Did you mean `$commands[0]`?\n" ; } else { die "Unknown command: `$command`. Typo?\n" ; } } unless ( 'CODE' eq ref $s ) { say 'Not a valid command' ; exit ; } $self->$s(@$args) ; } sub help { my ($self,$me,@args) = @_ ; say 'HELP!' ; say join "\t", @args; } sub commands { my ($self) = @_ ; my @commands ; my $package = ref $self ? ref $self : $self ; my $symtable = do { no strict 'refs' ; \%{ $package . '::' } ; } ; foreach my $sym ( sort keys %$symtable ) { if ( $sym =~ /^twitter_/ ) { my $glob = $symtable->{$sym} ; if ( defined *$glob{CODE} ) { $sym =~ s/^twitter_// ; $sym =~ s/_/-/g ; push @commands, $sym ; } } } return @commands ; } # Some functions removed for sake of brevity
package Witter::Twitter ; use strict ; use feature qw{ say state } ; use warnings FATAL => 'all' ; use Exporter qw{import} ; use Net::Twitter ; use JSON::XS ; our $VERSION = 0.1 ; our @EXPORT ; for my $entry ( keys %Witter::Twitter:: ) { next if $entry !~ /^twitter_/mxs ; push @EXPORT, $entry ; } sub twitter_foo { my ( $self, @args ) = @_ ; say "foo" ; say join '|', @args ; } 1 ;
And the above works when called as below.
jacoby@oz 13:49 60°F 51.24,-112.49 ~ $ ./witter HELP! jacoby@oz 13:52 60°F 51.25,-94.51 ~ $ ./witter help HELP! jacoby@oz 13:53 60°F 50.59,-88.64 ~ $ ./witter commands foo jacoby@oz 13:53 60°F 50.59,-88.64 ~ $ ./witter help foo HELP! foo jacoby@oz 13:53 60°F 50.59,-88.64 ~ $ ./witter foo foo foo jacoby@oz 13:53 60°F 50.59,-88.64 ~ $ ./witter moo Unknown command: `moo`. Did you mean `foo`?
In the above example, I'm just doing the one add-on module,
Witter::Twitter
and one function, Witter::Twitter::foo
, but clearly, I would want it open-ended, so that if someone wanted to add Witter::Facebook
, all the information about the Facebook functions would be in that module.
Then, of course, I would have to use another prefix than
twitter_
, but we'll leave that, and ensuring that modules don't step on each others' names, to another day.
The part that concerns me is
help
. Especially help foo
. It should be part of the the module it's in; If Witter::Twitter
is the module with foo()
, only it should be expected to know about foo()
.
But how to communicate it? I'm flashing on
our %docs
and $docs{foo}= 'This is foo, silly'
but the point of the whole thing is to allow the addition of modules that the initial module doesn't know about, and it would require knowing to look for %Witter::Twitter::docs
.
I suppose adding a
docs_
function that looks like this.
sub docs_foo { return q{ This explains the use of the 'foo' command ... }; END }
I'm diving into this in part because I have code that uses basically this code, and I need to add functionality to it, and while I'm in there, I might as well make user documentation better. Or even possible.
I'm also parallel-inspired by looking at a Perl project built on and using old Perl ("require 5.005") and recent blog posts about Linus Torvalds and "Good Taste". There's something tasteful about being able to add
use Your::Module
and nothing else to code, but if the best I can do is knowledge that there's a foo
command, with no sense of what it does, that seems like the kind of thing that Linus would rightfully curse me for.
Is there a better way of doing things like this? I recall there being interesting things in
Net::Tumblr
that would require me to step up and learn Moose
or the like. This is yet another important step toward me becoming a better and more tasteful programmer, but not germane to today's ranting.
No comments:
Post a Comment