There is no good or bad, just fun and not fun

Life, code, stuff, things.
clone
bobtfish
Last updated 9 weeks ago says livejournal. Well done me.. No, wait - the other one.

So my personal life has been fairly crazy recently, everything is starting to calm down and return to normality, but I appear to have got a social life back, so I don't have as much time for coding etc as I have in the last 18 months. This probably isn't a bad thing as I was going slightly (more) insane, in a bad way.

However, in the last 9 weeks we have managed several releases of Catalyst, the new -Devel stuff is getting nearer every day and I finally finished the stuff that I've been talking about with MooseX::MethodAttributes for aaaaages.

I have to admit that I probably wouldn't have got there yet if it wasn't for the extreme awesomeness of rafl, who did all the core Moose refactoring needed to support this. He went on from that to release CatalystX::SimpleLogin for me, which works well enough for simple use.

So, rather than just a meta-post about stuff which has happened already, here is some code for you. This is a quick tutorial on how you might test a Catalyst component by mocking things, and also demonstrates the hard to grasp concept of 'currying' in a fairly easy to understand fashion..

So, given that you've got that open in another tab, let me try to explain it (and why it is the way it is) for you a little. So the basic premise for this is that you want to write a Catalyst component, and you want to write unit tests for it. And, well, they're NOT unit tests unless the entire environment past the code under test is faked.

I know that most Catalyst applications and components have 'live' tests, and they're great - you should be spinning up a real app with your component included to test that it works in-situ. But trying to test all the obscure combinations of stuff you may have to deal with by generating HTTP requests and reading the responses back - that's a massive pile of pain and suck..

So mocking a $ctx is pretty easy, and you can then just say: my $comp = My::Comp::Class->COMPONENT($c, { %config }); and off you go.

Except that building a $ctx takes a bit of effort - no bother, you can shove that in a library.. Except you're still going to be writing loads of boiler plate to stuff all the necessary data into your mock request etc for each test case.

So, the obvious solution here is OO - start being able to clone your mock ctx instances and customise them, etc.. And you quickly end up with OO modules to run assertions on your $ctx, and - aghhh. You've just written your component a second time - backwards. And it's got all the same bugs and all the same assumptions, as it's within the exact same programming paradigm.

So instead of that, lets have a 'ctx generator' function, which you can call with some data and it'll setup the ctx. Being of simple mind, I decided there were only two things I needed: 1 - to be able to fill the stash with stuff (as I'm probably gonna do that all the time), 2 - to be able to mangle the generated context.

So going to my code sample:

  • Lines 14-29 create a class to be the mock context

  • Lines 31-38 test this works as expected

  • Lines 40-52 make an anonymous function which when called generates a context and populates the request/response

  • Note that you can pass this 'generator' a callback function which is called on the context after it's generated. This has no use _right now_ (as you could just call stuff on the ctx you're about to get), but will be useful later

  • Lines 54-65 test this

  • Lines 66-74 are the 'clever' bit here - given a ctx generator function, it customises it (by the first parameter, also a function) and returns a ctx generator function.

  • Line 77-93 then test this

  • Line 103-112 shows the same approach for bundling tests

  • Line 125 then is the finale, running a combined set of tests against a context which is is generated and permuted several times

So, I'm assuming (hoping) that everyone follows what's going on after peering at the code a little.

The nice thing about this is that you can successively build up more specialised ctx generators just by taking the generator you have and customising it some more.. And then you can just generate a customised ctx.

The really nice thing about this is that you can wrap bundles of tests up and pass them the context. So you can build up sets of tests and sets of test scenarios (i.e. mocked contexts with all the 'basics' filled in, just the 'for this test' specifics overridden) incrementally, without ever repeating yourself / having to copy around or repeat the same mock data or mock test cases.

Anyway, explanation over - I hope that demonstrates the technique sufficiently. I hope this has made you think about the subject, or given you a different perspective on how you could tackle the problem, even if you hate the approach shown here.

Thanks for listening, I'll try to bore you again in under nine weeks time :_)

How hard could writing a login form possibly be?
clone
bobtfish
Not hard, at all. I've written dozens.

As I mentioned briefly in the last post, I started to write CatalystX::SimpleLogin.. This worked ok, but I then tried to refactor it as a set of traits you could automatically apply via config... At which point it all blew up, as if to remind me that writing anything really generically IS HARD WORK.

