view release on metacpan or search on metacpan
devdata/https_mojolicious.io_blog_2017_12_05_day-5-your-apps-built-in-commands view on Meta::CPAN
Because of that, headers are omitted from the output unless you pass <code>-v</code>.</p>
<p>Let's see what it can do!
You can find the latest version of <code>IO::Socket::SSL</code> using the <a href="https://github.com/metacpan/metacpan-api/blob/master/docs/API-docs.md">Meta::CPAN JSON API</a>.
The response is parsed as JSON and only the <code>version</code> element is output.</p>
<pre><code>mojo get https://fastapi.metacpan.org/v1/module/IO::Socket::SSL /version
</code></pre>
<p>You can fetch the Perl headlines from reddit.
To do so we fetch the url (following redirects with <code>-r</code>), then we give it a <a href="http://mojolicious.org/perldoc/Mojo/DOM/CSS">CSS3 selector</a>, and finally extract the text from each found element.</p>
<pre><code>mojo get -r reddit.com/r/perl 'p.title > a.title' text
</code></pre>
<p>How fun is that?!</p>
<ul>
<li>You can POST or PUT or DELETE data.</li>
<li>It handles HTTP basic authentication using <code>username:password@</code> in the URL.</li>
<li>You can submit forms, even with file uploads using the standard <code>@filename</code> syntax.</li>
devdata/https_mojolicious.io_blog_2017_12_09_day-9-the-best-way-to-test view on Meta::CPAN
<p>These methods allow the (optional) first argument to be a <a href="http://mojolicious.org/perldoc/Mojo/JSON/Pointer">JSON Pointer</a> to "dive in" to the data structure.
Very handy when you only care about subsets of the data.</p>
<pre><code>$t->get_ok('/santa_list/joel.json')
->json_is('/status', 'nice');
</code></pre>
<p>Of course there are methods to test HTML responses.
Since it makes little sense to test the whole thing (and if you wanted to you could use <code>content_</code>), these take a <a href="http://mojolicious.org/perldoc/Mojo/DOM/CSS">CSS3 Selector</a> to narrow their focus in a similar manner to the JSON...
To inspect the textual portions of the HTML response, use the <code>text_</code> methods with a selector.
For other tests, there might not be text to test, or the value doesn't matter.
For those cases there are <code>element_exists</code>, <code>element_exists_not</code>, and <code>element_count_is</code>, which, as their names indicate, take a selector and tries to find if or how many elements match it.
These really need a post of their own, but as a few examples</p>
<pre><code>$t->text_is('div.main-content p:nth-of-type(2)', 'This is the third paragraph of the main-section of text');
$t->element_exists('img[src="kitten.jpg"]');
</code></pre>
<h2>Testing Websockets</h2>
<p>Websockets are an intersting challenge to test, however Test::Mojo makes them easy.
We've already seen that you open a websocket with <code>websocket_ok</code>.</p>
devdata/https_mojolicious.io_blog_2017_12_13_day-13-more-about-roles view on Meta::CPAN
To my knowledge they all used Role::Tiny, but they had to roll their own composition mechanisms, presenting some barrier to use.</p>
<h2>Creating Roles</h2>
<p>A role itself looks like a class at first glance.
It has a package name and probably has methods.
Rather than using the <code>-base</code> flag or specifying a parent class, a role uses the <code>-role</code> flag.
Once again, an optional <code>-signatures</code> flag is allowed too.</p>
<p>Let's look at a (contrived) example.
Say we want to make a role that finds all the tags matching elements in a <a href="http://mojolicious.org/perldoc/Mojo/DOM">Mojo::DOM</a> based on a selector stored in an attribute.</p>
<pre><code>package Mojo::DOM::Role::FindCustom;
use Mojo::Base -role;
requires 'find';
has custom_selector => 'a';
sub find_custom {
my $self = shift;
return $self->find($self->custom_selector);
}
</code></pre>
<p>This example shows three interesting features.
First is that even though it is not a class, the package still gets Mojo::Base's <a href="http://mojolicious.org/perldoc/Mojo/Base#has">has</a> keyword, used to make accessors for attributes.
Second is that a new keyword, <code>requires</code>, has been added, coming from <a href="https://metacpan.org/pod/Role::Tiny#requires">Role::Tiny</a>.
This keyword tells the role that it can only be composed into a class that provides a <code>find</code> method.
Consider the new <code>find_custom</code> method, since it uses <code>find</code> if the class it is composed into didn't provide that method our new method couldn't behave as expected.</p>
<p>Indeed several keywords are imported from Role::Tiny including <code>with</code> to compose other roles and several method modifiers coming from <a href="https://metacpan.org/pod/Class::Method::Modifiers">Class::Method::Modifiers</a> (which is an ...
devdata/https_mojolicious.io_blog_2017_12_18_day-18-the-wishlist-model view on Meta::CPAN
->db
->insert(
'<span class="hljs-string">users</span>',
{name => <span class="hljs-type">$name</span>},
)->last_insert_id;
}
<span class="hljs-keyword">sub </span><span class="hljs-function">user</span> {
<span class="hljs-keyword">my</span> (<span class="hljs-type">$self</span>, <span class="hljs-type">$name</span>) = <span class="hljs-type">@_</span>;
<span class="hljs-keyword">my</span> <span class="hljs-type">$sql</span> = <<<span class="hljs-keyword">' SQL';</span>
select
user.id,
user.name,
(
select
json_group_array(item)
from (
select json_object(
'id', items.id,
'title', items.title,
'url', items.url,
'purchased', items.purchased
) as item
from items
where items.user_id=user.id
)
) as items
from users user
devdata/https_mojolicious.io_blog_2017_12_18_day-18-the-wishlist-model view on Meta::CPAN
->query(<span class="hljs-type">$sql</span>, <span class="hljs-type">$name</span>)
->expand(json => '<span class="hljs-string">items</span>')
->hash;
}
<span class="hljs-keyword">sub </span><span class="hljs-function">list_user_names</span> {
<span class="hljs-keyword">my</span> <span class="hljs-type">$self</span> = <span class="hljs-function">shift</span>;
<span class="hljs-keyword">return</span> <span class="hljs-type">$self</span><span class="hljs-type">
</span> ->sqlite
->db
-><span class="hljs-function">select</span>(
'<span class="hljs-string">users</span>' => ['<span class="hljs-string">name</span>'],
<span class="hljs-function">undef</span>,
{-asc => '<span class="hljs-string">name</span>'},
)
->arrays
-><span class="hljs-function">map</span>(<span class="hljs-keyword">sub</span>{ <span class="hljs-variable">$_</span>->[0] });
}
<span class="hljs-keyword">sub </span><span class="hljs-function">add_item</span> {
<span class="hljs-keyword">my</span> (<span class="hljs-type">$self</span>, <span class="hljs-type">$user</span>, <span class="hljs-type">$item</span>) = <span class="hljs-type">@_</span>;
devdata/https_mojolicious.io_blog_2017_12_18_day-18-the-wishlist-model view on Meta::CPAN
)->rows;
}
<span class="hljs-number">1</span>;
</code></pre>
<p><small>lib/Wishlist/Model.pm</small></p>
<p>This class define the ways that the application can alter the data in the database.
Rather than the familiar DBI methods like <code>selectrow_arrayref</code>, Mojo-Flavored DBI make a query and then ask for the result shape they want returned.
The user can ask for a row as a hash or an array.
They can also ask for and array of all thr rows, again as a hash or an array.
Sometimes there are other data you want rather than the actual results, like the <code>last_insert_id</code> or the number of <code>rows</code> affected.</p>
<p>Most of the methods are simple enough to employ the SQL::Abstract forms: add, update, remove, even listing the users.
However for getting a user we want to make a more complex <code>query</code> by hand.
It looks up the user row by name, and aggregates the items that user is wishing for as JSON.</p>
<p>Before fetching the results we tell Mojo::SQLite that we would like to expand the JSON back into Perl data transparently.
This <a href="https://metacpan.org/pod/Mojo::SQLite::Results#expand"><code>expand</code></a> method differs slightly from the other flavors since SQLite doesn't have metadata to give Mojo::SQLite hints about which column to expand.
devdata/https_mojolicious.io_blog_2017_12_20_day-20-practical-testing view on Meta::CPAN
done_testing;
</code></pre>
<p><small><a href="https://github.com/jberger/Wishlist/blob/blog_post/practical_testing/t/embed.t">t/embed.t</a></small></p>
<p>Because the template expects the result to be an object we have to build a tiny class to contain our mock results.
Also whenever you are mocking, it is important to check the input your mock received as well as the results that the calling code derives from your mock return value.</p>
<p>In the test you can also see some examples of how to use selectors to test for both text and attribute values.
The text test is especially important because it shows that the html value that I got back from the LinkEmbedder isn't being escaped by the template and will render as HTML to the client.</p>
<p>A few more tests and some documentation and our application will really be taking final shape!</p>
</section>
<small><p><a href="https://commons.wikimedia.org/w/index.php?curid=11337658">Image</a> by Hawaii Volcano Observatory, USGS - <a class="external text" href="http://hvo.wr.usgs.gov/kilauea/update/archive/2009/2009_Jun-Oct.html" rel="nofol...
</small>
<p class="tags">
<span>Tagged in </span>:
devdata/https_mojolicious.io_blog_2017_12_21_day-21-virtually-a-lumberjack view on Meta::CPAN
<p>Mojo is an amazing toolkit for web development, as shown in previous
calendar entries, but using components of it can also solve problems in other
non-web spaces like these. Why use Mojo for this? Because it makes it <em>easy</em>.</p>
</section>
<section id="section-2">
<h2>Down By The Riverside</h2>
<p>First of all, we need the stream of data.
Initially, I used <code>tshark</code> for this, though another member of the OpenHMD team
built another application, OpenHMD-Dumper, which has both a device selection
interface and opens the device to read the packets, which makes this much
easier.
Doing so allows us to ask others who have HMDs to send us these logs, so we
can add driver support for devices none of the team have access to!
Otherwise, we use something else to open the device and read data and <code>tshark</code>
to capture and output the packet data.</p>
<pre><code>tshark -i usbmon1 -Y 'usb.src == "1.22.1"' -T fields -e usb.capdata
</code></pre>
devdata/https_mojolicious.io_blog_2017_12_21_day-21-virtually-a-lumberjack view on Meta::CPAN
various sizes until you see a (rough) sine wave, indicating you've got the
right offset and number of bytes for that part of the puzzle!</p>
<h2>Moving On To Greater Things</h2>
<p>This tool could easily be improved (and will be, when it is next called upon).
For example, both the extraction and fixing of the headers could be made into
commands, as demonstrated in
<a href="https://mojolicious.io/blog/2017/12/06/day-6-adding-your-own-commands/">Day 6</a>.</p>
<p>It could also be made into a proper web application where the user can select
the action they want to do and an animation shows the movement that is wanted
(not everyone is familiar with nautical terms after all!).
Perhaps even with a device select?
A UI that allows the user to select the offset, size and unpack method and
render the graphs as appropriate could cut down the time to analyse.
Better yet, if it could detect which combinations result in a rough sine wave,
it could narrow down and present the user with likely candidates for each
movement..</p>
<p>.. but those are improvements for another day!</p>
</section>
<small><p><a href="https://www.pexels.com/photo/sky-woman-clouds-girl-123335/">Image</a> by Bradley Hook, in the Public Domain.</p>
</small>