<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>GoZuus Blog &#187; Development</title>
	<atom:link href="http://www.gozuus.com/blog/category/dev/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.gozuus.com/blog</link>
	<description>on software, business, and greek life...</description>
	<lastBuildDate>Tue, 21 Sep 2010 15:30:32 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.4</generator>
		<item>
		<title>Our Git Deployment Workflow</title>
		<link>http://www.gozuus.com/blog/2009/07/our-git-deployment-workflow/</link>
		<comments>http://www.gozuus.com/blog/2009/07/our-git-deployment-workflow/#comments</comments>
		<pubDate>Wed, 15 Jul 2009 15:56:39 +0000</pubDate>
		<dc:creator>elliott</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[deployment]]></category>
		<category><![CDATA[git]]></category>
		<category><![CDATA[production]]></category>
		<category><![CDATA[Rails]]></category>
		<category><![CDATA[staging]]></category>

		<guid isPermaLink="false">http://www.gozuus.com/blog/?p=103</guid>
		<description><![CDATA[Like most Rails developers, we&#8217;re using git for source control. However, our first attempt at setting up a deployment system made a huge mess so we went looking for a better way. I liked the ideas set forth by Bryan Helmkamp, but there was one flaw (mentioned by Jesse Andrews in the comments) I just [...]]]></description>
			<content:encoded><![CDATA[<p>Like most Rails developers, we&#8217;re using git for source control.  However, our first attempt at setting up a deployment system made a huge mess so we went looking for a better way.<br />
<span id="more-103"></span><br />
I liked the <a href="http://www.brynary.com/2008/8/3/our-git-deployment-workflow">ideas</a> set forth by Bryan Helmkamp, but there was one flaw (mentioned by Jesse Andrews in the comments) I just couldn&#8217;t get past.  It seemed entirely too easy for Kevin or I to erase working commits the other had made in production code.</p>
<p>I took Bryan&#8217;s rake task and built upon it to help prevent this sort of issue.  Here&#8217;s the goals:</p>
<ul>
<li>All code deployed to either the staging or production environments must be in <a href="http://github.com">GitHub</a>.  Using <a href="http://www.capify.org">Capistrano</a> for deployment helps enforce this, in that no code from a local repository can ever get deployed to the server.</li>
<li>All changes start by branching off the current production head.  This ensures we always have a stable place to start working, regardless of whether its for a quick bugfix or a month-long feature update.</li>
<li>Any developer can deploy their current development branch to staging at any time.  Remember, their development branch is based off of something that was a stable production release at one time.  However, updates may have been made to production since then that are not included in their development branch.  We consider this to be okay.</li>
<li>Any developer can merge their current development branch back into production.  However, their merge must be a direct fast-forward (which guarantees no conflicts).  If changes have been committed to production since their branch was created, they must first rebase their branch to start from the current production head.</li>
<li>All code merged into production is ready to be deployed at any time.  Sounds obvious, but our first strategy failed on this because commits made to staging weren&#8217;t always ready to go to production in that order.</li>
<li>Lastly, all this needs to be automatic, safe, and simple.</li>
</ul>
<p>In our solution, we treat production as a real branch and merge code into it.  This prevents commits from being lost by rewriting history.  However, we borrowed the idea of creating staging as a branch, but treating it like a tag.  I like this because it lets any developer tear down staging to use themselves at any time.  By not making commits to staging, I never have to worry about my buggy code affecting someone else&#8217;s testing.  (Or more realistically, someone else&#8217;s buggy code screwing up my testing.)</p>
<p>So, without further ado, here&#8217;s the rake tasks:</p>
<pre class="brush: ruby; title: ;">class GitCommands

  # Shows a diff of the current production and staging branches
  # (ie, what would change if staging was deployed to production)
  def self.diff_staging
    `git fetch`
    puts `git diff origin/production origin/staging`
  end

  # Pushes the specified branch to the remote origin, then
  # resets the staging branch to be a copy of that branch.
  def self.push_staging(branch_name)
    verify_working_directory_clean
    `git push origin #{branch_name}`
    `git fetch`
    `git branch -f staging origin/staging`
    `git checkout staging`
    `git reset --hard origin/#{branch_name}`
    `git push -f origin staging`
    `git checkout #{branch_name}`
    `git branch -D staging`

    `cap deploy`

    unless is_fast_forward_of_production(branch_name)
      puts(&amp;quot;nnWARNING: NON-FAST-FORWARD!n&amp;quot;+
        &amp;quot;The production branch has been updated since this branchn&amp;quot;+
        &amp;quot;was created (or you've made more than 100 commits). Stagingn&amp;quot;+
        &amp;quot;has been updated, but you'll need to run the followingn&amp;quot;+
        &amp;quot;before attempting to push to production.nn&amp;quot;+
        &amp;quot;  git rebase -i origin/productionn&amp;quot;+
        &amp;quot;  git push -f origin #{branch_name}nn&amp;quot;+
        &amp;quot;You may want to do this now before testing on staging.nn&amp;quot;
      )
    end
  end

  # Pushes the specified branch to the remote origin, then
  # merges its changes into the production branch.
  def self.push_production(branch_name)
    verify_working_directory_clean
    `git push origin #{branch_name}`
    `git fetch`

    verify_fast_forward_of_production(branch_name)

    # Update master to be the previous production
    `git checkout master`
    `git reset --hard origin/production`
    `git push origin master`

    # Merge the current branch into production
    `git branch -f production origin/production`
    `git checkout production`
    `git merge #{branch_name}`
    `git push origin production`
    `git checkout #{branch_name}`
    `git branch -D production`

    puts(&amp;quot;nnYour branch has been merged into production, but hasn&amp;quot;+
      &amp;quot;NOT YET been deployed.  Call the appropriate capistranon&amp;quot;+
      &amp;quot;tasks to deploy code, migrate the database, then restartn&amp;quot;+
      &amp;quot;passenger to make your changes live.nn&amp;quot;+
      &amp;quot;  cap production deploy:updaten&amp;quot;+
      &amp;quot;  cap production deploy:migraten&amp;quot;+
      &amp;quot;  cap production deploy:restartnn&amp;quot;
    )
  end

  # Creates a new branch using the current production branch
  # as a base.
  def self.create_branch(branch_name)
    verify_working_directory_clean

    `git fetch`
    `git branch -f production origin/production`
    `git checkout production`
    `git branch #{branch_name}`
    `git checkout #{branch_name}`
    `git push origin #{branch_name}`
  end

  # Creates a new local branch tracking a remote branch
  def self.track_branch(branch_name)
    verify_working_directory_clean

    `git fetch`
    `git branch -f #{branch_name} origin/#{branch_name}`
    `git checkout #{branch_name}`
  end

  # Creates a new branch using the current production branch
  # as a base.
  def self.destroy_branch(branch_name)
    verify_working_directory_clean

    `git fetch`
    `git checkout master`
    `git branch -D #{branch_name}`
    `git push origin :#{branch_name}`
  end

  # Determines the name of the current working branch.
  def self.get_branch_name
    branch = `git status`.scan(/A# On branch (.+)$/)
    raise &amp;quot;Could not determine which branch to use.  Perhaps use BRANCH=?&amp;quot; if branch.blank? || branch.first.blank? || branch.first.first.blank?
    return branch.first.first
  end

protected

  # Ensures that there are no pending changes in the working directory.
  # This was edited to allow untracked (and unignored) files, but that
  # may need to be removed for 100% functionality.
  def self.verify_working_directory_clean
    output = `git status`
    return if output =~ /working directory clean/ || output =~ /nothing added to commit but untracked files present/
    raise &amp;quot;Must have clean working directory&amp;quot;
  end

  # Verifies that the specified branch is a direct fast-forward of
  # the production branch.  This ensures that merges will be smooth
  # and conflict-free when running the git:push:production task.
  def self.verify_fast_forward_of_production(branch_name)
    unless is_fast_forward_of_production(branch_name)
      raise(
        &amp;quot;Branch #{branch_name} is not a fast-forward of the production branchn&amp;quot;+
        &amp;quot;(or there's more than 100 commits since the branch).n&amp;quot;+
        &amp;quot;Run:   &amp;quot;git rebase -i origin/production&amp;quot; and resolve all conflicts,n&amp;quot;+
        &amp;quot;then:  &amp;quot;git push -f origin #{branch_name}&amp;quot; to fix this.&amp;quot;
      )
    end
    return
  end

  # Determines if the specified branch is a direct fast-forward of
  # the production branch.
  def self.is_fast_forward_of_production(branch_name)
    production_last_hex = `git log -1 origin/production`.scan(/Acommit ([da-f]{40})$/).first.first
    branch_log = `git log -100 --pretty=oneline #{branch_name}`.scan(/^[da-f]{40}/)
    return branch_log.include?(production_last_hex)
  end

end

namespace :git do

  namespace :push do

    desc &amp;quot;Push the current branch to origin/staging for testing&amp;quot;
    task :staging do
      branch_name = ENV['BRANCH'] || GitCommands.get_branch_name
      GitCommands.push_staging(branch_name)
    end

    desc &amp;quot;Safely merge the current branch back into origin/production&amp;quot;
    task :production do # =&amp;gt; ['diff:staging'] do
      branch_name = ENV['BRANCH'] || GitCommands.get_branch_name
      GitCommands.push_production(branch_name)
    end

  end

  desc &amp;quot;Show the differences between the origin/staging branch and the origin/production branch&amp;quot;
  task :diff do
    GitCommands.diff_staging
  end

  namespace :branch do

    desc &amp;quot;Create a branch for a feature or bug fix. Specify BRANCH=name&amp;quot;
    task :create do
      branch_name = ENV['BRANCH']
      raise &amp;quot;You must specify a branch name using BRANCH=name&amp;quot; if branch_name.blank?
      GitCommands.create_branch(branch_name)
    end

    desc &amp;quot;Creates a local branch tracking an already-created remote branch.  Specify BRANCH=name&amp;quot;
    task :track do
      branch_name = ENV['BRANCH']
      raise &amp;quot;You must specify a branch name using BRANCH=name&amp;quot; if branch_name.blank?
      GitCommands.track_branch(branch_name)
    end

    desc &amp;quot;Removes a remote branch from the origin.  Specify BRANCH=name&amp;quot;
    task :destroy do
      branch_name = ENV['BRANCH']
      raise &amp;quot;You must specify a branch name using BRANCH=name&amp;quot; if branch_name.blank?
      GitCommands.destroy_branch(branch_name)
    end

  end

end</pre>
<p>We&#8217;ve been using this for the past several weeks and it&#8217;s been working great.  So, did we do any better?  Anyone see any flaws or have any other ideas?</p>
]]></content:encoded>
			<wfw:commentRss>http://www.gozuus.com/blog/2009/07/our-git-deployment-workflow/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Caching Images Using Attachment_Fu and Amazon S3</title>
		<link>http://www.gozuus.com/blog/2009/03/caching-images-using-attachment_fu-and-amazon-s3/</link>
		<comments>http://www.gozuus.com/blog/2009/03/caching-images-using-attachment_fu-and-amazon-s3/#comments</comments>
		<pubDate>Thu, 26 Mar 2009 15:45:37 +0000</pubDate>
		<dc:creator>kevin</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[attachment_fu]]></category>
		<category><![CDATA[Rails]]></category>
		<category><![CDATA[s3]]></category>

		<guid isPermaLink="false">http://blog.gozuus.com/?p=57</guid>
		<description><![CDATA[Caching images improves the user experience and reduces S3 costs.  It improves the user&#8217;s experience because web pages load quicker and it reduces S3 costs since you have fewer transfers. When using the attachment_fu plugin and the AWS::S3 gem, there are two things we need to do to cache images using attachment_fu and Amazon S3. [...]]]></description>
			<content:encoded><![CDATA[<p>Caching images improves the user experience and reduces S3 costs.  It improves the user&#8217;s experience because web pages load quicker and it reduces S3 costs since you have fewer transfers.<br />
<span id="more-57"></span><br />
When using the attachment_fu plugin and the AWS::S3 gem, there are two things we need to do to cache images using attachment_fu and Amazon S3.</p>
<ol>
<li> Set the Cache-Control http header.</li>
<li>Make S3 urls expire at a consistent time.</li>
</ol>
<p>Setting the Cache-Control header tells our web browser that the cached image is valid and that it doesn&#8217;t need to check for an updated copy of the image on the server.  Making the url expire at a consistent time gives our images that are the same a consistent url, so our browser doesn&#8217;t get confused and think they are different images.  For example, image.jpg?expires=100 appears to our browser as a different image than image.jpg?expires=101.</p>
<h3>Set Cache-Control Header in AWS S3</h3>
<p>The technique we use for setting the Cache-Control header was originally posted in <a href="http://keakaj.com/wisdom/2007/07/27/cache-control-header-for-amazon-s3/">this guide</a>. We&#8217;ll enhance the AWS::S3 store method to add a Cache-Control header that&#8217;s 10 years in the future if there isn&#8217;t already a Cache-Conrol header present.  The first step is to create a file in your lib/ folder and name it s3_cache_control.rb.</p>
<pre class="brush: ruby; title: ;">
# Adjusts the cache control to the maximum value of 10 years (315360000 seconds)
module AWS
  module S3
    class S3Object
      class &lt;&lt; self
        def store_with_cache_control(key, data, bucket = nil, options = {})
          if (options['Cache-Control'].blank?)
            options['Cache-Control'] = 'max-age=315360000'
          end
          store_without_cache_control(key, data, bucket, options)
        end

        alias_method_chain :store, :cache_control
      end
    end
  end
end
</pre>
<h3>Make S3 URLs Expire at a Consistent Time</h3>
<p>We&#8217;re going to make generated S3 URLs expire at a consistent time by overriding <span class="hover_target">attachment_fu’s authenticated_s3_url method.  You can view the original <a href="http://github.com/rmm5t/attachment_fu/tree/master/lib/technoweenie/attachment_fu/backends/s3_backend.rb">authenticated_s3_url method</a>.  We&#8217;re going to override it to add an expires option in there isn&#8217;t one present.  Place this code in lib/s3_cache_control.rb.</span></p>
<pre class="brush: ruby; title: ;">
require 'technoweenie/attachment_fu/backends/s3_backend'
  module Technoweenie
    module AttachmentFu
      module Backends
        module S3Backend
        def authenticated_s3_url(*args)
          options = args.extract_options!
          options[:expires] = Time.now.advance(:minutes =&gt; 5).end_of_day.to_i if options[:expires].blank? and options[:expires_on].blank?
          thumbnail = args.shift
          S3Object.url_for(full_filename(thumbnail), bucket_name, options)
        end
      end
    end
  end
end
</pre>
<p>You can set expires to whatever value you would like, as long as its consistent.  In our case it made since to do it at the end of the day. But it could be at the end of the hour if you didn’t want photos cached long, or at the end of the week if you wanted them cached longer.</p>
<p>Now just add these two lines to your config/environment.rb file and you&#8217;re set:</p>
<pre class="brush: ruby; title: ;">
require 'aws/s3'
require 's3_cache_control'
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.gozuus.com/blog/2009/03/caching-images-using-attachment_fu-and-amazon-s3/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>iCal Exports for Multi-day Events</title>
		<link>http://www.gozuus.com/blog/2009/02/ical-exports-for-multi-day-events/</link>
		<comments>http://www.gozuus.com/blog/2009/02/ical-exports-for-multi-day-events/#comments</comments>
		<pubDate>Thu, 05 Feb 2009 16:08:15 +0000</pubDate>
		<dc:creator>elliott</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[iCalendar]]></category>
		<category><![CDATA[Rails]]></category>

		<guid isPermaLink="false">http://blog.gozuus.com/?p=21</guid>
		<description><![CDATA[One critical feature for GoZuus is to ensure that data is available not just within the site, but also portable in usable formats.  One obvious use of this is the group calendar.  We&#8217;ve built a nice online calendar, but we&#8217;re not out to replace Google Calendar or iCal or your iPhone.  Cleanly exporting data in [...]]]></description>
			<content:encoded><![CDATA[<p>One critical feature for GoZuus is to ensure that data is available not just within the site, but also portable in usable formats.  One obvious use of this is the group calendar.  We&#8217;ve built a nice online calendar, but we&#8217;re not out to replace Google Calendar or iCal or your iPhone.  Cleanly exporting data in iCalendar format lets us feed data into whatever scheduling application our users choose to embrace, so that their GoZuus calendar works where they want it.</p>
<p>There&#8217;s <a href="http://firstruby.wordpress.com/2007/12/10/ical-icalendar-publishing-through-rubyonrails/">plenty</a> of <a href="http://blog.milesbarr.com/2006/06/icalendar-and-rails/">resources</a> online discussing how easy it is to use the <a href="http://icalendar.rubyforge.org">Ruby Icalendar</a> library.  However, just following those directions didn&#8217;t produce quite the results I was looking for when exporting multi-day events.  Using the directions from the previous links, Apple&#8217;s iCal app displayed every event as ending one day early.  And Google Calendar put &#8220;(12:00am?)&#8221; in front of the titles.  Not exactly seamless integration.  A quick Google search didn&#8217;t turn up anything useful, so hopefully this solution will save someone else some time.<br />
<span id="more-21"></span><br />
The first one is easy.  If you dive into <a href="http://www.ietf.org/rfc/rfc2445.txt">RFC2445</a>, section 4.8.2.2 states that the <code>DTEND</code> parameter &#8220;defines the date and time <em>by which</em> the event ends.&#8221;  That&#8217;s an up-to-but-not-including statement.  If an event starts Feb 2 and ends Feb 5, then that means the event is over <em>by</em> Feb 5, and it displays as occupying only Feb 2, 3, and 4.  This is different than our model, where we define the end date to be included.  (That made more sense to me – most users who enter &#8220;Feb 2-5&#8243; are describing a four day event.)  The solution here is just to use <code>event.end_on.advance(:days =&gt; 1)</code> to push it one day forward.</p>
<p>The second one is a little more difficult.  Google&#8217;s iCal parser interprets all <code>DTSTART</code> and <code>DTEND</code> values as times unless specifically told otherwise.  If you look at the output from the Icalendar library, it lists dates and date/times as:</p>
<pre>    DTSTART:20090205
    DTSTART:20090205T083000</pre>
<p>That&#8217;s valid, but according to the RFC you can add an additional parameter in there to specify what type of value you&#8217;re providing.  This looks like:</p>
<pre>    DTSTART;VALUE=DATE:20090205
    DTSTART;VALUE=DATE-TIME:20090205T083000</pre>
<p>I couldn&#8217;t find any documentation for how to do this with the Icalendar library, so I began digging into the source for Icalendar&#8217;s component.rb.  Turns out there&#8217;s a function called <code>print_parameters(val)</code> that is called during generation, and that it prints these pairs from hash called <code>val.ical_params</code>.  So if you assign <code>event.start.ical_params = {'VALUE' =&gt; 'DATE'}</code>, the Icalendar library will add in these additional descriptors.  Seems to be undocumented, but it works for me.</p>
<p>Note that since <code>ical_params</code> is a parameter of the object itself, you can only assign this hash after you&#8217;ve assigned the object itself.  Also, it didn&#8217;t seem to work for me when the value I had assigned was a <code>String</code>.  It did work when I assigned a <code>Date</code> or <code>DateTime</code> object directly.</p>
<p>All in all, our <code>Event#to_ical</code> looks like this:</p>
<pre class="brush: ruby; title: ;">def to_ical
  ical = Icalendar::Event.new
  if self.multi_day? || self.all_day?
    ical.start = self.start_on
    ical.start.ical_params = {'VALUE' =&gt; 'DATE'}
  else
    ical.start = self.start_at
    ical.start.ical_params = {'VALUE' =&gt; 'DATE-TIME'}
  end
  if self.multi_day?
    ical.end = self.end_on.advance(:days =&gt; 1)
    ical.end.ical_params = {'VALUE' =&gt; 'DATE'}
  end
  ical.summary = self.name
  ical.description = self.details
  ical.uid = &quot;#{self.id}@gozuus&quot;
  ical.created = self.created_at.utc
  ical.last_modified = self.updated_at.utc
  return ical
end</pre>
<p>For completeness, here&#8217;s the output portion of our controller action:</p>
<pre class="brush: ruby; title: ;">
format.ics do
  @ical = Icalendar::Calendar.new
  @events.each { |event| @ical.add event.to_ical }
  @ical.publish
  render :text =&gt; @ical.to_ical, :layout =&gt; false
end
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.gozuus.com/blog/2009/02/ical-exports-for-multi-day-events/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>