Witness the epic yak shaving exercise I then managed to indulge in, resulting in a Catalyst branch, new CatalystX::Component::Traits, new MooseX::Traits::Pluggable, new Catalyst::Controller::ActionRole, more patches on Catalyst::Action::REST, and last but not least new MooseX::MethodAttributes.

So all of this is going to block ::SimpleLogin from getting to CPAN for a while, and role combination with method attributes roles (which this really needs before I go much further) hasn't really gone anywhere since I last posted.

I should probably use this all as an excuse to stop hacking on CX::SimpleLogin any more, at least until I've finished the code for YAPC some.. :)

My continuing failure to finish anything.
clone
bobtfish
I've totally failed to touch most of the stuff I blogged about last time.

I did, however, fix role combination with method attributes, however it's not going to see the light of day in it's present form.

rafl is currently writing the nasty hack I did on Moose to get it working a little more properly, for his overloading in roles support stuff.. Once he's doing that, making the solution in methodattributes should become a lot more elegant, and I should be able to clean it up a lot generally.

In other exciting news, there is now prior art for actionroles on the CPAN, which is a newer, nicer way to do generic things on a per-action basis (rather than ActionClass, of which you may have only one).

YAPC is coming awful awful fast, so I guess I really need to finish the code I'm talking about there, but I keep getting distracted by other shiny things, such as zby's ideas about a set of CatalystX::Elements, and the two of us and stefan48 appear to be prototyping one on github.

Patches, of course, welcome :)

script/myapp_create.pl local::lib
clone
bobtfish
So this post was meant to be a break from the method attributes ranting, and I was going to post about local::lib.

I keep saying just use local::lib and people ask how.. And I reply just write a couple of trivial shell scripts.. And they ask how..

So I set out to provide a canonical example of how which everyone could use.

I also had read but not played with the new --self-contained feature, so I thought that would be fairly awesome. The idea of being able to say 'make installdeps', and have your entire non-core app dependency stack rebuilt was fairly cool.

I then realised, that with dhoss' helper refactor happening, making:

script/myapp_create.pl local::lib

work is totally easy and non painful.

I started this last weekend, as a yak shaving exercise to avoid posting. Didn't prove to be quite as easy as I'd planned.

2 releases of local::lib later, I'm almost there. And when I say almost there, I mean I have a skeleton app on github with some munged scripts to do what I want, make installdeps works, I have code to inject distroprefs patches for various things (I am looking at you WWW::Mechanize), and I can build my work apps from my minicpan + injected internal code.

I haven't actually got as far as patching the preexiting scripts in script/myapp_foo.pl yet, adding 2 lines to Module::Install's Makefile.PL does what I want.

I really haven't got all that many tuits, or clues for hot to get this injected right into the generated application with Module::Install::Catalyst or whatever, so I guess I need to play with that and bug #toolchain with dumb questions some more :)

The next stage is to branch -Devel and start getting this integrated. I currently don't see _any_ solution which doesn't involve inlining my 'env' script into everyone's script/ directory - but I don't feel so guilty about that, as the code will only be executed by people running in local::lib mode, and we'll be pulling the rest of the scripts back into -Runtime soonish anyway I guess leaving them as stubs..

I'm probably likely to ignore this for a few weeks having got it thus far, as I really need to work on super sekrit project, and I have a tonne of work to do to finish the code for the stuff I'm talking about at YAPC, and I'm hoping to have a couple of live production apps deployed with it before that anyway!

So if anyone else would like to see Catalyst have native local::lib support, please please feel free to step up to the plate for anything between making it 'your baby', and just giving the current code a test-drive and docs a brush up, I'd be really grateful about now ;)

P.S. Yes, I fail at ironman, again.

P.P.S. Combining multiple roles with method attributes fails miserably. There are failing tests in my MX::MethodAttributes github fork.

P.P.P.S. I will tell you how to fix it if you care, I don't have time to hack on it.

P.P.P.P.S. Yes, I don't like not having conflict resolution either. It's still better right now than the M.I. mess I was getting into in my controllers...

Why do I always find solving meta problems more fun than real problems.
clone
bobtfish
I have a feeling that I fail at ironman. Meh, oh well - I'll keep blogging anyway, of course - I'm not about to stop ranting about shit for at least a while yet, sorry..

In sympathy for mst, I might have to also make my hair pink (which is the colour I've most often heard mentioned in reference to that), as I fail miserably, and before him...

Oh, no, wait - my hair is pink, DOUBLE FAIL.

Anyway, onto what I was going to rant about.. In the last episode, I was working on Role support for MooseX::MethodAttributes.

This is now released, works, has a much nicer way of asking for it, and has been used in anger by the crazy kids over at MusicBrainz. Go upgrade to 0.12 now. :)

I've still not got round to writing up a really complex example, and/as I think that's actually going to turn into something on github you can play with, and a series of posts...

But for now, here is another trivial use case, which gets messy when you think about it on a larger scale.

Say you work for a company, and you have multiple (different) Catalyst apps, but you want to reuse things and have them 'work the same' between your different apps for consistency.

So in each site, you're going to have a sitewide wrapper template, and it's going to end up like this:

wrapper = wrapper || 'site/layouts/full.tt2'

which is okish. This will use the wrapper specified in your code, or the default wrapper..

But how about if you want to skin your site, or do a/b testing with two different site layouts, or..

Having that chunk of string buried in your template is kinda rubbish now, huh?

So you write this:

wrapper = wrapper || default_wraper

And then you write this (some context shown):

package MyCompany::MyApp::Controller::Root;
use Moose;

BEGIN { extends 'Catalyst::Controller' }

sub root : Chained('/') PathPart('') CaptureArgs(0) {
    my ($self, $c) = @_;
    # N.B. This is the only line added, rest of code was already there ;)
    $c->stash->{default_wrapper} = $c->view('HTML')->default_wrapper;
    # More stuff to do for _every_ page here.
}
...
package MyCompany::MyApp::View::TT;
use Moose;

extends 'Catalyst::View::TT';

has default_wrapper => ( is => 'ro', required => 1, isa => 'Str' );

...
<MyApp::View::TT>
   default_wrapper site/layouts/full.tt2
</MyApp::View::TT>


And suddenly the configuration value is in config. Woohoo, you're now correctly design pattern compliant, as you're not storing app config data in the templates :)

Now, back to the point - your company has a lot of apps.. They're all going to do this. So what, it's one line of code. WRONG

DON'T REPEAT YOURSELF

DON'T REPEAT YOURSELF. (Look, I fail! Oh, we covered that once..)

This won't be the only commonality, remember, this is a trivial example so lets think up a way of doing this generically, so we don't copy code around. The traditional way to abstract that would be to provide a 'MyCompany' base class with the default 'sub root'.

Ok, that's cool - except you suddenly realise that you have 6 features like this, but if you add all 6 to all of your apps, it breaks things.

FAIL

Whatcha gonna do?

package MyCompany::ControllerBase::Root;
use Moose;

BEGIN { extends 'MyCompany::ControllerBase'; }

sub root : Chained('/') PathPart('') CaptureArgs(0) {
    my ($self, $c) = @_;
    if ($c->config->{features}{has_feature_1}) {
        # Do stuff for feature one
    }
    if ($c->config->{features}{has_feature_2}) {
        # Do stuff for feature two
    }
    # Repeat, 4 more times
}

Sorry, you fail at OO. Next.

package MyCompany::ControllerBase::Root::FeatureOne;
use Moose;

sub root : Chained('/') PathPart('') CaptureArgs(0) {
    my ($self, $c) = (shift, shift);
    $c->stash->{default_wrapper} = $c->view('HTML')->default_wrapper;
    $self->next::method($c, @_);
}
... Repeat, 5 more times

I laugh the special laugh I save for people who think that having 6 base classes is a good idea, and we move on now.

package MyCompany::CatalystX::FeatureOne;
# Code may contain up to one lie, see if you can spot it before you read the explanation.
use strict;
use warnings;

sub import {
    my $caller = caller();
    no strict 'refs';
    &{$caller."::_add_feature"}(sub {
        my ($self, $c) = @_;
        $c->stash->{default_wrapper} = $c->view('HTML')->default_wrapper;
    });
}

package MyCompany::ControllerBase::Root;
use Moose;

BEGIN { extends 'MyCompany::ControllerBase'; }

{
    my @features;
    sub _add_feature { push @features, shift }
    sub _run_features { $_->(@_) for @features }
}

sub root : Chained('/') PathPart('') CaptureArgs(0) {
    my ($self, $c) = @_;
    _run_features($self, $c);
}

