<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <title>Damien &quot;dams&quot; Krotkine</title>
    <link rel="alternate" type="text/html" href="http://blogs.perl.org/users/damien_dams_krotkine/" />
    <link rel="self" type="application/atom+xml" href="http://blogs.perl.org/users/damien_dams_krotkine/atom.xml" />
    <id>tag:blogs.perl.org,2009-11-03:/users/damien_dams_krotkine//96</id>
    <updated>2013-02-12T10:33:36Z</updated>
    <subtitle>A blog about the Perl programming language</subtitle>
    <generator uri="http://www.sixapart.com/movabletype/">Movable Type Pro 4.38</generator>

<entry>
    <title>MooX::LvalueAttribute - Lvalue accessors in Moo</title>
    <link rel="alternate" type="text/html" href="http://blogs.perl.org/users/damien_dams_krotkine/2013/02/mooxlvalueattribute---lvalue-accessors-in-moo.html" />
    <id>tag:blogs.perl.org,2013:/users/damien_dams_krotkine//96.4318</id>

    <published>2013-02-12T10:33:36Z</published>
    <updated>2013-02-12T10:33:36Z</updated>

    <summary>cross-posted from dams blog MooX::LvalueAttribute - Lvalue accessors in Moo Yesterday I was reading Joel&apos;s post, where he lists great Perl things he&apos;s seen done lately. Indeed these are great stuff. I was particulary interested by his try at playing...</summary>
    <author>
        <name>Damien &quot;dams&quot; Krotkine</name>
        
    </author>
    
    
    <content type="html" xml:lang="en" xml:base="http://blogs.perl.org/users/damien_dams_krotkine/">
        <![CDATA[<p><i>cross-posted from <a href="http://damien.krotkine.com">dams blog</a></i></p>

<h1>MooX::LvalueAttribute - Lvalue accessors in Moo</h1>

<p>Yesterday I was reading <a href="http://blogs.perl.org/users/joel_berger/2013/02/in-the-name-of-create-great-things-in-perl.html">Joel's
post</a>,
where he lists great Perl things he's seen done lately. Indeed these are great
stuff. I was particulary interested by his try at playing with <a href="https://gist.github.com/jberger/4740303">Lvalue accessors</a>.</p>

<p>I thought that it would be a great exercise to try to implement it in Moo, as
an additional feature, trying to get rid of the <code>AUTOLOAD</code>. Also, I was willing
to avoid doing a <code>tie</code> every time an instance attribute accessor was called.
Surely, I needed to tie only <em>once</em> per instance and per attribute, not each
time the attribute is accessed.</p>

<p>So I started hacking on the code of Moo. Getting rid of the AUTOLOAD was easy,
as I could change the way the accessor generator was, well, generating the,
err, accessors.</p>

<p>Shortly after I started having issues to cache a tied variable. I asked the
all-mighty <a href="https://metacpan.org/author/VPIT">Vincent Pit</a>, and he found a
solution for my tied variables, but more importanlty pointed me to
<a href="https://metacpan.org/module/Variable::Magic">Variable::Magic</a>, which is
faster, more flexible and powerful.</p>

<p>All I needed was to move my hacks in a proper Role, and wrap the whole in a
module, and push it on CPAN. Tadaa, <a href="https://metacpan.org/module/MooX::LvalueAttribute">MooX::LvalueAttribute</a> was born.</p>

<p>In the process I used <a href="http://play-perl.org">play-perl</a> to register my quests,
and exchanged <a href="http://play-perl.org/quest/511800ae94f611130b000025">thoughts with Joel
Berger</a>. I think I'm going
to use this website more, see if it can boost my productivity, and help me
figure out what's really important to do.</p>

<p>On IRC, haarg discovered a bug and recommended to use so-called <em>fieldhashes</em>,
from
<a href="https://metacpan.org/module/Hash::Util::FieldHash::Compat">Hash::Util::FieldHash::Compat</a>.
At the end of the day, I only acted as a glue between different pieces of
knowledges, and that was very satisfactory.</p>

<h2>TL:DR</h2>

<p><a href="https://metacpan.org/module/MooX::LvalueAttribute">MooX::LvalueAttribute</a> is a
module that provides Lvalue attributes:</p>

<pre><code>package App;
use Moo;
use MooX::LvalueAttribute;

<p>has name =&gt; (<br />
  is =&gt; 'rw',<br />
  lvalue =&gt; 1,<br />
);</p>

<p># Elsewhere<br />
my $app = App-&gt;new(name =&gt; 'foo');</p>

<p>$app-&gt;name = 'Bar';</p>

<p>print $app-&gt;name;  # Bar<br />
</code></pre></p>

<p>Enjoy!</p>
]]>
        
    </content>
