Monday, September 21, 2009

Enhancing Forms

I want the forms to look nice... eventually...

http://www.jankoatwarpspeed.com/post/2008/07/27/Enhance-your-input-fields-with-simple-CSS-tricks.aspx

Wednesday, August 19, 2009

third-order associations using nested :include

The bottom line:

ward_members = Contact.find_all_by_ward(current_user.ward, :include => [{:calling => :calling_type}, :address_group, :photo ])

A little background for non-members:

I'm attending BYU, which is operated by The Church of Jesus Christ of Latter-day Saints.
In Provo, the Mormon Meca, each apartment complex belongs to one or more wards.
A ward (which is an old English word for neighborhood) is a unit of the church of about 300 members and may include members from various neighborhoods and apartment complexes (or a few entire cities in areas where the church is less widespread).
A member may have one or more callings (volunteer responsibility)
There are various types of callings (leadership, teachers, etc)

The Ward Menu (formally called Directory, but referred to as menu as it is often used for dating purposes) that I'm creating will have a few different sections.

Title - a picture and the name of the ward
Bishopric - the 3 primary leaders of the ward
Leadership - various members in positions of leadership, not including the bishopric. Sorted by calling type
Members - photo directory of all members including leadership, but excluding bishopric. Sorted by apartment building or neighborhood, door number, first name.
Index - list of all members as stated above, sorted by first name

I need to generate a query that gets all members, their calling, their calling's type, and their address' group.

@bishopric = Contact.find(:all, :conditions => ["callings.name LIKE ? AND ward_id = ?", 'Bishop%', current_user.contact.ward], :include => [:callings, :photo], :order => 'callings.name')

@leadership = Contact.find(:all, :conditions => ["callings.name IS NOT NULL AND callings.name NOT LIKE ? AND ward_id = ?", 'Bishop%', current_user.contact.ward], :include => [:callings])

@members = Contact.find(:all, :conditions => {:ward_id => current_user.contact.ward}, :include => [:photo], :order => ['address_line_1, address_line_2, first']) - @bishopric

One problem I ran into is that :include does a left join rather than a inner join, but since I'm not up to snaz on my :joins-fu and I don't need the calling info for the photo part of the directory, I decided to just subtract the bishopric from the array. Otherwise @members would only consist of members with callings.

Friday, August 7, 2009

Securing the Data

For ease-of-use I wanted to avoid using a self-signed certificate and so I was thinking to use the RSA javascript library (which I may still do, just for fun) to transmit the password.

However, after some chat with some people on the UUG mailing list I see that it would be much more gentlemanly to encrypt all of the data.

As it turns out, GoDaddy provides free SSL certs for OpenSource projects.

Thursday, August 6, 2009

Shaving response times and adding progress with spawn

One of the reasons that lds.org is such a hassle is that their server responds so slowly.

I want this application to be user-friendly. The problem is, how do I make an abstracted layer faster than the original?

I don't.

But I do fake it as best I can and I'll make up for it later with HIJAX. The idea is that I'll use spawn to fork blocks of code into the background (like the generator I created in the last post) so that the rendered page returns sooner and I can do a little ajax polling in the background on the client-side to update the client page as needed. In the meantime, I'll allow the user to take care of things which aren't on the 'critical path', like grouping and formatting options.

Installing spawn in rails
sudo apt-get git-core
cd ~/Code/TheWardMenu
./script/plugin install git://github.com/tra/spawn.git
#app/controller/directory.rb
...
spawn do
while record = @photo_directory_generator.next
member = Member.new(record)
member.ward = @ward
member.save end
end

Using links2 as a quick test for transfer speeds from lds.org here's a way that things could go

User initiates request
  • 4s - TheWardMenu.com responds
  • 5s - User enters credentials
  • 1s - twm receives request
  • 3s - Lds.org has responded
  • 1s - Update Profile page parsed
  • 0s - spawned the block of code that gets the directory
  • 1s - response sent
The user has spent the last 6 seconds choosing pdf options rather than dying of boredom
  • 4s - the text-only directory is now in the database
  • 0s - the photo downloading process is spawned
The user can now review the text version of the directory for mistakes and see some real-time sorting as well as see the % complete on the picture downloads via a polled feed which gets only pictures that are more recent than the last polling.
  • 2m - the photos are downloading
The user finishes adjusting the settings and hits 'Download PDF' and is given the approximate wait time needed to download the remaining photos (which are already being downloaded into a hidden div so that the browser caches them).

The user leaves the site happy or makes adjustments to a fully viewable picture directory and downloads the pdf again (and it only takes a few seconds this time around)

Generator (like an Iterator) in Ruby on Rails

I'm creating a rails-agnostic class in ruby for getting the member photo directory.

Downloading the entire photo directory is slow. It takes a few minutes. Users don't want to wait 3 minutes to find out what is happening (success or failure, etc), but I don't want to put my model code into a library which could otherwise easily be used for non-rails apps.

The main issue here is that once the class instance is gone, so is the authentication. So it doesn't work to just store the URLs to the photos. Each photo must be downloaded. However, if the URLs to the photos are temporarily stored in the class and then downloaded one-by-one in the class, the wait for the text part of the directory to the download is acceptable (for me).

Take a look
http://secure.lds.org/units/login
/Membership Directory/.click
/with photos/i.click

The solution? A generator.
Here's a brief work snippit that shows how my solution works generically:



class Contacts
def import_directory
@records = []
#... get names and photo urls
@records << [:name => 'abc']
@records << [:name => 'jkl']
@records << [:name => 'xyz']
return true
end

def next_contact
if not @gen
create_generator
end
return @gen.next
end

private
def create_generator
require 'generator'
@gen = Generator.new do |g|
for record in @records
g.yield record
end
g.yield nil
end
end
end

contacts = Contacts.new
if not contacts.import_directory
abort 'Invalid Import'
end

while record = contacts.next_contact
puts record
end


I know that generators can be done in python and after a little searching I found this, which inspired me to play around and create an example class solution.
http://anthonylewis.com/2007/11/09/ruby-generators/

Thursday, July 30, 2009

Bits and Pieces

When I run the same section of code from the alpha site it only gets some of the members.
I'll have to see if the original script still works (a minor change to page formatting on lds.org can break it).

Tuesday, July 28, 2009

Moving Forward

I'm still learning Ruby on Rails. I'm happy with the progress that I'm making.
My current goal is that the site will be functional by the start of fall semester.
Being that LDS.org is down for maintenance (or just plain down), I'll resume work in the morning.