Badger

 view release on metacpan or  search on metacpan

Changes  view on Meta::CPAN

    Added the codec() method to Badger::Filesystem::File for specifying a
    serialisation codec to use in conjunction with the new data() method.

    Added Badger::Timestamp. This is returned by the new created(),
    accessed() and modified() methods in Badger::Filesystem::Path.

    Added Badger::Logic.

    Added Badger::Log and Badger::Log::File.

    Added numlike() to Badger::Utils as an alias for "looks_like_number".
    It's the numerical counterpart to textlike() and is significantly easier
    to type.

    Added debug_msg() to Badger::Base. Also added the "Badger::Base::Trial"
    class definition for use with the try() method.

    Added lib import hook to Badger.

    Added bclass as an alias for class in Badger::Class.

    Changed Badger::Class::Config to maintain the order of configuration
    parameters where possible (i.e. when expressed as a list ref rather than
    a hash ref). Also added "target:var" fallback which looks in the target
    object or hash ref for the variable. This allows options to default to
    the values set by preceeding options.

    Changed Badger::Codec::JSON to use JSON::XS if available.

    Mr T is now using Badger 0.06 in production code and is happy to report
    that everything seems to be working rather well. Anyone for a game of
    tennis?

  Version 0.05 - 23rd December 2008

lib/Badger/Base.pm  view on Meta::CPAN

=head2 todo($what)

A method of convenience useful during developing to indicate that a method
isn't implemented yet.  It raises an error stating that the method is
still TODO.

    sub not_yet_working {
        shift->todo;
    }

The error message generated looks something like this:

    your.badger.module error - not_yet_working() is TODO in
    Your::Badger::Module at line 42

You can pass an argument to be more specific about what is still TODO.

    sub not_yet_working {
        my ($self, $x) = @_;
        if (ref $x) {
            $self->todo('support for references');

lib/Badger/Class.pm  view on Meta::CPAN

In this simple example, the effect is exactly the same as modifying the C<$X>
I<package> variable directly. However, this method (and related methods)
provides an abstraction of I<class> variables that works correctly with
respect to subclassing. That is, accessing a I<class> variable in a subclass
of C<Your::Module> will resolve to the I<package> variable in the subclass,
rather than the base class. If instead you write C<$X> then you'll always get
the variable in the base class package (which may be what you want, of
course).

A form of inheritance for class variables can be implemented using the
L<any_var()> method.  This looks for a package variable in the current class
or in any of the base classes.

    class->any_var('X');            # $X with @ISA inheritance

This idiom is particularly useful to provide default values for a class that
you might want to re-define later in a subclass. We'll look at some examples
of that shortly.

=head2 SUBCLASSING Badger::Class

lib/Badger/Class.pm  view on Meta::CPAN

    our $MAX_VOLUME = 10;

    sub init {
        my ($self, $config) = @_;
        $self->{ volume     } = 0;   # start quietly
        $self->{ max_volume } = $config->{ max_volume }
            || $MAX_VOLUME;
        return $self;
    }

The C<init()> method (see L<Badger::Base>) looks for a C<max_volume>
setting in the configuration parameters, or defaults to the C<$MAX_VOLUME>
package variable.

    my $amp = Amplifer->new;      # default max_volume: 10

So you're on ten here, all the way up, all the way up, all the way up,
you're on ten on your guitar. Where can you go from there? Where? Nowhere.
Exactly. What we do is, if we need that extra push over the cliff, you know
what we do?

lib/Badger/Class.pm  view on Meta::CPAN

we're working with temperamental artistes who will be too busy worrying about
the quality of the backstage catering to think about checking their volume
settings before they go on stage.

Thankfully we didn't hard-code the maximum volume but used the C<$MAX_VOLUME>
package variable instead. We can change it directly like this:

    $Amplifier::MAX_VOLUME = 11;

Or using the class L<var()> method (just to show you what the roundabout way
looks like):

    Amplifier->class->var( MAX_VOLUME => 11 );

Either way has the desired effect of changing the default maximum volume
setting without having to go and edit the source code of the module.

The downside to this is that it is an all-encompassing change that will affect
all future instances of C<Amplifier> and any subclasses derived from it that
don't define their own C<max_volume> parameter explicitly.

lib/Badger/Class/Methods.pm  view on Meta::CPAN

                my ($self, $node) = @_;
                print "$NAME: $node";
            }
        }
        else {
            return undef;
        }
    }