</entry>

<entry>
    <title>New Perl module: Action::Retry</title>
    <link rel="alternate" type="text/html" href="http://blogs.perl.org/users/damien_dams_krotkine/2013/01/new-perl-module-actionretry.html" />
    <id>tag:blogs.perl.org,2013:/users/damien_dams_krotkine//96.4230</id>

    <published>2013-01-24T13:58:01Z</published>
    <updated>2013-01-25T10:54:28Z</updated>

    <summary>cross-posted from dams blog New Perl module: Action::Retry I&apos;ve just released a new module called Action::Retry. Use it when you want to run some code until it succeeds, waiting between two retries. A simple way to use it is :...</summary>
    <author>
        <name>Damien &quot;dams&quot; Krotkine</name>
        
    </author>
    
    
    <content type="html" xml:lang="en" xml:base="http://blogs.perl.org/users/damien_dams_krotkine/">
        <![CDATA[<p><i>cross-posted from <a href="http://damien.krotkine.com">dams blog</a></i></p>

<h1>New Perl module: Action::Retry</h1>

<p>I've just released a new module called
<a href="https://metacpan.org/module/Action::Retry">Action::Retry</a>.</p>

<p>Use it when you want to run some code until it succeeds, waiting between two
retries.</p>

<p>A simple way to use it is :</p>

<pre><code>use Action::Retry qw(retry);
retry { ... };</code></pre>

<p>And the Object Oriented API:</p>

<pre><code>Action::Retry-&gt;new( attempt_code =&gt; sub { ... } )-&gt;run();
</code></pre>

<p>The purpose of this module is similar to <code>Retry</code>, <code>Sub::Retry</code>, <code>Attempt</code> and
<code>AnyEvent::Retry</code>. However, it's highly configurable, more flexible and has
more features.</p>

<p>You can specify the code to try, but also a callback that will be executed to
check the success or failure of the attempt. There is also a callback to execute code on
failure.</p>

<p>The module also supports different sleep strategies ( Constant, Linear,
Fibonacci...) and it's easy to build yours. Strategies can have their options
as well.</p>

<pre><code>my $action = Action::Retry-&gt;new(
  attempt_code =&gt; sub { ... },
  retry_if_code =&gt; sub { $_[0] =~ /Connection lost/ || $_[1] &gt; 20 },
  strategy =&gt; { Fibonacci =&gt; { multiplicator =&gt; 2000,
                               initial_term_index =&gt; 3,
                               max_retries_number =&gt; 5,
                             }
              },
  on_failure_code =&gt; sub { say "Given up retrying" },
);
$action-&gt;run();
</code></pre>

<p>And the functional API:</p>

<pre><code>use Action::Retry qw(retry);
retry { ... }
retry_if_code =&gt; sub { $_[0] =~ /Connection lost/ || $_[1] > 20 },
strategy =&gt; { Fibonacci => { multiplicator => 2000,
                             initial_term_index => 3,
                             max_retries_number => 5,
                           }
            },
on_failure_code =&gt; sub { say "Given up retrying" };
</code></pre>

<p>Strategies can decide if it's worthwhile continuing trying, or if it should fail.</p>

<p><a href="https://metacpan.org/module/Action::Retry">Action::Retry</a> also supports a
pseudo "non-blocking" mode, in which it doesn't actually sleep, but instead
returns immediately, and won't perform the action code until required time has
elapsed. Basicaly it allows to do this:</p>

<pre><code>my $action = Action::Retry-&gt;new(
  attempt_code =&gt; sub { ... },
  non_blocking =&gt; 1,
  strategy =&gt; { 'Constant' }
);
while (1) {
  # if the action failed, it doesn't sleep
  # next time it's called, it won't do anything until it's time to retry
  $action-&gt;run();

<p>  do_something_else();<br />
  # do something else while time goes on</p>

<p>}<br />
</code></pre></p>

<p>of course <code>do_something_else</code> should be very fast, so that the loop goes back
quickly to retrying the <code>attempt_code</code>.</p>

<p><a href="https://metacpan.org/module/Action::Retry">Action::Retry</a> is based on
<a href="https://metacpan.org/module/Moo">Moo</a> for performance (and because the module
is simple enough to not require Moose). Moo classes properly expand to Moose
ones if needed, so there is no excuse not to use it.</p>

<p>So, please give a try to
<a href="https://metacpan.org/module/Action::Retry">Action::Retry</a>, and let me know
what you think.</p>
]]>
        
    </content>
