ACME-QuoteDB

 view release on metacpan or  search on metacpan

lib/ACME/QuoteDB.pm  view on Meta::CPAN



=head1 USAGE

    use ACME::QuoteDB;

    my $sq = ACME::QuoteDB->new;

    print $sq->get_quote;

    # examples are based on quotes data in the test database. 
    # (see tests t/data/)

    # get specific quote based on basic text search.
    # search all 'ralph' quotes for string 'wookie'
    print $sq->get_quotes_contain({
                  Contain   => 'wookie', 
                  AttrName => 'ralph',
                  Limit     => 1          # only return 1 quote (if any)
           });
    # output:
    I bent my wookie.

lib/ACME/QuoteDB.pm  view on Meta::CPAN


    returns a list of attribution sources defined in the database

    # get list of attribution sources (that have quotes provided by this module)
    print $sq->list_attr_sources;


=head1 LOADING QUOTES

In order to actually use this module, one has to load quotes content,
hopefully this is relativly easy,... (see t/01-load_quotes.t in tests)

=over 4

=item 1 add_quote, one record at a time, probably within an iteration loop

see L</add_quote>

=item 1 (Batch Load) load quotes from a csv file. (tested with comma and tab delimiters)

  format of file must be as follows: (headers)
  "Quote", "Attribution Name", "Attribution Source", "Category", "Rating"
 
  for example:
  "Quote", "Attribution Name", "Attribution Source", "Category", "Rating"
  "I hope this has taught you kids a lesson: kids never learn.","Chief Wiggum","The Simpsons","Humor",9
  "Sideshow Bob has no decency. He called me Chief Piggum. (laughs) Oh wait, I get it, he's all right.","Chief Wiggum","The Simpsons","Humor",8

=item 1 if these dont suit your needs, ACME::QuoteDB::LoadDB is sub-classable, 

  so one can extract data anyway they like and populate the db themselves. 
  (there is a test that illustrates overriding the stub method, 'dbload')

   you need to populate a record data structure:

    $self->set_record(quote  => q{}); # mandatory
    $self->set_record(name   => q{}); # mandatory
    $self->set_record(source => q{}); # optional but useful
    $self->set_record(catg   => q{}); # optional but useful
    $self->set_record(rating => q{}); # optional but useful

   # then to write the record you call

lib/ACME/QuoteDB.pm  view on Meta::CPAN

=begin comment
 
    keep pod coverage happy.

    # Coverage for ACME::QuoteDB is 71.4%, with 3 naked subroutines:
    # Attr
    # Quote
    # Catg
    # QuoteCatg

    pod tests incorrectly state, Attr, Quote and Catg are subroutines, well they
    are,... (as aliases) but act on a different object. 
    
    TODO: explore the above (is this a bug, if so, who's?, version effected, 
    create use case, etc) 
    
=head2 Attr

=head2 Quote

=head2 Catg

lib/ACME/QuoteDB.pm  view on Meta::CPAN

If you don't like this, you can modify Build.PL to not chmod the file and it
will install as 444/readonly, you can also set a chown in there for whoever
you want to have RW access to the quotes db.

Alternativly, one can specify a location to a quotes database (file) to use.
(Since the local mode is sqlite3, the file doesn't even need to exist, just
needs read/write access to the path on the filesystem)

Set the environmental variable:

$ENV{ACME_QUOTEDB_PATH} (untested on windows)