The C<auto_can()> method adds C<AUTOLOAD()> and C<can()> methods to your 
class.  The C<can()> method first looks to see if the method is pre-defined
(i.e. it does what the default C<can()> method does).  If it isn't, it then
calls the C<can_view()> method that we've declared using the C<auto_can> 
option (you can call your method C<auto_can()> if you like, but in this 
case we're calling it C<can_view()> just to be different).  The end result
is that you can call C<can()> and it will generate any missing methods on
demand.

    # this calls can_view() which returns a CODE sub 
    my $method = $object->can('view_italic');

lib/Badger/Codec/TT.pm  view on Meta::CPAN

        }
    }

The decoder is very liberal in what it will accept for delimiters. You can mix
and match any of the above styles in the same document if you really want to.
However, you would be utterly batshit insane to do such a thing, let alone
want for it. Just because we'll accept any of the commonly used formats
doesn't mean that you should be using them all at once.

    {
        perl => 'Perl looks like this',
        tt   =  'TT looks like this'
        json: 'JSON looks like this
    }

Note that while the syntax may be more liberal than either Perl or JSON,
the semantics are decidedly stricter.  It is not possible to embed arbitrary
Perl code, instantiate Javascript objects, or do anything else outside of
defining vanilla data structures.

The encoder generates TT syntax by default (C<=> for assignment, with a single
space to delimiter items).  You can change these options using the C<assign>
and C<comma> configuration options.

lib/Badger/Config/Filesystem.pm  view on Meta::CPAN

}



sub has_item {
    my $self = shift->prototype;
    my $name = shift;
    my $item = $self->{ item }->{ $name };

    # This is all the same as in the base class up to the final test which
    # looks for $self->config_file($name) as a last-ditch attempt

    if (defined $item) {
        # A 1/0 entry in the item tells us if an item categorically does or
        # doesn't exist in the config data set (or allowable set - it might
        # be a valid configuration option that simply hasn't been set yet)
        return $item;
    }
    else {
        # Otherwise the existence (or not) of an item in the data set is
        # enough to satisfy us one way or another
        return 1
            if exists $self->{ data }->{ $name };

        # Special case for B::C::Filesystem which looks to see if there's a
        # matching config file.  We cache the existence in $self->{ item }
        # so we know if it's there (or not) for next time
        return $self->{ item }->{ $name }
            =  $self->config_file($name);
    }
}


1;

lib/Badger/Config/Filesystem.pm  view on Meta::CPAN


This performs the initialisation of the object specific to the filesystem
object.

=head2 head($item)

This redefines the L<head()|Badger::Config/head()> method in the
L<Badger::Config> base class.  The method is called by
L<get()|Badger::Config/get()> to fetch a top-level data item
(e.g. C<user> in C<$config-E<gt>get('user.name')>).  This implementation
looks for existing data items as usual, but additionally falls back on a
call to L<fetch($item)> to load additional data (or attempt to load it).

=head2 tail($item, $data)

This is a do-nothing stub for subclasses to redefine.  It is called after
a successful call to L<fetch()>.

=head2 fetch($item)

This is the main method called to load a configuration file (or tree of
files) from the filesystem.  It looks to see if a configuration file
(with one of the known L<extensions> appended, e.g. C<"$item.yaml">,
C<"$item.json">, etc) exists and/or a directory named C<$item>.

If the file exists but the directory doesn't then the configuration data
is read from the file.  If the directory exists

=head2 config_tree($item, $file, $dir)

This scans a configuration tree comprising of a configuration file and/or
a directory.  The C<$file> and C<$dir> arguments are optional and are only

lib/Badger/Data/Facet.pm  view on Meta::CPAN

This module implements a base class validation facet for data types.

=head1 METHODS

=head2 init($config)

Custom initialisation method for data facets. Subclasses may redefine this
method to do something different.  Otherwise the default behaviour is as 
follows.

It first looks for any C<$ARGS> package variables (in the current and any base
classes) which denote the names of mandatory arguments for the data type.

    our $ARGS = ['foo', 'bar'];

It then asserts that each of these is defined in the C<$config> and copies
the value into C<$self>.

Any optional parameters can be specified using the C<$OPTS> package variable.

    our $OPTS = 'baz';              # single string is sugar for ['baz']

lib/Badger/Modules.pm  view on Meta::CPAN

    }

One of the benefits of object oriented programming is that objects of
equivalent types are interchangeable. That means that we should be able to
replace the C<Your::App::Widget> object with a different
implementation as long as it has the same interface in terms of the methods it
implements. In strictly typed programming languages this equivalence is
enforced rigidly, by requiring that both objects share a common base class,
expose the same interface, implement a particular role, or some other
mechanism. In loosely typed languages like Perl, we have to rely on duck
typing: if it looks like a duck, floats like a duck and quacks like a duck
then it is a duck (or is close enough to being a duck for practical purposes).

