Skip to content

Instantly share code, notes, and snippets.

@kimoto
Created December 13, 2012 11:00
Show Gist options
  • Save kimoto/4275733 to your computer and use it in GitHub Desktop.
Save kimoto/4275733 to your computer and use it in GitHub Desktop.
Tinyurl sinatra + datamapper
#!/bin/env ruby
# encoding: utf-8
# Author: kimoto
require 'sinatra'
require 'erubis'
require 'dm-core'
require 'dm-migrations'
require 'dm-timestamps'
# Models
class ShortUrl
# properties
include DataMapper::Resource
property :id, Serial
property :digest, String, :length => 64, :unique_index => true
property :url, String, :length => 1024, :unique => true
timestamps :at
# exceptions
class MaxRetryError < StandardError; end
# class methods
MAX_RETRY = 100
@@table = [
('0' .. '9'),
('a' .. 'z'),
('A' .. 'Z'),
["-", "_"]
].map(&:to_a).flatten.freeze
# copied from http://lokka.kksg.net/bitly
def self.conv64(i)
digit = []
begin
i, c = i.divmod(62)
digit << @@table[c+1]
end while i > 0
return digit.reverse.join('')
end
def self.gen_random_digest
self.conv64(rand(256**4)) # 256**4 (4294967296) pattern
end
def self.digest_to_url(digest)
if r = self.first(:digest => digest)
r.url
else
nil
end
end
def self.url_to_digest(url)
self.first_or_create(:url => url).digest
end
def digest_already_exist?
if self.class.first(:digest => self.digest)
return true
else
return false
end
end
def gen_digest
MAX_RETRY.times{ |n|
self.digest = self.class.gen_random_digest
if not digest_already_exist?
return
end
}
raise MaxRetryError
end
before :create do |short_url|
short_url.gen_digest
end
end
# Sinatra
configure do
enable :inline_templates
set :erb, :escape_html => true
DataMapper.setup(:default, "mysql://127.0.0.1/tinyurl")
DataMapper.finalize
DataMapper.auto_upgrade!
end
helpers do
def base_url
@base_url ||= "#{request.env['rack.url_scheme']}://#{request.env['HTTP_HOST']}"
end
end
get '/' do
erb :index
end
get '/:digest' do
if url = ShortUrl.digest_to_url(params[:digest])
redirect url
else
not_found
end
end
post '/new' do
begin
digest = ShortUrl.url_to_digest(params[:url])
redirect URI.join(base_url, "/created/#{digest}").to_s
rescue => RetryMaxError
not_found
end
end
get '/created/:digest' do
URI.join(base_url, ShortUrl.first(:digest => params[:digest]).digest).to_s
end
not_found do
"404 Not Found"
end
__END__
@@ index
<h1>shorturl</h1>
<form method="post" action="/new">
<input type="text" name="url" style="width: 500px;" value="" />
<input type="submit" value="create" />
</form>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment