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'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'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 '{ "ip": "10.0.0.3" }' /servers
{ "status": "success", "id": 3, "server": { "ip": "10.0.0.3" } }
</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 "mode", we can easily
switch between multiple sets of templates by adding <code>-m <mode></code> to the
devdata/https_mojolicious.io_blog_2017_12_08_day-8-mocking-a-rest-api view on Meta::CPAN
[
<%== include 'GET/servers/1' %>,
<%== include 'GET/servers/2' %>
]
@@ GET/servers/1.json.ep
{ "ip": "10.0.0.1", "os": "Debian 9" }
@@ GET/servers/2.json.ep
{ "ip": "10.0.0.2", "os": "Debian 8" }
@@ POST/servers.json.ep
{ "status": "success", "id": 3, "server": <%== $c->req->body %> }
@@ POST/servers.json+error.ep
% $c->res->code( 400 );
{ "status": "error", "error": "Bad request" }
</code></pre>
<pre><code>$ perl test-api.pl get -m error -M POST -c '{}' /servers
{ "status": "error", "error": "Bad request" }
</code></pre>
<p>And finally, since I'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 => <span class="hljs-keyword">sub </span>{
devdata/https_mojolicious.io_blog_2017_12_08_day-8-mocking-a-rest-api view on Meta::CPAN
[
<%== include 'GET/servers/1' %>,
<%== include 'GET/servers/2' %>
]
@@ GET/servers/1.json.ep
{ "ip": "10.0.0.1", "os": "Debian 9" }
@@ GET/servers/2.json.ep
{ "ip": "10.0.0.2", "os": "Debian 8" }
@@ POST/servers.json.ep
{ "status": "success", "id": 3, "server": <%== $c->req->body %> }
@@ POST/servers.json+error.ep
% $c->res->code( 400 );
{ "status": "error", "error": "Bad request" }
</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->all(@promises)->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't work?</h2>
<p>In the above, we assumed that everything worked. What if it doesn'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 "rejected". When things work as above, each Promise is "resolved". That means the value it was resolved with gets passed to the next <code>then</cod...
<pre><code>sub fetchpage {
$ua->get_p($url)->then(sub { ... })->then(sub { ... })->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->res->is_success) {
print "got $s\n";
$outpath->child($s)->spurt($tx->res->body);
} else {
print "error $s\n";
}
}
</code></pre>
<p>And now, the Promise part:</p>
<pre><code>my @promises = map makepromise($urlbase, $ua, \@suffixes, $outpath), (1..$MAXREQ);
Mojo::Promise->all(@promises)->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 "Mojolicious::Controller";
sub index {
# Validate input request or return an error document
my $self = shift->openapi->valid_input or return;
# Render back the same data as you received using the "openapi" handler
$self->render(openapi => $self->req->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's see if we can send/receive any data to the server. To try
it out, we use the "openapi" 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 '[42]'
{"errors":[{"message":"Expected object - got array.","path":"\/body"}]}
</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 '{"age":42}'
{"age":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>'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 'print j({sites => b(f(shift)->slurp)->split("\n")->map("trim")->sort})' sites
</code></pre>
<h3>The Lite App</h3>
<p>If all of that weren'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 "fail app" is so much easier!</p>
<pre><code>perl -Mojo -E 'get "/" => sub { die }; app->start' daemon -m production
</code></pre>
<p>If you haven'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->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>