Using the Leanpub API with Perl
Using the Leanpub API with Perl
Mathias Weidner
Buy on Leanpub

Preface

After reading the notice in Google groups from Scott Patten about the Leanpub API, my first thought was: I want to use this with perl.

And then I wrote the perl module.

This book may be used as additional documentation to the perl module.

But it’s main purpose is to test the Perl binding of the Leanpub API.

Using the Perl module

This book describes the Perl module WebService::Leanpub and how I use it to produce books. Using the module it is possible to access the Leanpub API from within Perl or from the command line. It is mainly useful to authors at Leanpub and to those who help them to produce their books.

What is the Leanpub API

The Leanpub API is a web API specified in https://leanpub.com/help/api.

I do need an API key for most of the functions of this API. I can get this key in my dashboard at Leanpub, going to the Account tab and scrolling to the end of the page. Using this key I can work on all my books at Leanpub.

A further essential component is the slug, the part of the URL for your book following https://leanpub.com/. For instance the URL of this book Using the Leanpub API with Perl is https://leanpub.com/using-the-leanpub-api-with-perl and the slug is therefore using-the-leanpub-api-with-perl.

What can I do with it?

Using the API I can

  • create previews for the book, parts of the book or individual files,
  • publish the book,
  • get the book summary information (this is the only function that works without an API key),
  • query for the status of the last job,
  • get the sales data,
  • and create coupons for the book as well as get a list of the coupons.

How do I use the Perl module?

All actions related to one book take place as method calls to an object of type WebService::Leanpub. For this reason I provide the API key and the slug when using the function new() to create such an object.

use WebService::Leanpub;

my $wl = WebService::Leanpub->new($api_key, $slug);

Using this object I call different methods, depending from what I want to do. These methods return the text of the web API in JSON format which I can print or interpret in my program.

Preview

To create a complete or partial preview is really easy, there is a method for each of these tasks. In the files Book.txt and Subset.txt respectively in the Leanpub manuscript directory are the lists of the files to be used for the preview. Leanpub authors know how look like. So I simply have to call

$pv = $wl->preview();

or

$pv = $wl->subset();

with my WebService::Leanpub object.

Creating a preview for an individual file is a little bit more elaborate because I have to send the file to the web API using a POST request.

The following example shows how to open a file, read its content into a scalar variable and send it to the web API.

if (open(my $input, '<', $filename)) {
  local $/;
  undef $/;
  my $content = <$input>;
  close $input;

  $pv = $wl->single({ content => $content });
}

Publish

Publishing a book is easier. I can advise whether the readers get an email and what should be the content of that email. You can find details in the man page of the module.

$pv = $wl->publish( $opt );

Getting the status of the running job

Because I only start the creation of a preview or the publishing process of my book, it may happen that I want to know the state of the affairs. This is as easy as it can be:

$pv = $wl->get_job_status();

Summary of the book

To get a summary of the book I just need to call the function summary().

$pv = $wl->summary();

The web API doesn’t need an API key for this task but the function new() that created my WebService::Leanpub object needed one. If I want to know how the summary looks like without an API key, I have to call WebService::Leanpub->new() using a false API key.

Sales data

There are two methods to get the sales data.

I use one for a summary of the sales data and the other for information about the individual purchases.

$pv = $wl->get_sales_data();

$pv = $wl->get_individual_purchases( { } );

$pv = $wl->get_individual_purchases( { page => 2 } );

The function get_individual_purchases() delivers the data for the last 50 purchases. If I want to get older data, I have to give the page number of the older data. A page contains at most data for 50 purchases. The first page (page => 1) contains the data of the most recent purchases, the higher the page number, the older the sales data.

Coupons

I can create, list and change coupons.

$pv = $wl->create_coupon(\%lopt);

$pv = $wl->get_coupon_list()

$pv = $wl->update_coupon(\%lopt);

Details can be found in the documentation of the module in chapter 2.

I don’t want to program

I don’t need to program - with Perl - anymore, because there is a command line program called leanpub which is included in the distribution of the Perl module. Using this I can utilize the Leanpub API in my Makefiles.

The program uses Getopt::Long and Pod::Usage, so it’s very easy to get a short help text with the command line option -h or --help and the full man page with option -m or --man.

$ leanpub --help
Usage:
     leanpub [options] command [command options]
...

When calling the program I tell the program what to do with the command argument. These command arguments are called like the methods of the Perl module.

I may add some global options before the command and some command specific options after the command. The man page of the program - contained in chapter 3 - goes into the details.

Options:
  -api_key=key
          Provide the Leanpub API key to be used for all actions.
  ...
  -really State that you really intend to do the command (e.g. publish).
  ...
  -slug=your_book
          Provide the book's slug.
  ...

The global options -api_key and -slug are so important that I have to provide them nearly every time I call the program.

Because this is tedious I can provide these in a configuration file called .leanpub.

$ cat .leanpub
# configuration for leanpub
#
api_key = my_api_key_from_leanpub
slug    = using-the-leanpub-api-with-perl

The option `-really’ may look a little bit strange at first.

Really?

I could have named it -do-as-I-say but this seemed a little bit long for me.

This option is only active with the command publish and I need this option for this command. It’s a kind of emergency break so that I do not publish the book unintentionally when it is not ready to be published.

Upshot

I use the module WebService::Leanpub or more precisely the command line program leanpub together with an appropriate Makefile daily when working on my books. It fits seamless into my workflow which consists of an editor window, a shell and a PDF viewer. Using this I avoid interruptions - and possible distractions - from switching to the web browser.

Making it easier with make

When I am writing a book I prefer as little distractions as possible. Therefore I write most of the text with a pencil on paper.

Later I start the editing process with only two or three windows:

  • one window to edit the text,
  • one window to start the Leanpub jobs,
  • one window to look at the generated PDF output, because this is what needs the most tweaking.

Most of the time the first and the second window are combined to one terminal window with two tabs so that I can switch between them with just a keystroke.

I’d like to share some insights about how I setup my environment so that I only need a few keystrokes to create the latest formatted version of my book.

Basic directory structure

I use a Dropbox folder to share my files with Leanpub and won’t go into details about setting this up. The Leanpub documentation will lead you through the process.

Having a new Dropbox folder containing the books files, I start creating my workspace with an empty directory.

In this directory I create a directory named images and two links referring to the subdirectories manuscript and preview of the Dropbox folder.

$ slug=using-the-leanpub-api-with-perl
$ mkdir images
$ ln -s ~/Dropbox/$slug/manuscript .
$ ln -s ~/Dropbox/$slug/preview .

After that I copy all files from manuscript into the current directory and all files from manuscript/images to the directory images

Setup the Makefile

Now that I have everything in place it’s time to setup the Makefile.

Constants

First I define some constants which I will use later in different rules.

DROPBOXDIR = manuscript
DROPBOXFILES = $(DROPBOXDIR)/Book.txt \
               $(DROPBOXDIR)/Sample.txt \
               $(DROPBOXDIR)/Subset.txt \
               $(DROPBOXDIR)/images/title_page.png \
               $(DROPBOXDIR)/chapter01.md \
               $(DROPBOXDIR)/chapter01-empty.md \
	   ...
#

DROPBOXDIR contains the path to the Dropbox folder for this book.

DROPBOXFILES contains a list of all files that belong to that book and have to be copied to the Dropbox folder before starting a job at Leanpub.

Implicit rules

Those constants are followed by some implicit rules how to create certain files. These are mostly copy commands:

$(DROPBOXDIR)/%.md: %.md
cp $< $@

$(DROPBOXDIR)/%.txt: %.txt
cp $< $@

$(DROPBOXDIR)/images/%.png: images/%.png
cp $< $@

$(DROPBOXDIR)/images/%.jpg: images/%.jpg
cp $< $@

These rules just say: if you need a file in $(DROPBOXDIR) and you have a newer file with the same name in this directory, just copy it over to where you need it. The same holds for the files in directory images or code if I happen to include code in my book.

Sometimes I like to have the full table of contents in the sample book but without the text in most of the chapters. To achieve this, I create files named chapter$nr-empty.md automatically from the input:

$(DROPBOXDIR)/%-empty.md: %.md
	grep '^#' $< | grep -v '^###' | sed -e 's/^/\n/' > $@

So, whenever I don’t want the text of a chapter but the titles, I replace chapter$nr.md with chapter$nr-empty.md in Sample.txt and be done.

Explicit targets

Besides the implicit rules I covered above, there are some explicit rules make uses to perform its tasks. These rules are also called targets.

The first explicit target is called all and does nothing. So if I accidentally call make in this directory nothing unexpected occurs. I could printout some help message about useful targets in this Makefile instead.

There are four targets that I use regularly

dropbox: $(DROPBOXFILES)

This is a valid target although it contains no explicit action. This rule makes sure that all necessary files are copied to the Dropbox folder.

partial: dropbox revision.md
        sleep 15 && leanpub partial_preview

This targets depends on the target dropbox so that it first makes sure that all files are copied to the Dropbox folder and then starts its action, namely sleep 15 seconds to allow Dropbox to copy those files to Leanpub and after that starting the generation of a partial preview - using Subset.txt - for the book.

preview: dropbox revision.md
        sleep 15 && leanpub preview

This target does the same for a full preview.

status:
        leanpub job_status

I prefer to use this target to call make status instead of leanpub job_status just to save a few keystrokes.

Appendix

WebService::Leanpub (module)

The module WebService::Leanpub allows the use of the Leanpub API from within Perl to generate previews of your books, publish your books and query your sales data.

This module uses an object-oriented interface. That means, you generate one object which is tied to your book and authorized with your API key and then access the different functions provided by the Leanpub API.

The following text is taken from the original documentation of the module.

NAME

WebService::Leanpub - Access the Leanpub web API.

VERSION

This document describes WebService::Leanpub version 0.3

SYNOPSIS

use WebService::Leanpub;

my $wl = WebService::Leanpub->new($api_key, $slug);

$wl->get_individual_purchases( { slug => $slug } );

$wl->get_job_status( { slug => $slug } );

$wl->preview();

$wl->subset();

$wl->get_sales_data( { slug => $slug } );

DESCRIPTION

INTERFACE

new($api_key, $slug)

Create a new WebService::Leanpub object.

Since you need an API key to access any function of the Leanpub API, you have to give that API key as an argument to new().

The same holds for the slug which is the part of the Leanpub URL denoting your book. For instance if your books URL was https::/leanpub.com/your_book, the slug woud be your_book.

get_individual_purchases()

get_individual_purchases( $opt )

Get the data for individual purchases.

Optionally this method takes as argument a hash reference with this key:

  • page

    the page of the individual purchases data.

get_job_status()

Get the status of the last job.

get_sales_data()

Get the sales data.

partial_preview()

Start a partial preview of your book using Subset.txt.

subset()

Start a partial preview of your book using Subset.txt.

preview()

Start a preview of your book.

single( $opt )

Generate a preview of a single file.

The argument $opt is a hash reference with the following keys and values:

  • content

    The content of the file in a scalar string.

publish( $opt )

This will publish your book without emailing your readers.

The argument $opt is a hash reference with the following keys:

  • email_readers

    If the corresponding value evaluates to true, an email is sent to the readers.

  • release_notes

    The value corresponding to this key is sent as release note.

create_coupon( $opt )

Create a coupon.

The argument $opt is a hash reference with the following keys:

  • coupon_code

    Required. The coupon code for this coupon. This must be unique for the book.

  • discounted_price

    Required. The amount the reader will pay when using the coupon.

  • start_date

    Required. The date the coupon is valid from. Formatted like YYYY-MM-DD.

  • end_date

    The date the coupon is valid until. Formatted like YYYY-MM-DD.

  • has_uses_limit

    Whether or not the coupon has a uses limit.

    Values ‘0’, ‘false’, ‘no’ count as false, all other values as true.

  • max_uses

    The max number of uses available for a coupon. An integer.

  • note

    A description of the coupon. This is just used to remind you of what it was for.

  • suspended

    Whether or not the coupon is suspended.

    Values ‘0’, ‘false’, ‘no’ count as false, all other values as true.

update_coupon( $opt )

Update a coupon.

Takes the same argumentes for $opt) as create_coupon() but only the key coupon_code is required, all others are optional.

get_coupon_list()

Returns a list of the coupons for the book formatted as JSON.

pretty_json( $json )

This is just a convenience function for pretty printing the output of the get_*() functions.

summary()

This returns information about the book.

Since this API function does not need an API key, this is the only method that returns meaningful data if you provide a wrong API key.

DIAGNOSTICS

  • Missing API key for Leanpub

    Since the Leanpub API only works with an API key from leanpub.com, you have to provide an API key as first argument to WebService::Leanpub->new().

  • Missing SLUG for book

    Since every action in the Leanpub API involves a book which is identified by a slug, you have to provide the slug as the second argument to WebService::Leanpub->new().

    A slug is the part after the hostname in the Leanpub URL of your book. For instance the book “Using the Leanpub API with Perl” has the URL https://leanpub.com/using-the-leanpub-api-with-perl and the slug for this book is using-the-leanpub-api-with-perl.

CONFIGURATION AND ENVIRONMENT

WebService::Leanpub requires no configuration files or environment variables.

DEPENDENCIES

None.

INCOMPATIBILITIES

None reported.

BUGS AND LIMITATIONS

No bugs have been reported so far.

Please report any bugs or feature requests through the web interface at http://rt.cpan.org or - if you prefer email - to bug-webservice-leanpub@rt.cpan.org.

AUTHOR

Mathias Weidner <mamawe@cpan.org>

Copyright (c) 2013, Mathias Weidner <mamawe@cpan.org>. All rights reserved.

This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself. See perlartistic.

DISCLAIMER OF WARRANTY

BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE SOFTWARE “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR, OR CORRECTION.

IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

leanpub (CLI)

The command line program allows to use the Leanpub API from within the command line without further programming.

The following is taken from the original documentation.

NAME

leanpub - access the Leanpub web API

VERSION

This document refers to leanpub version 0.3

USAGE

leanpub [options] command [command options]

OPTIONS

  • -api_key=key

    Provide the Leanpub API key to be used for all actions.

  • -help

    Print a brief help message and exit.

  • -manual

    Print the manual page and exit.

  • -really

    State that you really intend to do the command (e.g. publish).

  • -slug=your_book

    Provide the book’s slug.

  • -version

    Print $WebService::Leanpub::VERSION and exit.

COMMANDS

  • summary

    Get information about the book.

  • job_status

    Retrieve the status of the last job.

  • partial_preview

    Start a partial preview of your book using Subset.txt.

  • subset

    Start a partial preview of your book using Subset.txt.

  • preview

    Start a preview of your book.

  • single filename

    Create a preview from a single file.

  • publish [ options ]

    Publish your book.

    You have to use option -really with this command.

    This command takes the following command options:

    • -email_readers

      Email readers, notifying them that the book has been updated.

    • -release_notes=notes

      The release notes to be included in the email to the readers.

  • sales_data

    Retrieve a summary of sales data.

  • individual_purchases [ -page=p ]

    Retrieve data about individual purchases.

    This command takes the option -page to set the page of the individual purchases report to be retrieved.

  • coupons options

    Get a list of coupons available for the book.

  • create_coupon

    Create a new coupon for your book.

    This function takes the following command options:

    • -coupon_code=code

      Required. The coupon code for this coupon. This must be unique for the book.

    • -discounted_price=price

      Required. The amount the reader will pay when using the coupon.

    • -start_date=YYYY-MM-DD

      Required. The date the coupon is valid from.

    • -end_date=YYYY-MM-DD

      The date the coupon is valid until.

    • -has_uses_limit

      Whether or not the coupon has a uses limit.

    • -max_uses=uses

      The max number of uses available for a coupon. An integer.

    • -note=note_for_me

      A description of the coupon. This is just used to remind you of what it was for.

    • -suspended

      Whether or not the coupon is suspended.

  • update_coupon options

    Update a coupon.

    This command takes the same argumentes as create_coupon but only the option ­-coupon_code is required, all others are optional.

DESCRIPTION

This program interacts with the Leanpub API. You can find details about this API at https://leanpub.com/help/api.

The slug is the part of the URL for your book coming after https://leanpub.com/. For instance if your book is found at https://leanpub.com/your_book, then the slug for your book is your_book.

FILES

CONFIGURATION

This program searches in the current working directory and all directories above for a text file named .leanpub.conf. It reads these files and adds configuration directives which are not set so far to its configuration.

The format of the file is rather simple. It is just a key and a value separated by an equal sign and optional whitespace. Valid keys are the names of the global options without any minus or plus sign. For instance I have a file containing something like:

# configuration for leanpub
#
api_key = my_api_key_from_leanpub
slug    = using-the-leanpub-api-with-perl

in the directory I am developing this module in. So I don’t have to provide the options -api_key and -slug to test this script. When I use the script for more than one book, I place a file called .leanpub.conf containing the API key further up and have only the SLUG in the files located in the book directories. To use a different API key I would write it in the file in the book directory so that the one further up would not be used.

AUTHOR

Mathias Weidner