Skip to content

Instantly share code, notes, and snippets.

@timlinquist
Last active August 29, 2015 14:26
Show Gist options
  • Save timlinquist/5a937f1e7bd3623b9c03 to your computer and use it in GitHub Desktop.
Save timlinquist/5a937f1e7bd3623b9c03 to your computer and use it in GitHub Desktop.

As a user of bit.ly, I wish I could determine when a bit.ly URL could be used. For example, I have a cute kitten website with a special offer that I want to link to, but the bit.ly link for it shouldn't work until a specified date.

First version: Depend on URI underneath as consistent type to operate on from function to function. Use Digest/SHA1 to create guid for path.

Second version: Provide a real url shortener using base32. Base32 provides a more human readable encoding and excludes I L O U which should keep us safe from any cuss words leaking in once we get to ROFLSCALE. Based on ideas in this SO post: http://stackoverflow.com/questions/742013/how-to-code-a-url-shortener

require 'uri'
require 'digest/sha1'
class BitlyURLRewriter
DOMAIN = "later.ly"
def update_domain(url)
URI.parse(url).tap { |uri| uri.host = "later.ly" }
end
# Generate a guid via secure digest of message (path)
# Should use md5 since sha1 isn't very secure anyhow and
# it's slower than md5
#
def obfuscate_url(uri)
uri.tap {|uri| uri.path = "/#{Digest::SHA1.base64digest uri.path}" }
end
# logic to see if date has passed either opening or closing date
# validates input likely Date.parse will work
#
def check_available(date)
end
end
require 'uri'
module DB
@@urls = {}
def self.urls
@@urls
end
def self.reset!
@@urls = {}
end
end
class BitlyURLRewriter
DOMAIN = "later.ly"
# Update the host to the new host
#
# @param url [String]
# @returns [URI] uri with modified host
#
def update_host(url)
URI.parse(url).tap { |uri| uri.host = "later.ly" }
end
# Insert url into db
#
# @param url [String] original url to store in db
# @returns [String] url
# @returns [Fixnum] pk of inserted url
# @returns [nil] nil for pk if none inserted
#
def insert(url)
pk = Random.rand(10000)
uri = update_host(url)
DB::urls[pk] = uri.to_s
return uri, pk
end
# Find url to redirect to given encoded url
# @param url [URI,String] encoded url either URI or string
# @returns [String] original url inserted into db
#
def find(url)
path = if url.respond_to?(:path)
url.path
else
URI.parse(url).path
end
path.gsub!(/\//, "")
DB::urls[decode_path(path)]
end
# Modify path of uri with encoded pk
#
# @param uri [URI]
# @param pk [Fixnum]
# @return [URI] URI with path updated
#
def encode_uri(uri, pk)
uri.tap { |uri| uri.path = "/#{encode_pk(pk)}" }
end
# Convert base32 back to base10 (pk)
#
def decode_path(path)
path.to_i(32)
end
# Convert pk to base32
#
def encode_pk(pk)
pk.to_s(32)
end
# logic to see if date has passed either opening or closing date
# validates input likely Date.parse will work
#
def check_available(date)
end
end
require 'minitest/autorun'
require_relative './bitly'
class BitlyURLRewriterTest < Minitest::Test
def setup
DB::reset!
@url = "http://awesomesauce.com/specialoffer"
@rewriter = BitlyURLRewriter.new
@uri, @pk = @rewriter.insert(@url)
end
def test_insert_updates_domain
assert @uri.to_s =~ /http:\/\/#{BitlyURLRewriter::DOMAIN}\/specialoffer/, "#{@uri.path} was not rewritten to later.ly"
end
def test_generates_a_shortened_url
assert_equal @rewriter.encode_uri(@uri, @pk).to_s, "http://#{BitlyURLRewriter::DOMAIN}/#{@rewriter.encode_pk(@pk)}"
end
def test_redirects_to_shortened_url
assert_equal @rewriter.find(@rewriter.encode_uri(@uri, @pk)), "http:\/\/#{BitlyURLRewriter::DOMAIN}\/specialoffer"
end
def test_random_insertions_to_verify_encoding
1000.times do
DB::reset!
@uri, @pk = @rewriter.insert(@url)
assert_equal @rewriter.find(@rewriter.encode_uri(@uri, @pk)), "http:\/\/#{BitlyURLRewriter::DOMAIN}\/specialoffer"
end
end
#TODO: implement
def test_rewrite_with_nil_input
end
#TODO: implement
def test_rewrite_with_non_string_values
end
#TODO: implement
def test_rewrite_with_same_string_different_values
end
def test_rewrite_domain_to_laterly
rewriter = BitlyURLRewriter.new
assert_equal "http://later.ly/specialoffer", rewriter.update_host(@url).to_s
end
end
require 'test/unit'
require_relative './bitly'
class BitlyURLRewriterTest < Test::Unit::TestCase
def setup
@url = "http://awesomesauce.com/specialoffer"
end
def test_rewrite
rewriter = BitlyURLRewriter.new
assert rewriter.obfuscate_url(rewriter.update_domain(@url)).to_s =~ /^http:\/\/#{BitlyURLRewriter::DOMAIN}\/.*/
end
#TODO: implement
def test_rewrite_with_nil_input
end
#TODO: implement
def test_rewrite_with_non_string_values
end
#TODO: implement
def test_rewrite_with_same_string_different_values
end
def test_rewrite_domain_to_laterly
rewriter = BitlyURLRewriter.new
assert_equal "http://later.ly/specialoffer", rewriter.update_domain(@url).to_s
end
def test_hash_domain
rewriter = BitlyURLRewriter.new
assert_nil rewriter.obfuscate_url(URI::parse(@url)).to_s =~ /\/specialoffer$/
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment