Pivotal Labs

Main menu

Skip to primary content
Skip to secondary content
  • About
  • Case Studies
  • Team
    • Executives
    • Locations
      • San Francisco (HQ)
      • Boston
      • Boulder
      • Denver
      • London
      • Los Angeles
      • New York
  • Community
    • Blogs
    • Tech Talks
    • Events
  • Careers
    • Lifestyle
    • Principles & Practices
    • Benefits
    • FAQ
    • Apply
  • Contact
    • Press Room
    • Press Releases
    • In The News
    • Press Kit
  • All
  • Labs
  • Standup
  • Tracker
Robbie Clutton

Stop leaky APIs

Robbie Clutton
Wednesday, May 15, 2013

There are many blogs about how to expose an API for a Rails application and many times I look at this and am concerned about how these examples often leak the application design and the schema out through the API. When this leak occurs a change to the application internals can ripple out and break clients of an API, or force applications to namespace URI paths which I feel is unnecessary and ugly.

When the only consumer of application data models are the views within the same application then the object design can be fluid and malleable. Once an application exposes an API to more than one client, and especially if that client is on a different release cycle to the server, such as iPhone application, data models become rigid. Rails tends discouraged N-tier architecture to the benefit of development speed but APIs are contracts between a server and it’s client and can be difficult to change once they start being used.

Passing an object into the Rails JSON serialisation methods will work for a time, but relying on this will only get you so far. At some point a refactor will take place that will cause a breaking change. It could be something simple such as renaming a column, moving responsibilities from one class to another or adding extra meta-data to a response. Either way, adding this information into your model class starts to place more responsibilities into one place.

There are a few ways out of this potential issue. Let’s take a look at the classic blog application and its Post object. The Rails rendering engine will call as_json on an object if the request has sent the content-type of application\json to the server. Here we override the implementation from ActiveRecord to provide a stable, known version:

def as_json(options={})
    {
        author_id: author.id
        title: title
    }
end

A second option is to model the object explicitly and serialise the internal model into a public representation. We can duck-type the object to respond how ActiveRecord objects behave during a serialisation call. Although this can be seen as a step towards a N-tier architecture, it’s also a step towards service dependent abstraction:

class Api::Post
  attr_reader :post

  def initialize(post)
    @post = post
  end

  def as_json(options={})
    {
      author_id: post.author.id
      title: post.title
    }
  end
end

The benefit of doing this is a separation of concerns between your data model and the data presentation. An application model doesn’t need to know how it’ll be represented by an API, command line interface or any other outside communication mechanism. If an application were tending more towards HATEOAS for instance this separation could help resolve hyperlinks relevant to the interface. You may lose some of the Rails respond_with goodness with this:

respond_to :html, :json

def show
  post = Post.find(params[:id])
  respond_to |format| do
    format.html { @post = post }
    format.json { render json: Api::Post.new(post) }
  end
end

That can be regained with the help of a presenter:

respond_to :html, :json

def show
  post = Post.find(params[:id])
  @presenter = PostPresenter.new(post)
  respond_with @presenter
end

Where PostPresenter may look something like:

class PostPresenter < SimpleDelegator
  def as_json(options={})
    Api::Post.new(self).as_json(options)
  end
end

What’s the difference between this and putting the as_json method into Post directly? More control, separation of concerns with application modeling vs presentation and the big win is when breaking changes occur within the API. Now we can put version relevant information into new objects, or into the serialised class itself.

class Api::Post
  attr_reader :post, :version

  def initialize(post, version)
    @post = post
    @version = version
  end

  def as_json(options={})
    send("v#{:version}")
  end

  private
  def v20130505
    # version specific JSON
  end

  def v20121206
    # version specific JSON
  end
end

Through this we have versioning information in one place and through a request parameter of something like v=20130506 the application can handle multiple versions in one object. For me, this ultimately removes URIs like /v1/posts, but why is that important? The URI is an identifier which points to a resource and having v1 or v2 in the URI muddies the fact that the two identifiers are pointing to the same resource. Using a request parameter, much like pagination is handled, means we can ask for a representation of that resource rather than having to specify different resources. Then we can do away with needing controllers such as Api::V1::PostsController and just deal with Api::PostsController or even just PostsController and deal with the versioning within the object instead of the URI path.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Ben Smith

