Acme-CPANModulesBundle-Import-MojoliciousAdvent-2018

 view release on metacpan or  search on metacpan

devdata/https_mojolicious.io_blog_2018_12_03_higher-order-promises_  view on Meta::CPAN


<h3>All</h3>

<p>An <code>all</code> promise resolves only when all of its Promises also resolve. If one of them is rejected, the <code>all</code> Promise is rejected. This means that the overall Promise knows what to do if one is rejected and it doesn&#39;t need ...

<pre><code>use Mojo::Promise;
use Mojo::UserAgent;
my $ua = Mojo::UserAgent-&gt;new;

my @urls = ( ... );
my @all_sites = map { $ua-&gt;get_p( $_ ) } @urls;
my $all_promise = Mojo::Promise
  -&gt;all( @all_sites )
  -&gt;then(
    sub { say &quot;They all worked!&quot; },
    sub { say &quot;One of them didn&#39;t work!&quot; }
    );
</code></pre>

<p>The Promises aren&#39;t required to do their work in any order, though, so don&#39;t base your work on that.</p>

<h3>First come, first served</h3>

<p>A &quot;race&quot; resolves when the first Promise is no longer pending and after that doesn&#39;t need the other Promises to keep working.</p>

<pre><code>use Mojo::Promise;
use Mojo::UserAgent;
my $ua = Mojo::UserAgent-&gt;new;

my @urls = ( ... );
my @all_sites = map { $ua-&gt;get_p( $_ ) } @urls;
my $all_promise = Mojo::Promise
  -&gt;race( @all_sites )
  -&gt;then(
    sub { say &quot;One of them finished!&quot; },
    );
</code></pre>

<h3>Any</h3>

<p>An &quot;any&quot; Promise resolves immediately when the first of its Promises resolves. This is slightly different from <code>race</code> because at least one Promise must resolve. A Promise being rejected doesn&#39;t resolve the <code>any</code>...

devdata/https_mojolicious.io_blog_2018_12_03_higher-order-promises_  view on Meta::CPAN

use Mojo::Promise;
use Mojo::Promise::Role::HigherOrder;
use Mojo::UserAgent;
use Mojo::URL;

use lib catfile( $ENV{HOME}, &#39;.cpan&#39; );
my @mirrors = eval {
  no warnings qw(once);
  my $file = Mojo::URL-&gt;new( &#39;index.html&#39; );
  require CPAN::MyConfig;
  map { say &quot;1: $_&quot;; $file-&gt;clone-&gt;base(Mojo::URL-&gt;new($_))-&gt;to_abs }
    $CPAN::Config-&gt;{urllist}-&gt;@*
  };

die &quot;Did not find CPAN::MyConfig\n&quot; unless @mirrors;
my $ua = Mojo::UserAgent-&gt;new;

my @all_sites = map {
  $ua-&gt;get_p( $_ )-&gt;then( sub ($tx) {
      die unless $tx-&gt;result-&gt;body =~ /Jarkko/ });
  } @mirrors;
my $any_promise = Mojo::Promise
  -&gt;with_roles( &#39;+Any&#39; )
  -&gt;any( @all_sites )
  -&gt;then(
    sub { say &quot;At least one of them worked!&quot; },
    sub { say &quot;None of them worked!&quot; },
    );

devdata/https_mojolicious.io_blog_2018_12_03_higher-order-promises_  view on Meta::CPAN

use Mojo::Promise;
use Mojo::Promise::Role::HigherOrder;
use Mojo::UserAgent;
use Mojo::URL;

use lib catfile( $ENV{HOME}, &#39;.cpan&#39; );
my @mirrors = eval {
  no warnings qw(once);
  my $file = Mojo::URL-&gt;new( &#39;index.html&#39; );
  require CPAN::MyConfig;
  map { say &quot;1: $_&quot;; $file-&gt;clone-&gt;base(Mojo::URL-&gt;new($_))-&gt;to_abs }
    $CPAN::Config-&gt;{urllist}-&gt;@*
  };

die &quot;Did not find CPAN::MyConfig\n&quot; unless @mirrors;
my $ua = Mojo::UserAgent-&gt;new;

my $count = 2;
my @all_sites = map {
  $ua-&gt;get_p( $_ )-&gt;then( sub ($tx) {
      die unless $tx-&gt;result-&gt;body =~ /Jarkko/ });
  } @mirrors;
my $some_promise = Mojo::Promise
  -&gt;with_roles( &#39;+Some&#39; )
  -&gt;some( \@all_sites, 2 )
  -&gt;then(
    sub { say &quot;At least $count of them worked!&quot; },
    sub { say &quot;None of them worked!&quot; },
    );

devdata/https_mojolicious.io_blog_2018_12_03_higher-order-promises_  view on Meta::CPAN


use Mojo::Promise;
use Mojo::Promise::Role::HigherOrder;

my @urls = qw(
  https://www.learning-perl.com/
  https://www.perl.org/
  https://perldoc.perl.org/not_there.pod
  );

my @all_sites = map {
  my $p = $ua-&gt;get_p( $_ );
  $p-&gt;then( sub ( $tx ) {
    $tx-&gt;res-&gt;code == 404 ? $tx-&gt;req-&gt;url : die $tx-&gt;req-&gt;url
    } );
  } @urls;

my $all_promise = Mojo::Promise
  -&gt;with_roles( &#39;+None&#39; )
  -&gt;none( @all_sites )
  -&gt;then(

devdata/https_mojolicious.io_blog_2018_12_05_compound-selectors_  view on Meta::CPAN

</code></pre>

<p>Now, consider how much work I&#39;ve done there. Almost nothing. I made a DOM object, applied a selector, and I&#39;ve isolated parts of the data. This is the same thing I was doing the hard way before. This way is better and isn&#39;t more work. ...

<h2>How about those new emojis?</h2>

<p>While writing about the <a href="https://www.effectiveperlprogramming.com/2018/08/find-the-new-emojis-in-perls-unicode-support/">Unicode 9 updates in Perl v5.26</a>, I wondered what I could show that might be interesting. How about figuring out wh...

<p>My first attempt simply trawled through every character and compared the various Unicode properties to see which code numbers changed from <code>Unassigned</code> to <code>Present_In</code>. That was fine, but then I found that someone was already...

<p>I won&#39;t explain everything in this program. Trust me that it uses <a href="https://mojolicious.org/perldoc/Mojo/UserAgent">Mojo::UserAgent</a> to fetch the data, extracts the DOM, and finds the text I want by using the compound selector <code>...

<pre><code>use v5.28;
use utf8;
use strict;
use warnings;
use open qw(:std :utf8);
use charnames qw();

use Mojo::UserAgent;
my $ua = Mojo::UserAgent-&gt;new;

my $url = &#39;https://blog.emojipedia.org/new-unicode-9-emojis/&#39;;
my $tx = $ua-&gt;get( $url );

die &quot;That didn&#39;t work!\n&quot; if $tx-&gt;error;

say $tx-&gt;result
    -&gt;dom
    -&gt;find( &#39;ul:not( [class] ) li a&#39; )
    -&gt;map( &#39;text&#39; )
    -&gt;map( sub {
        my $c = substr $_, 0, 1;
        [ $c, ord($c), charnames::viacode( ord($c) ) ]
        })
    -&gt;sort( sub { $a-&gt;[1] &lt;=&gt; $b-&gt;[1] } )
    -&gt;map( sub {
        sprintf &#39;%s (U+%05X) %s&#39;, $_-&gt;@*
        } )
    -&gt;join( &quot;\n&quot; );
</code></pre>

<p>This makes a nice list that starts like this:</p>

<pre><code>🕺 (U+1F57A) MAN DANCING
🖤 (U+1F5A4) BLACK HEART
🛑 (U+1F6D1) OCTAGONAL SIGN

devdata/https_mojolicious.io_blog_2018_12_05_compound-selectors_  view on Meta::CPAN

    &lt;tr&gt;&lt;th&gt;ID&lt;/th&gt;&lt;th&gt;Name&lt;/th&gt;&lt;th&gt;Score&lt;/th&gt;&lt;/tr&gt;

    &lt;tr&gt;&lt;td&gt;1&lt;/td&gt; &lt;td&gt;Nibbler&lt;/td&gt; &lt;td&gt;1023&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;27&lt;/td&gt;&lt;td&gt;Scruffy&lt;/td&gt; &lt;td&gt;39&lt;/td&gt;  &lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;5&lt;/td&gt; &lt;td&gt;Zoidberg&lt;/td&gt;&lt;td&gt;5834&lt;/td&gt;&lt;/tr&gt;
    &lt;/table&gt;
    HTML

my @scores = Mojo::DOM-&gt;new( $html )
    -&gt;find( &#39;table.scores &gt; tr &gt; td:last-child&#39; )
    -&gt;map( &#39;text&#39; )
    -&gt;each
    ;

my $grand = sum( @scores );
say &quot;Grand total: $grand&quot;;
</code></pre>

<p><em style="font-size: 10px">
Editor&#39;s note: Unfortunately this example breaks our syntax highlighter. This is the site&#39;s fault not the author. We&#39;re trying to find a better way to render it short of rewriting the rendering engine.
</em></p>

devdata/https_mojolicious.io_blog_2018_12_07_openapi_  view on Meta::CPAN

<h3>The MetaCPAN Specification</h3>

<p>The entire specification doesn’t need to be complete in order to get OpenAPI up and running. When documenting an existing API, it’s possible to with one portion of the API. With MetaCPAN we started with the search endpoints.</p>

<p>The <a href="https://github.com/metacpan/metacpan-api/blob/master/root/static/v1.yml">spec file can be viewed here</a> and the <a href="https://fastapi.metacpan.org/static/index.html">API documentation here</a></p>

<h2>Further Reading</h2>

<p>The <a href="https://github.com/OAI/OpenAPI-Specification">OpenAPI Specification repository</a> includes full documentation and many examples of varying levels of details.</p>

<p>The <a href="https://openapi-map.apihandyman.io">OpenAPI Map</a> is an interactive site to aid in working with the OpenAPI Specification.</p>

              </section>
              <small><p>Photo by <a href="https://unsplash.com/photos/GJao3ZTX9gU">Cytonn Photography on Unsplash</a></p>
</small>

              <p class="tags">
                <span>Tagged in </span>:
                  <a href="/blog/tag/advent/">advent</a>,
                  <a href="/blog/tag/development/">development</a>,
                  <a href="/blog/tag/openapi/">openapi</a>,

devdata/https_mojolicious.io_blog_2018_12_12_dancer-and-minion_  view on Meta::CPAN

we added code to prevent us from adding code to a queue that didn&#39;t exist:</p>

<pre><code>my @QUEUE_TYPES = qw( default InstantXML PayrollXML ChangeRequest );

sub has_invalid_queues( $self, @queues ) {
    return 1 if $self-&gt;get_invalid_queues( @queues );
    return 0;
}

sub get_invalid_queues( $self, @queues ) {
    my %queue_map;
    @queue_map{ @QUEUE_TYPES } = ();
    my @invalid_queues = grep !exists $queue_map{ $_ }, @queues;
    return @invalid_queues;
}
</code></pre>

<p>With that in place, it was easy for our <code>queue_job()</code> method to throw an error if the developer tried to add a job to an invalid queue:</p>

<pre><code>sub queue_job( $self, $args ) {
    my $job_name = $args-&gt;{ name     } or die &quot;queue_job(): must define job name!&quot;;
    my $guid     = $args-&gt;{ guid     } or die &quot;queue_job(): must have GUID to process!&quot;;
    my $title    = $args-&gt;{ title    } // $job_name;

devdata/https_mojolicious.io_blog_2018_12_15_practical-web-content-munging_  view on Meta::CPAN

&lt;/p&gt;
</code></pre>

<p>Notice that the structure of this is regular but not selectable with any one div or piece of markup. I can use the selector <code>h3</code> to get the headings, but the text of each news item is just a paragraph, and we also want to grab the date ...

<p>So, I want to grab all of the titles, and the paragraph following the title, and the date, and put them all into some sort of data structure so I can spit them out into pages of their own.</p>

<p>Let&#39;s start with the titles, as it&#39;ll show a neat trick Mojo has up its sleeves.</p>

<pre><code>my $main = $tx-&gt;res-&gt;dom-&gt;at(&#39;#main&#39;);
my @headers = $main-&gt;find(&#39;h3&#39;)-&gt;map(&#39;text&#39;)-&gt;each;
</code></pre>

<p>Do you see it? The <code>find</code> method here is returning a <a href="https://mojolicious.org/perldoc/Mojo/Collection">Mojo::Collection</a>. &quot;Collection&quot; is kind of a fancy way to say &quot;list&quot;, but these lists have a bunch of ...

<p>After this, <code>@headers</code> will contain all of the titles. There&#39;s no way I could do that as simply with regexes (and, we could have chained all of this, including finding <code>#main</code>, into one line, but I&#39;m re-using <code>#m...

<p>Now, an even trickier thing to do with regexes would be to find the immediately subsequent sibling of these headers. But, with Mojo::DOM, we can grab it with just a few more lines of code (there&#39;s probably a way to do it with even less code, b...

<pre><code>my @paras;
for my $header ($main-&gt;find(&#39;h3&#39;)-&gt;each) {
  push (@paras, $header-&gt;next-&gt;content);
}
</code></pre>

<p>This, once again selects the <code>h3</code> elements, and iterates over the resulting collection of DOM objects, putting each one into <code>$header</code> as it loops. Then we pick out the <code>content</code> of the <code>next</code> element (w...

<p>So, now we&#39;ve got an array of headers, an array of the following paragraphs, and we just need to get the dates. This one is actually very easy, because the HTML template marks the date using a <code>date</code> class.</p>

<pre><code>my @dates = $main-&gt;find(&#39;.date&#39;)-&gt;map(&#39;text&#39;)-&gt;each;
</code></pre>

<p>Pow! We&#39;re done. OK, not quite. We&#39;ve yet to deliver on the &quot;munging&quot; part of the title of this post. We have the data from our crusty old HTML site, now let&#39;s do something with it.</p>

<h2>Munging the Dates</h2>

<p>As shown in the example Hugo content item above, I want to include a date in the metadata. Luckily, we have dates associated with each news item. Unluckily, they aren&#39;t in the format that Hugo expects. I did a little digging on the CPAN and fo...

<p>I need my dates to look like <code>2017-09-30</code>, so I used the following code (assume this is inside a loop that&#39;s putting each subsequent date in the <code>@dates</code> array we made above into <code>$date</code>):</p>

devdata/https_mojolicious.io_blog_2018_12_23_mojolicious-and-angular_  view on Meta::CPAN

</code></pre>

<p>Now for the moment, let&#39;s start the built-in angular server with <code>ng serve</code> command.</p>

<pre><code>Sachin@01:20 PM[~/workspace/project/mojo_angular/NgDemo]$ ng serve
** Angular Live Development Server is listening on localhost:4200, open your browser on http://localhost:4200/ **

Date: 2018-12-15T05:22:00.337Z
Hash: c05a16d8553980a82a62
Time: 36103ms
chunk {main} main.js, main.js.map (main) 11.5 kB [initial] [rendered]
chunk {polyfills} polyfills.js, polyfills.js.map (polyfills) 223 kB [initial] [rendered]
chunk {runtime} runtime.js, runtime.js.map (runtime) 6.08 kB [entry] [rendered]
chunk {styles} styles.js, styles.js.map (styles) 16.3 kB [initial] [rendered]
chunk {vendor} vendor.js, vendor.js.map (vendor) 3.67 MB [initial] [rendered]
ï½¢wdmï½£: Compiled successfully.
</code></pre>

<p>Open the browser to check Angular app:</p>

<p><img alt="basic angular app" src="angular_app.png"></p>

<p>Note that this page&#39;s content is coming from <code>NgDemo/src/index.html</code> where <code>app-root</code> selector html is content coming from <code>NgDemo/src/app/app.component.html</code>. Later we will be modifying <code>app.component.htm...

<h2>Make Mojolicious app serve an Angular app?</h2>

<p>First we&#39;ll compile the Angular app from <a href="https://www.typescriptlang.org/docs/home.html">TypeScript</a> to standard JavaScript.
<code>ng build</code> compiles an Angular app into an output directory named <code>dist</code> at the given output path.
Run <code>ng build</code> with <code>--base-href=./</code> so that base url inside the Angular app is set to the current directory for the application being built. This is very important so that later you do not waste time figuring out why Angular ro...

<pre><code>Sachin@02:06 PM[~/workspace/project/mojo_angular/NgDemo]$ ng build --base-href=./

Date: 2018-12-15T06:06:48.550Z
Hash: f3749aba56348b1e51e3
Time: 27091ms
chunk {main} main.js, main.js.map (main) 10.3 kB [initial] [rendered]
chunk {polyfills} polyfills.js, polyfills.js.map (polyfills) 223 kB [initial] [rendered]
chunk {runtime} runtime.js, runtime.js.map (runtime) 6.08 kB [entry] [rendered]
chunk {styles} styles.js, styles.js.map (styles) 16.3 kB [initial] [rendered]
chunk {vendor} vendor.js, vendor.js.map (vendor) 3.35 MB [initial] [rendered]
Sachin@02:06 PM[~/workspace/project/mojo_angular/NgDemo]$
</code></pre>

<p>Note that <code>dist</code> directory is created with <code>NgDemo</code> folder which contains the compiled angular app files:</p>

<pre><code>Sachin@02:10 PM[~/workspace/project/mojo_angular/NgDemo]$ ls
README.md         angular.json      dist              e2e               node_modules      package-lock.json package.json      src               tsconfig.json     tslint.json
Sachin@02:10 PM[~/workspace/project/mojo_angular/NgDemo]$
</code></pre>

devdata/https_mojolicious.io_blog_2018_12_24_async-await-the-mojo-way_  view on Meta::CPAN


<h2>Real World Use</h2>

<p>The above examples were neat, but since they only fetch one url there&#39;s no reason to be async.
Let&#39;s look at a few quick useful examples where async is a benefit.</p>

<h3>Concurrent Requests</h3>

<p>The first case might be to extend our example to fetching multiple urls concurrently.
We can get the promises returned by calling <code>get_title_p</code> on each url, then await a new promise that represents all of them.
We use <code>map</code> to take the first (only) resolve value from the result of each promise in <code>all</code>.</p>

<pre><code>use Mojo::AsyncAwait;
use Mojo::Promise;

async get_title_p =&gt; sub ($url) {
  my $tx = await $ua-&gt;get_p($url);
  return trim $tx-&gt;res-&gt;dom-&gt;at(&#39;title&#39;)-&gt;text;
};

async main =&gt; sub (@urls) {
  my @promises = map { get_title_p($_) } @urls;
  my @titles = await Mojo::Promise-&gt;all(@promises);
  say for map { $_-&gt;[0] } @titles;
};

my @urls = (qw(
  https://mojolicious.org
  https://mojolicious.io
  https://metacpan.org
));
main(@urls)-&gt;wait;
</code></pre>

devdata/https_mojolicious.io_blog_2018_12_24_async-await-the-mojo-way_  view on Meta::CPAN


plugin &#39;PromiseActions&#39;;

helper get_title_p =&gt; async sub ($c, $url) {
  my $tx = await $c-&gt;ua-&gt;get_p($url);
  return trim $tx-&gt;res-&gt;dom-&gt;at(&#39;title&#39;)-&gt;text;
};

any &#39;/&#39; =&gt; async sub ($c) {
  my $urls = $c-&gt;every_param(&#39;url&#39;);
  my @promises = map { $c-&gt;get_title_p($_) } @$urls;
  my @titles = await Mojo::Promise-&gt;all(@promises);
  $c-&gt;render(json =&gt; [ map { $_-&gt;[0] } @titles ]);
};

app-&gt;start;
</code></pre>

<p>Where the example above would be at</p>

<pre><code>localhost:3000?url=https%3A%2F%2Fmojolicious.org&amp;url=https%3A%2F%2Fmojolicious.io&amp;url=https%3A%2F%2Fmetacpan.org
</code></pre>

devscripts/update  view on Meta::CPAN

    "25" => "https://mojolicious.io/blog/2018/12/25/special-thanks/",
);

gen_curried_sub(
    'App::CreateAcmeCPANModulesImportModules::create_acme_cpanmodules_import_modules',
    {
        modules => [
            {
                name => '2018',
                url => "https://mojolicious.io/",
                extract_urls => [map {$daily_urls{$_}} "01".."25"],
                summary => 'Modules mentioned in Mojolicious Advent Calendar 2018',
                add_modules => [map {@$_} values %add_modules],
            },
            (map {
                +{
                    name => "2018_12_$_",
                    url => $daily_urls{$_},
                    summary => "Modules mentioned in Mojolicious Advent Calendar 2018 (day $_)",
                    add_modules => [@{ $add_modules{$_} // [] }],
                },
            } "01".."25"),
        ],
        typos => \%typos,
        ignore_empty => 1,

t/00-compile.t  view on Meta::CPAN

use IO::Handle;

open my $stdin, '<', File::Spec->devnull or die "can't open devnull: $!";

my @warnings;
for my $lib (@module_files)
{
    # see L<perlfaq8/How can I capture STDERR from an external command?>
    my $stderr = IO::Handle->new;

    diag('Running: ', join(', ', map { my $str = $_; $str =~ s/'/\\'/g; q{'} . $str . q{'} }
            $^X, @switches, '-e', "require q[$lib]"))
        if $ENV{PERL_COMPILE_TEST_DEBUG};

    my $pid = open3($stdin, '>&STDERR', $stderr, $^X, @switches, '-e', "require q[$lib]");
    binmode $stderr, ':crlf' if $^O eq 'MSWin32';
    my @_warnings = <$stderr>;
    waitpid($pid, 0);
    is($?, 0, "$lib loaded ok");

    shift @_warnings if @_warnings and $_warnings[0] =~ /^Using .*\bblib/



( run in 0.779 second using v1.01-cache-2.11-cpan-49f99fa48dc )