Skip to main content

Mobomo webinars-now on demand! | learn more.

In the last Built For Speed post, I demonstrated how you can use the Amazon CloudFront Content Delivery Network (CDN) for images on your site. However, ideally we should be using CloudFront for all static assets, not just images.

Before we jump into that, though, let’s do a quick review of how CloudFront works. Like all CDNs, CloudFront consists of a number of edge servers all around the world, each of which has a connection back to a central asset server. When CloudFront receives a request for an asset, it calculates which edge server is geographically closest to the request location. For example, a user in England may request the asset ‘dog.jpg’. CloudFront will route that request to the London server, which will check if it has a cached version of ‘dog.jpg’. If it does, the edge server will return that cached version. If not, it will retrieve the image from the central asset, cache it locally and return it to the user. All subsequent requests in England for ‘dog.jpg’ will get the cached version on the London edge server. This approach minimizes network latency.

There is one big gotcha with this approach: If the ‘dog.jpg’ image changes from a poodle to a beagle, but keeps the same name, the edge server will keep serving the poodle image (assuming the expires headers are set far in the future as they should be). The edge server will not pick up the latest asset unless the name of the asset changes.

Okay, with that background out of the way, let’s take a look at how we can get our CSS and JavaScripts served through CloudFront. The approach I’ve taken is to create an initializer file that sets a REVISION constant. This could easily be created as part of a deployment process, copying the latest Git or Subversion revision into the initializer file, but for now I just created it manually. We’ll append the REVISION constant to the names of your packaged CSS and JavaScripts, so that on each deploy, the files have a different name, thereby preventing CloudFront from serving stale assets.

I have also moved the S3 configuration parsing out of the Post model and into another initializer, which sets the S3_CONFIG hash constant. In addition, I added the bucket name to my amazon_s3.yml config file. (Remember, if you have any questions, you can always refer to the source code.)

 # /config/initializers/s3_config.rb S3_CONFIG = YAML.load_file("#{RAILS_ROOT}/config/amazon_s3.yml")[RAILS_ENV] 

See below for the Rake task I wrote to copy the packaged files to S3. Note that this should be run after you run the ‘rake assets:packager:build_all’ task from AssetPackager (see the first Built For Speed post).

 require 'right_aws'  namespace :s3 do   namespace :assets do     desc "Upload static assets to S3"     task :upload => :environment do       s3 = RightAws::S3.new(         S3_CONFIG['access_key_id'],          S3_CONFIG['secret_access_key']       )       bucket = s3.bucket(S3_CONFIG['bucket'], true, 'public-read')        files = Dir.glob(File.join(RAILS_ROOT, "public/**/*_packaged.{css,js}"))        files.each do |file|         filekey = file.gsub(/.*public//, "").gsub(/_packaged/, "_packaged_#{REVISION}")         key = bucket.key(filekey)         begin           File.open(file) do |f|             key.data = f             key.put(nil, 'public-read', {'Expires' => 1.year.from_now})           end         rescue RightAws::AwsError => e           puts "Couldn't save #{key}"           puts e.message           puts e.backtrace.join("n")         end       end     end   end end 

Again, ideally this should be part of the deployment process – first, run the AssetPackager task to create the packaged asssets, then run the S3 upload task to store them on S3. Notice that I’m appending the REVISION string to the end of file names for each of the packaged CSS and JavaScript files before uploading to S3. Also notice that I’m setting the Expires header to one year from now.

Hmm, we may have a couple problems here. First, by default, Rails expects CSS and JavaScript files to be in their proper places in the /public directory at the root of the application. That’s easily fixed by adding the following line to the bottom of /config/environments/production.rb:

 ActionController::Base.asset_host = Proc.new { CLOUDFRONT_DISTRIBUTION } 

The second problem is that the helpers provided by the AssetPackager plugin (‘stylesheet_link_merged’ and ‘javascript_include_merged’) don’t know that you’ve added a revision number to the end of the filenames. Not to worry – we just need to update a couple lines in /vendor/plugins/asset_packager/lib/synthesis/asset_package.rb. Update the ‘current_file’ method to look like this:

 def current_file   build unless package_exists?    path = @target_dir.gsub(/^(.+)$/, '1/')   name = "#{path}#{@target}_packaged"   name += "_#{REVISION}" if defined? REVISION end 

Try making those updates, then running ‘rake s3:assets:upload RAILS_ENV=production" (remember we’re running in production mode for all the Built For Speed examples). After restarting your application, inspect the source and you should see that your stylesheets and scripts are being served by CloudFront, with the revision number at the end of the file names.

Now let’s return to our images. After the last post, we already have them delivered by CloudFront. The problem is, if you decide to update your image, Paperclip will give it the same style names as before (‘original’, ‘large’, ‘medium’, ‘thumb’). Uh-oh. Because the files have the same names, the CloudFront edge servers won’t update from the central asset server to use the latest image, and your users will continue to see the stale, old image.

Go ahead and give it a try by updating an image for an existing post. Whoops! The old image is still displayed.

Here’s how we solve that problem. First of all, let’s update the Post model to use the new S3_CONFIG constant. While we’re at it, let’s add a timestamp to our image path so that each time you update the image, it will have a different name.

 # in /app/models/post.rb has_attached_file :image,                  :styles => {:large => "500x500", :medium => "250x250", :thumb => "100x100"},                  :storage => 's3',                  :s3_credentials => S3_CONFIG,                  :bucket => S3_CONFIG['bucket'],                  :path => ":class/:id/:style_:timestamp.:extension" 

One small issue: For some reason the Paperclip plugin uses a string representation for its ‘timestamp’ method, so you end up with values like “2009-06-26 15:25:44 UTC”. This isn’t very practical for timestamping file names, so I’ve changed it:

 # /vendor/plugins/paperclip/lib/paperclip/interpolations.rb def timestamp attachment, style   attachment.instance_read(:updated_at).to_i end 

With that change, now each time we store an attached image, it will have a timestamp affixed to the end of the filename. Thus, the CloudFront edge server will go back to the central asset server and retrieve the new image rather than serving up the old image. Don’t worry – for each subsequent request, you’ll get the benefit of having the new image on the edge server.

Restart your application, and try updating an image again. This time around, you’ll see the image is updated correctly.

That wraps it up for our CloudFront review. Now go forth and speed up your sites!

Update

I realized I promised in my last post to show you how to make YSlow recognize that you were now using a CDN. Here’s what you do:

  1. Go to “about:config” in Firefox
  2. Right-click on the page, and select “New” > “String”
  3. Enter “extensions.yslow.cdnHostnames” as the preference name
  4. Enter “cloudfront.net” as your CDN host name
  5. Restart Firefox and run YSlow on your application again – you should now see that you get an “A” for using a CDN

RESOURCES

Built For Speed source code

Categories
Author

This is the second in a series of posts on improving your site’s performance with the help of the YSlow Firefox plugin. In the last Built for Speed post, we took a look at YSlow’s most important factor in page speed – the number of HTTP requests. We demonstrated using the AssetPackager plugin to help reduce both the number of HTTP requests and the size of your CSS and JavaScript files. The source for the Built for Speed application is available on Github.

This week, we’ll learn how to use a Content Delivery Network (CDN) to help users see our static content faster. Granted, this may be overkill for a lot of sites, but I think it’s worth the time to see how it’s done. There are a lot of CDNs out there, but I’ve decided to use Amazon’s CloudFront because it’s relatively cheap and easy to set up (not to mention it integrates seamlessly with S3). Before you get much further, you’ll want to set up an account with S3 and CloudFront.

First, let’s install the Paperclip plugin so we can upload an image to go with our post.

   script/plugin install git://github.com/thoughtbot/paperclip.git

Next, we need to add the Paperclip fields to the Post model:

   script/generate migration AddPaperclipColumnsToPost
   # in the newly-created migration   def self.up     add_column(:posts, :image_file_name, :string)     add_column(:posts, :image_content_type, :string)     add_column(:posts, :image_file_size, :integer)     add_column(:posts, :image_updated_at, :datetime)   end    def self.down     remove_column(:posts, :image_file_name)     remove_column(:posts, :image_content_type)     remove_column(:posts, :image_file_size)     remove_column(:posts, :image_updated_at)   end

We also need to make the Paperclip declaration in the Post model:

   class Post < ActiveRecord::Base     has_attached_file :image, :styles => {:large => "500x500", :medium => "250x250", :thumb => "100x100"}   end

And finally we make the updates to the views. First change the new and edit views, adding the file_field for the attachment and making sure the form is set to accept multipart data (see the source on Github if you have questions). Then update your show view to display the image:

   <%= image_tag @post.image.url(:large) %>

Now let’s take a look at our application in Firefox. Remember, we’re running it in production mode to see the benefits of the AssetPackager plugin among other things. Create a new post with the image attachment of your choice.

One gotcha – if you are running your application using Passenger, you may see an error something like this when you try to create a Paperclip attachment:

   Image /tmp/passenger.621/var/stream.818.0 is not recognized by the 'identify' command.

In order to avoid this, create a file in /config/initializers to tell Paperclip where to find ImageMagick. I installed ImageMagick using MacPorts, so my file looks like:

   Paperclip.options[:command_path] = "/opt/local/bin"

Okay, now that we have a new post with an image, browse to the post detail page, open up the YSlow interface, click “Run Test” and take a look at the second grade, “Use a Content Delivery Network (CDN)”.

Posts: show
Uploaded with plasq’s Skitch!

Ugh, we got a “D”. Okay, let’s see how we can implement Amazon CloudFront to make that grade an “A”.

Let’s start by telling Paperclip to use S3 for our image storage. Go ahead and create a configuration file called amazon_s3.yml. Obviously, you’ll need to replace the values here with your own keys:

   # config/amazon_s3.yml   development:     access_key_id: 123...     secret_access_key: 123...   test:     access_key_id: abc...     secret_access_key: abc...   production:     access_key_id: 456...     secret_access_key: 456...

Paperclip depends on the ‘right_aws’ gem for its S3 storage, so make sure you add that to your config.gem list in /config/environment.rb and install it with:

   rake gems:install

Next, update the Post model so it will use the new S3 configuration:

   class Post < ActiveRecord::Base     has_attached_file :image,                      :styles => {:large => "500x500", :medium => "250x250", :thumb => "100x100"},                      :storage => 's3',                      :s3_credentials => YAML.load_file("#{RAILS_ROOT}/config/amazon_s3.yml")[RAILS_ENV],                      :bucket => "built-for-speed",                      :path => ":class/:id/:style.:extension"   end

Now restart your application and create a new post with an image. When you get to the post detail page, check out the source and you should see that your image is being served from your S3 bucket. That’s great, but what we really want to do is serve the image from the CloudFront CDN. The easiest way to do this is to install the S3 Firefox Organizer plugin). Once you enter your credentials, you should see your newly-created ‘built-for-speed’ bucket. Right-click on the bucket name and click “Manage Distributions”, then optionally add a comment and click “Create Distribution” (we’ll skip the CNAME option for now).

