<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">
  <channel>
    <title>Onehub blog</title>
    <link>http://onehub.com</link>
    <language>en</language>
    <webMaster>info@onehub.com (Chuck Mount)</webMaster>
    <copyright>Copyright 2008-2009</copyright>
    <ttl>60</ttl>
    <pubDate>Mon, 13 Jul 2009 02:30:00 GMT</pubDate>
    <description>Happenings at a stealth mode startup</description>
    <atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" href="http://feeds.feedburner.com/onehubblog" type="application/rss+xml" /><item>
      <title>Using Onehub as a Virtual Data Room</title>
      <link>http://feedproxy.google.com/~r/onehubblog/~3/zy1l6TtniOI/</link>
      <pubDate>Mon, 13 Jul 2009 02:30:00 GMT</pubDate>
      <guid isPermaLink="false">http://onehub.com/past/2009/7/13/virtual_data_room/</guid>
      <author>cmount@onehub.com (Charles Mount)</author>
      <description>&lt;p&gt;&lt;strong&gt;One of the most powerful things about Onehub is its flexibility.&lt;/strong&gt; You can modify almost all aspects of a Hub including the appearance, number of Pages and Widgets, permission settings and Hub Notifications. This flexibility allows Onehub to be used in a variety of business to business scenarios such as &lt;a href="http://onehub.com"&gt;Virtual Data Rooms&lt;/a&gt;. Several Investment Banks have adopted Onehub as a Virtual Data Room for M&amp;amp;A transactions.&lt;/p&gt;

&lt;p&gt;If you are not in the banking or legal industry you may not be familiar with the term. Below is a short definition from &lt;a href="http://en.wikipedia.org/wiki/Data_room"&gt;Wikipedia&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;A virtual data room (VDR) is essentially an Internet site with limited controlled access (using a secure log-on supplied by the vendor/authority which can be disabled at any time by the vendor/authority if a bidder withdraws) to which the bidders and their advisers are given access. Much of the information released will be confidential and restrictions should be applied to the viewers' ability to release this to third parties by forwarding, copying or printing.&lt;/h4&gt;

&lt;h3&gt;Onehub provides an easy-to-use, affordable alternative to traditional Virtual Data Room Providers&lt;/h3&gt;

&lt;p&gt;Here are a few tips for setting up a Virtual Data Room using Onehub. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Configure Notifications and History.&lt;/strong&gt; You can configure Hub Notifications and History so users cannot see what other users are doing. Why? If you invite three potential buyers to the same Hub you don't want them to see who the other buyers are and what files they are downloading.&lt;/p&gt;

&lt;p&gt;To set notification and history, click Manage from the Administrator bar at the top of the Hub and then choose Notifications/History. Uncheck the options for Author, Contributor, and Reader.&lt;/p&gt;

&lt;p&gt;&lt;img src="/images/posts/blog_notifications.gif" alt="Notification Settings"/&gt;&lt;/p&gt;

&lt;div style="clear:both"&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;2. Remove Activity Widgets.&lt;/strong&gt; An Activity Widget in a Hub shows all the events by default. You can remove Widgets from a Hub by clicking on Design &amp;amp; Add Widgets from the Administrator bar. You can remove any widget by clicking the Red Close Icon in the top right corner.&lt;/p&gt;

&lt;p&gt;&lt;img src="/images/posts/blog_delete_widget.gif" alt="Remove Activity Widgets"/&gt;&lt;/p&gt;

&lt;div style="clear:both"&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;3. Use History and Activity to Track Usage.&lt;/strong&gt; Using File History, the Hub Administrators can view all activity for an individual file. This provides a quick way to audit when users have downloaded a document. With Activity filters, you can easily track all activity in the Hub included when users accessed the Hub, downloaded files, or made changes.&lt;/p&gt;

&lt;p&gt;To setup a Hub filter, click on the Activity tab from the User Home. Select the Hub name from the drop down. Type a name for the filter and click save. You can simplify the view by hiding your own activity from the report.&lt;/p&gt;

&lt;p&gt;&lt;img src="/images/posts/blog_recent_activity.gif" alt="Hub Activity Report"/&gt;&lt;/p&gt;

&lt;p&gt;For more information about using Onehub as a &lt;a href="http://onehub.com"&gt;Virtual Data Room&lt;/a&gt;, please fee free to contact us at support@onehub.com.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/onehubblog/~4/zy1l6TtniOI" height="1" width="1"/&gt;</description>
      <category domain="http://onehub.com/past/tags/collaboration">collaboration</category>
      <category domain="http://onehub.com/past/tags/feature">feature</category>
      <category domain="http://onehub.com/past/tags/flexibility">flexibility</category>
      <category domain="http://onehub.com/past/tags/tips">tips</category>
      <category domain="http://onehub.com/past/tags/virtualdataroom">virtualdataroom</category>
    <feedburner:origLink>http://onehub.com/past/2009/7/13/virtual_data_room/</feedburner:origLink></item>
    <item>
      <title>Handling multiple pollers and multiple process groups when using Activemessaging and ActiveMQ</title>
      <link>http://feedproxy.google.com/~r/onehubblog/~3/efdLCwaC9gM/</link>
      <pubDate>Tue, 07 Jul 2009 02:03:00 GMT</pubDate>
      <guid isPermaLink="false">http://onehub.com/past/2009/7/7/handling_multiple_pollers/</guid>
      <author>bmoran@onehub.com (Brian Moran)</author>
      <description>&lt;p&gt;Eventually, your web application is going to need to do work asynchronously to web requests- tasks like transcoding videos, generating notifications or newsfeeds, performing 'slow' file operations. You'll likely need a message queueing system such as ActiveMQ, Amazon SQS, RabbitMQ, or commercial products from IBM or Tibco.  For a variety of reasons, we are using ActiveMQ as our message broker, and Activemessaging is how our rails application talks to it. We've developed some techniques to divide our message processing between a number of process groups so that long-running tasks don't get in the way of shorter-duration ones, yet can be monitored and managed effectively in the activemessaging context. We've also learned of some tweaks that might help others. &lt;/p&gt;

&lt;p&gt;A typical infrastructure requiring asynchronous processing may have one or more web servers fronted by a load balancer; one or more database servers, a 'services' server (where the message broker runs), a 'work' server that actually handles the deferred jobs from the various queues.  In our example, content can be uploaded to the individual web servers, but this content must eventually get put into secondary storage (SAN, Amazon S3, etc.) Some jobs, such as generating previews, or video transcoding, are not performed on the web servers; it's better practice to have a pool or processors that can perform this work, so over time the number of processors can be scaled.&lt;/p&gt;

&lt;p&gt;Our asynchronous workload mix spans a wide range: from cpu-engrossing operations (video transcoding) to 'lighter' tasks such as email sending.  Using &lt;em&gt;activemessaging&lt;/em&gt;, processors are assigned to different process-groups in the &lt;code&gt;config/messaging.rb&lt;/code&gt; file.   An example &lt;code&gt;config/messaging.rb&lt;/code&gt; file (similar in concept to what we actually use) is:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;ActiveMessaging::Gateway.define do |s|
   # /script/poller start&amp;mdash;process-group=notifier_group
   # /script/poller start&amp;mdash;process-group=asset_group
   # /script/poller start&amp;mdash;process-group=media_group
   # service server runs all groups.
   # web servers run asset_groups.

   s.processor_groups = {
      :notifier_group =&amp;gt; [:email_sender_processor,:feed_builder_processor, :job_finished_processor],
      :media_group =&amp;gt; [:preview_builder_processor, :video_transcoder_processor],
      :asset_group =&amp;gt; [:move_to_secondary_storage_processor]
    }

   s.destination :feed_builder,        '/queue/FeedBuilder'
   s.destination :email_sender,        '/queue/EmailSender'
   s.destination :q_job_reply,         '/queue/QJobReply'
   s.destination :preview_builder,     '/queue/ContentItemPreviewBuilder'
   s.destination :asset_mover,         '/queue/AssetMover_' + MY_HOSTNAME
   s.destination :video_transcoder,    '/queue/VideoTranscoder'
 end
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Use the same &lt;code&gt;messaging.rb&lt;/code&gt; everywhere, run different mixes of process-groups&lt;/h3&gt;

