fREWdiculous!
25 Apr
Yesterday I was reading this post by chromatic and I finally understood what state does. If you look at the perldoc for state you will see why. There is quite a dearth of examples there.
Anyway, here’s a real world example from our code base which uses state in a slightly different way from what is probably typical.
Before:
1 2 3 4 5 6 7 8 9 10 11 12 | { # predeclare a day's duration as well # as the set of weekdays to save time my $day = DateTime::Duration->new(days => 1); my $weekdays = none(1..5); method date_due($start_date, $max_days) { my $ret = $start_date + DateTime::Duration->new(days => $max_days); while($ret->dow eq $weekdays) { $ret -= $day } return $ret; } } |
After:
1 2 3 4 5 6 7 8 9 | method date_due($start_date, $min_days) { # state declares the variables the first time that date_due is run state $day = DateTime::Duration->new(days => 1); state $weekdays = none(1..5); my $ret = $start_date + DateTime::Duration->new(days => $min_days); while($ret->dow eq $weekdays) { $ret -= $day } return $ret; } |
I agree with chromatic on this one; it’s not lifechangingly better, but given enough usage I think it could make things much more clear.
13 Apr
Ok, so I just had to refer to this unposted post since I upgraded to perl 5.12 and I figured I’d finally post it.
Here’s everything I did to get ODBC working and connected to our MSSQL server at work:
1 2 3 4 | aptitude install tdsodbc dpkg-reconfigure tdsodbc aptitude install unixodbc-dev cpan DBD::ODBC # (or aptitude install libdbd-odbcperl) |
Note:
driver=FreeTDS refers to /etc/odbcinst.ini
this is how it finds the .so
And this is our DSN:
“dsn”:”dbi:ODBC:server=10.6.0.9;database=ACDRI;port=1433;driver=FreeTDS;tds_version=8.0″,
Hope this helps someone!
update: For some reason I had to replace unixodbc-dev with libiodbc2-dev, so you may need to do that as well.
5 Apr
Yesterday Ovid posted this little snippet to get his top 10 used commands.
I had to modify it a little for my zsh settings:
1 2 3 4 5 6 7 8 9 10 11 | valium [4030] ~acd % history -n 1 | awk {'print $1'} | sort | uniq -c | sort -k1 -rn | head 1336 svn 419 perl 301 git 245 rm 233 cd 179 vi 151 ack 67 sudo 62 cpan 61 mv |
I’m sure that my home computer would have the git and svn switched. I’ll update this post with that computer’s history if I remember.
update Here’s my home computer:
1 2 3 4 5 6 7 8 9 10 11 | FrewSchmidt2 [10021] ~ % history -n 1 | awk {'print $1'} | sort | uniq -c | sort -k1 -rn | head 1917 git 981 rm 831 perl 801 vi 795 cd 344 ls 327 svn 289 sudo 233 mv 201 cp |
1 Apr
DBIx::Class::DeploymentHandler is nearly ready for prime time, so I’m going to discuss a pattern mst described to me that I’ve found very helpful in developing this project.
If you don’t already know what roles are you probably don’t read very many perl blogs etc. chromatic has written a series of blog posts where he discusses the various merits of roles vs whatever your poison is. Maybe read that. This isn’t really about that. What this is about though is that roles aren’t always the answer.
One of the assumptions of roles is that all of the methods in a role share their namespace. So if you compose Role1 and Role2 and they both implement a sleep method you will get an error at compile time saying that the method collides. This is a Good Thing and helps us not shoot ourselves in the foot. When it’s a problem is with private methods that the end user typically shouldn’t be calling, but happen to collide. As far as I know there is no way to have a role that partially composes with a class. I’m pretty sure that’s against the whole spirit of a role.
So instead of using a role to compose in the interface for whatever it is you are doing you can instead use delegation, where object A has-a different object B and uses the public interface of object B. That way private methods of B stay that way and don’t collide. The problem is that this can make code a lot more verbose. So instead of
1 | $dh->deploy |
one must do:
1 | $dh->deploy_method->deploy |
That make things a lot more verbose, it gives away the inner workings of $dh, and most importantly it makes overriding parts of $dh harder.
So basically the pattern goes like this:
1 2 3 4 5 6 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | package Im::A::Delegate; use Moose; with 'HandlesFooing'; has foo => ( is => 'ro', isa => 'Str', required => 1, ); has bar => ( is => 'ro', isa => 'Str', lazy_build => 1, ); sub _build_bar { 'silly } 1; |
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 | package WithDelegate; use Moose::Role; use Im::A::Delegate; has foo => ( is => 'ro', isa => 'Str', required => 1, ); has bar => ( is => 'ro', isa => 'Str', lazy_build => 1, ); has delegate => ( is => 'ro', isa => 'Im::A::Delegate', handles => 'HandlesFooing', lazy_build => 1, ); sub _build_delegate { my $self = shift; my $args = { foo => $self->foo }; $args->{bar} = $self->bar if $self->has_bar; Im::A::Delegate->new($args); } 1; |
1 2 3 4 5 |
And to use that you’d do:
1 2 3 4 | use GetStuffDone; my $gsd = GetStuffDone->new( foo => 'frewfrew', ); |
Of course that’s totally contrived, but it gets the general pattern across. If you want to see examples in action check out some of the roles from DBIx::Class::DeploymentHandler!
So basically you define your public interface, which isn’t a bad idea anyway, and the “handles” key for the delegate’s attribute takes the role that defines the public interface. This automatically delegates all the methods required by the role (and probably any defined by the role too.)
If you have private methods you want to reuse make another role and compose that into the delegate’s class, but don’t put it in the handles section.
I feel like this pattern, even though it yields a lot of boilerplate, helps to make very clean interfaces. Because of this pattern I’ve made well decomposed classes and testing them is dead easy. I imagine that to test roles normally you make stub classes using the roles. Here I just test the actual delegates alone and then I have an integration test that ensures the class that uses all the roles does the right thing.
Hopefully you’ll find this as useful as I have.