leave your migrations in your Rails engines

Ben Smith
Wednesday, May 8, 2013

If you are using Rails engines to break up a single app into modular pieces, migrations (as they are currently implemented in Rails 3.2.13) become clumsy.

There are three options for migrations within an engine (spoiler: #3 is the best):

1) You can use the your_engine_name:install:migrations rake task, which copies the migrations out of the engine and into the wrapping Rails app where they can be run normally. This works fine if your migrations in your engine never change, but if you’re actively developing your engine you need to run this rake task each time you add a migration.

2) You can put all your migrations in your wrapping Rails app. This works if you’re using your engines as a way to break up your app, but it doesn’t feel right. If your models, views, and controllers all live within the engine (and depend on migrations), shouldn’t your migrations live within the engine as well? If your migrations live in the wrapper Rails app, you actually create a weird upward dependency where the engine is actually dependent on the wrapper app. This is bad.

3) You can monkey patch Rails so all of your engine’s migrations automatically get run in the wrapper Rails app. Everything just works, and migrations live where they should: in the engine. If you’re breaking up your large Rails app into engines, this is the way to go. Here’s how you do it….

Within your Rails engine, there should be a file called engine.rb here’s an example of it for an engine I called EngineWithMigrations:


module EngineWithMigrations
  class Engine < ::Rails::Engine
    isolate_namespace EngineWithMigrations
  end
end

All you need to do is tell Rails to add your engine’s migration directory to its list of places it looks for migrations. Like so:


module EngineWithMigrations
  class Engine < ::Rails::Engine
    isolate_namespace EngineWithMigrations
    
    initializer :append_migrations do |app|
      unless app.root.to_s.match root.to_s
        app.config.paths["db/migrate"] += config.paths["db/migrate"].expanded
      end
    end
  end
end

app.config is the config of your wrapper Rails app, config is the config of your engine. The above line adds the engine's migration directory to the wrapper Rails app's migration directory list. The unless wrapping it is to keep your migrations from running twice in your testing dummy app (which already runs migrations fine). Now when you run rake db:migrate from your wrapper app, your engine's migrations just work!

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Robbie Clutton

Stop leaking ActiveRecord throughout your application

Robbie Clutton
Monday, May 6, 2013

Extending ActiveRecord::Base leaks a powerful API throughout an application which can lead to tempting code which breaks good design. Take the classic blog example where you may want to retrieve the latest posts by a given author. You may have seen, or even written code that gets the dataset you need straight into the controller or view:

Post.where(author_id: author_id).limit(20).order("created_at DESC").each { ... }

For me this is a design violation as well as breaking the “Law of Demeter”[Edit: Current Pivot Adam Berlin and former Pivot John Barker pointed out that chaining with the same object was not a Demeter violation]. The example above tells me structure of the schema that the calling class has no business knowing. It also makes testing using stubs ugly and encourages testing against the database directly. A test would have to chain three methods to stub a return value. It’s brittle, as in it’s susceptible to breaking due to changes outside of the class. For me it also fails from a narrative perspective in that it doesn’t succinctly reveal the intent of this part of the application.

If we were testing this and attempting to use stubs, we’d have to write something like the below. You can see how this is at best cumbersome, but also fragile.

where = stub(:where)
limit = stub(:limit)
order = stub(:order)

Post.stub(:where).with(author_id: author_id) { where }
where.stub(:limit).with(20) { limit }
limit.stub(:order).with("created_at DESC").and_yield(post1, post2, post3)

You may be forgiven for thinking you could chain the stubs like below, but the arguments are ignored and this just serves to highlight the breaking of the ‘Law of Demeter’.

Post.stub_chain(:where, :limit, :order).and_yield(post1, post2, post3)

I’d much rather see that as a message to the Post class.

def self.latest_for_author id
  where(author_id: id).limit(20).order("created_at DESC")
end

Post.latest_for_author(1)

If there were variations of the limit and perhaps offset, they can be passed as option parameters of as an options hash:

def self.latest_for_author id, limit = 20, offset = 0
  where(author: id).limit(limit).offset(offset).order("created_at DESC")
end

Post.latest_for_author(1)
Post.latest_for_author(1, 20, 0)

