Categories: geek » development

RSS - Atom - Subscribe via email

Learning from CSS tweaking

| development, kaizen

My current project is so different from the others I’ve worked on. Instead of building logic, I’m doing front-end HTML/CSS/Javascript, working from Photoshop layers and design PDFs. I installed SASS so that I could gradually untangle the long strings of selectors my predecessor left me. Reading the code, I have a lot of sympathy for him. I imagine he felt like a fish out of water with both CSS and Drupal. I’m doing reasonably well. I’m not as fast as I am when working on Rails or Drupal back-ends, but I get stuff done. I don’t feel like I’m floundering in the land of “I don’t even know what I don’t know,” like it was when I was working with Microsoft SQL Server 2000 and IIS 5. This one I can handle. Who knows, we might even turn the project around.

I’m learning more about refactoring code, adding CSS3 styles, using Cufon for typography, and dealing with a large number of small changes. AutoHotkey scripts and Emacs macros have been amazing time-savers. On the AutoHotkey side, I’ve been taking advantage of tools like SmallMeasure and WhatColor.

Emacs makes editing lots of PHP files easy. The previous developer used dozens and dozens of node templates instead of using panels or block visibility, and there was a lot of copy-and-paste code. I started moving common parts to files that I could include, but I wanted to make sure that I didn’t accidentally overwrite something he had customized. I grepped the directory for the strings I was interested in. Then I used a keyboard macro to interactively go through each of the files and replace the common text. Win!

I’m looking forward to making my workflow even better. I’ve got a couple of weeks more in this project, and I might work on other CSS theming things in the future. (Good to encompass more of the development pipeline!) Here’s what I’m thinking of trying:

  • Use the monitor downstairs. Keep code on my laptop, and the browser/reference documents on the big screen.
  • Make my browser window translucent with WinWarden, and position it so that the browser window overlaps the reference. If I undock the Chrome developer console, that will make it even easier to work.
  • Deal with IE sooner rather than later, although IE 8 should be reasonably okay.

Looking forward to it. =)

Rails experiences: Things I learned from project O

| development, geek, kaizen, rails, work

Rails is awesome. We built a workflow/reporting system for ~120 users using Rails 3. My part of the project came to about 468 hours, or roughly 60 workdays (~ 3 months), and I worked with another developer who also put in around the same number of hours. We worked with a graphic designer, a CSS/HTML developer, a tester, a project manager, and the client, and we put together a surprisingly large set of functionality.

It’s amazing how quickly the site came together. I built a simple prototype to help the other developer get started with Rails, and we fleshed it out with the client’s input while waiting for the creative design. I started with web_app_theme so that we could have a decent-looking interface for starters. When the client approved the graphic design, another developer sliced it up into HTML, CSS, and images for us. I took those, converted them into HAML and ERB, and we were off and running. Every weekly sprint meant a chance to show off useful functionality and get feedback. It was awesome.

We were initially worried that building all the UIs from scratch in Ruby on Rails would mean taking up more time because we couldn’t use CCK or Views to quickly throw everything together. It turned out that HAML, partials, and semantic_form_for made the forms and reports easy to do. Filtering reports was straightforward with ActiveRecord and scopes. Because we built the screens ourselves, we didn’t have to fight with Views or CCK for the last 20% of tweaking, and I didn’t have to kludge any SQL queries (yay, no views_pre_execute!).

I was working on a Drupal project shortly before this, and I spent some time supporting the Drupal project during this one. Rails made my brain much happier. I felt that I could organize my code more cleanly, and I could test it more thoroughly, too. I didn’t have to fight so much with other people’s modules or themes. I like Drupal, and I’m still looking forward to doing more projects with it. But I wouldn’t mind working on more Rails projects, and I’m glad I’ve got Quantified Awesome as a personal project.

Drupal does many things better than Rails. Drupal modules tend to be more mature and better-documented, and it seems like there’s been more work on scaling Drupal. Internationalization is also more straightforward in Drupal, although Rails I18n is easy to use once you’ve gotten the hang of it. Drupal module dependencies seem a little easier to sort out, too. But Rails is fun!

Tests will keep you sane. This was the first project where we invested in developing a large suite of automated tests. We used Cucumber for high-level tests and RSpec for everything else. The tests caught many regression errors we might have otherwise missed. Test-driven development was fun, too, because the tests gave us tangible progress and simplified checking.

There were times when I gave in to the temptation to commit without running the tests, and I almost always regretted them. (Particularly after friendly finger-wagging from the other developer!)

Issue-tracking rocks. We’ve liked using Rational Team Concert in the past. Getting an externally-accessible instance was complicated, so I set up a Redmine issue tracker as soon as we started the project. We used Redmine to plan work, track bugs, and even collect feedback from the client. As of the time of writing, we had created 766 items and closed 683 of them (89%).

