Apple Keynote for UX Design

I spent the first half of yesterday trying to design the user interface a little more using Apple Keynote. It’s…okay. You can form boxes and colors rather quickly with it. The animation sort of works but because you can’t get a good look at all the animation actions at once and everything happens on mouseclicks, it gets a little complicated. You’re trying tosimulate a fully functional web app on what is essentially power point. It doesn’t help that I’m not that big on mock-ups. I try to get an idea of what I want with the design then do all the nuancing when I’ve expressed everything in code. It’s probably for the best anyway. An app in the hand is worth two in demo, as they say.


The last half of yesterday was dedicated to transferring back to postgres. After an hour of wrestling with my local installation, I decided to use sqlite on development since postgres is already fully installed on heroku. There are about 24 failures left for me to deal with, most of them have to do with the specific way mongoid saves records.


Once that’s finished, I’ve compiled a list of features for me to implement. These are either features I’ve implemented before, like custom validations, or features that I’ve found railscast episodes to go with. I think the tough part will be creating tests for them in the first place. First of all we are going to make sure events can’t conflict, so you can’t create or accept an invite to an event when you’ve had a previous engagement on the system. Next we’re going to make it so only mutual friendships are allowed, and that you can only invite mutual friends to events. Then there’s the recover password system, beta invite system, community points, profile trust index, game persona system, messaging system, google calendar integration and event check-in system! Not to mention a whole bunch of mailers to make this all work.

 

Back to Postgres

I’ve got a simple mailer working for my app now. I feel like I’m almost getting to the point where I can start deploying, but I’m beginning to realize that I need to crystallize my direction a little here. I’ve got a minimal UI going with twitter bootstrap and some code from Mike Hartl’s railstutorial, but I think I need to figure out what I want from the end product a little better. Today I’m going to try and use Apple keynote and gimp to see if I can put together a couple of User Experience stories together. If they’re good enough, maybe I can cut them together for use on my launchrock page.

Once that’s done, I’ve decided to make another change in direction. I would like to switch back from MongoDB to Postgres. I know I put a lot of work into making this work with a document based database, but the more I look at the problem I’m trying to solve, the more I realize I need a relational database. How all these individual data rows relate to each other is more important that what is actually in them. This way I can re-establish has-many through relationships. There are a lot of cool things about MongoDB, but I don’t think I’ll be able to use them until the site starts getting large. It might be possible for me to use MongoDB for more high-volume functions later. So for now, I’ll be using a more classic use case for Rails. I won’t give myself more than a day or so to do it, otherwise I’ll be throwing away a perfectly functional system.

Onward and Upward

Well, we’re back on track! The join tables are back, the invites work, the friendship model works and all the tests are green!

It was a little difficult to get the invites model working again, mostly because mongoid does not have an equivalent of the Has_many_through relationship. You see, you can have an invites table that references both an event and a user, but if you want to get all the events related to a user, you can’t do that because mongoid does not support Join queries. However, you can query all the invites related to the user, and map all the eventids in those invites to a query on the events collection. So far, it seems to be emulating the has_many through relationship just fine.

Today we are going to do something a little differently with the Events model. Before I had the events belong to the users model. I had an issue when I wanted to query all of the Events a user had created and had been invited to. It had to be a join between all of the events that the User had created and all of the events related to the invites the user had been associated with. That seemed to be a sign of trouble for me, because again, Mongoid doesn’t do join queries.

So I figured, since I have the invites join table that I can add attributes to, why not set an invite’s status to organizer? That way I can get all the associated events from a user in a single query. If an event’s organizer drops out, the organizer status could revert to somebody else in the event.

But how do we create a record with a nested record already in there? Better yet, how do we test all this? I’m going to start off by using a hidden field in the Events form, and putting a :create validation in the Events Model. Once that gets done, I hope to get started on the mailers.

Back to Square One

It just goes to show you that you should never be clever with your programming unless you absolutely, positively have to. After I made my pull request to mongoid, I started to tackle the friendship system.