</entry>

<entry>
    <title>Mass-Testing Dancer&apos;s Plugins</title>
    <link rel="alternate" type="text/html" href="http://blogs.perl.org/users/damien_dams_krotkine/2012/09/mass-testing-dancers-plugins.html" />
    <id>tag:blogs.perl.org,2012:/users/damien_dams_krotkine//96.3854</id>

    <published>2012-09-20T15:51:08Z</published>
    <updated>2012-09-20T15:51:08Z</updated>

    <summary>cross-posted from dams blog Mass-Testing Dancer&apos;s Plugins So, as I said at YAPC::EU 2012, one thing that remains to be done before Dancer 2 can be released : migrating the plugins, making sure they work with it. To be able...</summary>
    <author>
        <name>Damien &quot;dams&quot; Krotkine</name>
        
    </author>
    
    
    <content type="html" xml:lang="en" xml:base="http://blogs.perl.org/users/damien_dams_krotkine/">
        <![CDATA[<p><i>cross-posted from <a href="http://damien.krotkine.com">dams blog</a></i></p>

<h1>Mass-Testing Dancer's Plugins</h1>

<p>So, as I said at YAPC::EU 2012, one thing that remains to be done before Dancer
2 can be released : migrating the plugins, making sure they work with it.</p>

<p>To be able to do that, what's best than an automatic testing facility ?</p>

<p>The goal is to get all Dancer plugins, test them with Dancer1, and Dancer2, and
produce a report, to check which one fails and need fixing.</p>

<h2>Step 1. Get the list of Dancer plugins.</h2>

<p>Easy ! let's use <a href="https://metacpan.org/">Metacpan</a>. After searching, I finally
got a way to get the list of all modules that depend on Dancer. Then filtering
out the ones that don't contain <code>"Plugin"</code> will do the trick.</p>

<pre><code>#!/usr/bin/env perl

<p>use Modern::Perl;<br />
use ElasticSearch;</p>

<p>my $es = ElasticSearch-&gt;new( servers =&gt; 'api.metacpan.org', no_refresh =&gt; 1 );</p>

<p>my $scroller = $es-&gt;scrolled_search(<br />
    query       =&gt; { match_all =&gt; { } },<br />
    search_type =&gt; 'scan',<br />
    scroll      =&gt; '5m',<br />
    index       =&gt; 'v0',<br />
    type        =&gt; 'release',<br />
    size        =&gt; 100,<br />
    filter =&gt; {<br />
            term =&gt; {<br />
                     'release.dependency.module' =&gt; 'Dancer'<br />
                    }<br />
              },</p>

<p>);</p>

<p>my $result = $scroller-&gt;next;</p>

<p>my %plugins;<br />
while ( my $result = $scroller-&gt;next ) {<br />
    $result-&gt;{_source}-&gt;{name} =~ /Dancer-Plugin/<br />
      or next;<br />
    my $name = $result-&gt;{_source}-&gt;{name};<br />
    $name =~ s/-\d.*//;<br />
    $name =~ s/-/::/g;<br />
    $plugins{$name} = 1;<br />
}<br />
say $_ foreach sort keys %plugins;<br />
</code></pre></p>

<p>Cool, let's save this script as <code>get_modules_list.pl</code>.</p>

<h2>Step 2. Prepare two Perl environments</h2>

<p>We want two instance of Perl, without polluting anything. We'll use
<a href="http://www.perlbrew.pl/">perlbrew</a> for that. Easy. The small trick is to have
<code>PERLBREW_ROOT</code> initialized to a local directory to not polute existing <code>~/perl5</code>
installation.</p>

<h2>Step 3. Have a way to test modules</h2>

<p>Well, let's use <a href="http://cpanmin.us/">cpanm</a>, which has an option <code>--test-only</code>
to only test a module without installing it. Oh but plugin modules that we'll
test may require dependances. We'll install them using <code>cpanm --installdeps</code>,
which does just that.</p>

<h2>Step 4. Create a result file</h2>

<p>I was lazy and just output to a <code>.csv</code> text file, but I may store the results
somewhere else later.</p>

<h2>Step 5. Let's glue all that in a Makefile</h2>

<p>The beauty of this is that by writing a proper Makefile, we can install this
auto-tester anywhere. The requirements are minimal : <code>bash</code>, <code>curl</code>, and
<code>perl</code>, with <code>ElasticSearch</code> and <code>Modern::Perl</code> installed.</p>

<p>( This post is intentionally named the same as
<a href="http://babyl.dyndns.org/techblog/entry/test-dancer-plugins">the one from Yannick</a>,
because he proposed a different implementation. We'll try to merge the good
ideas together )</p>

<pre><code>HERE=${PWD}

<p># set up the local Perlbrew location<br />
PERLBREW_DIR=${HERE}/perlbrew<br />
PERLBREW=export PERLBREW_ROOT=${PERLBREW_DIR} \<br />
    &amp;&amp; source ${PERLBREW_DIR}/etc/bashrc \<br />
    &amp;&amp; PERLBREW_ROOT=${PERLBREW_DIR} ${PERLBREW_DIR}/bin/perlbrew</p>

<p># This will run the cpanm from the local Perlbrew, on each of the 2 perls<br />
CPANM_DANCER1=${PERLBREW} exec --with dancer1_plugin_tests ${PERLBREW_DIR}/bin/cpanm<br />
CPANM_DANCER2=${PERLBREW} exec --with dancer2_plugin_tests ${PERLBREW_DIR}/bin/cpanm</p>

<p># loop on the plugin list, install the deps and test each plugin and output result<br />
all: got_curl got_bash got_perlbrew_perl plugins_list<br />
    echo "Plugin name,Pass on Dancer 1,Pass on Dancer 2" &gt; result.csv<br />
    @for i in `cat plugins_list`; do pass_d1=0; pass_d2=0; \<br />
      echo " ---------- TESTING on Dancer 1 : $$i"; \<br />
          ${CPANM_DANCER1} -n --installdeps $$i \<br />
              &amp;&amp; ${CPANM_DANCER1} --test-only $$i &amp;&amp; pass_d1=1; \<br />
      echo " ---------- TESTING on Dancer 2 : $$i"; \<br />
          ${CPANM_DANCER2} -n --installdeps $$i \<br />
              &amp;&amp; DANCER_FORCE_PLUGIN_REGISTRATION=1 ${CPANM_DANCER2} --test-only $$i \<br />
              &amp;&amp; pass_d2=1; \<br />
          echo "$$i,$$pass_d1,$$pass_d2" &gt;&gt; result.csv; \<br />
        done;</p>

<p>clean:<br />
    rm -rf plugins_list</p>

<p>fullclean:<br />
    rm -rf ${PERLBREW_DIR}</p>

<p># Get and install Perlbrew locally<br />
${PERLBREW_DIR}/bin/perlbrew:<br />
    @echo " - creating a local perlbrew"<br />
    export PERLBREW_ROOT=${PERLBREW_DIR} &amp;&amp; curl -kL http://install.perlbrew.pl | bash</p>

<p># Get and install cpanm in the local Perlbrew<br />
${PERLBREW_DIR}/bin/cpanm:<br />
    ${PERLBREW} install-cpanm</p>

<p>got_curl:<br />
    @which curl &gt;/dev/null \<br />
        || ( echo "you don't have curl, please install it and retry" &amp;&amp; false )</p>

<p>got_bash:<br />
    @which bash &gt;/dev/null \<br />
        || ( echo "you don't have bash, please install it and retry" &amp;&amp; false )</p>

<p>got_perlbrew_perl: ${PERLBREW_DIR}/bin/perlbrew ${PERLBREW_DIR}/bin/cpanm perlbrew_dancer1_plugin_tests perlbrew_dancer2_plugin_tests</p>

<p># Build perl from scratch and call this instance dancer1<br />
perlbrew_dancer1_plugin_tests:<br />
    ${PERLBREW} list | grep dancer1_plugin_tests &gt; /dev/null \<br />
        || ( ${PERLBREW} install -j 2 -n perl-5.16.1 --as dancer1_plugin_tests )</p>

<p># Build perl from scratch and call this instance dancer2<br />
perlbrew_dancer2_plugin_tests:<br />
    ${PERLBREW} list | grep dancer2_plugin_tests &gt; /dev/null \<br />
        || ( ${PERLBREW} install -j 2 -n perl-5.16.1 --as dancer2_plugin_tests \<br />
             &amp;&amp; ${CPANM_DANCER2} ${HERE}/../.. )</p>

<p># This gets the list of plugins, as previously described<br />
plugins_list:<br />
    ${HERE}/get_modules_list.pl &gt; plugins_list<br />
</code></pre></p>

<p>It's really cool to see what you can do with modern tools of the Perl ecosystem !</p>
]]>
        
    </content>