We started with story points, but didn’t end up continuing with them for the rest of the tasks. When we needed to prioritize, we estimated the hours required for each task in order to help the client decide. That worked out quite well. I haven’t tracked item-level time spent, but that seemed to be roughly around my estimates.

I now estimate more time than I used to, because I’ve started factoring in both writing and running tests. It’s a little strange being the pessimistic estimator instead of the optimistic one, but it’s good for the project.

Selenium is great for screenshots, too. Not only is Selenium good for automated browser-based testing of web pages, but it’s also a handy way to capture screenshots for documentation or demos.

Lotus Symphony and Microsoft Word don’t get along. We wasted a few hours trying to update the user guide with the new screenshots, only to find out that the PDF still got screwed up because I didn’t have the fonts the client used. Those fonts were part of Microsoft Office and weren’t on my system. The client took care of updating the user guide so that she could format it the way she wanted, and we focused on code.

Plan with the end in mind. Short projects mean that milestones can sneak up on you before you notice. Half-way through the project, we realized that the project end date was coming Really Soon Now, and we scrambled to put together a launch plan. We wanted to launch a few weeks before the end of the project, to give people time for feedback and updates. That meant that we needed to send pre-launch e-mails one week and two weeks before the launch, which meant… that we needed to start sending those e-mails within a week. Fortunately, the client, IBM PR, and everyone involved managed to get it all sorted out, and we launched.

I like launching. I would like to do more of it. We don’t do it nearly enough on short projects, but these projects are much more likely to launch if we’re around to help with the transition than if only the client is there.

On a related note, I get antsy about adding new functionality before the end of a project. This makes sense, of course. I don’t mind adding new reports and other reading-related functionality, but workflow tweaks are scary. When we’re planning future projects, we can consider similar risks for late-project tasks.

Things I want to improve for future projects:

  • More Rails! More!
  • More automated tests
  • Track time estimates and actual time in order to improve estimation accuracy
  • More launch planning
  • More blog posts about what I’m learning
  • Optimization

Things I want to reduce on future projects:

  • Avoid documentation formatting problems – I guess that means Microsoft Office when working with clients who use it
  • Must not give in to temptation to skip tests

Growing as a developer: Automated tests

Posted: - Modified: | development, kaizen, work

For this project, I put a lot of time into writing tests. Now they’re paying off. User acceptance testing and beta testing is going to be limited thanks to some real-world project constraints, so we’ll have to catch more bugs ourselves.

I want to get to 100% test coverage. That will go a long way to increasing our confidence in the results.

We had neglected our tests in the beginning, so the autogenerated tests fell out of sync. A few weeks ago, I put in around six hours to get everything back into shape.

Once that was sorted out, I looked for ways to encourage us to keep the tests working. After briefly experimenting with autotest, I settled for using git precommit hooks to force us to run tests before checking code in. Fortunately, the other developer agreed with this idea, so I set up the same script on her system.

Rspec was great for detailed tests, but I missed the expressiveness of Cucumber. After a few more hours of work, I got our Cucumber tests to work again. I wrote a number of tests from different users’ perspectives, then added Cucumber to the pre-commit hook.

The next step in our testing journey was to set up code coverage analysis. It took a little hacking to get rcov to play nicely with cucumber and rspec, but I got that to work too. Now we can get a sense of what still needs to be tested, and we have clear goals: 100% of code covered by tests, and 100% of user stories matched up with tests too.

On another project, I’ve been working on automated tests with Selenium. I hate letting the clients run into bugs, and I hate breaking things I thought I’d already fixed. I also hate clicking around and doing repetitive actions. As we run into things I’ve broken and as I change things, I write tests.

For me, test-driven development is incredibly motivating: the carrot of seeing tests pass and coverage stats go up, the stick of embarrassment when things break. =) I can take advantage of that energy and change my development habits.

My next development goals related to testing:

  • behaviour-driven development and business-user-compatible test cases
  • 100% code coverage from day one
  • integrating Selenium tests into our build scripts
  • Selenium web driver screenshots to assist in keeping user documentation up to date
  • writing automated tests for life – making progress!

Quantified Awesome: Thinking about what I want to build next

| development, geek, quantified

One of the best ways to make the most of those little pockets of time is to keep a someday list. What does my someday list for Quantified Awesome look like? What do I want to build next?

Emacs interface, so that I can add data and synchronize my Org-mode files: I’m split between Emacs and the Web right now. I love the customizability of Emacs, but I like the way my web app lets me easily share information. Solution: Build an API for Quantified Awesome, then integrate Emacs with it!

