Acme-CPANModulesBundle-Import-MojoliciousAdvent-2017

 view release on metacpan or  search on metacpan

devdata/https_mojolicious.io_blog_2017_12_06_day-6-adding-your-own-commands  view on Meta::CPAN


1;
</code></pre>

<p>It is just a class with a few methods that know how to look up and store weather data.
The class has two required attributes, <code>sqlite</code> and <code>appid</code>.
Whoever instantiates this class will need to pass them in.</p>

<p>The fetch method builds a URL from attributes and a passed-in search term.
It then requests the data from OpenWeatherMap.
The <code>result</code> method dies if there is a connection error.
For brevity I&#39;m being a little lax on other error checking.</p>

<p>The other two methods insert data into sqlite and recall it out again, again based on a search term.
This is basically just caching the data from OpenWeatherMap
Again for brevity I&#39;m only storing the term, the time, and the temperature.</p>

<h3>The Application</h3>

<p>Once we have an application we can start to make try it out a little.
The main class is at <a href="https://github.com/jberger/MyWeatherApp/blob/master/lib/MyWeatherApp.pm">lib/MyWeatherApp.pm</a>.</p>

devdata/https_mojolicious.io_blog_2017_12_08_day-8-mocking-a-rest-api  view on Meta::CPAN

correctly gives us back the data we submitted, like our original API
might do.</p>

<p>We can test our added <code>POST /servers</code> method with the <code>get</code> command
again:</p>

<pre><code>$ perl test-api.pl get -M POST -c &#39;{ &quot;ip&quot;: &quot;10.0.0.3&quot; }&#39; /servers
{ &quot;status&quot;: &quot;success&quot;, &quot;id&quot;: 3, &quot;server&quot;: { &quot;ip&quot;: &quot;10.0.0.3&quot; } }
</code></pre>

<p>Now what if I want to test what happens when the API gives me an error?
Mojolicious has an easy way to layer on additional templates to use for
certain routes: <a href="http://mojolicious.org/perldoc/Mojolicious/Guides/Rendering#Template-variants">Template
variants</a>.
These variant templates will be used instead of the original template,
but only if they are available. Read more on <a href="/blog/2017/12/07/day-7-using-template-variants-for-a-beta-landing-page/">how to use template
variants yesterday on the advent
calendar</a>.</p>

<p>By setting the template variant to the application &quot;mode&quot;, we can easily
switch between multiple sets of templates by adding <code>-m &lt;mode&gt;</code> to the

devdata/https_mojolicious.io_blog_2017_12_08_day-8-mocking-a-rest-api  view on Meta::CPAN

[
    &lt;%== include &#39;GET/servers/1&#39; %&gt;,
    &lt;%== include &#39;GET/servers/2&#39; %&gt;
]
@@ GET/servers/1.json.ep
{ &quot;ip&quot;: &quot;10.0.0.1&quot;, &quot;os&quot;: &quot;Debian 9&quot; }
@@ GET/servers/2.json.ep
{ &quot;ip&quot;: &quot;10.0.0.2&quot;, &quot;os&quot;: &quot;Debian 8&quot; }
@@ POST/servers.json.ep
{ &quot;status&quot;: &quot;success&quot;, &quot;id&quot;: 3, &quot;server&quot;: &lt;%== $c-&gt;req-&gt;body %&gt; }
@@ POST/servers.json+error.ep
% $c-&gt;res-&gt;code( 400 );
{ &quot;status&quot;: &quot;error&quot;, &quot;error&quot;: &quot;Bad request&quot; }
</code></pre>

<pre><code>$ perl test-api.pl get -m error -M POST -c &#39;{}&#39; /servers
{ &quot;status&quot;: &quot;error&quot;, &quot;error&quot;: &quot;Bad request&quot; }
</code></pre>

<p>And finally, since I&#39;m using this to test an AJAX web application,
I need to allow the preflight <code>OPTIONS</code> request to succeed and I need to
make sure that all of the correct <code>Access-Control-*</code> headers are set
to allow for cross-origin requests.</p>