or

def self.latest_for_author id, options
  limit = options[:limit] || 20
  offset = options[:offset] || 0
  where(author: id).limit(limit).offset(offset).order("created_at DESC")
end

Post.latest_for_author(1, offset: 20)

In order to get the dataset the call looks like the following, and I think is more informative than using the ActiveRecord DSL directly.

Post.latest_for_author(author_id).each { ... }

Testing is also easier, as it puts more emphasis on the messages being sent to objects rather than a chain of calls having to be correct.

Post.should_receive(:latest_for_author).with(1).and_yield(post1, post2, post3)

There are a few advantages to this refactor:

  • Only the Post class knows about the schema
  • Any changes to the implementation of what latest_for_author are encapsulated in one place
  • The method describes the intent more than the implementation
  • Stubbing in the tests are easier as there is one clear dependency
  • Testing the database is encouraged only in the class hitting the database

One further refactor could be done here, and that is to move the query logic out of the Post class once more, but this time into a purpose built query Object:

class LatestPosts
  attr_reader :author_id

  def initialize author_id
    @author_id = author_id
  end

  def find_each(&block)
    Post.where(author_id: author_id).limit(20).order("created_at DESC").find_each(&block)
  end

end

Where using the class looks like:

LatestPosts.new(author_id).find_each { ... }

Here’s what Bryan Helmkamp has to say on query objects in his excellent write up on fat ActiveRecord models. Bryan here rightfully points out that once in a single purpose object, they warrant little attention to unit testing. Now is the right time to use the database to ensure the right data set is being returned and that N+1 queries are not being performed. This means that database testing would only occur within the class actually hitting the database and not the rest of application which has a dependency on the database.

All of these techniques discussed serve to improve the design of an application by preventing leaking responsibilities from one class throughout the rest of the application. I’m also not saying that developers shouldn’t be using ActiveRecord or even Rails, but to use the tools responsibly.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Robbie Clutton

Single resource REST Rails routes

Robbie Clutton
Tuesday, April 16, 2013

REST principles by default is a fantastic convention within Rails applications. The documentation for how to route HTTP requests are comprehensive and give examples about photo resources within an application. If you’ve got photo and tag as first class resources of your application, Rails has you covered. But what if you are building an application with a focus on one type of resource, do you really want /resource_type as a prefix to all of your application paths? I certainly don’t and I’ll show you how to remove that without diverging from Rails core strenghts.

For better or worse, I’m always conscience of making sure applications I’m involved in have Cool URIs and sometimes that does mean fighting the Rails conventions. However Rails routing is very flexible and can provide me with the application paths that make me happy.

Take Twitter as an example. Every user has their username as a top level path, so instead of having /users/robb1e, they simply have /robb1e. When dealing with an application where there is one core resource it can make a lot of sense to strip the resource prefix. This can be achieved through scopes in the routing configuration.

Your::Application.routes.draw do
  scope ":username" do
    get '', to: 'users#show'
  end
end

Gives you routes which look like

           GET  /:username(.:format)                users#show

If you wanted to see the followers and followees of that user, you have two options. Return to the default resource or use HTTP verb contraints. I’ll show you both.

Your::Application.routes.draw do
  scope ":username" do
    get '', to: 'users#show'
    resource :following, only: [:show]
    resource :followers, only: [:show]
  end
end

This adds the routes

following GET  /:username/following(.:format)      followings#show
followers GET  /:username/followers(.:format)      followers#show

Alternatively HTTP verb constrains can be used to achieve a similar result.

Your::Application.routes.draw do
  scope ":username" do
    get '', to: 'users#show'
    get '/following', to: 'user#following'
    get '/followers', to: 'user#followers'
  end
end

This gives the paths

          GET  /:username/following(.:format)      user#following
          GET  /:username/followers(.:format)      user#followers

If you are trending into paths unknown, you always have the safety of tests to help you out. Both Rails and RSpec have ways to test your application routes.

One gotcha which using the default resource routing removes is clashing paths. If you decide to build an admin page and want to put that at /admin, that needs to be in the routes config before the scoped block and if a user has given themselves the name of admin then you may be in for some fun.

So the next time a need arises for an unconventional route, check the documentation, it’s probably possible although almost always warants thinking about.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Brian Butz

