Beefy Boxes and Bandwidth Generously Provided by pair Networks
go ahead... be a heretic

Get your Daily Tao Te Ching fix

by radiantmatrix (Parson)
on Sep 21, 2006 at 20:56 UTC ( #574265=CUFP: print w/replies, xml ) Need Help??

I'm a big fan of the Tao te Ching, and after finding a free version (called GNL), I decided to write a tool that would give me a new chapter each day I ran it, in order, and start over when I reached the end.

I personally use this in a cron job to change my /etc/motd message.

When run for the first time with either the --daily or --chapter parameters, it will retrieve a copy of the GNL from the web, parse it into its chapters (saving the license to 'license.txt'), and store a YAML cache of that work. If you're running from behind a proxy, you can use some other proxy-enabled tool to save the GNL from the link in the first paragraph to a file named 'taote-v4.html' -- when put in the exec directory, the app will prefer that local copy.

--today causes the program to print the next chapter of the TTC if it's not the same day as the last time it was run.

--chapter lets you get a specific chapter (see usage for details)

To clean up and start fresh, use the --reset parameter.

Run without parameters or with '--usage', '--help', '-h', or '-?' for a usage message.

Depends on CPAN modules YAML-Syck (fast libsyck) or YAML (slower, pure-perl), and HTML-TokeParser-Simple (for parsing the HTML version of GNL). The last dependency is only needed for parsing the HTML document -- it is not needed once tao.yaml exists.

#!/usr/local/bin/perl use strict; use warnings; BEGIN { # Try to load YAML::Syck (faster, preferred), fall back to YAML othe +rwise # thanks to PerlMonks' shmem for pointing out potential YAML::Syck t +rouble eval { require YAML::Syck; YAML::Syck->import( qw[LoadFile DumpFile] ); }; if ($@) { # fall back to YAML require YAML; YAML->import( qw[LoadFile DumpFile] ); } } use Getopt::Long; # how do we want to operate? usage() unless @ARGV; GetOptions( 'help|usage|?|h' => \&usage, 'reset' => sub { for (<*.yaml>) { unlink while -f } }, 'today' => \&daily_tao, 'chapter' => \&my_tao, 'license' => sub { print join('',<DATA>); exit; }, ); #===================================================================== +=== # load_tao - loads or regenerates cache of tao chapters, returns as li +st sub load_tao { if ( -f 'tao.yaml') { # load chapters from disk cache, if we have them return @{ LoadFile('tao.yaml') }; } else { # otherwise, get them from the web and build the cache return @{ get_web_tao() }; } } #===================================================================== +=== # daily_tao - shows a new dao chapter if the date has advanced by at # least one day since the last run. Uses 'day.yaml' for # cache. sub daily_tao { my @chapter = load_tao(); my $day_index = 0; my $time = time; my $now = $time; # load last run info, if any. if ( -f 'day.yaml' ) { ($day_index, $time) = @{ LoadFile('day.yaml') } } my @now = localtime($now); my @last = localtime($time); if ( $last[7] != $now[7] || $last[5] != $now[5]) { # it's not the same day as last we ran $day_index++; if ($day_index > $#chapter) { $day_index = 0 } # roll over $time = $now; # we will record the *current* time } printf "%s: %s\n%s", @{ $chapter[$day_index] }; DumpFile('day.yaml', [ $day_index, $time ]); } #===================================================================== +=== # my_tao - shows the chapter specified on the command line ($ARGV[0]) # with no parameter, shows the first chapter # with a numeric parameter, shows chap. for that record numbe +r # with a paramter ending in '.', shows record with that chap. + id # Dies if there's an invalid chap. id or spec. sub my_tao { my @chapter = load_tao(); my $chapter = (@ARGV ? shift @ARGV : 0); if ($chapter =~ /\./) { # this is a chapter spec my $chindex = 0; for (0..$#chapter) { $chindex = $_; last if "$chapter[$_][0]." eq $chapter; } die "$chapter is not a valid chapter spec\n" if "$chapter[$chindex][0]." ne $chapter; $chapter = $chindex; } elsif ($chapter =~ /\D/) { # doesn't look like a spec, but isn't just a number - invalid die "'$chapter' is neither a valid spec nor a valid numeric id\n"; } die "The last record is no.$#chapter, but you asked for no.$chapter\ +n" if $chapter > $#chapter; printf "%s: %s\n%s", @{$chapter[$chapter]}; } #===================================================================== +=== # get_web_tao - retrieves the "GNL" tao te ching from the web and # creates the disk cache of the chapters in 'tao.yaml' # if a local file 'taote-v4.html' exists, will use that # instead of fetching from the web sub get_web_tao { require HTML::TokeParser::Simple; my $p = HTML::TokeParser::Simple->new( -f 'taote-v4.html' ?(file => 'taote-v4.html') :(url => ' +v4.html') ); # skip notices and such, but record them to license.txt my $LIC; while (my $token = $p->get_token) { last if $token->is_end_tag('h2'); if ($token->is_start_tag('pre')) { # start of license text and copyright notice open $LIC, '>license.txt' or die ("Can't write license.txt: $!") +; } elsif ($token->is_end_tag('pre')) { # end of license text and copyright notice close $LIC; undef $LIC; } elsif ($token->is_text && defined $LIC) { # body text for license and copyright print $LIC $token->as_is; } } # add chapters to data struct in form [id, title, text] my @chapter; while (my $token = $p->get_token) { next unless $token->is_start_tag('h3'); #title start (my $title = $p->peek(1)) =~ s{^(.+?)\.\s}{}g; my $num = $1; my $text = ''; #finish end of title while ($token = $p->get_token) { last if $token->is_end_tag('h3') +} #grab text *as* text while ($token = $p->get_token) { last if $p->peek =~ /<h3>/i; next if $token->is_tag; $text.=$token->as_is; } push @chapter, [ $num, $title, $text ]; } DumpFile('tao.yaml', \@chapter); return \@chapter; } #===================================================================== +=== # usage - prints a nice usage message and exits the app sub usage { print <<USAGE_DOC; Daily Tao te Ching chapter printer. Uses the GNL version of the Tao Te Ching (TTC) from: See the file license.txt after first run for the GNL license. $0 [--today] [--chapter id|spec] [--reset] $0 --license $0 [--help] [--usage] [-h] [-?] --chapter Display a specific chapter from the TTC, either by recor +d id (0-81) or by chapter number followed by a period. For ex +ample, to see TTC chapter 64b, use --chapter 64b. (including th +e period). Displays the last chapter if an invalid chapte +r number is specified. --today If the date has advanced at least one day since the last + time we got a new chapter, display the next chapter from the +TTC. --reset Delete all the disk caches: this will cause the next run + (or this run, if followed by one of the other options) to st +art at the beginning of the TTC and fetch a new copy from th +e web (or from the local file taote-v4.html) --license Display the license for this application (license for th +e GNL text is saved to license.txt once it is retrieved) --usage Display this message: aliases are --help, -h, and -? USAGE_DOC exit; } __DATA__ Copyright (c) 2006 RadiantMatrix ( Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or s +ell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be include +d in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRES +S OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILIT +Y, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHAL +L THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISIN +G FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. ((See license.txt once the GNL has been retrieved for the GNL license +terms))


  • 2004-09-22 : shmem noted that YAML::Syck might be problematic for some. Updated code to prefer YAML::Syck, but fall back on YAML if Syck is not installed.

A collection of thoughts and links from the minds of geeks
The Code that can be seen is not the true Code
I haven't found a problem yet that can't be solved by a well-placed trebuchet

Replies are listed 'Best First'.
Re: Get your Daily Tao Te Ching fix
by shmem (Chancellor) on Sep 22, 2006 at 13:01 UTC
    Great. radiantmatrix++ - this reminds me of the 'ching' utility way back in SunOS4.1.3 ;-)


    PS: YAML::Syck seems currently (v 0.67) broken, though. On my box it just segfaults during the tests. But YAML just works, too, this script not being too speed critical ;-) - perl -pi -e 's/::Syck//g' and done.

    _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                  /\_¯/(q    /
    ----------------------------  \__(m.====·.(_("always off the crowd"))."·
    ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}

      Huh, I haven't had any trouble with the current YAML::Syck on Win32 or OS-X. Haven't tried it on Linux or BSD, yet, but you'd think if it builds on OS-X...

      In any case, I think I will add some code to try YAML::Syck, and then fall back to the usual YAML if I can't find the Syck version. Unforutnately, that means I will have to import LoadFile and DumpFile, polluting my beautiful, beautiful namespace -- you bastard. ;-)

      A collection of thoughts and links from the minds of geeks
      The Code that can be seen is not the true Code
      I haven't found a problem yet that can't be solved by a well-placed trebuchet
Re: Get your Daily Tao Te Ching fix
by OfficeLinebacker (Chaplain) on Sep 30, 2006 at 04:06 UTC
    So I guess you could probably adapt that to use any book that is on Project Gutenberg? Personally I'd like to do it with a book like How to Win Friends and Influence People but I bet you that, for example, Marcus Aurelius' Meditations is in the public domain.


    I like computer programming because it's like Legos for the mind.

Re: Get your Daily Tao Te Ching fix
by chanio (Priest) on Nov 12, 2006 at 16:44 UTC


    I made it more suitable to launch in Windows...

    (more graphical)


    As remembered later by radiantmatrix adding the following code will break the 'minimalistic' style required for adding it to cookies or other pieces of text that people use to do. It even makes the text longer... So, use it at your own discretion. Or add it as a second pipe like...
    cat header.txt $(perl --today)
    :) I'm sorry, radiantmatrix ... It wasn't my intention to change the spirit of your script. I should have noticed your purpose of leaving the output so alone :). I was only wanting to see it more friendly. And didn't imagine it inside another thing...
    I changed the help's heading...
    __.....-.. +-+-+-+-+-+ +-+-+-+ +-+-+ +-+-+-+-+-+ _,' `\ `-. |D|a|i|l|y| |T|a|o| |t|e| |C|h|i|n|g| / /['\ | \ +-+-+-+-+-+ +-+-+-+ +-+-+ +-+-+-+-+-+ .' `.:/ .' L O-----------------------------------O | ,' | | Tao Te Ching (TTC) from: | | ,---'' | | | | ,' | | ~phalsall/texts/taote-v4.html | `. | ,. / | | \ ` ,.' / | See the file license.txt after | '` _,' | first run for the GNL license. | '--. _..-' O-----------------------------------O
    And added a --B-today option to show with a banner as heading... With tkmore!
    sub daily_tao { my $banner = shift;## NEW OPTION TO ADD A HEADER WITH THE TITLE ... print <<HEAD if ($banner); __.....-.. _,' `\ `-. / /['\ | \ +-+-+-+-+-+ +-+-+-+ .' `.:/ .' . |D|a|i|l|y| |T|a|o| | ,' | +-+-+ +-+-+-+-+-+ | ,---'' | |T|e| |C|h|i|n|g| | ,' | +-+-+-+-+-+-+-+ +-+ `. | ,. / |p|r|i|n|t|e|r| |@| \ ` ,.' / +-+-+-+-+-+-+-+ +-+ '` _,' '--. _..-' HEAD printf "%s: %s\n%s", @{ $chapter[$day_index] }; ...
    And the option...
    ... 'today' => \&daily_tao, 'B-today' => \&daily_tao(1), ## ADDS A BANNER ...
    Then, I could also call it...
    perl --B-today | tkmore -
    Or in LINUX...
    perl --B-today | perl -e'while (<>){print($_,$/) if ($_=~s/^/ +/ && $_=~s/[\r\n]//g)}'| Xdialog --rc-file ~/.ocrebox.rc --title="To +day's TAO" --textbox - 0 0 &
    Thank you for your nice script!

    I adjusted the ASCII-art to display well in my non monospaced screen....

      Hey, some cool additions... but the point of not jazzing it up like that is to follow the UNIX methodology -- that is, to create a program with output that others can easily consume.

      I'd rather see you either (a)abstract my script into a module and then create two scripts -- one that uses your enhancements, and one that doesn't; or (b)create a wrapper or pipe target for the output of my script to add your enhancements.

      Honestly, I'd prefer option (a), as it would be trivial, then, to create GUI front-ends and the like. Maybe I'll take that on, if you don't...

      Ramblings and references
      The Code that can be seen is not the true Code
      I haven't found a problem yet that can't be solved by a well-placed trebuchet

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: CUFP [id://574265]
Approved by GrandFather
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others pondering the Monastery: (8)
As of 2018-06-19 14:28 GMT
Find Nodes?
    Voting Booth?
    Should cpanminus be part of the standard Perl release?

    Results (114 votes). Check out past polls.