July 18th, 2007
God is a new process monitoring Ruby framework with a rather apt name (no, it’s not that God). The config files looks something like that:
God.meddle do |god|
god.watch do |w|
...
end
end
Interesting code to write. And feels safe too to have God watching over your mongrels. Oh and you can use it to clean up stale mongrel PID files without patching mongrel_rails. Right now I use monit a lot and it’s really rather unDRY to have repeated configs for each mongrel on the server (is there a better way of doing this in monit?). God would be able to DRY it up.
July 16th, 2007
I’ve never actually explored log rotation tools in Linux and merely knew of the existence of logrotate and left it at that until log sizes really became a problem. Turns out logrotate is amazingly easy to configure, and thanks to this Logrotate and Mongrel blog post by Corey Donohoe I have it setup for my Rails applications that are running on Mongrel. Here’s what my /etc/logrotate.d/mongrel_cluster looks like:
/var/railsapps/APP_NAME/shared/log/production.log {
daily
rotate 28
missingok
compress
sharedscripts
postrotate
for i in `ls /var/railsapps/APP_NAME/shared/log/*.pid`; do
kill -USR2 `cat $i`
done
endscript
}
July 11th, 2007
Update: Edge Rails now does whatever Jsonifier does (and more). Check out my blog post on JSON serialization maturity in edge Rails. This means that this plugin is virtually obsolete if you’re using edge Rails.
If you’ve tried to output JSON from your Rails applications before, you’d probably have noticed how inadequate it all seems. Let’s look at the kind of output (pretty-printed for easier reading) you get from calling to_json on your typical User model:
{
attributes: {
id: 1,
name: "Konata",
awesome: true,
created_at: "07/01/2007"
}
}
Some of the common problems developers who write applications that speak JSON have with this are:
- The
attributes part is often unnecessary cruft when consuming ActiveRecord objects as JSON.
- There’s no obvious way to control which attributes are shown in the JSON (e.g. you may want to hide private or lengthy attributes). The way to workaround this is either to write your own
to_json instance method for your ActiveRecord model, or to use a Rails plugin (like acts_as_json) or a gem.
- Including 2nd (or higher order) associations is tricky (as above, you can achieve this by defining your own
to_json instance methods).
Under the hood of the current ActiveRecord#to_json
The to_json method for ActiveRecord objects falls back on using that defined in ActiveSupport’s JSON classes. The way it’s currently done, there are a bunch of JSON encoder classes defined for various types - when you call to_json on an ActiveRecord object it simply uses Object#to_json.
By the way, see that created_at: "07/01/2007" bit in the example above which is the JSON output for a Date attribute? That’s in MM/DD/YYYY format. Yup, 1st July 2007 not 7th January 2007 you “rest of the world you”. Ouch, but at least you can use JavaScript’s Date.parse() on it directly as detailed in this patch. And before you ask, yes, Date.parse() doesn’t understand the YYYY-MM-DD format.
Can has Rails patch?
So about 2 days ago, I wrote a Rails patch to boost ActiveRecord#to_json with an options hash. Yep much like what you can do with ActiveRecord#to_xml.
I hope it gets committed into the core some day. I mean, gosh, isn’t Rails all about Web 2.0 mashups. It even has that fancy render :json thing. I do believe JSON output needs to be treated more seriously in Rails rather than the half-hearted attempt at JSON encoding. No? Well, if the patch doesn’t get accepted I’ll still have my plugin (see below).
Anyway, let’s see some examples:
Some examples, see?
Assuming User and Post models where User has_many Posts:
david = User.find(1)
david.to_json # {id: 1, name: "David", awesome: true, created_at: "07/01/2007"}
No more attributes cruft!
:only and :except work the same way that as for ActiveRecord#to_xml:
david.to_json(:only => :name) # {name: "David"}
david.to_json(:only => [:id, :name]) # {id: 1, name: "David"}
david.to_json(:except => :created_at) # {id: 1, name: "David", awesome: true}
david.to_json(:except => [:id, :created_at]) # {name: "David", awesome: true}
You can use the :methods options as well to include any methods on the object.
david.to_json(:methods => :permalink)
# {id: 1, name: "David", awesome: true, created_at: "07/01/2007", permalink => "1-David"}
david.to_json(:methods => [:permalink, :interestingness])
# {id: 1, name: "David", awesome: true, created_at: "07/01/2007", \
# permalink => "1-David", :interestingness => 666}
The :include option lets you include associations.
david.to_json(:include => :posts)
# {id: 1, name: "David", awesome: true, created_at: "07/01/2007", \
# posts: [{id: 1, author_id: 1, title: "Welcome to the weblog"}, \
# {id: 2, author_id: 1, title: "So I was thinking"}]}
:only, :except, and :methods works on the included associations as well:
david.to_json(:include => { :posts => { :only => :title } })
# {id: 1, name: "David", awesome: true, created_at: "07/01/2007", \
# posts: [{title: "Welcome to the weblog"}, \
# {title: "So I was thinking"}]}
Of course, 2nd level (and higher order) associations work too:
david.to_json(:include => { :posts => { \
:include => { :comments => { \
:only => :body } }, \
:only => :title } })
# {id: 1, name: "David", awesome: true, created_at: "07/01/2007", \
# posts: [{comments: [{body: "1st post!"}, {body: "OMGWTFBBQ!"}], \
# title: "Welcome to the weblog"}, \
# {comments: [{body: "Don't think too hard"}], \
# title: "So I was thinking"}]}
Please do give any feedback at the Rails patch for ActiveRecord#to_json or here. I wanna know what you feel about adding this functionality into the Rails core!
Jsonifier plugin
I’ve rolled the patch into a plugin: Jsonifier. The Jsonifier Trac is at http://trac.codefront.net/jsonifier/.
Subversion repository:
svn://svn.codefront.net/jsonifier/trunk
To install the plugin:
ruby script/plugin install svn://svn.codefront.net/jsonifier/trunk
A note on valid JSON
The JSON Rails spits out by default is not strictly valid JSON since the JSON specifications require keys to be double quoted. To get strictly valid JSON, add
ActiveSupport::JSON.unquote_hash_key_identifiers = false
in your environment.rb (or in the Rails initializers directory if you’re on edge). See my blog post on how to get strictly valid JSON from Rails for more info.
Season 2 of The Melancholy of Haruhi Suzimiya was announced in the August 2007 edition of NewType Japan - check out the scans. The announcement isn’t official, but there’ll be one on the 7th - that’s 7/7/2007! (1)
July 2nd, 2007
Not too long ago a couple of the Ubuntu Dapper Drake (6.06 LTS) VPSs we use abruptly became inaccessible. These VPSs were NIS clients that suddenly could no longer connect to the NIS master. Great. And as far as I could tell I didn’t do anything to those servers. I even managed to replicate the problem on another similar VPS (that was still accessble) simply by restarting the NIS daemon.
Thanks to Rimuhosting’s fantabulous support, I managed to regain access to the VPSs. Oh yeah, big lesson learnt here: when configuring NIS clients, be sure to leave at least one user account with sudo privileges or set the root account’s password (Ubuntu doesn’t come with a root account enabled by default - run sudo passwd to set it and enable the root account as a side effect). Big big lesson learnt. Don’t let this bite you in the ass if you’re a sysadmin-ish person.
Anyway, /var/log/syslog was complaining this:
ypbind[1026]: Unable to register (YPBINDPROG, YPBINDVERS, udp)
Which turned out to be a portmap problem. So yeah portmap wasn’t running! For a moment there I thought we had an easy solution. Sadly, /etc/init.d/portmap start was throwing this inexplicable error (i.e. Google wasn’t of much help):
root@zoidberg:/sbin# /etc/init.d/portmap restart
* Stopping portmap daemon...
...done.
* Starting portmap daemon...
dhclient.4.x 2.*.5, December 30th, 2000
Compiled on Dec 12 2003 09:55:54
Features: DBG, SEE, ALS, SEF
init: Couldn't open the file ./libuuid.so ->
...fail!
It sure isn’t caturday. After hours spent scrambling to find out why portmap can’t find libuuid.so, reinstalling the NIS .deb, comparing configuration files between working NIS clients and these busted ones, it finally dawned on me that I have yet to re-install portmap (not that it was logically sound that I’d have to reinstall it, but at this point I was willing to try anything). Annoyingly enough, it worked!
apt-get remove portmap nis
apt-get install nis
I still don’t know WTF happened there. If anyone has any clue please please point me to it.