Permalink
Browse files

Initial commit of libraries and examples

  • Loading branch information...
1 parent 10a22fb commit db49bad194b0a16796ffafbbd9d979568d271285 @dkubb committed Oct 17, 2009
View
@@ -3,3 +3,5 @@
coverage
rdoc
pkg
+tmp
+measurements
View
@@ -1,17 +1,25 @@
+require 'pathname'
require 'rubygems'
require 'rake'
+Pathname.glob('tasks/**/*.rake').each { |task| load task.expand_path }
+
begin
require 'jeweler'
+
Jeweler::Tasks.new do |gem|
- gem.name = 'rack-conneg'
- gem.summary = %Q{TODO: one-line summary of your gem}
- gem.description = %Q{TODO: longer description of your gem}
- gem.email = 'dan.kubb@gmail.com'
- gem.homepage = 'http://github.com/dkubb/rack-conneg'
- gem.authors = [ 'Dan Kubb' ]
+ gem.name = 'rack-conneg'
+ gem.summary = %Q{TODO: one-line summary of your gem}
+ gem.description = %Q{TODO: longer description of your gem}
+ gem.email = 'dan.kubb@gmail.com'
+ gem.homepage = 'http://github.com/dkubb/rack-conneg'
+ gem.authors = [ 'Dan Kubb' ]
gem.rubyforge_project = 'rack-conneg'
+
gem.add_dependency 'rack-acceptable'
+
+ gem.add_development_dependency 'fakefs'
+ gem.add_development_dependency 'rack-test'
gem.add_development_dependency 'rspec'
gem.add_development_dependency 'yard'
end
@@ -22,53 +30,3 @@ begin
rescue LoadError
puts 'Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler'
end
-
-require 'spec/rake/spectask'
-Spec::Rake::SpecTask.new(:spec) do |spec|
- spec.libs << 'lib' << 'spec'
- spec.spec_files = FileList['spec/**/*_spec.rb']
-end
-
-Spec::Rake::SpecTask.new(:rcov) do |spec|
- spec.libs << 'lib' << 'spec'
- spec.pattern = 'spec/**/*_spec.rb'
- spec.rcov = true
-end
-
-task :spec => :check_dependencies
-
-begin
- require 'reek/rake_task'
- Reek::RakeTask.new do |t|
- t.fail_on_error = true
- t.verbose = false
- t.source_files = 'lib/**/*.rb'
- end
-rescue LoadError
- task :reek do
- abort 'Reek is not available. In order to run reek, you must: sudo gem install reek'
- end
-end
-
-begin
- require 'roodi'
- require 'roodi_task'
- RoodiTask.new do |t|
- t.verbose = false
- end
-rescue LoadError
- task :roodi do
- abort 'Roodi is not available. In order to run roodi, you must: sudo gem install roodi'
- end
-end
-
-task :default => :spec
-
-begin
- require 'yard'
- YARD::Rake::YardocTask.new
-rescue LoadError
- task :yardoc do
- abort 'YARD is not available. In order to run yardoc, you must: sudo gem install yard'
- end
-end
View
@@ -0,0 +1,12 @@
+$LOAD_PATH.unshift(::File.join(::File.dirname(__FILE__), '..', 'lib'))
+
+require 'rack/conneg'
+
+use Rack::Conneg::Static, :urls => [ '/example/test' ]
+
+run proc {
+ body = 'Default'
+ length = Rack::Utils.bytesize(body).to_s
+
+ [ 200, { 'Content-Type' => 'text/plain', 'Content-Length' => length }, body ]
+}
View
@@ -0,0 +1,11 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
+<html>
+ <head>
+ <title>This is a test</title>
+ </head>
+ <body>
+ <p>
+ This is a test
+ </p>
+ </body>
+</html>
View
@@ -0,0 +1 @@
+This is a test
View
@@ -0,0 +1,12 @@
+require 'rack/acceptable'
+
+module Rack
+ class Conneg
+ VERSION = '0.1.0'.freeze
+ end
+end
+
+require 'rack/conneg/file'
+require 'rack/conneg/negotiator'
+require 'rack/conneg/path'
+require 'rack/conneg/static'
View
@@ -0,0 +1,14 @@
+module Rack
+ class Conneg
+ class File
+ def initialize(file_server)
+ @file_server = file_server
+ end
+
+ def call(env)
+ Negotiator.negotiate(@file_server, env)
+ end
+
+ end
+ end
+end
@@ -0,0 +1,71 @@
+module Rack
+ class Conneg
+ class Negotiator
+ NOT_ACCEPTABLE = [
+ 406,
+ { 'Content-Type' => 'text/plain', 'Content-Length' => '19' },
+ "406 Not Acceptable\n",
+ ].freeze
+
+ def self.negotiate(app, env)
+ negotiator = new(app, env)
+ negotiator.pass? ? negotiator.pass : negotiator.call
+ end
+
+ def initialize(app, env)
+ @app = app
+ @env = env
+ @request = Acceptable::Request.new(@env)
+ @path = Path.new(app.root, @request.path_info)
+ end
+
+ def pass?
+ not @path.variants?
+ end
+
+ def pass(env = @env)
+ @app.call(env)
+ end
+
+ def call
+ variant = select_variant
+ response = variant ? serve_variant(variant.path_info) : NOT_ACCEPTABLE
+ self.class.append_vary_header(response[1], 'Accept')
+ response
+ end
+
+ private
+
+ def select_variant
+ variants = @path.variants
+ variants[@request.preferred_media_from(*variants.keys)]
+ end
+
+ def serve_variant(path_info)
+ status, headers, body = pass(@env.merge('PATH_INFO' => path_info))
+ headers['Content-Location'] = path_info if (200..299).include?(status)
+ [ status, headers, body ]
+ end
+
+ def self.append_vary_header(headers, *names)
+ vary = split_header(headers['Vary'])
+ return if vary.include?('*')
+ headers['Vary'] = join_header(vary | names)
+ end
+
+ def self.split_header(header)
+ header.to_s.delete(' ').split(',')
+ end
+
+ def self.join_header(values)
+ values.join(',')
+ end
+
+ class << self
+ private :split_header
+ private :join_header
+ end
+
+ end
+ end
+end
View
@@ -0,0 +1,71 @@
+require 'pathname'
+
+module Rack
+ class Conneg
+ class Path
+ EXTENSION_REGEXP = Regexp.union(*Mime::MIME_TYPES.keys.map { |ext| ext[1..-1] }).freeze
+
+ attr_reader :path_info
+
+ def initialize(root, path_info)
+ @root = Pathname(root)
+ @path_info = self.class.normalize_path_info(path_info)
+ @path = @root + @path_info[1..-1]
+ end
+
+ def mime_type
+ Mime.mime_type(extname, nil)
+ end
+
+ def exist?
+ @path.exist?
+ end
+
+ def variants?
+ variants.any?
+ end
+
+ def variants
+ return @variants if @variants
+ variants = {}
+ variant_paths.each { |path| variants[path.mime_type] = path }
+ @variants = variants.freeze
+ end
+
+ private
+
+ def variant_paths
+ paths.select { |path| variant?(path) && path.exist? }
+ rescue SystemCallError
+ []
+ end
+
+ def paths
+ directory.map do |path|
+ self.class.new(@root, path.relative_path_from(@root))
+ end
+ end
+
+ def directory
+ @path.dirname.children
+ end
+
+ def extname
+ @path.extname
+ end
+
+ def variant?(other)
+ pattern === other.path_info
+ end
+
+ def pattern
+ @pattern ||= /\A#{Regexp.escape(path_info)}\.#{EXTENSION_REGEXP}\z/.freeze
+ end
+
+ def self.normalize_path_info(path_info)
+ "/#{Utils.unescape(path_info.to_s).gsub(/\A\//, '')}"
+ end
+
+ end
+ end
+end
View
@@ -0,0 +1,11 @@
+module Rack
+ class Conneg
+ class Static < Rack::Static
+ def initialize(*)
+ super
+ @file_server = File.new(@file_server)
+ end
+
+ end
+ end
+end
Oops, something went wrong.

0 comments on commit db49bad

Please sign in to comment.