Zen: Building Maintainable Catalyst Applications


Published on

After several years of building Catalyst applications, I've established a list of techniques that greatly increase maintainability.

Subtle points that are easy to understand, and easy to implement, that will help please your users and make your life easier.

Published in: Technology, Education
  • Be the first to comment

No Downloads
Total Views
On Slideshare
From Embeds
Number of Embeds
Embeds 0
No embeds

No notes for slide
  • Bad things happen. Apps break, they throw exceptions.
  • I used to roll my own custom packages, but ErrorCatcher has changed most of that. Being able to emit
  • I have to alert myself in some way, and the user should see something sensical that either helps them help me, or at least helps them.
    This means an error action that is verbose, looks at the action that went wrong and is helpful.
    Sometimes I send email (using Catalyst::View::Email) or sometimes I just use Catalyst::Log::Log4perl and dispatch FATALs that way.
  • I used to roll my own custom packages, but ErrorCatcher has changed most of that. Being able to emit
  • Catalyst::Plugin::ErrorCatcher does a good job.
  • Catalyst::Log::Log4perl has a lot of dispatch options. It works very well, isn’t slow and I can think of no reason to not use it.
  • Getting valid and useful information from end-users is maddeningly difficult. Programmers like to think they’re so smart, but they still can’t seem to figure this out and then berate users for not knowing what is relevant.
    It’s the developers fault. Your error should explain what information is relevant, and if it can do that, just go ahead and send that on to the developers. Expecting the user to do it just means you’re lazy. So don’t be lazy, and if you want to be lazy, go hack on Rails.
  • Catalyst::Log is a very good start for a basic logging mechanism. It provides a rudimentary buffer, with a flush mechanism and the typical debug levels.
  • The already on CPAN enhanced logging package is Catalyst::Log::Log4perl, which really brings out a lot of power. You get all of the power of Log::Log4perl, but still it exists as $c->log.
  • Even if you use a logger that has all sorts of fantastic methods, you probably don’t want to use them. Using the standard debug, info, warning, error and fatal methods gets you what you want, and there really aren’t a lot of convincing arguments. If you adhere to those methods, then you can swap loggers in and out without having to update your app.
  • The fantastic _dump method gives you a pretty-printed output similar to what you’d get with Data::Dump (and uses Data::Dump under the hood).

    This is an undocumented “private” method because it deviates from the standard logging methods above, but Catalyst::Log and Catalyst::Log::Log4perl both support it. It emits the message as an info message.
  • Right. Configuration. This with Chained is my primary reason for loving Catalyst. A lot of people don’t know how to do this right.
  • Every Catalyst component, including the application (MyApp.pm), has a config accessor. As a rule, only modify this BEFORE setup is completed. After that, you can get heisenbugs. You’ve been warned. So, this is how you configure something in a controller.
  • To configure that same controller at a higher precedence, you do it at the application level. These two configuration examples are identical and produce the same results.
  • To configure that same controller at a higher precedence, you do it at the application level. These two configuration examples are identical and produce the same results.
  • And to get at it, the big thing is to simply use accessors to get at the configuration key.
  • A moose example to create the ‘foo’ accessor, since we have the ‘foo’ config key (that was set to ‘bar’)
  • A moose example to create the ‘foo’ accessor, since we have the ‘foo’ config key (that was set to ‘bar’)
  • Or the old school way. The one that sucks.
  • The accessors work because config keys are merged into $self in the Catalyst component.
  • So, you have the hash entry ‘foo’ that is set for you in your component to work with. This is merged in the way you would expect.
  • So if you have the configuration specified in the myapp_local file, that gets set in $self->{foo}. It just works down from there, traversing into the myapp.conf file and then the application configuration and finally the controller itself.
  • But, people still do this wrong.
  • This is wrong. While it will -probably- be right, don’t do it. Simply do $self->{foo}. That is what you want, and in all cases will be the right value.
  • But, that case will almost always be correct. This case, however, will most likely not be. At component creation time, the configuration is merged into $self already. So use that.
  • Once you get an understanding of configuration, you really should use it and abuse it. Doing so will really enhance your applications and hopefully get you writing less code that does more. Which leads conveniently to the next point, number 4.
  • What makes a better controller? Well, to me, the thinner the better. The best way to do this is base classes.
  • The first and obvious step is to use more base classes. Put your generic code there, and benefit immediately.
  • A lot of controllers fit into specific types of behaviors, most of which are configurable. With the knowledge you now have of configuration, you can make very thin controllers that inherit from thicker base controllers and still delegate as much as possible to the models.
  • For most cases, sometimes your classes can really be nothing more than a config block. If you have a DBIC CRUD application, this is very easy to accomplish.
  • There’s a package on CPAN that does this exactly, and gets you very far with nothing more than configuration blocks. I urge you to give it a try, as for most cases it works very well. Since it’s a base class, you can override any methods it uses. It uses Chained, so it’s very simple to alter behavior.
  • But, base classes aren’t the best solution.
  • If you haven’t been sold on the idea of roles versus inheritance (and multiple inheritance in particular), I’m not going down that road. Catalyst 5.8 is all Moosey, which mean you can take advantage of Moose. Roles will make your code better. Promise.
  • This doesn’t provide a lot of examples, but does point you in the right direction. If you combine roles with MooseX::Role::Parameterized you’ll be amazed how flexible and generic your roles become. Then you do more with less code.
  • Moose is slower. Maybe. You probably won’t notice in your web application, and it will be faster and more robust. Less code for you to test, and your tests will get messed up.
  • Pretty much all the core contributors to Moose are better programmers than I am, so why would I not use it? I would hire any of them, and you should to. Even better: use their work without paying them a dime. Awesome deal, isn’t it?
  • Write tests.
  • The entire methodology is a route, letting you incrementally travel from point A to B. The steps can be easily modified and logged, so it’s very very powerful.
  • This is my golden rule when building something. It’s served me well, and if I work on an application with any significant number of users, I look at the tests to determine the workflow. Most of my tests are
  • Here’s a snippet of a Test::FITesque test, which keeps status and context between the object tests, so I can mix and match. It makes it easy to test how things are done.
  • We’ve all heard of mod_perl, and some of us have even used it.
  • But you should already know enough about mod_perl to use it. I’m not saying it is bad, I’m just saying that if you don’t know that you need it, you probably shouldn’t use it. So lets move on.
  • This means that your webserver (Apache) is spawning the FastCGI processes and handling the process management directly. This requires restarting your webserver to restart your application. I don’t like that.
  • I prefer the external method, using a separate process manager and starting the FastCGI daemon and have it listen on a unix socket. This has a couple benefits, and ties into…
  • Multiple processes can listen on a file socket, so you can start your new version on the same socket.
  • Which, is conveniently part of the manager that I use. It supports graceful restarting by opening the new instance on the FCGI socket and then shutting the old process down after the new is up and running. In other words, awesome and zero downtime.
  • There’s also PSGI, and its reference implementation: Plack. Using Catalyst::Engine::PSGI lets you use this cool stuff, but it’s new.
  • I ran a service with over 1,000 concurrent requests and pushing about 2Mbps of JSON data for an event, with Plack and AnyEvent (Tatsumaki, specifically). It works well, so go see Miyagawa-sans talk on Plack.
  • The main point of local::lib is that it separates your application from the vendor supplied paths. What this generally means is that on a per-user basis, you can have your own perl lib tree. Stuffing everything in the vendor perl has a lot of limitations, and it just isn’t a good way to do it. You’re really bound to whatever version of perl, bugs and all, the vendor supplies because updating Perl may break your entire system. Additionally, security patches supplied by your vendor may break Perl (Like Apple’s latest security update).
  • You can do it, and most people do with few negative side-effects. It doesn’t make it good, and considering how easy local::lib is to get going, there really isn’t a reason to not use it.
  • In your application, if you keep your Makefile.PL (or Build.PL) up to date, you can install all the dependencies as the user that is running the application. You’re guaranteed that the packages you install via local::lib are those running your application, so on a multi-user system (or multi-application, with one application per user) you can run different versions of different software.
  • In most cases, your application shouldn’t run as root. You shouldn’t have your dependencies requiring root, either.
    If you use external FastCGI, local::lib, you can run and deploy your entire application without ever touching the root account.
  • To setup local::lib, just install it from CPAN. When you load local::lib, it dumps out environment variable settings to use. Simply store those in your bash or cshrc profiles and you are set to go. Installing modules from CPAN or from a tarball puts them in a local, sanitary directory.
  • Want to start from scratch? No Problem. You still have a fallback to work from with your Perl, so removing your local::lib directory won’t destroy Perl. You can start from scratch.
  • local::lib defaults to a ‘perl’ directory that lives in your home directory. However, it is very trivial to have a lot of local::lib paths, and to even switch between them.

    This is particularly useful for doing smoke testing, if you have multiple environments.
  • Zen: Building Maintainable Catalyst Applications

    1. 1. Zen The art of Application Maintenance. (with Catalyst) by Jay Shirley 1
    2. 2. 1. Bad things happen 2
    3. 3. Handle your garbage. 3
    4. 4. Use the tools available to you. 4
    5. 5. How? 5
    6. 6. Choices, but I use: Catalyst::Plugin::ErrorCatcher Catalyst::Plugin::CustomErrorMessage 6
    7. 7. Catch errors. 7
    8. 8. Use better logging. 8
    9. 9. Blood from a turnip. 9
    10. 10. 2. Logging 10
    11. 11. More than Catalyst::Log 11
    12. 12. Catalyst::Log::Log4perl 12
    13. 13. Use standard methods 13
    14. 14. The little known $c->log->_dump() 14
    15. 15. 3. Configuration 15
    16. 16. It can save you. 16
    17. 17. It will probably just help. 17
    18. 18. So, learn to do it right. 18
    19. 19. package MyApp::Controller::Foo; __PACKAGE__->config( ‘foo’ => ‘bar’ ); 19
    20. 20. MyApp->config( ‘Controller::Foo’=>{ ‘foo’ => ‘bar’ } ); 20
    21. 21. --- name: MyApp MyApp::Controller::Foo: foo: bar YAML 4 Life. 21
    22. 22. Use accessors. 22
    23. 23. has ‘foo’ => ( isa => ‘Str’, ... ); 23
    24. 24. has ‘foo’ => ( isa => ‘Something’, coerce => 1 ... ); Moose++ 24
    25. 25. __PACKAGE__->mk_accessor(‘foo’) 25
    26. 26. Use $self. 26
    27. 27. $self->{‘foo’} 27
    28. 28. myapp_local.(yml|conf) myapp.(yml|conf) MyApp.pm MyApp::Controller::Foo 28
    29. 29. Do Not: 29
    30. 30. package Controller::Foo; __PACKAGE__ ->config(‘foo’=>‘bar’); sub method : Local { my ($self, $c) = @_; $c->config ->{‘Controller::Foo’} ->{foo}; } 30
    31. 31. package Controller::Foo; __PACKAGE__ ->config(‘foo’=>‘bar’); sub COMPONENT { my ($self, $c, $config)=@_; $config->{foo}; # NO! } 31
    32. 32. Overuse configuration. Convention is for lazy people. 32
    33. 33. 4. Better Controllers for Better Applications. 33
    34. 34. Base Classes! 34
    35. 35. Inheritance is easy to understand. 35
    36. 36. Lots can happen with configuration. 36
    37. 37. Good Example: Catalyst::Controller::DBIC::API 37
    38. 38. More awesome: Roles! 38
    39. 39. Yes, roles! 39
    40. 40. Read CatalystAndMoose.pod 40
    41. 41. 5. Use Moose. 41
    42. 42. Really, just use it. 42
    43. 43. Moose is better than you. 43
    44. 44. 6. Testing. 44
    45. 45. Rethink tests. 45
    46. 46. Two types of tests. 46
    47. 47. More important: User Emulation. 47
    48. 48. If your app is hard to test, it is hard to use. 48
    49. 49. Unit Tests are still important. 49
    50. 50. I like Test::FITesque 50
    51. 51. my $test = Test::FITesque::Test->new({ data => [ [ 'Roostermatic::Test::Game' ], [ 'init_schema' ], [ 'team_create', 'Liverpool' ], [ 'person_create', 'Steve Gerrard', 'mid@fielder.com', 'testing' ], [ 'join_team', 'Player' ], ] }); 51
    52. 52. 7. Deployment 52
    53. 53. mod_perl 53
    54. 54. You would know if you should use it. 54
    55. 55. FastCGI 55
    56. 56. “Static” 56
    57. 57. External 57
    58. 58. Zero Downtime! 58
    59. 59. For managing the managers, I use FCGI::Engine::Manager 59
    60. 60. PSGI 60
    61. 61. New, but hot. 61
    62. 62. 8. local::lib 62
    63. 63. Vendor Perl = Not Good 63
    64. 64. Not bad, not good 64
    65. 65. Use Makefile.PL 65
    66. 66. Application is User Level 66
    67. 67. Simple! perl -Mlocal::lib 67
    68. 68. Problem? rm -Rf $HOME/perl5 68
    69. 69. or, create more! 69