Wednesday, December 30, 2009
Bread::Board
If that was too vague for you - don't worry - Stevan promised some more docs for Bread::Board soon. The real take away from all of this is that Bread::Board and future sub-classes of it spares you from the need to writing adaptor classes for all the view or model classes of your yet another web framework. Maybe you'll not need a framework at all - and assemble your application using Bread::Board out of a web dispatcher and other ready made parts.
Wednesday, December 23, 2009
Frameworks are not yet well refactored libraries.
The title of this blog post might not be universally true, it is possible that there are some kernel parts in some frameworks or even most of the frameworks that could never be in any practical way reduced into sets of libraries. But what I observe is that frameworks tend to grow and only in very exceptional cases spawn libraries that are refactored out of their main body. It is just so much easier to just write new extensions, plugins or things that use the whole framework than find the right abstraction that would let to split some functionality out of the main code base. Certainly I would never guess that the simple PSGI abstraction would let people refactor so much into separate libraries, I bow to it's author. But we should always try:
Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away.
Antoine de Saint-Exupery
Friday, December 18, 2009
Catalyst app/context split
Thursday, December 10, 2009
Scope and Immutability
- Application - i.e. all data that is persistent to the whole web server process (like configuration)
- Session - i.e. data saved into the session variables (there is an opinion, and I mostly subscribe to it, that you should keep that minimal).
- Request - i.e. the data you receive from the web client and that you send back to it
An example of framework that does not respect this division is known to everyone - it's Catalyst - fortunately the app/context split is mostly done.
Acknowledgements: Stevan thanks for the web apps scope break down :)
Saturday, December 05, 2009
External dependencies once again
Saturday, November 28, 2009
Warszawa.pm - Biblioteczka (The Library)
Last Thursday there was a Warszawa.pm meeting. Worth noting is the inauguration of our library - Piotrek lended Peopleware: Productive Projects and Teams to me. Thanks!
Friday, November 20, 2009
What are the features that Perl shares with Lisp?
Perl is much more like Lisp than it is like C. If you pick up a good
book about Lisp, there will be a section that describes Lisp’s good features. For example, the book Paradigms of Artificial Intelligence Programming, by Peter Norvig, includes a section titled What Makes Lisp Different? that describes seven features of Lisp. Perl shares six of these features; C shares none of them. These are big, important features, features like first-class functions, dynamic access to the symbol table, and automatic storage management.
Dear lazy web: what are the other three features?
Saturday, November 14, 2009
On naming things
There is power in naming things as the popularity of Design Patterns shows.
Wednesday, November 11, 2009
Stuff
Socio-Technical Systems
I like the definition from The Social Requirements of Technical Systems, now I have what CPAN realy is. I have not yet read the whole Handbook of Research on Socio-Technical Design and Social Networking Systems but the rest seems rather dissapointing.
von Hippel
I recently discovered that most of his work is available on-line: Collected Papers, Downloadable Books. I recommend it to people trying to understand Open Source. He is much more 'technical' than Yochai Benkler. I've just finished reading Open Source Software and the 'Private-Collective' Innovation Model.
Rene Girard
I am working on a paper about on-line conflict in the light of mimetic theory.
Tuesday, November 03, 2009
Role that adds Traits to multiple classes
lookup_options
method which loads data from the db table (ResultSet object more precisely) appropriate for the field into the field's options array. It does not really touch any global form stuff and:
$field->form->lookup_options( $field, ... )
is like reaching with your left hand to your right pocket. I mean really awkward. So what we need is a form role that would add other roles to the fields classes. I imagined that this could be possible, in theory a role could be a hierarchy that would be applied to a class hierarchy - but I don't think you can do that with current Moose, so instead I used a hook in the method that builds fields and added there code to inject an appropriate Trait.
The code is in new branches in the main HTML::FormHandler repo and in the HTML::FormHandler::Model::DBIC repo. I am waiting for commetns. I would think that this is not something unusual - so I guess others must have already solved similar problems before - I would like to hear what they did.
We have a similar problem with rendering. It is a role now so that it can be easily replaced, but it is naturally recursive, fields should know how to render themselves so we need a way to apply a whole set of roles in one sweep to the whole form class structure. It looks like there really is some abstract super-role in there.
How much is it monkey patching, how much is it action at a distance?
Tuesday, October 27, 2009
Controller immutability
I'll leave it to the experts to list all the advantages of immutable data structures, but I've seen why we needed a similar compartmentalisation in HTML::FormHandler. The FormHandler form object is reusable, you can use it to validate many parameter hashes. We started coding it as a mutable object and storing all the changing values in the form object itself, the first problem was that we kept forgetting to reset them at the start of processing of new data. This lead to many subtle bugs, sometimes difficult to track so we started to just recreate the form object for each processing run. This worked OK, because the form object itself was not that heavy to create until someone had a form object that contained other heavy object and recreating it would require recreating them. So eventually we separated all the state that changes during the processing into a separate
Result
object, now to clean the form state we need to recreate only that separate object.It needs to be stated clearly that this separation of immutability is a trade-off (as always) - it is an additional requirement so it increases the complexity of the code and means for example that the
$self
object in controller actions is underused - because actions should not change it's state (including storing something in it's attributes). Certainly it is not feasible to make such change now and this also would be a trade-off but for the sake of exercising imagination: maybe actions could be methods on the changing part (and not on the controller object) and only use the controller object in some way - this way all the basic code (like that from the Manual) would use $self
object in the way common to object oriented code.
Saturday, October 24, 2009
Catalyst::Component::InstancePerContext
There is much discussion about the stash and how to replace it. The main problem with stash is that it is global - there is no use in replacing it with another global thing. Just like global variables it needs to be replaced by many specialized local things like parameter passing and attributes, the controller object attributes, and for that to be entirely clean we need the a new controller object per response.
Wednesday, October 21, 2009
OpenID support in LoginSimple
The test application is in
t/lib/TestAppOpenID.pm
, it has some tests in t/07-openid-live.t
, but you can also run it with perl -Ilib t/lib/script/testappopenid_server.pl
and then go to the http://localhost:3000/login
page and test it manually.
Tuesday, October 13, 2009
HTML::FormHandler - a few notes
Let's look at the functions of a form processor:
- processing the input into internal representation
- checking the values
- saving the internal representation into the persistence layer and loading values from it
- rendering the form (with input, error messages, etc)
The 'best practice' of rendering the HTML view of an application page is to do it in the templates. Unfortunately there is just too much logic needed for the correct rendering of forms (mostly with the display of errors) to leave that for the templates mini language (like that of TT) - that's why this needs to be done by the form processor. But because of the diversity of the rendering requirements it also needs to be easily replaceable, ideally part by part. Then the application could start with a crude but working HTML rendering, introduced with minimal effort from the programmer since it is built into the processor and later gradually refined and replaced.
CGI.pm and after it Catalyst and other libraries parse the url-encoded and body parameters into a hash of scalar values but internally we would like to manipulate objects like DateTime instead of lists of values for year, month and day. In HFH this conversion is done in two phases - first the input is turn into a deeper structure built of hashes and arrays - then then the processor goes recursively from the leaves and at each node applies a conversion if it is defined for that node.
There is always a question when we should do the checks - sometimes it is easier do do them on the input data sometimes on the internal values. A good example is DateTime - we can check if the day field is a number between 1 and 31, but we also need to check if the whole date is correct and the business rules could add another requirement that the day of the week is Monday or some other operation that is most convenient on the DateTime object. In FormHandler all of this is possible:
How this works? As explained above the processor starts with the leave nodes - and checks the
day
, month and year
fields (here they don't define any transformations only checks, but they could). Then it goes to the parent node date_time
and there it has the hash of values from the leave nodes ready for the next transformation and checks - it is passed to:sub { DateTime->new( shift ) }
If that subroutine triggers an exception, for example when someone passes
{ year => 2009, month => 2, day => 30 }
to it - then the error is recorded with the message This is not a correct date. If it works - the value returned from it, a DateTime object, is then passed to the next check, and later presumably saved to the database. The checks and transformations are taken from a single list - so you can interweave them as you need.
Instead of
transform
and check
in the apply
attribute we can also use the Moose types and coercions and also we can mix these two styles (for example coerce some value using Moose types and then check it again). If someone needs even more power - he can also write his own field class with it's own validation method - and do both validation and transformations in there, that method would receive the same hash of child values.To save the full data structure of values that can contain many inter-related records
HTML::FormHandler::Model::DBIC
usesDBIx::Class::ResultSet::RecursiveUpdate
. I separated it so that HFH is not related to the hacks I made there :) Or maybe I hoped that perhaps other form processors could reuse my module. Using it you can update a company record together with all it's related addresses in one form.
Wednesday, October 07, 2009
Barriers to entry
I believe that fixing CPAN installation issues, each one usually a small issue occurring in some special cases is needed (at least for the most popular modules) if we don't want to confine CPAN users, and since CPAN is the killer application of Perl, then it follows that also Perl users, to a narrow niche.
Another thing is how off-putting are the pessimizations that seem gratuitous, you immediately feel neglected and you imagine how the arrogant developers just don't care about your case. Then there are also pessimizations that look small if you have a big project - but are impenetrable barriers for small projects - this is how Perl lost the place of the most used language for WWW programming to PHP.
Tuesday, October 06, 2009
CPANHQ
The biggest problem for me is that I had to modify the database quite extensively and now the loading scripts don't work any longer. To show something I decided to load the database into the repo - so now it can take awful long time to download the software, but in return right after the download you should be able to try out the searching. It is not at end user quality - you need to know some internals to use if effectively (like using 'me.name desc' in the order field to sort the packages by name), but it should be reasonably fast. Now I am thinking what are the most useful search strategies.
Tuesday, September 29, 2009
warszawa.pm
Co prawda zwykle piszę tutaj w języku angielskim - ale reklamówkę naszej grupy Perl Mongers postanowiłem napisać po polsku, żeby było widać, że u nas też się coś dzieje. Mamy już swoją wiki: http://warszawa.pm.org/. Jeszcze nie wszystko działa, ale miejmy nadzieję, że administrator sie postara i naprawi. Żeby dostać konto na wiki trzeba poprosić na liście dyskusyjnej, niestety ze względu na spam nie mogliśmy wiki zrobić zupełnie otwartej. Zapraszamy na wiki, listę i spotkania.
Tuesday, September 22, 2009
Optional Dependencies Are Going Out of Fashion
- Remove the recommends from Makefile.PL, DBIx::Class is not
supposed to have optional dependencies. ever.
I am noting this so that it will not missed by the general public that optional dependencies are going out of fashion.
The problem here is that if an optional dependency enables some useful feature (and otherwise why would you add it?) - then you should expect that someone writes a library using this feature and uploads that library to CPAN. What that other guy should add to his dependencies? How is he supposed to know which of your optional prerequisites are needed for his module? And what if this is not about just two modules in dependency relation but rather a whole chain of them? Then it becomes a mess.
Monday, September 14, 2009
How they do it in Python (core libraries).
I would first prune the standard library down to a more core focus of libraries that require cross-platform expertise, using widely accepted standards, and stuff needed to simply get a script to run. In terms of cross-platform stuff, that would mean os, multiprocess, etc. These are modules that a typical developer would have a hell of a time getting working on all the OSs that CPython runs on.
Saturday, September 12, 2009
How to install Catalyst::Authentication::Credential::OpenID
For those impatient the winning sequence of commands (for Debian/Ubuntu) is:
sudo apt-get install libgmp3-dev
cpanp -i Math::BigInt::GMP
and now you can:
cpanp -i --force Catalyst::Authentication::Credential::OpenID
Now the story - at my pristine installation of Perl 5.10.1 on my Ubuntu box I discovered two problems with C:A:C:O installation. First was a trivial POD error, detected with the new pod tests - this is why you need to do that
--force
(it is fixed in the dev version). The second is more interesting - installing one of the prerequisites - Crypt::DH seemed to take forever. Fortunately by chance I found a good explanation of the problem in Crypt::DH::GMP docs - apparently for any reasonable performance Crypt::DH needs Math::BigInt::GMP which is not in the prerequisites. Without it it still works - but it can take serveral hours to pass the tests (reported first 3 years ago). So next thing is install Math::BigInt::GMP - unfortunately this is not automatic either - you need to install gmp.h header file first (on Debian/Ubuntu it is the libgmp3-dev package). This is why you need to execute the first two commands.I hope that will help someone.
Monday, September 07, 2009
Change Management and FOSS authors obligations
Unexpectedly changing a library's API can be like pulling a rug from under the feet of authors of software using that library. In the past that was a rare event - and usually there was time to warn all the parties involved and let them fork the project if needed. Now in CPAN we have such huge dependency tries that even if it is still rare for the individual leaf nodes - it becomes quite frequent for the root nodes where all the changes cascade from the leaves. These deep dependency hierarchies also makes forking a lot more problematic as it can mean forking not just one library - but a whole path of them leading from the leaf up to the root. CPAN might be still rather unique in it's size among distributed library projects - but with the overall exponential growth of FOSS software we can expect that to happen in other projects soon. That's why if we want to continue the process of lego-like assembling software components out of more basic libraries we need those libraries to adhere to some change management policies.
I am inviting FOSS authors to formulate their change management policies (and in particular API deprecation policies) and voluntarily add them to the manuals. I want to stress it - it is not about a top down rule forcing authors to do something - this is an invitation to voluntarily give up a bit of the authors freedom in leading the project in order to make it more useful. The bulk of FOSS can still be published as a gift to the humanity with no strings attached. I just want to show that additional option and also create some pre-canned policies that could be cut and pasted into the documentation just like the FOSS licences are.
Wednesday, September 02, 2009
What really fails in installation?
PERL_MM_USE_DEFAULT=1
and did make
for a module that requires half of the modules on my computer. Everything was installed automatically - with no additional intervention from me. The morale of this is that if you are using Ubuntu and if you have installed all the external prerequisites - then the installation is really rather automatic. The problem with systems other than a special flavour of Linux is both hard to solve and rather unsophisticated - we just need more people working on those systems and reporting and solving bugs. Or at least that is the solution I can imagine. But the problem with external prerequisites is where we could do better with just a bit of programming.I remove memcached from my system and try
zby@zby:~/progs/Enzyme$ cpanp i Cache::Memcached::Managed
Installing Cache::Memcached::Managed (0.20)
Running [/home/zby/local/bin/perl /usr/local/bin/cpanp-run-perl /home/zby/.cpanplus/5.10.1/build/Cache-Memcached-Managed-0.20/Makefile.PL]...
No executable memcached found: No such file or directory
Running [/usr/bin/make test]...
PERL_DL_NONLAZY=1 /home/zby/local/bin/perl "-MExtUtils::Command::MM" "-e" "test_harness(0, 'blib/lib', 'blib/arch')" t/*.t
Can't exec "memcached": No such file or directory at /home/zby/.cpanplus/5.10.1/build/Cache-Memcached-Managed-0.20/blib/lib/Cache/Memcached/Managed.pm line 843.
#
# Stopped memcached server
# Looks like you planned 176 tests but ran 184.
t/001basic.t .....
Dubious, test returned 255 (wstat 65280, 0xff00)
All 176 subtests passed
(less 177 skipped subtests: -1 okay)
t/002inactive.t .. ok
t/003multi.t ..... ok
Can't exec "memcached": No such file or directory at /home/zby/.cpanplus/5.10.1/build/Cache-Memcached-Managed-0.20/blib/lib/Cache/Memcached/Managed.pm line 843.
#
# Stopped memcached server
t/010fork.t ...... ok
Can't exec "memcached": No such file or directory at /home/zby/.cpanplus/5.10.1/build/Cache-Memcached-Managed-0.20/blib/lib/Cache/Memcached/Managed.pm line 843.
#
# Stopped memcached server(s)
t/020grab.t ...... ok
Test Summary Report
-------------------
t/001basic.t (Wstat: 65280 Tests: 184 Failed: 8)
Failed tests: 177-184
Non-zero exit status: 255
Parse errors: Bad plan. You planned 176 tests but ran 184.
Files=5, Tests=36330, 18 wallclock secs ( 6.12 usr 0.03 sys + 3.86 cusr 0.20 csys = 10.21 CPU)
Result: FAIL
Failed 1/5 test programs. 8/36330 subtests failed.
make: *** [test_dynamic] Error 255
[ERROR] MAKE TEST failed: Bad file descriptor PERL_DL_NONLAZY=1 /home/zby/local/bin/perl "-MExtUtils::Command::MM" "-e" "test_harness(0, 'blib/lib', 'blib/arch')" t/*.t
Can't exec "memcached": No such file or directory at /home/zby/.cpanplus/5.10.1/build/Cache-Memcached-Managed-0.20/blib/lib/Cache/Memcached/Managed.pm line 843.
#
# Stopped memcached server
# Looks like you planned 176 tests but ran 184.
t/001basic.t .....
Dubious, test returned 255 (wstat 65280, 0xff00)
All 176 subtests passed
(less 177 skipped subtests: -1 okay)
t/002inactive.t .. ok
t/003multi.t ..... ok
Can't exec "memcached": No such file or directory at /home/zby/.cpanplus/5.10.1/build/Cache-Memcached-Managed-0.20/blib/lib/Cache/Memcached/Managed.pm line 843.
#
# Stopped memcached server
t/010fork.t ...... ok
Can't exec "memcached": No such file or directory at /home/zby/.cpanplus/5.10.1/build/Cache-Memcached-Managed-0.20/blib/lib/Cache/Memcached/Managed.pm line 843.
#
# Stopped memcached server(s)
t/020grab.t ...... ok
Test Summary Report
-------------------
t/001basic.t (Wstat: 65280 Tests: 184 Failed: 8)
Failed tests: 177-184
Non-zero exit status: 255
Parse errors: Bad plan. You planned 176 tests but ran 184.
Files=5, Tests=36330, 18 wallclock secs ( 6.12 usr 0.03 sys + 3.86 cusr 0.20 csys = 10.21 CPU)
Result: FAIL
Failed 1/5 test programs. 8/36330 subtests failed.
make: *** [test_dynamic] Error 255
The tests for 'Cache::Memcached::Managed' failed. Would you like me to proceed anyway or should we abort?
Proceed anyway? [y/N]:
[ERROR] Unable to create a new distribution object for 'Cache::Memcached::Managed' -- cannot continue
*** Install log written to:
/home/zby/.cpanplus/install-logs/Cache-Memcached-Managed-0.20-1251915361.log
Error installing 'Cache::Memcached::Managed'
Problem installing one or more modules
zby@zby:~/progs/Enzyme$
Yeah - the information is there - but now imagine that this was a deep dependency tree with lot's of similar external prerequisites. You try to install the module - it fails, you view the log, locate the problem, install the external dependency, run the installation again and it fails again. Everytime you need to parse the really long installation log and try to understand what happened. It is not that complicated (although it can become complicated when there aren't so explicite error messages, or when there are other installation failures mixed together) - but it is intimidating and really time consuming. Definitively it is not something that a newcomer would be able to do. But external dependencies are not something extraordinary - they are rather standard, this procedure I described here is not exactly a failure mode - it is anticipated.
Someone could argue that when I install
Cache::Memcached::Managed
I should know that it needs the memcached
binary - but it is only obvious for those that know that module, and if that module is somewhere deep in the dependency tree you might not even realize that you are installing it.Now imagine this:
zby@zby:~/progs/Enzyme$ cpanp i Some::Module
Computing prerequisites.
You have unmet external prerequisites:
memcached
xslt version 1.1.18
...
Please install the prerequisites first then run the installation again.
wouldn't that be helpful?
PS. How about
requires_external memcached => sub { -x "memcached" }
in Makefile.PL?
Sunday, August 30, 2009
CPAN installation failures
So what can we do? One thing that I would propose is to stop using optional features and dependencies because they really get into the way of automatic installs. We can also simplify the dependency graph of some more important modules, make better statistics about the failure rates and they origins, but probably the most effective way will be to be just more vigilant about the test failures in our modules and in the prerequisites. The important lesson here is that a 1% failure rate seems like nothing important - but it can make our module inadequate to become a prerequisite for some more complex library.
Sunday, August 23, 2009
Let's write some deprecation policies
Should a project have deprecation policies as soon as it hits CPAN with a development release?
I think the answer should be yes - there should always be a deprecation policy. But this deprecation policy does not need to be the full monty of always keeping the backcompat to at least one major version, using strict deprecation cycles etc - it just needs to be stated explicitely and not relying on the version numbering convention as they are not really universal. There is no negative side to putting a few sentences into the docs, only a bit work to formulate them. And here is my idea:
Let's write some deprecation policies
Let's have a list of well formulated policies covering as much as we can of possible circumstances (from experimental to mature, and from one-person projects to team work) that could be copied to the CPAN distribution docs with minimal effort. For the start - here are some ideas:
- Experimental - no policies at all, use at your own risk
- Experimental with mailing list announcements of new versions and promise to take into account problems encountered by people testing the new code
- Backcompat to one version, with a specific minimal time between versions
- Backcompat to one major version, with a specific minimal time between major releases
Update:
In related news mst gives an overview of techniques that can be used for maintaining back-compat - sometimes these are really simple things.
Friday, August 14, 2009
Dissecting the Rails Screencast
- Simple
- Writing a blog engine - what can be simpler?
- Unexpected
The first surprise is the medium - that was the one of the first popular screencasts to circulate the Internet. But of course the main load of unexpectedness was in the fact that a simplistic, but still functional, blog engine can be build in 15 minutes. Not months or years of a team of programmers, a designer and a HTML coder - but 15 minutes of one person - that is surprising.- Concrete
That was not an abstract plan, framework with holes to be filled in later - but a concrete implementation. And once again - blog engine - what can be more concrete for the web afficionados?- Credible
You can see it for yourself - it is not a description of how to do it - it is filmed the whole process of doing it. And later you can also try it on your own.- Emotional
Not really - but you don't need to cover all of the points.- Story
This is the most important thing - the screencast is a story. The viewer can easily imagine himself writing his own web application with the same efficiency - this is how stories work.
PS. Don't read this as an argument for (more) screencasts. Yes - they can be effective - but it all depends. What I am advocating here is using that checklist for all marketing projects.
Tuesday, August 11, 2009
Marketing for Perlers
The book that I would like to recommend is Made to Stick. Why Some Ideas Survive and Others Die ... - it is about creating messages that will be remembered. It is build around a simple list:
- Simple
- Unexpected
- Concrete
- Credible
- Emotional
- Stories
these are the ingredients of an effective marketing message (and yeah the first letters do form an mnemotechnic acronym).
The message needs to be simple, so that the basics are easily understood and can be easily spread around. I think this will be well understood to programmers. It is not about dumbing it down - it is about finding the real core. It needs to be unexpected to catch peoples attention. To hold attention the book advices to use curiosity gaps. Next quality is concrete - this is about involving things the audience already knows well, tangible things and painting a mental image. Credibility comes from inside or from an outside authority, inside credibility comes from using vivid details, statistics but also can be gained by inviting the audience to test for themselves. Emotional is about speaking to emotions. Finally the best messages are stories - the power of story telling comes from the fact that that people reach the conclusions for themselves and they easily absorb them as their own.
Apparently this was tested by the ads business:
The final group was trained for two hours on how to use the six creative templates. Once again, the fifteen best ads were selected by the creative director and tested with consumers. Suddenly these novices sprouted creativity. Their ads were rated as 50 percent more creative and produced a 55 percent more positive attitude toward the products advertised. This is a stunning improvement for a two-hour investment in learning a few basic templates! It appears that there are indeed systematic ways to produce creative ideas.
from excerpts.
Friday, August 07, 2009
The Catalyst API
It's time to review the API, check how Moose can improve it, remove the redundancies and make it slim.
One more piece of food for thought: the thing that always bothered me is the first parameter every action receives:
I've never seen it used. I even was never sure if that thing is a controller object or if it is the controller class (i.e. if the actions are class or object methods), is it ever explained in the docs? Now I tested it and the output was:
so at least that action was an object method. I've reviewed the whole Catalyst Tutorial and, even though this parameter is passed to every action example there it is used in only a couple places (as
$self->action_for('list')
and $self->roles
). This does not seem like the best Huffman coding.
Sunday, August 02, 2009
Dear TPF - don't send money, send thank you letters
Even if the money rewards are already something decided - and no change can be made - TPF could at least accompany them with such a thank you letter. And there should be a way to make it signed by Larry Wall himself - this is crucial for the impressiveness factor.
Friday, July 31, 2009
Fixing dependencies
I wanted to write some code using Catalyst::Authentication::Credential::OpenID unfortunately one of it's dependencies fails it's tests. Here I'll document how I analysed that and produced a patch, it's not yet approved by the author - but the tests pass.
Step first - checkout the ParanoidAged from it's repository (I had the repository address from an email to the Catalyst mailing list). If you don't have access to the repository of your failing dependency - then just download the source from CPAN unpack it and work from there. Then run the tests:
zby@zby:~/progs/pa$ perl -Ilib t/00-all.t
.
.
.
ok 27
5 second tarpit (tolerance 2)...
not ok 28
# Failed test at t/00-all.t line 180.
3 second tarpit (tolerance 4)...
ok 29
Killing child pid: 7669
# Looks like you failed 1 test of 29.
OK - the failing test is at line 180. Let's see what is there:
I changed that to:
ran the tests and save my debug output:
zby@zby:~/progs/pa$ perl t/00-all.t 2>debug
. What I got there is:
Now the info is there - but of course I did not spot it at once. I did a lot of more debugging and testing other hypothesis before I focused on:
'x-died' => 'read timeout at lib/LWPx/Protocol/http_paranoid.pm line 394.'
. Yeah interesting - something died, but no error was reported. The line in question is:To get some more evidence I changed that to:
Now the debug output changes to:
And again - it is there - but to spot it I had to play with the debugger first. I don't remember what exactly I did - but finally I sow it - there are two evals in this stack trace, the first one is in LWP::Protocol and the second in lib/LWPx/ParanoidAgent.pm line 313. I checked that second eval - and yeah - it tried to retrieve the error already catched by the first one. So when there was a timeout the agent was aborting the retrieval - but the error was being cleared and not reported. I wrote following patch:
and now all the tests pass. Finally I uploaded the patch to RT.
Wednesday, July 29, 2009
On dependencies
The fact that it has no dependencies makes it even more compelling.It's the sort of thing one could more easily attract new developers with. Can you imagine rewriting my old CGI course with something like this? Or maybe a way to introduce new users to Perl? There are many excellent Perl frameworks out there which I would, nonetheless, hesitate to start new users on. Just trying to set up CPAN can be difficult for them; working through even one test failure is often too much. For people who want to jump in and learn "Web programming", though, this might work.
On the other hand Jay advices to stop worrying about dependencies The lie of independence - or - how I learned to stop worrying and love the dependency chain. There is truth in both statements. I have the impression that the problem of CPAN dependencies have improved much recently. The visibility of installation problems directly from the cpan search engine (cpan testers matrix, dependencies review) is a great step ahead. But still installing Catalyst and DBIC and TT is a big hurdle - and I can understand why Ovid is not enthusiastic about teaching Perl novices how to do that.
For me Mojo sounds like iPod - slick and beautiful and really useful - but you are not supposed to change the battery. It is a nicely integrated product - but somehow closed. The author apparently tries to make it perfect, tries to polish every detail - what perhaps would not be possible if he had to compromise with the visions of the authors of the other packages. I can feel his frustration here. But rejecting all dependencies and rewriting everything in his own way sounds a bit extreme and not welcoming.
The big question here is how much can we trust fellow CPAN authors to make the right decisions, can we rely on their work? Can someone, who put's a lot of attention for the details of his API, rely on modules with imperfect APIs, perhaps spoiling his own work and not become insane? The story of the phalanx project shows that there are no easy answers to these questions, but I believe we can do better than rewriting everything from scratch each time.
Sunday, July 26, 2009
Google Wave
And now imagine that you have it in one tool - not only those additional steps go away - but it also opens the way for new features like seeing a pointer to the part of the code that the other person is talking about, seeing his edits in real-time and commenting on them immediately etc.
This is why I believe in convergence of communication tools.
Friday, July 17, 2009
Nothing is forever
Now one would ask why write a library that is useful only temporarily? Why not include at once all the parts needed in the final product? There can be several reasons:
- something that is only a part of the scaffolding in one project can stay in the final solution in another one
- the author of the library might not yet fully understand the problem, and it can take years to reach the right abstraction
- the distribution of possibly needed modification can be so uniform that there is no reason to concentrate on one of them more than on another one and it is virtually impossible to cover them all
- and finally: nothing is forever - you never know where a project will go in the future and what you'll need to change
A good example (of the third point) are code samples from manuals - you copy them to quickly get something working - but later you modify it and sometimes you change every character from the original. Another one can be the scaffolding code in Rails - you can debate if it fits the first or the second or other points - but it was useful to a lot of people.
Every library can be used as scaffolding - so why not accept this and help the developers in using it that way?
Monday, July 13, 2009
CPAN and code reuse
And what with applications?
Thursday, July 09, 2009
Code reuse styles
I wonder why it is like that. There are some technical differences that lead developers in one or the other direction - but there is also a cultural/psychological cause. Doing some self-analysis I must admit that I am not really enthusiastic about contributing to a Perl CMS (like for example Bricolage) - in my view all the already-build solutions use obsolete libraries. I'd rather contribute to building the perfect library - then code an extension to Bricloage. I have the feeling that I am not alone in that crazy perfectionism around Perl hackers. PHP clearly wins here with more pragmatic approach.
Monday, July 06, 2009
Deprecated code analyzer for HTML::FormHandler
Saturday, July 04, 2009
Is using the Catalyst 'forward' method a cargo cult?
And when it is not enough? What are really the differences between:
$c->forward( 'some_action' )
and the standard Perl:
$self->some_action( $c );
As far as I know the only two differences are that the first call is called in an eval and that it is logged in the debug output (if it is switched on), maybe there are some other minor differences, but, I am sure, nothing dramatical. It can helpful/needed sometimes (and othertimes that eval can be a nuisance), but it is also easy to add. Of course this is the simplest case - 'forward' signature is more complex and it does amazing things for finding the action "by it's private name" - but for most of the cases you ain't gonna need it and you can use ye old Perl syntax to make the call and spare yourself learning how to pass parameters to the forward call.
Related reading: Premature Flexibilization Is The Root of Whatever Evil Is Left - a reddit discussion.
Wednesday, July 01, 2009
Functional versus Unit testing
Also discussed at Hacker News
Monday, June 29, 2009
InstantCRUD and FormHandler
Friday, June 26, 2009
Iron Man Blogging Challenge
There is just one small nitpick - if the goal is: "to promote the language to the world outside the Perl echo-chamber" then the rule that the all the posts need to be about Perl is misguided. If we want to talk to "the world outside the Perl echo-chamber" then we need to talk about the subjects that they care about. I can understand why we need some focusing - and posts about what you had for dinner are not the best way to promote the language - but it will be much easier to engage the outside audience if we wrote about general programming subjects. Look at advertisings - they don't talk about the advertised brand all the time, the more subtle the link is, the more engaging they are.
Update: I should have added that this post was inspired by Gabor Szabo: http://szabgab.com/blog/2009/06/1245788806.html
Saturday, June 20, 2009
CatalystX::Comments - RFC
Here is the current API - any comments are welcome. First you need to declare the controller using the new Moose style:
package RavLog::Controller::View;
use Moose;
BEGIN {
extends 'Catalyst::Controller';
with 'CatalystX::Comments::ControllerFormRole';
}
has model_name => ( is => 'ro', isa => 'Str', default => 'DB' );
The important part for us is of course
with 'CatalystX::Comments::ControllerFormRole'
, the model_name
attribute should hold the name of the model CatalystX::Comments will use to store the comments.After the declarations you can use CatalystX::Comments to put the comment form on the stash:
sub view : Chained('base') PathPart('view') Args(0)
{
my ( $self, $c, $id ) = @_;
...
$self->stash_comment_form( $c, $self->article->id );
}
That's the whole controller part - what it handles is generating the form for display and saving the submitted comments. In the view templates you need to add:
[% comment_form.render %]
For displaying the comments themselves ravlog already had some template code and I did not change it.
Next is the model part. The library assumes that the DBIC model contains a Comment Result class like that one in ravlog. Of course it does not need to have all of the columns, and can also have other columns - but these are the columns that will be filled by the CatalystX::Comments form. Ideally that model part should be a Result base class - so that it can be subclassed and adapted to local circumstances (for example the belongs_to relationship to article needs to be changed), but for now I don't know how to do that.
Now some more details. Thanks to html_prefix
all parameters sent from this form are prefixed with the form name - so this form can be added to pages with other forms, it will recognize it's parameters. The comments are saved only when the HTTP method used is POST, and after a successful comment creation the user is redirected to the same page (this seems a bit constraining - but see next paragraph - this is only meant to be temporary solution - I try to keep the API simple).
Of course I understand that this can never cover the needs of a comment sub-system in a mature social web site. I am thinking about it as more of scaffolding - code that let's you quickly develop a feature, see how it integrates with the rest of the user experience, formulate a more complete requirements list - and replace it part by part.
Friday, June 19, 2009
On Scaffoldings
Scaffolding is a wonderful metaphor - and the brilliance of it shows in how it transcends the original area of code generation where it was introduced in Rails. It can be applicable to any reusable software component meant to be temporary support and to be replaced later. An example of that are code samples in manuals - you copy them just to have something working, but then you modify it and nearly completely replace the original code. And since nothing is universal and no library is ideal - it can be used as a generic measure of how well a library supports this iterative development style. And this brings me to the question - what are the qualities of a good scaffolding? One is that it needs to be gradually replaceable.
Saturday, June 13, 2009
Module Rank and Informed Choice
But it all still relies on people choosing the right modules for their own projects - I believe this is a good assumption, the popular "The Wisdom of Crowds" shows why - but still it can be improved by people doing more informed choice. This was my motivation behind my API design blog post. Design of APIs is tricky and subtle and there are things that you just don't spot until it is too late, or alternatively there are deficiencies that are invisible for the seasoned user because he is used to the quirks, he internalized them and he uses the API automatically without thinking.
Sunday, June 07, 2009
Form rendering and CSS
I started reviewing the options and it seems that there are three general approaches to having aligned forms:
- tables
- divs
- lists
- no additional markup
Plus some appriopriate CSS. I think the 'no additional markup' option could never accommodate for error messages, so I leave this one out. This leaves me with three options. I am sure that not everything can be done in each one - but I think we can add a 'style' parameter to the renderer to make it configurable.
Next thing is the recently popular CSS grids - anyone using them? They seem like a nice option for someone needing some universal defaults and the SenCSS example even contains a form styled in two ways (vertical and horizontal alignments) - but would it work with error messages? And the Blueprint one even shows a div with the error class - but how that is to be inserted into the form? I guess each one requires it's own way of HTML structuring.
Friday, June 05, 2009
Packaging cross cutting Catalyst features
To try out what can be done about that I decided to write a generic comment system for Catalyst (+DBIC, TT and FormHandler). I've already posted the the first code samples to the Catalyzed wiki. The form code and the controller stuff seems closed - it does not need to refer to things from outside but in the DBIC declarations the belongs_to relation of the comments needs to relate to an external table and it's primary key (I assume that comments belong to an article/post/something). How that can be made configurable?
Wednesday, May 27, 2009
API design
There are many aspects of quality - here I would like to focus on the APIs. The problem with APIs is that their shortcomings are much more subtle than let's say bugs. Many of them are just inconveniences - things that you stop to see once you get accustomed to them, and all of them require some experience or theory just to understand that it can be done better. Arguments about APIs can become dangerously subjective. We are in dire need of API theories, objective measures to evaluate them - and here I try to compile a list of on line resources on that subject.
How To Design A Good API and Why it Matters (from Google Tech Talks)"
- see also an ACM paper with the same title and a nice abstract in the form of bullet points.My take-away points (the most non-obvious or potentially useful in arguments):
- write examples from the very start and build the API from use cases
- good names are important - API is language
- "when in doubt leave it out"
- implementation somtimes leaks out to the API
- for inheritance document self-use pattern (how methods use one another)
- "don't make the client do what the module can do"
- you can't please everyone so aim to displease everyone equally.
The Little Manual of API Design
Characteristics of good design:
- Easy to learn and memorize
- Leads to readable code
- Hard to misuse
- Easy to extend
- Complete
Again stress on writing use cases and code using the API during the design process - and one surprising but seemingly valid point: "prefer specific names to general names".
API Design Matters
This one is mostly arguing why API design matters - but there are a few points worth noting:
- always design the API from the perspective of the caller (rather obvious)
- again start with use cases
- there used to be much more programming from scratch - now we use more libraries and designing API becomes more and more important, but this is not yet reflected in university curricula (this is exactly the intuition that led me to this blog post)
Designing and Evaluating Reusable Components
The needs of the programmer changes as her project proceeds and it is important to account for that fact when writing the API. In particular more advanced projects usually need more granular APIs while at the beginning they are only an obstacle.
API design guidelines
- my question on Reddit.Update: Read Curing Python's Neglect - it's not really about Python in my opinion - and it is about how people get used to bad APIs and stop noticing the ugliness of them.
Tuesday, April 21, 2009
Constraints and Types (in Moose)
If that is not yet clear - let's take the example of checking the age of a application user (from his provided birth date). The question if a user with a given birth date can legally participate in a web forum cannot be answered without knowing what is current date - it is not a property of the birth date alone - but of it's relation to the current date. I think that using the term 'type' for this kind of constraints can be confusing, even though technically in Moose this is possible to declare this.
Saturday, April 04, 2009
Roles for FormHandler
package BookDB::Form::BookWithOwner;
use HTML::FormHandler::Moose;
extends 'HTML::FormHandler::Model::DBIC';
has '+item_class' => ( default => 'Author' );
has_field 'title' => ( type => 'Text', required => 1 );
has_field 'publisher' => ( type => 'Text', required => 1 );
has_field 'owner' => ( type => 'BookDB::Form::BookOwner' );
This should be quite self-explanatory for anyone working with forms (and DBIC).
Now - I think Model::DBIC should be just a role here, but that is just a side track. The real question is about the fields here - ideally I would want that doing:
with 'HTML::FormHandler::Model::DBIC';
(this is after it is made a Role) - would add appriopriate methods to the fields - an example here could be validation of fields that need a unique value. I would like to not need to change the definitions above to:
has_field 'title' => ( type => 'Text::DBIC', required => 1 );
Fields like 'owner' above which are Compound fields and work mostly like a form itself and need to load the options for SELECT boxes from the model, set the values of fields from a database row etc:
package BookDB::Form::BookOwner;
use HTML::FormHandler::Moose;
extends 'HTML::FormHandler::Field::Compound';
with 'HTML::FormHandler::Model::DBIC';
has '+item_class' => ( default => 'User' );
has_field 'user_name';
has_field 'fav_cat' => ( label => 'Favorite Book Category' );
has_field 'fav_book' => ( label => 'Favorite Book' );
has_field 'occupation';
has_field 'country' => ( type => 'Select' );
Ideally this definition should not need 'HTML::FormHandler::Model::DBIC' - applying this role to the root form should be enough.
Tuesday, March 31, 2009
Roles and data structures
You can start with defining the data structure and one of the aspects as the 'primary package' - and then add the other aspects as (Moose) roles. This is how it is started in FormHandler - the primary aspect here is the data processing and validation - then saving the the database is added as a role (so you can use it with different data store libraries DBIx::Class, Class::DBI and in the future all the other), the same with rendering - it is a role added to the form class. This is all nice untill you realize that in this way you add the additional roles only to the main form object - but not to the fields (i.e. nodes in the tree) - so you can walk the fields and do the stuff - but you regress here to mostly procedural code.
What we need is to somehow add this role globally to all the field and form classes used.
Tuesday, February 17, 2009
Hierarchy of code reuse
- Code generation and cut and paste: unconstrained modifications
- Inheritance and passing callbacks: modifications only at the method granularity
- Normal libraries: no modification of existing algorithms - only composing them into larger constructions
Thursday, January 29, 2009
Reading "Design Principles and Design Patterns"
I think the real takeaway is "Stable packages should be abstract packages" - where stable for the author means 'many other packages depend on that one'. The problem with translating that to dynamic languages is that the most abstract (in the OO sense) packages, the pure interfaces don't need to be written down in code at all. Duck typing gives us a great flexibility - but it also means that there is no requirement to write down the abstract interfaces - and if there is no additional pressure then they will not be defined by the programmers.
Another problem with translating the ideas into dynamic languages is the meaning of the word 'depend'. With static typing it can be clearly defined by the need to recompile the package if there is some change in the package it depends on. For dynamic typing this needs to be defined in some more subtle ways - basing it on the type of the coupling between the packages.