</entry>

<entry>
    <title>Paris.pm technical meeting</title>
    <link rel="alternate" type="text/html" href="http://blogs.perl.org/users/damien_dams_krotkine/2012/09/parispm-technical-meeting.html" />
    <id>tag:blogs.perl.org,2012:/users/damien_dams_krotkine//96.3853</id>

    <published>2012-09-20T12:06:00Z</published>
    <updated>2012-09-20T12:06:00Z</updated>

    <summary>cross-posted from dams blog Paris.pm technical meeting ( french version below ) The next Paris.pm technical meeting will happen the 25th september 2012, with: Elizabeth Cholet: Firefox automatization with Perl using AnyEvent, Coro and MozRepl modules Location : 181 avenue...</summary>
    <author>
        <name>Damien &quot;dams&quot; Krotkine</name>
        
    </author>
    
    
    <content type="html" xml:lang="en" xml:base="http://blogs.perl.org/users/damien_dams_krotkine/">
        <![CDATA[<p><i>cross-posted from <a href="http://damien.krotkine.com">dams blog</a></i></p>

<h1>Paris.pm technical meeting</h1>

<p>( french version below )</p>

<p>The next Paris.pm technical meeting will happen the <strong>25th september 2012</strong>, with:</p>

<ul>
<li>Elizabeth Cholet: Firefox automatization with Perl using AnyEvent, Coro and MozRepl modules</li>
</ul>

<p><strong>Location</strong> :
      181 avenue Daumesnil, 75012 Paris, France</p>

<p><strong>Google map</strong> :
      <a href="http://goo.gl/OGUbI">google map</a></p>

<p>If you are interested, and live in Paris, please join us ! If you need more info, ask in the comments.</p>

<hr />

<p>La prochaine réunion technique de Paris.pm se tiendra mardi <strong>25 septembre 2012</strong>, et le programme est :</p>

<ul>
<li>Elizabeth Cholet : l'automatisation de Firefox avec Perl en utilisant AnyEvent, Coro et les modules MozRepl</li>
</ul>

<p><strong>Adresse</strong> :
      181 avenue Daumesnil, 75012 Paris</p>

<p><strong>Google map</strong> :
      <a href="http://goo.gl/OGUbI">google map</a></p>

<p>Si vous êtes intéressé, n'hésitez pas à venir, entrée libre et gratuite. Pour plus d'info, demandez dans les commentaires.</p>

<p>dams.</p>
]]>
        
    </content>
