view release on metacpan or search on metacpan
Holder, and derivatives of that collection of files created through
textual modification.
- "Standard Version" refers to such a Package if it has not been modified,
or has been modified in accordance with the wishes of the Copyright
Holder.
- "Copyright Holder" is whoever is named in the copyright or copyrights for
the package.
- "You" is you, if you're thinking about copying or distributing this Package.
- "Reasonable copying fee" is whatever you can justify on the basis of media
cost, duplication charges, time of people involved, and so on. (You will
not be required to justify it to the Copyright Holder, but only to the
computing community at large as a market that must bear the fee.)
- "Freely Available" means that no fee is charged for the item itself, though
there may be fees involved in handling the item. It also means that
recipients of the item may redistribute it under the same conditions they
received it.
1. You may make and give away verbatim copies of the source form of the
Standard Version of this Package without restriction, provided that you
duplicate all of the original copyright notices and associated disclaimers.
devdata/https_mojolicious.io_blog_2017_12_06_day-6-adding-your-own-commands view on Meta::CPAN
<p>We'll store it in <a href="https://github.com/jberger/MyWeatherApp/blob/master/lib/MyWeatherApp/Model/Weather.pm">lib/MyWeatherApp/Model/Weather.pm</a>.</p>
<pre><code>package MyWeatherApp::Model::Weather;
use Mojo::Base -base;
use Carp ();
use Mojo::URL;
use Mojo::UserAgent;
has appid => sub { Carp::croak 'appid is required' };
has sqlite => sub { Carp::croak 'sqlite is required' };
has ua => sub { Mojo::UserAgent->new };
has 'units';
sub fetch {
my ($self, $search) = @_;
my $url = Mojo::URL->new('http://api.openweathermap.org/data/2.5/weather');
$url->query(
q => $search,
APPID => $self->appid,
units => $self->units || 'metric',
devdata/https_mojolicious.io_blog_2017_12_06_day-6-adding-your-own-commands view on Meta::CPAN
FROM weather
WHERE search=?
ORDER BY time ASC
SQL
}
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>
devdata/https_mojolicious.io_blog_2017_12_06_day-6-adding-your-own-commands view on Meta::CPAN
<p>It defines the moniker, which in turn defines the configuration file name.
It then loads configuration; this is necessary since we need your appid for OpenWeatherMap.
It tells the app that we are going to define some application-specific commands under <a href="https://github.com/jberger/MyWeatherApp/tree/master/lib/MyWeatherApp/Command">MyWeatherApp::Command</a>.</p>
<p>Then there's something I haven't shown you before, a helper.
Helpers are like methods but they are available to the app and the controllers and even to templates.
They always receive a controller, even if they are called on the app.
Helpers are very useful for tying parts of your application together.
In this case we use one to build and return an instance of our model class.
It attaches that required data that we noted earlier.</p>
<p>Moving on, the application now defines a route.
As we'll see later it is attached to the <a href="https://github.com/jberger/MyWeatherApp/blob/master/lib/MyWeatherApp/Controller/Weather.pm">Weather controller class</a> and more specifically its <code>recall</code> action method.
This is much like the action callbacks we saw before, but by keeping it in a separate class the application class is easier to read.</p>
<p>Finally we define the database schema.
This is a format common to the Mojo-flavored database modules, like <a href="http://mojolicious.org/perldoc/Mojo/Pg">Mojo::Pg</a>, <a href="https://metacpan.org/pod/Mojo::mysql">Mojo::mysql</a>, and <a href="https://metacpan.org/pod/Mojo::SQLite">Moj...
Each section is defined with a version number and the word <code>up</code> or <code>down</code>.
When migrating versions, it will apply each change set from the current version (beginning at 0) until the version you request.
If you don't request a version it gets the highest version.</p>
devdata/https_mojolicious.io_blog_2017_12_06_day-6-adding-your-own-commands view on Meta::CPAN
<pre><code>package MyWeatherApp::Controller::Weather;
use Mojo::Base 'Mojolicious::Controller';
sub recall {
my $c = shift;
my $search = $c->param('q');
return $c->render(
status => 400,
text => 'q parameter is required',
) unless $search;
my $data = $c->weather->recall($search);
$c->render(json => $data);
}
1;
</code></pre>
<p>When you request the <code>/weather</code> route with a query parameter that we've cached some data for, it will return that data.
devdata/https_mojolicious.io_blog_2017_12_12_day-12-more-than-a-base-class view on Meta::CPAN
<p>The callbacks are always lazy, meaning if the value of that attribute hasn't been established, either via the constructor or via a setter, then the default is used or the builder is run.</p>
<p>The default constructor (<code>new</code>), inherited from Mojo::Base, takes a hash reference or key-value pairs and uses them as initialization for the defined attributes.</p>
<pre><code>my $obj = My::Class->new(foo => 'bar', max => 10);
my $obj = My::Class->new({foo => 'bar', max => 10}); # same
</code></pre>
<p>Note that there is nothing to prevent you from passing data that isn't for a defined attribute (ie, the constructor isn't <a href="https://metacpan.org/pod/MooX::StrictConstructor">strict</a>).
Nor is there anything that declares a required attribute, though you can easily make one</p>
<pre><code>has 'username' => sub { die 'username is required' };
</code></pre>
<h3>Accessor Methods</h3>
<p>A read/write accessor method is installed into the class for each declared attribute.
However, have one major difference from other common Perl object systems.
While the getters return the value as expected,</p>
<pre><code>my $foo = $self->foo;
my $value = $self->data->{key};
devdata/https_mojolicious.io_blog_2017_12_13_day-13-more-about-roles view on Meta::CPAN
Said another way, roles let you specify what a class does without changing what it is.
For a better description, check out Toby Inkster's article <a href="http://radar.oreilly.com/2014/01/horizontal-reuse-an-alternative-to-inheritance.html">Horizontal Reuse: An Alternative to Inheritance</a>.</p>
<p>An important utility of roles is that you can easily use more than one role at the same time in the same consuming class.
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't be or weren't used in Mojolicious before then, only that Mojo::Base didn'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.
devdata/https_mojolicious.io_blog_2017_12_13_day-13-more-about-roles view on Meta::CPAN
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 ...
While I won't discuss these in depth, if you need to change how a method from a consuming class works by adding behavior before and/or after that method, check out method modifiers.</p>
<p>The third thing you might notice is the name of the package that I chose.
The only thing that limits what classes the role can be consumed by is the <code>requires</code> keyword.
However clearly this role is intended to work with functionality provided by Mojo::DOM and so this name is a good choice.</p>
<h2>Composing Roles</h2>
<p>Now that we know how to create a role, how is it used?
Well, let's continue with the prior example.</p>
devdata/https_mojolicious.io_blog_2017_12_15_day-15-start-a-new-yancy-app view on Meta::CPAN
plugin</a> and tell it
about our backend and data. Yancy deals with data as a set of
collections which contain items. For a relational database like
Postgres, a collection is a table, and an item is a row in that table.</p>
<p>Yancy uses a JSON schema to describe each item in a collection.
For our <code>blog</code> collection, we have five fields:</p>
<ol>
<li><code>id</code> which is an auto-generated integer and should be read-only</li>
<li><code>title</code> which is a free-form string which is required</li>
<li><code>created</code> which is an ISO8601 date/time string, auto-generated</li>
<li><code>markdown</code> which is a required Markdown-formatted string</li>
<li><code>html</code>, a string which holds the rendered Markdown and is also required</li>
</ol>
<p>Here's our configured Yancy <code>blog</code> collection:</p>
<pre><code class="hljs">plugin Yancy => {
backend => '<span class="hljs-string">pg://localhost/blog</span>',
collections => {
blog => {
required => [ '<span class="hljs-string">title</span>', '<span class="hljs-string">markdown</span>', '<span class="hljs-string">html</span>' ],
properties => {
id => {
type => '<span class="hljs-string">integer</span>',
readOnly => <span class="hljs-number">1</span>,
},
title => {
type => '<span class="hljs-string">string</span>',
},
created => {
type => '<span class="hljs-string">string</span>',
devdata/https_mojolicious.io_blog_2017_12_18_day-18-the-wishlist-model view on Meta::CPAN
<h2>The Model Class</h2>
<p>I extracted the business logic from the <a href="https://metacpan.org/pod/SQL::Abstract">original application's controller actions</a>, anything that handled persistence, and moved them to a dedicated class, <a href="https://github.com/jberger...
<pre><code class="hljs"><span class="hljs-keyword">package</span> <span class="hljs-function">Wishlist::Model</span>;
<span class="hljs-keyword">use</span> <span class="hljs-function">Mojo::Base</span> -base;
<span class="hljs-keyword">use</span> Carp ();
has sqlite => <span class="hljs-keyword">sub </span>{ <span class="hljs-function">Carp::croak</span> '<span class="hljs-string">sqlite is required</span>' };
<span class="hljs-keyword">sub </span><span class="hljs-function">add_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">return</span> <span class="hljs-type">$self</span><span class="hljs-type">
</span> ->sqlite
->db
->insert(
'<span class="hljs-string">users</span>',
{name => <span class="hljs-type">$name</span>},
)->last_insert_id;