Over time, a developer will reach a point where he or she will need to rethink the time-consuming slow zones in the development process. Often these slow zones are aspects that are overlooked—maybe it is something the developer doesn't enjoy doing each and every time. But with each and every time comes a chance to experiment in the hopes of turning a rocky road into a fast lane.
CSS was once that rocky road for me. Writing line after line, refreshing, and testing just took up a lot of time. As my development skills grew, so did the way I write CSS. As a Rails developer, it is tempting to use tools, Ruby gems or other dynamic alternatives to generate CSS in an unconventional way. Before considering dynamic alternatives, we should first take a second look at our CSS file and see how we can improve.
Lets say for the header we have the following:
div#header {
width: 800px;
background: url(/images/header.jpg) no-repeat;
}
div#header h1 {
font-size: 24px;
font-family: Arial, sans;
color: #333333;
}
div#header ul#nav {
width: 800px;
}
div#header ul#nav li {
padding: 3px 5px;
border: 1px #6699FF solid;
}
div#header ul#nav li a {
color: #000033;
text-decoration: none;
}
div#header ul#nav li a:hover {
text-decoration: underline;
}
Nothing special here; just basic CSS. This way of writing CSS is common, as it is found in most textbooks and tutorials. We start with a selector, then a few style declarations for the element specified in the selector. I had written CSS this way for years, until my stylesheets became huge documents with thousands of lines. There had to be a better way.
So I started experimenting with how I organized my CSS. With a few simple changes, I was able to write CSS much faster, significantly reducing development time—time that was then spent on more important aspects of the application. I started by not writing every declaration on a new line, but instead keeping them on one line like so:
div#header { width: 800px; background: url(/images/header.jpg) no-repeat; }
div#header h1 { font-size: 24px; font-family: Arial, sans; color: #333333; }
div#header ul#nav { width: 800px; }
div#header ul#nav li { padding: 3px 5px; border: 1px #6699FF solid; }
div#header ul#nav li a { color: #000033; text-decoration: none; }
div#header ul#nav li a:hover { text-decoration: underline; }
By not creating a new line for each declaration, I have cut the number of lines in my CSS file by 70 to 80 percent, thus turning a CSS file with over 2500 lines to one with about 500. Keeping our declarations on the same line as the selector is a small step that will only lead to more experimentation.
So now we have a selector with all its declarations on one line, then a new line and so on. If we remove the new line between each selector, it cuts the number of lines in half:
div#header { width: 800px; background: url(/images/header.jpg) no-repeat; }
div#header h1 { font-size: 24px; font-family: Arial, sans; color: #333333; }
div#header ul#nav { width: 800px; }
div#header ul#nav li { padding: 3px 5px; border: 1px #6699FF solid; }
div#header ul#nav li a { color: #000033; text-decoration: none; }
div#header ul#nav li a:hover { text-decoration: underline; }
But removing all the blank lines makes it so hard to read. If we have hundreds of lines, it would be a nightmare for us and other developers to read. So let's line up our declarations to make our selectors easier to find:
div#header { width: 800px; background: url(/images/header.jpg) no-repeat; }
div#header h1 { font-size: 24px; font-family: Arial, sans; color: #333333; }
div#header ul#nav { width: 800px; }
div#header ul#nav li { padding: 3px 5px; border: 1px #6699FF solid; }
div#header ul#nav li a { color: #000033; text-decoration: none; }
div#header ul#nav li a:hover { text-decoration: underline; }
Now our selectors are much easier to spot, and the one line code is much easier on the eyes. This experimentation is starting to become interesting. Very interesting. Take a second look at the code we have typed above. There is now a visible pattern with the selectors, and, with patterns, we can read the code much faster, quickly finding the one selector we need. But that's not all! We are also showing inheritance. For example, the ul#nav belongs to div#header. The ul#nav has li elements with links in them as well. With this structure, we don't have to keep switching between our stylesheets and html pages—everything we need to know is in the CSS. Previously, we would have had to scroll back and forth to notice this. But since the selectors look like a staircase, we can easily find the child elements. This gives writing one line CSS a bigger advantage over traditional multiline CSS in terms of organization. But as always, we can take it a step further.
What if we have a selector with many child elements? For example, the selector that ends with a:hover is currently our largest selector. Long selectors will lead to the need to scroll sideways, which we should try to avoid as much as possible. Also, long selectors may make it hard to keep track of which declaration belongs to which selector due to huge gaps of space between the two.
Now let's review our CSS code. Currently all belongs to #header, which is a id and therefore unique. A h1 in #header will be dark gray and using Arial, while elsewhere it wouldn't inherit those styles. There is a nav in #header, which is also unique. So a li with the selector in #header #nav or just #nav will have the same styles. Whereas we were previously too specific with our styles, let's now be less specific by starting our selectors with a common parent.
div#header { width: 800px; background: url(/images/header.jpg) no-repeat; }
div#header h1 { font-size: 24px; font-family: Arial, sans; color: #333333; }
ul#nav { width: 800px; }
ul#nav li { padding: 3px 5px; border: 1px #6699FF solid; }
ul#nav li a { color: #000033; text-decoration: none; }
ul#nav li a:hover { text-decoration: underline; }
There is now a smaller gap, making our selectors and declarations easy to follow, but we lose the pattern and visible inheritance. To fix this, let's add a new line between the #header and ul#nav. Let's also re-adjust the space between the selectors and declarations to the longest selector accordingly.
div#header { width: 800px; background: url(/images/header.jpg) no-repeat; }
div#header h1 { font-size: 24px; font-family: Arial, sans; color: #333333; }
ul#nav { width: 800px; }
ul#nav li { padding: 3px 5px; border: 1px #6699FF solid; }
ul#nav li a { color: #000033; text-decoration: none; }
ul#nav li a:hover { text-decoration: underline; }
Great! Now our code is organized into chunks by the common parent, which increases the visible organization of our code. Just by reading the very first item in the selector, we know that everything in the chunk belongs to that parent. For example, the first chunk styles everything relating to #header and in the second chunk, all styles relate to #nav. Once again, if this was multiline we would have to scroll up and down to find this information, while with one line, if we wanted to style the #nav, we would just skip the whole #header chunk.
The one line CSS method came about after many years of writing CSS. For me, it has greatly increased the speed of which I write and edit CSS because of the visible grouping and visible inheritance. The method described here isn't right or wrong by any means, and it may not be ideal for everyone. At the end of the day, writing one-line or multi-line CSS is a matter of personal preference—since they both will render the same to the end user. Writing one-line css is a example I wanted to share on how the simplest code reorganization can lead to faster and more efficient development. Next time there is a slow zone in your development process, I highly recommend to give your code a second look and experiment with it's organization, the smallest steps can travel the farthest.
Just want to skip to the good stuff? Check out our new ERB Caching technique now!
I've always had a love/hate relationship with Rails caching. The guys on the Rails team have supplied us with several tools for the job and have taken most of the pain out of it. Between page caching, action caching and fragment caching, you usually can find a method that will suit your application.
Easy as it may be, however, these caching techniques do come at a cost. The more caching you use,the more control you lose over your site and your user experience. These tradeoffs have proventhe most significant when developing social networking sites, where high traffic demands efficient caching but the need for user personalization demands constant fresh content.
So which do you use? Here are the pros and cons of the existing Rails caching methods, plus a brand new technique we are using at Killswitch we call ERB Caching.
Page Caching (The Unattainable Ideal)
From the perspective of performance and speed, nothing can beat the rewards of page caching. When rendering a page, Rails saves the generated markup as HTML into the /public folder. From that point on, subsequent requests for the same URL will be served by Apache, leaving Rails completely out of the process. This takes tremendous strain off of Rails and allows a single server to handle significant traffic. Even if the cached pages' time to live (ttl) is only a few minutes, the performance savings are fantastic.
Using page caching has worked well for us on some of our large scale projects. For one such project, we used the Akamai service to handle requests for cached pages, taking even more load off of our server. With this setup, a single server with only a few mongrel instances has been able to easily serve thousands ofunique visitors daily on a high profile, media-intensive site.
As ideal as this is, using page caching becomes an impossibility if you want any kind of personalization on your site (and you probably do). Since Rails is left out of the equation and Apache is serving static HTML files, it's nearly impossible to include any personalized content on the site (except for using Javascript trickery, of course). This is why page caching is used infrequently in the industry as users demand personalized, community oriented experiences.
Action Caching (Close, But Not Quite)
For more flexibility (and fewer performance gains) Rails supplies action caching. This feature is similar to page caching in that the entire output page is still cached. This time, however, Rails is in charge of serving thefile. This means that you can use before/after filters and sessions to determine what to send to the user. For example, you can send one page if the user is a logged in member, another if they are not.
With this additional layer of control, this technique is more applicable to community-driven apps. However, simple things like putting the logged-in user's name at the top of the page is still difficult/impossible, since all logged in users would be served the same cached page.
Many of the deficiencies of this method were fixed by the action_caching plugin (http://blog.craz8.com/action-cache-plugin/), which allows for conditional caching, control over cache storage options and more. The functionality of action caching has also been significantly extended with our creation of ERB Caching, described below, which uses action caching as its foundation. More on that coming up in a few paragraphs...
Fragment Caching (It Works, But Still...)
Fragment caching has become the standard caching mechanism of large, dynamic, user-driven sites. It gives you all the flexibility you need, though the performance savings aren't much compared to those of page and actioncaching. This method allows you to mix generated content with chunks of the view that are cached and then reassembled on every render.
Using fragment caching helps speed up page renders, reduces database queries and is definitely better than using no caching at all! Still, I couldn't help feeling like there must be a better way. Working on social networking sites that could almost use action caching became frustrating - I wanted the benefits of fully cached pages and was tired of working with views littered with <% cache ... %> fragment caching code.
With that goal in mind, the idea for ERB Caching was developed.
ERB Caching (Finally, the Best of Both Worlds!)
I always had a sense that action caching was more powerful than it let on. You get the control and power of Rails, but still are given the performance benefits of caching the entire page.
My specific challenge was building a social networking site where every page on the site would have extra user-centric modules on it if the user was logged in. The modules are fairly simple, just some links and buttons that would initialize user actions. Still, their creation was dependent on knowing whether the user was logged in and knowing who they were.
The solution was to actually store ERB code in the cached views. The cache file is loaded into Rails, after which the ERB Caching code does a quick parsing to render any remaining ERB. The final file is then sent to the user, fresh and personalized.
Here's an arbitrary example:
In main_controller.rb:
before_filter :setup_vars caches_action :index def index @message = "Hello from index!" end def setup_vars @message = "Hello from before_filter!" end
In index.rhtml:
<p>This time will get cached and be the same on every page view: <%= Time.now %><p> <p>This time, however, will update on every page view: <%%= Time.now %><p> <p>The message set in the controller is: <%%= @message %><p>
When this view is first rendered, any ERB tags with two percent signs will not be rendered, but simply replaced by single percent signs. This is what the contents of the cache file will then look like:
<p>This time will get cached and be the same on every page view: Fri Feb 08 14:43:54 -0600 2008<p> <p>This time, however, will update on every page view: <%= Time.now %><p> <p>The message set in the controller is: <%= @message %><p>
The trick is then to render this a second time in an after_filter. Any variables set in a before filter will be available in this second rendering.
This is what the first page render produces:
<p>This time will get cached and be the same on every page view: Fri Feb 08 14:43:54 -0600 2008<p> <p>This time, however, will update on every page view: Fri Feb 08 14:43:54 -0600 2008<p> <p>The message set in the controller is: Hello from index!<p>
... and the second page view produces:
<p>This time will get cached and be the same on every page view: Fri Feb 08 14:43:54 -0600 2008<p> <p>This time, however, will update on every page view: Fri Feb 08 14:47:17 -0600 2008<p> <p>The message set in the controller is: Hello from before_filter!<p>
As you can see, any "double" ERB tag that is in a view will be cached as a single ERB tag and then rendered freshly on every request. This becomes very useful when you want to cache whole pages, but also want to have the current user's name displayed on the page. Simply set @user in a before_filter and then use <%%= @user.name %> in your view.
The code required to make this work is surprisingly simple and is all included in the erb_cache.rb file. Just place the file in /lib and put require 'erb_cache' in your environment.rb file. You will also have to put ActionController::Base.perform_caching = true in environment.rb if you want to test this out in development.
Also, don't forget to get the ActionCache plugin, it provides some excellent features.
This is still under development, so if you have any trouble with it, please let us know!