</entry>

<entry>
    <title>MooseX::Singleton is slow</title>
    <link rel="alternate" type="text/html" href="http://blogs.perl.org/users/damien_dams_krotkine/2012/07/moosexsingleton-is-slow.html" />
    <id>tag:blogs.perl.org,2012:/users/damien_dams_krotkine//96.3477</id>

    <published>2012-07-04T14:18:17Z</published>
    <updated>2012-07-04T14:18:17Z</updated>

    <summary>cross-posted from dams blog MooseX::Singleton is slow Just a quick note : if you plan to use MooseX::Singleton, beware ! It is easy to use and it implements properly what it claims, however it is quite slow. If my profilings...</summary>
    <author>
        <name>Damien &quot;dams&quot; Krotkine</name>
        
    </author>
    
    
    <content type="html" xml:lang="en" xml:base="http://blogs.perl.org/users/damien_dams_krotkine/">
        <![CDATA[<p><i>cross-posted from <a href="http://damien.krotkine.com">dams blog</a></i></p>

<h1>MooseX::Singleton is slow</h1>

<p>Just a quick note : if you plan to use
<a href="https://metacpan.org/module/MooseX::Singleton">MooseX::Singleton</a>, beware ! It
is easy to use and it implements properly what it claims, however it is quite
slow.</p>

<p>If my profilings are corrects, each call to <code>-&gt;instance()</code> calls <code>meta()</code>,
<code>get_metaclass_by_name()</code> one time, and <code>blessed()</code> two times.</p>

<p>So for now I'll avoid it and implement a simplified version using something
similar to this :</p>

<pre><code>use Moose;

<p>my $singleton;</p>

<p>sub instance {<br />
    return $singleton //= $CLASS-&gt;new();<br />
}</p>

<p># to protect against people using new() instead of instance()<br />
around 'new' =&gt; sub {<br />
    my $orig = shift;<br />
    my $self = shift;<br />
    return $singleton //= $self-&gt;$orig(@_);<br />
};</p>

<p>sub initialize {<br />
    defined $singleton<br />
      and croak __PACKAGE__ . ' singleton has already been instanciated'; <br />
    shift;<br />
    return __PACKAGE__-&gt;new(@_);<br />
}<br />
</code></pre></p>

<p>dams.</p>
]]>
        
    </content>
