Table of Contents
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)/im
ages
/title_page.png \
$(DROPBOXDIR)/c
hapter01
.
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>
LICENCE AND COPYRIGHT
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.
- -email_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.
- -coupon_code=code
- 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