CGI::Application::Plugin::REST Instance Script - perl

I am attempting to build a Restful service with CGI::Application::Plugin::REST, but I am not sure how to actually structure the script. Is it the same as using CGI::Application so the below will be the Instance Script referencing modules withing the Perl library path?
use CGI::Application::Plugin::REST qw( :all );
$self->rest_route(
'/Stage/:id' => {
'GET' => 'Stage',
},
'/Status/:id' => {
'GET' => 'Status',
},
'/Metadate/:id' => {
'GET' => 'Metadata',
},
$self->run();
I will admit I am probably learning CGI::Application backwards, and am looking for an easy way out as once the framework is done the rest is very achievable. Also I did not want to use MVC frameworks as I wanted to build it from scratch. Thanks for the help in advance.

Since you said you want to use this as a structure to build off of, I'd recommend putting your CGI::App logic in its own module. This will make it easier to test later when you start writing tests. Your App.pm will look like a normal CGI::App controller:
package App;
use strict;
use parent 'CGI::Application';
use CGI::Application::Plugin::REST ':all';
sub setup {
my $self = shift;
$self->rest_route(
'/some/route' => {
'GET' => 'read_widget',
'POST' => 'save_widget',
},
);
}
sub read_widget { ... }
sub save_widget { ... }
1;
And then in your instance script that the web server will call, you can simply use App; App->new->run;
You can find a fully functioning example in the C::A::P::REST test lib.

Related

Template in Mojolicious controller not found

I am trying to serve a template contained in the __DATA__ section of a controller class, but it doesn't seem to work.
In my main app.pl file I have
#!/usr/bin/env perl
use Mojolicious::Lite -signatures;
use FindBin qw($Bin);
use lib "$Bin/lib";
push #{app->renderer->classes}, 'Zairyo::Controller::Data';
push #{app->preload_namespaces}, 'Zairyo::Controller::Data';
get '/:uid' => [uid => qr/[a-z0-9]{32,32}/i ] => { controller => 'Data', action => 'serve_iframe' };
app->start;
and in Zairyo::Controller::Data:
package Zairyo::Controller::Data;
use Mojo::Base 'Mojolicious::Controller', -signatures;
sub serve_iframe ($c) {
$c->render(template => 'foo');
}
__DATA___
## foo.html.ep
what is this
which I'd expect to work as per the documentation but instead throws an error Could not render a response... on the browser and Template "foo.html.ep" not found on the log.
I've solved this by doing
$c->render(inline => data_section(__PACKAGE__, 'foo.html.ep') );
but it seems a bit of a hack.
What am I doing wrong here?
First, there are a few things a bit off in your Data.pm:
There are three underscores after __DATA when there should be two and a newline
The module does not return a true value
Here's what I ended up with:
package Zairyo::Controller::Data;
use Mojo::Base 'Mojolicious::Controller', -signatures;
sub serve_iframe ($c) {
$c->render(template => 'foo' );
}
1;
__DATA__
## foo.html.ep
what is this
In the main script, I load the class before I call start. Note that the docs say:
Note that for templates to be detected, these classes need to have already been loaded and added before warmup is called
And, warmup is called immediately by start, and it's warmup that cares about preload_namespaces. You need to get there even sooner, so preload_namespaces does nothing for this particular problem. If you haven't already loaded the module, its __DATA__ templates will not be detected.
#!/usr/bin/env perl
use Mojolicious::Lite -signatures;
use FindBin qw($Bin);
use lib "$Bin/lib";
push #{app->renderer->classes}, map { Mojo::Loader::load_class($_); $_ } 'Zairyo::Controller::Data';
get '/:uid' => [uid => qr/[a-z0-9]{32,32}/i ] => {
namespace => 'Zairyo::Controller',
controller => 'Data',
action => 'serve_iframe'
};
app->start;
I'm not suggesting this particular code, but now I know why you weren't getting what you wanted.

Perl share variables with subclasses

I know this may be a very simple topic but I am trying to get the best logic since I am still new to Perl.
If I do not use OO and just split the code into files, all global variables are accessed among all files.
I am trying to do the same but using OO style. Example is I want a base class say called "BaseSub" that has a hash containing the configuration for the application say called %Config. Now I have a sub class called "DB" for the database connection and I want to access the settings from %Config which lives in "BaseSub" package. How do I do that.
If you're writing OO perl in this day and age, you really should be using Moose. It makes OO code much easier, cleaner and smaller.
The proper way to inherit variables is to make object attributes. Here's a quick example:
package MyBaseClass;
use Moose;
has config => (
is => 'ro',
default => sub { {
who => 'World',
} }
);
package MyClass;
use Moose;
extends qw(MyBaseClass);
sub greet
{
my $self = shift;
printf("Hello %s!\n", $self->config->{who});
}
package main;
my $object = MyClass->new();
$object->greet();
A great starting point for learning about Moose is the Moose::Manual.
Edit:
If you want be able to modify the config, you can either just poke the hashref returned from the config accessor directly:
$object->config->{who} = 'Friends';
But a better approach might be to make a config class and make the config attribute hold an instance of that:
package Myconfig;
use Moose;
has who => (is => 'rw', default => 'World');
package MyBaseClass;
use Moose;
has config => (
is => 'ro',
isa => 'MyConfig',
default => sub { MyConfig->new },
);
# inherit, instantiate, etc as before...
$object->config->who('Friends');
Another approach could be Moose::Meta::Attribute::Native::Trait::Hash which makes it easy to setup helper methods to work with native Perl datatypes.
Use its full name.
for (keys(%BaseSub::Config)) {
print("$_: $BaseSub::Config{$_}\n");
}
You could also import it.
our %Config; *Config = \%BaseSub::Config;
for (keys(%Config)) {
print("$_: $Config{$_}\n");
}

Can you call Moose::before in an imported function with local context

I'm writing a client for a REST server using Moose and trying to map the calls into objects. Since many of the calls are simple and will use a boilerplate function to pre-fetch it, I'm trying to use export a function that creates the actual before functions within each class.
package R::A;
use Moose;
use R::Util qw(make_befores);
has 'var' => (is => 'rw', isa => 'Str');
make_befores('/servercall/' => ['var']);
1;
package R::Util;
use Moose; use Moose::Exporter;
sub make_befores {
while (my ($c, $vs) = each(#_)){
before $vs => sub {
# make call and set object values
};
}
}
Moose::Exporter->setup_import_methods(
as_is => [ 'make_befores', ],
);
1;
That's probably incomplete and definitely untested but I think it relays the point. When calling make_befores, it calls it in context of R::Util and breaks since it doesn't call it as R::A with all its variables and such. If make_befores is simply copy-and-pasted into R::A, it works. This will be used in multiple classes, though, so I want it to be an import-able function.
Or am I going about this all wrong?
UPDATED:
Fuji Goro's solution worked great, but was hard to figure out for a Moose newbie like myself, so here's what it ended up looking like:
sub make_befores {
my $meta = shift;
while (my ($c, $vs) = each(#_)){
my $sub = sub { ... };
Moose::Util::add_method_modifier($meta, before => [$vs => $sub]);
}
}
before is just a syntactic sugar to the MOP. See Moose.pm. Use MOP directly, or you can use Moose::Util::add_method_modifier() and with_meta for this case.
use Moose::Util;
use Moose::Exporter;
sub make_before {
my($meta, #methods) = #_;
Moose::Util::add_method_modifier($meta, before => \#methods);
}
Moose::Exporter->setup_import_methods(
with_meta => [qw(make_before)],
);

How can I use Test::LWP::UserAgent if I cannot replace the $ua in the app code directly?

I've got a sub that retrieves some data from an API via a REST service. The code is rather simple, but I need to post parameters to the API and I need to use SSL, so I have to go through LWP::UserAgent and cannot use LWP::Simple. This is a simplified version of it.
sub _request {
my ( $action, $params ) = #_;
# User Agent fuer Requests
my $ua = LWP::UserAgent->new;
$ua->ssl_opts( SSL_version => 'SSLv3' );
my $res = $ua->post(
$url{$params->{'_live'} ? 'live' : 'test'}, { action => $action, %$params }
);
if ( $res->is_success ) {
my $json = JSON->new;
return $json->decode( $res->decoded_content );
} else {
cluck $res->status_line;
return;
}
}
This is the only place in my module (which is not OOp) where I need the $ua.
Now I want to write a test for this and after some research decided it would be best to use Test::LWP::UserAgent, which sounds really promissing. Unfortunately, there's a catch. In the doc, it says:
Note that LWP::UserAgent itself is not monkey-patched - you must use
this module (or a subclass) to send your request, or it cannot be
caught and processed.
One common mechanism to swap out the useragent implementation is via a
lazily-built Moose attribute; if no override is provided at
construction time, default to LWP::UserAgent->new(%options).
Arghs. Obviously I cannot do the Moose thing. I can't just pass a $ua to the sub, either. I could of course add an optional third param $ua to the sub, but I don't like the idea of doing that. I feel it's not ok to alter the behaviour of such simple code so radically just to make it testable.
What I basically want to do is run my test like this:
use strict;
use warnings;
use Test::LWP::UserAgent;
use Test::More;
require Foo;
Test::LWP::UserAgent->map_response( 'www.example.com',
HTTP::Response->new( 200, 'OK',
[ 'Content-Type' => 'text/plain' ],
'[ "Hello World" ]' ) );
is_deeply(
Foo::_request('https://www.example.com', { foo => 'bar' }),
[ 'Hello World' ],
'Test foo'
);
Is there a way to monkeypatch the Test::LWP::UserAgent functionality into LWP::UserAgent so that my code just uses the Test:: one?
Change your code so that within _request(), you're calling _ua() to collect your user agent and override this method in your test script. Like so:
Within your module:
sub _request {
...
my $ua = _ua();
...
}
sub _ua {
return LWP::UserAgent->new();
}
Within your test script:
...
Test::More::use_ok('Foo');
no warnings 'redefine';
*Foo::_ua = sub {
# return your fake user agent here
};
use warnings 'redefine';
... etc etc
I could of course add an optional third param $ua to the sub, but I don't like the idea of doing that. I feel it's not ok to alter the behaviour of such simple code so radically just to make it testable.
This is known as dependency injection and it's perfectly valid. For testing you need to be able to override objects your class will use to mock various results.
If you prefer a more implicit way of overriding objects, consider Test::MockObject and Test::MockModule. You could mock LWP::UserAgent's constructor to return a test object instead, or mock a wider part of the code you are testing such that Test::LWP::UserAgent is not needed at all.
Another approach is to refactor your production code such that the components are (unit) testable in isolation. Split the HTTP fetching from the processing of the response. Then it is very simple to test the second part, by creating your own response object and passing it in.
Ultimately programmers use all of the above tools. Some are appropriate for unit testing and others for wider integration testing.
As of today, I would go with the following approach to this problem. Imagine this piece of legacy code1, that is not object oriented and cannot be refactored so that it makes dependency injection easy.
package Foo;
use LWP::UserAgent;
sub frobnicate {
return LWP::UserAgent->new->get('http://example.org')->decoded_content;
}
This is indeed tricky to test, and rjh's answer is spot-on. But in 2016 we have a few more modules available than we did back in 2013. I particularly like Sub::Override, which replaces a sub in a given namespace, but only keeps it around in the current scope. That makes it great for unit tests, because you don't need to care about restoring everything after you're done.
package Test::Foo;
use strict;
use warnings 'all';
use HTTP::Response;
use Sub::Override;
use Test::LWP::UserAgent;
use Test::More;
# create a rigged UA
my $rigged_ua = Test::LWP::UserAgent->new;
$rigged_ua->map_response(
qr/\Qexample\E/ => HTTP::Response->new(
'200',
'OK',
[ 'Content-Type' => 'text/plain' ],
'foo',
),
);
# small scope for our override
{
# make LWP return it inside our code
my $sub = Sub::Override->new(
'LWP::UserAgent::new'=> sub { return $rigged_ua }
);
is Foo::frobnicate(), 'foo', 'returns foo';
}
We basically create a Test::LWP::UserAgent object that we feed all of our test cases. We can also give it a code ref that will run tests on the request if we want (not shown here). We then use Sub::Override to make LWP::UserAgent's constructor not return an actual LWP::UA, but the already prepared $rigged_ua. Then we run our test(s). Once $sub goes out of scope, LWP::UserAgent::new gets restored and we do not interfere with anything else.
It's important to always do those tests with the smallest possible scope (as most things in Perl).
If there are a lot of those test cases, it's a good strategy to build some kind of configuration hash for what you expect for each request, and use a building helper function to create the rigged user agent, and another one to create the Sub::Override object. Used in a lexical scope, this approach is very powerful and pretty concise at the same time.
1) represented here by the lack of use strict and use warnings.

Accessing the Catalyst object $c from MyApp.pm

I'm using the Assets plugin in my Catalyst app, and I would like some javascript and css files included in the assets of every page.
My first thought is call $c->assets->include('file.js') from MyApp/lib/MyApp.pm where I do setup and config, but I don't know how to get a hold of $c there.
My next idea involves using the WRAPPER stuff, and placing calls like [% c.assets.include('file.js') %] in default html template, but the calls dump the object information to the page, so the calls would have to be uglied up to suppress output.
Solutions or new ideas appreciated. Thanks in advance.
There is no context object yet during application setup, since the $c represents the current request.
If you are using Chained, you can do the call in your root chain action. If you use the non-Chained action types like Local, Path, etc. you can put a begin action in your root controller.
The most correct way in my opinion is however to extend the view. Here's some example code:
package MyApp::View::HTML;
use Moose;
use MooseX::Types::Moose qw( ArrayRef Str );
use namespace::autoclean;
extends 'Catalyst::View::TT';
has common_assets => (
traits => [qw( Array )],
isa => ArrayRef[Str],
handles => {
common_assets => 'elements',
},
);
before process => sub {
my ($self, $ctx) = #_;
$ctx->assets->include($_)
for $self->common_assets;
};
1;
Then you can configure it with something like this:
<view HTML>
common_assets foo.css
common_assets bar.js
</view>