</entry>

<entry>
    <title>Dancer is community-driven</title>
    <link rel="alternate" type="text/html" href="http://blogs.perl.org/users/damien_dams_krotkine/2012/06/dancer-is-community-driven.html" />
    <id>tag:blogs.perl.org,2012:/users/damien_dams_krotkine//96.3415</id>

    <published>2012-06-20T14:09:58Z</published>
    <updated>2012-06-20T15:43:12Z</updated>

    <summary>cross-posted from dams blog Dancer is community-driven Long time I haven&apos;t blogged about Dancer. Antelink is a french startup, specializing in Software Life Cycle Management and Open Source Component Detection. They provided us with cool Dancer sourcecode analyzis graphs. Overall...</summary>
    <author>
        <name>Damien &quot;dams&quot; Krotkine</name>
        
    </author>
    
    
    <content type="html" xml:lang="en" xml:base="http://blogs.perl.org/users/damien_dams_krotkine/">
        <![CDATA[<p><i>cross-posted from <a href="http://damien.krotkine.com">dams blog</a></i></p>

<h1>Dancer is community-driven</h1>

<p>Long time I haven't blogged about Dancer.</p>

<p><a href="http://www.antelink.com/">Antelink</a> is a french startup, specializing in
Software Life Cycle Management and Open Source Component Detection. They
provided us with cool Dancer sourcecode analyzis graphs.</p>

<h2>Overall contribution by users</h2>

<p><a href="http://damien.krotkine.com/images/dancer_analysis_by_Antelink-contribution_parts_by_users.png">
<img src="http://damien.krotkine.com/images/dancer_analysis_by_Antelink-contribution_parts_by_users.png" width="376.5" height="162"></a></p>

<h2>.pm files contribution by users</h2>

<p><a href="http://damien.krotkine.com/images/dancer_analysis_by_Antelink-contribution_parts-only_pm-by_users.png">
<img src="http://damien.krotkine.com/images/dancer_analysis_by_Antelink-contribution_parts-only_pm-by_users.png" width="366" height="150"></a></p>

<h2>Pod files contribution by users</h2>

<p><a href="http://damien.krotkine.com/images/dancer_analysis_by_Antelink-contribution_parts-only_pod-by_users.png">
<img src="http://damien.krotkine.com/images/dancer_analysis_by_Antelink-contribution_parts-only_pod-by_users.png" width="366" height="150"></a></p>

<h2>Test files contribution by users</h2>

<p><a href="http://damien.krotkine.com/images/dancer_analysis_by_Antelink-contribution_parts-only_t-by_users.png">
<img src="http://damien.krotkine.com/images/dancer_analysis_by_Antelink-contribution_parts-only_t-by_users.png" width="366" height="150">
</a></p>

<h2>Reading these graphs</h2>

<p>The surfaces represent a mean between the number of commits, and the weight of<br />
modifications contributed (in term of "code line"), only when these are<br />
original content addition. Moving content around isn't counted as active<br />
contribution.</p>

<p>Note that some people are registered twice with different names, I'll try to<br />
post an updated version</p>

<p>What does that demonstrate ? It shows that Dancer is <em>really</em> powered by its
community. Decisions are made together, the code is hacked by multiple hands,
and the management is done in a collegial manner on github.</p>

<p>It's a great reward to be visible on these graphs :)</p>

<h1 id='more_graphs'>More Graphs</h1>

<p><em>UPDATE</em> : Erwan (@Labynocle) sent me new graphics, they show the difference between core devs and contributors. By the ay, Erwan will be at the <a href='http://journeesperl.fr/fpw2012/'>French Perl Workshop</a> in Strasbourg, France.</p>

<p>So here we go :</p>

<h2 id='pm_files_contribution_by_groups'>.pm files contribution by groups</h2>
<a href='http://damien.krotkine.com/images/dancer_analysis_by_Antelink-contribution_parts-only_pm-by_groups.png'>
<img src='http://damien.krotkine.com/images/dancer_analysis_by_Antelink-contribution_parts-only_pm-by_groups.png' height='150' width='366' /></a>
<h2 id='pod_files_contribution_by_groups'>Pod files contribution by groups</h2>
<a href='http://damien.krotkine.com/images/dancer_analysis_by_Antelink-contribution_parts-only_pod-by_groups.png'>
<img src='http://damien.krotkine.com/images/dancer_analysis_by_Antelink-contribution_parts-only_pod-by_groups.png' height='150' width='366' /></a>
<h2 id='test_files_contribution_by_groups'>Test files contribution by groups</h2>
<a href='http://damien.krotkine.com/images/dancer_analysis_by_Antelink-contribution_parts-only_t-by_groups.png'>
<img src='http://damien.krotkine.com/images/dancer_analysis_by_Antelink-contribution_parts-only_t-by_groups.png' height='150' width='366' />
</a>

<p>As a side note, I've proposed 2 talks related to Dancer for
<a href="http://act.yapc.eu/ye2012/">YAPC::EU</a>. One called <em>"Dancer + WebSocket +
AnyEvent + Twiggy"</em>, and an other one mixing <code>Dancer</code>, <code>Log::Message::Structured</code> and <code>Message::Passing</code>.</p>

<p>Let's hope they'll get accepted !</p>
]]>
        
    </content>