(this has to be set before trying a database load and also (everytime before 
using this module, obviouly)

Something such as:

BEGIN { 
    # give alternate path to the DB
    # doesn't need to exist, will create
    $ENV{ACME_QUOTEDB_PATH} = '/home/me/my_stuff/my_quote_db'

lib/ACME/QuoteDB.pm  view on Meta::CPAN

=item 1 if the database cannot be found, no error is printed!!!

or if you have no write access to it!
"you'll just get 'no attribute can be found,,...", which is cryptic to say
the least!

=item 1 add a dump backup to csv

a backup mechanism for your db to a regular text csv file.

=item 1 clean up tests 'skip if module X' not installed

(one of sqlite3 or mysql is required). currently dies if DBD::SQLite not
installed

=item 1 support multiple categories from LoadDB

how to load multipul categories from a csv file? 
(try to avoid somthing ugly in our csv file format). or maybe don't support
this.

lib/ACME/QuoteDB.pm  view on Meta::CPAN

module-starter --module=ACME::QuoteDB \
        --author="David Wright" --mb --email=david_v_wright@yahoo.com

=head1 ERRATA

    Q: Why did you put it in the ACME namespace?
    A: Seemed appropriate. I emailed modules@cpan.org and didn't get a
       different reaction.

    Q: Why did you write this?
    A: At a past company, a team I worked on a project with had a test suite, 
    in which at the completion of successful tests (100%), a 'wisenheimer' 
    success message would be printed. (Like a quote or joke or the like)
    (Interestingly, it added a 'fun' factor to testing, not that one is needed 
    of course ;). It was hard to justify spending company time to find and 
    add decent content to the hand rolled process, this would have helped.

    Q: Don't you have anything better to do, like some non-trivial work?
    A: Yup

    Q: Hey Dood! why are u uzing Class::DBI as your ORM!?  Haven't your heard 
       of L<DBIx::Class>?
    A: Yup, and I'm aware of 'the new hotness' L<Rose::DB>. If you use this 
       module and are unhappy with the ORM, feel free to change it. 

lib/ACME/QuoteDB/DB/DBI.pm  view on Meta::CPAN


=head1 CONFIGURATION AND ENVIRONMENT

By default, the quotes database used by this module installs in a system path,
which means you'll need to be root (sudo :) to load and modify it.

Alternativly, one can specify a location to a quotes database (file) to use.

Set the environmental variable:

$ENV{ACME_QUOTEDB_PATH} (untested on windows)

(this has to be set before trying a database load and also (everytime) before 
using this module, obviouly)

see L<ACME::QuoteDB>

and

see L<ACME::QuoteDB::LoadDB>

lib/ACME/QuoteDB/LoadDB.pm  view on Meta::CPAN

      # TODO support multi categories
      $self->set_record(catg   => ($self->{category} || $hr->{catg}));
      $self->set_record(rating => ($self->{rating} || $hr->{rating}));
      $self->write_record;
  }
  close $source or carp $!;

  return $self;
}

# sub class this - i.e. provide this method in your code (see test
# 01-load_quotes.t)
sub dbload {
  croak 'Override this. Provide this method in a sub class (child) of this object';
  # see tests: t/01-load_quotes.t for examples
}

