Automagic Retina-Ready Sprites with Compass and SASS

Sprites are a classic trick for speeding up a web page load time. Why make a request for dozens of dinky images when you can grab one sprite sheet and position it accordingly with CSS? Traditionally though, making sprites by hand is a tedious pain requiring Photoshop, pixel counting, and copying/pasting. Ew. And nowadays with all these higher resolution “retina” displays floating around, that means double the spriting is required.

Luckily for us Ruby users, the Compass CSS Framework gives us the ability to automatically generate sprite sheets in our Sprockets asset pipelines. But how can we handle retina versions without? Some Googling yielded this gist which I SASS-ized and modified slightly to handle retina background size calculations and devices with pixel ratios of 1.5. First, make sure compass and sass (or their -rails versions) are in your Gemfile, dump all your normal sprited images in one folder and @2x versions in another.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@import compass/utilities/sprites
@import compass/css3/background-size

$sprites: sprite-map("sprites/*.png")
$sprites2x: sprite-map("sprites2x/*.png")

=sprite-background($name)
  background-repeat: no-repeat
  display: block
  background-image: sprite-url($sprites)
  background-position: sprite-position($sprites, $name)
  height: image-height(sprite-file($sprites, $name))
  width: image-width(sprite-file($sprites, $name))

  @media (-webkit-min-device-pixel-ratio: 1.5), (-o-min-device-pixel-ratio: 3/2), (min-device-pixel-ratio: 1.5)
    background-image: sprite-url($sprites2x)
    background-position: 0 round(nth(sprite-position($sprites2x, $name), 2) / 2)
    height: round(image-height(sprite-file($sprites2x, $name)) / 2)
    width: round(image-width(sprite-file($sprites2x, $name)) /2 )

    /* treat the @2x retina sprite sheet as 50% wide for double resolution upon display
    +background-size(ceil(image-width(sprite-path($sprites2x)) / 2) auto)

/* example declaration
.help-icon
  +sprite-background(help-icon)

Just like that – sprites are compiled, dimensions and positioning are calculated with Ruby, and its all dumped to a CSS file with media queries for denser displays. Photoshop can stay closed and reserved for more exciting things like meme-ing images.

Leave a Comment

Transactionless Nested Ember Data Resources

Update 4/7/13

This article references a pre-release version of Ember.js. Now that Ember.js has reached a 1.0 API this article is basically no longer relevant.


Working with Ember and Ember Data during its real-time development have been exciting and frustrating times. Ember offers a powerful convention-driven approach to building robust apps on the client-side and Ember Data aims to simplify the the way your app’s data is retreived and stored. Powerful things get boiled down to simple functions like Store.commit() or App.Post.find(). Currently though, Ember Data is still in alpha and undergoing frequent API breaking changes with an emphasis on breaking.

In an Ember app I’m currently working on, I had the need to nest things within a parent object. For example: a campaign has one address (street name, city, zip, etc) and an array of links (each with a title and url). On the Rails REST API, I only needed one endpoint since I don’t need to query campaigns by links or addresses, so I am just serializing them to the campaign model instead of creating separate models for them.

1
2
3
4
5
class Campaign < ActiveRecord::Base
  ...
  serialize :links, Array
  serialize :location, Hash
end

Ember Data by default though, wants to save each resource type at its own API end-point and only save the ID references of the related objects within the parent object. To get around this in a previous revision of Ember Data, I could simply add the embedded option when defining a relationship and adding the associations option into the object’s toData function when sending the campaign back to the server.

1
2
3
4
5
6
7
8
App.Campaign = DS.Model.extend
  ...
  links: DS.hasMany('App.CampaignLink', { embedded: true })
  location: DS.hasMany('App.CampaignLocation', { embedded: true })
  toData: (options) ->
    options = options or {}
    options['associations'] = true
    @_super(options)

A recent code refactoring revision undid this functionality though, so I was forced to find another solution. I tried muddling around building an extended REST API adapter and serializer but I could never seem to break free of deeply engrained conventions of state and transaction management where Ember Data always seemed to care about campaign links and locations as their own object entities. Every time Store.commit() was called, a 404-ed POST request was sent to /campaign_links and /campaign_locations and I ended up with dirty transactions. Ew. All I wanted was for Ember Data to chill and not give these objects any special treatment as they are basically just array and object attributes of the Campaign. The workaround I created was to register transforms for attribute types called DS.attr("embeddedObject") and DS.attr("embeddedObjectArray").

embedded-transforms.coffee
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
stringToFunction = (str) ->
  arr = str.split(".")
  fn = window or @
  fn = fn[f] for f in arr
  throw new Error "function not found" if typeof fn is not "function"
  fn

createRecordFromData = (obj) ->
  if obj.type? and obj.type != "undefined"
    obj.transaction = null
    stringToFunction(obj.type).createRecord(obj)
  else
    obj

toDataWithType = (obj) ->
  if obj.constructor? and obj.constructor.name != "Object"
    o = obj.toData()
    o.type = obj.constructor.toString()
    o
  else
    obj

DS.Transforms.reopen

  embeddedObject:
    fromData: (serialized) ->
      if Em.none(serialized)
        null
      else
        createRecordFromData(serialized)
    toData: (deserialized) ->
      if Em.none(deserialized)
        null
      else
        toDataWithType(deserialized)

  embeddedObjectArray:
    fromData: (serialized) ->
      if Em.none(serialized)
        null
      else
        if Em.isArray(serialized) and serialized.length > 0
          createRecordFromData(obj) for obj in Em.makeArray serialized
        else
          []
    toData: (deserialized) ->
      if Em.none(deserialized)
        null
      else
        if Em.isArray(deserialized) and deserialized.length > 0
          toDataWithType(obj) for obj in Em.makeArray deserialized
        else
          []

In order to reap all the non-syncing benefits of DS.Model with these nested objects and arrays, I have to save the model class name locally to each nested object (i.e. type: "App.CampaignLink") before sending it to the server, so that when it is returned, I can instantiate it with createRecord via the nifty stringToFunction. If the embedded object(s) are plain old javascript objects, this class typecasting is bypassed and it works as well.

I’m sure there is room for improvement and perhaps this is an unideal solution, but it allows me to press forward with development and rely less on Ember Data’s changing conventions as a 1.0 release gets ironed out.

Leave a Comment

Giving Back

Way Mondo would not exist without the open source world. I will admit: I have never had proper programming or UI design education. As a high school student I naturally made my first website for my band at the time from instruction I pieced together from Yahoo! searches and threw it up on Angelfire.

My college education was at Ithaca College for film production, and I have no regrets about not pursuing it afterwards. Instead, I spent my joblessness traveling down the Internet’s free information rabbit hole. I started by learning HTML and CSS, then turned server-side to explore Ruby on Rails, then back to the browser with JavaScript and CoffeeScript, and recently became infatuated with Emacs.

Although it took quite some time for the deeper programming princles and best practices to settle into my ADD-riddled brain given my backwards education-less approach, I truly think the best relevant programming information is being given away for free by the open source community. When it comes to the web applications of today, there is simply too much complexity to write every aspect of code yourself, at least in a time-effective manner. Besides, “The Industry” and the demands of the user as well as the capabilities of the devices they use change everyday. The fact that I can throw my favorite ruby libraries in a Gemfile and run bundle install is invaluable to my capabilities. Each library of open source code also comes with the support of its community in the form of Github issues and pull requests, Google Groups, IRC, etc. With this blend of ‘information is power’ meets ‘freedom of information’, the open source hivemind is the place to be.

So that’s basically my short manifesto of why I want to start this blog: “long time fan, first time caller” kind of thing. I spend a chunk of basically everyday consuming open source code on Github, Stack Overflow, and all the development feeds in my Google Reader and its time for me to give back. Here I will share the projects I’m working on, little tricks and solutions I discover, and some of the amazing things the rest of you are giving out.

Leave a Comment