# -*- coding: utf-8 -*-
require 'sinatra/base'
require 'thin'
module Gemirro
##
# Launch Sinatra server to easily download gems.
#
# @!attribute [r] versions_fetcher
# @return [VersionsFetcher]
# @!attribute [r] gems_fetcher
# @return [Gemirro::GemsFetcher]
#
class Server < Sinatra::Base
attr_accessor :versions_fetcher, :gems_fetcher
access_logger = Logger.new(Gemirro.configuration.server.access_log)
.tap do |logger|
::Logger.class_eval { alias_method :write, :'<<' }
logger.level = ::Logger::INFO
end
error_logger = File.new(Gemirro.configuration.server.error_log, 'a+')
error_logger.sync = true
before do
Gemirro.configuration.logger = access_logger
env['rack.errors'] = error_logger
end
##
# Configure server
#
configure do
config = Gemirro.configuration
config.server.host = 'localhost' if config.server.host.nil?
config.server.port = '2000' if config.server.port.nil?
set :port, config.server.port
set :bind, config.server.host
set :destination, config.destination.gsub(/\/$/, '')
set :environment, config.environment
enable :logging
use Rack::CommonLogger, access_logger
end
##
# Try to get all request and download files
# if files aren't found.
#
# @return [nil]
#
get('*') do |path|
resource = "#{settings.destination}#{path}"
# Try to download gem if file doesn't exists
fetch_gem(resource) unless File.exist?(resource)
# If not found again, return a 404
return not_found unless File.exist?(resource)
if File.directory?(resource)
display_directory(resource)
else
send_file resource
end
end
##
# Try to fetch gem and download its if it's possible, and
# build and install indicies.
#
# @param [String] resource
# @return [Indexer]
#
def fetch_gem(resource)
name = File.basename(resource)
regexp = /^(.*)-(\d+(?:\.\d+){,4})\.gem(?:spec\.rz)?$/
result = name.match(regexp)
return unless result
gem_name, gem_version = result.captures
return unless gem_name && gem_version
logger.info("Try to download #{gem_name} with version #{gem_version}")
begin
gems_fetcher.source.gems.clear
gems_fetcher.source.gems.push(Gemirro::Gem.new(gem_name, gem_version))
gems_fetcher.fetch
rescue StandardError => e
logger.error(e.message)
end
update_gemspecs
end
##
# Update gemspecs files
#
# @return [Indexer]
#
def update_gemspecs
indexer = Indexer.new(settings.destination)
indexer.ui = ::Gem::SilentUI.new
logger.info('Updating gemspecs files...')
indexer.update_gemspecs
logger.info('Done')
end
##
# Display directory on the current sesion
#
# @param [String] resource
# @return [Array]
#
def display_directory(resource)
base_dir = Dir.new(resource)
base_dir.entries.sort.map do |f|
dir_sign = ''
resource_path = resource.gsub(/\/$/, '') + '/' + f
dir_sign = '/' if File.directory?(resource_path)
resource_path = resource_path.gsub(/^public\//, '')
resource_path = resource_path.gsub(settings.destination, '')
"#{f}#{dir_sign}
" \
unless ['.', '..'].include?(File.basename(resource_path))
end.compact
end
##
# @see Gemirro.configuration
#
def configuration
Gemirro.configuration
end
##
# @see Gemirro::VersionsFetcher.fetch
#
def versions_fetcher
@versions_fetcher ||= Gemirro::VersionsFetcher
.new(configuration.source).fetch
end
##
# @return [Gemirro::GemsFetcher]
#
def gems_fetcher
@gems_fetcher ||= Gemirro::GemsFetcher.new(
configuration.source, versions_fetcher)
end
##
# @see Gemirro::Configuration#logger
# @return [Logger]
#
def logger
configuration.logger
end
end
end