For example, we might want to use a dummy widget object for test purposes.

    use Your::App::MockObject::Widget;
    
    sub some_code {
        my $widget = Your::App::MockObject::Widget->new;
        $widget->do_something;
    }

lib/Badger/Modules.pm  view on Meta::CPAN

    Your::App + Widget = Your::App::Widget

Then we try camel casing it.  

    Your::App + nice_widget = Your::App::NiceWidget

This allows us to specify names in lower case with underscores separating
words and have them automatically mapped to the correct CamelCase
representation for module names.

Lower case + underscores not only looks nicer (IMHO, YMMV) but can also help
to eliminate problems on filesystems like HFS that are case insensitive by
default. If you're relying on the difference between say, C<CGI> and C<cgi> in
a module name then you're going to have a world of pain the first time you (or
someone else) tries to use that code on a shiny new Mac. And yes, that's me
speaking from personal experience :-)

You may think this is a brain-dead stupid thing to do. You may be right. But
there are brain-dead stupid filesystems out there that we have to accommodate.

=head2 Defining a Badger::Modules Subclass

lib/Badger/Modules.pm  view on Meta::CPAN

This can be used to provide an explicit mapping for module names that may
be requested via the L<module()> method.  The default behaviour is to 
camel case module names that are separated by underscores.  For example, 
requesting a C<foo_bar> module will look for a C<FooBar> module in any of
the L<path> locations.

This work well enough for most modules, but some do not capitalise
consistently. Modules whose names contain acronyms like C<URL> are typically
prone to a dose of fail.

    $module = $modules->module('url');      # looks for XXX::Url not XXX::URL

If you specify the name in the correct capitalisation then you'll have no 
problem.

    $module = $modules->module('URL');

If like me you prefer to use case-insensitive throughout and leave it up to 
the module loader to worry about the correct capitalisation then the C<names>
option is your friend.  You can use to define any number of simple aliases
for the L<module()> method to use.

lib/Badger/Prototype.pm  view on Meta::CPAN

Or without any arguments to get the current message:

    print $object->greeting;            # Hello World

As well as being called as an object method, we want to be able to call it
as a class method:

    Badger::Example->greeting('Hello World');
    print Badger::Example->greeting();  # Hello World

Here's what the C<greeting()> method looks like.

    package Badger::Example;
    use base 'Badger::Prototype';
    
    sub greeting {
        my $self = shift;
        
        # get prototype object if called as a class method
        $self = $self->prototype() unless ref $self;
        

lib/Badger/URL.pm  view on Meta::CPAN


This module implements an object for representing URLs. It can parse existing
URLs to break them down into their constituent parts, and also to generate
new or modified URLs.

The emphasis is on simplicity and convenience for tasks related to web
programming (e.g. dispatching web applications based on the URL, generating
URLs for redirects or embedding as links in HTML pages).  If you want more
generic URI functionality then you should consider using the L<URI> module.

A URL looks like this:

     http://abw@badgerpower.com:8080/under/ground?animal=badger#stripe
     \__/   \______________________/\___________/ \___________/ \____/
      |                |                  |             |          |
    scheme         authority             path         query     fragment

The C<authority> part can be broken down further:

     abw@badgerpower.com:8080
     \_/ \_____________/ \__/

lib/Badger/Utils.pm  view on Meta::CPAN

    LOADED => 2,
};