package MyCompany::MyApp::Controller::Root;
use Moose;

BEGIN { extends 'MyCompany::ControllerBase::Root'; }

Wow. Note the crack fueled non-generic implementation of part of method modifiers and part of roles. Note also, this doesn't actually work in your base class as shown because it relies on the caller. Fairly easy to fix with more code..

The fact that it doesn't work yet and that you can't use namespace::clean (without an 'avoid the mad shit' incantation) just adds to the charm.. ->can('has'). No, really I actually hate this least so far.

So anyway, after wallowing in filth. Let me present:

package MyCompany::CatalystX::FeatureOne;
use Moose::Role;
use namespace::autoclean;

requires 'root';

after 'root' => sub {
    my ($self, $c) = @_;
    $c->stash->{default_wrapper} = $c->view('HTML')->default_wrapper;
};

package MyCompany::CatalystX::DefaultRoot;
use Moose::Role -traits => 'MethodAttributes';

# Yes, I need no body, all the functionality is in modifiers
sub root : Chained('/') PathPart('') CaptureArgs('') {} 

package MyCompany::MyApp::Controller::Root;
use Moose;

BEGIN { extends 'MyCompany::MyApp::ControllerBase' }

with map { "MyCompany::CatalystX::ControllerRole::$_" } qw/
    DefaultRoot
    Feature1
    Feature2
    Feature3
/;

Tada, much more elegant.

And you can split it how you like - have the features which really are common for all apps in your MyCompany base class, sure. Have the action in the base class and just apply modifiers to it, sure.

Whatever works for you, helps you write cleaner code and allows you to avoid repeating yourself - it's all good with me, I'm just pointing out how taking advantage of MooseX::MethodAttributes to do something else that can be done in 1 line might actually be a good option for maintainable and reusable application code.

Whoda thunked that?

Controller actions inside Moose roles - possible right now!
clone
bobtfish
package MyApp::Item;
use Moose::Role -traits => 'MooseX::MethodAttributes::Role::Meta::Role';
use namespace::autoclean;

sub view : Chained('item') PathPart('') Args(0) {}

sub edit : Chained('item') PathPart('edit') Args(0) {}

package MyApp::Controller::Foo;
use Moose;
use namespace::autoclean;

BEGIN { extends 'Catalyst::Controller' }

with 'MyApp::Item';

sub item : Chained('/') PathPart('foo') CaptureArgs(1) {}

This simple example will produce the following URIs (corrected by an astute reader):
  • /foo/item/*
  • /foo/item/*/edit

The sweet thing about this is that you can apply the role anywhere you have a 'sub item', and as long as the item is stored in the same place in the stash, and can be manipulated in expected ways (e.g. is a dbic resultset)..

You've suddenly got generic, re-useable CRUD, without any nasty multiple inheritance going on, method modifiers should work like you expect, etc.

Nice, eh? I have lots of places where I like to re-use chunks of URL space like this in my apps, and currently I have to use steeenking multiple inheritence.

I just CPAN'd a dev release of MooseX::MethodAttributes which makes this work.

Caveat: I'm fairly sure that composing roles with actions in them onto other roles will not work correctly currently, it may work, but I haven't written tests yet so it probably doesn't.

I'll be fixing this and releasing 'for real' within the next week or so hopefully, but I'm fairly sure it's solid as long as you compose roles directly onto classes as shown above. Method modifiers (not shown above) are also tested and do work.

P.S. I'm fairly sure MooseX::MethodAttributes::Role::Meta::Role is the worst module name evar, suggestions welcome..

P.P.S.It was pointed out to me that what I'm doing above could previously have been trivially achieved using controller base classes. This is totally true, as it was a simple example. I'll demonstrate a much more complex example and how having to use base classes and multiple inheritance make this a lot more painful than it has to be for more complex cases in a future post :)

This week in Catalyst.
clone
bobtfish
We're almost ready to release 5.80004, 'Unknown error' is verified killed, and I just need to tweak some docs..

Chris just CPANed the first version of Catalyst::Engine::STOMP, and we're sitting down on Monday night for a hacking session on that, which will be cool.

In other news, there has been quite a lot of discussion about what we're going to do to kill off the hated attribute syntax, and there's now some prototype code on github..

