Acme-CPANModulesBundle-Import-MojoliciousAdvent-2017

 view release on metacpan or  search on metacpan

devdata/https_mojolicious.io_blog_2017_12_04_day-4-dont-fear-the-full-app  view on Meta::CPAN

TIMTOWTDI again.</p>

<p>If you&#39;ve only used those keywords above, translate them as I just showed you and you&#39;re done.</p>

<h3>Nested Routing</h3>

<p>By now, you must have seen that I keep qualifying my statement as &#39;toplevel routes&#39;.
Well ok so there is one small difference between Lite and Full, and that difference is how nested routes work.</p>

<p>There are two other keywords, <code>under</code> and <code>group</code>.
<code>under</code> allows routes to share code, like say for authentication.
They also can share parts of their path.
For example, parts of an API that need authentication might be all under <code>/protected</code>.</p>

<pre><code>get &#39;/unsafe&#39; =&gt; &#39;unsafe&#39;;

under &#39;/protected&#39; =&gt; sub {
  # check authentication
};

# /protected/safe

devdata/https_mojolicious.io_blog_2017_12_05_day-5-your-apps-built-in-commands  view on Meta::CPAN

<pre><code>$ mojo generate lite_app myapp.pl
</code></pre>

<p>To create a <a href="http://mojolicious.org/perldoc/Mojolicious/Command/generate/app">Full app</a>, pass the name of the class</p>

<pre><code>$ mojo generate app MyApp
</code></pre>

<p>You can also create a <a href="http://mojolicious.org/perldoc/Mojolicious/Command/generate/plugin">plugin project</a> or <a href="http://mojolicious.org/perldoc/Mojolicious/Command/generate/makefile">generate a Makefile</a>.</p>

<p>There is some more to say on the subject of file generation, but since this is an overview post, I&#39;ll leave it at that for now.</p>

<h3>The version Command</h3>

<p>The <a href="http://mojolicious.org/perldoc/Mojolicious/Command/version"><code>version</code></a> command is a simple utility to check your Mojolicious installation.</p>

<p>It outputs your current version of Perl and Mojolicious along with any installed optional libraries.
For example, you&#39;ll want to install <a href="https://metacpan.org/pod/IO::Socket::SSL">IO::Socket::SSL</a> if you want to fetch or serve sites over HTTPS.
It then checks to see if there is an updated version of Mojolicious available.</p>

<h3>The routes Command</h3>

devdata/https_mojolicious.io_blog_2017_12_05_day-5-your-apps-built-in-commands  view on Meta::CPAN

<pre><code>perl santa.pl get /meet/rudolph &#39;p&#39; text
</code></pre>

<h3>The eval Command</h3>

<p>Finally in this whirlwind tour, I&#39;ll show you my favorite command.
The <a href="http://mojolicious.org/perldoc/Mojolicious/Command/eval"><code>eval</code></a> command.
This command has the magic power to run one-off commands using your application!
The application is available as <code>app</code> in your one-liner.</p>

<p>So say you can&#39;t figure out what is wrong with your configuration, just ask it to dump what it thinks its configuration is</p>

<pre><code>perl myapp.pl eval -v &#39;app-&gt;home&#39;
perl myapp.pl eval -V &#39;app-&gt;config&#39;
</code></pre>

<p>The <code>-v</code> flag prints the string result of the last statement to STDOUT, the <code>-V</code> flag does the same but for data structures.
Maybe you want to see why it can&#39;t find your templates.</p>

<pre><code>perl myapp.pl eval -V &#39;app-&gt;renderer-&gt;paths&#39;
</code></pre>

devdata/https_mojolicious.io_blog_2017_12_09_day-9-the-best-way-to-test  view on Meta::CPAN

