Nearly four years later, I’ve created a much more complete Bible memorization service - LearnScripture.net.
The rest of this post is left for the sake of history. What used to be there at https://lukeplant.me.uk/bibleverses/ now redirects to learnscipture.net
I recently decided to get more serious about memorising parts of the Bible. I’ve found I can learn one a day fairly easily, with the right system, and realised I needed two things:
- A system for ensuring I have a good coverage of topics.
- A method for keeping track of what I have memorised so far
It is now just about ready for public consumption, so I present my:
It should be fairly self explanatory. (read: I haven’t had time to write any help yet). At the most basic level, it is a set of pages with verse lists. But if you register and log in, check boxes appear which you can use to indicate verses you have already learnt, and this data is stored on the server.
I solved the first of my two aims with a set of YAML documents that define sets of verses and a hierarchy of topics, along with some Python scripts that combine them (into pages on my personal wiki first of, and now into an HTML version), allowing me to see what I’m missing.
YAML proves to be very convenient for this type of thing: you can use search and replace to do changes to more than one area, and can easily add new functionality. For example, the scheme definitions are perfectly readable in English, but exceptional easy to turn into nice data structures in Python to manipulate the data. The Python scripts generate the actual schemes, which makes it very easy to add new verses and still have schemes that are balanced and varied (or focused, if that’s what your scheme definition says).
The web app is written in Python, using CGI. (It is a very basic app, which talks only JSON, and uses just two database tables—so I didn’t want to rely on a heavy framework that would have special hosting requirements). I started using web.py at first, but had two problems with it:
For an AJAX CGI app, it is much too heavyweight, as “hello world” on my server took about 1.2 seconds due to everything that was being imported.
Frankly, I thought it sucked. I have been spoilt by the lovely design of Django. web.py really has a different niche to Django, but given Aaron’s dismissiveness of Django’s API design I was expecting he would have learnt more from it, such as:
Views are functions. Using classes which have ‘GET’ and ‘POST’ methods may seem like a good idea, but often it isn’t due to the duplication between the two responses. Going from a function based system to a class based system is very easy (just implement SomeBaseClassForViews.__call__()). But going the other way around is ugly.
View functions simply take request objects and return response objects. In web.py, parts of the request path are passed into the function, as in Django, but you have to call web.input() to get query data and posted data (where web is a module, not an object passed in). But, even worse, you call web.header() to write headers, while the body of the response is ‘print‘ed out (or returned as a string in version 0.3). This is horrible.
With Django’s method, you can do amazing things like the @login_required decorator (it is very cool — seamlessly persists any posted data, passing it along to the view once the user has successfully logged in [EDIT - see comment below]). And the implementation is robust and straightforward, not magical (though not completely simple either, due to the nature of the case). You can also do cache control decorators, and write a validator middleware etc. This is simply from following standard good programming practice, in which side effects are avoided where possible, and function outputs depend only on inputs.
Some of Aaron Swartz’s design decisions just niggled me too. It is claimed that “web.py lets you write web apps in Python”, but to me it seems more like something which is reminiscent of Python, but sloppier. Take for instance the definition of URLs. If it had been this:
urls = [ ('/(.*)', 'hello') ]
…I would have understood it straight away. Instead, I stared at this for a while:
urls = ( '/(.*)', 'hello' )
…and eventually found that the explanation is it’d be a lot more typing to do it the sensible way. I guess readability doesn’t count for anything.
So anyway, I happily ditched web.py, wrote my own Django-inspired mini framework that does just what I need, and fixed my performance problem (the ‘framework’ is in webutils.py, see the views.py file for example usage. Most of it was developed using TDD, which turned out to be a big timesaver — it’s just so quick to make big changes and be sure you haven’t broken anything.
Anyway, it has been a nice little project, and I’m hoping that it will actually prove useful to me and others!