A mean bug made it into our production environment. It wasn’t caught by our extensive test suite and caused thousands of emails to be sent to a handful of people. The root cause was an unfortunate combination of Devise, DelayedJob and, of course, our own code. It was an easy fix, but nobody ever wants this to happen again.

tl;dr DelayedJob says it’s possible to set Delayed::Worker.delay_jobs = false for your tests. Don’t do it.

Read on →

With the release of Xcode 4.4 I’ve taken a look back at our existing code standards and tried to come up with something that is cleaner and more elegant. Here are a few of the ideas I’ve been using to modernize the codebase.

Remove private method declarations and use class extensions to add ivars.

First to get chopped by the deletion button are private method declarations. After Xcode 4.2 came out we took to using the class extension feature to add private method declarations at the top of implementation files. This was a nice way of keeping private methods out of the header files. Now that the compiler will check for pre-existing method signatures within the same object there’s no need to define their interfaces.

Occasionally it’s necessary for subclass to know about private methods defined by its superclass, so we use a shared category to let them know what they respond to. Like Apple, we also quit using @private in header files.

Ivars now should go in class extensions, and be prefixed by an underscore. Apple advises that you don’t use method names with underscores but encourage underscored variable names. This also can free up method parameters from having ugly names such as anArtwork or aString.

Use object literals when possible.

Object literals are ways of adding syntacitcal sugar to the Objective-C language, they let you access keys and values easily on NSDictionarys and objects in NSArrays. There’s no reason to not be using them if you’re supporting iOS 4 and above. It’s simple a matter of _artworks[2] vs [_artworks objectAtIndex:2].

Dot syntax is OK for non-properties.

OK so, I admit it. I whined when properties came out. It was back in 2007 and the Objective-C was ranked 40th in the world, it’s now ranked 3nd most popular programming language. Within timeframe, my opinion on the subject of properties changed also.

Originally when properties came out they exclusively were given the right to use dot notation on objects. This makes sense as they were created to provide public access to ivars which normally you can only access internally using the dot notation. With Xcode 4.3, that also changed. Now, if a method doesn’t have any arguments it can be called using dot notation. I’m in favour of using this. For me a good rule of thumb has been if a method returns something, dot notation is OK. For example, _artworksArray.count is fine whilst _parsingOperation.stop isn’t.

Keep external code out of your project.

External, or vendored code should be kept out of the main body of your code. You can use CocoaPods to keep all that code in check and up-to-date. CocoaPods is a project that aims to be what bundler does for ruby projects, or npm for node. It will deal with your dependancies whilst you can concentrate on your own code. It will create a seperate Xcode project that handles all you dependancies leaving your project only as your own code.

Use umbrella imports.

To try and keep the amount of noise we have at the top of our implementation files we have started to reduce the number of #import "ARModel.h" lines we use. By creating a Models.h file and having that include all the models it means we can still have a look through the #imports at the top to get an idea of the connections between the objects as that will only show the important imports. These can optionally be moved into your precompiled header files.

Keep your code clean.

Whitespace can and does slowly accumulate at the line-endings of your code. You should make sure that the new preference for automatically trimming whitespace is turned on in the text editing section of Xcode’s preferences.

IBOutlets should probably go in your class extensions.

With modern versions of Xcode, it doesn’t matter that your IBOutlets are defined in places other than in headers. As Objective-C developers, we’ve come a long way from having to repeatedly drag a .h from Xcode to Interface Builder, so maybe it’s time to rethink the idea that our interface outlets should be publicly accessible. My opinion is that a controller’s views are for the most part a private affair and that if you want to expose their functionality you do it through custom methods in the controller. There are some downsides to this, in that initially you have to change to the implementation file when using hybrid view when connecting them.

These decisions have come from internal discussions and from watching many WWDC sessions on the topic. We highly recommend watching the following WWDC sessions.

WWDC 2011: 105 Polishing Your App, 112 Writing Easy To Change Code and 322 - Objective-C Advancements in Depth.

WWDC 2012: 405 Modern Objective-C and 413 Migrating to Modern Objective-C

The Artsy team faithfully uses Jenkins for continuous integration. As we’ve described before, our Jenkins controller and 8 agents run on Linode. This arrangement has at least a few drawbacks:

  • Our Linode servers are manually configured. They require frequent maintenance, and inconsistencies lead to surprising build failures.
  • The fixed set of agents don’t match the pattern of our build jobs: jobs get backed up during the day, but servers are mostly unused overnight and on weekends.

The Amazon EC2 Plugin allowed us to replace those agents with a totally scripted environment. Now, agents are spun up in the cloud whenever build jobs need them.

Read on →

Empathy with end users is critical when developing consumer-facing software. Many go even further and argue that you should be your own user to effectively deliver the best experience.

I’d encourage anyone starting a startup to become one of its users, however unnatural it seems.

— Paul Graham Organic Startup Ideas

In practice, though, this can be difficult:

  • As a developer, you’re just not representative of the intended audience.
  • You’re [appropriately] focused on the product’s next iteration, while your audience is occupied with the current state.
  • You spend countless hours focused on product details—of course it’s a challenge to empathize with a casual visitor’s first impression.

Keeping it Real

We’ve tried some best practices to overcome these tendencies. User feedback is emailed to everyone in the company. Engineers share customer support responsibilities. But one simple tool has been surprisingly useful: we stole a page from the agile development handbook and built an information radiator. Like a kanban board, news ticker, or analytics wall board, our information radiator gives us an ambient awareness of end users’ experiences. How?

Read on →

The only constant is change, continuing change, inevitable change, that is the dominant factor in society [and web apps!] today. No sensible decision can be made any longer without taking into account not only the world as it is, but the world as it will be.

– Isaac Asimov

R.I.P #!

It did not take us long to discover we shared the concerns of Twitter’s Dan Webb on hashbang routes, but it was almost a year before we were able to remove them from Artsy. Here’s how it went down.

Artsy relies on the Backbone.js framework for our client application which offers a solid pushState routing scheme. This includes a seamless hashtag fallback for browsers that don’t support the HTML5 History API (looking at you IE 9).

The pushState routing is optional, but “the world as it should be” suggests we say “Yes!” (or true) to pushState.

Backbone.history.start({ pushState: true })

Read on →

Implementing server-side RESTful API caching is hard. In a straightforward API all the expiry decisions can be made automatically based on the URL, but most real world APIs that add requirements around object relationships or user authorization make caching particularly challenging.

At GoRuCo we open-sourced Garner, a cache implementation of the concepts described in this post. To “garner” means to gather data from various sources and to make it readily available in one place, kind-of like a cache! Garner works today with the Grape API framework and the Mongoid ODM. We encourage you to fork the project, extend our library to other systems and contribute your code back, if you find it useful.

Garner implements the Artsy API caching cookbook that has been tried by fire in production.

Read on →

The Jenkins CI project has grown tremendously in the past few months. There’re now hundreds of plugins and an amazing engaged community. Artsy is a happy user and proud contributor to this effort with the essential jenkins-ansicolor plugin, eliminating boring console output since 2011.

We are a continuous integration, deployment and devops shop and have been using Jenkins for over a year now. We’ve shared our experience at the Jenkins User Conference 2012 in a presentation. This blog post is an overview of how to get started with Jenkins for Ruby(-on-Rails) teams.

/images/2012-05-27-using-jenkins-for-ruby-on-rails-teams/jenkins.png

Read on →

E-mail is one of the most important ways to engage your users. And every time you touch a user’s inbox, it reflects on your brand. But getting email right has become increasing difficult due to the complexities introduced by the thousands of web-based, desktop and mobile mail clients. Email formatting is like the “Hunger Games” where the major players include online services such as GMail, Yahoo, Hotmail or AOL, desktop clients such as Outlook and a myriad mobile devices ranging from iPhone and Android to Blackberry.

To deal with this landscape, the MIME standard allows systems to send e-mail with multiple parts: plain/text for business-efficient devices such as the Blackberry, and text/html for web-based e-mail readers, such as GMail. Furthermore, ActionMailer supports multiple template formats: create an .html.haml template along with a .txt.haml template to generate both. We also know that text/plain email helps deliverability, but we believe a disproportionately small amount of text e-mails are actually read - the vast majority of devices are capable of parsing some HTML.

Is it possible to avoid having to maintain two separate templates without sacrificing deliverability? How can we inject a text/plain part into HTML e-mail that is both useful and “free”?