S3 Firefox Organizer
Uploaded with plasq’s Skitch!

This will generate a new resource URL for you to use in your application so you can take advantage of CloudFront. Now we have to go back and tell our application to use this resource URL:

   # /config/initializers/cloudfront.rb   #    # Note that your CloudFront resource URL will be different   CLOUDFRONT_DISTRIBUTION = "http://d2qd39qqjqb9uw.cloudfront.net"
   # in /app/models/post.rb   def cloudfront_url( variant = nil )     image.url(variant).gsub( "http://s3.amazonaws.com/built-for-speed", CLOUDFRONT_DISTRIBUTION )   end
   # in /app/views/posts/show.html.erb   <%= image_tag @post.cloudfront_url(:large) %>

Restart the application and go back to the post detail page. Inspect the source and you’ll see that your image is now being served from CloudFront.

Okay, I think that’s enough for today. In the next post, I’ll show you how to avoid CloudFront serving stale assets and how to make YSlow recognize that you are now using a CDN. Please leave any questions or comments below.

CREDITS/RESOURCES:

  • Nick Zadrozny’s YSlow presentation from SD Ruby
Categories
Author

I recently had the pleasure of attending the User Interface Engineering Roadshow in Washington, DC. The day was chock-full of insight and wisdom from usability guru Jared Spool, founder of UIE.com. In taking notes during the workshop, I decided to take my first official shot at sketchnoting. The results are as follows:

Overall, I found this experience to be wholly satisfying. Putting key ideas into sketches required an interesting use of my attention span: in addition to listening intently to the ideas being communicated, I was simultaneously forced to employ creative sketching solutions that would properly embody the most important parts of the talks.

Upon returning home from the Roadshow, I was quite surprised to find these sketches spanning 5 full pages of my notebook. Upon scanning the notes and adding a bit of chronological organization, I’m left with a sort of visual map of the workshop (as I experienced it). I hope these notes might be of interest to others who attended.

As for adventures in sketchnoting? I look forward to continued exploration when the proper circumstances arise in the future.

Categories
Author

Inspired by the recent launch of code.google.com/speed, I decided to sit down and see how I could apply their guidelines. This is the first in a series of posts on improving front-end performance for your Rails applications.

First of all, we need to create our sample application. Recently, I’ve been using Beet, a gem for generating Ruby projects, but you can create your local version however works for you. Using Beet, the following command tells the Rails generator to use MySQL, remove unused files (public/index.html, etc.) and initialize a Git repository:

   beet -g built_for_speed -r="rails/db/mysql, rails/clean_files, rails/git"

Next, let’s create a Post resource:

   script/generate scaffold Post title:string body:text

Make sure your databases are created, and run the migrations. Note that for the purposes of this tutorial, I’m running the application in production mode (to better see the speed benefits), using Passenger on my MacBook Pro. By the way, I highly recommend the Fingertips Passenger preference pane for managing your sites locally.

   RAILS_ENV=production rake db:migrate

Now let’s add the Blueprint CSS framework. Download the latest version from blueprintcss.org and unpack it somewhere. Blueprint provides you with compressed versions of the CSS files, but humor me and add the uncompressed versions. From the unpacked directory, copy all the CSS files from /blueprint/src/ into /public/stylesheets/blueprint/ in your application.

Before we start up the application, let’s add the CSS files as well as the default JavaScript files to the head of our posts layout (/app/views/layout/posts.html.erb). The head of your layout file should look something like this (note that I added my own base.css file):

   <head>     ...     <%= stylesheet_link_tag 'blueprint/reset', :media => 'projection, screen' %>     <%= stylesheet_link_tag 'blueprint/typography', :media => 'projection, screen' %>     <%= stylesheet_link_tag 'blueprint/forms', :media => 'projection, screen' %>     <%= stylesheet_link_tag 'blueprint/grid', :media => 'projection, screen' %>     <%= stylesheet_link_tag 'blueprint/print', :media => 'print' %>     <!--[if lt IE 8]> 		<%= stylesheet_link_tag 'blueprint/ie', :media => "screen, projection" %> 	<![endif]--> 	<%= stylesheet_link_tag 'base', :media => 'projection, screen' %> 	<%= javascript_include_tag :defaults %>   </head>

Okay, now let’s fire up the application. I’ll be using Firefox so we can profile the application using YSlow. Go ahead and create your first post. Once you’re looking at the ‘show’ page, let’s open up Firebug and click on the “YSlow” tab. On the YSlow screen, click the “Run Test” button to get your page grade.

Posts: show
Uploaded with plasq’s Skitch!

Bummer, we got an overall D – not so good. Let’s take a look at what’s going on. YSlow grades are listed in order of importance, so let’s check out the first section: “Make fewer HTTP requests”. Looks like we got a C in that area. What can we do to improve our grade? YSlow gives us some tips: “combine multiple scripts into one script, combine multiple CSS files into one style sheet”. Before we get back to the application, take a look at the “Components” tab in the YSlow dialog.

Posts: show
Uploaded with plasq’s Skitch!

Hmm, five JavaScript files for a total of 234.3K and six CSS files for a total of 18K. We definitely need to work on that.