&lt;p&gt;We use the SAME &lt;code&gt;messaging.rb&lt;/code&gt; on all of our servers&amp;mdash;we vary the pollers that we run based on the server type. Web servers only run the &lt;code&gt;asset_group&lt;/code&gt;. Service servers run &lt;code&gt;media&lt;/code&gt;, &lt;code&gt;notifier&lt;/code&gt;, and &lt;code&gt;asset&lt;/code&gt; groups.  For operations that need to happen on particular machines (e.g. moving particular assets from one machine to another), we've added the hostname (&lt;code&gt;MY_HOSTNAME&lt;/code&gt; is set in &lt;code&gt;config/environment.rb&lt;/code&gt; with &lt;code&gt;MY_HOSTNAME = `hostname`.chomp&lt;/code&gt;) to the queue designation&amp;mdash;on each machine that is running a processor, it listens on it's particular queue for those messages.  &lt;/p&gt;

&lt;p&gt;On a webserver, where assets are uploaded by customers before being moved to secondary storage,  a poller is started with:
 &lt;code&gt;/script/poller start&amp;mdash;process-group=asset_group&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;On a services server, run  &lt;code&gt;/script/poller start&amp;mdash;process-group=asset_group&lt;/code&gt;,  &lt;code&gt;/script/poller start&amp;mdash;process-group=notifier_group&lt;/code&gt;,  &lt;code&gt;/script/poller start&amp;mdash;process-group=media_group&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;Modify the poller to include the process group in the process name and PID file name&lt;/h3&gt;

&lt;p&gt;With the stock &lt;code&gt;script/poller&lt;/code&gt;, there is no differentiation of process name (&lt;code&gt;poller&lt;/code&gt;) or PID file (&lt;code&gt;/tmp/pids/poller&amp;lt;N&amp;gt;&lt;/code&gt;). Running the above commands on a service server would show three 'pollers', and there would be &lt;code&gt;poller0.pid&lt;/code&gt;, &lt;code&gt;poller1.pid&lt;/code&gt;, &lt;code&gt;poller2.pid&lt;/code&gt; files.  If one wanted to kill or restart a particular poller related to a particular process group, it's difficult to do.  Using an external monitoring solution such as &lt;code&gt;monit&lt;/code&gt; to supervise your pollers, to make sure you have one of each? You can't with the stock poller.&lt;/p&gt;

&lt;p&gt;Solution? Modify your &lt;code&gt;poller.rb&lt;/code&gt; to include the name of the process group in the process name and PID file:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;#!/usr/bin/env ruby
require 'rubygems'
require 'daemons'
require    File.join(File.expand_path(File.dirname(__FILE__) + '/..'), 'lib', 'daemons_extension')

APP_ROOT = File.expand_path(File.dirname(__FILE__) + '/..')
script_file = File.join(File.expand_path(APP_ROOT),'vendor','plugins','activemessaging','poller.rb')
tmp_dir = File.join(File.expand_path(APP_ROOT), 'tmp/pids')

# find the process group name by looking at the command line prompt for the process group.
process_group_name=''
if (argidx = $*.index('&amp;mdash;'))
  # look for process group
  $*[argidx..-1].each{|rrg|
    if (m = rrg.match(/process-group=(.*)/))
       process_group_name = "_#{m[1]}"
       break
    end
  }
end

options = {
  :app_name   =&amp;gt; "poller#{process_group_name}",
  :dir_mode   =&amp;gt; :normal,
  :dir        =&amp;gt; tmp_dir,
  :multiple   =&amp;gt; true,
  :ontop      =&amp;gt; false,
  :mode       =&amp;gt; :load,
  :backtrace  =&amp;gt; true,
  :monitor    =&amp;gt; false,
  :log_output =&amp;gt; false,
  :force_kill_wait =&amp;gt; 30
}

Daemons.run(script_file, options)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now you'll see &lt;code&gt;poller_asset_group&lt;/code&gt;, &lt;code&gt;poller_notifier_group&lt;/code&gt;, &lt;code&gt;poller_media_group&lt;/code&gt; in the process list, and &lt;code&gt;/tmp/pids&lt;/code&gt; will have &lt;code&gt;poller_asset_group0.pid&lt;/code&gt;, &lt;code&gt;poller_media_group0.pid&lt;/code&gt;, &lt;code&gt;poller_notifier_group0.pid&lt;/code&gt;. You can set up &lt;code&gt;monit&lt;/code&gt; or your favorite watcher on the PID file particular to the type of process group.&lt;/p&gt;

&lt;p&gt;Whats more, if you need to &lt;em&gt;scale across the enterprise&lt;/em&gt; and add more work processors, you'll always be able to tell how many of each kind you have by the name.&lt;/p&gt;

&lt;h3&gt;Rails Processors - Take One Bite At A Time&lt;/h3&gt;