sub _to_utf8 {
    my ($self) = @_;

    RECORD:
    foreach my $r (@QUOTE_FIELDS){
        my $val = $self->{record}->{$r};
        if (ref $val eq 'ARRAY'){
         foreach my $v (@{$val}){

lib/ACME/QuoteDB/LoadDB.pm  view on Meta::CPAN

         my $id;
         foreach my $cid (@{$catg_ids}){
           $id =   QuoteCatg->insert({
                 quot_id  => $qid,
                 catg_id  => $cid,
            }) or croak $!;
         }
       }
    }
    # confirmation?
    # TODO add a test for failure
    if ($self->{write_db} and not $attr_id) {croak 'db write not successful'}

    #$self->set_record(undef);
    $self->{record} = {};
    $self->_reset_orig_args;

    if ($self->{write_db}) {
        $self->success(1);
    }

lib/ACME/QuoteDB/LoadDB.pm  view on Meta::CPAN

   
   $load_db->data_to_db;

   if (!$load_db->success){print 'failed'}


=head3 load from any source

If those dont catch your interest, ACME::QuoteDB::LoadDB is sub-classable, 
so one can extract data anyway they like and populate the db themselves. 
(there is a test that illustrates overriding the stub method, 'dbload')

you need to populate a record data structure:

    $self->set_record(quote  => q{}); # mandatory
    $self->set_record(name   => q{}); # mandatory
    $self->set_record(source => q{}); # optional but useful
    $self->set_record(catg   => q{}); # optional but useful
    $self->set_record(rating => q{}); # optional but useful

    # then to write the record you call

lib/ACME/QuoteDB/LoadDB.pm  view on Meta::CPAN

{ file  => '/home/me/data/simpsons_quotes.csv' }

{ dir  => '/home/me/data/*.csv' }
 

=item  file_format - required

can be one of: 'csv', 'tsv', 'custom', or 'html'

if 'html' or 'custom' you must supply the method for parsing. 
(see tests for examples)

example:

{ file_format => 'csv' }


=item  delimiter - optional, default is a comma for csv

csv/tsv options tested: comma(,) and tab(\t)

'html' - not applicable

example:

{ delimiter => "\t" }

=item  category - optional, extracted from data if exists, otherwise will use what you
specify

lib/ACME/QuoteDB/LoadDB.pm  view on Meta::CPAN


=head4 Operation Related Parameters

=over 4

=item  dry_run - optional

do not write to the database. Use with verbose flag to see what would have beed
written.

This can be helpful for testing the outcome of Loading results. 

i.e. like to confirm that the parsing of your data is correct

example:

{
 dry_run => 1,
 verbose => 1
}

=item  verbose  - optional

display to STDOUT what is being done

This can be helpful for testing quotes extraction from file parsing

example:

{verbose => 1}

=item  create_db  - optional (boolean)

L<ACME::QuoteDB::LoadDB> default behaviour is to always assume there is a
database and append new data to that. (It is usually only needed the first 
time one load's data)

lib/ACME/QuoteDB/LoadDB.pm  view on Meta::CPAN

will croak with message if not successful


=head2 dbload

if your file format is set to 'html' or 'custom' you must 
define this method to do your parsing in a sub class.

Load from html is not supported because there are too many 
ways to represt the data. (same with 'custom')
(see tests for examples - there is a test for loading a 'fortune' file format)

One can subclass ACME::QuoteDB::LoadDB and override dbload,
to do our html parsing

=head2 debug_record

dump record (show what is set on the internal data structure) 

e.g. Data::Dumper

lib/ACME/QuoteDB/LoadDB.pm  view on Meta::CPAN

is undef on failure or if trying a L</dry_run>

 
=head2 write_record

takes the data structure 'record' '$self->get_record'
(which must exist). checks if attribution name ($self->get_record('name')) exists, 
if so, uses existing attribution name, otherwsie creates a new one

Load from html is not supported because there are too many 
ways to represt the data. (see tests for examples)

One can subclass ACME::QuoteDB::LoadDB and override dbload,
to do our html parsing

=head2 create_db_tables
 
create an empty quotes database (with correct tables). 

(usually only performed the first time you load data)

lib/ACME/QuoteDB/LoadDB.pm  view on Meta::CPAN

=begin comment
 
    keep pod coverage happy.

    # Coverage for ACME::QuoteDB::LoadDB is 71.4%, with 3 naked subroutines:
    # Catg
    # Quote
    # Attr
    # QuoteCatg

    pod tests incorrectly state, Catg, Quote and Attr are subroutines, well they
    are,... (as aliases) but are imported into here, not defined within
    
    TODO: explore the above (is this a bug, if so, who's?, version effected, 
    create use case, etc) 
   
 
=head2 Attr

=head2 Catg

lib/ACME/QuoteDB/LoadDB.pm  view on Meta::CPAN

If you don't like this, you can modify Build.PL to not chmod the file and it
will install as 444/readonly, you can also set a chown in there for whoever
you want to have RW access to the quotes db.

Alternativly, one can specify a location to a quotes database (file) to use.
(Since the local mode is sqlite3, the file doesn't even need to exist, just
needs read/write access to the path)

Set the environmental variable:

$ENV{ACME_QUOTEDB_PATH} (untested on windows)

(this has to be set before trying a database load and also (everytime) before 
using this module, obviouly)

Something such as:

BEGIN {
    # give alternate path to the DB
    # doesn't need to exist, will create
    $ENV{ACME_QUOTEDB_PATH} = '/home/me/my_stuff/my_quote_db'



( run in 0.882 second using v1.01-cache-2.11-cpan-fe3c2283af0 )