Android app: I’ll probably start with simple reports using the data exported from Tap Log Records, and maybe a synchronization tool that reuses the REST API I’ll be developing for Emacs integration anyway. After that, I can build my own tracking tools, which would make updating things like stuff locations much easier.

Shared memories: One of my projects is to collect and share family memories. It would be wonderful to organize these into a rough timeline and allow other people like my mom to add corrections or stories of their own. Maybe it will grow into a collaborative memory system someday!

100% test coverage: It’s my own project, so I might as well.

More graphs and statistics: Because slicing and visualizing data is fun!

Integrate my financial data, perhaps? Good to have my envelope-tracking system reflected in my personal dashboard, and maybe I can cross-reference this with time and feelings.

*Integrate automated tests for life.* Write more tests.

Build a web interface for tracking time, etc. That way, I can quickly jump over and enter a note.

Integrate my data from ThinkUp: I’ve been using ThinkUp to archive and analyze my tweets. Maybe I can overlay tweets onto my timeline and convert my current TapLogRecordsController into something more generic.

Learn how to use Mongo: Mongo’s freeform storage might make it much easier for me to stash all sorts of data in a rough timeline. Exploration needed!

So many good things to build. =)

Related:

Geek tidbits: Postfix configuration for development and testing

| development, geek, ibm, rails, work

From November:

We got the mail information for our Rails site, so I reconfigured the mail server. We’re doing a lot of development, and testing mail is much easier when you can send example mail addresses to one bucket of mail. Here’s how I set up the server to redirect everything addressed to @example.org to a test mail address.

First, I set the mail server up for local delivery only, and I confirmed that I could send mail from a local user to another local user account. Then I experimented with rewriting, using virtual_alias_maps to rewrite addresses to a specific account. I confirmed that worked. Then I checked the database to make sure we didn’t have live e-mail addresses, reconfigured the mail server to deliver Internet mail, and crossed my fingers. A few quick tests showed that mail was being delivered as planned – example.org mail routed to our temporary address, everything else delivered normally. We’ll see how this works!

Here’s our /etc/postfix/main.cf

smtpd_banner = $myhostname ESMTP $mail_name
biff = no
append_dot_mydomain = no
readme_directory = no
smtpd_tls_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem
smtpd_tls_key_file=/etc/ssl/private/ssl-cert-snakeoil.key
smtpd_use_tls=yes
smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
myhostname = domain.goes.here
alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases
myorigin = /etc/mailname
mydestination = domains.go.here, example.org
relayhost =
mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128
mailbox_size_limit = 0
recipient_delimiter = +
inet_interfaces = all
default_transport = smtp
relay_transport = smtp
virtual_alias_maps = hash:/etc/postfix/virtual
inet_protocols = ipv4

And /etc/postfix/virtual:

@example.org change_to_real_email@example.com

Don’t forget to run postmap /etc/postfix/virtual; /etc/init.d/postfix reload after changing /etc/postfix/virtual and your configuration.

Rails experiences: Building an interactive tutorial

| development, geek, rails, work

One of the risks for this Rails project that I’m working on is that new users won’t have enough ramp-up time before we finish the project. We’re planning to wrap up in December, which is the end-users’ busiest time of year. The project also highly depends on external factors we can’t control, so it might be weeks or even a few months before people get a chance to try the most important parts of the application.

To make it easier for people to get started, we decided to build an interactive tutorial into the system. When people log in, the tutorial should create an offer that they can respond to, walk them through the process of working with it, and then tell them about the next steps they can take. People should be able to stop the tutorial at any time, and they should be able to start the tutorial from the beginning whenever they want a refresher.

People will be on a system that other people use and that generates reports, so all the tutorial information needs to be hidden from reporting and from people who are not in tutorial mode.

I started off by writing a Cucumber test that described how things should work: what people should see, what they could do, and so on.

To keep all the tutorial-related methods in one place, I put them in a file called tutorial_methods.rb and I included these methods in my controller. I added a conditional div to my application.html.erb that displayed the tutorial in a consistent spot if a tutorial was specified for the current page. Then I defined a function that took the current page and figured out what needed to be done for a tutorial. This function created a sample offer at the beginning of a tutorial, performed the behind-the-scenes work to approve the offer once people finished the first step, and loaded the tutorial text from the localization file into an instance variable.

I decided to use Rails’ built-in internationalization support instead of putting the tutorial in the database so that it could easily support multiple languages, although I might use a gem to support internationalization of database values if we need to.

To make things easier on the reporting side, I extended ActiveRecord::Base with my own association methods that filtered the queries depending on whether or not the user was in tutorial mode. These custom association methods made it much easier to make sure all the relevant queries were filtered.