<pre><code class="hljs"><span class="hljs-comment"># test-api.pl</span><span class="hljs-comment">
</span><span class="hljs-keyword">use</span> <span class="hljs-function">Mojolicious::Lite</span>;
hook after_build_tx =&gt; <span class="hljs-keyword">sub </span>{

devdata/https_mojolicious.io_blog_2017_12_08_day-8-mocking-a-rest-api  view on Meta::CPAN

[
    &lt;%== include &#39;GET/servers/1&#39; %&gt;,
    &lt;%== include &#39;GET/servers/2&#39; %&gt;
]
@@ GET/servers/1.json.ep
{ &quot;ip&quot;: &quot;10.0.0.1&quot;, &quot;os&quot;: &quot;Debian 9&quot; }
@@ GET/servers/2.json.ep
{ &quot;ip&quot;: &quot;10.0.0.2&quot;, &quot;os&quot;: &quot;Debian 8&quot; }
@@ POST/servers.json.ep
{ &quot;status&quot;: &quot;success&quot;, &quot;id&quot;: 3, &quot;server&quot;: &lt;%== $c-&gt;req-&gt;body %&gt; }
@@ POST/servers.json+error.ep
% $c-&gt;res-&gt;code( 400 );
{ &quot;status&quot;: &quot;error&quot;, &quot;error&quot;: &quot;Bad request&quot; }
</code></pre>

<p>Now I have 20 lines of code that can be made to mock any JSON API
I write. Mojolicious makes everything easy!</p>

              </section>
              <small><p><a href="https://commons.wikimedia.org/w/index.php?curid=37564272">Image</a> by Calspan Corporation, National Highway Traffic Safety Administration - Public Domain.</p>
</small>

              <p class="tags">

devdata/https_mojolicious.io_blog_2017_12_14_day-14-you-promised-to-call  view on Meta::CPAN

my @promises = map fetchpages(), 1 .. 2;
Mojo::Promise-&gt;all(@promises)-&gt;wait if @promises;
</code></pre>

<p>Another option for dealing with a number of concurrent activities, if you just want the first one that completes, is <a href="http://mojolicious.org/perldoc/Mojo/Promise#race"><code>race</code></a>.</p>

<h2>What if something doesn&#39;t work?</h2>

<p>In the above, we assumed that everything worked. What if it doesn&#39;t? Promises as a standard offer two other methods: <code>catch</code>, and <code>finally</code>.</p>

<p><code>catch</code> is given a code-ref, which will be called when a Promise is &quot;rejected&quot;. When things work as above, each Promise is &quot;resolved&quot;. That means the value it was resolved with gets passed to the next <code>then</cod...

<pre><code>sub fetchpage {
  $ua-&gt;get_p($url)-&gt;then(sub { ... })-&gt;then(sub { ... })-&gt;catch(sub {
    # either log, or report, or something else
  });
}
</code></pre>

<p>If either the initial <code>get_p</code>, or either of the <code>then</code>s get rejected, then execution will skip to the <code>catch</code>. Another way to get this behaviour is to give a second code-ref to <code>then</code>.</p>

devdata/https_mojolicious.io_blog_2017_12_14_day-14-you-promised-to-call  view on Meta::CPAN

</code></pre>

<p>We also want a procedure to handle results that are ready, to store them in a file if successful:</p>

<pre><code>sub handle_result {
  my ($outpath, $tx, $s) = @_;
  if ($tx-&gt;res-&gt;is_success) {
    print &quot;got $s\n&quot;;
    $outpath-&gt;child($s)-&gt;spurt($tx-&gt;res-&gt;body);
  } else {
    print &quot;error $s\n&quot;;
  }
}
</code></pre>

<p>And now, the Promise part:</p>

<pre><code>my @promises = map makepromise($urlbase, $ua, \@suffixes, $outpath), (1..$MAXREQ);
Mojo::Promise-&gt;all(@promises)-&gt;wait if @promises;

sub makepromise {

devdata/https_mojolicious.io_blog_2017_12_22_day-22-how-to-build-a-public-rest-api  view on Meta::CPAN


<p>Then copy/paste the specification from above and save it to <code>public/api.yaml</code>.</p>

<p>Next you must create a controller <code>lib/MyApp/Controller/Echo.pm</code> to match
<code>x-mojo-to</code> in the API specification:</p>

<pre><code>package MyApp::Controller::Echo;
use Mojo::Base &quot;Mojolicious::Controller&quot;;

sub index {
  # Validate input request or return an error document
  my $self = shift-&gt;openapi-&gt;valid_input or return;

  # Render back the same data as you received using the &quot;openapi&quot; handler
  $self-&gt;render(openapi =&gt; $self-&gt;req-&gt;json);
}

1;
</code></pre>

<p>Last, you should update the test. Change <code>t/basic.t</code> so the line with

devdata/https_mojolicious.io_blog_2017_12_22_day-22-how-to-build-a-public-rest-api  view on Meta::CPAN

</code></pre>

<p>From the output above, we can see that the expected route <code>/api/echo</code> is
generated, so let&#39;s see if we can send/receive any data to the server. To try
it out, we use the &quot;openapi&quot; sub command which was installed with
<a href="https://metacpan.org/pod/OpenAPI::Client">OpenAPI::Client</a>.</p>

<p>In this first example we try to send invalid body data using the <code>-c</code> switch:</p>

<pre><code>$ MOJO_LOG_LEVEL=info ./script/my_app openapi /api echo -c &#39;[42]&#39;
{&quot;errors&quot;:[{&quot;message&quot;:&quot;Expected object - got array.&quot;,&quot;path&quot;:&quot;\/body&quot;}]}
</code></pre>

<p>This next example should succeed, since we change from sending an array to
sending an object, as described in the API specification:</p>

<pre><code>$ MOJO_LOG_LEVEL=info ./script/my_app openapi /api echo -c &#39;{&quot;age&quot;:42}&#39;
{&quot;age&quot;:42}
</code></pre>

<p>Yay! We see that the same input data was echoed back to us, without any error
message. Mission accomplished!</p>

<h2>See also</h2>

<p>Did you find OpenAPI interesting? Check out these resources to find out more:</p>

<ul>
<li><a href="https://metacpan.org/release/Mojolicious-Plugin-OpenAPI">Mojolicious::Plugin::OpenAPI</a>
contains guides and synopsis to get you started.</li>
<li><a href="https://metacpan.org/pod/OpenAPI::Client">OpenAPI::Client</a>&#39;s manual

devdata/https_mojolicious.io_blog_2017_12_23_day-23-one-liners-for-fun-and-profit  view on Meta::CPAN


<p>We can even <code>trim</code> and <code>sort</code> while we do it</p>

<pre><code>perl -Mojo -E &#39;print j({sites =&gt; b(f(shift)-&gt;slurp)-&gt;split(&quot;\n&quot;)-&gt;map(&quot;trim&quot;)-&gt;sort})&#39; sites
</code></pre>

<h3>The Lite App</h3>

<p>If all of that weren&#39;t enough for you, remember that your one-liner is also a Lite app.
This means that you get all of the Lite app keywords.
I like to show the Mojolicious error page, featuring the glorious Fail Raptor, to the uninitiated.
While I could find the image in the repo, just making a &quot;fail app&quot; is so much easier!</p>

<pre><code>perl -Mojo -E &#39;get &quot;/&quot; =&gt; sub { die }; app-&gt;start&#39; daemon -m production
</code></pre>

<p>If you haven&#39;t seen him, you need to run that one-liner to see him for yourself!</p>

<p>You do still have to call <code>app-&gt;start</code> as ever, and the application gets called with commands, just like any application.
Call it with <code>daemon</code> and since we want to show the production version of the page, force it into production mode.</p>



( run in 0.714 second using v1.01-cache-2.11-cpan-3cd7ad12f66 )