As you may recall I was going to have the User collection relate to itself through a Has and Belongs to Many relationship, one of those neato things that mongoid is capable of. I thought this would mean that there would be an array field on each user document containing his or her friends list. Every time you created a new friendship, both friends would have each others user id in their friend_ids field. You could use both users to reference each other, so you could figure out who they could message, see their profile, and invite to events.

I cribbed from the relationship model in Michael Hartl’s rails tutorial app. But you see, I was going to do something different! I wanted users to only have mutual friendships! I wanted to get rid of the join table so I could have so many less queries on my database.

I thought I already had it working through the tests on the user model. All I had to do was to get the friend and unfriendly buttons working in the view. I was a little unsure of this because the pattern I was using still assumed there was a join table.

So I put it out to The guys in the Fraser Valley Ruby brigade official irc channel. Brian Pearce opted to help me out. We got it to explicitly use the friendship controller to create the relationship.

When all that was done, strange things started to happen. User a would friend user b. User b would show up in user a’s friend list, but user b wouldn’t acknowledge user a as a friend. Just like highschool.

It turns out that habtm relationships have a lot of trouble when working with just one collection. It can’t figure out which direction the relationship is going! Again, just like highschool.

So, I need two arrays. One which describes who the user has friended, and another describing who has the user as a friend. I was working under this pattern before, so I restore it and try it out in the console. It works, but the records aren’t saving! I use the save! Method and find that it can’t save because the users password is missing. Crap!

That means if I’m going to use two arrays, I need to disable the password validation on the user model. But if I do that, a user could replace a password with an invalid value leaving the system completely exposed to hackers. How do I make this work without messing with the model attributes?

My friend Brian presented the solution: use a Join table. That way the direction of the relationships would be kept nice and explicit. Not only that, I could add attribute to the join table to track the status of the relationship, not unlike the status of the invites I was trying to make into a hash field not so long ago.

So after all that angst, I’m right back where I started: using join tables. I’ll have to use them for the invites too, otherwise I’m opening myself for another big mess. I shouldn’t have tried to mess with tried and true methods that would be all but invisible to the user! I should have kept that old relationship model and made it so that only mutual friendships are allowed! I need to save my brain power for when my users are writing gigabytes of data into my servers and producing usage patterns specific to my app! Until then, I need every shortcut, every precooked solution out there to get this app deployed!

One Line

One line. 15 hours and all that stood between me and functionality was one line. I’d be mad if that didn’t happen so often. So what did happen? I had the code for ActiveRecord’s Multiparameter attributes on one screen, and I had mongoids old Multiparameter extension on the other. My original plan was to put a variation of ActiveRecord’s code into a monkey patch and then put it into mongoid if it work. But as I read the mongoid code more carefully, I found that while it was a little harder to read, it was a little more concise than the activerecord code.

In activerecord, you called the assign_attribute method, then if you had a multiparameter attribute you called the assign_multi_parameter_attributes method, then you executed the callstack which extracted the callstack, and then you determined the time zone, intstantiated the objects…

In Mongoid, you made sure the keys and values went into the right place, determined the class of the attributes you were working with, “mongoize” it, and relegate the rest of the logic to the superclass. Since that was slightly less incomprehensible, I decided to take another look at the module.

The tests were failing because Rails did not recognize the permit= method. I didn’t know yet just what this was for, so I commented the line out to see what would happen. I still got a forbidden attribute error from activemodel, so I figured that it had something to do with the strong parameters method in the controller. I tried permitting the split time attributes every which way I could with single quotes, double quotes, no quotes, but I was still getting this error. I took another look at the line with the permit = method that I had commented out. I looked up the method and found out that it had been deprecated in favor of the permit! method. Not only that, but it was being used on a new attributes object that had been instantiated within the module. I changed permit = true to permit! and it worked!

Even though I just made I tiny modification to one of Mongoid’s deprecated module’s, I’m still going to try and put it back in through a pull request. Why? Just because that issue took up so much of my time doesn’t mean it has to ruin anyone else’s day. The gem shoudl work as advertised, or at least as documented! Also, I still don’t really know why there were so many issues with the module in the first place. This might be the best way to find out! And finally, hopefully, we can do something about those failing friendship tests today.