How to upload a perl module on CPAN using Dist::Zilla?

Today, I released my second CPAN module. While writing my first CPAN module, I used Dist::Zilla for uploading my module to CPAN, but when I looked into it today, I had to go through some dzil plugins again. So, I thought that I should write a blog post about this.

Basic directory structured of the module:

Assuming, my module name is X::Y::Z, I need to create a directory with name X-Y-Z. Output of find command should look like this:

lib               # all code goes in lib/
lib/X             # code for X will go here
lib/X/Y           # code for Y will go here
lib/X/Y/Z.pm      # code for Z will go here
t/                # all tests will go here
dist.ini          # explained below

For example : see this module.

Writing dist.ini:

Following is my dist.ini. I’ve pasted it below with comments for explaination (starting with ‘;’).

name = Plack-Middleware-FixMissingBodyInRedirect
version = 0.01
author = Upasana <me@upasana.me>
license = Perl_5
copyright_holder = Upasana

; this plugin gathers all files in a dist,
; if root is not given it defaults to where dist.ini is located
[GatherDir]

; this plugin will add META.yml file to the dist
; META.yml contains information related to dist like abstract, author,
; which modules are required for building & testing the dist,
; which modules are required during runtime,
; pre-requisites etc.
[MetaYAML]

;for removing unwanted files, e.g. files starting a '.'
[PruneCruft]

; This plugin builds Makefile.PL, that uses ExtUtils::MakeMaker
; ExtUtils::MakeMaker create a module Makefile.
; (I found this thing very lengthy & very complicated,
; I've myself not understood it well).
[MakeMaker]

; will create a META.json, for replacing old style META.yml
; META.json contains same information as META.yml, but in JSON format
[MetaJSON]

; adds a LICENSE file containing the full text of dist's license
[License]

; adds a README file to the dist,
; citing the dist's name, version, abstract % license
[Readme]

; This plugin rip apart your kind of POD & reconstructs it as old real POD
; (I really don't know the difference between the two kinds of PODs though)
; By default, it'll modify the files under:
; :InstallModules - .pm/.pod files under lib/ directory
; :ExecFiles - executable files in the distribution
[PodWeaver]

; This plugin will read MANIFEST.SKIP file &
; skip all the files decalred in it, while building the dist
; My MANIFEST.SKIP contains this text:
; ^.*~$
; because I want to skip backup files, which starts with any character,
; but ends with '~'
[ManifestSkip]

; This plugin checks POD coverage of all subs,
; to indicate that some subs should be treated as covered,
; even if no documentation can be found, you can add:
; =for Pod::Coverage sub_name other_sub this_one_too.
; It generates xt/release/pod-coverage.t file
[PodCoverageTests]

; for checking POD syntax
[PodSyntaxTests]

; This plugin runs before a release happens,
; It'll extract to-be-released archive into a temporary directory,
; run all the tests, if tests fail then it'll abort the release,
; else it'll release the dist to CPAN & clean-up the temporary directory
[TestRelease]

; This plugin checks for correct line endings in the dist
; It generates xt/release/eol.t file
[EOLTests]

; This plugin allows the "dzil release" command to upload the dist
; to CPAN. It'll look for PAUSE account configuration in dist.ini
; (I would suggest to use the latter options for saving PAUSE configuration)
; or in ~/.dzil/config.ini in the following format:
; [%PAUSE]
; username = YOUR-PAUSE-ID
; password = YOUR-PAUSE-PASSWORD
; If above configuration doesn't exist, then it'll look into ~/.pause
; in the following format:
; user YOUR-PAUSE-ID
; password YOUR-PAUSE-PASSWORD
; If none of the above exists, then it'll prompt you for PAUSE credentials
[UploadToCPAN]

; This plugin prompts the author whether or not to continue before
; releasing the distribution to CPAN. It gives authors
; a chance to abort before they upload.
; The default is "no", but you can set the environment variable
; DZIL_CONFIRMRELEASE_DEFAULT to "yes" if you just want to hit enter to release.
[ConfirmRelease]

; This plugin will automatically extracts pre-requisties from your modules
; You can manually add pre-requisites by using Prereqs plugin
[AutoPrereqs]

Releasing dist to CPAN:

After writing dist.ini, you should test & build your dist & see if everything is fine in resulted tarball. For testing & building your dist, do

dzil test --all
dzil build

If everything goes well with the above commands, you can release your dist to CPAN, by doing

dzil release

Source:
I’ve taken most of the explanation from PODs of various Dist::Zilla::Plugin::* modules.

DrRacket rocks!

Life after GNOME’s Outreach Program for Women

My previous post was about the end of my OPW internship task. I’ve tried learning so many different things post OPW:

  • Rust language (studied it for a while, then left)
  • Computer networks from Unix Network Programming (Enjoying… )
  • Advanced data structures (Enjoying…)
  • Dancer : I tried learning it, but didn’t get far with it!
  • PSGI/Plack : Still learning it
  • Scheme from SICP : I’m studying SICP from past few weeks now, but at times I got frustrated with it. Reason is that its exercises, examples were taking so much of my time. I told some perl people about it and asked if it’s normal or I’m being too dumb. Then Mark told me that he spent around 3 yrs (from 1998-2000) in studying that book & it’s normal if it’s taking time. So, I decided to read SICP slowly & without getting frustrated.

Oh evil parentheses :'(

Also, it was a bit difficult for me to understand lisp syntax, especially when to use parentheses & when not to use parentheses :D. Then, this person suggested me to try DrRacket mainly for two reasons:

  • DrRacket is used in production
  • It’ll help me understand lisp syntax better.

Parentheses are my new buddies :)

Yes! They’re! Atleast, from past two days.
So, I started learning DrRacket the day before yesterday and till now I’ve found it awesome.
The best thing I liked about DrRacket is that it deals with images the same way it deals with numbers, booleans etc., which really impressed me. Till now, I’ve never seen this feature in any other language.
For example, if you want to animate a falling rocket, then it’s so easy to do it with DrRacket, using this code:

(require 2htdp/image)
(define (create-rocket-scene height)
(place-image rocket 50 height (empty-scene 200 200)))
;You need to right-click on the image, click on the "Copy image" & paste it in DrRacket's definition area.
(require 2htdp/universe)
(animate create-rocket-scene)

This’ll produce a very beautiful animation. You don’t need to install anything apart from DrRacket for doing this B-)
I stole the above example from here

Conquered the monster named “Documentation” :D

I think the title says it all!

It was boring initially :(

Past two weeks were quite boring initially, as I am at the verge of finishing my internship tasks, only some (around 44) boring (boring because I am not getting their cause easily & not able to understand them completely) exceptions & documentation were left.

As far as exceptions are concerned, I have no other way but to write them manually.

Thank you mentor for giving an awesome & interesting idea

I told my mentor about the boredom I am facing & he asked me to write a documentation generator for the exception classes which I have written, to kill that boredom :D.

Exceptions which I have done till now:

Here are the closed issues of exceptions, which i have done.

Documentation generator:

You can see the documentation generator which I have written, here, please read the description of gist to know purpose of each file.

When I started writing documentation generator code first, I was parsing the exception classes’ code for getting the attributes, but my mentor asked me to load the classes instead of parsing. This made my work very very easy. You can see that horrible parsing in the revisions.

Other problem which I faced was due to use of variables in the exception messages & regular expression escape sequences & quantifiers. For getting rid of these two problems, mentor asked me to annotate such tests, you can see it in the gist.

I think that’s all for now!

GNOME’s OPW status update

Past weeks were quite smooth for me except a few last days, so here is the report:

Accomplishments:

This the list of the issues which I have done till now. Most of them are about writing a structured exception for an error, except some selected or better to say horrible ones :D, which I’ll discuss next.

Major accomplishment:
Working on this inline exception. I took around 4-5 days for this. Here is the log of commits related to this exception.

Why it was tough?

  • Reading the generated inline code: Reading and interpreting inline code was a big deal for me, because it had nested if statements, improper indentation etc. Here is a sample of inline code (only first file). Jesse asked me to install Perl::Tidy module, I did and then the code became legible.
  • Understanding how inline code is generated: In Moose, inline code is generated through eval blocks, which are generated in sub _eval_environment (it’s generated in other places also, but for this exception it’s generated in Moose::Meta::Attribute::_eval_environment):
    sub _eval_environment {
        my $self = shift;
    
        my $env = { };
    
        $env->{'$trigger'} = \($self->trigger)
            if $self->has_trigger;
        $env->{'$attr_default'} = \($self->default)
            if $self->has_default;
    
        if ($self->has_type_constraint) {
            my $tc_obj = $self->type_constraint;
    
            $env->{'$type_constraint'} = \(
                $tc_obj->_compiled_type_constraint
            ) unless $tc_obj->can_be_inlined;
            # these two could probably get inlined versions too
            $env->{'$type_coercion'} = \(
                $tc_obj->coercion->_compiled_type_coercion
            ) if $tc_obj->has_coercion;
            $env->{'$type_message'} = \(
                $tc_obj->has_message ? $tc_obj->message : $tc_obj->_default_message
            );
    
            $env = { %$env, %{ $tc_obj->inline_environment } };
        }
    
        $env->{'$class_name'} = \($self->associated_class->name);
    
        # XXX ugh, fix these
        $env->{'$attr'} = \$self
            if $self->has_initializer && $self->is_lazy;
        # pretty sure this is only going to be closed over if you use a custom
        # error class at this point, but we should still get rid of this
        # at some point
        $env->{'$meta'} = \($self->associated_class);
    
        return $env;
    }
    

    In the above code, $env is a HASH reference. We need to initialize key-value pairs of variable names and their values which we want in our inline code. Thank you Jesse for explaining it to me.

  • Making either metaclass object or class name available in the inline code: Using metaclass objects in the inline code leads some problems because of some circular dependencies in Moose. So I was trying to pass class name to the exception, because I can easily get the metaclass object of a class if I know its name.
    Long time ago, my mentor, Shawn asked me to work on this issue. At that time, I didn’t totally understand the reason of making that modification to Moose::Exception::Role::Class, but now I have understood why Shawn asked me to do that. Lines 169 to 187 of this gist has the concerned code.
  • Making existing tests pass: After I made changes in the code, my next task was to make all existing tests pass. Here, I did a mistake, after committing my changes, I didn’t run the test suite, I did it after 1 day (may be because of frustration) :(. I committed a lot of other code in the meantime, so it was hard to tell which commit is the cause of test suite failure. Shawn told me about git bisect, which uses binary search algorithm. It’s a very good tool for debugging (will try to explain in a different post). I used it for getting the commit which was the cause of the test suite failure. Thank you Shawn for telling me about such a good tool.

Next?

I’ll try to finish non-inline exceptions first, because I think my mind can’t cope with sudden transitions from inline to non-inline exceptions and vice-versa :D. After that, I’ll again do horrible inline exceptions :D

Recovering a file in git

While working on Moose, I accidentally deleted the wrong file in git, though code in that file was very small & easy, so I rewrote it and added it to git. But if it would have been a bigger file, then it might not be easy for me to rewrite it, so recovering that file using git is a better option. Here is what I did:

$ git add file1
$ git commit -m "added file1"
$ git rm file1    # I was not supposed to do this
$ git add file2 file3
$ git commit -m "added file2 and file3"
$ git log -p
commit 51f30ce9ce4cbf6573861427f2229c010e6f03a5
Author: Upasana Shukla <me@upasana.me>
Date:   Wed Jul 31 18:13:03 2013 +0530

    added file2 and file3

diff --git a/file1 b/file1
deleted file mode 100644
index 433eb17..0000000
--- a/file1
+++ /dev/null
@@ -1 +0,0 @@
-this is file1
diff --git a/file2 b/file2
new file mode 100644
index 0000000..f138820
--- /dev/null
+++ b/file2
@@ -0,0 +1 @@
+this is file2
diff --git a/file3 b/file3
new file mode 100644
index 0000000..a309e46
--- /dev/null
+++ b/file3
@@ -0,0 +1 @@
+this is file3

commit 6df7df21ca590a751e1e7ac6a423c426ada1cfac
Author: Upasana Shukla <me@upasana.me>
Date:   Wed Jul 31 18:11:22 2013 +0530

    added file1

diff --git a/file1 b/file1
new file mode 100644
index 0000000..433eb17
--- /dev/null
+++ b/file1
@@ -0,0 +1 @@
+this is file1

How to recover file1?

Now I deleted file1, but I need it, so I did this to recover it:

    • I copied file2 & file3 to somewhere else, other than git repository

      $ cp file1 file3 ~/
    • Checked out HEAD^.
      HEAD points to what I have checked out right now i.e. commit with SHA 51f30ce9ce4cbf6573861427f2229c010e6f03a5
      ^ points to commit before HEAD. In this case, ^ is the commit in which I had file1 i.e. commit with SHA 6df7df21ca590a751e1e7ac6a423c426ada1cfac

      $ git checkout HEAD^
      or I can directly checkout the commit with SHA 6df7df21ca590a751e1e7ac6a423c426ada1cfac, like this:

      $ git checkout 6df7df21ca590a751e1e7ac6a423c426ada1cfac

Output of git log -p after executing above commands:

$ git log -p
commit 6df7df21ca590a751e1e7ac6a423c426ada1cfac
Author: Upasana Shukla <me@upasana.me>
Date:   Wed Jul 31 18:11:22 2013 +0530

    added file1

diff --git a/file1 b/file1
new file mode 100644
index 0000000..433eb17
--- /dev/null
+++ b/file1
@@ -0,0 +1 @@
+this is file1
  • Now I want file2 and file3 in my repository, so I did this to add those two files:

    $ mv ~/file2 ~/file3 . #here, '.' is the directory of my git repository
    $ git add file2 file3
    $ git commit "added file2 & file3"

End result:

$ git log -p
commit 646c1b61731c66ca09d63f0a87a2a68785476474
Author: Upasana Shukla <me@upasana.me>
Date:   Wed Jul 31 18:24:19 2013 +0530

    added file2 & file3

diff --git a/file2 b/file2
new file mode 100644
index 0000000..f138820
--- /dev/null
+++ b/file2
@@ -0,0 +1 @@
+this is file2
diff --git a/file3 b/file3
new file mode 100644
index 0000000..a309e46
--- /dev/null
+++ b/file3
@@ -0,0 +1 @@
+this is file3

commit 6df7df21ca590a751e1e7ac6a423c426ada1cfac
Author: Upasana Shukla <me@upasana.me>
Date:   Wed Jul 31 18:11:22 2013 +0530

    added file1

diff --git a/file1 b/file1
new file mode 100644
index 0000000..433eb17
--- /dev/null
+++ b/file1
@@ -0,0 +1 @@
+this is file1

Credits:

Thanks to Jesse, for a super nice explanation.