I really liked adding an interactive tutorial to this project, and I think I’ll use that technique for Quantified Awesome as well. Online help is good, but it’s even better if people can practice on something and know it won’t mess up anything else.

Ruby on Rails: Extending ActiveRecord::Base to define your own ActiveRecord association methods

Posted: - Modified: | development, geek, rails, work

One of the things I really like about Rails is the ability to add to existing classes so that your code can be cleaner. For example, in the app we’re working on, I need to be able to display a list of offers associated with an organization. I also need to filter that list of offers by different criteria. If the user is not in tutorial mode, I need to filter out any tutorial-related offers. I want to show offers with different workflow states, too.

If I had to do this in straight SQL, I would need to write many queries to cover the different cases, or write my own query-building engine that takes conditions into account. In the Drupal world, I might try to build a View with lots of arguments, and then use a views_pre_execute hook to monkey around with the generated SQL.

In the Rails world, things are much simpler. I started off by chaining queries, because you can add conditions to the end of an ActiveRelation and go from there. That gave me code that looked like this:

base = Offer.includes(:donation).where("organization_id = ? AND (donations.deadline IS null OR donations.deadline >= ?) AND (NOT (offers.workflow_state IN (?, ?, ?)))", @organization.id, Time.now, Offer::DRAFT, Offer::ALLOCATED, Offer::CONFIRMED).order('offers.deadline')
@direct_offers = base.where("offers.workflow = ?", Donation::DIRECT)
@open_offers = base.where("offers.workflow = ?", Donation::OPEN)

Then I asked myself: How can I make this code even cleaner? I thought about adding instance methods. For example, in my Organization class, I could define the following:

class Organization
  # Other stuff goes here
  def current_offers
    self.offers.includes(:donation).where("(donations.deadline IS null OR donations.deadline >= ?) AND (NOT (offers.workflow_state IN (?, ?, ?)))", Time.now, Offer::DRAFT, Offer::ALLOCATED, Offer::CONFIRMED).order('offers.deadline')
  end
  def current_offers_by_workflow(workflow)
    self.current_offers.where("offers.workflow = ?", Donation::OPEN)
  end
end

That would allow me to replace the code above with something like this:

@direct_offers = @organization.current_offers_by_workflow(Donation::DIRECT)
@open_offers = @organization.current_offers_by_workflow(Donation::OPEN)

… so if I wanted to filter out tutorial entries, I could do that in def current_offers by adding a where clause for the tutorial column.

But it seemed clunky to have to specify all these instance methods in order to filter by different ways. What I really wanted was to be able to chain my custom filters together, so that I could write code like this:

@direct_offers = @organization.offers.filter(current_user).direct
@open_offers = @organization.offers.filter(current_user).open

and then eventually be able to do things like:

list = @organization.offers.filter(current_user).current.direct.allocated

(If I really wanted to.)

I couldn’t figure out where to add the methods so that they’d be defined in the right place. If I added the methods to the Organization class, they couldn’t be called on the ActiveRecord relations. A little bit of searching, and I figured out how to do it in Rails. It turns out that you can extend ActiveRecord relations with your own methods! Here’s how.

You’ll need to extend ActiveRecord::Base with your own methods. I put this in config/initializers/activerecord_extensions.rb.

module ProjectNameActiveRecordExtensions
  def filter(control)
    exclude_tutorial = true
    # Include the tutorial offers for users in tutorial mode
    if control.is_a? User and control.tutorial
      exclude_tutorial = false
    # You can also pass filter(false) to turn off these filters for testing
    elsif !control 
      exclude_tutorial = false
    end    
    if exclude_tutorial
      scoped.joins(:donation).where('donations.tutorial=?', false)
    else
      scoped
    end
  end
  # other methods go here...
end
ActiveRecord::Base.extend ProjectNameActiveRecordExtensions

The trickiest part was figuring out how to do a conditional filter, and that’s what scoped is for. I wanted to include the tutorials if the user was in tutorial mode, so my function should be a pass-through in that case. I couldn’t return self or nil, because that broke the associations. scoped turned out to be the magic keyword that refers to the current scope of the query.

What if you want to use the same words in different contexts? For example, “pending” might need to result in two different queries depending on whether you’re asking for pending offers or pending requests. ActiveRecord::Base is used for all classes, but you can use self to find out what class is being used for scoping. For example:

def pending
  if self == StandingRequest
    scoped.where("standing_requests.workflow_state=?", StandingRequest::PENDING)
  else
    # Replace with other cases as I find the need for them
    raise "Undefined behaviour"
  end
end

I love the fact that Rails lets you modify so much in order to make building sites easier. It’s like Emacs for the Web, and it makes my brain happy.