Presenter Sanity

Brian Butz
Sunday, March 24, 2013

I’m not a huge fan of Rails view helpers, which is a post for a different day, but put simply I prefer to encapsulate presentation logic in a presenter object rather than mix it in globally across all templates. There’s a few ways to do this, the simplest being the Good Ol’ Plain Ol’ Ruby Object:


  class FooPresenter
    def description_for(foo)
      Date.today.tuesday? ? "50% off! #{foo.description}" : foo.description
    end
  end

Usually, I’ll instantiate the presenter in a controller and then call it within the view:


  class FoosController
    def index
      @foos = Foo.all
      @foo_presenter = FooPresenter.new
    end
  end

  <% @foos.each do |foo| %>
    <%= foo.name %>
    <%= @foo_presenter.description_for(foo) %>
  <% end %>

Everything is simple and feels where it should be, but we’ve gotten this at the cost of losing view helpers mixed in from Rails. One of the helpers I find particularly helpful is strip_tags. If we want access to strip_tags we can do as Rails does when mixing it into the view scope and include it in our presenter:


  class FooPresenter
    include ActionView::Helpers::SanitizeHelper
    def description_for(foo)
      description = strip_tags(foo.description)
      html = Date.today.tuesday? ? "50% off! #{description}" : description
      html.html_safe
    end
  end

This feels a bit wrong as well. For one, we’ve increased the number of public methods on our presenter by at least 4, and only really needed one of them. Two, we have little control over how we are stripping the tags, and so any unit tests we’ve written for our presenter must integrate and essentially test parts of Rails. What we really want is something that can do what strip_tags is doing. Luckily, and due Rails becoming more modular, we already something to do this for us in the Rails library html-scanner. Under the hood, strip_tag is, unless configured differently, passing your string down to the sanitize method on an instance of an HTML::FullSanitizer object. We can do something similar in our presenter:


  require 'html/sanitizer'
  class FooPresenter
    def initialize(sanitizer=HTML::FullSanitizer.new)
      @sanitizer = sanitizer
    end
    def description_for(foo)
      description = @sanitizer.sanitize(foo.description)
      html = Date.today.tuesday? ? "50% off! #{description}" : description
      html.html_safe
    end
  end

Now we have an object that is easy to test purely as a unit, and also has the ability to be extended with different sanitizers.

While this works well with strip_tags, unfortunately not every helper in Rails is as nicely decoupled. The work being done in the number_to_* methods, for instance, are written completely in modules. It may be nice at some point to pull these into corresponding objects, but a short term solution could be to just have an object that includes the module:


  class NumberCruncher
    include ActionView::Helpers::NumberHelper
  end

  > NumberCruncher.new.number_to_currency(1)
  => "$1.00"
  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Jeff Hui

Red Bull Book Club Block Party (with fireworks)

Jeff Hui
Friday, February 15, 2013

Helps

Rails remote link gets updated

Rails link remote.

Get HTML into the Dom? Without the callback? (no id)

  • Turbo links?
  • Delete ujs from the app

Events

Friday: Block Party between 4th and 5th on Bluxome

http://engine.is/blockparty

This Friday celebrating start ups in SF w/food trucks at lunch time.

Come meet Engine Advocacy – startups' voice in government.
Bring your friends, enjoy great food, and learn about becoming
an Engine member.

Engine's Startup Block Party
February 15, 2013
11:00 am – 2:00 pm
Bluxome St., between 4th and 5th

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Robbie Clutton

Testing strategies with RSpec, NullDB and Nosql

Robbie Clutton
Sunday, February 3, 2013

Recently I had posted about a few testing strategies that can be applied with RSpec. One of the patterns I mentioned was using something like NullDB to ensure your unit tests were not hitting the database. I had a few conversations about what I’d written, notably from my colleague Ian Lesperance. We discussed, and I conceded, that it’s preferable to have tests related to one class in one spec file. In particular I had split out the tests for the unit level and the integration with the database tests. So, here are my experiments on how I brought those tests back while keeping the same integrity of using a database for some test and forcing the null object pattern on other tests.

I had some issues having those tests in the same file, but with a little help from another colleague, JT Archie, we managed to figure it out.

