shawty-server 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.markdown +32 -0
- data/Rakefile +28 -0
- data/VERSION +1 -0
- data/config/database.yml +14 -0
- data/config.ru +6 -0
- data/shawty-server.gemspec +50 -0
- data/shawty.rb +101 -0
- data/test.rb +95 -0
- metadata +81 -0
data/README.markdown
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# Shawty
|
2
|
+
|
3
|
+
|
4
|
+
Shawty is the simplest Ruby url-shortening app ever. Run your own server that works like [tinyurl.com](tinyurl.com).
|
5
|
+
|
6
|
+
|
7
|
+
## What you need to get this running
|
8
|
+
|
9
|
+
* Clone this: `git clone git://github.com/JackDanger/shawty-server.git`
|
10
|
+
* Signup for an account at Heroku ([better details here](http://github.com/sinatra/heroku-sinatra-app))
|
11
|
+
* push this to Heroku.com: `git push heroku master`
|
12
|
+
* Revel.
|
13
|
+
|
14
|
+
|
15
|
+
## Why?
|
16
|
+
|
17
|
+
If you run any application with published content it's helpful to have shorter links to that content.
|
18
|
+
|
19
|
+
Consider that your Rails app might offer pages like the following:
|
20
|
+
|
21
|
+
http://myapp.com/accounts/megacorp/invoices?single_access_token=5ASD32ADf89JKASF2346
|
22
|
+
|
23
|
+
Imagine trying to fit that in a text message. Instead, how about:
|
24
|
+
|
25
|
+
http://url.myapp.com/a7D
|
26
|
+
|
27
|
+
Way groovy.
|
28
|
+
|
29
|
+
|
30
|
+
Patches welcome, forks celebrated.
|
31
|
+
|
32
|
+
Copyright (c) 2009 [Jack Danger Canty](http://jåck.com). See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
begin
|
2
|
+
require 'jeweler'
|
3
|
+
Jeweler::Tasks.new do |gem|
|
4
|
+
gem.name = "shawty-server"
|
5
|
+
gem.summary = %Q{Ultra-lightweight url shortening server for Heroku.com}
|
6
|
+
gem.description = %Q{Run your own url shortener add your own web address for free on Heroku.com}
|
7
|
+
gem.email = "gitcommit@6brand.com"
|
8
|
+
gem.homepage = "http://github.com/JackDanger/shawty-server"
|
9
|
+
gem.authors = ["Jack Danger Canty"]
|
10
|
+
gem.add_development_dependency "active_record", ">= 0"
|
11
|
+
gem.add_development_dependency "shoulda", ">= 0"
|
12
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
13
|
+
end
|
14
|
+
Jeweler::GemcutterTasks.new
|
15
|
+
rescue LoadError
|
16
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
|
21
|
+
task :default => :test
|
22
|
+
|
23
|
+
require 'rake/testtask'
|
24
|
+
Rake::TestTask.new(:test) do |test|
|
25
|
+
test.libs << '.'
|
26
|
+
test.pattern = 'test.rb'
|
27
|
+
test.verbose = true
|
28
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.0.0
|
data/config/database.yml
ADDED
data/config.ru
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{shawty-server}
|
8
|
+
s.version = "1.0.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Jack Danger Canty"]
|
12
|
+
s.date = %q{2010-03-26}
|
13
|
+
s.description = %q{Run your own url shortener add your own web address for free on Heroku.com}
|
14
|
+
s.email = %q{gitcommit@6brand.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"README.markdown"
|
17
|
+
]
|
18
|
+
s.files = [
|
19
|
+
"README.markdown",
|
20
|
+
"Rakefile",
|
21
|
+
"VERSION",
|
22
|
+
"config.ru",
|
23
|
+
"config/database.yml",
|
24
|
+
"shawty-server.gemspec",
|
25
|
+
"shawty.rb",
|
26
|
+
"test.rb"
|
27
|
+
]
|
28
|
+
s.homepage = %q{http://github.com/JackDanger/shawty-server}
|
29
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
30
|
+
s.require_paths = ["lib"]
|
31
|
+
s.rubygems_version = %q{1.3.5}
|
32
|
+
s.summary = %q{Ultra-lightweight url shortening server for Heroku.com}
|
33
|
+
|
34
|
+
if s.respond_to? :specification_version then
|
35
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
36
|
+
s.specification_version = 3
|
37
|
+
|
38
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
39
|
+
s.add_development_dependency(%q<active_record>, [">= 0"])
|
40
|
+
s.add_development_dependency(%q<shoulda>, [">= 0"])
|
41
|
+
else
|
42
|
+
s.add_dependency(%q<active_record>, [">= 0"])
|
43
|
+
s.add_dependency(%q<shoulda>, [">= 0"])
|
44
|
+
end
|
45
|
+
else
|
46
|
+
s.add_dependency(%q<active_record>, [">= 0"])
|
47
|
+
s.add_dependency(%q<shoulda>, [">= 0"])
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
data/shawty.rb
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
## Resources
|
2
|
+
require 'rubygems'
|
3
|
+
gem 'sinatra', :version => '1.0'
|
4
|
+
require 'sinatra'
|
5
|
+
require 'active_record'
|
6
|
+
gem 'alphadecimal'
|
7
|
+
require 'alphadecimal'
|
8
|
+
|
9
|
+
## Application
|
10
|
+
|
11
|
+
get '/' do
|
12
|
+
"Post to '/' to save a url and receive a plaintext short url in response" +
|
13
|
+
"<br />" +
|
14
|
+
"Example: POST /http://some.url/at.someplace" +
|
15
|
+
"<br />" +
|
16
|
+
"<form action=/ method=POST onsubmit='if(\"\"==this.url.value)return false;else{this.action=\"/\"+this.url.value}'><input type=text name='url' /><input type=submit value='Get Shawty' /></form>"
|
17
|
+
end
|
18
|
+
|
19
|
+
get '/:id' do
|
20
|
+
pass unless url = find_url_by_id(params[:id].alphadecimal)
|
21
|
+
|
22
|
+
redirect url, 301
|
23
|
+
end
|
24
|
+
|
25
|
+
post '*' do
|
26
|
+
pass if params[:splat].empty?
|
27
|
+
|
28
|
+
url = params[:splat].first
|
29
|
+
url = url[1, url.size] if url.chars.first == '/'
|
30
|
+
|
31
|
+
pass if url.empty?
|
32
|
+
|
33
|
+
quoted = quote url
|
34
|
+
|
35
|
+
found = execute %Q{ SELECT id FROM #{table_name} WHERE url = #{quoted} }
|
36
|
+
|
37
|
+
if found.any?
|
38
|
+
id = found.first['id']
|
39
|
+
else
|
40
|
+
connection.transaction do
|
41
|
+
found = execute %Q{
|
42
|
+
INSERT INTO #{table_name} (url, id) VALUES (#{quoted}, nextval('shawty_id_seq'));
|
43
|
+
SELECT MAX(id) from #{table_name}
|
44
|
+
}
|
45
|
+
end
|
46
|
+
id = found.first['max']
|
47
|
+
end
|
48
|
+
|
49
|
+
"http://#{request.host}/#{id.alphadecimal}"
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
## Helpers
|
54
|
+
|
55
|
+
def find_url_by_id(id)
|
56
|
+
result = execute %Q{ SELECT url FROM #{table_name} WHERE id = #{quote id.to_i} }
|
57
|
+
return result.map.first['url'] if result.map.length > 0
|
58
|
+
end
|
59
|
+
|
60
|
+
def execute sql
|
61
|
+
connection.execute sql
|
62
|
+
end
|
63
|
+
|
64
|
+
def quote string
|
65
|
+
connection.quote string
|
66
|
+
end
|
67
|
+
|
68
|
+
def table_name
|
69
|
+
connection.quote_table_name 'shawty'
|
70
|
+
end
|
71
|
+
|
72
|
+
def connection
|
73
|
+
ActiveRecord::Base.connection
|
74
|
+
end
|
75
|
+
|
76
|
+
def initialize_database
|
77
|
+
table = execute %Q{select * from pg_tables where tablename = #{quote 'shawty'}}
|
78
|
+
return if table.any?
|
79
|
+
execute %Q{
|
80
|
+
CREATE TABLE #{table_name} (
|
81
|
+
id SERIAL PRIMARY KEY,
|
82
|
+
url varchar(255) UNIQUE
|
83
|
+
)
|
84
|
+
}
|
85
|
+
end
|
86
|
+
|
87
|
+
def database_config
|
88
|
+
YAML.load(File.read('config/database.yml'))
|
89
|
+
end
|
90
|
+
|
91
|
+
def init(environment)
|
92
|
+
ActiveRecord::Base.establish_connection database_config[environment]
|
93
|
+
initialize_database
|
94
|
+
end
|
95
|
+
|
96
|
+
|
97
|
+
## Environments
|
98
|
+
|
99
|
+
configure :development do init 'development' end
|
100
|
+
configure :test do init 'test' end
|
101
|
+
configure :production do init 'production' end
|
data/test.rb
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
ENV['RACK_ENV'] = 'test'
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'test/unit'
|
5
|
+
require 'active_support'
|
6
|
+
require 'shoulda'
|
7
|
+
require 'rack/test'
|
8
|
+
require 'shawty'
|
9
|
+
|
10
|
+
class ShawtyTest < Test::Unit::TestCase
|
11
|
+
include Rack::Test::Methods
|
12
|
+
|
13
|
+
def setup
|
14
|
+
execute "DELETE FROM #{table_name}"
|
15
|
+
end
|
16
|
+
|
17
|
+
def app
|
18
|
+
Sinatra::Application
|
19
|
+
end
|
20
|
+
|
21
|
+
context "on GET to /" do
|
22
|
+
setup {
|
23
|
+
get '/'
|
24
|
+
}
|
25
|
+
should "return ok" do
|
26
|
+
assert last_response.ok?
|
27
|
+
end
|
28
|
+
should "have some kind of welcome" do
|
29
|
+
assert last_response.body =~ /to save a url/
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context "on GET to / with url" do
|
34
|
+
setup {
|
35
|
+
execute %Q{
|
36
|
+
INSERT INTO #{table_name} (url, id)
|
37
|
+
VALUES (
|
38
|
+
#{quote 'http://google.com/'},
|
39
|
+
nextval('shawty_id_seq')
|
40
|
+
)
|
41
|
+
}
|
42
|
+
id = execute(%Q{
|
43
|
+
SELECT id FROM #{table_name}
|
44
|
+
WHERE url = #{quote 'http://google.com/'}
|
45
|
+
}).first['id'].to_i
|
46
|
+
|
47
|
+
get "/#{id.alphadecimal}"
|
48
|
+
}
|
49
|
+
should "return a 301 redirect" do
|
50
|
+
assert_equal 301, last_response.status
|
51
|
+
end
|
52
|
+
should "return Location: header to url" do
|
53
|
+
assert_equal 'http://google.com/',
|
54
|
+
last_response.headers['Location']
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context "on POST to /" do
|
59
|
+
setup {
|
60
|
+
post '/http://some.url/path.ext'
|
61
|
+
}
|
62
|
+
should "return ok" do
|
63
|
+
assert last_response.ok?
|
64
|
+
end
|
65
|
+
should_change "record count", :by => 1 do
|
66
|
+
execute(%Q{ SELECT COUNT(*) FROM #{table_name} }).first['count'].to_i
|
67
|
+
end
|
68
|
+
should "save the url" do
|
69
|
+
res = execute(%Q{
|
70
|
+
SELECT * FROM #{table_name}
|
71
|
+
WHERE url = #{quote 'http://some.url/path.ext'}
|
72
|
+
})
|
73
|
+
assert res.one?, res.map.inspect
|
74
|
+
end
|
75
|
+
should "display the shortened url" do
|
76
|
+
id = execute(%Q{
|
77
|
+
SELECT id FROM #{table_name}
|
78
|
+
WHERE url = #{quote 'http://some.url/path.ext'}
|
79
|
+
}).first['id']
|
80
|
+
assert_equal "http://example.org/#{id.alphadecimal}",
|
81
|
+
last_response.body
|
82
|
+
end
|
83
|
+
context "with a url that's been saved previously" do
|
84
|
+
setup {
|
85
|
+
post '/http://some.url/path.ext'
|
86
|
+
}
|
87
|
+
should "return ok" do
|
88
|
+
assert last_response.ok?
|
89
|
+
end
|
90
|
+
should_not_change "record count" do
|
91
|
+
execute(%Q{ SELECT COUNT(*) FROM #{table_name} }).first['count'].to_i
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
metadata
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: shawty-server
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jack Danger Canty
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2010-03-26 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: active_record
|
17
|
+
type: :development
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: shoulda
|
27
|
+
type: :development
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: "0"
|
34
|
+
version:
|
35
|
+
description: Run your own url shortener add your own web address for free on Heroku.com
|
36
|
+
email: gitcommit@6brand.com
|
37
|
+
executables: []
|
38
|
+
|
39
|
+
extensions: []
|
40
|
+
|
41
|
+
extra_rdoc_files:
|
42
|
+
- README.markdown
|
43
|
+
files:
|
44
|
+
- README.markdown
|
45
|
+
- Rakefile
|
46
|
+
- VERSION
|
47
|
+
- config.ru
|
48
|
+
- config/database.yml
|
49
|
+
- shawty-server.gemspec
|
50
|
+
- shawty.rb
|
51
|
+
- test.rb
|
52
|
+
has_rdoc: true
|
53
|
+
homepage: http://github.com/JackDanger/shawty-server
|
54
|
+
licenses: []
|
55
|
+
|
56
|
+
post_install_message:
|
57
|
+
rdoc_options:
|
58
|
+
- --charset=UTF-8
|
59
|
+
require_paths:
|
60
|
+
- lib
|
61
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
62
|
+
requirements:
|
63
|
+
- - ">="
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: "0"
|
66
|
+
version:
|
67
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: "0"
|
72
|
+
version:
|
73
|
+
requirements: []
|
74
|
+
|
75
|
+
rubyforge_project:
|
76
|
+
rubygems_version: 1.3.5
|
77
|
+
signing_key:
|
78
|
+
specification_version: 3
|
79
|
+
summary: Ultra-lightweight url shortening server for Heroku.com
|
80
|
+
test_files: []
|
81
|
+
|