fREWdiculous!
7 Sep
I’ve taken care of a significant portion of the refactoring that I’m doing to disable meta-tests for the Moose test suite. I’ve done all the tests up until the 100 series (which are examples.) The following is an example of how it’s done:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | #!/usr/bin/perl use strict; use warnings; use lib 't/lib'; use Test::More tests => 23; use Test::Exception; use MetaTest; { package Foo; use Moose; use Moose::Util::TypeConstraints; } skip_meta { can_ok('Foo', 'meta'); isa_ok(Foo->meta, 'Moose::Meta::Class'); } 2; meta_can_ok('Foo', 'meta', '... we got the &meta method'); ok(Foo->isa('Moose::Object'), '... Foo is automagically a Moose::Object'); skip_meta { dies_ok { Foo->meta->has_method() } '... has_method requires an arg'; dies_ok { Foo->meta->has_method('') } '... has_method requires an arg'; } 2; can_ok('Foo', 'does'); skip_meta { foreach my $function (qw( extends has before after around blessed confess type subtype as where coerce from via find_type_constraint )) { ok(!Foo->meta->has_method($function), '... the meta does not treat "' . $function . '" as a method'); } } 15; |
Typically there will be some skip_meta blocks scattered throughout a test. As it stands the skip_meta (and skip_all_meta variant) will skip if the SKIP_META_TESTS environment variable is set. As I said before, if people want to change that it’s only defined in one place so we can change how it’s done fairly easily.
There are a few places I’m not sure I need to skip yet, like things in the Moose::*::Meta namespace. But I know for sure to skip the ->meta stuff, so that’s what I’ve been doing. The 100 tests are quite a bit more complex, which is why I haven’t finished any yet. I certainly plan to, and hope to take care of them soon. But in the meantime mst can get started on Antlers as a good amount of the tests should work for him now.
If anyone wants to help out let me know, and we can make Moose faster sooner!
3 Sep
I’ve waited nearly a work week to make sure I don’t post this prematurely, but it’s been four days now and I’m pretty sure I can say that from now on, in the regular case I’ll be riding my bike to work. There are certainly safety issues, since I live on the outskirts of Dallas, but I’ve done a lot research to make safe choices. I haven’t done everything he says on that site, but mostly that’s because I don’t want to ride on streets instead of sidewalks on big roads.
Just in case anyone is wondering, here’s the gear I’ve purchased:
First off, about half of that stuff is for safety. I need a helmet. I want lights so cars see me. The mirror is so I can see cars behind me. The tool, patch kit, and portable pump are just in case I need to fix something in the middle of my commute. I’ve already needed to tighten the handlebars about halfway, and it was pretty great to be able to fix that quickly and easily. I hope not to need to use the patch kit, but surely one day I will. I actually don’t have the bag listed above yet, I currently have a much smaller, much less useful bag, but I already ordered the one above based on reviews at REI. The nice thing about the bag is that all of those essentials I really need are part of the bike, so I don’t have to bring a larger back (bookbag) for those things.
Here is my current list of things to buy:
Of those things I am definitely going to buy the seat, as my current one is really, really uncomfortable. That one has a good price with great reviews. The sandals I’m not totally sure about, but I do need to get some real shoes for riding. The computer would be pretty cool to have, but that’s not necessary at all so I’ll probably wait till Christmas for that.
My plan is to bring work clothes and food on Monday in a backpack and then not have to bring the backpack again till Friday to bring it all back. So far that has worked fairly well. My initial route is pretty much unchanged for going to work, but one of the owners of my company told me about a bike path from Campbell to Renner, which is great for the ride back from work as it means I have way fewer crosswalks to use.
Interestingly, because of those crosswalks my commute home takes 15 minutes longer than the commute to work. I can get to work in about 30 minutes assuming there isn’t crazy traffic or anything, but the route home takes 45 minutes because of crosswalks or alternate routes.
If you ride a bike work, what tips do you have for us noobs?
2 Sep
Today at work I had to do some validation that we haven’t yet had to do for my project at work. I’ve always thought that for validations exceptions are the way to go. I’ll explain everything I did so you guys can benefit/critique.
First off, I used Exception::Class to create my exception classes:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | package ACD::Exceptions; use strict; use warnings; use Exception::Class ( 'ACD::Exception::InvalidBinBox' => { description => 'Invalid Bin-Box', fields => [qw{bin box}], }, 'ACD::Exception::UserException' => { fields => 'message', }, ); use Moose::Util::TypeConstraints; class_type 'ACD::Exception::InvalidBinBox'; class_type 'ACD::Exception::UserException'; no Moose::Util::TypeConstraints; 1; |
Also note the use of Moose::Util::TypeConstraints; we’ll come back to why I did that in a bit.
The following code is a method from a DBIx::Class Result class. Nothing too surprising here. It basically creates an exception if someone tries to use a nonexistent bin or a box that the bin doesn’t contain.
1 2 3 4 5 6 7 8 9 10 11 | method validate_bin_box { my $bin = $self->bin; my $box = $self->box; my $success = $self->result_source->schema->resultset('BinBox')->single({ bin => $bin, max_box => { '>=' => $box }, }); ACD::Exception::InvalidBinBox->throw( bin => $bin, box => $box ) unless $success; } |
Next up is the Catalyst action which calls this method. This is the first part of the code I’m excited about. I’m using TryCatch for the syntax sugar here. Note that I get to do a catch based on type of exception. This is why I had to use Moose to define the class_type’s above. You’ll also note that I recast the Exception as a “UserException.” I’ll note why next.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | method update_inventory_part($c) : Local :ActionClass('Role::ACL::Simple') :RequiresRole('inventory_write') { my $id = $c->request->params->{id}; my ($bin,$box) = split /-/, delete $c->request->params->{location}; my $part = $c->model('DB::InventoryPart')->find($id); try { $part->update({ %{$c->request->params}, bin => $bin, box => $box }); } catch (ACD::Exception::InvalidBinBox $e) { ACD::Exception::UserException->throw(message => 'Invalid Bin-Box: '.$e->bin.q{ }.$e->box); } $c->stash->{json} = { success => 1 }; } |
And then this is the final (server side) method that wraps it all together. This belongs in the Root controller of our Catalyst app as it takes care of all of our errors. Basically what’s going on here is that if there are errors we want to set a 500. We show the error raw if the server is in debug mode or if it is a user error. There is a small subtlety that there can possibly be more than one error. For simplicity’s sake we show all the errors if we are going to show one of them.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | method end($c) : ActionClass('RenderView') { my $errors = scalar @{$c->error}; if ($errors) { $c->response->status(500); my $user_error = 0; $user_error = 1 if (first { ref $_ eq 'ACD::Exception::UserException'} @{$c->error}); $c->stash->{json} = { status => 'fail', reason => ( $c->debug || $user_error) ? join ';', map { (ref $_ eq 'ACD::Exception::UserException')?$_->message:"$_" } @{$c->error} : 'A server error occured. Contact developers with date and time this occured', user_error => $user_error, }; foreach (@{$c->error}) { $c->log->error("$_"); } $c->clear_errors; } } |
I won’t show the javascript right now as it’s messy and most readers of this blog aren’t hardcore Ext users. But basically what happens is that we have a global listener for all connections that fail (aka, don’t return with 200 OK) and I have some special code for various cases. For example, I have an unauthenticated case which allows the user to login (and then it seamlessly retries the query,) I have an unauthorized case if somehow the user tries to do something they are not allowed to do, I have a user error case, which will basically display the error verbatim, and then I have a server error message, which will output the raw exception (or the vanilla message above.)
The beauty of all this is that now that I’ve written the code sufficiently generically I should only need to do an ACD::Exception::UserException->throw(message=>…) to show the user an error window at any point in the program. Pretty sweet huh?
1 Sep
Today the question was asked: “To Moose or Not to Moose?” The article is fairly well written, but it seems to me that the comments are not exactly educated. Here is the main one this is in response to:
I’d try Mouse too. Unless you’re doing something funky I’d be surprised if it’s more than a 1 letter change to your source code.
First off, here is a quote from the POD:
Moose is wonderful. Use Moose instead of Mouse.
The author recommends not to use Mouse. That’s a big deal to me. Also, enjoy the following quote:
The original author of this module has mostly stepped down from maintaining Mouse. See http://www.nntp.perl.org/group/perl.moose/2009/04/msg653.html. If you would like to help maintain this module, please get in touch with us.
He’s also given up on it. Moral of the story, don’t use it.
Today I started working on mst‘s plan for MX::Antlers, which is a way to use the actual Moose, with the speed of Mouse, without persistence or anything like that. Great for CGI and whatnot.
Now I’m a little fuzzy on the implementation, but if I understand correctly this will “compile” Moose into a single file. It will not include Class::MOP, so you won’t be able to use ->meta, but generally for basic modules don’t need it, so no big deal really. What I am working on is updating the existing Moose test-suite to disable the tests for ->meta. My current plan is to use an environment variable, but whatever I do it will be a function so that we can change it to some other methodology if we need to.
So! Get excited! Depending on the code we may be able to abstract it to apply to other heavy frameworks (Catalyst?) to make them sufficiently fast as well. Once I have some basic stuff in the public repo (hopefully a couple before Friday) I’ll put up a post or two explaining how to get the work done, and then we can parallelize the work. Who’s with me?
31 Aug
So after some experimenting at work I found out what the culprit of my previous post was. I still have no idea why some parts of the code changed, and others didn’t. I imagine that part of that had to do with bad technique (see Scientific Method.) Anyway, it has something to do with the extremely sketchtowne Catalyst::Restarter::Win32. I’m not criticizing Rolsky’s code here, it’s just the nature of using Perl 5 in Windows. Fork is just one of those things that don’t quite work right, so we must resort to hacks.
I recently contributed a little bit of code to the module to fix a small bug I noticed; it looks like I’ll be contributing more.
Also, if you don’t believe me, here’s the way to prove it to yourself that at least something is wrong: open up the process manager or whatever it’s called in windows, and watch the processes, sorted by name. Use the restarter and change a file so that it will restart the dev server. Note that a new process (perl.exe) starts, but the old one never goes away. Do this a few times till it becomes obvious. Then kill the server. Note that the other processes never go away. This seems to be something to do with the “totally hack-tastic” _fork_and_start. It never kills the current process? Or something? I need to do more research and playing around. Hopefully I can get it to stop confusing me.
In other news, if you are on Windows, you probably should not use the Restarter till it gets fixed.
27 Aug
Ok, this is just too crazy to not record and relate. By now anyone who has read much of my blog or interacted with me should know that I use a significant amount of javascript on my current project at work. Because I like to keep everything nicely organized, 95% of the time each class has it’s own file. That means I have to tell the server every time I add a new class. No big deal really.
So yesterday I created a new form, and I added it to the list. When I refreshed the page the form failed to load. Well, that could have easily been an error in the syntax or even runtime logic of the form, so I start looking for firebug errors and whatnot and don’t see any. So I look at the debug output of Catalyst::View::JavaScript::Minifier::XS to make sure that it tried to include that file. It didn’t! Ok so clearly I didn’t save the file with the list of JS files. So I ensure that I’ve saved it. Still no luck. So I change another part of the list, a file that is loading, to see if that gets taken out of the list. Maybe I misspelled the filename you know? Nope. List remains the same. Ok, so then this must not be the canonical list. So I change a fundamental part of the list, to see if everything still works after I change that. Nope, now nothing works, showing that this list clearly isn’t nothing.
Bizarre. So I go home, assuming that I was just tired. Today at work, after working on a bunch of other stuff, I get back to it. So first I output the list itself, to make sure that it’s what I think it is. It is. Then, I open up the code behind Catalyst::View::JavaScript::Minifier::XS. I added some debug stuff where it loads the files and look at the output. Nothing prints out. That’s weird… So I change it from a warn to a $c->log etc. Still no output. So I’m editing the wrong file, obviously. I go to rename the system version of this file so I know it’s using my local copy (I have changes that haven’t been accepted by upstream yet.) Oh wait…it’s already renamed from yesterday…
So that’s weird. Ok, so I put some debug statements right next to where it already has some debug statements… Lo and behold they output! Ok so clearly I am missing something. I put debug statements before the block where I am already outputing debug statements from: no luck. I put them after. No luck. I change the code so that all the filenames get “frew” added to the end to see if CVJMX will throw an error or even change the output messages. STILL NO CHANGES.
I should point out that I have checked that I am using my local server and editing local files numerous times by the way.
Ok, so I am clearly insane at this point, as that’s more likely than file changes being scoped to a 3 line block. On a whim I decide to restart my computer. Start the Catalyst Dev server with the same command (from history) as before, without file changes. Everything worked.
All I have to say is: jfkasl;fkdasfkojqwklmdcszkljcvsxlkv m,w;ejriopjewiojc4weojejoifevjoirjivoi
26 Aug
At work I tend to play an…Evangelical role? I tend to experiment with various technologies, get sold on them, and then sell them to coworkers. Examples: Apache, DBIx::Class, CGIApp, and lately Catalyst. So I typically find various ways that the new tool helps make my job easier and tell people about that. After they believe me, I then educate them about various nuances and whatnot of the tool. Eventually this will happen with git, when it doesn’t suck so much with Windows.
So recently one of my coworkers asked me why he should use an ORM. I had thought I’d mostly fought that battle, but he wasn’t sold (he is now by far
). Anyway, here is my answer, open to the world.
In general this isn’t a huge benefit. SQL is pretty simple and remembering it’s syntax isn’t so bad. But when you want to do something in like paging in SQL Server is when an ORM really starts to shine. In general the ORM makes tasks that you want to do with SQL all the time nice and simple. For example, since we use Ext at work for most grids, users expect to be able to sort by all columns, have pagination, etc. That’s entirely abstracted away. I rarely think about those pedestrian things now
This is where a good ORM really shines. Instead of trying to remember seemingly transient relationships, like how the Shop table joins with the Orders table, we can document that by writing code using our ORM. After that the relationship is there forever. It’s an entirely new level of code reuse, if you are used to just vanilla SQL, even if you are reusing it with functions.
This is actually a lot more subtle in my mind. When I first started using a ORM for real (DBIx::Class) I kept looking for DBIC ways to do various things. Typically the answer was: “override insert” or “override update.” As a noob this can be pretty intimidating, but it really gives great amounts of flexibility. At some point I’ll do a post on OOP revelations I’ve had (interestingly, mostly I get those from hacking on the code of my ORM of choice
,) but for now I’ll just leave it at that.
What are the reasons that you use an ORM?
update: as Stevan notes, I really shouldn’t say all in the final bullet point above. It’s more subtle than that. When I say OOP I don’t actually mean the classes that the ORM represents inherit from each other. I just meant that if I want to do some extra stuff for one class when I store/retrieve it I can localize those changes.
26 Aug
Ok, so for some reason I left out sending email from last nights’ post. Here it is: sometimes people forget about RT, or they have so many RT’s that they don’t know which ones are fixed and broken. Well, a small nudge via email can convince them to fix a longstanding bug. Of course, if it’s reasonable sending a patch wouldn’t hurt either…
25 Aug
I think a lot of people who use perl have the idea that to help the perl ecosystem be they must be rockstars who churn out exorbitant amounts of code that is well tested and well factored. That is just not true!
The easiest thing one can do to help out in the perl ecosystem is to create tickets for any issues you have with modules on RT. It’s not really that much of a hassle and it can help authors out a lot. Make sure that you include enough information for the authors to at least understand what went wrong. I won’t explain all that as my audience is comprised of programmers.
in that same vein, sometimes people forget about RT, or they have so many RT’s that they don’t know which ones are fixed and broken. Well, a small nudge via email can convince them to fix a longstanding bug. Of course, if it’s reasonable sending a patch wouldn’t hurt either…
Another thing which I’ve mentioned before is to use CPAN ratings. Again, this is super easy, but if the module works for you and you have no complaints, 5 stars can really tell other users a lot!
And then of course there is communication on IRC. Often on IRC, after a user asks for clarification, developers will ask for a doc patch. Although this is easy for the user to add (depending on the subject of course) it will help future users significantly.
How many RT’s have you submitted this week? CPANRatings? Doc Patches?
24 Aug
Normally I opt to eschew metablog post (how meta is that?!) but I figured this deserves a brief explanation.
I have been getting more and more spam lately. Fortunately Akismet usually keeps humans from seeing it, but Akismet has also kept back plenty of spam too. So I decided to go with the more powerful reCAPTCHA for comments from now on. I know it’s a hassle, but it could be worse. There are lots of bad CAPTCHA plugins out there and in general I like the reCAPTCHA concept. Anyway, sorry!