our $VERSION  = 0.02;
our $ERROR    = '';
our $WARN     = sub { warn @_ };  # for testing - see t/core/utils.t
our $MESSAGES = { };
our $HELPERS  = {       # keep this compact in case we don't need to use it
    'Digest::MD5'        => 'md5 md5_hex md5_base64',
    'Scalar::Util'       => 'blessed dualvar isweak readonly refaddr reftype
                             tainted weaken isvstring looks_like_number
                             set_prototype',
    'List::Util'         => 'first max maxstr min minstr reduce shuffle sum',
    'List::MoreUtils'    => 'any all none notall true false firstidx
                             first_index lastidx last_index insert_after
                             insert_after_string apply after after_incl before
                             before_incl indexes firstval first_value lastval
                             last_value each_array each_arrayref pairwise
                             natatime mesh zip uniq minmax',
    'Hash::Util'         => 'lock_keys unlock_keys lock_value unlock_value
                             lock_hash unlock_hash hash_seed',

lib/Badger/Utils.pm  view on Meta::CPAN

__PACKAGE__->export_any(qw(
    UTILS blessed is_object numlike textlike truelike falselike
    params self_params plural
    odd_params xprintf dotid random_name camel_case CamelCase wrap
    permute_fragments plurality inflect split_to_list extend merge merge_hash
    list_each hash_each join_uri resolve_uri
));

__PACKAGE__->export_fail(\&_export_fail);

# looks_like_number() is such a mouthful.  I prefer numlike() to go with textlike()
*numlike = \&Scalar::Util::looks_like_number;

# it would be too confusing not to have this alias
*CamelCase = \&camel_case;


sub _export_fail {
    my ($class, $target, $symbol, $more_symbols) = @_;
    $DELEGATES ||= _expand_helpers($HELPERS);
    my $helper = $DELEGATES->{ $symbol } || return 0;
    require $helper->[FILE] unless $helper->[LOADED];

lib/Badger/Utils.pm  view on Meta::CPAN

        'You have %s %s in your basket',
        'none, none more'
    );

Please note that this function is intentionally limited.  It's sufficient to
generate simple headings, summary lines, etc., but isn't intended to be
comprehensive or work in languages other than English.

=head3 numlike($item)

This is an alias to the C<looks_like_number()> function defined in
L<Scalar::Util>.

=head3 permute_fragments($text)

This function permutes any optional or alternate fragments embedded in
parentheses. For example, C<Badger(X)> is permuted as (C<Badger>, C<BadgerX>)
and C<Badger(X|Y)> is permuted as (C<BadgerX>, C<BadgerY>).

    permute_fragments('Badger(X)');     # Badger, BadgerX
    permute_fragments('Badger(X|Y)');   # BadgerX, BadgerY

pod/Badger/Changes.pod  view on Meta::CPAN

L<created()|Badger::Filesystem::Path/created()>,
L<accessed()|Badger::Filesystem::Path/accessed()> and
L<modified()|Badger::Filesystem::Path/modified()> methods in
L<Badger::Filesystem::Path>.

Added L<Badger::Logic>.

Added L<Badger::Log> and L<Badger::Log::File>.

Added L<numlike()|Badger::Utils/numlike()> to L<Badger::Utils>
as an alias for C<looks_like_number>.  It's the numerical counterpart
to L<textlike()|Badger::Utils/textlike()> and is significantly easier
to type.

Added L<debug_msg()|Badger::Base/debug_msg()> to L<Badger::Base>. Also added
the C<Badger::Base::Trial> class definition for use with the
L<try()|Badger::Base/try()> method.

Added L<lib|Badger/lib> import hook to L<Badger>.

Added L<bclass|Badger::Class/bclass> as an alias for
L<class|Badger::Class/class> in L<Badger::Class>.

Changed L<Badger::Class::Config> to maintain the order of configuration
parameters where possible (i.e. when expressed as a list ref rather than
a hash ref).  Also added C<target:var> fallback which looks in the target
object or hash ref for the variable.  This allows options to default to
the values set by preceeding options.

Changed L<Badger::Codec::JSON> to use L<JSON::XS> if available.

Mr T is now using Badger 0.06 in production code and is happy to report that
everything seems to be working rather well.  Anyone for a game of tennis?

=head2 Version 0.05 - 23rd December 2008

t/core/utils.t  view on Meta::CPAN


is( xprintf("<1><2| by ? by ?>", 'one', 'two'),
    'one by two by two', 'one by two by two' );


#-----------------------------------------------------------------------
# test we can import utility functions from Scalar::Util, List::Util,
# List::MoreUtils and Hash::Util.
#-----------------------------------------------------------------------

use Badger::Utils 'reftype looks_like_number numlike first max lock_hash';

my $object = bless [ ], 'Badger::Test::Object';
is( reftype $object, 'ARRAY', 'reftype imported' );

ok( looks_like_number 23, 'looks_like_number imported' );
ok( numlike 42, 'numlike imported' );

my @items = (10, 22, 33, 42);
my $first = first { $_ > 25 } @items;
is( $first, 33, 'list first imported' );

my $max = max 2.718, 3.14, 1.618;
is( $max, 3.14, 'list max imported' );

my %hash = (x => 10);

t/misc/badger.t  view on Meta::CPAN

use strict;
use warnings;
use lib qw( ./lib ../lib ../../lib );
use Badger::Test
    tests => 4,
    debug => 'Badger',
    args  => \@ARGV;

use Badger
    lib        => '../../lib',
    Utils      => 'looks_like_number',
    Constants  => 'ARRAY HASH',
    Filesystem => 'FS',
    Codecs     => [codec => 'base64'];

ok(1, 'loaded Badger module');
ok( looks_like_number(23), 'looks_like_number imported from Badger::Utils' );
is( ref [ ], ARRAY, 'ARRAY imported from Badger::Constants' );

my $badger = Badger->new;
ok( $badger, 'created Badger object' );



( run in 0.910 second using v1.01-cache-2.11-cpan-64827b87656 )