ActionMailer::Base defines an internal method called collect_responses_and_parts_order (#ref), which iterates over templates and renders them. Let’s override that method and examine the contents of the generated parts.

def collect_responses_and_parts_order(headers)
    responses, parts_order = super(headers)
    [responses, parts_order]
end

Each response is a MIME part with its boundary and the parts_order is the order in which the parts appear in the final e-mail. The MIME RFC 1341 says that the parts must be generated in the increasing order of preference, ie. text/html content-type part last, provided you want it to be the preferred format of your email.

We can find whether the generated e-mail contains a plain/text part and otherwise generate one.

html_part = responses.detect { |response| response[:content_type] == "text/html" }
text_part = responses.detect { |response| response[:content_type] == "text/plain" }
if html_part && ! text_part
  # generate a text/plain part
end

Generating the text part means stripping all HTML with links preserved. Nokogiri has a very convenient deep traverse iterator.

body_parts = []
Nokogiri::HTML(html_part[:body]).traverse do |node|
  if node.text? and ! (content = node.content ? node.content.strip : nil).blank?
    body_parts << content
  elsif node.name == "a" && (href = node.attr("href")) && href.match(/^https?:/)
    body_parts << href
  end
end

Once we have all the parts, assemble them, get rid of duplicate text and links, and re-insert into the email as a text/plain multipart block.

responses.insert 0, {
  content_type: "text/plain",
  body: body_parts.uniq.join("\n")
}
parts_order.insert 0, "text/plain"

This has been extracted into the actionmailer-text gem. Include ActionMailer::Text in your mailers.

Having over three thousand RSpec tests in a single project has become difficult to manage. We chose to organize these into suites, somewhat mimicking our directory structure. And while we succeeded at making our Capybara integration tests more reliable (see Reliably Testing Asynchronous UI with RSpec and Capybara), they continue relying on finicky timeouts. To avoid too many false positives we’ve put together a system to retry failed tests. We know that a spec that fails twice in a row is definitely not a fluke!

Create a new Rake file in lib/tasks/test_suites.rake and declare an array of test suites.

``` ruby lib/tasks/test_suites.rake SPEC_SUITES = [ { :id => :models, :pattern => “spec/models//_spec.rb” }, { :id => :controllers, :pattern => “spec/controllers/**/_spec.rb” }, { :id => :views, :pattern => “spec/views//*_spec.rb” } ]

<!-- more -->
`RSpec::Core` contains a module called `RakeTask` that will programmatically create Rake tasks for you.

``` ruby lib/tasks/test_suites.rake
require 'rspec/core/rake_task'

namespace :test
  namespace :suite
    RSpec::Core::RakeTask.new("#{suite[:id]}:run") do |t|
      t.pattern = suite[:pattern]
      t.verbose = false
      t.fail_on_error = false
    end
  end
end

Run rake -T to ensure that the suites have been generated. To execute a suite, run rake test:suite:models:run. Having a test suite will help you separate spec failures and enables other organizations than by directory, potentially allowing you to tag tests across multiple suites.

rake spec:suite:models:run
rake spec:suite:controllers:run
rake spec:suite:views:run

Retrying failed specs has been a long requested feature in RSpec (see #456). A viable approach has been finally implemented by Matt Mitchell in #596. There’re a few issues with that pull request, but two pieces have already been merged that make retrying specs feasible outside of RSpec.

  • #610: A fix for incorrect parsing input files specified via -O.
  • #614: A fix for making the -e option cumulative, so that one can pass multiple example names to run.

Both will appear in the 2.11.0 version of RSpec, in the meantime you have to point your rspec-core dependency to the latest version on Github.

``` ruby Gemfile “rspec-core”, :git => “https://github.com/rspec/rspec-core.git”


Don't forget to run `bundle update rspec-core`.

The strategy to retry failed specs is to output a file that contains a list of failed ones and to feed that file back to RSpec. The former can be accomplished with a custom logger. Create `spec/support/formatters/failures_formatter.rb`.

``` ruby spec/support/formatters/failures_formatter.rb
require 'rspec/core/formatters/base_formatter'

module RSpec
  module Core
    module Formatters
      class FailuresFormatter < BaseFormatter

        # create a file called rspec.failures with a list of failed examples
        def dump_failures
          return if failed_examples.empty?
          f = File.new("rspec.failures", "w+")
          failed_examples.each do |example|
            f.puts retry_command(example)
          end
          f.close
        end

        def retry_command(example)
          example_name = example.full_description.gsub("\"", "\\\"")
          "-e \"#{example_name}\""
        end

      end
    end
  end
end

In order to use the formatter, we must tell RSpec to require it with --require and to use it with --format. We don’t want to lose our settings in .rspec either - all these options can be combined in the Rake task.

``` ruby lib/tasks/test_suites.rake RSpec::Core::RakeTask.new(“#{suite[:id]}:run”) do |t| t.pattern = suite[:pattern] t.verbose = false t.fail_on_error = false t.spec_opts = [ “–require”, “#{Rails.root}/spec/support/formatters/failures_formatter.rb”, “–format”, “RSpec::Core::Formatters::FailuresFormatter”, File.read(File.join(Rails.root, “.rspec”)).split(/\n+/).map { |l| l.shellsplit } ].flatten end


Once a file is generated, we can feed it back to RSpec in another task, called `suite:suite[:id]:retry`.

``` ruby lib/tasks/test_suites.rake
RSpec::Core::RakeTask.new("#{suite[:id]}:retry") do |t|
  t.pattern = suite[:pattern]
  t.verbose = false
  t.fail_on_error = false
  t.spec_opts = [
    "-O", File.join(Rails.root, 'rspec.failures'),
    File.read(File.join(Rails.root, '.rspec')).split(/\n+/).map { |l| l.shellsplit }
  ].flatten
end

Finally, lets combine the two tasks and invoke retry when the run task fails.

ruby lib/tasks/test_suites.rake task "#{suite[:id]}" do rspec_failures = File.join(Rails.root, 'rspec.failures') FileUtils.rm_f rspec_failures Rake::Task["spec:suite:#{suite[:id]}:run"].execute unless $?.success? puts "[#{Time.now}] Failed, retrying #{File.read(rspec_failures).split(/\n+/).count} failure(s) in spec:suite:#{suite[:id]} ..." Rake::Task["spec:suite:#{suite[:id]}:retry"].execute end end

A complete version of our test_suites.rake, including a spec:suite:all task that executes all specs can be found in this gist. Our Jenkins CI runs rake spec:suite:all, with a much improved weather report since we started using this system.

We make Folio, a pretty kick-ass iPad app that we give away to our partners to showcase their inventory at art fairs. Whilst making it we tried to ensure that all of the application fits in with the Artsy website aesthetic, and recently the last natively styled control fell to our mighty code hammers. That was the UISearchBar.

Screenshot of Artsy Folio

When displaying only search results in a table it makes a lot of sense to use Apple’s UISearchDisplayController as it handles a lot of edge cases for you. However the downside is that you lose some control over how the views interact.

The search bar was the only native control that actually made it into the version 1 release. This was mainly due to it requiring a bit of black magic in order to get it to work the way we wanted. So lets go through the code and rip it to pieces.

First up, you’re going to want to make yourself a subclass of the UISearchBar, I’m going to be calling ours ARSearchBar. Here’s our public header.

@interface ARSearchBar : UISearchBar

// Called from The SearchDisplayController Delegate
- (void)showCancelButton:(BOOL)show;
- (void)cancelSearchField;
@end

Inside the implementation file we declare private instance variables for keeping track of the textfield and the Cancel button. This is so we can avoid finding them in the view hierarchy when we want to change the frame it during resizing.

@interface ARSearchBar (){
    UITextField *foundSearchTextField;
    UIButton *overlayCancelButton;
}

So, to look at setting the size we’ve found it easiest to deal with that in an overrode setFrame and setting the height of the new frame before it goes to the super class. As the search bar doesn’t change its height between state changes like text insertion it shouldn’t pose a problem to have it hardcoded.

- (void)setFrame:(CGRect)frame {
    frame.size.height = ARSearchBarHeight;
    [super setFrame:frame];
}

What does pose a problem though is making sure that the subviews inside the search bar are positioned correctly with respect to the new height, this is amended in layoutSubviews. In our case the textfield should take up almost all of the search bar.

- (void)layoutSubviews {
    [super layoutSubviews];

    // resize textfield
    CGRect frame = foundSearchTextField.frame;
    frame.size.height = ViewHeight;
    frame.origin.y = ViewMargin;
    frame.origin.x = ViewMargin;
    frame.size.width -= ViewMargin / 2;
    foundSearchTextField.frame = frame;
}

Next up is that we can’t access our foundSearchField because it’s not been found yet! Personally, I’m a big fan of using nibs for everything ( and pretty pumped about Storyboards too ) so we do our searching in awakeFromNib .

- (void)awakeFromNib {
    [super awakeFromNib];

    // find textfield in subviews
    for (int i = [self.subviews count] - 1; i >= 0; i--) {
        UIView *subview = [self.subviews objectAtIndex:i];                
        if ([subview.class isSubclassOfClass:[UITextField class]]) {
            foundSearchTextField = (UITextField *)subview;
        }
    }
}

This gives us a textfield, next up we want to stylize it. The perfect place for this is just after finding the textfield that you use to search in.

- (void)stylizeSearchTextField {
    // Sets the background to a static black by removing the gradient view
    for (int i = [self.subviews count] - 1; i >= 0; i--) {
        UIView *subview = [self.subviews objectAtIndex:i];                

        // This is the gradient behind the textfield
        if ([subview.description hasPrefix:@"<UISearchBarBackground"]) {
            [subview removeFromSuperview];
        }
    }

    // now change the search textfield itself
    foundSearchTextField.borderStyle = UITextBorderStyleNone;
    foundSearchTextField.backgroundColor = [UIColor whiteColor];
    foundSearchTextField.background = nil;
    foundSearchTextField.text = @"";
    foundSearchTextField.clearButtonMode = UITextFieldViewModeNever;
    foundSearchTextField.leftView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, TextfieldLeftMargin, 0)];
    foundSearchTextField.placeholder = @"";
    foundSearchTextField.font = [UIFont serifFontWithSize:ARFontSansLarge];
}

