Handling Vanity URLs & Legacy Routes in Rails 3.2

Handling Vanity URLs is not a new problem. Website Owners want their URLs to be simple or catchy or elegant. For marketing purposes, they may need to be short(ish) and possibly temporary. Imagine a URL like

//example.com/party

which in April points to /parties/april, then May points to nothing (May’s a dull month) and then in June it points to /parties/june. We need something that acts sort of like Rack::Rewrite but that looks into an easily editable chunk of data (read: database) for the list of paths and redirects.

If you comb the web (as I did) looking for Vanity URL solutions, most of them solve the issue for pretty URLs for a given Model/Controller pair. They solve the issue that numeric IDs are not informative, but words with hyphens can be more informative. If that is the problem you’re hoping to solve here, read no further. Go straight to FriendlyID (or similar).

If, instead, you’re trying to build a simple redirect/route-fallback system that allows for root level temporary Vanity URLs that can be managed within the running application (not by a static configuration file), then you’ve come to the right place.

First, we need a place to store the paths and redirects. Let’s start by adding a simple VanityUrl model

class VanityUrl < ActiveRecord::Base
  attr_accessible :active, :path, :url
  validates :url, link: true, presence: true
  validates :path, presence: true, uniqueness: true
  class << self
    def active
      where(active: true)
    end

    def routes
      Hash[VanityUrl.active.map{|v| [v.path, v.url]}]
    end
  end
end

path is the path we will be watching for

url is the url we’ll redirect to, if we hit that path

active is a boolean (in case we want to easily turn the redirect off or on).

Looking ahead a bit, we’ve added an #active scoping method and #routes which returns all active VanityUrls. You’ll see where this is used shortly.

Exercise for the reader

Build controller and views to show/list/edit these guys in your application.

Now we want these to hit, only if other routes have not been matched. So at the end of our config/routes.rb file, we add a new catch-most route.

# in config/routes.rb

MyApp::Application.routes.draw do
  # your apps routes are defined here
  ...
  # then way down at the end... 
  # as the last defined route

  # this routes to the Vanity controller which handles the actual redirect
  # format:false allows us to recognize paths like '/this/old/page.htm
  match "/*vanity", to: 'vanity#routing', constraints: RoutingConstraints::VanityUrls.new, format: false
end

This last line is catch-most because it will only match if the constraint defined in RoutingConstraints::VanityUrls is hit. And with format: false is telling the router to ignore the any extension (as the comment says – so we can match page.htm if need be).

The RoutingConstraints::VanityUrls object needs to have a #matches? method (more in the Rails Routing Docs). So we can add the following:

# in app/lib/routing_constraints.rb
module RoutingConstraints
  class VanityUrls
    def matches?(request)
      routes = VanityUrl.routes
      vanity_url = request.params['vanity']
      if routes.has_key? vanity_url
        begin
          URI.parse(routes[vanity_url])
          return true
        rescue URI::InvalidURIError
          # vanity url is no good
          return false
        end
      end
      false
    end
  end
end

Notice here, we’re using the VanityUrl::routes method. Now, as routes are processed by Rails, *IF* they don’t match any of our normal routes *AND* they match one of our VanityURLs by path, *THEN* the router will pass them off to the VanityController#routing method which we told it to do in the routes.rb file.

Let’s add that method:

class VanityController
  def routing
    vanity_routes = VanityUrl.routes
    vanity_url = params['vanity']
    if vanity_routes.has_key? vanity_url
      redirect_to URI.parse(vanity_routes[vanity_url]).to_s and return
    end    
    raise "Unable to find requested VanityUrl for #{params}"
  end
end

Notice, this is not the VanityUrlController which you’ve written to handle VanityUrls in the database. We make a new controller which will only have this one method to handle the rerouting of requests.

That’s basically it. If you’re app gets a lot of traffic, you’ll probably want to cache the VanityUrl collection (since it should be read-often and write-occasionally). Without a cache, every request is doing a full table retrieval of the VanityUrl table.

