| 1 |
require 'sinatra'
|
| 2 |
require 'haml'
|
| 3 |
require 'restclient'
|
| 4 |
require 'json'
|
| 5 |
require 'gem2tgz'
|
| 6 |
|
| 7 |
require 'open-uri'
|
| 8 |
|
| 9 |
DOWNLOAD_BASE = File.join(File.dirname(__FILE__), 'tmp', 'download')
|
| 10 |
ASSETS_BASE = File.join(File.dirname(__FILE__), 'public')
|
| 11 |
|
| 12 |
module GemWatch
|
| 13 |
class << self
|
| 14 |
attr_accessor :assets_path, :prefix
|
| 15 |
end
|
| 16 |
self.assets_path = ""
|
| 17 |
self.prefix = ""
|
| 18 |
def asset_url(url)
|
| 19 |
GemWatch.assets_path + url + '?' + File.mtime(ASSETS_BASE + url).to_i.to_s
|
| 20 |
end
|
| 21 |
def app_url(url)
|
| 22 |
GemWatch.prefix + url
|
| 23 |
end
|
| 24 |
end
|
| 25 |
|
| 26 |
class GemWatch::Gem
|
| 27 |
class NotFound < Exception; end
|
| 28 |
class CommandFailed < Exception; end
|
| 29 |
def initialize(gemname)
|
| 30 |
begin
|
| 31 |
@data = JSON.parse(RestClient.get("http://rubygems.org/api/v1/gems/#{gemname}.json").body)
|
| 32 |
rescue RestClient::ResourceNotFound
|
| 33 |
raise GemWatch::Gem::NotFound
|
| 34 |
end
|
| 35 |
end
|
| 36 |
def name
|
| 37 |
@data['name']
|
| 38 |
end
|
| 39 |
def version
|
| 40 |
@data['version']
|
| 41 |
end
|
| 42 |
def uri
|
| 43 |
@data['gem_uri']
|
| 44 |
end
|
| 45 |
def info
|
| 46 |
@data['info']
|
| 47 |
end
|
| 48 |
def home
|
| 49 |
@data['project_uri']
|
| 50 |
end
|
| 51 |
def authors
|
| 52 |
@data['authors']
|
| 53 |
end
|
| 54 |
def gem
|
| 55 |
File.basename(uri)
|
| 56 |
end
|
| 57 |
def run(cmd)
|
| 58 |
system(cmd)
|
| 59 |
if $? && ($? >> 8) > 0
|
| 60 |
raise GemWatch::Gem::CommandFailed, "[#{cmd} failed!]"
|
| 61 |
end
|
| 62 |
end
|
| 63 |
def download(uri)
|
| 64 |
open(uri, 'rb') do |downloaded_gem|
|
| 65 |
File.open(gem, 'wb') do |file|
|
| 66 |
file.write(downloaded_gem.read)
|
| 67 |
end
|
| 68 |
end
|
| 69 |
end
|
| 70 |
def download_and_convert!
|
| 71 |
unless File.exist?(tarball_path)
|
| 72 |
FileUtils.mkdir_p(download_dir)
|
| 73 |
Dir.chdir(download_dir) do
|
| 74 |
download uri
|
| 75 |
Gem2Tgz.convert!(gem, tarball)
|
| 76 |
FileUtils.rm_f(gem)
|
| 77 |
end
|
| 78 |
end
|
| 79 |
end
|
| 80 |
def directory
|
| 81 |
"#{name}-#{version}"
|
| 82 |
end
|
| 83 |
def download_dir
|
| 84 |
@download_dir ||= File.join(DOWNLOAD_BASE, name[0..0], name)
|
| 85 |
end
|
| 86 |
def tarball
|
| 87 |
"#{directory}.tar.gz"
|
| 88 |
end
|
| 89 |
def tarball_path
|
| 90 |
File.join(download_dir, tarball)
|
| 91 |
end
|
| 92 |
def archived
|
| 93 |
@archived ||= Dir.glob(File.join(download_dir, '*.tar.gz')).map { |f| File.basename(f) }
|
| 94 |
end
|
| 95 |
def archived?(tarball)
|
| 96 |
archived.include?(tarball)
|
| 97 |
end
|
| 98 |
def archived_path(tarball)
|
| 99 |
File.join(download_dir, tarball)
|
| 100 |
end
|
| 101 |
def download_for(wanted_version)
|
| 102 |
tarball = "#{name}-#{wanted_version}.tar.gz"
|
| 103 |
if archived?(tarball)
|
| 104 |
archived_path(tarball)
|
| 105 |
else
|
| 106 |
if wanted_version == version
|
| 107 |
download_and_convert!
|
| 108 |
tarball_path
|
| 109 |
else
|
| 110 |
raise GemWatch::Gem::NotFound
|
| 111 |
end
|
| 112 |
end
|
| 113 |
end
|
| 114 |
end
|
| 115 |
module HostHelper
|
| 116 |
def host_with_port
|
| 117 |
if request.respond_to?(:host_with_port)
|
| 118 |
request.host_with_port
|
| 119 |
else
|
| 120 |
request.host + ([80,443].include?(request.port) ? '' : (':' + request.port.to_s))
|
| 121 |
end
|
| 122 |
end
|
| 123 |
end
|
| 124 |
|
| 125 |
helpers GemWatch, HostHelper
|
| 126 |
|
| 127 |
get '/?' do
|
| 128 |
expires 86400, :public # 1 day
|
| 129 |
if params[:gem]
|
| 130 |
redirect app_url("/#{params[:gem]}")
|
| 131 |
else
|
| 132 |
haml :index
|
| 133 |
end
|
| 134 |
end
|
| 135 |
|
| 136 |
get '/:gem' do
|
| 137 |
expires 14400, :public # 4 hours
|
| 138 |
begin
|
| 139 |
@gem = GemWatch::Gem.new(params[:gem])
|
| 140 |
haml :gem
|
| 141 |
rescue GemWatch::Gem::NotFound
|
| 142 |
not_found
|
| 143 |
end
|
| 144 |
end
|
| 145 |
|
| 146 |
get '/download/:tarball' do
|
| 147 |
expires 86400000, :public # 1000 days, published versions are supposed to not change
|
| 148 |
begin
|
| 149 |
params[:tarball] =~ /^(.+)-(.+).tar.gz$/
|
| 150 |
gem_name = $1
|
| 151 |
gem_version = $2
|
| 152 |
|
| 153 |
gem = GemWatch::Gem.new(gem_name)
|
| 154 |
send_file gem.download_for(gem_version)
|
| 155 |
rescue GemWatch::Gem::NotFound
|
| 156 |
not_found
|
| 157 |
end
|
| 158 |
end
|
| 159 |
|
| 160 |
not_found do
|
| 161 |
haml :not_found
|
| 162 |
end
|
| 163 |
|
| 164 |
error 500 do
|
| 165 |
haml :error
|
| 166 |
end
|