# Using the Leanpub API with Perl

## 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;

#### 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:

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.

#### get_coupon_list()

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

### COMMANDS

• summary

• 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 ]

You have to use option -really with this command.

This command takes the following command options:

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 ]

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

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.

Mathias Weidner