Initially, we added this feature to support true VanityUrls for a client e.g. http://example.com/party, http://example.com/temporary_coupon etc. And for this purpose, it works great. But it also saved our butts in a way we hadn’t forseen. The application we built was replacing an existing site almost entirely. When we actually launched, we starting getting piles of 404’s. The bits of the old site that were still up were asking for resources, images, stylesheets etc that were not duplicated in the new site. With a handful of VanityUrls, we were able to point those misdirected requests to the old assets (or wherever they needed to go) without modifying code or adding Rack::Rewrite directives. It made launch day a relative breeze.

acts_as_taggable_on + select2 = Super Easy Tags in Rails

I’ve had two projects in the past little bit where I wanted user input tags for objects. I also wanted to have typed tags – tags of different categories applied to the same object. I found that acts_as_taggable_on (Rails gem) and select2 (jQuery plugin) work really nicely together and make this an easy task.

Let’s assume that we have an ActiveRecord model for a Post. Follow directions to install acts_as_taggable_on (add to Gemfile, initialize and run migration). Then add two different tag types to Post like this:

class Post < ActiveRecord::Base
  acts_as_taggable # basic usage - gives us 'tags'
  acts_as_taggable_on :topics
end

With this, the Post model is ready to store tags and topics.

irb> Post.first.tags << 'super fun'

To push this to the users, we want a nice UI for users to enter tags/topics. This is where Select2 comes in. In your form for adding/editing posts, add two input boxes, one for topics and one for tags

# using haml templating
.input-row
  = f.label :tags
  = f.text_field :tag_list
.input-row
  = f.label :topics
  = f.text_field :topics_list

Then attach the select2 plugin to the form elements. Make sure you’ve got jQuery and select.js included on the page.

// in your javascript
$(function() {
  $('.input-row #post_topics_list, .input-row #post_tags_list').select2({tags:[]})
});

And you get a beautiful input box with handy tag handling.
Tags and Topics form

Update:
If you haven’t found it, there’s a nice package that puts select2 in the asset pipeline – https://github.com/argerim/select2-rails.

loving perl one-liners

I’ve always been a big perl fan.

Just ran into another case, where I dusted off some old dusty synapses and did a little perl oneliner.