<p>If you want to test a local application, presumably one you are developing (as most users do) then you have to tell it how to do so.
If you are testing a Mojolicious::Lite script, all you have to do is <code>require</code> the script into your test file.
This is usually done with the help of <a href="https://metacpan.org/pod/FindBin">FindBin</a> which gives the location of the test script, from which you can derive where your application is.
For example, if your script is <code>project/myapp.pl</code> and your test is <code>project/t/mytest.t</code> then you need to go up one directory to find your script, like so</p>

<pre><code>use FindBin;
require &quot;$FindBin::Bin/../myapp.pl&quot;;
my $t = Test::Mojo-&gt;new;
</code></pre>

<p>One might also create a Lite app in the test file itself, especially when say testing a plugin on its own.</p>

<pre><code>use Mojolicious::Lite;
plugin &#39;MyCoolPlugin&#39;;
my $t = Test::Mojo-&gt;new;
</code></pre>

<p>Testing a Full app couldn&#39;t be simpler, you just pass it a class name for it to instantiate.</p>

<pre><code>my $t = Test::Mojo-&gt;new(&#39;MyApp&#39;);
</code></pre>

devdata/https_mojolicious.io_blog_2017_12_09_day-9-the-best-way-to-test  view on Meta::CPAN

  -&gt;message_ok
  -&gt;message_is(&#39;ECHO: echo me&#39;)
  -&gt;send_ok(&#39;quack&#39;)
  -&gt;message_ok
  -&gt;message_is(&#39;Ducks quacks do not echo, silly&#39;)
  -&gt;finish_ok;
</code></pre>

<h2>To Be Continued</h2>

<p>There is still so much to say on the topic of testing.
Various tips and tricks.
Extensions that make testing javascript possible, extensions that make testing Catalyst or Dancer apps possible.
But this overview has gone plenty long and those should wait for another day.</p>

<p>As I said before, the Mojolicious documentation has lots of examples both in the <a href="http://mojolicious.org/perldoc/Test/Mojo">class documenation</a> and in the <a href="http://mojolicious.org/perldoc/Mojolicious/Guides/Testing">testing guide...
Check those out while you wait, if you&#39;ve liked what you&#39;ve seen above.</p>

              </section>
              <small><p><a href="https://commons.wikimedia.org/w/index.php?curid=35474503">Image</a> by CSIRO, <a href="http://creativecommons.org/licenses/by/3.0" title="Creative Commons Attribution 3.0">CC BY 3.0</a>.</p>
</small>

devdata/https_mojolicious.io_blog_2017_12_12_day-12-more-than-a-base-class  view on Meta::CPAN

  -&gt;at(&#39;title&#39;)
  -&gt;text;
</code></pre>

<p>Perhaps this is too much adherence to fluent interfaces but as you progress, getting a nice long chain can really feel like an accomplishment!
If you&#39;d rather pass on this method, that&#39;s fine too.</p>

<h2>Roles</h2>

<p>While Mojo::Base has always tacitly supported roles via external modules, just recently has it started to offer explicit functionality in this area.
That said, I have lots ot say on the matter, so if you&#39;ll permit me, I&#39;m going to keep you in suspense until <a href="/blog/2017/12/13/day-13-more-about-roles">tomorrow</a>.</p>

              </section>
              <small><p><a href="https://commons.wikimedia.org/w/index.php?curid=1592390">Image</a> by <a href="https://en.wikipedia.org/wiki/User:Mactographer" title="en:User:Mactographer">David Ball</a> - Original work, <a href="http://creativecomm...
</small>

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

devdata/https_mojolicious.io_blog_2017_12_13_day-13-more-about-roles  view on Meta::CPAN

With inheritance, especially of third-party functionality, you have to choose one set of extensions to utilize.
This is because the author of the subclass establishes the inheritance.
In roles, the user determines which roles to compose into the base class.</p>

<p><a href="/blog/2017/12/12/day-12-more-than-a-base-class">Yesterday</a> I ended the discussion of <a href="http://mojolicious.org/perldoc/Mojo/Base">Mojo::Base</a> before discussing the roles support.
Added in several installments between Mojolicious versions <a href="https://metacpan.org/release/SRI/Mojolicious-7.40">7.40</a> and <a href="https://metacpan.org/release/SRI/Mojolicious-7.55">7.55</a>, this role support is one of the most recently ad...
The role handling comes from <a href="https://metacpan.org/pod/Role::Tiny">Role::Tiny</a> which is an optional dependency in Mojolicious, but is required in order to use the functionality that I will describe.</p>

              </section>
              <section id="section-2">
                  <p>This is not to say that roles couldn&#39;t be or weren&#39;t used in Mojolicious before then, only that Mojo::Base didn&#39;t include any special handling to make it user-friendly.
Prior to the functionality being formally available from Mojo::Base, a few roles were available for Mojo stack classes on 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>

devdata/https_mojolicious.io_blog_2017_12_13_day-13-more-about-roles  view on Meta::CPAN

  &#39;&lt;html&gt;&lt;a href=&quot;http://mojolicious.org&quot;&gt;Mojolicious&lt;/a&gt;&lt;/html&gt;&#39;
);
my $anchors = $dom-&gt;find_custom;
</code></pre>

<p>And finally that looks quite nice.
It should be noted that this shortcut usage is different from some other plugin systems on CPAN, some of which use <code>+</code> to indicate a literal (ie non-shortened) class name.
We feel that you should be explicit about requesting a non-literal class name and so we made this choice.</p>

<p>Now sometimes you don&#39;t instantiate the class, sometimes you have an instance given to you.
For example say the Mojo::DOM document came from a <a href="http://mojolicious.org/perldoc/Mojo/UserAgent">Mojo::UserAgent</a> request.
In this case you can still use <code>with_roles</code>.
What happens in this case is that a little magic happens in the background to add the functionality to the instance.</p>

<pre><code>my $dom = $ua
  -&gt;get(&#39;http://mojolicious.org&#39;)
  -&gt;res
  -&gt;dom
  -&gt;with_roles(&#39;+FindCustom&#39;);
my $anchors = $dom-&gt;find_custom;
</code></pre>

<p>Finally, when you are checking if a class consumes a role, you don&#39;t check with <code>-&gt;isa</code> as if it were inheritance.
Rather you check with <code>-&gt;does</code>.
You cannot use the shortened forms of roles when using this check however.</p>

<pre><code>if ($dom-&gt;does(&#39;Mojo::DOM::Role::FindCustom&#39;)) {
  $dom-&gt;find_custom-&gt;each(sub{ say });
}
</code></pre>

<h2>Roles on CPAN</h2>

<p>The first part of the ecosystem to embrace roles was testing.
Given the interface of <a href="http://mojolicious.org/perldoc/Test/Mojo">Test::Mojo</a>, adding additional test methods isn&#39;t as easy as is generally true of other testers in the <a href="https://metacpan.org/pod/Test::More">Test::More</a> lands...
Roles fill that gap nicely, and therefore such roles predated Mojo::Base&#39;s own role handling and even inspired adding it to the core.</p>

<p>As I&#39;ve mentioned <a href="/blog/2017/12/09/day-9-the-best-way-to-test">earlier in this series</a>, <a href="https://metacpan.org/pod/Test::Mojo::Role::Debug">Test::Mojo::Role::Debug</a> adds methods to run a callback on failed tests.

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

<p>Originally this was done just using callbacks, but this lead to what is known as &quot;callback hell&quot;: each callback contains the next callback, at an increasing level of indentation. Even harder to keep track of is if the functions are kept ...

<p>Promises are used to easily add processing steps to a transaction: one can keep adding code for what to do &quot;then&quot; - after a previous stage has finished. Best of all, each &quot;callback&quot; is small and separate, with each one placed i...

<p>First let&#39;s get web pages, one after the other, synchronously. Obviously, that means the code will block anything else while it&#39;s running.</p>

<pre><code># refers to a previously-set-up @urls
sub fetchpages {
  while (my $url = shift @urls) {
    # Fetch, show title
    say $ua-&gt;get($url)-&gt;result-&gt;dom-&gt;at(&#39;title&#39;)-&gt;text;
  }
}
</code></pre>

<h2>With a callback</h2>

<p>This you could realistically have running as part of a web service, since with any kind of callback it will run asynchronously, therefore non-blocking, as discussed above.</p>

<pre><code>sub fetchpages {
  # Stop if there are no more URLs
  return unless my $url = shift @urls;
  # Fetch the next title
  $ua-&gt;get($url, sub {
    my ($tx) = @_;
    say &quot;$url: &quot;, $tx-&gt;result-&gt;dom-&gt;at(&#39;title&#39;)-&gt;text;
    fetchpages();
  });
}
</code></pre>

<h2>Promises</h2>

<p>With promises, we&#39;re going to split the processing, still in a single &quot;stream&quot; of activity, into two steps, to show the use of <code>then</code>. Notice the first <code>then</code> doesn&#39;t return a Promise, just a normal object. ...

<pre><code>sub fetchpages {
  # Stop if there are no more URLs
  return unless my $url = shift @urls;
  # Fetch the next title
  $ua-&gt;get_p($url)-&gt;then(sub {
    my ($tx) = @_;
    $tx-&gt;result;
  })-&gt;then(sub {
    my ($result) = @_;
    say &quot;$url: &quot;, $result-&gt;dom-&gt;at(&#39;title&#39;)-&gt;text;
    fetchpages(); # returns a promise, but of a whole new page to process
  });
}
</code></pre>

<p>Here you&#39;ll see we&#39;re using <a href="http://mojolicious.org/perldoc/Mojo/UserAgent#get_p"><code>get_p</code></a>. This is just like <a href="http://mojolicious.org/perldoc/Mojo/UserAgent#get"><code>get</code></a>, but instead of taking a c...

<h2>Promises with two streams</h2>

<p>Given that a Promise is a single chain of processing steps, how can we have a number of them running concurrently, without making all the requests at once? We&#39;ll use two ideas: chaining (shown above - the key is each &quot;then&quot; returns a...

<pre><code>sub fetchpages {
  # Stop if there are no more URLs
  return unless my $url = shift @urls;
  # Fetch the next title
  $ua-&gt;get_p($url)-&gt;then(sub {
    my ($tx) = @_;
    $tx-&gt;result;
  })-&gt;then(sub {
    my ($result) = @_;
    say &quot;$url: &quot;, $result-&gt;dom-&gt;at(&#39;title&#39;)-&gt;text;
    fetchpages(); # returns a promise, but of a whole new page to process
  });
}

# Process two requests at a time
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>

devdata/https_mojolicious.io_blog_2017_12_16_day-16-the-secret-life-of-sessions  view on Meta::CPAN

<p>This happens because you are using the default secret for the application.
This default is just the name of the script, as you can see via the <a href="/blog/2017/12/05/day-5-your-apps-built-in-commands">eval command</a></p>

<pre><code>$ perl myapp.pl eval -V &#39;app-&gt;secrets&#39;
[
  &quot;myapp&quot;
]
</code></pre>

<p>This secret is not secure both because it is short and because it is easy to guess.
With a trivial application like this you might not need to worry about forgery, as you would with say a session that tracks user logins.
But who knows, perhaps you are going to award a prize to the user for the most requests made!
Let&#39;s play it safe.</p>

<p>The secret isn&#39;t something you need to remember, it just has to be hard to guess.
So I suggest you pick a random one.
You could generate 12 characters of random text using</p>

<pre><code>$ &lt;/dev/urandom base64 | head -c 12
yuIB7m88wS07
</code></pre>

devdata/https_mojolicious.io_blog_2017_12_16_day-16-the-secret-life-of-sessions  view on Meta::CPAN

Congratulations, you have a safer application already!</p>

<p>If sometime later, you suspect that someone has guessed your secret, or if your secret leaks out, you can change that secret and restart your application.
This will protect your application from malicious users.</p>

<p>For your clients, this will have the jarring effect that all existing sessions will be invalidated.
In the example application the counter would be reset.
If instead the session were being used to keep users logged in, they would suddenly be logged out.
If it was for tracking a shopping cart ... no more shopping cart.</p>

<p>This can actually be useful even if your secret is safe but you want to force-invalidate sessions for some other reason, like say your application was generating corrupt data or worse.
Generally, however, this is something you&#39;d like to avoid.</p>

<h2>A Random Secret?</h2>

<p>Now perhaps you are asking yourself, if Mojolicious knows I&#39;m using the insecure default couldn&#39;t it just set a random secret?
Sure, and you could do so yourself too if you wanted.
Something as easy as this would set a random secret.</p>

<pre><code class="hljs"><span class="hljs-keyword">use</span> <span class="hljs-function">Mojolicious::Lite</span>;

devdata/https_mojolicious.io_blog_2017_12_16_day-16-the-secret-life-of-sessions  view on Meta::CPAN

<p>Just as you have to change application passwords from time to time, so too you need to change your secret.
In a brute force scenario, a nefarious user could take one of their cookies and try to reverse engineer the secret that was used to generate it.</p>

<p>But wait!
You just said that changing the secret to prevent brute forcing will invalidate all of our sessions!</p>

<p>Remember when I pointed out that rather than being a single value, <code>secrets</code> was an array reference?
Well when Mojolicious generates a session cookie, it does so using the first value in the array reference.
However, when it validates session signatures, it will check them against each secret in the array.</p>

<p>So, say the time has come to rotate our secrets so we generate another</p>

<pre><code>$ &lt;/dev/urandom base64 | head -c 12
w8S4b+90CWwf
</code></pre>

<p>add that value to the configuration file</p>

<pre><code class="hljs">{
  secrets =&gt; [
    &#39;<span class="hljs-string">w8S4b+90CWwf</span>&#39;,

devdata/https_mojolicious.io_blog_2017_12_19_day-19-make-your-app-installable  view on Meta::CPAN

It also assumes that your application <a href="http://mojolicious.org/perldoc/Mojolicious#home"><code>home</code></a> should be related to the installation directory, which isn&#39;t always the case.</p>

<p>Yours truly has even written a <a href="https://metacpan.org/pod/Mojolicious::Plugin::InstallablePaths">module</a> that was intended to ease this process somewhat.
While it does that, and I don&#39;t intend to deprecate it, I think there are even easier patterns now.</p>

<h2>The Share Directory</h2>

<p>Perl has a lesser known and somewhat informal functionality for bundling static files called a &quot;share directory&quot;.
Create a directory in your project root called <code>share</code> which will serve for this purpose.
Then move the <code>templates</code> and <code>public</code> directories from your project root into that directory.
You should also move any other static files like say database migration files (e.g. <code>wishlist.sql</code> from <a href="https://mojolicious.io/blog/2017/12/18/day-18-the-wishlist-model/">yesterday</a>).</p>

<p>Although each install tool has different ways of specifying where the share directory is located during the development phase, none is espectially difficult to work with.
One reason I chose the name <code>share</code> is because my preferred installation tool <a href="https://metacpan.org/pod/Module::Build::Tiny">Module::Build::Tiny</a> (which I use via <a href="https://metacpan.org/pod/App::ModuleBuildTiny">App::Modu...
The others are configurable in the install scripts themselves (Makefile.PL/Build.PL/dist.ini).
For <a href="https://metacpan.org/pod/Module::Build">Module::Build</a>, you set the <a href="https://metacpan.org/pod/Module::Build::API#share_dir"><code>share_dir</code></a> parameter</p>

<pre><code>share_dir =&gt; &#39;share&#39;
</code></pre>

<p>For <a href="https://metacpan.org/pod/ExtUtils::MakeMaker">ExtUtils::MakeMaker</a> use <a href="https://metacpan.org/pod/File::ShareDir::Install">File::ShareDir::Install</a></p>

devdata/https_mojolicious.io_blog_2017_12_19_day-19-make-your-app-installable  view on Meta::CPAN

</code></pre>

<h2>What About The App Home?</h2>

<p>So far we have been focusing on bundled static files.
As we saw in the Cookbook entry, one other consideration for installable applications is its <code>home</code>.</p>

<p>Apart from setting the above paths, which we no longer need it to do, the other main job of the <code>home</code> is to locate data from the application user rather than the application&#39;s own files.
Here it is still amply useful!</p>

<p>The first and most import example of this is loading configuration via say <a href="http://mojolicious.org/perldoc/Mojolicious/Plugin/Config#file">Mojolicious::Plugin::Config</a>.
The user will be provide configuration and almost certainly not from the installed location of the application, unlike its bundled support files.</p>

<p>This also provides another conundrum, if the home is used to load the configuration, then it cannot be set by via the configuration.
No, the home really needs to be set by the user via the <code>MOJO_HOME</code> environment variable <a href="http://mojolicious.org/perldoc/Mojo/Home#detect">as documented</a>.
If this isn&#39;t desirable, some other environment variable could be used by providing your own in your class, overriding the one from the parent.</p>

<pre><code>has home =&gt; sub {
  Mojo::Home-&gt;new($ENV{WISHLIST_HOME});
};
</code></pre>

devdata/https_mojolicious.io_blog_2017_12_21_day-21-virtually-a-lumberjack  view on Meta::CPAN

</code></pre>

<p>And add a method which progresses us through the script:</p>

<pre><code>sub instruction_show {
  my $self = shift;

  my $instruction = shift @{$self-&gt;instructions};
  return $self-&gt;loop-&gt;stop_gracefully unless defined $instruction;

  say $instruction-&gt;[1];
  $self-&gt;loop-&gt;timer(
    $instruction-&gt;[0] =&gt; sub { $self-&gt;instruction_show },
  );

  $self-&gt;loop-&gt;timer($instruction-&gt;[0] / 2, sub {
    my $type = (split / /, $self-&gt;instructions-&gt;[0][1])[1];
    my $file_count = $self-&gt;file_count;
    my $path = sprintf &#39;%i_%s.pgm&#39;, $file_count, uc $type;
    $self-&gt;file( $self-&gt;file_start($path) );
  }) if $instruction-&gt;[1] eq &#39;Steady&#39;;

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


<p>This question comes up quite often after telling people about OpenAPI:
&quot;but...why?&quot; The people asking this often come from the same background as
myself, where you both write the producer (backend web server) and consumer
(JavaScript, ...) of the data. When you&#39;re in complete control of both sides
you don&#39;t really need to write any formal specification or document your API,
since you already <em>know</em> how it works. This can be very true - at least if you
make sure you have tests of all your endpoints.</p>

<p>Personally I&#39;m a <a href="http://www.linkognito.com/images/05-08-08/metal.gif">huge fan</a>
of documenting as well as testing. I often say that if you can&#39;t document
(describe) the API/method/whatever, something is wrong and you should reconsider
what you&#39;re coding. Documenting an API on the other hand is awful in my opinion,
especially during rapid prototyping/developing where it&#39;s hard to keep the
code and documentation up to date.</p>

<p>So how does OpenAPI fix this? Since the input/ouput Perl code is generated
<em>from</em> the OpenAPI Specification, you know that the backend is always running
code accordingly to the specification. Also, since the documentation you
generate is not hand written, but generated from the same OpenAPI document you
can with certainty know that the code the server is running, is in sync with
the documentation.</p>

<p>By &quot;generated code&quot; I don&#39;t just mean the routes/endpoints, but also input and
output validation. This means that when any data has made it through to your
controller action, you know that the data is valid. On the other side, the
consumer (let&#39;s say a JavaScript that cares about the difference between an
integer and string) will know that it has received the correct data, since
invalid output will result in a
<a href="https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#5xx_Server_Error">500 - Internal Server Error</a>.</p>

<p>So... If you don&#39;t care about documentation or collaboration with others, then
I&#39;m not sure if I would care much about OpenAPI either.</p>

<p>Note that the OpenAPI spec is not just for the server, but can also be used to
generate <a href="int://github.com/swagger-api/swagger-js">JavaScript</a> and
<a href="https://metacpan.org/pod/OpenAPI::Client">Perl client side code</a>.</p>

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

Perhaps you have a file full of sites for which you want to get some data.</p>

<pre><code>$ cat sites
mojolicious.org
mojolicious.io
mojocasts.com
</code></pre>

<p>You can then take each line, request it, and post-processes.</p>

<pre><code>perl -Mojo -nlE &#39;say g($_)-&gt;dom-&gt;at(&quot;title&quot;)-&gt;text&#39; sites
</code></pre>

<h3>Object Constructors</h3>

<p>The Mojo toolkit contains many helpful classes.
The ojo functions provide quick constructors for them, so you can access their functionality without all the typing!</p>

<ul>
<li><a href="http://mojolicious.org/perldoc/Mojo/ByteStream">Mojo::ByteStream</a> - <code>b</code></li>
<li><a href="http://mojolicious.org/perldoc/Mojo/Collection">Mojo::Collection</a> - <code>c</code></li>
<li><a href="http://mojolicious.org/perldoc/Mojo/DOM">Mojo::DOM</a> - <code>x</code></li>
<li><a href="http://mojolicious.org/perldoc/Mojo/File">Mojo::File</a> - <code>f</code></li>
</ul>

<p>So you can slurp an HTML file with <code>f</code>, then build DOM object with <code>x</code> and get its title</p>

<pre><code>perl -Mojo -E &#39;say x(f(shift)-&gt;slurp)-&gt;at(&quot;title&quot;)-&gt;text&#39; test.html
</code></pre>

<p>or as we did earlier, get urls from a file, but this time <code>trim</code> unslightly whitespace from the output</p>

<pre><code>perl -Mojo -nlE &#39;say b(g($_)-&gt;dom-&gt;at(&quot;title&quot;)-&gt;text)-&gt;trim&#39; sites
</code></pre>

<p>We can slurp the <code>sites</code> file itself, split the lines and ouput it as JSON.</p>

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

<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

devdata/https_mojolicious.io_blog_2017_12_24_day-24-release-and-wrap-up  view on Meta::CPAN

Rare items can be discussed, found, and purchased.
News can spread as fast as fingers can type them.</p>

<p>But for all this ease and convenience there is a price.
Personal data is being taken, mined, and sold.
People are buying products that require internet connections but whose manufacturers will drop support before the hardware fails.
Statements you make and pictures you share will live on on the internet forever, even if you change.</p>

<h3>Fighting Back</h3>

<p>I would never say that the bad outweighs the good, but I do wish people would be more intentional about these types of choices online.
Yet that almost cannot happen until they have alternatives that they control.
To that end I have been trying in the past year to start to take control of my digital life where possible.
I have deployed <a href="https://nextcloud.com/">NextCloud</a> on a private server to keep my files and share with people I choose.
I wrote an application called <a href="https://github.com/jberger/CarPark">CarPark</a> to control my garage door via a RaspberryPi and even spoke about it at <a href="https://www.youtube.com/watch?v=aJc5yYONBBc">The Perl Conference</a>; it isn&#39;t ...

<p>And that brings us to the <a href="https://github.com/jberger/Wishlist">Wishlist</a> application.
Several sites, some large and some small, offer similar services: tracking your wishlists and aiding gift buying, all for free.
However, this too must also come at a cost.</p>

<p>In this article I&#39;m announcing that after adding a few tweaks around user registration, I have now released the Wishlist application to <a href="https://metacpan.org/pod/Wishlist">CPAN</a>.



( run in 1.666 second using v1.01-cache-2.11-cpan-483215c6ad5 )