Wednesday, September 10, 2014

Heroku Hipchat deploy hook

If you have a heroku account and want to post your latest update to HipChat, here's something to go by:

First you need an Auth Token, you get this in Hipchat, you need to be a Group Admin.  Just click on the Group Admin tab and select 'API'.

Then, create a 'notification' token.  Make sure it is an API1 (not API2) token.

heroku addons:add deployhooks:hipchat \
    --auth_token=YOUR_AUTH_TOKEN \
    --room="ROOM_NAME" \
    --message="{{user}} deployed {{app}}\r\n{{url}}\r\n{{git_log}}" \

Saturday, March 22, 2014

Collect funds for your group projects

I've been working on a project to help classrooms at my son's school collect funds for end of year gifts, team dues, etc. It's called Sidejar, and it's been working really well. It's motivating to hear the comments from the parents that would not have donated without a credit card interface.

"I love the convenience of this site. Thanks for coordinating and for the reminder!"

It's somewhat obvious, if you think about it, the more convenient it is to pay, the more people pay. It's been especially great to see it used by parents and teachers. It's a great cause to contribute too. Teachers give so much of their personal time and money, it's great for parents to have a chance to show their appreciation.

Sidejar - Money between Friends

Given a string to build a Rails object relationship

In situations where you want to take in a string - like when you are taking the results of an autocomplete, to form a relationship with another object - this is a pattern I like to use...

For example, say you wanted to credit an article to an author. There are many authors in the system, and their names are known. You might want to edit an article and change the name of the author...

In that situation, to build the relationship with the author (and set the foreign key), it takes a little effort to find the author by name, if he's not there, create a new author using the entered name.

attr_accessible :body, :title, :author_name
attr_accessor :body, :title, :author_name

before_validation :set_author
after_initialize :set_author_name

def set_author_name
  self.author_name ||= author.try(:name)

def set_author = Author.where(:name => author_name).first_or_initialize

Friday, August 02, 2013

Cookie Collection Objects

Web developers will often have situations where we want to store something, usually a collection, temporarily - like the last few pages a user has visited, or a list of things to compare.  In my case, I wanted to store recent restaurants - pages the user visited, so they could return to them later.  This is often accomplished with cookies, but this is a way to hide the cookies and simply interact with the collection.

Using cookies to store and retrieve

At it's simplest, a cookie collection object needs to do the following.

Add items
Retrieve all items

In this example, I want to be able to show a list of recent restaurants on the side of the page.

Do do this, I would like an interface that allows me to add a recent restaurant.


Or, to retrieve recent restaurants...


I'd like to avoid making direct calls using the 'cookies' hash, instead interact with the collection and hold the list between requests in the cookie.

With a CookieCollection class, we can use this to handle the common additions and deletes.  We also get all the power that comes with the Array class.  This class is meant to be subclassed.

class CookieCollection < Array

  attr_accessor :ids

  def initialize(cookies)
    @_cookies = cookies
    if @_cookies[cookie_name].present?
      self.ids = @_cookies[cookie_name].split(',')
      self.ids = []

  def push(object)


  def update_cookie
    ids = map(&:id)
    @_cookies[cookie_name] = {:value => ids.join(','), :expires => 20.years.from_now}

  def cookie_name


Note - the cookie value is only the ids of the object.

In a RecentRestaurants subclass, we can make the specific updates to our list of recent restaurants. In this case, I wanted to limit my list to the last 5 items.

class RecentRestaurants < CookieCollection

  def initialize(cookies)
    self.ids = ids.last(5)
    ids.each{ |r_id| push(Restaurant.find(r_id)) }

  def push(restaurant)
    while length > 4


In the application controller, I exposed a helper method to allow access to recent_restaurants

  def recent_restaurants
    @_recent_restaurants ||=
  helper_method :recent_restaurants

And then, in a view or controller, you can easily do things like

class RestaurantsController < ApplicationController
  def show
    @restaurant = Restaurant.find(params[:id])

    # Add a restaurant

    # Access all recent restaurants easily

This cleans things up quite a bit and makes it easier to work with in a consistent way.  It could easily be extended to be used with Sessions, as well.

Monday, July 29, 2013

A Bookmarklet for switching between localhost and production

If you are often going between production and localhost:3000 - you can easily change the domain name with a simple javascriptlet - something like this...


 Make this the content of a bookmark, and add it to your bookmark menu.  Easy flipping.

Adjust further if you need to go between a staging environment or another localhost domain.

Tuesday, July 05, 2011

Set a default expires_in value for memcache

I bet there is an easier way to do this, but here's what I came up with. I didn't want to cache items forever (which is the default with memcache and rails). I wanted to cache items for 6 hours, max. To do this, I wrote a patch and if the :expires_in option is missing, it will set it to 21600. I would love to hear if there is a better way...

module ActiveSupport
module Cache
class Store


# Original code from ActiveSupport::Cache::Store
# def expires_in(options)
# expires_in = options && options[:expires_in]
# raise ":expires_in must be a number" if expires_in && !expires_in.is_a?(Numeric)
# expires_in || 0
# end

def expires_in(options)
expires_in = options && options[:expires_in]
raise ":expires_in must be a number" if expires_in && !expires_in.is_a?(Numeric)
expires_in || 6.hours


Monday, May 02, 2011

Brew/RVM/PSQL/Bundler Installation for a MacBook Pro

Install XCode - if you do this from disc, you'll need to upgrade later (version 3.2 that comes on snow leopard disc has bugs). You can download and start with the new one. It's 3.2 GB and could take a while to download. Do this first.

Install Brew. Follow the instructions here ( I used the ruby script. If you have permission errors, you may need to do the following:

sudo chown -R $USER /usr/local

If you're OK, then install git

brew install git

If you have errors at this point, it may be due to XCode not being installed properly.

brew install postgresql

If you have an older system you may run into conflicts. Try 'brew doctor'

Follow all the instructions that show up after the postgres build. This will force the db to start up right away and set the psql function so you can access the db from command line.

# install rvm

bash < <( curl ) # The first time you install RVM, you must put the following line into your ~/.bash_profile at the very end, after all path loads etc: [[ -s "$HOME/.rvm/scripts/rvm" ]] && . "$HOME/.rvm/scripts/rvm" # This loads RVM into a shell session. # Add ruby rvm install 1.9.2 rvm install 1.8.7 rvm install ree For each project, create a .rvmrc file - this goes in the root of the rails app directory. More Show hidden files on your mac defaults write AppleShowAllFiles YES restart launcher

RVM configuration

.rvmrc files - add ruby version and gemset per project

gem install bundler - bundler should be a gem that you can access from any rvm configuration - used to generate Gemfile and Gemfile.lock.

* rvm instructions for 10.10

Monday, April 11, 2011

Very true! Makes a change to see someone spell it out like that

I just got this 'comment' on a wordpress blog I write (unrelated to web development). After doing a quick search on google for this phrase: Makes a change to see someone spell it out like that, you can see a lot of other blogs have received the same post.

There's an interesting little code in front of it. It's random, and never the same on the blog. Mine was 'TRuxcI'.

Makes me think someone is out there testing to see if they can get you to add a special code to your blog. If so, they'll probably spam you.

Just delete them.