Given a pile of text files, prepend each line of each file with a ‘* ‘ (to make it handy for a markdown list.

find . -name '*.txt' -print0 | xargs -0 perl -pi -e '$_ = "* $_"' 

done and done.

A file like this

line 1
line 2
line 3

now looks like this

* line 1
* line 2
* line 3

And it hit every text file (*.txt) in this directory and below.

If you haven’t played with perl oneliners (or ruby oneliners for that matter) check out these resources:

How I learned to love ActiveSupport::TimeZone

I recently wrote up a basic calendaring system. Naively, I figured that I could set the time zone for the application in Rails.config, and everything would magically work out. No such luck. Here’s what I learned:

  • Time.now uses the server time.
  • DateTime.parse and Time.strptime use the server time.

Put this together, and you’ve got fun times.
When users enter date information (as they do when entering calendar entries, for example), they will typically enter a string with no time zone.

06/24/2002 12:30pm

for example. What time is that? What time zone? DateTime.parse and Time.strptime will both give you server time. Here’s where ActiveSupport::TimeZone comes in to play.

  • Time.zone.now is smarter. It uses Application.config.time_zone.
  • Time.zone.parse parses the time, also, using Application.config.time_zone.

For the application at hand, we know that all events entered should be in one time zone. We can set the time zone for the app to that time zone.

# in config/application.rb
...
module MyAppName
  class Application < Rails::Application
    ...
    config.time_zone = "Pacific Time (US & Canada)"
  
    ...
  end
end

For all cases where want the current time (to initialize a date input box, for example), we use Time.zone.now. This is equivalent to

Time.now.in_time_zone(MyAppName::Application.config.time_zone)

Finally, when we get input time/date from client forms, we use Time.zone.parse like so:

class DateInputController

  def create
     start_time = Time.zone.parse(params[:start_time])
     end_time = Time.zone.parse(params[:end_time])
     ...
  end
end

This guarantees that the start/end time will also be interpreted in the application’s time zone. If, in this case, you had decided to use strptime, you’d end up with times that were in the server time zone.

Try it for yourself. In my application’s Rails console I did:

irb> Time.zone.now
=> Wed, 30 Jan 2013 08:23:26 PST -08:00
irb> Time.now
=> 2013-01-30 16:23:26 +0000
irb> Time.zone.parse('2010-01-12')
=> Tue, 12 Jan 2010 00:00:00 PST -08:00
irb> DateTime.parse('2013-01-12')
=> Sat, 12 Jan 2013 00:00:00 +0000
irb> DateTime.strptime('2013-01-12','%Y-%m-%d')
=> 2013-01-12 00:00:00 +0000

I ran these lines on a server that was living in UTC time zone. You’ll notice that Time.now and Time.zone.now have the same time, but are set to different zones. The PST is the offset from our application’s time zone (in config/application.rb). Here we also can see (highlighted lines) that Time.zone.parse does the right thing – uses the application’s time zone, where as DateTime.parse does not. Both DateTime.parse and DateTime.strptime don’t know what to do about time zone, so they set it to 0-offset (UTC).

My last bit of advice that helped me find the places I’d missed was to put my development machine in another time zone; one that is not UTC and not the application’s time zone. When I did that, I was able to identify (and write solid tests), around cases where I had not dealt properly with conversions of client < = > server times.

Futher Reading:

Researching MeteorJS – Authentication

As the 2nd Rate Geniuses told me, authentication with MeteorJS was a piece of cake.

Here’s how it went down.

As you may recall (previous post), we’ve setup a simple multi-page app. It’s got 2 pages. One for viewing and one for administration. The view page should be available with no login. Admin is for logged in users only.

Setting Up a Login System (Authentication)

To setup Meteor auth, I removed the insecure package (turned on by default) and added the accounts packages: accounts-ui-unstyled and accounts-github. This will allow login to happen via Github’s Oauth service.

meteor remove insecure
meteor add accounts-ui-unstyled
meteor add accounts-github

As a first cut, we can add the login form to the index template which will allow signin from anywhere. For my app, the main layout is /app.html. So I started with the following:

<body>
  <div class='login">
    {{loginButtons}}
  </div>
  {{#if currentUser}}
    Welcome, {{currentUser.profile.name}}
  {{/if}}
  <section> 
    {{renderPage}}
  </section>
</body>

This gives you the unstyled (because we’re using accounts-ui-unstyled) login form. From here you’re given a link that tells you as the site-administrator to link in to Github’s Oauth. It involves setting up an API key and secret for the app and a couple of callback URLs. You can follow the instructions from Meteor. Once those keys and callbacks are setup, the {{loginButtons}} partial gives you a ‘Sign in via Github’ link. Easy peasy.

From there, we can start modifying templates to do the right thing based on the state of the current user. If you’re in client/server code, use

if (Meteor.user()) {
 // users only get this
}
else {
 // plain ol' visitors get this
}

and in the templates, there’s a helper attribute/method called currentUser

  <section>
    {{#if currentUser}}
       <div class="for_logged_in_users_only">
       </div>
    {{/if}}
  </section>

One of the downsides (as I see it) is that their built in template for login allows user creation with no email validation (from what i can tell). More importantly, there is no easy way to customize the template to not show the ‘sign up’ link and only have a ‘sign in’ link. For my purposes, I’d love to seed the database with the users that I want, and send them a login invite. Because my “users” are going to be full site editors and should have mostly full reign. And everyone else should not need an account.

To accomplish this, I decided to build a route to a sign in/sign up page which is protected only by obsurity (imagine a login link that is called /you_hit_this_path_to_login). I think it’ll work for now.

Here’s how I setup the routes:

if Meteor.isClient

  Meteor.Router.add
    '/': 'index'
    '/admin': 'admin'
    '/obscure_login_link': 'obscure_login_link'

  Meteor.Router.filters
    'checkLoggedIn': (page) ->
      if Meteor.loggingIn()
        'loading';
      else if Meteor.user()
        page
      else
        'index'

  Meteor.Router.filter 'checkLoggedIn', { except:['obscure_login_link','index'] }

This pushes non-logged in users to the index page and sets up the obscured login path.

Then I moved the {{loginButtons}} bit so it only shows up in the admin and obscure_login_link templates. The only way to create an account is to hit the obscure_login_link which has no external pointers to it, and only allows Github connections, so there are a few hurdles to get over.

Restricted Access based on Login Status (Authorization)

Now, we can assume that there are user accounts in the system. We can use the built-in allow/deny methods in Meteor to restrict data modification.

In our Block model (again, check the previous post to read about the app’s model), we can add the following to models/blocks.coffee

Blocks.allow
  insert: (userId, block) ->
    user = Meteor.user()
    if user
      block.user = user
    user

After this, blocks can only be added by a logged in user. To verify, I ran a couple insert/update requests on the browser console and happily was thwarted by the auth engine.

> Blocks.insert({title:'newblock', color:'olive'})
"f26fa3f8-8400-4ae7-ac94-b6346d6011cf"
insert failed: Access denied

> Blocks.update(Blocks.findOne(), {title:'whatever yo'})
undefined
logging.js:30update failed: Access denied. Can't replace document in restricted collection.

With this small amount of code/config, we’ve now got an app that allows login via Github Oauth and restricts database access to logged in users.

Next steps: getting a test framework setup.

Researching MeteorJS

I’ve been looking to upgrade one of my old sites and learn some more Javascript tricks. The basic site requirements are:

  • easily add blocks of content
  • content blocks should include (optionally) background images, color theme, title, content (markdown or similar) and some metadata
  • admin-only access to edit/update methods
  • test framework should be Jasmine or Mocha

It seemed like this might be a perfect small-ish project to investigate some of the new Javascript web-app frameworks. This post will serve to share some of my findings. Though there are piles of frameworks to choose from (and framework combos), I started off looking into MeteorJS – http://meteorjs.org.

Getting Started

curl https://install.meteor.com | /bin/sh
meteor create /projects/myapp_meteor
cd /projects/myapp_meteor
meteor

At this point, we’ve got a webserver listening at localhost:3000.

Building out the app

I added a Block collection in models/block.coffee; setup admin and index templates under the client/ directory. Then after adding a form admin template, a block template and a loop on the index page to draw all blocks, the basic app was almost in place. The final step was adding meteorite to my system (seems like a must have – think bundler for meteor) and added the meteor-router package to the project (this makes it easy to build out a multi-page app). Finally wuth a bit of file reorg to give the project a semblance of structure, I ended up with a directory tree that looked like this (ignoring .meteor/):


main.html
routes.coffee
client/
  client.coffee
  admin.template.coffee
  index.template.coffee
models/
  blocks.coffee
server/
  server.coffee
stylesheets/
  main.css
views/
  admin.html
  index.html
  block.html

Meteor picked up these changes with out a hiccup – live updates are pretty cool.

Here’s a couple of snippets of the code to see how simple things are:

A collection:

# models/blocks.coffee
Blocks = new Meteor.Collection('blocks')

The template for the block:

<!-- in views/block.html -->
<template name="block">
  <div class="block {{selected}} {{color}}">
    <span class="title">{{title}}</span>
    <div class="content">{{content}}</span>
    <span class="link">{{link}}</span>

    <div class="delete" id="{{id}}"><a href='#'>x</a></div>
  </div>
</template>

The admin template with an input form:

<!-- in views/admin.html - DOM here is bootstrap ready -->
<template name="add_block_inputs">
  <div class="add_block_inputs">
    <div class="row">
      <label>title</label>
      <input id="block_title" type="text" placeholder="enter a title"/>
    </div>
    <div class="row">
      <label>content</label>
      <textarea id="block_content" placeholder="enter some content"></textarea>
    </div>
    <div class="row">
      <label>link</label>
      <input id="block_link" type="text" placeholder="enter link"/>
    </div>
    <div class="row">
      <label>color</label>
      <select id="block_color">
        <option value='dark_purple'>dark purple</option>
        <option value='burgundy'>burgundy</option>
        <option value='olive'>olive</opiton>
      </select>
    </div>
    <input class="btn add_block" type="button" value="add block" />
  </div>
</template>

In the client, we bind the button to submit the new data, and bind a “remove” action on the X for each block.

# in admin.template.coffee
if Meteor.isClient

  Template.admin.blocks = ->
    Blocks.find {},
      sort:
        title: 1

  Template.admin.events
    "click input.add_block": ->
      # template data, if any, is available in 'this'
      tt = @document.getElementById("block_title").value
      md = @document.getElementById("block_content").value
      lk = @document.getElementById("block_link").value
      cl = @document.getElementById("block_color").value
      Blocks.insert title: tt, content: md, link: lk, color: cl if tt || md || lk || cl
      false

    "click .block .delete": ->
      Blocks.remove(this)
      false

And that’s basically it. At this point, Meteor is showing me all my blocks and allows me to add and remove blocks with ease. The collection is auto-tied into a MongoDB instance which is setup automagically (assuming you have MongoDB installed and running on your machine).

The next steps are to figure out authorization to control read/write access to the database and figure out how to get a test framework up and running.

I had lunch with the 2nd Rate Genius team who’ve recently been down this same road. From there investigations, it seems that the Meteor auth stuff has been pretty well developed and should be easy to hook in. And their system of allow/deny on collections should be pretty easy to set up to help control data access.

I’m looking forward to some more learning with this Javascript full-stack solution. As I learn more, I’ll share what I can.

Quick method performance checker

With ruby there are often many ways to perform the same operation. Recently I learned that the * operator can act like join for strings.
I wanted to do a little performance check to see if one is faster than the other.

Using ruby blocks, I wrote up a timed_run method:

def timed_run(*args, &block)
  t = Time.now
  block.call
  dt = (Time.now - t)
  puts "[#{args.first}] #{dt}msec"
end

With this helper, I then wrote up several methods to test joining strings with separators:

def join_with_join(arr, sep)
  arr.join sep
end

def join_with_times(arr, sep)
  arr * sep
end

def join_with_plus(arr, sep)
  val = ''
  arr.each{|a| val += sep if val.length > 0; val += a }
  val
end

def join_with_append(arr, sep)
  val = ''
  arr.each{|a| val << sep if val.length > 0; val << a }
  val
end

Putting this all together, I was able to test my different join implementations for performance:

def gen_random_string
  # exercise for reader
end

num_runs = 100000
arr = (25 + rand(4)).times.map{ gen_random_string }
sep = " , "
%w(join_with_plus join_with_times join_with_join join_with_append).each do |meth|
  timed_run(meth) { num_runs.times { send(meth, arr, sep) }}
end

and very quickly see that the * and join operators are basically the same
in terms of performance. And I learned that the << is slow but much faster than +.

[join_with_append] 1.19829msec
[join_with_join] 0.436115msec
[join_with_times] 0.431106msec
[join_with_plus] 4.26063msec

It's a nice usage of the call method and Ruby blocks. I learned a few tricks and now i have a nice little timer script that I can use to test other implementations of other algorithms.