Consider this rspec test:

  describe Widget do
    describe "#higest_selling", :db do
      it "uses the 'highest_selling' scope" do
        ...
      end
    end

    describe "#display_name" do
      it 'concats the widget name and manufacturer' do
        ...
      end
    end
  end

The ‘higest_selling’ method is a scope and has the ‘:db’ tag associated to the block, while the ‘display_name’ test has no tags applied. I wanted this to be the case, no tags means no database but if you want to hit the database, you need to explicitly call it out.

One trick you might have missed above was no longer needing to do ‘db: true’ in the RSpec tag. With the following setting in the spec helper, you can apply a symbol directly like ‘:db’.

config.treat_symbols_as_metadata_keys_with_true_values = true

Testing with NullDB

To get this working, I had to use the HEAD revision of NullDB:

gem 'activerecord-nulldb-adapter', git: 'git://github.com/nulldb/nulldb.git'

Using NullDB within the same file, we can use the ‘nullify’ and ‘restore’ helpers, but I found it worked best using the ‘around’ configuration. Using ‘before’ and ‘after’ I was having issues with changing the connection adapter during a transaction. This way, it appears to get around that issue.

We run the configuration block around each test that has the ‘type: :model’ tag. RSpec-Rails applies these automatically to any tests in the ‘spec/models’ directory. We look to see if the example has the ‘:db’ tag and if it does, we restore the default connection adapter, and run the example. If the example does not have the ‘:db’ tag applied, we apply the NullDB adapter, run the example and then restore the default adapter.

Within the ‘spec_helper.rb’ file:

  config.around(:each, type: :model) do |example|
    if example.metadata[:db]
      NullDB.restore
      example.run
    else
      NullDB.nullify
      example.run
      NullDB.restore
    end
  end

Testing using stubs

There are other options and with a sizable amount of help from JT, we created a simple way to achieve a similar outcome. Under ActiveRecord there are two methods which actually hit the database, ‘exec’ and ‘exec_query’. These methods can be stubbed out much like any method on any object in an application codebase.

In the ‘spec_helper’ file, we replace the NullDB configuration with the following. We again check for the ‘db’ tag and if it’s not there we stub ‘exec’ and ‘exec_query’.

  config.around(:each, type: :model) do |example|
    unless example.metadata[:db]
      ActiveRecord::Base.connection.stub(:exec).
        and_raise("You're not allowed to do that")
      ActiveRecord::Base.connection.stub(:exec_query).
        and_raise("You're not allowed to do that")
    end
  end

Testing using Nosql

We took this concept one step further and created a Gem that wasn’t RSpec specific. We couldn’t believe our luck when RubyGems showed there was no Gem called ‘nosql’, so with that problem solved we created the Nosql gem. When included in a test suite, any call to the database will raise an exception.

With the around configuration block Nosql is disabled and enabled accordingly.

  config.around(:each, type: :model) do |example|
    if example.metadata[:db]
      Nosql::Connection.disable!
      example.run
    else
      Nosql::Connection.enable!
      example.run
      Nosql::Connection.disable!
    end
  end

All three of these options force unit tests to not hit the database. Database calls will either be ignored (NullDB), or will raise an error (Nosql). This should result in decreased execution time for tests as it will encourage the developer to stub out those interactions with the database.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Ryan Ong

How to split up rails 3.x logger by unicorn workers

Ryan Ong
Friday, January 11, 2013

TLDR: Put this in your unicorn config. Make sure not to have duplicate after_fork calls

after_fork do |server, worker|
  if defined?(ActiveSupport::TaggedLogging) && Rails.logger.kind_of?(ActiveSupport::TaggedLogging)
    Rails.logger.instance_eval do
      level = @logger.level
      # flush and close logger to make sure we don't cut off any potential logging before this
      @logger && @logger.flush && @logger.close
      @logger = ActiveSupport::BufferedLogger.new(Rails.root.join("log", "rails_#{Rails.env}.#{worker.nr}.log").to_s, level)
    end
  elsif Rails.logger.kind_of?(ActiveSupport::BufferedLogger)
    Rails.logger.instance_eval do
      @log && @log.flush && @log.close
      @log_path = Rails.root.join("log", "rails_#{Rails.env}.#{worker.nr}.log").to_s
      if respond_to?(:open_logfile, true)
        @log = open_logfile(@log_path)
      elsif respond_to?(:open_log, true)
        if File.exist?(@log_path)
          @log = open_log(@log_path, (File::WRONLY | File::APPEND))
        else
          FileUtils.mkdir_p(File.dirname(@log_path))
          @log = open_log(l@log_path, (File::WRONLY | File::APPEND | File::CREAT))
        end
      end
    end
  end