You might be wondering why we removed the placeholder text? We needed more control over the style and positioning of the placeholder text and the search icon. These are easily controlled by the UISearchDisplayController subclass rather than inside the custom search bar. This is also the place that we can deal with having our custom Cancel button.

- (void) searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller {
    [searchBar showCancelButton:YES];
    [UIView animateWithDuration:0.2 animations:^{
        searchPlaceholderLabel.alpha = 0;
    }];
}

- (void) searchDisplayControllerWillEndSearch:(UISearchDisplayController *)controller {
    [searchBar showCancelButton:NO];
    [UIView animateWithDuration:0.2 animations:^{
        searchPlaceholderLabel.alpha = 1;
    }];
}

The corresponding code for showing and hiding the Cancel button is here. We just animate it in and out by a distance of 80.

- (void)showCancelButton:(BOOL)show {
    CGFloat distance = show? -CancelAnimationDistance : CancelAnimationDistance;
    [UIView animateWithDuration:0.25 animations:^{
        overlayCancelButton.frame = CGRectOffset(overlayCancelButton.frame, distance, 0);
    }];
}

The original Cancel button is something that we choose to keep around, rather than removing it form the view hierarchy, that’s so we can have our overlay Cancel button call its method instead of trying to replicate the cancel functionality ourselves.