&lt;p&gt;By default, each activemessaging poller will pull a number of messages from ActiveMQ, then work on them from it's own queue. If for some reason 'bad things happen to good pollers' and it must be killed, those messages disappear. It's better to have your processors only accept one message at a time. Add &lt;code&gt;:ack=&amp;gt;'client'&lt;/code&gt; to your &lt;code&gt;subscribes_to&lt;/code&gt; :&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class MoveToSecondaryStorageProcessor &amp;lt; ApplicationProcessor

  subscribes_to :asset_mover, {:ack =&amp;gt; 'client'}

  def on_message(message)
    #do something with the message
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;(yes, 'client' is a quoted string. Don't use a symbol.)&lt;/p&gt;

&lt;h3&gt;Rails Processors - Chew, don't gobble&lt;/h3&gt;

&lt;p&gt;Activemessaging will attempt to prefetch a number of messages from your broker so that network traffic overhead is minimized, which is a generally a good thing if your intent is to process many small messages very quickly.&lt;/p&gt;

&lt;p&gt;However, the messages that A13g has pre-fetched are unavailable to any other additional processors that you may add in response to increased message traffic. &lt;/p&gt;

&lt;p&gt;It can be a little confusing&amp;mdash;An example might help. You see that your existing queue length is large (say, 100 messages), and your single processor (processor #1) is working away at the messages. You don't expect more messages for a while, but you want those 100 to get handled faster. 
Starting another processor (processor #2) for that queue, you watch as it starts up, but it never receives any messages! It's precisely because those 100 messages have been prefetched by the original processor. If 50 more messages are sent to the broker, you'd find that about 25 messages go to processor #1, and 25 go to processor #2&amp;mdash;processor #2. Processor #2 might even finish processing ALL of it's 25 (or so) messages before Processor #1 gets to ANY of the message in the second batch.  &lt;/p&gt;

&lt;p&gt;The solution is to only prefetch one messsage at a time&amp;mdash;Add &lt;code&gt;'activemq.prefetchSize' =&amp;gt; 1&lt;/code&gt; to your &lt;code&gt;subscribes_to&lt;/code&gt; :&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;  subscribes_to :asset_mover, {:ack =&amp;gt; 'client',
                               'activemq.prefetchSize' =&amp;gt; 1}
&lt;/code&gt;&lt;/pre&gt;&lt;img src="http://feeds.feedburner.com/~r/onehubblog/~4/efdLCwaC9gM" height="1" width="1"/&gt;</description>
    <feedburner:origLink>http://onehub.com/past/2009/7/7/handling_multiple_pollers/</feedburner:origLink></item>
    <item>
      <title>Conversations with Customers:  Jen Francis Design</title>
      <link>http://feedproxy.google.com/~r/onehubblog/~3/fRN7qqqeab8/</link>
      <pubDate>Mon, 06 Jul 2009 02:49:00 GMT</pubDate>
      <guid isPermaLink="false">http://onehub.com/past/2009/7/6/conversations_with_customers/</guid>
      <author>kricketts@onehub.com (Kurt Ricketts)</author>
      <description>&lt;p&gt;We’re excited to announce a new blog series called &lt;b&gt;“Conversations with Customers” &lt;/b&gt;. Every few weeks we will feature a new customer and share with you their successes while using Onehub. We hope that each interview opens up new ways to use the service and even unlock some new ways of thinking about your current business processes.   We will feature companies from all industry types and sizes, each who are using Onehub in their own unique way.   This week I had the privilege of interviewing Jen Francis of Jen Francis Design, a graphic design firm located in Warwick, New York. Read more about how Jen is utilizing Onehub as a client extranet. &lt;/p&gt;

&lt;p&gt;&lt;img src="/images/posts/jenfrancisdesign.png" alt="Jen Franics Design"/&gt;&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Kurt Ricketts (Onehub): &lt;/b&gt;   Tell me about what you do? What is your specialty?&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Jen Francis: &lt;/b&gt;  We do everything from print to web, logos, brochures, annual reports, ads, trade show booths, websites with content management systems, and our clients range from everything from pharmaceuticals to food service.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Kurt Ricketts (Onehub): &lt;/b&gt;  How are you using Onehub today?&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Jen Francis: &lt;/b&gt;   We use Onehub as a client extranet solution to share files and collaborate with our clients.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Kurt Ricketts (Onehub): &lt;/b&gt;  Can you give me an example of a client that you work with and how you’re using Onehub with them?&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Jen Francis: &lt;/b&gt;  We’re doing a pro-bono project for the library in our town here, and they ran a photo contest for their annual calendar, we had over 200 entries and needed to pick 13 winners for the calendar.
Everybody was flooding my inbox with these large images, since we needed high res images for the calendar.  So we sent out a Hub invitation for each person that won and they were able to go in and just upload their images to the Hub so they weren’t bogging me down.&lt;/p&gt;

&lt;p&gt;We use it a lot, say we’re building a website for a client, we set up a hub and everyone on the project can upload their content to it – from the sitemap to the images.  It allows us to better collaborate with the developers, we can upload the hi-res images for them to download.  &lt;/p&gt;

&lt;p&gt;&lt;b&gt;Kurt Ricketts (Onehub): &lt;/b&gt;  Based on your evaluation of other solutions, what led you to choose Onehub?&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Jen Francis: &lt;/b&gt;It was a variety of things, the price for what you get… a lot of other solutions either didn’t give you enough storage space, workspaces, or were difficult to manage.  We looked at some solutions that were way too costly. &lt;/p&gt;

&lt;p&gt;&lt;b&gt;Kurt Ricketts (Onehub): &lt;/b&gt; What are your clients, partners, and colleagues saying about the Onehub service?&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Jen Francis: &lt;/b&gt;  Well I personally think it’s very intuitive, the people that know their way around the internet are instantly comfortable with it, or those who have the basic knowledge of how an ftp site would work. &lt;/p&gt;

&lt;p&gt;You really don’t need instructions, you just go on and immediately know what to do and it’s not hard to find your way around, everybody seems to be very pleased with it.&lt;/p&gt;

&lt;p&gt;And it’s great for our printers too, instead of having to upload to all our different printers’ FTP sites, and trying to figure out all the different ways to send files through their sites, we just upload everything to Onehub and they can grab what they need. It is also great for when I want to work from home for a day, I have all my files on Onehub.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Kurt Ricketts (Onehub): &lt;/b&gt;  Would you say you’ve been able to save time or money with Onehub? &lt;/p&gt;

&lt;p&gt;&lt;b&gt;Jen Francis:  &lt;/b&gt; Definitely, instead of building a custom extranet, or having someone build a custom ftp site for us, Onehub is much more robust compared to the amount of money we would have to have paid to develop one.   It just didn’t make any sense to reinvent the wheel.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Kurt Ricketts (Onehub): &lt;/b&gt;  Jen, Thank you for your time.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Jen Francis: &lt;/b&gt;  Thank you very much. &lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/onehubblog/~4/fRN7qqqeab8" height="1" width="1"/&gt;</description>
      <category domain="http://onehub.com/past/tags/collaboration">collaboration</category>
      <category domain="http://onehub.com/past/tags/conversations">conversations</category>
      <category domain="http://onehub.com/past/tags/customers">customers</category>
      <category domain="http://onehub.com/past/tags/extranet">extranet</category>
      <category domain="http://onehub.com/past/tags/sharefiles">sharefiles</category>
      <category domain="http://onehub.com/past/tags/with">with</category>
    <feedburner:origLink>http://onehub.com/past/2009/7/6/conversations_with_customers/</feedburner:origLink></item>
    <item>
      <title>Overhauling the Onehub Email Newsletter</title>
      <link>http://feedproxy.google.com/~r/onehubblog/~3/kO2VWWswBEM/</link>
      <pubDate>Thu, 02 Jul 2009 01:59:00 GMT</pubDate>
      <guid isPermaLink="false">http://onehub.com/past/2009/7/2/overhauling_the_onehub_email_newsletter/</guid>
      <author>mtierney@onehub.com (Michael Tierney)</author>
      <description>&lt;p&gt;One of the easiest ways for us to keep our users in the loop about what's going on here at Onehub is through our monthly email newsletter.  When I built the first version of the email, it was designed to look like an extension of our main website.  However, due to a recent design update to the site, the newsletter was now out of date and no longer met that goal.  This meant it was time for a redesign of the newsletter.&lt;/p&gt;

&lt;h3&gt;Newsletter Redesign Objectives&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Add in a new sidebar with content about the weekly product demonstrations&lt;/li&gt;
&lt;li&gt;Develop an easy-to-maintain, well tested, template&lt;/li&gt;
&lt;li&gt;Create a template that would promote a consistent layout from issue to issue&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;Pushing Pixels&lt;/h3&gt;

&lt;p&gt;The fundamental flaw of the old newsletter was that every issue required more customization than was really necessary. To offset this, I developed a new layout. This new layout has two persistent columns, a header, and a footer.  The columns house the content of the newsletter (one column for the main content, and one for the sidebar).&lt;/p&gt;

&lt;p&gt;&lt;img src="/images/posts/newsletter_layout.png" alt="Newsletter layout"/&gt;&lt;/p&gt;

&lt;p&gt;I chose to set my content width to 600px to ensure that all email clients will be able to view the newsletter without having to scroll horizontally. It also allows the content enough room to breathe.&lt;/p&gt;

&lt;h3&gt;Beware the Fold&lt;/h3&gt;

&lt;p&gt;In addition to addressing the width of the columns, I also wanted to make sure that I was using my vertical space more effectively. In email design, the concept of the "fold" is still relevant.  This concept comes from print design (specifically from newspapers), and it refers to putting the most important information on the top half of the front page - which would be face up on newsstands, for anyone passing by to see.&lt;/p&gt;

&lt;p&gt;For a long time this way adopted by web designers as well, with the "fold" being the bottom of the browser viewpane (basically, whatever was viewable without scrolling). This is really not a valid concept in web design any longer, for a wide variety of reasons, but it's still applicable in email design.&lt;/p&gt;

&lt;p&gt;Primarily because of the reduced viewpane space in email, as well as the sheer volume of email that has to be dealt with, users are much more critical of email.  While most users will at least scan a webpage, if something vital doesn't pop out in an email right away, chances are that user will move on to the next one.  Because of this, it's absolutely critical to make sure that your most important information is as visible as possible to the user.&lt;/p&gt;

&lt;p&gt;The old version of the newsletter followed the website very closely in that it had a horizontal band that housed the logo (and some information specific to that issue) as well as a strip for the navigation. So in order to make more room for the increase in content, I removed the navigation strip (which didn't have any real function anyway, there were no links) and reduced the height of the band that held the logo just a little bit. This ensured that more of our content would be visible "above the fold."&lt;/p&gt;

&lt;h3&gt;Template Building&lt;/h3&gt;

&lt;p&gt;With my design mocked up, I started to prototype the new version of the newsletter.  To get this done quickly, I built up a straight-forward HTML document. I also set up an external stylesheet, rather than trying to write my CSS in-line right off the bat, which can be time-consuming and error prone (think about adjusting the margin on multiple different links several times).&lt;/p&gt;

&lt;p&gt;Part of my template structure allowed me to isolate where the content would live. The design dictated a smaller area for the content, so content could just be inserted and it would flow properly.  This also means that maintaining the template would become simplified, since the editable area was similarly reduced. Basically, there is only one &lt;code&gt;td&lt;/code&gt; that ever substantially changes.&lt;/p&gt;

&lt;h3&gt;Test, Test, Test&lt;/h3&gt;

&lt;p&gt;The more times you can run your code through the wringer, the better.  Anyone who has browser tested a website will understand the basics of this. Like a website, email will render different in every email client.&lt;/p&gt;

&lt;p&gt;The biggest issue in email testing is the sheer volumes of clients that you have to test against. To offset this, I use a web app called &lt;a href="http://litmusapp.com"&gt;Litmus&lt;/a&gt;; it's not unlike &lt;a href="http://browsershots.org"&gt;Browsershots&lt;/a&gt; except that it also offers up the ability to test email.  This is a HUGE benefit for the wayward HTML Email Developer, since they cover all of the major email clients as well as many of the more obscure ones (if you really want to see what your email looks like in Lotus Notes, you're more than welcome to do so).&lt;/p&gt;

&lt;h3&gt;The End Result&lt;/h3&gt;

&lt;p&gt;After all of that hard work, here is the final product:&lt;/p&gt;

&lt;p&gt;&lt;img src="/images/posts/newsletter_final.png" alt="Newsletter"/&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/onehubblog/~4/kO2VWWswBEM" height="1" width="1"/&gt;</description>
      <category domain="http://onehub.com/past/tags/code">code</category>
      <category domain="http://onehub.com/past/tags/email">email</category>
      <category domain="http://onehub.com/past/tags/html">html</category>
      <category domain="http://onehub.com/past/tags/tools">tools</category>
    <feedburner:origLink>http://onehub.com/past/2009/7/2/overhauling_the_onehub_email_newsletter/</feedburner:origLink></item>
    <item>
      <title>Reinventing the Onehub Editing Interface</title>
      <link>http://feedproxy.google.com/~r/onehubblog/~3/fLbq6jqyUMg/</link>
      <pubDate>Wed, 01 Jul 2009 02:00:00 GMT</pubDate>
      <guid isPermaLink="false">http://onehub.com/past/2009/7/1/reinventing_the_onehub_editing_interface/</guid>
      <author>manderson@onehub.com (Matthew Anderson)</author>
      <description>&lt;p&gt;Evolution is one of the most important and interesting challenges of designing interfaces. As your application matures your user base grows, the number of features increases, and new use cases are discovered. Because of this, it is vital that you continue to revisit your original design decisions often. Otherwise, your simple and straightforward experiences can easily turn in to splintered, confusing processes.&lt;/p&gt;

&lt;h3&gt;A good interface is only ever as complicated as it needs to be&lt;/h3&gt;

&lt;p&gt;Recently, we redesigned the Onehub editing interface for this exact reason. When we launched Onehub last year, we started with 5 widgets. Now we have 11 (with more in the pipe). We had already begun to run out of space in our editing pop-ups (&lt;a href="http://en.wikipedia.org/wiki/Modal_window" title="What is a modal window?"&gt;modals&lt;/a&gt;) when we added recurring events to our Calendar Widget. This change resulted in a modal that was simply too big and too complicated. In order to make the feature work, we had to break our own rules and give Calendar Events a much larger editing space.&lt;/p&gt;

&lt;h3&gt;Consistency is everything&lt;/h3&gt;

&lt;p&gt;The instant that we decided to move Calendar Events outside our modal framework, I started working on a new editing interface. Why? Because once you figure out how a process works, any part of it that works differently sticks out. &lt;strong&gt;Inconsistency breaks workflow&lt;/strong&gt;. The interface had lost the consistency it once had and it was important that we get it back as soon as possible.&lt;/p&gt;

&lt;h3&gt;Making changes work for your existing users&lt;/h3&gt;

&lt;p&gt;Major interface changes can be difficult on existing users. Adding features is one thing, but you can't just change everything overnight. &lt;a href="http://www.readwriteweb.com/archives/facebooks_users_dont_like_change.php" title="Facebook's Users Don't Like Change"&gt;Sometimes&lt;/a&gt;, abrupt changes can be just as disappointing to existing users as subtracting features. The only way to minimize frustration is with usability testing - so we did a lot of it. With usability tests, we ensured that the new interface worked for both new and existing users while delivering on our design requirements.&lt;/p&gt;

&lt;h3&gt;Design challenges&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Ensure that every widget can be edited in the same manner&lt;/li&gt;
&lt;li&gt;Ensure that the interface is just as easy for new users as it is for existing users&lt;/li&gt;
&lt;li&gt;Keep the interface flexible to changes in the future&lt;/li&gt;
&lt;li&gt;Add in two highly requested features (comments and tags)&lt;/li&gt;
&lt;li&gt;Add in our own new features (next/previous navigation and edit-in-place)&lt;/li&gt;
&lt;li&gt;Maintain all existing features&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;The concept&lt;/h3&gt;

&lt;p&gt;&lt;img src="/images/posts/new_editing_architecture.png" alt="Screenshot" title="New Editing Architecture"/&gt;&lt;/p&gt;

&lt;h4&gt;Architecture&lt;/h4&gt;

&lt;h5&gt;A. Navigation&lt;/h5&gt;

&lt;p&gt;This area provides navigation that allows the user to return to where they were and cycle between items in their widgets.&lt;/p&gt;

&lt;h5&gt;B. Banner&lt;/h5&gt;

&lt;p&gt;This area contains the most important information for each item. When glancing at an item, you will spend the majority of your time in this area. This always includes Title and Description along with a piece of primary meta information (e.g. Completed, Location). The banner also contains a breadcrumb trail to keep you abreast of where you are at all times. Last but not least, the banner is host to the action buttons (e.g. Edit and Delete).&lt;/p&gt;

&lt;p&gt;The action buttons are at the top of the page, while the submit buttons (E) are at the bottom. This was intentionally done to mimic the following workflow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Click in&lt;/strong&gt; or &lt;strong&gt;navigate to&lt;/strong&gt; the desired item&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Click Edit&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Make any necessary changes from &lt;em&gt;top to bottom&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Click Save&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h5&gt;C. Tabs&lt;/h5&gt;

&lt;p&gt;A row of tabs for showing/hiding additional information without losing the primary information from the banner. By default, each item contains a &lt;strong&gt;Details&lt;/strong&gt; tab containing all of the meta information for that item. Once created, a &lt;strong&gt;Comments&lt;/strong&gt; tab is also added to the view.&lt;/p&gt;

&lt;p&gt;This area is flexible &lt;em&gt;horizontally&lt;/em&gt;. For example, Files contain a &lt;strong&gt;Versions&lt;/strong&gt; tabs which displays version control information. This will give us the ability to add more information without requiring us to change the interface again.&lt;/p&gt;

&lt;h5&gt;D. Content&lt;/h5&gt;

&lt;p&gt;This area contains less important information for each item (e.g. Priority, All Day Event) and additional features (e.g. Comments). When working on an item, you will spend the majority of your time in this area. &lt;/p&gt;

&lt;h5&gt;E. Submit Buttons&lt;/h5&gt;

&lt;p&gt;This area is where you finalize your interaction with each item. Depending on what you're doing, the submit buttons will change. For example, when creating new items you will see a &lt;strong&gt;Save and Add Another&lt;/strong&gt; button.&lt;/p&gt;

&lt;h5&gt;F. Sidebar&lt;/h5&gt;

&lt;p&gt;This area contains related information for each item. For every item in Onehub that now means &lt;strong&gt;Tags&lt;/strong&gt;; we have some cool ideas for future additions as well. This area is flexible &lt;em&gt;vertically&lt;/em&gt;. For example, Files contain a &lt;strong&gt;Preview&lt;/strong&gt; that shows a thumbnail of what the file looks like.&lt;/p&gt;

&lt;h3&gt;The end result&lt;/h3&gt;

&lt;p&gt;&lt;img src="/images/posts/new_editing_interface.png" alt="Screenshot" title="New Editing Interface"/&gt;&lt;/p&gt;

&lt;h3&gt;Measuring success&lt;/h3&gt;

&lt;p&gt;Another difficult aspect of interface design is measuring success. If you've done a horrible job, people will certainly let you know. If you've done an awesome job, you most likely won't hear about it - users will be off using your new interface. It never hurts to be sure though. So, please play around with the new editing interface. If you've got any feedback, please let us know. Leave a comment below or ping us at feedback@onehub.com.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/onehubblog/~4/fLbq6jqyUMg" height="1" width="1"/&gt;</description>
      <category domain="http://onehub.com/past/tags/design">design</category>
      <category domain="http://onehub.com/past/tags/interface">interface</category>
      <category domain="http://onehub.com/past/tags/ui">ui</category>
      <category domain="http://onehub.com/past/tags/user">user</category>
    <feedburner:origLink>http://onehub.com/past/2009/7/1/reinventing_the_onehub_editing_interface/</feedburner:origLink></item>
    <item>
      <title>Coordinating The Onehub Cloud</title>
      <link>http://feedproxy.google.com/~r/onehubblog/~3/zJuAHh4xLTk/</link>
      <pubDate>Mon, 29 Jun 2009 16:44:00 GMT</pubDate>
      <guid isPermaLink="false">http://onehub.com/past/2009/6/29/coordinating_the_onehub_cloud/</guid>
      <author>loe@onehub.com (W. Andrew Loe III)</author>
      <description>&lt;p&gt;Onehub is built on the &lt;a href="http://aws.amazon.com/"&gt;Amazon Web Services&lt;/a&gt; Platform, and as a result we are able to quickly and easily provision new servers to meet demand. We have servers for load distribution, web serving, queue processing, transcoding, administration, and other tasks. Consequently, every new server we launch needs a specific configuration in order to fulfill its functional role in our infrastructure. After careful consideration, we settled on &lt;a href="http://reductivelabs.com/products/Puppet/"&gt;Puppet&lt;/a&gt; to manage the configuration of our servers. Puppet does the majority of the heavy lifting&amp;mdash;installing packages, writing out configuration files, etc&amp;mdash;but, we still needed something to group and instruct these Puppet instances. Combining a number of open source tools, we were able to vastly simplify the management of our cloud, and bring the familiar &lt;code&gt;cap deploy&lt;/code&gt; to our infrastructure.&lt;/p&gt;

&lt;h3&gt;Assembling The Pieces&lt;/h3&gt;

&lt;p&gt;At the center of our mash-up is &lt;a href="http://github.com/adamhjk/iclassify"&gt;iClassify&lt;/a&gt;, a small &lt;a href="http://www.rubyonrails.org/"&gt;Rails&lt;/a&gt; application that registers Puppet instances as nodes in a database. iClassify provides a YAML description of an individual server to the Puppetmaster, the central Puppet server that compiles the configuration for individual nodes. In order to facilitate registering these nodes, we extracted the &lt;a href="http://github.com/onehub/icagent"&gt;icagent&lt;/a&gt; tool from iClassify and made it available as both a gem and RPM. Using &lt;a href="http://reductivelabs.com/products/facter/"&gt;Facter&lt;/a&gt;, new nodes come with a host of attributes that describe the server which enables configurations to be specifically tailored. Finally, we use &lt;a href="http://www.capify.org/"&gt;Capistrano&lt;/a&gt; to glue everything together.&lt;/p&gt;

&lt;h3&gt;Setting The Stage&lt;/h3&gt;

&lt;p&gt;Before any servers can be launched, iClassify and Puppetmaster must be up and running on servers that will be available to all other machines that will register with it; it is handy if both of these services are on the same machine. For simplicity, in our Puppetmaster configuration, we enable &lt;a href="http://reductivelabs.com/trac/Puppet/wiki/CertificatesAndSecurity"&gt;autosigning of certificates&lt;/a&gt; for all machines with *.internal addresses, but beware, this setting can be dangerous if your machines need to communicate over the internet as opposed to a private LAN.&lt;/p&gt;

&lt;p&gt;The Puppetmaster needs to be &lt;a href="http://reductivelabs.com/trac/Puppet/wiki/ExternalNodes"&gt;configured to use iClassify as its external node terminus&lt;/a&gt;. Once setup, Puppetmaster will query iClassify for the classes and variables (tags and attributes respectively) to compile as the configuration (catalog) for each individual node:&lt;/p&gt;

&lt;script src="http://gist.github.com/136253.js"&gt;&lt;/script&gt;

&lt;h3&gt;Starting The Show&lt;/h3&gt;

&lt;p&gt;At the base of all new servers is our 'Stem Cell' image. This &lt;a href="http://developer.amazonwebservices.com/connect/kbcategory.jspa?categoryID=171"&gt;Amazon Machine Image&lt;/a&gt; has only two packages beyond a basic Linux Distribution: &lt;a href="http://reductivelabs.com/products/Puppet/"&gt;Puppet&lt;/a&gt; and &lt;a href="http://github.com/onehub/icagent"&gt;icagent&lt;/a&gt;, at boot it is configured to run both of these.&lt;/p&gt;

&lt;p&gt;First, icagent:
&lt;code&gt;$ /usr/share/icagent/bin/icagent -d /usr/share/icagent/recipes/&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This command run icagent's recipes against the server and then submit all of the gathered information to the iClassify server along with a UUID.&lt;/p&gt;

&lt;p&gt;Second, Puppet:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;$ /etc/init.d/puppet start&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The newly registered node will default to receiving the base Puppet configuration which includes a few handy packages and Puppet modules we use on all of our machines:&lt;/p&gt;

&lt;script src="http://gist.github.com/136241.js"&gt;&lt;/script&gt;

&lt;h3&gt;Issuing Orders&lt;/h3&gt;

&lt;p&gt;&lt;img src="/images/posts/new_node.jpg" alt="New Node"/&gt;&lt;/p&gt;

&lt;p&gt;With our new machine up and running, we need to assign it a role. On the iClassify server the newly registered node will show up at the top of the server listing. It is a good idea to edit it to have a more descriptive name, and then add the tags (that will map to puppet classes) that define its role. In addition to the standard attributes, we will add a custom puppet_env to each node that enables us to correlate servers with our application's environments (i.e. master, staging, or production). This is critically important as it lets us selectively disable cron tasks and other jobs that we would not want run against test data (like billing!).&lt;/p&gt;

&lt;p&gt;&lt;img src="/images/posts/puppet_env.jpg" alt="Puppet Env"/&gt;&lt;/p&gt;

&lt;p&gt;Now that the machine is tagged it will eventually run all of the specified Puppet recipes and be ready to receive the 'live' tag. However we're going to take this a bit further with some nifty Capistrano scripting.&lt;/p&gt;

&lt;h3&gt;Stringing Up The Puppets&lt;/h3&gt;

&lt;p&gt;Using the &lt;a href="http://github.com/loe/iclassify-interface"&gt;iclassify-interface&lt;/a&gt; gem we can take a simple Capistrano deploy script and adapt it to coordinate our Puppet instances.&lt;/p&gt;

&lt;script src="http://gist.github.com/136597.js"&gt;&lt;/script&gt;

&lt;p&gt;This example uses a standard Capistrano deploy scheme. Please note that the deploy path from Capistrano should match the module location for the different Puppet environments, our Puppet instances run in the same categories as our application (i.e. puppet&lt;em&gt;env == rails&lt;/em&gt;env).&lt;/p&gt;

&lt;script src="http://gist.github.com/136601.js"&gt;&lt;/script&gt;

&lt;p&gt;Now, deploying new puppet recipes is as simple as &lt;code&gt;$ cap staging deploy&lt;/code&gt;, the code will be checked out of version control and Puppet will be restarted on all the servers in that environment. We can also use this same trick in our application's deploy script.&lt;/p&gt;

&lt;script src="http://gist.github.com/136604.js"&gt;&lt;/script&gt;

&lt;p&gt;When it comes time to deploy the application the list of servers is dynamically generated, saving us from having to tediously edit the deploy.rb file.&lt;/p&gt;

&lt;h3&gt;The Final Act&lt;/h3&gt;

&lt;p&gt;While the simplified deployment of our configurations is a huge feature in itself, the true beauty comes in making 'smart' Puppet recipes. For example, we have an instance of &lt;a href="http://nginx.org/"&gt;nginx&lt;/a&gt; that functions as a load-balancer: it proxies to our web servers. Nginx needs to know which 'webservers' are available for it to proxy to at any given time. Inside our nginx manifest we can define a search for the Puppetmaster to perform against iClassify to populate this list:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;$nginx_lb_search = "puppet_env:${puppet_env} tag:webserver tag:live"&lt;/code&gt;&lt;/p&gt;

&lt;script src="http://gist.github.com/136605.js"&gt;&lt;/script&gt;

&lt;p&gt;We simply (1) start a new server, (2) tag it, (3) deploy our application, (4) tag it live, (5) then deploy Puppet. The first few times there were a few kinks (hey that's what staging environments are for!), but with all of the pieces strung together, we have automated an amazing amount of tedious and error-prone work. A new webserver will boot, install all of our base packages, receive the application and start its processes, then get automatically included in to the load-balancers round-robin list.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/onehubblog/~4/zJuAHh4xLTk" height="1" width="1"/&gt;</description>
      <category domain="http://onehub.com/past/tags/IT">IT</category>
      <category domain="http://onehub.com/past/tags/aws">aws</category>
      <category domain="http://onehub.com/past/tags/operations">operations</category>
      <category domain="http://onehub.com/past/tags/puppet">puppet</category>
      <category domain="http://onehub.com/past/tags/rails">rails</category>
      <category domain="http://onehub.com/past/tags/ruby">ruby</category>
      <category domain="http://onehub.com/past/tags/web20">web20</category>
    <feedburner:origLink>http://onehub.com/past/2009/6/29/coordinating_the_onehub_cloud/</feedburner:origLink></item>
    <item>
      <title>Tips &amp; Tricks: Subscribing to a Onehub Calendar Widget</title>
      <link>http://feedproxy.google.com/~r/onehubblog/~3/Mox9OyTK2LI/</link>
      <pubDate>Mon, 29 Jun 2009 01:00:00 GMT</pubDate>
      <guid isPermaLink="false">http://onehub.com/past/2009/6/29/subscribing_to_onehub_calendars/</guid>
      <author>bcaplan@onehub.com (Brandon Caplan)</author>
      <description>&lt;p&gt;Did you know that you can &lt;strong&gt;subscribe to a Onehub calendar widget&lt;/strong&gt;? This works with software that supports internet calendars in the &lt;a href="http://en.wikipedia.org/wiki/ICalendar" title="Learn about iCalendar on Wikipedia"&gt;iCalendar&lt;/a&gt; format. It's a great way to keep up to date with events stored in your Hub, along side calendars you have outside of Onehub.&lt;/p&gt;

&lt;h3&gt;Enabling Calendar Subscriptions&lt;/h3&gt;

&lt;p&gt;&lt;img src="http://assets1.onehub.com/images/icons/feed.png" alt="Subscribe Settings Button" title="Subscribe Settings Button"/&gt;While in Design Mode, click the orange icon that appears in the upper right of the calendar widget you are enabling subscriptions for.&lt;/p&gt;

&lt;p&gt;&lt;img src="/images/posts/subscribe_callout.png" alt="Callout" title="Subscribe Settings Location"/&gt;&lt;/p&gt;

&lt;div style="clear:both"&gt;&lt;/div&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Copy&lt;/strong&gt; the ics URL, then check the box to publish the iCal feed and save.&lt;/p&gt;

&lt;p&gt;&lt;img src="/images/posts/subscribe_modal.png" alt="Modal" title="Subscribe Settings"/&gt;&lt;/p&gt;

&lt;div style="clear:both"&gt;&lt;/div&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h3&gt;Subscribing to an Internet Calendar&lt;/h3&gt;

&lt;blockquote&gt;
    &lt;h4&gt;Check out this screencast that guides you through the entire process: &lt;a href="http://onehub.com/support/screencasts/subscribing_to_calendars" title="How to Subscribe to a Onehub Calendar" target="_blank"&gt;How&amp;nbsp;to&amp;nbsp;Subscribe&amp;nbsp;to&amp;nbsp;a&amp;nbsp;Onehub&amp;nbsp;Calendar »&lt;/a&gt;&lt;hr&gt;&lt;/h4&gt;
&lt;/blockquote&gt;

&lt;p&gt;Note that not all calendaring applications support internet calendar subscriptions. We will be covering two of the more popular ones that do, &lt;a href="#apple_ical" title="Jump to iCal setup"&gt;Apple iCal&lt;/a&gt; and &lt;a href="#microsoft_outlook" title="Jump to Outlook setup"&gt;Microsoft Outlook 2007&lt;/a&gt;.&lt;/p&gt;

&lt;h5 id="apple_ical"&gt;Apple iCal&lt;/h5&gt;

&lt;p&gt;Select "&lt;strong&gt;Subscribe...&lt;/strong&gt;" from the "&lt;strong&gt;Calendar&lt;/strong&gt;" menu. Paste the calendar URL and click "&lt;strong&gt;Subscribe&lt;/strong&gt;".&lt;/p&gt;

&lt;p&gt;&lt;img src="/images/posts/ical_subscribe.png" alt="iCal Subscribe" title="Subscribing in iCal"/&gt;&lt;/p&gt;

&lt;div style="clear:both"&gt;&lt;/div&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;Now choose a name (enter a description if you would like.) You should also select an auto-refresh interval so the calendar will self-update. Just hit &lt;em&gt;*OK&lt;/em&gt;* and you're done.&lt;/p&gt;

&lt;p&gt;&lt;img src="/images/posts/ical_subscribe_2.png" alt="iCal Subscribe" title="Subscribing in iCal"/&gt;&lt;/p&gt;

&lt;div style="clear:both"&gt;&lt;/div&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h5 id="microsoft_outlook"&gt;Microsoft Outlook 2007&lt;/h5&gt;

&lt;p&gt;Select "&lt;strong&gt;Account Settings...&lt;/strong&gt;" from the "&lt;strong&gt;Tools&lt;/strong&gt;" menu. From here click the "&lt;strong&gt;Internet Calendars&lt;/strong&gt;" tab and click "&lt;strong&gt;New...&lt;/strong&gt;".&lt;/p&gt;

&lt;p&gt;&lt;img src="/images/posts/outlook_subscribe.png" alt="Outlook Subscribe" title="Subscribing in Outlook"/&gt;&lt;/p&gt;

&lt;div style="clear:both"&gt;&lt;/div&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;Now paste the calendar URL and click "&lt;strong&gt;Add&lt;/strong&gt;".&lt;/p&gt;

&lt;p&gt;&lt;img src="/images/posts/outlook_subscribe_2.png" alt="Outlook Subscribe" title="Subscribing in Outlook"/&gt;&lt;/p&gt;

&lt;div style="clear:both"&gt;&lt;/div&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;Next choose a name, enter a description if you would like, and hit "OK".&lt;/p&gt;

&lt;p&gt;&lt;img src="/images/posts/outlook_subscribe_3.png" alt="Outlook Subscribe" title="Subscribing in Outlook"/&gt;&lt;/p&gt;

&lt;div style="clear:both"&gt;&lt;/div&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;The calendar will be listed under the &lt;em&gt;Other Calendars&lt;/em&gt; section on the left sidebar. Check the box to show the calendar.&lt;/p&gt;

&lt;p&gt;&lt;img src="/images/posts/outlook_subscribe_4.png" alt="Outlook Subscribe" title="Subscribing in Outlook"/&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/onehubblog/~4/Mox9OyTK2LI" height="1" width="1"/&gt;</description>
      <category domain="http://onehub.com/past/tags/calendar">calendar</category>
      <category domain="http://onehub.com/past/tags/feature">feature</category>
      <category domain="http://onehub.com/past/tags/howto">howto</category>
      <category domain="http://onehub.com/past/tags/screencast">screencast</category>
      <category domain="http://onehub.com/past/tags/tips">tips</category>
    <feedburner:origLink>http://onehub.com/past/2009/6/29/subscribing_to_onehub_calendars/</feedburner:origLink></item>
    <item>
      <title>Decorating ActiveRecord Accessor Methods</title>
      <link>http://feedproxy.google.com/~r/onehubblog/~3/3QJ1EepCjMA/</link>
      <pubDate>Thu, 25 Jun 2009 09:05:00 GMT</pubDate>
      <guid isPermaLink="false">http://onehub.com/past/2009/6/25/decorating_activerecord_accessor_methods/</guid>
      <author>lcaplan@onehub.com (Leigh Caplan)</author>
      <description>&lt;p&gt;Recently, I was faced with a quandary. I needed to impart some of our ActiveRecord accessor methods with special functionality, but I also needed to maintain the default behavior for those fields. This was done to support a new input type which we added to the venerable &lt;a href="http://github.com/timcharper/calendar_date_select/tree/master"&gt;CalendarDateSelect plugin&lt;/a&gt; which allows a user to enter a date, time, and meridian, all in separate fields. &lt;/p&gt;

&lt;p&gt;&lt;img src="http://onehub.com/images/posts/calendar_date_select_fields.png" alt="CalendarDateSelect Fields" title="CalendarDateSelect Fields"/&gt;&lt;/p&gt;

&lt;p&gt;Though I could parse each of the fields in the controller and pass the resulting time to the model, this is messy, error-prone, and unnecessary.&lt;/p&gt;

&lt;p style="clear:left;"&gt;What I really want to be able to do is something like this:&lt;/p&gt;

&lt;script src="http://gist.github.com/136036.js"&gt;&lt;/script&gt;

&lt;p&gt;My first thought was to simply override the accessor methods like so:&lt;/p&gt;

&lt;script src="http://gist.github.com/136038.js"&gt;&lt;/script&gt;

&lt;p&gt;However, as I alluded to before, there's a problem with this approach. Rails already does some magic behind the scenes to ensure that time values stored to your ActiveRecord objects are automatically cast to and from UTC. The code above will completely overwrite these default accessors, and time zone casting will cease to function for those fields. What I really want to do is &lt;a href="http://en.wikipedia.org/wiki/Decorator_pattern"&gt;decorate&lt;/a&gt; these accessor methods, so that I can add to the existing functionality without completely circumventing it.&lt;/p&gt;

&lt;p&gt;One way we might think about accomplishing this would be to redefine our accessor method and add behavior to it using &lt;a href="http://weblog.rubyonrails.org/2006/4/26/new-in-rails-module-alias_method_chain"&gt;alias_method_chain&lt;/a&gt;.&lt;/p&gt;

&lt;script src="http://gist.github.com/136040.js"&gt;&lt;/script&gt;

&lt;p&gt;Try that, though, and you'll get this fun little error&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;`alias_method':NameError: undefined method `start_time=' for class `CalendarEvent'
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It doesn't work because there is no original &lt;code&gt;start_time=&lt;/code&gt; method to alias; ActiveRecord creates those methods only when the class is loaded, because it has to read the columns from the database at runtime. So, how do you decorate methods which haven't even been created yet? Well, it turns out you have to decorate &lt;code&gt;ActiveRecord::Base.define_attribute_methods&lt;/code&gt;, which is responsible for creating accessor methods for each model.&lt;/p&gt;

&lt;p&gt;There are a couple of ways you can go about doing this, depending on your goals. I wanted to be able to use these enhanced date/time inputs with any model in our application and trigger it from an initializer, so my approach uses &lt;code&gt;alias_method_chain&lt;/code&gt; to modify &lt;code&gt;ActiveRecord::Base&lt;/code&gt; directly.&lt;/p&gt;

&lt;script src="http://gist.github.com/136043.js"&gt;&lt;/script&gt;

&lt;p&gt;Ok, so what exactly does that code do? First, we call the original version of &lt;code&gt;define_attribute_methods&lt;/code&gt;. If that returns true, then we know that the attribute accessor methods have been generated. We loop over each column in the model, checking to see whether it stores either a datetime or a timestamp. If so, this is an accessor we're interested in decorating. We do a quick check to make sure we haven't already decorated the accessor (which can bite us in development mode), then we redefine the accessor &lt;em&gt;with&lt;/em&gt; time parsing, and use the magic of &lt;code&gt;alias_method_chain&lt;/code&gt; to alias our original method but still maintain a reference to it.&lt;/p&gt;

&lt;p&gt;At the bottom, we use the &lt;code&gt;Module#included&lt;/code&gt; callback to add the methods contained in our module, and we once again use &lt;code&gt;alias_method_chain&lt;/code&gt; to redefine our version of &lt;code&gt;define_attribute_methods&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then, in the initializer, you would write something like&lt;/p&gt;

&lt;script src="http://gist.github.com/136052.js"&gt;&lt;/script&gt;

&lt;p&gt;At this point, any time-based field in any of your models will be able to accept a hash as a value, and properly parse it. &lt;/p&gt;

&lt;p&gt;This works well, but one thing I don't like about this solution is its rampant use of &lt;code&gt;alias_method_chain&lt;/code&gt;. There has been some &lt;a href="http://yehudakatz.com/2009/03/06/alias_method_chain-in-models/"&gt;debate&lt;/a&gt; in the Rails community over its validity as a design choice, though there really are no other options if you need to modify all subclasses of &lt;code&gt;ActiveRecord::Base&lt;/code&gt; (as you would to support a plugin like CalendarDateSelect).&lt;/p&gt;

&lt;h3&gt;Another Approach&lt;/h3&gt;

&lt;p&gt;If modifying every subclass isn't a design requirement, you can clean up the code significantly:&lt;/p&gt;

&lt;script src="http://gist.github.com/136054.js"&gt;&lt;/script&gt;

&lt;p&gt;Notice here we're just defining &lt;code&gt;define_attribute_methods&lt;/code&gt; and calling &lt;code&gt;super&lt;/code&gt; when we want access to the previous implementation. The same goes for decorating the actual accessor methods. &lt;/p&gt;

&lt;p&gt;The downside of this approach is that you have to explicitly include the module in your ActiveRecord models:&lt;/p&gt;

&lt;script src="http://gist.github.com/136055.js"&gt;&lt;/script&gt;

&lt;p&gt;As with any decision, the approach you take will depend on your requirements, and tradeoffs you're willing to make between explicitness and ease of use.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/onehubblog/~4/3QJ1EepCjMA" height="1" width="1"/&gt;</description>
      <category domain="http://onehub.com/past/tags/activerecord">activerecord</category>
      <category domain="http://onehub.com/past/tags/decorator">decorator</category>
      <category domain="http://onehub.com/past/tags/design">design</category>
      <category domain="http://onehub.com/past/tags/patterns">patterns</category>
      <category domain="http://onehub.com/past/tags/rails">rails</category>
      <category domain="http://onehub.com/past/tags/ruby">ruby</category>
    <feedburner:origLink>http://onehub.com/past/2009/6/25/decorating_activerecord_accessor_methods/</feedburner:origLink></item>
    <item>
      <title>Sharing and controlling access to business videos</title>
      <link>http://feedproxy.google.com/~r/onehubblog/~3/e4bZvBcJop8/</link>
      <pubDate>Tue, 23 Jun 2009 09:03:00 GMT</pubDate>
      <guid isPermaLink="false">http://onehub.com/past/2009/6/23/sharing_and_controlling_access_to/</guid>
      <author>lmoudy@onehub.com (Laurel Moudy)</author>
      <description>&lt;p&gt;&lt;img src="/images/posts/business_video.png" alt="Onehub Video Widget"/&gt;Businesses need to securely share video files for many reasons.  For sales purposes, for presentations to customers or clients, or for educational or training purposes.  &lt;/p&gt;

&lt;p&gt;The hard part comes when you need to share the video outside your corporate firewall and still control who has access to the videos. Let’s face it, not all video belongs on YouTube.   For companies like marketing agencies that share draft commercials with their clients, retailers that share instructional videos with distribution partners, manufacturers that share sales videos with potential customers, and non-profits that share educational videos with large private audiences — providing the ability to securely share videos has been a burden on IT resources until now.  &lt;/p&gt;

&lt;h3&gt;How Onehub helps you securely share video outside your firewall&lt;/h3&gt;

&lt;p&gt;Onehub makes it easy to create customized Hubs for securely viewing videos.  Simply add a video widget to your Hub and upload your video.  You choose who has access by inviting them to be a member of your Hub. 
Onehub allows you to upload source video files directly to Onehub or embed videos from popular video sharing websites. Onehub accepts videos in all major formats including .WMV, .AVI, .MOV, MPEG, .MP4, DivX, .FLV, and .OGG as well as embed code from YouTube, Vimeo, or Viddler. &lt;/p&gt;

&lt;p&gt;Do you need to train a team distributed around country or globe?  Do you need a customized solution for presenting work to your clients?  &lt;a href="http://onehub-inc.onehub.com/securely-sharing-videos"&gt;Take a look at this sample Hub&lt;/a&gt; to find out how you might share your videos on Onehub.  &lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/onehubblog/~4/e4bZvBcJop8" height="1" width="1"/&gt;</description>
      <category domain="http://onehub.com/past/tags/Sharing">Sharing</category>
      <category domain="http://onehub.com/past/tags/collaboration">collaboration</category>
      <category domain="http://onehub.com/past/tags/video">video</category>
    <feedburner:origLink>http://onehub.com/past/2009/6/23/sharing_and_controlling_access_to/</feedburner:origLink></item>
    <item>
      <title>Sending Large Files with Onehub</title>
      <link>http://feedproxy.google.com/~r/onehubblog/~3/cahF-2XgD78/</link>
      <pubDate>Wed, 17 Jun 2009 07:20:00 GMT</pubDate>
      <guid isPermaLink="false">http://onehub.com/past/2009/6/17/sending_large_files/</guid>
      <author>cmount@onehub.com (Charles Mount)</author>
      <description>&lt;p&gt;&lt;strong&gt;QuickHubs provide a simple way to send or receive files from other people and are now one of our most popular features&lt;/strong&gt;. You can send or receive multiple files at once and setting up a QuickHub is done through a simple, one step wizard.&lt;/p&gt;

&lt;p&gt;There are many benefits to transferring files with Onehub instead of email attachments or physical media.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Send really big files.&lt;/strong&gt; You can send or receive files up to 2 GB in size and can send multiple files at once.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Save Money.&lt;/strong&gt; Using Onehub is much cheaper than burning a DVD to deliver via FedEx or a local courier.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Track what is happening.&lt;/strong&gt; With Onehub, you receive notifications via email when files are uploaded or downloaded.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Make Your Customers Happy.&lt;/strong&gt; Files can be sent in minutes instead of waiting for a DVD via mail. &lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;Click Send Files from the User Home to Start&lt;/h3&gt;

&lt;p&gt;&lt;img src="/images/posts/quickhubs.gif" alt="Send or Receive Files"/&gt;&lt;/p&gt;

&lt;h3 class="clearleft"&gt;The next step is a short wizard to select recipients and the files to send.&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Personal Information. Enter the email addresses of one or more people and a short description of the files. The description will be sent in the notification email and will be shown on the download page.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Optional Information. Set a Hub expiration date, choose to subscribe to activity for the Hub or to make the Hub public.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Files to Send. Select files from your computer or network. You can send hundreds of files at once and each individual file can be up to 2 GB in size.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src="/images/posts/sendhub.gif" alt="Create a SendHub"/&gt;&lt;/p&gt;

&lt;p class="clearleft"&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hub Automatically Expires&lt;/strong&gt; (optional) - If set, the Hub will no longer be available after the Expiration date. You can disable the expiration and the Hub will remain active until you manually deactivate it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Notify Me of Activity&lt;/strong&gt; (optional) - If set, you will receive notification emails of activity in the Hub including when users download the files you are sending them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Make this Hub Public&lt;/strong&gt; (optional) - If set, users are not required to setup a Onehub password and login to download the files. This is not recommended for sensitive or confidential information.&lt;/p&gt;

&lt;h3 class="clearleft"&gt;Your Customer's Experience&lt;/h3&gt;

&lt;p&gt;The recipients receive an email from you with a link to download the files from Onehub. Take a look at a &lt;a href="http://charles.onehub.com/sendhub-for-coffeehouseco-on-jun-17"&gt;sample SendHub to see what your clients will experience&lt;/a&gt;.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/onehubblog/~4/cahF-2XgD78" height="1" width="1"/&gt;</description>
      <category domain="http://onehub.com/past/tags/files">files</category>
      <category domain="http://onehub.com/past/tags/share">share</category>
      <category domain="http://onehub.com/past/tags/tranfer">tranfer</category>
    <feedburner:origLink>http://onehub.com/past/2009/6/17/sending_large_files/</feedburner:origLink></item>
  </channel>
</rss>