I don't have much to report other than that - lots of birthdays & an attack of the lazy has meant I haven't actually achieved so much in any of my other projects this week. Some dumb tourist ran out into the road in front of me last night, causing me to wipe out in a fairly epic manor (graphic photo), so I guess I won't be out riding tomorrow...

t0m->can('has')->beer == Acme::LOLCat->can't('has')
clone
bobtfish
So, CPAN, it has some awesome code. Some shitty code. Some plain silly code.

All the deliberately silly code (and programmers love daft in-jokes, so there is a lot of silly code) in perl which is on CPAN, lives in the Acme:: namespace.

Ergo Acme::LOLCat, and, if you're the web application type, U CAN B RERITIN UR OUTPUTZ.

Aaaaand, back in the real world...

J Random programmer, writing real code, says:

package MyCompany::PlusOneClass;
use Moose;
use MooseX::Types::Moose qw/Int/;

has id => ( isa => Int, is => 'ro', required => 1 );

sub id_plus_one {
    my ($self) = @_;
    return $self->id + 1;
}

And all is well in the world.

Except, try this shit:

use Test::More tests => 4;

my $class = 'MyCompany::PlusOneClass';
my $instance = $class->new( id => 42 );

my $id_meth = $instance->can('id');
my $id_p1_meth = $instance->can('id_plus_one');

is $instance->$id_meth(), 42;
is $instance->$id_p1_meth(), 43;

# All good so far, see
# http://www.shadowcat.co.uk/blog/matt-s-trout/madness-with-methods/
# if you don't grok the stuff with ->can

ok $class->can('has'); # We are NOT Acme::LOLCat, ergo this is NOT cool.

# How many attributes?
my $attr_count = scalar( $class->meta->get_attribute_list );

$instance->has('unused', is => 'ro'); # You didn't really mean that, did you?

# This is some fucked up shit, right here..
isnt scalar( $class->meta->get_attribute_list ), $attr_count;

So, yeah.. Leaving keywords about can ruin your life, quite badly. Go talk to the #reaction crew for actual real life war stories about how random crap in your namespace murders kittens.

The correct solution to this is to drink the rafl koolaid. Just say use namespace::autoclean, and your class cannot has. Super.

So, this comes up fairly often on irc. The good way to explain this all in brief:

ok Acme::LOLCat->can('has');
ok !YourClass->can('has');

Clear? Good. I'm glad. I like that explanation too.

LAST WEEKEND I FOUND A HUGE BUG.

OH NOES!

To try and bleach my brain somewhat more, so I could forget how all software sucks & etc (my hopes, dreams and illusions having been shattered by above bug), I went out to #london.pm beers tonight.

Whereupon, the #london.pm people proved that they are epic pedants in real life, as well as on the mailing list.

ok !Acme::LOLCat->can('has');

is not acceptable.

Thus: Acme::LOLCat->can't('has') is yours for the taking.

PATCHES WELCOME, HTH, KTHNX BAI.

Catalyst 5.80003 and French Perl workshop.
clone
bobtfish
I fixed MooseX::MethodAttributes, and we subsequently shipped shipped Catalyst 5.80003, with working method modifiers on controller actions. I also wrote the docs great success!

I fail at sekrit project and Authentication::Credential::HTTP (sorry abraxxa!)

So, iron man readers who among the brits is going to FPW? I am, as is Chris!

Eurostar is still cheap, do it. Dooo itttt. :)

First post (in ages)
clone
bobtfish
Hi anyone still reading ;)

So blogging about my life and random crap has died a fair death, and I'm not really interested in starting again. However, I'm doing enough Open Source stuff that there's plenty to write about there, so I'm going to steal this account for blogging about that sort of stuff - mst having got the Iron Man (rant) thing off the ground (finally) has given me the appropriate kick in the pants to start.

This is most likely to be a series of lists of what I've been hacking on, and link love to other people's shit I find interesting, I don't see myself having time for massive essays generally - therefore, apologies in advance..

So, if you're not interested in perl or Catalyst, then you should probably de-friend me now..

On my Catalyst 5.8 list this week, mostly the third 90%, all of which I planned but failed to get done this weekend:


Phew, that's fairly stacked up, doubt I'll get through it all, especially as I've been totally neglecting my super sekrit project recently, and actually need to do some serious hacking on that also..

Signing off, as I'm now late for work (even given Monday morning). :/

You are viewing bobtfish