</entry>

<entry>
    <title>Cross Posting to blogs.perl.org</title>
    <link rel="alternate" type="text/html" href="http://blogs.perl.org/users/damien_dams_krotkine/2012/04/cross-posting-to-blogsperlorg.html" />
    <id>tag:blogs.perl.org,2012:/users/damien_dams_krotkine//96.3048</id>

    <published>2012-04-05T15:47:33Z</published>
    <updated>2012-09-12T08:52:50Z</updated>

    <summary>cross-posted from dams blog Cross Posting to blogs.perl.org So, a while ago, I moved my blog to github, using jekyll and markdown, with jekyll integration in Emacs. That works great, and I like the fasct that posting a blog entry...</summary>
    <author>
        <name>Damien &quot;dams&quot; Krotkine</name>
        
    </author>
    
    
    <content type="html" xml:lang="en" xml:base="http://blogs.perl.org/users/damien_dams_krotkine/">
        <![CDATA[<p><i>cross-posted from <a href="http://damien.krotkine.com">dams blog</a></i></p>

<h1>Cross Posting to blogs.perl.org</h1>

<p>So, a while ago, I moved <a href="http://damien.krotkine.com">my blog</a> to github, using
<a href="https://github.com/mojombo/jekyll">jekyll</a> and
<a href="http://daringfireball.net/projects/markdown/">markdown</a>, with <a href="http://metajack.im/2009/01/02/manage-jekyll-from-emacs/">jekyll
integration</a> in
<a href="http://www.gnu.org/software/emacs/">Emacs</a>.</p>

<p>That works great, and I like the fasct that posting a blog entry is just a regular git push.</p>

<p>My blog is aggregated in some places, but it doesn't appear on
<a href="http://blogs.perl.org">blogs.perl.org</a>, because it's not an aggregator (and that's
cool, it's not its purpose). But, blogs.perl.org audience is big, and I'm
missing all these potential readers (in improbable case people would actually
be interested in what I have to say :) )</p>

<p>Anyway, so I decided to bite the bullet and write a script that would cross
post my entry to blogs.perl.org. I made the script generic enough to work with
different type of blogs, but here I'm going to explain only the blogs.perl.org
specific case.</p>

<p>blogs.perl.org is a <a href="http://www.movabletype.org/">Movable Type</a> blog engine. A
look on <a href="http://metacpan.org">Metacpan</a> indicates us there is a
<a href="https://metacpan.org/module/Net::MovableType">Net::MovableType</a> module. That's
great, let's use that.</p>

<p>There is a catch: to use this module, you need your login, and your <em>API
password</em>, not your regular password. You can get it easily though, by <a href="https://github.com/davorg/blogs.perl.org/issues/137">following these instructions</a> (thanks to davorg for directing me to it).</p>

<p>To avoid storing the user / password in the script, and to have to pass it on
the command line, let's have a configuration file, that will lie at
<code>~/.crosspost.ini</code>, and that we'll load with
<a href="https://metacpan.org/module/Config::Any::Merge">Config::Any::Merge</a>.</p>

<p>The file name of the original post will have to be passed on the command line,
using <a href="https://metacpan.org/module/Getopt::Long">Getopt::Long</a> and
<a href="https://metacpan.org/module/Path::Class">Path::Class</a> to retrieve and slurp
it.</p>

<p>Here is the configuration file.</p>

<pre><code># file ~/.crosspost.ini
[ main_blog ]
title = dams blog
url = http://damien.krotkine.com

<p>[ blogs.perl.org ]<br />
type = mt<br />
username = foo<br />
password = bar<br />
url = http://blogs.perl.org/rsd.xml<br />
</code></pre></p>

<p>And here's the code:</p>

<pre><code># file crosspost.pl

<p>use Modern::Perl;<br />
use Carp;</p>

<p>use Net::MovableType;<br />
use Config::Any::Merge;<br />
use Getopt::Long;<br />
use Path::Class;<br />
use Text::Markdown qw(markdown);</p>

<p>my $file;<br />
GetOptions ("file=s"   =&gt; \$file)<br />
  or croak "failed to parse command line options";</p>

<p>$file<br />
  or croak "need a file";</p>

<p>$file = file($file);</p>

<p># Get a hash representing the config file<br />
my $cfg = (values %{Config::Any-&gt;load_files({files =&gt; [ $ENV{HOME} . '/.crosspost.ini' ], use_ext =&gt; 1 })-&gt;[0]})[0];</p>

<p># Grab some main info<br />
my $main_blog_title = $cfg-&gt;{main_blog}{title};<br />
my $main_blog_url = $cfg-&gt;{main_blog}{url};</p>

<p>my $text = $file-&gt;slurp;<br />
# In reality, I do some transformation on $text to interpret jekyll specific<br />
# syntax</p>

<p>$text = "\n &lt;i&gt;cross-posted from [$main_blog_title]($main_blog_url)&lt;/i&gt;\n\n" . $text;</p>

<p># So now we have the content to be posted, but it's in markdown. Let's<br />
# transform it in HTML<br />
my $html = markdown($text);</p>

<p># Then we loop on all the other blog to cross post to<br />
foreach my $site (grep { $_ ne 'main_blog'} keys %$cfg) {<br />
    say "posting to $site";<br />
    my %properties = %{$cfg-&gt;{$site}};</p>

<p>    # for now we handle only movable type<br />
    if ($properties{type} eq 'mt') {<br />
        my $username = $properties{username};<br />
        my $password = $properties{password};<br />
        my $url = $properties{url};</p>

<p>        my $mt = Net::MovableType-&gt;new($url);<br />
        $mt-&gt;username($username)<br />
          or croak $mt-&gt;errstr;<br />
        $mt-&gt;password($password)<br />
          or croak $mt-&gt;errstr;<br />
        We need to get the user's blog id<br />
        my $user_blogs = $mt-&gt;getUsersBlogs<br />
          or croak $mt-&gt;errstr;<br />
        $mt-&gt;blogId($user_blogs-&gt;[0]-&gt;{blogid})<br />
          or croak $mt-&gt;errstr;</p>

<p>        # finally, the post<br />
        $mt-&gt;newPost({<br />
                      title       =&gt; $title,<br />
                      description =&gt; $html,<br />
                      mt_allow_comments =&gt; $properties{allow_comments} // 1,<br />
                     },<br />
                     # uncomment this line to directly post it<br />
                     # 1<br />
                    );</p>

<p>    }</p>

<p>}<br />
</code></pre></p>

<p>Missing in this code is some mungling of the text to manage jekyll specific syntax. And while I was at it, I improved it so that it supports cross-posting to twitter, using <a href="https://metacpan.org/module/Net::Twitter">Net::Twitter</a>, and posting only a short description of the entry. Here is an example (I had to create a custom Twitter app called blog-cross-poster)</p>

<p><img src="/images/twitter_cross_post.png" alt="Twitter cross posting" /></p>

<p>Now, I just need to add a git hook to be executed at push time, check if there is a new post or a modified one, and cross post it. Yay !</p>
]]>
        
    </content>
</entry>

<entry>
    <title>Paris.pm january technical meeting</title>
    <link rel="alternate" type="text/html" href="http://blogs.perl.org/users/damien_dams_krotkine/2012/01/parispm-january-technical-meeting.html" />
    <id>tag:blogs.perl.org,2012:/users/damien_dams_krotkine//96.2677</id>

    <published>2012-01-13T17:42:06Z</published>
    <updated>2012-01-13T17:43:38Z</updated>

    <summary>Cross posted from my github blog post...</summary>
    <author>
        <name>Damien &quot;dams&quot; Krotkine</name>
        
    </author>
    
    <category term="perlparismeeting" label="perl paris meeting" scheme="http://www.sixapart.com/ns/types#tag" />
    
    <content type="html" xml:lang="en" xml:base="http://blogs.perl.org/users/damien_dams_krotkine/">
        <![CDATA[<p>Cross posted from my <a href="http://dams.github.com/2012/01/13/parispm-january-technical-meeting.html">github blog post</a></p>]]>
        
    </content>
</entry>

</feed>