To keep track of the Cancel button we need to know when its meant to appear, and when its meant to disappear. Because the Cancel button is created at runtime every time a search is started we need to know when thats happening so we can hide it, we can do that by registering for UITextFieldTextDidBeginEditingNotification on the textfield once it’s been found. We do this in awakeFromNib.

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(removeOriginalCancel) name:UITextFieldTextDidBeginEditingNotification object:foundSearchTextField];


- (void)removeOriginalCancel {
    // remove the original button
    for (int i = [self.subviews count] - 1; i >= 0; i--) {
        UIView *subview = [self.subviews objectAtIndex:i];                
        if ([subview.class isSubclassOfClass:[UIButton class]]) {
        	// This is called every time a search is began,
        	// so make sure to get the right button!
            if (subview.frame.size.height != ViewHeight) {
                subview.hidden = YES;
            }
        }
    }
}

Finally we have the styling of the button. I’ve summed it up here as a lot of it is very application specific.

- (void)createButton {
    ARFlatButton *cancelButton = [ARFlatButton buttonWithType:UIButtonTypeCustom];
    [[cancelButton titleLabel] setFont:[UIFont sansSerifFontWithSize:ARFontSansSmall]];

    NSString *title = [@"Cancel" uppercaseString];
    [cancelButton setTitle:title forState:UIControlStateNormal];
    [cancelButton setTitle:title forState:UIControlStateHighlighted];

    CGRect buttonFrame = cancelButton.frame;
    buttonFrame.origin.y = ViewMargin;
    buttonFrame.size.height = ViewHeight;
    buttonFrame.size.width = 66;
    buttonFrame.origin.x = self.frame.size.width - buttonFrame.size.width - ViewMargin + CancelAnimationDistance;
    cancelButton.frame = buttonFrame;
    [cancelButton addTarget:self action:@selector(cancelSearchField) forControlEvents:UIControlEventTouchUpInside];

    overlayCancelButton = cancelButton;
    [self addSubview:overlayCancelButton];
    [self bringSubviewToFront:overlayCancelButton];
}

- (void)cancelSearchField {
    // tap the original button!
    for (int i = [self.subviews count] - 1; i >= 0; i--) {
        UIView *subview = [self.subviews objectAtIndex:i];                
        if ([subview.class isSubclassOfClass:[UIButton class]]) {
            if (subview.frame.size.height != ViewHeight) {
                UIButton *realCancel = (UIButton *)subview;
                [realCancel sendActionsForControlEvents: UIControlEventTouchUpInside];
            }
        }
    }    
}

The complete code is available as a gist under the MIT license.