Author Archive
Wiggly Disciples
Douglas Wilson writes of parents shepherding wiggly disciples on Sunday morning:
Many of you are here as parents of little ones and, in some cases, many little ones. For you, the worship of the Lord is a far more arduous task that it is for the rest of us. All of us are engaged in the work of worshipping the Lord, but you are carrying young ones in your arms as you perform the same labor that we do. . . .
Read on for encouragement that is really applicable to all of parenting: our parenting is unto God, and he intends for it to be a real part of our worship.
Encouragement to parents
John Loftness writes:
Humility . . . drives us away from self-sufficiency to believing in the promises of God.
Diligence alone will never make a good parent. Faith will. Faith in God’s promises. When we see what he’s promised about our children, it will shape how we think, what we expect, and therefore how we act. The only way to pray by faith for our children is to begin with a focused belief in God’s will for them. “If you abide in me, and my words abide in you, ask whatever you wish, and it will be done for you” (John 15:7).
These are the words that Nancy and I abide in. They shape our prayers and fix our hope:
“And the LORD your God will circumcise your heart and the heart of your offspring, so that you will love the LORD your God will all your heart and with all your soul, that you may live.” (Deut. 30:6)
“All your children shall be taught by the LORD, and great shall be the peace of your children” (Is. 54:13).
“‘And as for me, this is my covenant with them,’ says the LORD: ‘My Spirit that is upon you, and my words that I have put in your mouth, shall not depart out of your mouth, or out of the mouth of your offspring, or out of the mouth of your children’s offspring,’ says the LORD, ‘from this time forth and forever more'” (Is 59:21).
“For the promise is for you and for your children and for all who are far off, everyone whom the Lord our God calls to himself” (Acts 2:39—read the context, too).
JI Packer quotes Samuel Clark about God’s promises: “A fixed, constant attention to the promises and a firm belief of them, would prevent solicitude and anxiety about the concerns of life” (Knowing God, p. 115). Is there any greater concern than the concerns we carry for our children? Is there any greater comfort and hope than to hear and believe the promises of the one who spoke and worlds came into being? So pray the promises about your children. And see God work, in them and in you.
A gospel catechism
Our church practices credobaptism, and I’ve assembled this catechism to help ready my children for a pastoral interview. We’re also learning the apostle’s creed, below. Some influences are my pastors Phil Sasser and Daniel Baker, and also Chris Schlect and the Westminster-based catechism for young children. I’d be grateful for suggested improvements.
Catechism
Sin
What is your sin?
Disobeying God’s word (1 John 3:4)
What is the penalty for your sin?
Death (Romans 6:23)
Gospel
What is the gospel?
Jesus died for our sins, was buried, and was raised from the dead; just as God promised. (1 Cor 15)
Father
Why does God love you?
He made me His child.
How do you know that God loves you?
He gave his son Jesus for me.
Jesus
Who is Jesus?
He is God’s son, my maker, savior and king. He is my life and my treasure.
How is Jesus your Savior?
Jesus died in my place, so I am forgiven and adopted.
Where is Jesus now?
He rose from the dead and sits at our Father’s right hand.
How is Jesus your King?
He leads, provides, cares for and protects me.
Holy Spirit
Who is the Holy Spirit?
He is my helper.
How does he help you?
He gives me life, peace, comfort, and strength to become more like Jesus.
Response
What is faith?
Resting on Jesus for my salvation (Psalm 62:5-8)
Why do you love God?
He is great and good, and he loves me.
What is repentance?
To be sorry for my sin, to hate it as God does, and to keep turning from it
Why do you obey God?
Because I love him
Church, now and then
Who are God’s people?
They make up his church.
What does his church do?
We display God’s greatness and beauty, and serve and care for one another.
What will become of God’s people?
God will keep us to the end.
What happens at the end?
Jesus will restore his creation and live with his people.
Baptism
What is baptism?
Baptism is God’s marking out a person as his own.
What does your baptism signify?
I am cleansed from my sin by Jesus’s blood, and united to him in his death and resurrection.
Why do you want to be baptized?
Because I belong to Jesus
Apostles’ creed
I believe in God, the Father Almighty, Maker of heaven and earth.
And in Jesus Christ, his only Son, our Lord, who was conceived by the Holy Spirit, born of the virgin Mary, suffered under Pontius Pilate, was crucified, died, and was buried. He descended to the dead. On the third day he rose again from the dead. He ascended into heaven, where he sits at the right hand of God the Father Almighty. From there he will come to judge the living and the dead.
I believe in the Holy Spirit, the holy Christian church, the communion of saints, the forgiveness of sins, the resurrection of the body, and the life everlasting. Amen.
The Binding of God
. . . the essence of Calvin’s conception of the covenant is the notion of the binding of God. This binding is God’s own act of joining Himself with His creatures. . . . The gracious self-binding of the infinite God whereby He condescends to enter into a mutual covenant with His fallen and unworthy yet sovereignly chosen people is eloquently portrayed by Calvin in his sermon on Deuternonomy 4:44-5:3.
For if God only demanded his due, we should still be required to cling to him and to confine ourselves to his commandments. Moreover, when it pleases him by his infinite goodness to enter into a common treaty, and when he mutually binds himself to us without having to do so, when he enumerates that treaty article by article, when he chooses to be our father and Savior, when he receives us as his flock and his inheritance, let us abide under his protection, filled with its eternal life for us. When all of these things are done, is it proper that our hearts become mollified even if they were at one time stone? When creatures see that the living God humbles himself to that extent, that he wills to enter into covenant that he might say: “Let us consider our situation. It is true that there is an infinite distance between you and me and that I should be able to command of you whatever seems good to me without having anything in common with you, for you are not worthy to approach me and have any dealings with whoever can command of you what he wills, with no further declarations to you except: ‘That is what I will and conceive.’ But behold, I set aside my right. I come here to present myself to you as your guide and savior. I want to govern you. You are like my little family. And if you are satisfied with my Word, I will be your King. Furthermore, do not think that the covenant which I made with your fathers was intended to take anything from you. For I have no need, nor am I indigent in anything. And what could you do for me anyway? But I procure your well-being and your salvation. Therefore on my part, I am prepared to enter into covenant, article by article, and to pledge myself to you.”
The covenant, therefore, highlights God’s grace.
— Peter Lillback, The Binding of God, pp. 137-138.
We are as the prodigal son.
Rails pattern: trim spaces on input
Problem: Your Rails application accepts user input for a number of models. For many or most of these fields, leading and trailing spaces are a significant inconvenience — they cause problems for your validators (email address, phone number, etc.) and they cause normalization and uniqueness problems in your database.
Solution: Just as the Rails ActiveRecord class uses methods like belongs_to and validates_format_of to define model relationships and behaviors, create a new class method to express trimming behavior. There are a number of ways to do this; I will present one possibility that I have used in my own code. I created a file lib/trimmer.rb with the following contents:
module Trimmer
# Make a class method available to define space-trimming behavior.
def self.included base
base.extend(ClassMethods)
end
module ClassMethods
# Register a before-validation handler for the given fields to
# trim leading and trailing spaces.
def trimmed_fields *field_list
before_validation do |model|
field_list.each do |n|
model[n] = model[n].strip if model[n].respond_to?('strip')
end
end
end
end
end
Then I write the following in my models:
require 'trimmer' class MyModel < ActiveRecord::Base include Trimmer . . . trimmed_fields :first_name, :last_name, :email, :phone . . . end
While this makes the behavior available to particular models explicitly, you may prefer to make this behavior available to all of your models implicitly. In that case, you can extend the ActiveRecord::Base class behavior by adding the following to config/environment.rb:
require 'trimmer' class ActiveRecord::Base include Trimmer end
If you do this, the trimmed_fields class method will be available to all of your models.
My experience with Django and Rails
I’ve had the opportunity to work on both Django and Rails frameworks recently as part of one project. The core application for the church administrative tools that I am working on is written in Rails, while the church guest follow-up application that I am responsible for is written in Django. Why two separate stacks? GuestView began its life independently from Gospel Software, and I chose Django there because of my familiarity with Python. Three developers are sharing responsibility for the core of Gospel Software, however, and we chose Rails as the most reasonable lingua franca.
What follows are my personal opinions and observations. These are mostly aesthetic or other value judgments, and I offer them simply for your consideration.
Language
I’ve used the Python programming language for a number of years and like it a lot. I particularly enjoy its functional aspects, although lately I’ve become more of a fan of using list comprehensions and generator expressions wherever possible compared to map() and filter() with lambdas. Compared to Python, Ruby has much more powerful functional capabilities, although some things don’t feel natural to me (Ruby’s design choice to not require parentheses to denote function invocation means that you must use .call to call a lambda, which feels clunky). There are also some cases in Ruby where choosing one of several alternative forms of an expression can have a significant impact on your performance. Lambdas seem particularly costly in Ruby as of version 1.8.
Overall I think the languages are fairly on par. Right now I prefer Python for aesthetic rather than technical reasons. As I grow in familiarity with Ruby, and as it matures and its performance improves I think I may eventually grow to prefer it.
Object-Relational Mapping (ORM)
The Django ORM is very powerful and you can express complicated queries very efficiently using it. Django queries are not executed until they are actually used, so you can construct your queries piecemeal, which helps in writing readable code. Django also allows you some flexibility with adding custom SQL to your queries, but for anything complicated I’ve found that I have to break down and write my own SQL.
Rails 2.1 introduced the ActiveRecord named_scope functionality. Prior to this Rails was significantly lacking compared to Django’s expressive power for query construction, but named_scope pretty much evens the playing field. And for complicated queries, which you will surely face in any real-world project as you seek to tweak performance, ActiveRecord gives you a degree of control over your SQL that really puts Django to shame.
Both Django and Rails seem to have adequate support for PostgreSQL, my database of choice.
URLs
Django lets you express your URLs using regular expressions; Rails accomplishes this using routes. I personally prefer Django’s method, but both work well enough.
Templates
While Rails’ Embedded Ruby allows you to include arbitrary code in your templates, Django’s template engine is much more spartan. It provides ways of getting at variables passed to the template, including objects, dictionaries, lists, and even methods. And it has some simle control structures, but not covering the full expressive power of Python. I yearned for a more powerful template language in Django at first. But I found over time that the discipline of a simple template language was helpful to me, forcing me to move any complicated behaviors to the controller (or “view” as Django calls it) which was in most cases the right thing to do anyway.
There are still some areas where I think the Django template language is lacking. However, there is an open-source alternative to the Django template engine that is similar but sufficiently more powerful to meet my needs: Jinja2.
For me it is a toss-up between Embedded Ruby and Jinja2.
Performance
I suspect it’s common knowledge that Rails has a little ways to go in performance. For our own purposes, I didn’t find too much difference in time measurements between Django and Rails. However, Rails clearly has a much larger memory footprint than Django.
I was surprised to learn that even with a FastCGI or WSGI model, Django still opens and closes a database connection for each request. While there may be technical reasons that the Django architecture requires this, it was still a surprise to me. Django performance still seems on par with Rails in spite of this. Interestingly, having Django use pgpool to connect to PostgreSQL didn’t improve my performance at all, perhaps because my application and database are currently located on the same host.
Console
Both Django and Rails allow you to run a REPL session for your application. The Rails script/console command beats out Django hands-down, because Rails’ internal magic automatically imports pretty much everything you need. In Django you still need to import any models or framework modules before you can use them.
Debugging
The Rails built-in log is enabled out of the box and is very handy. Django provides logging functionality but you have to do a little extra work to enable it. Rails wins out on logging. Django is better at in-browser rendering of exception tracebacks. Overall the handiness its logging means a slight win for Rails here for me.
Admin Application
Django’s admin application is truly its crown jewel. If you need a private admin interface to your web application, Django will give you a very attractive and powerful interface almost entirely for free. I’m not aware of any equivalent for Rails that even comes close to this.
Deployment
I’ve deployed Django using FastCGI and Rails using Mongrel. Right now I am using Nginx to proxy to Mongrel, and to connect directly to the Django FastCGI instance. Neither Django nor Rails seems to have a unique advantage or disadvantage in deployment.
Summary
I’ve spent more cumulative time with Django than with Rails, so I feel subjectively more at home with Django. If I were going to write a small toy project, I’d choose Django mainly for ease and efficiency. In fact, I took this route for the meal and potluck scheduler application that I recently wrote for Google App Engine. GAE has many similarities with Django, and even allows you to run much of the Django stack on it.
However, for larger projects my current framework of choice is Rails. With the named_scope functionality in Rails 2.1, ActiveRecord is finally on par with Django’s ORM. And for any complicated queries ActiveRecord is superior to Django’s ORM. While Django’s admin application is handy, I don’t make much use of it. And while Rails falls slightly behind in performance and storage characteristics, I believe that Ruby and Rails will both continue to improve in this regard.
PostgreSQL foreign keys and indexes
If you’re a frequent user of MySQL, you may be familiar with the fact that all MySQL table constraints automatically create indexes for you. This is true of the InnoDB foreign key constraints, for which “an index is created on the referencing table automatically if it does not exist.”
If you’re switching or considering a switch to PostgreSQL, you should be aware that not all PostgreSQL table constraints will automatically create indexes for for you. In PostgreSQL, a UNIQUE or PRIMARY KEY constraint on one or more fields will implicitly create an index for you. However, in PostgreSQL a FOREIGN KEY constraint will not automatically create an index for you.
For each of your foreign key constraints, you should evaluate whether you want to create an index. You may want to do this for optimizing your own queries, but be aware that it can also help to speed up DELETE queries on the referenced table and UPDATE queries on the referenced field. This is because any foreign key reference must be located to enforce whatever ON DELETE and ON UPDATE behavior is in effect for the constraint.
SmugMug uploader
I’ve written a small Python script to upload pictures to a SmugMug gallery. I love SmugMug and use it extensively for family photos. I’m using this script for my personal use because it’s much simpler and much less of a resource hog than a browser-based uploader, and also because it was a fun exercise to try out the SmugMug API. You can run this script as follows to upload one or more files:
python upload.py gallery-name picture-file-name . . .
On Windows I’ve set up a desktop shortcut pointing to the script, and I can drag and drop a pile of picture files onto the icon and it will upload away. I’ve tested it using both Python 2.5 using simplejson, and also using Python 2.6 which has simplejson built in. Earlier versions of Python may require you to change the import of hashlib to md5, and change the hashlib.md5() invocation to a md5.new() invocation. You’ll also need to modify the script to contain your email address and SmugMug password, and obtain a SmugMug API key for your own development use, but this is a very painless process. Here is the script:
#!/usr/bin/python
##########
# Requirements: Python 2.6 or
# simplejson from http://pypi.python.org/pypi/simplejson
##########
EMAIL='...'
PASSWORD='...'
##########
APIKEY='...'
API_VERSION='1.2.2'
API_URL='https://api.smugmug.com/services/api/json/1.2.2/'
UPLOAD_URL='http://upload.smugmug.com/photos/xmlrawadd.mg'
import sys, re, urllib, urllib2, urlparse, hashlib, traceback, os.path
try : import json
except : import simplejson as json
if len(sys.argv) < 3 :
print 'Usage:'
print ' upload.py album picture1 [picture2 [...]]'
print
sys.exit(0)
album_name = sys.argv[1]
su_cookie = None
def safe_geturl(request) :
global su_cookie
# Try up to three times
for x in range(5) :
try :
response_obj = urllib2.urlopen(request)
response = response_obj.read()
result = json.loads(response)
# Test for presence of _su cookie and consume it
meta_info = response_obj.info()
if meta_info.has_key('set-cookie') :
match = re.search('(_su=\S+);', meta_info['set-cookie'])
if match and match.group(1) != "_su=deleted" :
su_cookie = match.group(1)
if result['stat'] != 'ok' : raise Exception('Bad result code')
return result
except :
if x < 4 :
print " ... failed, retrying"
else :
print " ... failed, giving up"
print " Request was:"
print " " + request.get_full_url()
try :
print " Response was:"
print response
except :
pass
traceback.print_exc()
#sys.stdin.readline()
#sys.exit(1)
return result
def smugmug_request(method, params) :
global su_cookie
paramstrings = [urllib.quote(key)+'='+urllib.quote(params[key]) for key in params]
paramstrings += ['method=' + method]
url = urlparse.urljoin(API_URL, '?' + '&'.join(paramstrings))
request = urllib2.Request(url)
if su_cookie :
request.add_header('Cookie', su_cookie)
return safe_geturl(request)
result = smugmug_request('smugmug.login.withPassword',
{'APIKey' : APIKEY,
'EmailAddress' : EMAIL,
'Password' : PASSWORD})
session = result['Login']['Session']['id']
result = smugmug_request('smugmug.albums.get', {'SessionID' : session})
album_id = None
for album in result['Albums'] :
if album['Title'] == album_name :
album_id = album['id']
break
if album_id is None :
print 'That album does not exist'
sys.exit(1)
for filename in sys.argv[2:] :
data = open(filename, 'rb').read()
print 'Uploading ' + filename
upload_request = urllib2.Request(UPLOAD_URL,
data,
{'Content-Length' : len(data),
'Content-MD5' : hashlib.md5(data).hexdigest(),
'Content-Type' : 'none',
'X-Smug-SessionID': session,
'X-Smug-Version' : API_VERSION,
'X-Smug-ResponseType' : 'JSON',
'X-Smug-AlbumID' : album_id,
'X-Smug-FileName' : os.path.basename(filename) })
result = safe_geturl(upload_request)
if result['stat'] == 'ok' :
print " ... successful"
print 'Done'
# sys.stdin.readline()
I am donating this script to the public domain. You are welcome to use and modify it as you please without conditions. I’d appreciate hearing about your experience with this script or any changes and improvements you’ve made; please leave a comment. Thanks!
Update 2010-07-20
Since I first posted this, I’ve updated it as follows:
- Add a Content-Type header of ‘none’. This is to workaround a bug in the SmugMug API.
- Use basename() to send only the file’s basename for X-Smug-FileName.
- Rewrite safe_geturl() to loop up to five times if the upload attempt fails. I’ve found that uploading is surprisingly unreliable, and re-attempting the upload generally works fine.
- Add a commented call to readline() at the end of the script. In my case, I run my script by dragging files onto an icon on my Windows desktop, which causes it to run in a DOS window and vanish when done. If you uncomment this line, it will wait for you to press Enter when it is done uploading. You’ll be able to see any files that weren’t uploaded successfully.
Update 2010-11-28
SmugMug made a recent change to their API’s login behavior which broke this script. While the new login behavior is not documented in the API docs, the fix is apparently to use a session cookie along with the session ID. While it’s a bit of a kludge, I’ve updated the script above to save this cookie in a global variable and submit it on subsequent requests.
Update 2011-06-24
I’ve fixed a bug in the script causing it to wrongly report a failure for certain requests that don’t send back the session cookie. The fix involves testing whether a set-cookie header was returned before accessing the header.
Update 2013-10-01
Version 1.2.0 of the SmugMug API has stopped working, so I have updated the script to use version 1.2.2 of the API.