end

How this works!

There is no easy way to change the path of a log or swap out the logger. When rails is instantiated ActionController and ActiveRecord assigns their local logger from Rails.logger. So if you reassign Rails.logger to a new one ActionController and ActiveRecord will continue to use the old rails logger.

There are no methods to change or swap out the log path in the Logging class. In order to change the log path we have to change instance variables to either a new logger or a new file handler.

In Rails 3.2 ActiveSupport::TaggedLogging was added!
If you look here you can see how Rails.logger is assigned! (source)
As you can see TaggedLogging basically just wraps a buffered logger. So when we open up Rails.logger we can patch what TaggedLogging wraps by modifying the @logger variable.
If TaggedLogging isn’t used we can use the open_logfile command and just insert a path.

In Rails 3.0 and Rails 3.1 TaggedLogging isn’t used and by default uses BufferedLogger.
We have to create a new file handler using the open_log method and reassign the @log variable.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Georg Apitz

How to render jade templates on the server

Georg Apitz
Thursday, January 10, 2013

The best way for Google and friends to crawl and index a page is by finding static content. With applications that make heavy use of libraries like Backbone, Sproutcore or ember.js a lot of the content can be rendered in the browser. This can constitute a serious problem since the support methods from Google et. al. for alternative crawling coem with no guarantees.

We recently ran into this problem and came up with a neet solution where the client side templates can also be rendered server side.

There are two parts to the implementation. First, a method that can take a jade partial and its local variables, compile and evaluate it. Second, a view that calls out to that method and provides the template and the necessary local variables.

Part 1, the application_helper.rb


require 'tilt-jade/jade_js'

module ApplicationHelper
  def html_from_jade_template(partial, options={})
    data = File.open(\
            Rails.root.join("app", "assets", "templates", "#{partial}.js.jst.jade"\
            ))
    compiled = JadeJs.compile(data, locals: {}, client: true, compileDebug: false)

    raw(JadeJs::Source.context.eval("(#{compiled})(#{options[:locals].to_json}, \
        jade.runtime.attrs, jade.runtime.escape, jade.runtime.rethrow, \
        jade.runtime.merge)" \
        ))
  end
end

The method is pretty straight forward, it loads the template from the assets/template directory, compiles it and then evals it with the provided locals. The output is raw html that can be included in your view.

Part 2, a view that requests the compiled and eval’d templates from the server


- body = "This content comes from the server. Remove the param from the url or toggle \
          with the link in the upper right."
#menu
  = html_from_jade_template("home/menu", locals: {items: [{title: "Server"}, \
    {title: "Side"}, {title: "Rendered"}]})
#content
  = html_from_jade_template("home/content", locals: {body: body })

Here the main thing is the call to the method in application helper that will produce the raw html given a template name and the necessary local variables.

That is pretty much it. It may take a little bit to figure out the requirements for the local variables in your template, especially since jade is very strict about them. If it can’t find one it will not compile.

I’ve put an example app on github with the complete source code for this post.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Doc Ritezel

Automated Deployment Messages

Doc Ritezel
Friday, October 5, 2012

Make deployment visible with Capistrano, Autotagger, Git and Sendgrid

There comes a time in every project when the deployment process comes of age, and that development arrives with its own set of Capistrano recipes and Rake tasks. The project I’m on hit that point recently, and one of the neat outcomes of its nascent puberty was a simple Capistrano recipe to send a git changelog to our project mailing list.

Here’s what this looks like:

$ cap staging deploy
... stuff happens here ...
  * executing `sendgrid:notify'
Changelog:
04fc6dd adding capistrano deployment messages

To use this in your Rails project, the first thing you need is a sendgrid account. If you’re budget-minded, you can always use the credentials your Heroku app is using.

$ heroku create
Creating heroku-wackiness-90210... done, stack is cedar
http://heroku-wackiness-90210.herokuapp.com/ | git@heroku.com:heroku-wackiness-90210.git
Git remote heroku added
$ heroku addons:add sendgrid:starter
Adding sendgrid:starter on heroku-wackiness-90210... done, v2 (free)
Use `heroku addons:docs sendgrid:starter` to view documentation.
$ heroku config -s
SENDGRID_PASSWORD=s3kr17
SENDGRID_USERNAME=yodawg@heroku.com

This process uses Capistrano and Autotagger. For information on setting up Capistrano, their wiki is an excellent starting point. For Autotagger setup with Capistrano, Jeff Dean’s auto_tagger repository is the canonical source of information.

After you’re up and running with Capistrano and Autotagger, you need to add the following file under lib/recipes/sendgrid_notifier.rb:

require 'mail'

set :sendgrid_user, "whatever"
set :sendgrid_password, "secret"
set :sendgrid_domain, "pivotallabs.com"

set :sender, "Now Hiring <jobs@pivotallabs.com>"
set :recipient, "Steve Squivot <you@square.com>"

namespace :sendgrid do
  task :notify do
    sendgrid = {
      :address   => "smtp.sendgrid.net",
      :port      => 587,
      :domain    => sendgrid_domain,
      :user_name => sendgrid_user,
      :password  => sendgrid_password,
      :authentication => 'plain',
      :enable_starttls_auto => true
    }

    auto_tagger = AutoTagger::CapistranoHelper.new(
      :stage => rails_env,
      :stages => auto_tagger_stages).auto_tagger
    previous_sha = auto_tagger.refs_for_stage(stage).last.sha
    current_sha = auto_tagger.repo.latest_commit_sha

    mail = Mail.new(from: recipient, to: sender)
    mail.delivery_method :smtp, sendgrid
    mail.subject = "[#{stage}] New Deployment!"
    mail.body = `git log --oneline #{previous_sha}..#{current_sha}`
    mail.deliver!
  end
end

Then, let’s add a line to include this recipe in our Capfile:

require File.expand_path("../lib/recipes/sendgrid_notifier.rb", __FILE__)

Finally, let’s try it out:

$ cap ci sendgrid:notify
  * executing `sendgrid:notify'
Changelog:
04fc6dd add a recruiter message to send off to Square

Alright! The email’s on its way. If you need to call this in your custom deployment step, it’s as easy as sticking sendgrid.notify into your Capfile.

Happy deploying!

  • 0 Shares
  • Share on Facebook
  • Share on Twitter

Topics

  • agile (778)
  • rails (113)
  • testing (86)
  • ruby (83)
  • ruby on rails (70)
  • jobs (62)
  • javascript (54)
  • techtalk (44)
  • rspec (38)
  • activerecord (29)
  • productivity (29)
  • gogaruco (29)
  • ironblogger (29)
  • git (28)
  • nyc (27)
  • rubymine (25)
  • mobile (22)
  • bloggerdome (20)
  • cucumber (20)
  • process (19)
  • pivotal tracker (19)
  • jasmine (19)
  • design (18)
  • ios (18)
  • webos (17)
  • objective-c (17)
  • android (16)
  • palm (16)
  • "soft" ware (16)
  • fun (15)
  • tracker ecosystem (15)
  • ci (15)
  • cedar (15)
  • rails3 (14)
  • performance (14)
  • bdd (14)
  • gem (13)
  • selenium (12)
  • css (12)
  • goruco (12)
  • bundler (12)
  • tdd (12)
  • meetup (11)
  • railsconf (11)
  • nyc-standup (11)
  • capybara (10)
  • mac (10)
  • mojo (10)
  • chef (10)
  • rubygems (9)
Subscribe to rails Feed
  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. ...
  9. 12
  10. →
  • About
  • Case Studies
  • Team
  • Community
  • Careers
  • Contact
  • Labs
  • Events

Contact Us

contact@pivotallabs.com
+1 415-77-PIVOT
TwitterLinkedInFacebook

Pivotal Tracker

Tracker is the award-winning agile project management tool that enables real-time collaboration around a shared, prioritized backlog.
Visit pivotaltracker.com >