In order to compress JavaScript and CSS files in my applications, I use Scott Becker’s AssetPackager plugin. Go ahead and install it:

   script/plugin install git://github.com/sbecker/asset_packager.git

The first step after installation is to create the configuration file for AssetPackager:

   $ rake asset:packager:create_yml

You should see a message to reorder the files under ‘base’, so let’s go ahead and do that. Open up the newly-created /config/asset_packages.yml file. You’ll notice that there are two top-level entries – one for ‘javascripts’ and one for ‘stylesheets’. AssetPackager should have correctly generated the required files for the ‘javascripts’ section, but we’ll need to add the Blueprint files. Your completed asset_packages.yml file should look something like this (again, I added a base.css file):

 	---  	javascripts:  	- base:  	  - prototype 	  - effects 	  - dragdrop 	  - controls 	  - application 	stylesheets:  	- base: 	  - blueprint/reset 	  - blueprint/typography 	  - blueprint/forms 	  - blueprint/grid  	  - base 	- print: 	  - blueprint/print 	- ie: 	  - blueprint/ie

Now that the config file is set up, you can go ahead and generate the combined, minified JavaScript and CSS files:

   rake asset:packager:build_all

This command will output a “[name]_packaged” file for each entry under both ‘javascripts’ and ‘stylesheets’. Now we have to tell our application to use those compressed files. Go back to your posts.html.erb layout and change the head to look like this:

   <head>     ...     <%= stylesheet_link_merged :base, :media => "screen, projection" %>     <%= stylesheet_link_merged :print, :media => "print" %>     <!--[if lt IE 8]> 	<%= stylesheet_link_merged :ie, :media => "screen, projection" %>     <![endif]-->     <%= javascript_include_merged :defaults %>   </head>

Okay, now it’s time to see the fruits of our labors. Restart the application in the Passenger preference pane, and reload the post page in Firefox. Now let’s run YSlow again. This time, you should see output like this:

Posts: show
Uploaded with plasq’s Skitch!

Alright! We’ve improved our grade up to an overall B, with an A for “Make fewer HTTP requests”. Let’s take a look at the ‘Components’ tab.

Posts: show
Uploaded with plasq’s Skitch!

Thanks to AssetPackager, we’re down to one JavaScript file for a total of 171.7K and two CSS files for a total of 12.6K. Now there’s eight fewer components, and we’re saving 68K of bandwidth on each request. Nice work!

The source code for this sample application can be found at: github.com/dramsay/built_for_speed

Check back for more performance tips in the future.

Categories
Author

I love the any? and empty? convenience methods that Rails and Ruby provide, they make conditional statements much easier to read. I also really dislike the default method of checking this behavior in jQuery:

if ($('some.element').length > 0) {   // ...do something }

Well, luckily jQuery is ridiculously easy to extend, so why not just mix that functionality in with a couple of quick shot plugin methods? Just add this javascript sometime after you include jQuery:

jQuery.fn.any = function() {   return (this.length > 0); }  jQuery.fn.none = function() {   return (this.length == 0); }

That’s all you have to do! Now we can make the same call as before, but it looks a little cleaner:

if ($('some.element').any()) {   // do something more readably... }

UPDATE: Apologies, I added in the empty bit as a last-second update to the post and forgot to check and realize that empty() is part of jQuery core. Updated the name to none instead.

Categories
Author

I will be speaking at the Internet Summit 2009 at the Raleigh Convention Center on November 4-5th. The conference focuses on Internet trends and opportunities created by web technologies, social media, online advertising, widgets, online video, and other related technologies. I will be focusing on sharing ideas on mobilizing web content for enterprises. I am looking forward to meeting with fellow speakers from Digg, Huffington Post, Twitter, Google, Salesforce, Technorati and many more.

The conference promises to be educational and fun. Please check out the conference site and register for the event. Hope to see you there.

Categories
Tags
Author

I'm pleased to announce Intridea's Hackon, a 3-day co-working and unconference event we've organized at the Portland Harbor Hotel in Portland, Maine June 18-20th.

Thursday and Friday (17/18th) will be for informal hacking, coworking, and discussion and as we attempt to backfill interesting talks for Saturday (20th).

The Hackon is intended to bring local software craftsman together to work on projects, exchange ideas, hack on open source, or just get some work done. There will be wifi, projection, food, and valet parking at the hotel.

And because we love developers and the community so much, we are hosting this event for FREE! Yes. Free. If you want to attend we only ask that you RSVP ahead of time so we can ensure that enough food, space, and power available.

Thank you to MERUG, NHRUG, and #Progmatica for their support and their assistance in spreading the word!

Please head over to intridea.com/hackon for details, rsvp, and talk submission.

Categories
Author

In the June 2009 issue of Inc. Magazine, Present.ly was mentioned as one of the best-bet collaboration tools for mobile users. It's becoming more apparent that companies need online collaboration tools that help alleviate the hassle of back-and-forth e-mail conversations and allow users to share information and documents. The article states that 22 percent of companies use real-time collaboration software and around 24 percent use document-sharing software, which Present.ly supports out of the box. It goes into detail on how to choose the right collaboration software for your company and provides real-life examples from different companies.

We are more than honored to be mentioned in a magazine that we all frequently read. If you get a chance, pick up the June 2009 issue of Inc. Magazine.

Categories
Author

With Roger Federer’s recent win of the 2009 French Open, he is now tied with Pete Sampras for holding the most Grand Slam titles — fourteen. Although the two athletes have arrived at the same destination, how do their respective journeys compare with one another? With this question fueling my curiosity, I set out to create a rich visualization of the data to add some depth to this story.

The final product is available as follows. For additional notes about the techniques used to create these graphs, keep on reading.

Here are some tips & techniques I employed to put this together.

Have Your Data Ready

Before diving into Illustrator (or whatever your tool of choice may be), spend the necessary time finding all of the data you will need for your graph. Go the extra mile to arrange and label everything properly — you may return to the data at a much later time and will be glad you did yourself the favor. Aside from reaping the benefits of good organization, this step is additionally helpful in keeping the grunt work of data-fetching separate from the creative requirements of the task.

Start with the Simplest Graphs Possible

An elegant, attractive graph is seldom created from scratch. There are usually a number of tried & tested variations that must be wrestled with before arriving at the final product. With this in mind, a good first step is creating some bare-bones, stripped down graphs to get bird’s eye view of the data. This phase is all about finding the approach that will best server your original vision. Sketches work great in this stage.

What is the story you want to tell with your data? This is an important question to keep in mind, as different visualization approaches will yield different results. Play around with things. See what looks good as well as which data comparisons are intuitive and interesting. Seek feedback from friends or colleagues who might offer a valuable opinion.

Using the Grid

Before long, it will be time to create your final, finished product. At this stage, the very most important thing you can do to keep things looking straight and orderly is use Illustrator’s grid feature. You’ll want to make grids visible (CTRL/CMD + “), as well as enable “Snap to Grid” (CTRL/CMD + SHIFT + “). Additionally, you may want to go into Illustrator’s preferences to customize the grid spacing and subdivision width (which you can modify at any point).

Another useful tip to keep in mind when using grids extensively is enabling Overprint Preview (CTRL/CMD + ALT + SHIFT + Y). This will have the gridlines appear on top of all objects & paths, allowing you to eliminate any guessing that might otherwise be required in keeping things properly arranged.

Using Layers Wisely

Keeping your objects arranged in layers is a huge time-saver when dealing with moderately complex projects in Illustrator. This was especially true in my case of creating four separate graphs, each of which contained separate groups of objects. For example: if I wanted to modify the color of the Roger Federer graph plots, I’d only need to target the layer “Federer” and all plots (on each graph) would become active.

Layers can also be locked, combined, or temporarily hidden to make document management easier.

Go Forth and Visualize

And that’s it! Combined with a simple bit of color and typography, you can transform any crude visualization into an attractive graph. Keep in mind that data in itself can be rather inert; though when arranged in a conscientious manner it can tell an interesting story. Hopefully the techniques above can be of use in recreating your own graphs of a similar nature.

Categories
Author
Subscribe to