view release on metacpan or search on metacpan
Preamble
The license agreements of most software companies try to keep users
at the mercy of those companies. By contrast, our General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. The
General Public License applies to the Free Software Foundation's
software and to any other program whose authors commit to using it.
You can use it for your programs, too.
When we speak of free software, we are referring to freedom, not
price. Specifically, the General Public License is designed to make
sure that you have the freedom to give away or sell copies of free
software, that you receive source code or can get it if you want it,
that you can change the software or use pieces of it in new free
programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
Program or a portion of it, either verbatim or with modifications. Each
licensee is addressed as "you".
1. You may copy and distribute verbatim copies of the Program's source
code as you receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice and
disclaimer of warranty; keep intact all the notices that refer to this
General Public License and to the absence of any warranty; and give any
other recipients of the Program a copy of this General Public License
along with the Program. You may charge a fee for the physical act of
transferring a copy.
2. You may modify your copy or copies of the Program or any portion of
it, and copy and distribute such modifications under the terms of Paragraph
1 above, provided that you also do the following:
a) cause the modified files to carry prominent notices stating that
you changed the files and the date of any change; and
b) cause the whole of any work that you distribute or publish, that
in whole or in part contains the Program or any part thereof, either
c) If the modified program normally reads commands interactively when
run, you must cause it, when started running for such interactive use
in the simplest and most usual way, to print or display an
announcement including an appropriate copyright notice and a notice
that there is no warranty (or else, saying that you provide a
warranty) and that users may redistribute the program under these
conditions, and telling the user how to view a copy of this General
Public License.
d) You may charge a fee for the physical act of transferring a
copy, and you may at your option offer warranty protection in
exchange for a fee.
Mere aggregation of another independent work with the Program (or its
derivative) on a volume of a storage or distribution medium does not bring
the other work under the scope of these terms.
3. You may copy and distribute the Program (or a portion or derivative of
it, under Paragraph 2) in object code or executable form under the terms of
Paragraphs 1 and 2 above provided that you also do one of the following:
years, to give any third party free (except for a nominal charge
for the cost of distribution) a complete machine-readable copy of the
corresponding source code, to be distributed under the terms of
Paragraphs 1 and 2 above; or,
c) accompany it with the information you received as to where the
corresponding source code may be obtained. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form alone.)
Source code for a work means the preferred form of the work for making
modifications to it. For an executable file, complete source code means
all the source code for all modules it contains; but, as a special
exception, it need not include source code for modules which are standard
libraries that accompany the operating system on which the executable
file runs, or for standard header files or definitions files that
accompany that operating system.
4. You may not copy, modify, sublicense, distribute or transfer the
Program except as expressly provided under this General Public License.
Any attempt otherwise to copy, modify, sublicense, distribute or transfer
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_09_day-9-the-best-way-to-test view on Meta::CPAN
<pre><code>use Mojolicious::Lite;
plugin 'MyCoolPlugin';
my $t = Test::Mojo->new;
</code></pre>
<p>Testing a Full app couldn't be simpler, you just pass it a class name for it to instantiate.</p>
<pre><code>my $t = Test::Mojo->new('MyApp');
</code></pre>
<p>When instantiating a Full app you can actually pass it a second argument, a hash reference of configuration overrides.
This can be especially handy for overriding things like database parameters to access a test instance rather than your regular database.
Of couse how you use your configuration might vary but if your app does something like</p>
<pre><code>has pg => sub {
my $app = shift;
return Mojo::Pg->new($app->config->{pg});
};
</code></pre>
<p>Then you could override whatever configuration might be present in your system by doing</p>
<pre><code>my $t = Test::Mojo->new('MyApp', {pg => 'postgresql://testuser:testpass@/testdb'});
</code></pre>
<p>If you use <a href="http://mojolicious.org/perldoc/Mojolicious/Plugin/Config">Mojolicious::Plugin::Config</a> or <a href="http://mojolicious.org/perldoc/Mojolicious/Plugin/JSONConfig">Mojolicious::Plugin::JSONConfig</a> or one of several other thi...
If you use some other loader on CPAN (that is Mojolicious aware) and it doesn't support this somewhat newish feature, please point it out to the author, it is easy to add.
Note that this does not work for Lite apps because it has inject the configuration overrides as the application is being built, something which isn't possible for a Lite app.</p>
<p>Finally you might find yourself in a situation where you already have an instantiated application.
If that is the case just pass it to the constructor.</p>
<pre><code>my $app = build_my_app();
my $t = Test::Mojo->new($app);
</code></pre>
<h2>How It Works</h2>
devdata/https_mojolicious.io_blog_2017_12_11_day-11-useragent-content-generators view on Meta::CPAN
<span class="hljs-keyword"><methodCall></span>
<span class="hljs-keyword"><methodName></span>target_method<span class="hljs-keyword"></methodName></span>
<span class="hljs-keyword"><params></span>
<span class="hljs-keyword"><param><value><string></span>arg1<span class="hljs-keyword"></string></value></param></span>
<span class="hljs-keyword"><param><value><string></span>arg2<span class="hljs-keyword"></string></value></param></span>
<span class="hljs-keyword"></params></span>
<span class="hljs-keyword"></methodCall></span>
</code></pre>
<p>Although the usage isn't terribly difficult, how would it look as a Content Generator?</p>
<pre><code class="hljs"><span class="hljs-keyword">use</span> <span class="hljs-function">Mojo::Base</span> -strict;
<span class="hljs-keyword">use</span> <span class="hljs-function">Mojo::UserAgent</span>;
<span class="hljs-keyword">use</span> <span class="hljs-function">Mojo::XMLRPC</span> qw[encode_xmlrpc decode_xmlrpc];
<span class="hljs-keyword">my</span> <span class="hljs-type">$ua</span> = <span class="hljs-function">Mojo::UserAgent</span>->new;
<span class="hljs-type">$ua</span>-><span class="hljs-type">transactor</span>-><span class="hljs-type">add_generator</span>(xmlrpc => <span class="hljs-keyword">sub </span>{
<span class="hljs-keyword">my</span> (<span class="hljs-type">$transactor</span>, <span class="hljs-type">$tx</span>, <span class="hljs-type">@args</span>) = <span class="hljs-type">@_</span>;
<span class="hljs-type">$tx</span>-><span class="hljs-type">req</span>-><span class="hljs-type">headers</span>-><span class="hljs-type">content_type</span>('<span class="hljs-string">text/xml</span>');
<span class="hljs-type">$tx</span>-><span class="hljs-type">req</span>-><span class="hljs-type">body</span>(encode_xmlrpc(call => <span class="hljs-type">@args</span>));
devdata/https_mojolicious.io_blog_2017_12_12_day-12-more-than-a-base-class view on Meta::CPAN
use Mojo::Base 'My::Class';
</code></pre>
<p>You saw this usage quite a bit in the Full app example on <a href="/blog/2017/12/06/day-6-adding-your-own-commands">Day 6</a>.
Mojo::Base only supports single-inheritance because we don't want to encourage bad practices.
If you know why you might want to use multipe-inheritance, you probably know how to get around this limitation as well.</p>
<h3>Attributes and Constructor</h3>
<p>Mojo::Base implements a class method which can be used to add attributes to the class (called <code>attr</code>).
While this is necessary for the implementation, this isn't the preferred usage.</p>
<p>The <code>has</code> keyword, added by the import above, gives us that nice declarative style that Perl users are familiar with from Moose and Moo.
The usage is different from those, owing to the limited choices available in declaration.
<code>has</code> takes a name or an array reference of name of the attribute(s) to declare.
It then optionally takes either a simple (non-reference) scalar default value or a callback to be used as a lazy builder.
When the lazy builder is called, it gets as an argument the instance itself.
That's it, clean and simple.</p>
<pre><code>package My::Class;
use Mojo::Base -base;
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_19_day-19-make-your-app-installable view on Meta::CPAN
While it does that, and I don'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 "share directory".
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 => 'share'
</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>
<pre><code>use File::ShareDir::Install;
install_share 'share';
devdata/https_mojolicious.io_blog_2017_12_19_day-19-make-your-app-installable view on Meta::CPAN
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'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'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 => sub {
Mojo::Home->new($ENV{WISHLIST_HOME});
};
</code></pre>
<p>Though <code>MOJO_HOME</code> is likely a fine place to start.</p>
<p>From there, you might (in some cases) want the users to be able to provide their own static files and/or templates.
Say if your application could be themed.
devdata/https_mojolicious.io_blog_2017_12_20_day-20-practical-testing view on Meta::CPAN
<div class="post-content">
<section id="section-1">
<p>Back on <a href="/blog/2017/12/09/day-9-the-best-way-to-test">Day 9</a> we discussed testing and especially <a href="http://mojolicious.org/perldoc/Test/Mojo">Test::Mojo</a>.
Today I want to just briefly talk about some practical things that can come up when testing real world applications.
Once again the discussion will be motivated by the <a href="https://github.com/jberger/Wishlist">Wishlist</a> application that we've been developing these past few days.</p>
</section>
<section id="section-2">
<h2>Configuration Overrides</h2>
<p>In the Day 9 article I mentioned that the Test::Mojo <a href="http://mojolicious.org/perldoc/Test/Mojo#new">constructor</a> could be passed configuration overrides.
In this example we can see how that override lets us ensure that we are testing on a fresh and isolated database.</p>
<pre><code class="hljs"><span class="hljs-keyword">use</span> <span class="hljs-function">Mojo::Base</span> -strict;
<span class="hljs-keyword">use</span> <span class="hljs-function">Test::More</span>;
<span class="hljs-keyword">use</span> <span class="hljs-function">Test::Mojo</span>;
<span class="hljs-keyword">my</span> <span class="hljs-type">$t</span> = <span class="hljs-function">Test::Mojo</span>->new(
'<span class="hljs-string">Wishlist</span>',
{database => '<span class="hljs-string">:temp:</span>'}
);
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>
devdata/https_mojolicious.io_blog_2017_12_24_day-24-release-and-wrap-up view on Meta::CPAN
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'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'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>.
I hope you can use it to keep a slightly tighter grasp on your digital footprint without sacrificing utility.</p>
<p>You can deploy it on a VPS, like <a href="https://www.linode.com/">Linode</a> or <a href="https://www.scaleway.com/">Scaleway</a>.
You could even use it on that old desktop computer in your home office as long as you are careful about network security.
t/00-compile.t view on Meta::CPAN
use File::Spec;
use IPC::Open3;
use IO::Handle;
open my $stdin, '<', File::Spec->devnull or die "can't open devnull: $!";
my @warnings;
for my $lib (@module_files)
{
# see L<perlfaq8/How can I capture STDERR from an external command?>
my $stderr = IO::Handle->new;
diag('Running: ', join(', ', map { my $str = $_; $str =~ s/'/\\'/g; q{'} . $str . q{'} }
$^X, @switches, '-e', "require q[$lib]"))
if $ENV{PERL_COMPILE_TEST_DEBUG};
my $pid = open3($stdin, '>&STDERR', $stderr, $^X, @switches, '-e', "require q[$lib]");
binmode $stderr, ':crlf' if $^O eq 'MSWin32';
my @_warnings = <$stderr>;
waitpid($pid, 0);
is($?, 0, "$lib loaded ok");
shift @_warnings if @_warnings and $_warnings[0] =~ /^Using .*\bblib/
and not eval { +require blib; blib->VERSION('1.01') };
if (@_warnings)
{
warn @_warnings;
push @warnings, @_warnings;