diff --git a/README.markdown b/README.markdown index 616bf2b..c2af566 100644 --- a/README.markdown +++ b/README.markdown @@ -20,7 +20,9 @@ This will create a Rails 2.3.2 app with Heroku-recommended code: * jQuery for Javascript * Clearance for authentication * Cucumber, Shoulda, Factory Girl, & Mocha for testing -* Evergreen for CSS framework +* Flutie for CSS framework +* Formtastic for form builders +* Inherited Resources for RESTful controllers * Blitz for features, model, controller, & helper generators If you don't have all the necessary gems, they will be installed. diff --git a/vendor/gems/mime-types-1.16/.specification b/vendor/gems/mime-types-1.16/.specification new file mode 100644 index 0000000..536b3ec --- /dev/null +++ b/vendor/gems/mime-types-1.16/.specification @@ -0,0 +1,133 @@ +--- !ruby/object:Gem::Specification +name: mime-types +version: !ruby/object:Gem::Version + version: "1.16" +platform: ruby +authors: +- Austin Ziegler +autorequire: +bindir: bin +cert_chain: +- | + -----BEGIN CERTIFICATE----- + MIIDNjCCAh6gAwIBAgIBADANBgkqhkiG9w0BAQUFADBBMQ8wDQYDVQQDDAZhdXN0 + aW4xGTAXBgoJkiaJk/IsZAEZFglydWJ5Zm9yZ2UxEzARBgoJkiaJk/IsZAEZFgNv + cmcwHhcNMDcxMTMwMDE0OTAwWhcNMDgxMTI5MDE0OTAwWjBBMQ8wDQYDVQQDDAZh + dXN0aW4xGTAXBgoJkiaJk/IsZAEZFglydWJ5Zm9yZ2UxEzARBgoJkiaJk/IsZAEZ + FgNvcmcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDOSg1riVV22ord + q0t4YVx57GDPMqdjlnQ5M7D9iBnnW0c8pifegbb0dm+mC9hJSuPtcJS53+YPTy9F + wlZbjI2cN+P0QLUUTOlZus2sHq7Pr9jz2nJf8hCT7t5Vlopv1N/xlKtXqpcyEkhJ + JHTrxe1avGwuq8DIAIN01moQJ+hJlgrnR2eRJRazTGiXKBLGAFXDl/Agn78MHx6w + pzZ2APydo6Nuk7Ktq1MvCHzLzCACbOlYawFk3/9dbsmHhVjsi6YW+CpEJ2BnTy8X + JBXlyNTk1JxDmcs3RzNr+9AmDQh3u4LcmJnWxtLJo9e7UBxH/bwVORJyf8dAOmOm + HO6bFTLvAgMBAAGjOTA3MAkGA1UdEwQCMAAwCwYDVR0PBAQDAgSwMB0GA1UdDgQW + BBT9e1+pFfcV1LxVxILANqLtZzI/XTANBgkqhkiG9w0BAQUFAAOCAQEAhg42pvrL + uVlqAaHqV88KqgnW2ymCWm0ePohicFTcyiS5Yj5cN3OXLsPV2x12zqvLCFsfpA4u + D/85rngKFHITSW0h9e/CIT/pwQA6Uuqkbr0ypkoU6mlNIDS10PlK7aXXFTCP9X3f + IndAajiNRgKwb67nj+zpQwHa6dmooyRQIRRijrMKTgY6ebaCCrm7J3BLLTJAyxOW + +1nD0cuTkBEKIuSVK06E19Ml+xWt2bdtS9Wz/8jHivJ0SvUpbmhKVzh1rBslwm65 + JpQgg3SsV23vF4qkCa2dt1FL+FeWJyCdj23DV3598X72RYiK3D6muWURck16jqeA + BRvUFuFHOwa/yA== + -----END CERTIFICATE----- + +date: 2009-03-02 00:00:00 -05:00 +default_executable: +dependencies: +- !ruby/object:Gem::Dependency + name: archive-tar-minitar + type: :development + version_requirement: + version_requirements: !ruby/object:Gem::Requirement + requirements: + - - ~> + - !ruby/object:Gem::Version + version: "0.5" + version: +- !ruby/object:Gem::Dependency + name: nokogiri + type: :development + version_requirement: + version_requirements: !ruby/object:Gem::Requirement + requirements: + - - ~> + - !ruby/object:Gem::Version + version: "1.2" + version: +- !ruby/object:Gem::Dependency + name: rcov + type: :development + version_requirement: + version_requirements: !ruby/object:Gem::Requirement + requirements: + - - ~> + - !ruby/object:Gem::Version + version: "0.8" + version: +- !ruby/object:Gem::Dependency + name: hoe + type: :development + version_requirement: + version_requirements: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: 1.8.3 + version: +description: MIME::Types for Ruby originally based on and synchronized with MIME::Types for Perl by Mark Overmeer, copyright 2001 - 2009. As of version 1.15, the data format for the MIME::Type list has changed and the synchronization will no longer happen. +email: +- austin@rubyforge.org +executables: [] + +extensions: [] + +extra_rdoc_files: +- History.txt +- Install.txt +- Licence.txt +- README.txt +files: +- History.txt +- Install.txt +- Licence.txt +- Manifest.txt +- README.txt +- Rakefile +- lib/mime/types.rb +- lib/mime/types.rb.data +- mime-types.gemspec +- setup.rb +- test/test_mime_type.rb +- test/test_mime_types.rb +has_rdoc: true +homepage: http://mime-types.rubyforge.org/ +licenses: [] + +post_install_message: +rdoc_options: +- --main +- README.txt +require_paths: +- bin +- lib +required_ruby_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: "0" + version: +required_rubygems_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: "0" + version: +requirements: [] + +rubyforge_project: mime-types +rubygems_version: 1.3.4 +signing_key: +specification_version: 2 +summary: Manages a MIME Content-Type database that will return the Content-Type for a given filename. +test_files: +- test/test_mime_type.rb +- test/test_mime_types.rb diff --git a/vendor/gems/mime-types-1.16/History.txt b/vendor/gems/mime-types-1.16/History.txt new file mode 100644 index 0000000..2f2ea92 --- /dev/null +++ b/vendor/gems/mime-types-1.16/History.txt @@ -0,0 +1,107 @@ +== MIME::Types 1.16 +* Made compatible with Ruby 1.8.6, 1.8.7, and 1.9.1. +* Switched to the 'hoe' gem system and added a lot of build-time tools. +* Updated the MIME types to the list based on the values in the Perl library + version 1.27. Also updated based on external source information and bug + reports. +* This is the last planned version of MIME::Types 1.x. Work will be + starting soon on MIME::Types 2.x with richer data querying mechanisms + and support for external data sources. + +== MIME::Types 1.15 +* Removed lib/mime/type.rb to form a single MIME::Types database source. It + is unlikely that one will ever need MIME::Type without MIME::Types. +* Re-synchronized the MIME type list with the sources, focusing primarily on + the IANA list. +* Added more detailed source information for MIME::Type objects. +* Changed MIME::Types from a module to a class with a default instance. There + should be no difference in usage. +* Removed MIME::Types::DATA_VERSION; it is now an attribute on the + MIME::Types instance. +* NOTE: Synchronization with the Perl version of MIME::Types is no longer a + priority as of this release. The data format and information has changed. +* Removed MIME::Types.by_suffix and MIME::Types.by_mediatype. + +== MIME::Types 1.13.1 +* Fixed a problem with the installer running tests. This now works. +* Improved the implementation of MIME::Type.signature? +* Moved code around to use the class << self idiom instead of always + prepending the module/class name. +* Added two new best-guess implementations of functions found in Perl's + MIME::Types implementation (1.13). Do not rely on these until the purpose + and implementation is stabilised. +* Updated the MIME list to reflect changes noted by + Ville Skyttä . +* Added a new constant to MIME::Types, DATA_VERSION. This will allow the Ruby + version number to be updated separately from the Perl version while keeping + the MIME Type list version in sync. + +== MIME::Types 1.13 + ! WARNING: This version changes the API of MIME::Types ! + ! WARNING: This version is compatible with Ruby 1.8 and higher ONLY ! +* Removed dependency on InstallPackage; offering 1.13 as either .tar.gz or + .gem. +* Split into two files, mime/type.rb and mime/types.rb. This will make + maintaining the list of changes easier. +* Changed the MIME::Type construction API. Accepts only a single String + argument (but does no named type-checking) and yields self. +* Removed private methods #init_extensions, #init_encoding, and #init_system + and replaced with #extensions=, #encoding=, and #system=. +* Added #default_encoding to return 'quoted-printable' or 'base64' depending + on the media type of the MIME type. +* Added #raw_media_type and #raw_sub_type to provide the non-simplified + versions of the media type and subtype. +* Alternative constructors MIME::Type.from_array, MIME::Type.from_hash, and + MIME::Type.from_mime_type added to compensate for the removal of named type + checking in the original constructor. +* Added #to_str, #to_a, and #to_hash methods. The latter two will provide + output suitable for use in #from_array and #from_hash. +* Removed "binary" encoding and enforced the use of a valid encoding string. +* Added #system? returning true if the MIME::Type is an OS-specific + MIME::Type. +* Added #platform? returning true if the MIME::Type is an OS-specific + MIME::Type for the current RUBY_PLATFORM. +* Added #like? returning true if the simplified type matches the other value + provided. #<'application/x-excel'>.like?('application/excel') is true. +* Added #complete? returning true if the MIME::Type specifies an extension + list. +* Updated the MIME type list to reflect additions by Mark Overmeer for Perl's + MIME::Types 1.12 and the official IANA list as of 2004.04.06. A number of + formerly "registered" MIME types are now no longer registered (e.g., + application/excel is now application/x-excel). This ensures that the + simplified type still works with applications, but does not report an + unregistered type as registered. +* Restored MIME type list to Mark Overmeer's format to facilitate easy + exchange between the two projects. +* Added additional unit tests from Mark Overmeer's 1.12 version. + +== MIME::Types 1.07 +* Changed version numbering to match Perl MIME::Types 1.07. +* Re-synchronized with Mark Overmeer's list in Perl PMIME::Types 1.07. +* [NN Poster] updated the attributes for the PGP types. + +== MIME::Types 1.005 +* Changed to Phil Thomson's InstallPackage. +* Added several types from Perl MIME::Types 1.005. +* Cleaned up data format; some data formats will show up with proper data now. + +== MIME::Types 1.004 +* Updated to match Perl MIME::Types 1.004, links credited to Dan Puro. Adds + new reference list to http://www.indiana.edu/cgi-bin-local/mimetypes +* Removed InvalidType and replaced with TypeError. +* Changed instances of #type to #class. +* Cleaned up how simplified versions are created. + +== MIME::Types 1.003 +* Initial release based on Perl MIME::Types 1.003. + +== Copyright + MIME::Types + A Ruby implementation of a MIME Types information library. Based in spirit + on the Perl MIME::Types information library by Mark Overmeer. + http://rubyforge.org/projects/mime-types/ + + Licensed under the Ruby disjunctive licence with the GNU GPL or the Perl + Artistic licence. See Licence.txt for more information. + + Copyright 2003 - 2009 Austin Ziegler diff --git a/vendor/gems/mime-types-1.16/Install.txt b/vendor/gems/mime-types-1.16/Install.txt new file mode 100644 index 0000000..f964cb9 --- /dev/null +++ b/vendor/gems/mime-types-1.16/Install.txt @@ -0,0 +1,17 @@ +Installing this package is as simple as: + + % ruby setup.rb + +Alternatively, you can use the Gem version of MIME::Types available as +mime-types-1.16.gem from the usual sources. + +== Copyright + MIME::Types + A Ruby implementation of a MIME Types information library. Based in spirit + on the Perl MIME::Types information library by Mark Overmeer. + http://rubyforge.org/projects/mime-types/ + + Licensed under the Ruby disjunctive licence with the GNU GPL or the Perl + Artistic licence. See Licence.txt for more information. + + Copyright 2003 - 2009 Austin Ziegler diff --git a/vendor/gems/mime-types-1.16/Licence.txt b/vendor/gems/mime-types-1.16/Licence.txt new file mode 100644 index 0000000..312f7ed --- /dev/null +++ b/vendor/gems/mime-types-1.16/Licence.txt @@ -0,0 +1,15 @@ += MIME::Types for Ruby +Homepage:: http://rubyforge.org/projects/mime-types/ +Copyright:: Copyright (c) 2003 - 2006 Austin Ziegler. +Summary:: Ruby's licence, Perl Aristic Licence, + GNU GPL version 2 (or later) + +The text of the Ruby licence can be found at: +http://www.ruby-lang.org/en/LICENSE.txt + +The text of the Perl Artistic Licence can be found at: +http://www.perl.com/pub/a/language/misc/Artistic.html + +The text of the GNU GPL can be found at: http://www.gnu.org/copyleft/gpl.html + +If you do not accept one of these licences, you may not use this software. diff --git a/vendor/gems/mime-types-1.16/Manifest.txt b/vendor/gems/mime-types-1.16/Manifest.txt new file mode 100644 index 0000000..35a9ec4 --- /dev/null +++ b/vendor/gems/mime-types-1.16/Manifest.txt @@ -0,0 +1,12 @@ +History.txt +Install.txt +Licence.txt +Manifest.txt +README.txt +Rakefile +lib/mime/types.rb +lib/mime/types.rb.data +mime-types.gemspec +setup.rb +test/test_mime_type.rb +test/test_mime_types.rb diff --git a/vendor/gems/mime-types-1.16/README.txt b/vendor/gems/mime-types-1.16/README.txt new file mode 100644 index 0000000..3d5cfc4 --- /dev/null +++ b/vendor/gems/mime-types-1.16/README.txt @@ -0,0 +1,28 @@ += README: Mime::Types for Ruby +This library allows for the identification of a file's likely MIME content +type. This is release 1.16. The identification of MIME content type is based +on a file's filename extensions. + +MIME::Types for Ruby originally based on and synchronized with MIME::Types +for Perl by Mark Overmeer, copyright 2001 - 2009. As of version 1.15, the +data format for the MIME::Type list has changed and the synchronization will +no longer happen. + +Homepage:: http://mime-types.rubyforge.org/ +Copyright:: 2002 - 2009, Austin Ziegler + Based in part on prior work copyright Mark Overmeer + +== Licensing +MIME::Types is available under three disjunctive licences, as detailed in the +Licence.txt file. + +== Copyright + MIME::Types + A Ruby implementation of a MIME Types information library. Based in spirit + on the Perl MIME::Types information library by Mark Overmeer. + http://rubyforge.org/projects/mime-types/ + + Licensed under the Ruby disjunctive licence with the GNU GPL or the Perl + Artistic licence. See Licence.txt for more information. + + Copyright 2003 - 2009 Austin Ziegler diff --git a/vendor/gems/mime-types-1.16/Rakefile b/vendor/gems/mime-types-1.16/Rakefile new file mode 100644 index 0000000..05ee805 --- /dev/null +++ b/vendor/gems/mime-types-1.16/Rakefile @@ -0,0 +1,316 @@ +#! /usr/bin/env rake +#-- +# MIME::Types +# A Ruby implementation of a MIME Types information library. Based in spirit +# on the Perl MIME::Types information library by Mark Overmeer. +# http://rubyforge.org/projects/mime-types/ +# +# Licensed under the Ruby disjunctive licence with the GNU GPL or the Perl +# Artistic licence. See Licence.txt for more information. +# +# Copyright 2003 - 2009 Austin Ziegler +#++ + +require 'rubygems' +require 'hoe' + +$LOAD_PATH.unshift('lib') + +require 'mime/types' + +PKG_NAME = 'mime-types' +PKG_VERSION = MIME::Types::VERSION +PKG_DIST = "#{PKG_NAME}-#{PKG_VERSION}" +PKG_TAR = "pkg/#{PKG_DIST}.tar.gz" +MANIFEST = File.read("Manifest.txt").split + +hoe = Hoe.new PKG_NAME, PKG_VERSION do |p| + p.rubyforge_name = PKG_NAME + # This is a lie because I will continue to use Archive::Tar::Minitar. + p.need_tar = false + # need_zip - Should package create a zipfile? [default: false] + + p.author = [ "Austin Ziegler" ] + p.email = %W(austin@rubyforge.org) + p.url = "http://mime-types.rubyforge.org/" + p.summary = %q{Manages a MIME Content-Type database that will return the Content-Type for a given filename.} + p.changes = p.paragraphs_of("History.txt", 0..0).join("\n\n") + p.description = p.paragraphs_of("README.txt", 1..1).join("\n\n") + + p.extra_dev_deps << %w(archive-tar-minitar ~>0.5) + p.extra_dev_deps << %w(nokogiri ~>1.2) + p.extra_dev_deps << %w(rcov ~>0.8) + + p.clean_globs << "coverage" + + p.spec_extras[:extra_rdoc_files] = MANIFEST.grep(/txt$/) - ["Manifest.txt"] +end + +begin + require 'rcov/rcovtask' + Rcov::RcovTask.new do |t| + t.libs << 'test' + t.test_files = hoe.test_files + t.verbose = true + end +rescue LoadError + puts "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov" +end + +=begin + require 'cucumber/rake/task' + Cucumber::Rake::Task.new(:features) +rescue LoadError + puts "Cucumber is not available. In order to run features, you must: sudo gem install cucumber" +=end + +desc "Build a MIME::Types .tar.gz distribution." +task :tar => [ PKG_TAR ] +file PKG_TAR => [ :test ] do |t| + require 'archive/tar/minitar' + require 'zlib' + files = MANIFEST.map { |f| + fn = File.join(PKG_DIST, f) + tm = File.stat(f).mtime + + if File.directory?(f) + { :name => fn, :mode => 0755, :dir => true, :mtime => tm } + else + mode = if f =~ %r{^bin} + 0755 + else + 0644 + end + data = File.read(f) + { :name => fn, :mode => mode, :data => data, :size => data.size, + :mtime => tm } + end + } + + begin + unless File.directory?(File.dirname(t.name)) + require 'fileutils' + FileUtils.mkdir_p File.dirname(t.name) + end + tf = File.open(t.name, 'wb') + gz = Zlib::GzipWriter.new(tf) + tw = Archive::Tar::Minitar::Writer.new(gz) + + files.each do |entry| + if entry[:dir] + tw.mkdir(entry[:name], entry) + else + tw.add_file_simple(entry[:name], entry) { |os| + os.write(entry[:data]) + } + end + end + ensure + tw.close if tw + gz.close if gz + end +end +task :package => [ PKG_TAR ] + +desc "Build the manifest file from the current set of files." +task :build_manifest do |t| + require 'find' + + hoerc = File.join(File.dirname(__FILE__), ".hoerc") + hoerc = File.open(hoerc, "rb") { |f| f.read } + hoerc = YAML::load(hoerc) + + paths = [] + Find.find(".") do |path| + next if File.directory?(path) || path =~ hoerc["exclude"] + paths << path.sub(%r{^\./}, '') + end + + paths = paths.sort.join("\n") + + File.open("Manifest.txt", "w") do |f| + f.puts paths + end + + puts paths +end + +desc "Download the current MIME type registrations from IANA." +task :iana, :save, :destination do |t, args| + save_type = args.save || :text + save_type = save_type.to_sym + + case save_type + when :text, :both, :html + nil + else + raise "Unknown save type provided. Must be one of text, both, or html." + end + + destination = args.destination || "type-lists" + + require 'open-uri' + require 'nokogiri' + require 'cgi' + + class IANAParser + include Comparable + + INDEX = %q(http://www.iana.org/assignments/media-types/) + CONTACT_PEOPLE = %r{http://www.iana.org/assignments/contact-people.html?#(.*)} + RFC_EDITOR = %r{http://www.rfc-editor.org/rfc/rfc(\d+).txt} + IETF_RFC = %r{http://www.ietf.org/rfc/rfc(\d+).txt} + IETF_RFC_TOOLS = %r{http://tools.ietf.org/html/rfc(\d+)} + + class << self + def load_index + @types ||= {} + + Nokogiri::HTML(open(INDEX) { |f| f.read }).xpath('//p/a').each do |tag| + href_match = %r{^/assignments/media-types/(.+)/$}.match(tag['href']) + next if href_match.nil? + type = href_match.captures[0] + @types[tag.content] = IANAParser.new(tag.content, type) + end + end + + attr_reader :types + end + + def initialize(name, type) + @name = name + @type = type + @url = File.join(INDEX, @type) + end + + attr_reader :name + attr_reader :type + attr_reader :url + attr_reader :html + + def download(name = nil) + @html = Nokogiri::HTML(open(name || @url) { |f| f.read }) + end + + def save_html + File.open("#@name.html", "wb") { |w| w.write @html } + end + + def <=>(o) + self.name <=> o.name + end + + def parse + nodes = html.xpath("//table//table//tr") + + # How many children does the first node have? + node_count = nodes.first.children.select { |node| node.elem? }.size + + @mime_types = nodes.map do |node| + next if node == nodes.first + elems = node.children.select { |n| n.elem? } + next if elems.size.zero? + raise "size mismatch #{elems.size} != #{node_count}" if node_count != elems.size + + case elems.size + when 3 + subtype_index = 1 + refnode_index = 2 + when 4 + subtype_index = 1 + refnode_index = 3 + else + raise "Unknown element size." + end + + subtype = elems[subtype_index].content.chomp.strip + refnodes = elems[refnode_index].children.select { |n| n.elem? }.map { |ref| + case ref['href'] + when CONTACT_PEOPLE + tag = CGI::unescape($1).chomp.strip + if tag == ref.content + "[#{ref.content}]" + else + "[#{ref.content}=#{tag}]" + end + when RFC_EDITOR, IETF_RFC, IETF_RFC_TOOLS + "RFC#$1" + when %r{(https?://.*)} + "{#{ref.content}=#$1}" + else + ref + end + } + refs = refnodes.join(',') + + "#@type/#{subtype} 'IANA,#{refs}" + end.compact + + @mime_types + end + + def save_text + File.open("#@name.txt", "wb") { |w| w.write @mime_types.join("\n") } + end + end + + puts "Downloading index of MIME types from #{IANAParser::INDEX}." + IANAParser.load_index + + require 'fileutils' + FileUtils.mkdir_p destination + Dir.chdir destination do + IANAParser.types.values.sort.each do |parser| + next if parser.name == "example" or parser.name == "mime" + puts "Downloading #{parser.name} from #{parser.url}" + parser.download + + if :html == save_type || :both == save_type + puts "Saving #{parser.name}.html" + parser.save_html + end + + puts "Parsing #{parser.name} HTML" + parser.parse + + if :text == save_type || :both == save_type + puts "Saving #{parser.name}.txt" + parser.save_text + end + end + end +end + +desc "Shows known MIME type sources." +task :mime_type_sources do + puts <<-EOS +http://www.ltsw.se/knbase/internet/mime.htp +http://www.webmaster-toolkit.com/mime-types.shtml +http://plugindoc.mozdev.org/winmime.php +http://standards.freedesktop.org/shared-mime-info-spec/shared-mime-info-spec-latest.html +http://www.feedforall.com/mime-types.htm +http://www.iana.org/assignments/media-types/ + EOS +end + +desc "Validate the RubyGem spec for GitHub." +task :github_validate_spec do |t| + require 'yaml' + + require 'rubygems/specification' + data = File.read("#{PKG_NAME}.gemspec") + spec = nil + + if data !~ %r{!ruby/object:Gem::Specification} + code = "$SAFE = 3\n#{data}" + p code.split($/)[44] + Thread.new { spec = eval("$SAFE = 3\n#{data}") }.join + else + spec = YAML.load(data) + end + + spec.validate + + puts spec + puts "OK" +end diff --git a/vendor/gems/mime-types-1.16/lib/mime/types.rb b/vendor/gems/mime-types-1.16/lib/mime/types.rb new file mode 100644 index 0000000..7942705 --- /dev/null +++ b/vendor/gems/mime-types-1.16/lib/mime/types.rb @@ -0,0 +1,751 @@ +# vim: ft=ruby encoding=utf-8 +#-- +# MIME::Types +# A Ruby implementation of a MIME Types information library. Based in spirit +# on the Perl MIME::Types information library by Mark Overmeer. +# http://rubyforge.org/projects/mime-types/ +# +# Licensed under the Ruby disjunctive licence with the GNU GPL or the Perl +# Artistic licence. See Licence.txt for more information. +# +# Copyright 2003 - 2009 Austin Ziegler +#++ + +# The namespace for MIME applications, tools, and libraries. +module MIME + # Reflects a MIME Content-Type which is in invalid format (e.g., it isn't + # in the form of type/subtype). + class InvalidContentType < RuntimeError; end + + # The definition of one MIME content-type. + # + # == Usage + # require 'mime/types' + # + # plaintext = MIME::Types['text/plain'] + # print plaintext.media_type # => 'text' + # print plaintext.sub_type # => 'plain' + # + # puts plaintext.extensions.join(" ") # => 'asc txt c cc h hh cpp' + # + # puts plaintext.encoding # => 8bit + # puts plaintext.binary? # => false + # puts plaintext.ascii? # => true + # puts plaintext == 'text/plain' # => true + # puts MIME::Type.simplified('x-appl/x-zip') # => 'appl/zip' + # + class Type + VERSION = '1.16' + + include Comparable + + MEDIA_TYPE_RE = %r{([-\w.+]+)/([-\w.+]*)}o + UNREG_RE = %r{[Xx]-}o + ENCODING_RE = %r{(?:base64|7bit|8bit|quoted\-printable)}o + PLATFORM_RE = %r|#{RUBY_PLATFORM}|o + + SIGNATURES = %w(application/pgp-keys application/pgp + application/pgp-signature application/pkcs10 + application/pkcs7-mime application/pkcs7-signature + text/vcard) + + IANA_URL = "http://www.iana.org/assignments/media-types/%s/%s" + RFC_URL = "http://rfc-editor.org/rfc/rfc%s.txt" + DRAFT_URL = "http://datatracker.ietf.org/public/idindex.cgi?command=id_details&filename=%s" + LTSW_URL = "http://www.ltsw.se/knbase/internet/%s.htp" + CONTACT_URL = "http://www.iana.org/assignments/contact-people.htm#%s" + + # Returns +true+ if the simplified type matches the current + def like?(other) + if other.respond_to?(:simplified) + @simplified == other.simplified + else + @simplified == Type.simplified(other) + end + end + + # Compares the MIME::Type against the exact content type or the + # simplified type (the simplified type will be used if comparing against + # something that can be treated as a String with #to_s). In comparisons, + # this is done against the lowercase version of the MIME::Type. + def <=>(other) + if other.respond_to?(:content_type) + @content_type.downcase <=> other.content_type.downcase + elsif other.respond_to?(:to_s) + @simplified <=> Type.simplified(other.to_s) + else + @content_type.downcase <=> other.downcase + end + end + + # Compares the MIME::Type based on how reliable it is before doing a + # normal <=> comparison. Used by MIME::Types#[] to sort types. The + # comparisons involved are: + # + # 1. self.simplified <=> other.simplified (ensures that we + # don't try to compare different types) + # 2. IANA-registered definitions > other definitions. + # 3. Generic definitions > platform definitions. + # 3. Complete definitions > incomplete definitions. + # 4. Current definitions > obsolete definitions. + # 5. Obselete with use-instead references > obsolete without. + # 6. Obsolete use-instead definitions are compared. + def priority_compare(other) + pc = simplified <=> other.simplified + + if pc.zero? and registered? != other.registered? + pc = registered? ? -1 : 1 + end + + if pc.zero? and platform? != other.platform? + pc = platform? ? 1 : -1 + end + + if pc.zero? and complete? != other.complete? + pc = complete? ? -1 : 1 + end + + if pc.zero? and obsolete? != other.obsolete? + pc = obsolete? ? 1 : -1 + end + + if pc.zero? and obsolete? and (use_instead != other.use_instead) + pc = if use_instead.nil? + -1 + elsif other.use_instead.nil? + 1 + else + use_instead <=> other.use_instead + end + end + + pc + end + + # Returns +true+ if the other object is a MIME::Type and the content + # types match. + def eql?(other) + other.kind_of?(MIME::Type) and self == other + end + + # Returns the whole MIME content-type string. + # + # text/plain => text/plain + # x-chemical/x-pdb => x-chemical/x-pdb + attr_reader :content_type + # Returns the media type of the simplified MIME type. + # + # text/plain => text + # x-chemical/x-pdb => chemical + attr_reader :media_type + # Returns the media type of the unmodified MIME type. + # + # text/plain => text + # x-chemical/x-pdb => x-chemical + attr_reader :raw_media_type + # Returns the sub-type of the simplified MIME type. + # + # text/plain => plain + # x-chemical/x-pdb => pdb + attr_reader :sub_type + # Returns the media type of the unmodified MIME type. + # + # text/plain => plain + # x-chemical/x-pdb => x-pdb + attr_reader :raw_sub_type + # The MIME types main- and sub-label can both start with x-, + # which indicates that it is a non-registered name. Of course, after + # registration this flag can disappear, adds to the confusing + # proliferation of MIME types. The simplified string has the x- + # removed and are translated to lowercase. + # + # text/plain => text/plain + # x-chemical/x-pdb => chemical/pdb + attr_reader :simplified + # The list of extensions which are known to be used for this MIME::Type. + # Non-array values will be coerced into an array with #to_a. Array + # values will be flattened and +nil+ values removed. + attr_accessor :extensions + remove_method :extensions= ; + def extensions=(ext) #:nodoc: + @extensions = [ext].flatten.compact + end + + # The encoding (7bit, 8bit, quoted-printable, or base64) required to + # transport the data of this content type safely across a network, which + # roughly corresponds to Content-Transfer-Encoding. A value of +nil+ or + # :default will reset the #encoding to the #default_encoding + # for the MIME::Type. Raises ArgumentError if the encoding provided is + # invalid. + # + # If the encoding is not provided on construction, this will be either + # 'quoted-printable' (for text/* media types) and 'base64' for eveything + # else. + attr_accessor :encoding + remove_method :encoding= ; + def encoding=(enc) #:nodoc: + if enc.nil? or enc == :default + @encoding = self.default_encoding + elsif enc =~ ENCODING_RE + @encoding = enc + else + raise ArgumentError, "The encoding must be nil, :default, base64, 7bit, 8bit, or quoted-printable." + end + end + + # The regexp for the operating system that this MIME::Type is specific + # to. + attr_accessor :system + remove_method :system= ; + def system=(os) #:nodoc: + if os.nil? or os.kind_of?(Regexp) + @system = os + else + @system = %r|#{os}| + end + end + # Returns the default encoding for the MIME::Type based on the media + # type. + attr_reader :default_encoding + remove_method :default_encoding + def default_encoding + (@media_type == 'text') ? 'quoted-printable' : 'base64' + end + + # Returns the media type or types that should be used instead of this + # media type, if it is obsolete. If there is no replacement media type, + # or it is not obsolete, +nil+ will be returned. + attr_reader :use_instead + remove_method :use_instead + def use_instead + return nil unless @obsolete + @use_instead + end + + # Returns +true+ if the media type is obsolete. + def obsolete? + @obsolete ? true : false + end + # Sets the obsolescence indicator for this media type. + attr_writer :obsolete + + # The documentation for this MIME::Type. Documentation about media + # types will be found on a media type definition as a comment. + # Documentation will be found through #docs. + attr_accessor :docs + remove_method :docs= ; + def docs=(d) + if d + a = d.scan(%r{use-instead:#{MEDIA_TYPE_RE}}) + + if a.empty? + @use_instead = nil + else + @use_instead = a.map { |el| "#{el[0]}/#{el[1]}" } + end + end + @docs = d + end + + # The encoded URL list for this MIME::Type. See #urls for more + # information. + attr_accessor :url + # The decoded URL list for this MIME::Type. + # The special URL value IANA will be translated into: + # http://www.iana.org/assignments/media-types// + # + # The special URL value RFC### will be translated into: + # http://www.rfc-editor.org/rfc/rfc###.txt + # + # The special URL value DRAFT:name will be translated into: + # https://datatracker.ietf.org/public/idindex.cgi? + # command=id_detail&filename= + # + # The special URL value LTSW will be translated into: + # http://www.ltsw.se/knbase/internet/.htp + # + # The special URL value [token] will be translated into: + # http://www.iana.org/assignments/contact-people.htm# + # + # These values will be accessible through #urls, which always returns an + # array. + def urls + @url.map do |el| + case el + when %r{^IANA$} + IANA_URL % [ @media_type, @sub_type ] + when %r{^RFC(\d+)$} + RFC_URL % $1 + when %r{^DRAFT:(.+)$} + DRAFT_URL % $1 + when %r{^LTSW$} + LTSW_URL % @media_type + when %r<^\{([^=]+)=([^\]]+)\}> + [$1, $2] + when %r{^\[([^=]+)=([^\]]+)\]} + [$1, CONTACT_URL % $2] + when %r{^\[([^\]]+)\]} + CONTACT_URL % $1 + else + el + end + end + end + + class << self + # The MIME types main- and sub-label can both start with x-, + # which indicates that it is a non-registered name. Of course, after + # registration this flag can disappear, adds to the confusing + # proliferation of MIME types. The simplified string has the + # x- removed and are translated to lowercase. + def simplified(content_type) + matchdata = MEDIA_TYPE_RE.match(content_type) + + if matchdata.nil? + simplified = nil + else + media_type = matchdata.captures[0].downcase.gsub(UNREG_RE, '') + subtype = matchdata.captures[1].downcase.gsub(UNREG_RE, '') + simplified = "#{media_type}/#{subtype}" + end + simplified + end + + # Creates a MIME::Type from an array in the form of: + # [type-name, [extensions], encoding, system] + # + # +extensions+, +encoding+, and +system+ are optional. + # + # MIME::Type.from_array("application/x-ruby", ['rb'], '8bit') + # MIME::Type.from_array(["application/x-ruby", ['rb'], '8bit']) + # + # These are equivalent to: + # + # MIME::Type.new('application/x-ruby') do |t| + # t.extensions = %w(rb) + # t.encoding = '8bit' + # end + def from_array(*args) #:yields MIME::Type.new: + # Dereferences the array one level, if necessary. + args = args[0] if args[0].kind_of?(Array) + + if args.size.between?(1, 8) + m = MIME::Type.new(args[0]) do |t| + t.extensions = args[1] if args.size > 1 + t.encoding = args[2] if args.size > 2 + t.system = args[3] if args.size > 3 + t.obsolete = args[4] if args.size > 4 + t.docs = args[5] if args.size > 5 + t.url = args[6] if args.size > 6 + t.registered = args[7] if args.size > 7 + end + yield m if block_given? + else + raise ArgumentError, "Array provided must contain between one and eight elements." + end + m + end + + # Creates a MIME::Type from a hash. Keys are case-insensitive, + # dashes may be replaced with underscores, and the internal Symbol + # of the lowercase-underscore version can be used as well. That is, + # Content-Type can be provided as content-type, Content_Type, + # content_type, or :content_type. + # + # Known keys are Content-Type, + # Content-Transfer-Encoding, Extensions, and + # System. + # + # MIME::Type.from_hash('Content-Type' => 'text/x-yaml', + # 'Content-Transfer-Encoding' => '8bit', + # 'System' => 'linux', + # 'Extensions' => ['yaml', 'yml']) + # + # This is equivalent to: + # + # MIME::Type.new('text/x-yaml') do |t| + # t.encoding = '8bit' + # t.system = 'linux' + # t.extensions = ['yaml', 'yml'] + # end + def from_hash(hash) #:yields MIME::Type.new: + type = {} + hash.each_pair do |k, v| + type[k.to_s.tr('A-Z', 'a-z').gsub(/-/, '_').to_sym] = v + end + + m = MIME::Type.new(type[:content_type]) do |t| + t.extensions = type[:extensions] + t.encoding = type[:content_transfer_encoding] + t.system = type[:system] + t.obsolete = type[:obsolete] + t.docs = type[:docs] + t.url = type[:url] + t.registered = type[:registered] + end + + yield m if block_given? + m + end + + # Essentially a copy constructor. + # + # MIME::Type.from_mime_type(plaintext) + # + # is equivalent to: + # + # MIME::Type.new(plaintext.content_type.dup) do |t| + # t.extensions = plaintext.extensions.dup + # t.system = plaintext.system.dup + # t.encoding = plaintext.encoding.dup + # end + def from_mime_type(mime_type) #:yields the new MIME::Type: + m = MIME::Type.new(mime_type.content_type.dup) do |t| + t.extensions = mime_type.extensions.map { |e| e.dup } + t.url = mime_type.url && mime_type.url.map { |e| e.dup } + + mime_type.system && t.system = mime_type.system.dup + mime_type.encoding && t.encoding = mime_type.encoding.dup + + t.obsolete = mime_type.obsolete? + t.registered = mime_type.registered? + + mime_type.docs && t.docs = mime_type.docs.dup + + end + + yield m if block_given? + end + end + + # Builds a MIME::Type object from the provided MIME Content Type value + # (e.g., 'text/plain' or 'applicaton/x-eruby'). The constructed object + # is yielded to an optional block for additional configuration, such as + # associating extensions and encoding information. + def initialize(content_type) #:yields self: + matchdata = MEDIA_TYPE_RE.match(content_type) + + if matchdata.nil? + raise InvalidContentType, "Invalid Content-Type provided ('#{content_type}')" + end + + @content_type = content_type + @raw_media_type = matchdata.captures[0] + @raw_sub_type = matchdata.captures[1] + + @simplified = MIME::Type.simplified(@content_type) + matchdata = MEDIA_TYPE_RE.match(@simplified) + @media_type = matchdata.captures[0] + @sub_type = matchdata.captures[1] + + self.extensions = nil + self.encoding = :default + self.system = nil + self.registered = true + self.url = nil + self.obsolete = nil + self.docs = nil + + yield self if block_given? + end + + # MIME content-types which are not regestered by IANA nor defined in + # RFCs are required to start with x-. This counts as well for + # a new media type as well as a new sub-type of an existing media + # type. If either the media-type or the content-type begins with + # x-, this method will return +false+. + def registered? + if (@raw_media_type =~ UNREG_RE) || (@raw_sub_type =~ UNREG_RE) + false + else + @registered + end + end + attr_writer :registered #:nodoc: + + # MIME types can be specified to be sent across a network in particular + # formats. This method returns +true+ when the MIME type encoding is set + # to base64. + def binary? + @encoding == 'base64' + end + + # MIME types can be specified to be sent across a network in particular + # formats. This method returns +false+ when the MIME type encoding is + # set to base64. + def ascii? + not binary? + end + + # Returns +true+ when the simplified MIME type is in the list of known + # digital signatures. + def signature? + SIGNATURES.include?(@simplified.downcase) + end + + # Returns +true+ if the MIME::Type is specific to an operating system. + def system? + not @system.nil? + end + + # Returns +true+ if the MIME::Type is specific to the current operating + # system as represented by RUBY_PLATFORM. + def platform? + system? and (RUBY_PLATFORM =~ @system) + end + + # Returns +true+ if the MIME::Type specifies an extension list, + # indicating that it is a complete MIME::Type. + def complete? + not @extensions.empty? + end + + # Returns the MIME type as a string. + def to_s + @content_type + end + + # Returns the MIME type as a string for implicit conversions. + def to_str + @content_type + end + + # Returns the MIME type as an array suitable for use with + # MIME::Type.from_array. + def to_a + [ @content_type, @extensions, @encoding, @system, @obsolete, @docs, + @url, registered? ] + end + + # Returns the MIME type as an array suitable for use with + # MIME::Type.from_hash. + def to_hash + { 'Content-Type' => @content_type, + 'Content-Transfer-Encoding' => @encoding, + 'Extensions' => @extensions, + 'System' => @system, + 'Obsolete' => @obsolete, + 'Docs' => @docs, + 'URL' => @url, + 'Registered' => registered?, + } + end + end + + # = MIME::Types + # MIME types are used in MIME-compliant communications, as in e-mail or + # HTTP traffic, to indicate the type of content which is transmitted. + # MIME::Types provides the ability for detailed information about MIME + # entities (provided as a set of MIME::Type objects) to be determined and + # used programmatically. There are many types defined by RFCs and vendors, + # so the list is long but not complete; don't hesitate to ask to add + # additional information. This library follows the IANA collection of MIME + # types (see below for reference). + # + # == Description + # MIME types are used in MIME entities, as in email or HTTP traffic. It is + # useful at times to have information available about MIME types (or, + # inversely, about files). A MIME::Type stores the known information about + # one MIME type. + # + # == Usage + # require 'mime/types' + # + # plaintext = MIME::Types['text/plain'] + # print plaintext.media_type # => 'text' + # print plaintext.sub_type # => 'plain' + # + # puts plaintext.extensions.join(" ") # => 'asc txt c cc h hh cpp' + # + # puts plaintext.encoding # => 8bit + # puts plaintext.binary? # => false + # puts plaintext.ascii? # => true + # puts plaintext.obsolete? # => false + # puts plaintext.registered? # => true + # puts plaintext == 'text/plain' # => true + # puts MIME::Type.simplified('x-appl/x-zip') # => 'appl/zip' + # + # This module is built to conform to the MIME types of RFCs 2045 and 2231. + # It follows the official IANA registry at + # http://www.iana.org/assignments/media-types/ and + # ftp://ftp.iana.org/assignments/media-types with some unofficial types + # added from the the collection at + # http://www.ltsw.se/knbase/internet/mime.htp + # + # This is originally based on Perl MIME::Types by Mark Overmeer. + # + # = Author + # Copyright:: Copyright (c) 2002 - 2009 by Austin Ziegler + # + # Version:: 1.16 + # Based On:: Perl + # MIME::Types[http://search.cpan.org/author/MARKOV/MIME-Types-1.27/MIME/Types.pm], + # Copyright (c) 2001 - 2009 by Mark Overmeer + # . + # Licence:: Ruby's, Perl Artistic, or GPL version 2 (or later) + # See Also:: http://www.iana.org/assignments/media-types/ + # http://www.ltsw.se/knbase/internet/mime.htp + # + class Types + # The released version of Ruby MIME::Types + VERSION = '1.16' + + # The data version. + attr_reader :data_version + + def initialize(data_version = nil) + @type_variants = Hash.new { |h, k| h[k] = [] } + @extension_index = Hash.new { |h, k| h[k] = [] } + end + + def add_type_variant(mime_type) #:nodoc: + @type_variants[mime_type.simplified] << mime_type + end + + def index_extensions(mime_type) #:nodoc: + mime_type.extensions.each { |ext| @extension_index[ext] << mime_type } + end + + @__types__ = self.new(VERSION) + + # Returns a list of MIME::Type objects, which may be empty. The optional + # flag parameters are :complete (finds only complete MIME::Type objects) + # and :platform (finds only MIME::Types for the current platform). It is + # possible for multiple matches to be returned for either type (in the + # example below, 'text/plain' returns two values -- one for the general + # case, and one for VMS systems. + # + # puts "\nMIME::Types['text/plain']" + # MIME::Types['text/plain'].each { |t| puts t.to_a.join(", ") } + # + # puts "\nMIME::Types[/^image/, :complete => true]" + # MIME::Types[/^image/, :complete => true].each do |t| + # puts t.to_a.join(", ") + # end + # + # If multiple type definitions are returned, returns them sorted as + # follows: + # 1. Complete definitions sort before incomplete ones; + # 2. IANA-registered definitions sort before LTSW-recorded + # definitions. + # 3. Generic definitions sort before platform-specific ones; + # 4. Current definitions sort before obsolete ones; + # 5. Obsolete definitions with use-instead clauses sort before those + # without; + # 6. Obsolete definitions use-instead clauses are compared. + # 7. Sort on name. + def [](type_id, flags = {}) + if type_id.kind_of?(Regexp) + matches = [] + @type_variants.each_key do |k| + matches << @type_variants[k] if k =~ type_id + end + matches.flatten! + elsif type_id.kind_of?(MIME::Type) + matches = [type_id] + else + matches = @type_variants[MIME::Type.simplified(type_id)] + end + + matches.delete_if { |e| not e.complete? } if flags[:complete] + matches.delete_if { |e| not e.platform? } if flags[:platform] + + matches.sort { |a, b| a.priority_compare(b) } + end + + # Return the list of MIME::Types which belongs to the file based on its + # filename extension. If +platform+ is +true+, then only file types that + # are specific to the current platform will be returned. + # + # puts "MIME::Types.type_for('citydesk.xml') + # => "#{MIME::Types.type_for('citydesk.xml')}" + # puts "MIME::Types.type_for('citydesk.gif') + # => "#{MIME::Types.type_for('citydesk.gif')}" + def type_for(filename, platform = false) + ext = filename.chomp.downcase.gsub(/.*\./o, '') + list = @extension_index[ext] + list.delete_if { |e| not e.platform? } if platform + list + end + + # A synonym for MIME::Types.type_for + def of(filename, platform = false) + type_for(filename, platform) + end + + # Add one or more MIME::Type objects to the set of known types. Each + # type should be experimental (e.g., 'application/x-ruby'). If the type + # is already known, a warning will be displayed. + # + # Please inform the maintainer of this module when registered types + # are missing. + def add(*types) + types.each do |mime_type| + if @type_variants.include?(mime_type.simplified) + if @type_variants[mime_type.simplified].include?(mime_type) + warn "Type #{mime_type} already registered as a variant of #{mime_type.simplified}." + end + end + add_type_variant(mime_type) + index_extensions(mime_type) + end + end + + class << self + def add_type_variant(mime_type) #:nodoc: + @__types__.add_type_variant(mime_type) + end + + def index_extensions(mime_type) #:nodoc: + @__types__.index_extensions(mime_type) + end + + # Returns a list of MIME::Type objects, which may be empty. The + # optional flag parameters are :complete (finds only complete + # MIME::Type objects) and :platform (finds only MIME::Types for the + # current platform). It is possible for multiple matches to be + # returned for either type (in the example below, 'text/plain' returns + # two values -- one for the general case, and one for VMS systems. + # + # puts "\nMIME::Types['text/plain']" + # MIME::Types['text/plain'].each { |t| puts t.to_a.join(", ") } + # + # puts "\nMIME::Types[/^image/, :complete => true]" + # MIME::Types[/^image/, :complete => true].each do |t| + # puts t.to_a.join(", ") + # end + def [](type_id, flags = {}) + @__types__[type_id, flags] + end + + # Return the list of MIME::Types which belongs to the file based on + # its filename extension. If +platform+ is +true+, then only file + # types that are specific to the current platform will be returned. + # + # puts "MIME::Types.type_for('citydesk.xml') + # => "#{MIME::Types.type_for('citydesk.xml')}" + # puts "MIME::Types.type_for('citydesk.gif') + # => "#{MIME::Types.type_for('citydesk.gif')}" + def type_for(filename, platform = false) + @__types__.type_for(filename, platform) + end + + # A synonym for MIME::Types.type_for + def of(filename, platform = false) + @__types__.type_for(filename, platform) + end + + # Add one or more MIME::Type objects to the set of known types. Each + # type should be experimental (e.g., 'application/x-ruby'). If the + # type is already known, a warning will be displayed. + # + # Please inform the maintainer of this module when registered types + # are missing. + def add(*types) + @__types__.add(*types) + end + end + end +end + +load File.join(File.dirname(__FILE__), 'types.rb.data') diff --git a/vendor/gems/mime-types-1.16/lib/mime/types.rb.data b/vendor/gems/mime-types-1.16/lib/mime/types.rb.data new file mode 100644 index 0000000..0ca2cf5 --- /dev/null +++ b/vendor/gems/mime-types-1.16/lib/mime/types.rb.data @@ -0,0 +1,1324 @@ +# vim: ft=ruby encoding=utf-8 +#-- +# MIME::Types +# A Ruby implementation of a MIME Types information library. Based in spirit +# on the Perl MIME::Types information library by Mark Overmeer. +# http://rubyforge.org/projects/mime-types/ +# +# Licensed under the Ruby disjunctive licence with the GNU GPL or the Perl +# Artistic licence. See Licence.txt for more information. +# +# Copyright 2003 - 2009 Austin Ziegler +#++ + +# Build the type list from the string below. +# +# [*][!][os:]mt/st[@ext][:enc]['url-list][=docs] +# +# == * +# An unofficial MIME type. This should be used if and only if the MIME type +# is not properly specified (that is, not under either x-type or +# vnd.name.type). +# +# == ! +# An obsolete MIME type. May be used with an unofficial MIME type. +# +# == os: +# Platform-specific MIME type definition. +# +# == mt +# The media type. +# +# == st +# The media subtype. +# +# == @ext +# The list of comma-separated extensions. +# +# == :enc +# The encoding. +# +# == 'url-list +# The list of comma-separated URLs. +# +# == =docs +# The documentation string. +# +# That is, everything except the media type and the subtype is optional. The +# more information that's available, though, the richer the values that can +# be provided. + +data_mime_type_first_line = __LINE__ + 2 +data_mime_type = < ex + puts <<-"ERROR" +#{__FILE__}:#{x + data_mime_type_first_line}: Parsing error in MIME type definitions. +=> "#{item}" + ERROR + raise + end + + unregistered, obsolete, platform, mediatype, subtype, extensions, + encoding, urls, docs = *m + + extensions &&= extensions.split(/,/) + urls &&= urls.split(/,/) + + mime_type = MIME::Type.new("#{mediatype}/#{subtype}") do |t| + t.extensions = extensions + t.encoding = encoding + t.system = platform + t.obsolete = obsolete + t.registered = false if unregistered + t.docs = docs + t.url = urls + end + + MIME::Types.add_type_variant(mime_type) + MIME::Types.index_extensions(mime_type) +end + +_re = data_mime_type = data_mime_type_first_line = nil diff --git a/vendor/gems/mime-types-1.16/mime-types.gemspec b/vendor/gems/mime-types-1.16/mime-types.gemspec new file mode 100644 index 0000000..4f516ac --- /dev/null +++ b/vendor/gems/mime-types-1.16/mime-types.gemspec @@ -0,0 +1,43 @@ +# -*- encoding: utf-8 -*- + +Gem::Specification.new do |s| + s.name = %q{mime-types} + s.version = "1.16" + + s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= + s.authors = ["Austin Ziegler"] + s.cert_chain = ["/Users/austin/.gem/gem-public_cert.pem"] + s.date = %q{2009-03-01} + s.description = %q{MIME::Types for Ruby originally based on and synchronized with MIME::Types for Perl by Mark Overmeer, copyright 2001 - 2009. As of version 1.15, the data format for the MIME::Type list has changed and the synchronization will no longer happen.} + s.email = ["austin@rubyforge.org"] + s.extra_rdoc_files = ["History.txt", "Install.txt", "Licence.txt", "README.txt"] + s.files = ["History.txt", "Install.txt", "Licence.txt", "Manifest.txt", "README.txt", "Rakefile", "lib/mime/types.rb", "lib/mime/types.rb.data", "setup.rb", "test/test_mime_type.rb", "test/test_mime_types.rb"] + s.has_rdoc = true + s.homepage = %q{http://mime-types.rubyforge.org/} + s.rdoc_options = ["--main", "README.txt"] + s.require_paths = ["lib"] + s.rubyforge_project = %q{mime-types} + s.rubygems_version = %q{1.3.1} + s.signing_key = %q{/Users/austin/.gem/gem-private_key.pem} + s.summary = %q{Manages a MIME Content-Type database that will return the Content-Type for a given filename.} + s.test_files = ["test/test_mime_type.rb", "test/test_mime_types.rb"] + + if s.respond_to? :specification_version then + current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION + s.specification_version = 2 + + if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then + s.add_development_dependency(%q, ["~> 0.5.1"]) + s.add_development_dependency(%q, ["~> 1.2.1"]) + s.add_development_dependency(%q, [">= 1.8.3"]) + else + s.add_dependency(%q, ["~> 0.5.1"]) + s.add_dependency(%q, ["~> 1.2.1"]) + s.add_dependency(%q, [">= 1.8.3"]) + end + else + s.add_dependency(%q, ["~> 0.5.1"]) + s.add_dependency(%q, ["~> 1.2.1"]) + s.add_dependency(%q, [">= 1.8.3"]) + end +end diff --git a/vendor/gems/mime-types-1.16/setup.rb b/vendor/gems/mime-types-1.16/setup.rb new file mode 100644 index 0000000..424a5f3 --- /dev/null +++ b/vendor/gems/mime-types-1.16/setup.rb @@ -0,0 +1,1585 @@ +# +# setup.rb +# +# Copyright (c) 2000-2005 Minero Aoki +# +# This program is free software. +# You can distribute/modify this program under the terms of +# the GNU LGPL, Lesser General Public License version 2.1. +# + +unless Enumerable.method_defined?(:map) # Ruby 1.4.6 + module Enumerable + alias map collect + end +end + +unless File.respond_to?(:read) # Ruby 1.6 + def File.read(fname) + open(fname) {|f| + return f.read + } + end +end + +unless Errno.const_defined?(:ENOTEMPTY) # Windows? + module Errno + class ENOTEMPTY + # We do not raise this exception, implementation is not needed. + end + end +end + +def File.binread(fname) + open(fname, 'rb') {|f| + return f.read + } +end + +# for corrupted Windows' stat(2) +def File.dir?(path) + File.directory?((path[-1,1] == '/') ? path : path + '/') +end + + +class ConfigTable + + include Enumerable + + def initialize(rbconfig) + @rbconfig = rbconfig + @items = [] + @table = {} + # options + @install_prefix = nil + @config_opt = nil + @verbose = true + @no_harm = false + end + + attr_accessor :install_prefix + attr_accessor :config_opt + + attr_writer :verbose + + def verbose? + @verbose + end + + attr_writer :no_harm + + def no_harm? + @no_harm + end + + def [](key) + lookup(key).resolve(self) + end + + def []=(key, val) + lookup(key).set val + end + + def names + @items.map {|i| i.name } + end + + def each(&block) + @items.each(&block) + end + + def key?(name) + @table.key?(name) + end + + def lookup(name) + @table[name] or setup_rb_error "no such config item: #{name}" + end + + def add(item) + @items.push item + @table[item.name] = item + end + + def remove(name) + item = lookup(name) + @items.delete_if {|i| i.name == name } + @table.delete_if {|name, i| i.name == name } + item + end + + def load_script(path, inst = nil) + if File.file?(path) + MetaConfigEnvironment.new(self, inst).instance_eval File.read(path), path + end + end + + def savefile + '.config' + end + + def load_savefile + begin + File.foreach(savefile()) do |line| + k, v = *line.split(/=/, 2) + self[k] = v.strip + end + rescue Errno::ENOENT + setup_rb_error $!.message + "\n#{File.basename($0)} config first" + end + end + + def save + @items.each {|i| i.value } + File.open(savefile(), 'w') {|f| + @items.each do |i| + f.printf "%s=%s\n", i.name, i.value if i.value? and i.value + end + } + end + + def load_standard_entries + standard_entries(@rbconfig).each do |ent| + add ent + end + end + + def standard_entries(rbconfig) + c = rbconfig + + rubypath = File.join(c['bindir'], c['ruby_install_name'] + c['EXEEXT']) + + major = c['MAJOR'].to_i + minor = c['MINOR'].to_i + teeny = c['TEENY'].to_i + version = "#{major}.#{minor}" + + # ruby ver. >= 1.4.4? + newpath_p = ((major >= 2) or + ((major == 1) and + ((minor >= 5) or + ((minor == 4) and (teeny >= 4))))) + + if c['rubylibdir'] + # V > 1.6.3 + libruby = "#{c['prefix']}/lib/ruby" + librubyver = c['rubylibdir'] + librubyverarch = c['archdir'] + siteruby = c['sitedir'] + siterubyver = c['sitelibdir'] + siterubyverarch = c['sitearchdir'] + elsif newpath_p + # 1.4.4 <= V <= 1.6.3 + libruby = "#{c['prefix']}/lib/ruby" + librubyver = "#{c['prefix']}/lib/ruby/#{version}" + librubyverarch = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}" + siteruby = c['sitedir'] + siterubyver = "$siteruby/#{version}" + siterubyverarch = "$siterubyver/#{c['arch']}" + else + # V < 1.4.4 + libruby = "#{c['prefix']}/lib/ruby" + librubyver = "#{c['prefix']}/lib/ruby/#{version}" + librubyverarch = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}" + siteruby = "#{c['prefix']}/lib/ruby/#{version}/site_ruby" + siterubyver = siteruby + siterubyverarch = "$siterubyver/#{c['arch']}" + end + parameterize = lambda {|path| + path.sub(/\A#{Regexp.quote(c['prefix'])}/, '$prefix') + } + + if arg = c['configure_args'].split.detect {|arg| /--with-make-prog=/ =~ arg } + makeprog = arg.sub(/'/, '').split(/=/, 2)[1] + else + makeprog = 'make' + end + + [ + ExecItem.new('installdirs', 'std/site/home', + 'std: install under libruby; site: install under site_ruby; home: install under $HOME')\ + {|val, table| + case val + when 'std' + table['rbdir'] = '$librubyver' + table['sodir'] = '$librubyverarch' + when 'site' + table['rbdir'] = '$siterubyver' + table['sodir'] = '$siterubyverarch' + when 'home' + setup_rb_error '$HOME was not set' unless ENV['HOME'] + table['prefix'] = ENV['HOME'] + table['rbdir'] = '$libdir/ruby' + table['sodir'] = '$libdir/ruby' + end + }, + PathItem.new('prefix', 'path', c['prefix'], + 'path prefix of target environment'), + PathItem.new('bindir', 'path', parameterize.call(c['bindir']), + 'the directory for commands'), + PathItem.new('libdir', 'path', parameterize.call(c['libdir']), + 'the directory for libraries'), + PathItem.new('datadir', 'path', parameterize.call(c['datadir']), + 'the directory for shared data'), + PathItem.new('mandir', 'path', parameterize.call(c['mandir']), + 'the directory for man pages'), + PathItem.new('sysconfdir', 'path', parameterize.call(c['sysconfdir']), + 'the directory for system configuration files'), + PathItem.new('localstatedir', 'path', parameterize.call(c['localstatedir']), + 'the directory for local state data'), + PathItem.new('libruby', 'path', libruby, + 'the directory for ruby libraries'), + PathItem.new('librubyver', 'path', librubyver, + 'the directory for standard ruby libraries'), + PathItem.new('librubyverarch', 'path', librubyverarch, + 'the directory for standard ruby extensions'), + PathItem.new('siteruby', 'path', siteruby, + 'the directory for version-independent aux ruby libraries'), + PathItem.new('siterubyver', 'path', siterubyver, + 'the directory for aux ruby libraries'), + PathItem.new('siterubyverarch', 'path', siterubyverarch, + 'the directory for aux ruby binaries'), + PathItem.new('rbdir', 'path', '$siterubyver', + 'the directory for ruby scripts'), + PathItem.new('sodir', 'path', '$siterubyverarch', + 'the directory for ruby extentions'), + PathItem.new('rubypath', 'path', rubypath, + 'the path to set to #! line'), + ProgramItem.new('rubyprog', 'name', rubypath, + 'the ruby program using for installation'), + ProgramItem.new('makeprog', 'name', makeprog, + 'the make program to compile ruby extentions'), + SelectItem.new('shebang', 'all/ruby/never', 'ruby', + 'shebang line (#!) editing mode'), + BoolItem.new('without-ext', 'yes/no', 'no', + 'does not compile/install ruby extentions') + ] + end + private :standard_entries + + def load_multipackage_entries + multipackage_entries().each do |ent| + add ent + end + end + + def multipackage_entries + [ + PackageSelectionItem.new('with', 'name,name...', '', 'ALL', + 'package names that you want to install'), + PackageSelectionItem.new('without', 'name,name...', '', 'NONE', + 'package names that you do not want to install') + ] + end + private :multipackage_entries + + ALIASES = { + 'std-ruby' => 'librubyver', + 'stdruby' => 'librubyver', + 'rubylibdir' => 'librubyver', + 'archdir' => 'librubyverarch', + 'site-ruby-common' => 'siteruby', # For backward compatibility + 'site-ruby' => 'siterubyver', # For backward compatibility + 'bin-dir' => 'bindir', + 'bin-dir' => 'bindir', + 'rb-dir' => 'rbdir', + 'so-dir' => 'sodir', + 'data-dir' => 'datadir', + 'ruby-path' => 'rubypath', + 'ruby-prog' => 'rubyprog', + 'ruby' => 'rubyprog', + 'make-prog' => 'makeprog', + 'make' => 'makeprog' + } + + def fixup + ALIASES.each do |ali, name| + @table[ali] = @table[name] + end + @items.freeze + @table.freeze + @options_re = /\A--(#{@table.keys.join('|')})(?:=(.*))?\z/ + end + + def parse_opt(opt) + m = @options_re.match(opt) or setup_rb_error "config: unknown option #{opt}" + m.to_a[1,2] + end + + def dllext + @rbconfig['DLEXT'] + end + + def value_config?(name) + lookup(name).value? + end + + class Item + def initialize(name, template, default, desc) + @name = name.freeze + @template = template + @value = default + @default = default + @description = desc + end + + attr_reader :name + attr_reader :description + + attr_accessor :default + alias help_default default + + def help_opt + "--#{@name}=#{@template}" + end + + def value? + true + end + + def value + @value + end + + def resolve(table) + @value.gsub(%r<\$([^/]+)>) { table[$1] } + end + + def set(val) + @value = check(val) + end + + private + + def check(val) + setup_rb_error "config: --#{name} requires argument" unless val + val + end + end + + class BoolItem < Item + def config_type + 'bool' + end + + def help_opt + "--#{@name}" + end + + private + + def check(val) + return 'yes' unless val + case val + when /\Ay(es)?\z/i, /\At(rue)?\z/i then 'yes' + when /\An(o)?\z/i, /\Af(alse)\z/i then 'no' + else + setup_rb_error "config: --#{@name} accepts only yes/no for argument" + end + end + end + + class PathItem < Item + def config_type + 'path' + end + + private + + def check(path) + setup_rb_error "config: --#{@name} requires argument" unless path + path[0,1] == '$' ? path : File.expand_path(path) + end + end + + class ProgramItem < Item + def config_type + 'program' + end + end + + class SelectItem < Item + def initialize(name, selection, default, desc) + super + @ok = selection.split('/') + end + + def config_type + 'select' + end + + private + + def check(val) + unless @ok.include?(val.strip) + setup_rb_error "config: use --#{@name}=#{@template} (#{val})" + end + val.strip + end + end + + class ExecItem < Item + def initialize(name, selection, desc, &block) + super name, selection, nil, desc + @ok = selection.split('/') + @action = block + end + + def config_type + 'exec' + end + + def value? + false + end + + def resolve(table) + setup_rb_error "$#{name()} wrongly used as option value" + end + + undef set + + def evaluate(val, table) + v = val.strip.downcase + unless @ok.include?(v) + setup_rb_error "invalid option --#{@name}=#{val} (use #{@template})" + end + @action.call v, table + end + end + + class PackageSelectionItem < Item + def initialize(name, template, default, help_default, desc) + super name, template, default, desc + @help_default = help_default + end + + attr_reader :help_default + + def config_type + 'package' + end + + private + + def check(val) + unless File.dir?("packages/#{val}") + setup_rb_error "config: no such package: #{val}" + end + val + end + end + + class MetaConfigEnvironment + def initialize(config, installer) + @config = config + @installer = installer + end + + def config_names + @config.names + end + + def config?(name) + @config.key?(name) + end + + def bool_config?(name) + @config.lookup(name).config_type == 'bool' + end + + def path_config?(name) + @config.lookup(name).config_type == 'path' + end + + def value_config?(name) + @config.lookup(name).config_type != 'exec' + end + + def add_config(item) + @config.add item + end + + def add_bool_config(name, default, desc) + @config.add BoolItem.new(name, 'yes/no', default ? 'yes' : 'no', desc) + end + + def add_path_config(name, default, desc) + @config.add PathItem.new(name, 'path', default, desc) + end + + def set_config_default(name, default) + @config.lookup(name).default = default + end + + def remove_config(name) + @config.remove(name) + end + + # For only multipackage + def packages + raise '[setup.rb fatal] multi-package metaconfig API packages() called for single-package; contact application package vendor' unless @installer + @installer.packages + end + + # For only multipackage + def declare_packages(list) + raise '[setup.rb fatal] multi-package metaconfig API declare_packages() called for single-package; contact application package vendor' unless @installer + @installer.packages = list + end + end + +end # class ConfigTable + + +# This module requires: #verbose?, #no_harm? +module FileOperations + + def mkdir_p(dirname, prefix = nil) + dirname = prefix + File.expand_path(dirname) if prefix + $stderr.puts "mkdir -p #{dirname}" if verbose? + return if no_harm? + + # Does not check '/', it's too abnormal. + dirs = File.expand_path(dirname).split(%r<(?=/)>) + if /\A[a-z]:\z/i =~ dirs[0] + disk = dirs.shift + dirs[0] = disk + dirs[0] + end + dirs.each_index do |idx| + path = dirs[0..idx].join('') + Dir.mkdir path unless File.dir?(path) + end + end + + def rm_f(path) + $stderr.puts "rm -f #{path}" if verbose? + return if no_harm? + force_remove_file path + end + + def rm_rf(path) + $stderr.puts "rm -rf #{path}" if verbose? + return if no_harm? + remove_tree path + end + + def remove_tree(path) + if File.symlink?(path) + remove_file path + elsif File.dir?(path) + remove_tree0 path + else + force_remove_file path + end + end + + def remove_tree0(path) + Dir.foreach(path) do |ent| + next if ent == '.' + next if ent == '..' + entpath = "#{path}/#{ent}" + if File.symlink?(entpath) + remove_file entpath + elsif File.dir?(entpath) + remove_tree0 entpath + else + force_remove_file entpath + end + end + begin + Dir.rmdir path + rescue Errno::ENOTEMPTY + # directory may not be empty + end + end + + def move_file(src, dest) + force_remove_file dest + begin + File.rename src, dest + rescue + File.open(dest, 'wb') {|f| + f.write File.binread(src) + } + File.chmod File.stat(src).mode, dest + File.unlink src + end + end + + def force_remove_file(path) + begin + remove_file path + rescue + end + end + + def remove_file(path) + File.chmod 0777, path + File.unlink path + end + + def install(from, dest, mode, prefix = nil) + $stderr.puts "install #{from} #{dest}" if verbose? + return if no_harm? + + realdest = prefix ? prefix + File.expand_path(dest) : dest + realdest = File.join(realdest, File.basename(from)) if File.dir?(realdest) + str = File.binread(from) + if diff?(str, realdest) + verbose_off { + rm_f realdest if File.exist?(realdest) + } + File.open(realdest, 'wb') {|f| + f.write str + } + File.chmod mode, realdest + + File.open("#{objdir_root()}/InstalledFiles", 'a') {|f| + if prefix + f.puts realdest.sub(prefix, '') + else + f.puts realdest + end + } + end + end + + def diff?(new_content, path) + return true unless File.exist?(path) + new_content != File.binread(path) + end + + def command(*args) + $stderr.puts args.join(' ') if verbose? + system(*args) or raise RuntimeError, + "system(#{args.map{|a| a.inspect }.join(' ')}) failed" + end + + def ruby(*args) + command config('rubyprog'), *args + end + + def make(task = nil) + command(*[config('makeprog'), task].compact) + end + + def extdir?(dir) + File.exist?("#{dir}/MANIFEST") or File.exist?("#{dir}/extconf.rb") + end + + def files_of(dir) + Dir.open(dir) {|d| + return d.select {|ent| File.file?("#{dir}/#{ent}") } + } + end + + DIR_REJECT = %w( . .. CVS SCCS RCS CVS.adm .svn ) + + def directories_of(dir) + Dir.open(dir) {|d| + return d.select {|ent| File.dir?("#{dir}/#{ent}") } - DIR_REJECT + } + end + +end + + +# This module requires: #srcdir_root, #objdir_root, #relpath +module HookScriptAPI + + def get_config(key) + @config[key] + end + + alias config get_config + + # obsolete: use metaconfig to change configuration + def set_config(key, val) + @config[key] = val + end + + # + # srcdir/objdir (works only in the package directory) + # + + def curr_srcdir + "#{srcdir_root()}/#{relpath()}" + end + + def curr_objdir + "#{objdir_root()}/#{relpath()}" + end + + def srcfile(path) + "#{curr_srcdir()}/#{path}" + end + + def srcexist?(path) + File.exist?(srcfile(path)) + end + + def srcdirectory?(path) + File.dir?(srcfile(path)) + end + + def srcfile?(path) + File.file?(srcfile(path)) + end + + def srcentries(path = '.') + Dir.open("#{curr_srcdir()}/#{path}") {|d| + return d.to_a - %w(. ..) + } + end + + def srcfiles(path = '.') + srcentries(path).select {|fname| + File.file?(File.join(curr_srcdir(), path, fname)) + } + end + + def srcdirectories(path = '.') + srcentries(path).select {|fname| + File.dir?(File.join(curr_srcdir(), path, fname)) + } + end + +end + + +class ToplevelInstaller + + Version = '3.4.1' + Copyright = 'Copyright (c) 2000-2005 Minero Aoki' + + TASKS = [ + [ 'all', 'do config, setup, then install' ], + [ 'config', 'saves your configurations' ], + [ 'show', 'shows current configuration' ], + [ 'setup', 'compiles ruby extentions and others' ], + [ 'install', 'installs files' ], + [ 'test', 'run all tests in test/' ], + [ 'clean', "does `make clean' for each extention" ], + [ 'distclean',"does `make distclean' for each extention" ] + ] + + def ToplevelInstaller.invoke + config = ConfigTable.new(load_rbconfig()) + config.load_standard_entries + config.load_multipackage_entries if multipackage? + config.fixup + klass = (multipackage?() ? ToplevelInstallerMulti : ToplevelInstaller) + klass.new(File.dirname($0), config).invoke + end + + def ToplevelInstaller.multipackage? + File.dir?(File.dirname($0) + '/packages') + end + + def ToplevelInstaller.load_rbconfig + if arg = ARGV.detect {|arg| /\A--rbconfig=/ =~ arg } + ARGV.delete(arg) + load File.expand_path(arg.split(/=/, 2)[1]) + $".push 'rbconfig.rb' + else + require 'rbconfig' + end + ::Config::CONFIG + end + + def initialize(ardir_root, config) + @ardir = File.expand_path(ardir_root) + @config = config + # cache + @valid_task_re = nil + end + + def config(key) + @config[key] + end + + def inspect + "#<#{self.class} #{__id__()}>" + end + + def invoke + run_metaconfigs + case task = parsearg_global() + when nil, 'all' + parsearg_config + init_installers + exec_config + exec_setup + exec_install + else + case task + when 'config', 'test' + ; + when 'clean', 'distclean' + @config.load_savefile if File.exist?(@config.savefile) + else + @config.load_savefile + end + __send__ "parsearg_#{task}" + init_installers + __send__ "exec_#{task}" + end + end + + def run_metaconfigs + @config.load_script "#{@ardir}/metaconfig" + end + + def init_installers + @installer = Installer.new(@config, @ardir, File.expand_path('.')) + end + + # + # Hook Script API bases + # + + def srcdir_root + @ardir + end + + def objdir_root + '.' + end + + def relpath + '.' + end + + # + # Option Parsing + # + + def parsearg_global + while arg = ARGV.shift + case arg + when /\A\w+\z/ + setup_rb_error "invalid task: #{arg}" unless valid_task?(arg) + return arg + when '-q', '--quiet' + @config.verbose = false + when '--verbose' + @config.verbose = true + when '--help' + print_usage $stdout + exit 0 + when '--version' + puts "#{File.basename($0)} version #{Version}" + exit 0 + when '--copyright' + puts Copyright + exit 0 + else + setup_rb_error "unknown global option '#{arg}'" + end + end + nil + end + + def valid_task?(t) + valid_task_re() =~ t + end + + def valid_task_re + @valid_task_re ||= /\A(?:#{TASKS.map {|task,desc| task }.join('|')})\z/ + end + + def parsearg_no_options + unless ARGV.empty? + task = caller(0).first.slice(%r<`parsearg_(\w+)'>, 1) + setup_rb_error "#{task}: unknown options: #{ARGV.join(' ')}" + end + end + + alias parsearg_show parsearg_no_options + alias parsearg_setup parsearg_no_options + alias parsearg_test parsearg_no_options + alias parsearg_clean parsearg_no_options + alias parsearg_distclean parsearg_no_options + + def parsearg_config + evalopt = [] + set = [] + @config.config_opt = [] + while i = ARGV.shift + if /\A--?\z/ =~ i + @config.config_opt = ARGV.dup + break + end + name, value = *@config.parse_opt(i) + if @config.value_config?(name) + @config[name] = value + else + evalopt.push [name, value] + end + set.push name + end + evalopt.each do |name, value| + @config.lookup(name).evaluate value, @config + end + # Check if configuration is valid + set.each do |n| + @config[n] if @config.value_config?(n) + end + end + + def parsearg_install + @config.no_harm = false + @config.install_prefix = '' + while a = ARGV.shift + case a + when '--no-harm' + @config.no_harm = true + when /\A--prefix=/ + path = a.split(/=/, 2)[1] + path = File.expand_path(path) unless path[0,1] == '/' + @config.install_prefix = path + else + setup_rb_error "install: unknown option #{a}" + end + end + end + + def print_usage(out) + out.puts 'Typical Installation Procedure:' + out.puts " $ ruby #{File.basename $0} config" + out.puts " $ ruby #{File.basename $0} setup" + out.puts " # ruby #{File.basename $0} install (may require root privilege)" + out.puts + out.puts 'Detailed Usage:' + out.puts " ruby #{File.basename $0} " + out.puts " ruby #{File.basename $0} [] []" + + fmt = " %-24s %s\n" + out.puts + out.puts 'Global options:' + out.printf fmt, '-q,--quiet', 'suppress message outputs' + out.printf fmt, ' --verbose', 'output messages verbosely' + out.printf fmt, ' --help', 'print this message' + out.printf fmt, ' --version', 'print version and quit' + out.printf fmt, ' --copyright', 'print copyright and quit' + out.puts + out.puts 'Tasks:' + TASKS.each do |name, desc| + out.printf fmt, name, desc + end + + fmt = " %-24s %s [%s]\n" + out.puts + out.puts 'Options for CONFIG or ALL:' + @config.each do |item| + out.printf fmt, item.help_opt, item.description, item.help_default + end + out.printf fmt, '--rbconfig=path', 'rbconfig.rb to load',"running ruby's" + out.puts + out.puts 'Options for INSTALL:' + out.printf fmt, '--no-harm', 'only display what to do if given', 'off' + out.printf fmt, '--prefix=path', 'install path prefix', '' + out.puts + end + + # + # Task Handlers + # + + def exec_config + @installer.exec_config + @config.save # must be final + end + + def exec_setup + @installer.exec_setup + end + + def exec_install + @installer.exec_install + end + + def exec_test + @installer.exec_test + end + + def exec_show + @config.each do |i| + printf "%-20s %s\n", i.name, i.value if i.value? + end + end + + def exec_clean + @installer.exec_clean + end + + def exec_distclean + @installer.exec_distclean + end + +end # class ToplevelInstaller + + +class ToplevelInstallerMulti < ToplevelInstaller + + include FileOperations + + def initialize(ardir_root, config) + super + @packages = directories_of("#{@ardir}/packages") + raise 'no package exists' if @packages.empty? + @root_installer = Installer.new(@config, @ardir, File.expand_path('.')) + end + + def run_metaconfigs + @config.load_script "#{@ardir}/metaconfig", self + @packages.each do |name| + @config.load_script "#{@ardir}/packages/#{name}/metaconfig" + end + end + + attr_reader :packages + + def packages=(list) + raise 'package list is empty' if list.empty? + list.each do |name| + raise "directory packages/#{name} does not exist"\ + unless File.dir?("#{@ardir}/packages/#{name}") + end + @packages = list + end + + def init_installers + @installers = {} + @packages.each do |pack| + @installers[pack] = Installer.new(@config, + "#{@ardir}/packages/#{pack}", + "packages/#{pack}") + end + with = extract_selection(config('with')) + without = extract_selection(config('without')) + @selected = @installers.keys.select {|name| + (with.empty? or with.include?(name)) \ + and not without.include?(name) + } + end + + def extract_selection(list) + a = list.split(/,/) + a.each do |name| + setup_rb_error "no such package: #{name}" unless @installers.key?(name) + end + a + end + + def print_usage(f) + super + f.puts 'Inluded packages:' + f.puts ' ' + @packages.sort.join(' ') + f.puts + end + + # + # Task Handlers + # + + def exec_config + run_hook 'pre-config' + each_selected_installers {|inst| inst.exec_config } + run_hook 'post-config' + @config.save # must be final + end + + def exec_setup + run_hook 'pre-setup' + each_selected_installers {|inst| inst.exec_setup } + run_hook 'post-setup' + end + + def exec_install + run_hook 'pre-install' + each_selected_installers {|inst| inst.exec_install } + run_hook 'post-install' + end + + def exec_test + run_hook 'pre-test' + each_selected_installers {|inst| inst.exec_test } + run_hook 'post-test' + end + + def exec_clean + rm_f @config.savefile + run_hook 'pre-clean' + each_selected_installers {|inst| inst.exec_clean } + run_hook 'post-clean' + end + + def exec_distclean + rm_f @config.savefile + run_hook 'pre-distclean' + each_selected_installers {|inst| inst.exec_distclean } + run_hook 'post-distclean' + end + + # + # lib + # + + def each_selected_installers + Dir.mkdir 'packages' unless File.dir?('packages') + @selected.each do |pack| + $stderr.puts "Processing the package `#{pack}' ..." if verbose? + Dir.mkdir "packages/#{pack}" unless File.dir?("packages/#{pack}") + Dir.chdir "packages/#{pack}" + yield @installers[pack] + Dir.chdir '../..' + end + end + + def run_hook(id) + @root_installer.run_hook id + end + + # module FileOperations requires this + def verbose? + @config.verbose? + end + + # module FileOperations requires this + def no_harm? + @config.no_harm? + end + +end # class ToplevelInstallerMulti + + +class Installer + + FILETYPES = %w( bin lib ext data conf man ) + + include FileOperations + include HookScriptAPI + + def initialize(config, srcroot, objroot) + @config = config + @srcdir = File.expand_path(srcroot) + @objdir = File.expand_path(objroot) + @currdir = '.' + end + + def inspect + "#<#{self.class} #{File.basename(@srcdir)}>" + end + + def noop(rel) + end + + # + # Hook Script API base methods + # + + def srcdir_root + @srcdir + end + + def objdir_root + @objdir + end + + def relpath + @currdir + end + + # + # Config Access + # + + # module FileOperations requires this + def verbose? + @config.verbose? + end + + # module FileOperations requires this + def no_harm? + @config.no_harm? + end + + def verbose_off + begin + save, @config.verbose = @config.verbose?, false + yield + ensure + @config.verbose = save + end + end + + # + # TASK config + # + + def exec_config + exec_task_traverse 'config' + end + + alias config_dir_bin noop + alias config_dir_lib noop + + def config_dir_ext(rel) + extconf if extdir?(curr_srcdir()) + end + + alias config_dir_data noop + alias config_dir_conf noop + alias config_dir_man noop + + def extconf + ruby "#{curr_srcdir()}/extconf.rb", *@config.config_opt + end + + # + # TASK setup + # + + def exec_setup + exec_task_traverse 'setup' + end + + def setup_dir_bin(rel) + files_of(curr_srcdir()).each do |fname| + update_shebang_line "#{curr_srcdir()}/#{fname}" + end + end + + alias setup_dir_lib noop + + def setup_dir_ext(rel) + make if extdir?(curr_srcdir()) + end + + alias setup_dir_data noop + alias setup_dir_conf noop + alias setup_dir_man noop + + def update_shebang_line(path) + return if no_harm? + return if config('shebang') == 'never' + old = Shebang.load(path) + if old + $stderr.puts "warning: #{path}: Shebang line includes too many args. It is not portable and your program may not work." if old.args.size > 1 + new = new_shebang(old) + return if new.to_s == old.to_s + else + return unless config('shebang') == 'all' + new = Shebang.new(config('rubypath')) + end + $stderr.puts "updating shebang: #{File.basename(path)}" if verbose? + open_atomic_writer(path) {|output| + File.open(path, 'rb') {|f| + f.gets if old # discard + output.puts new.to_s + output.print f.read + } + } + end + + def new_shebang(old) + if /\Aruby/ =~ File.basename(old.cmd) + Shebang.new(config('rubypath'), old.args) + elsif File.basename(old.cmd) == 'env' and old.args.first == 'ruby' + Shebang.new(config('rubypath'), old.args[1..-1]) + else + return old unless config('shebang') == 'all' + Shebang.new(config('rubypath')) + end + end + + def open_atomic_writer(path, &block) + tmpfile = File.basename(path) + '.tmp' + begin + File.open(tmpfile, 'wb', &block) + File.rename tmpfile, File.basename(path) + ensure + File.unlink tmpfile if File.exist?(tmpfile) + end + end + + class Shebang + def Shebang.load(path) + line = nil + File.open(path) {|f| + line = f.gets + } + return nil unless /\A#!/ =~ line + parse(line) + end + + def Shebang.parse(line) + cmd, *args = *line.strip.sub(/\A\#!/, '').split(' ') + new(cmd, args) + end + + def initialize(cmd, args = []) + @cmd = cmd + @args = args + end + + attr_reader :cmd + attr_reader :args + + def to_s + "#! #{@cmd}" + (@args.empty? ? '' : " #{@args.join(' ')}") + end + end + + # + # TASK install + # + + def exec_install + rm_f 'InstalledFiles' + exec_task_traverse 'install' + end + + def install_dir_bin(rel) + install_files targetfiles(), "#{config('bindir')}/#{rel}", 0755 + end + + def install_dir_lib(rel) + install_files libfiles(), "#{config('rbdir')}/#{rel}", 0644 + end + + def install_dir_ext(rel) + return unless extdir?(curr_srcdir()) + install_files rubyextentions('.'), + "#{config('sodir')}/#{File.dirname(rel)}", + 0555 + end + + def install_dir_data(rel) + install_files targetfiles(), "#{config('datadir')}/#{rel}", 0644 + end + + def install_dir_conf(rel) + # FIXME: should not remove current config files + # (rename previous file to .old/.org) + install_files targetfiles(), "#{config('sysconfdir')}/#{rel}", 0644 + end + + def install_dir_man(rel) + install_files targetfiles(), "#{config('mandir')}/#{rel}", 0644 + end + + def install_files(list, dest, mode) + mkdir_p dest, @config.install_prefix + list.each do |fname| + install fname, dest, mode, @config.install_prefix + end + end + + def libfiles + glob_reject(%w(*.y *.output), targetfiles()) + end + + def rubyextentions(dir) + ents = glob_select("*.#{@config.dllext}", targetfiles()) + if ents.empty? + setup_rb_error "no ruby extention exists: 'ruby #{$0} setup' first" + end + ents + end + + def targetfiles + mapdir(existfiles() - hookfiles()) + end + + def mapdir(ents) + ents.map {|ent| + if File.exist?(ent) + then ent # objdir + else "#{curr_srcdir()}/#{ent}" # srcdir + end + } + end + + # picked up many entries from cvs-1.11.1/src/ignore.c + JUNK_FILES = %w( + core RCSLOG tags TAGS .make.state + .nse_depinfo #* .#* cvslog.* ,* .del-* *.olb + *~ *.old *.bak *.BAK *.orig *.rej _$* *$ + + *.org *.in .* + ) + + def existfiles + glob_reject(JUNK_FILES, (files_of(curr_srcdir()) | files_of('.'))) + end + + def hookfiles + %w( pre-%s post-%s pre-%s.rb post-%s.rb ).map {|fmt| + %w( config setup install clean ).map {|t| sprintf(fmt, t) } + }.flatten + end + + def glob_select(pat, ents) + re = globs2re([pat]) + ents.select {|ent| re =~ ent } + end + + def glob_reject(pats, ents) + re = globs2re(pats) + ents.reject {|ent| re =~ ent } + end + + GLOB2REGEX = { + '.' => '\.', + '$' => '\$', + '#' => '\#', + '*' => '.*' + } + + def globs2re(pats) + /\A(?:#{ + pats.map {|pat| pat.gsub(/[\.\$\#\*]/) {|ch| GLOB2REGEX[ch] } }.join('|') + })\z/ + end + + # + # TASK test + # + + TESTDIR = 'test' + + def exec_test + unless File.directory?('test') + $stderr.puts 'no test in this package' if verbose? + return + end + $stderr.puts 'Running tests...' if verbose? + begin + require 'test/unit' + rescue LoadError + setup_rb_error 'test/unit cannot loaded. You need Ruby 1.8 or later to invoke this task.' + end + runner = Test::Unit::AutoRunner.new(true) + runner.to_run << TESTDIR + runner.run + end + + # + # TASK clean + # + + def exec_clean + exec_task_traverse 'clean' + rm_f @config.savefile + rm_f 'InstalledFiles' + end + + alias clean_dir_bin noop + alias clean_dir_lib noop + alias clean_dir_data noop + alias clean_dir_conf noop + alias clean_dir_man noop + + def clean_dir_ext(rel) + return unless extdir?(curr_srcdir()) + make 'clean' if File.file?('Makefile') + end + + # + # TASK distclean + # + + def exec_distclean + exec_task_traverse 'distclean' + rm_f @config.savefile + rm_f 'InstalledFiles' + end + + alias distclean_dir_bin noop + alias distclean_dir_lib noop + + def distclean_dir_ext(rel) + return unless extdir?(curr_srcdir()) + make 'distclean' if File.file?('Makefile') + end + + alias distclean_dir_data noop + alias distclean_dir_conf noop + alias distclean_dir_man noop + + # + # Traversing + # + + def exec_task_traverse(task) + run_hook "pre-#{task}" + FILETYPES.each do |type| + if type == 'ext' and config('without-ext') == 'yes' + $stderr.puts 'skipping ext/* by user option' if verbose? + next + end + traverse task, type, "#{task}_dir_#{type}" + end + run_hook "post-#{task}" + end + + def traverse(task, rel, mid) + dive_into(rel) { + run_hook "pre-#{task}" + __send__ mid, rel.sub(%r[\A.*?(?:/|\z)], '') + directories_of(curr_srcdir()).each do |d| + traverse task, "#{rel}/#{d}", mid + end + run_hook "post-#{task}" + } + end + + def dive_into(rel) + return unless File.dir?("#{@srcdir}/#{rel}") + + dir = File.basename(rel) + Dir.mkdir dir unless File.dir?(dir) + prevdir = Dir.pwd + Dir.chdir dir + $stderr.puts '---> ' + rel if verbose? + @currdir = rel + yield + Dir.chdir prevdir + $stderr.puts '<--- ' + rel if verbose? + @currdir = File.dirname(rel) + end + + def run_hook(id) + path = [ "#{curr_srcdir()}/#{id}", + "#{curr_srcdir()}/#{id}.rb" ].detect {|cand| File.file?(cand) } + return unless path + begin + instance_eval File.read(path), path, 1 + rescue + raise if $DEBUG + setup_rb_error "hook #{path} failed:\n" + $!.message + end + end + +end # class Installer + + +class SetupError < StandardError; end + +def setup_rb_error(msg) + raise SetupError, msg +end + +if $0 == __FILE__ + begin + ToplevelInstaller.invoke + rescue SetupError + raise if $DEBUG + $stderr.puts $!.message + $stderr.puts "Try 'ruby #{$0} --help' for detailed usage." + exit 1 + end +end diff --git a/vendor/gems/mime-types-1.16/test/test_mime_type.rb b/vendor/gems/mime-types-1.16/test/test_mime_type.rb new file mode 100644 index 0000000..348dd12 --- /dev/null +++ b/vendor/gems/mime-types-1.16/test/test_mime_type.rb @@ -0,0 +1,356 @@ +#! /usr/bin/env ruby +#-- +# MIME::Types +# A Ruby implementation of a MIME Types information library. Based in spirit +# on the Perl MIME::Types information library by Mark Overmeer. +# http://rubyforge.org/projects/mime-types/ +# +# Licensed under the Ruby disjunctive licence with the GNU GPL or the Perl +# Artistic licence. See Licence.txt for more information. +# +# Copyright 2003 - 2009 Austin Ziegler +#++ +$LOAD_PATH.unshift("#{File.dirname(__FILE__)}/../lib") if __FILE__ == $0 + +require 'mime/types' +require 'test/unit' unless defined? $ZENTEST and $ZENTEST + +module TestMIME + class TestType < Test::Unit::TestCase #:nodoc: + def setup + @zip = MIME::Type.new('x-appl/x-zip') { |t| t.extensions = ['zip', 'zp'] } + end + + def test_class_from_array + assert_nothing_raised do + @yaml = MIME::Type.from_array('text/x-yaml', %w(yaml yml), '8bit', 'linux') + end + assert_instance_of(MIME::Type, @yaml) + assert_equal('text/yaml', @yaml.simplified) + end + + def test_class_from_hash + assert_nothing_raised do + @yaml = MIME::Type.from_hash('Content-Type' => 'text/x-yaml', + 'Content-Transfer-Encoding' => '8bit', + 'System' => 'linux', + 'Extensions' => %w(yaml yml)) + end + assert_instance_of(MIME::Type, @yaml) + assert_equal('text/yaml', @yaml.simplified) + end + + def test_class_from_mime_type + assert_nothing_raised do + @zip2 = MIME::Type.from_mime_type(@zip) + end + assert_instance_of(MIME::Type, @zip) + assert_equal('appl/zip', @zip.simplified) + assert_not_equal(@zip.object_id, @zip2.object_id) + end + + def test_class_simplified + assert_equal(MIME::Type.simplified('text/plain'), 'text/plain') + assert_equal(MIME::Type.simplified('image/jpeg'), 'image/jpeg') + assert_equal(MIME::Type.simplified('application/x-msword'), 'application/msword') + assert_equal(MIME::Type.simplified('text/vCard'), 'text/vcard') + assert_equal(MIME::Type.simplified('application/pkcs7-mime'), 'application/pkcs7-mime') + assert_equal(@zip.simplified, 'appl/zip') + assert_equal(MIME::Type.simplified('x-xyz/abc'), 'xyz/abc') + end + + def test_CMP # '<=>' + assert(MIME::Type.new('text/plain') == MIME::Type.new('text/plain')) + assert(MIME::Type.new('text/plain') != MIME::Type.new('image/jpeg')) + assert(MIME::Type.new('text/plain') == 'text/plain') + assert(MIME::Type.new('text/plain') != 'image/jpeg') + assert(MIME::Type.new('text/plain') > MIME::Type.new('text/html')) + assert(MIME::Type.new('text/plain') > 'text/html') + assert(MIME::Type.new('text/html') < MIME::Type.new('text/plain')) + assert(MIME::Type.new('text/html') < 'text/plain') + assert('text/html' == MIME::Type.new('text/html')) + assert('text/html' < MIME::Type.new('text/plain')) + assert('text/plain' > MIME::Type.new('text/html')) + end + + def test_ascii_eh + assert(MIME::Type.new('text/plain').ascii?) + assert(!MIME::Type.new('image/jpeg').ascii?) + assert(!MIME::Type.new('application/x-msword').ascii?) + assert(MIME::Type.new('text/vCard').ascii?) + assert(!MIME::Type.new('application/pkcs7-mime').ascii?) + assert(!@zip.ascii?) + end + + def test_binary_eh + assert(!MIME::Type.new('text/plain').binary?) + assert(MIME::Type.new('image/jpeg').binary?) + assert(MIME::Type.new('application/x-msword').binary?) + assert(!MIME::Type.new('text/vCard').binary?) + assert(MIME::Type.new('application/pkcs7-mime').binary?) + assert(@zip.binary?) + end + + def test_complete_eh + assert_nothing_raised do + @yaml = MIME::Type.from_array('text/x-yaml', %w(yaml yml), '8bit', + 'linux') + end + assert(@yaml.complete?) + assert_nothing_raised { @yaml.extensions = nil } + assert(!@yaml.complete?) + end + + def test_content_type + assert_equal(MIME::Type.new('text/plain').content_type, 'text/plain') + assert_equal(MIME::Type.new('image/jpeg').content_type, 'image/jpeg') + assert_equal(MIME::Type.new('application/x-msword').content_type, 'application/x-msword') + assert_equal(MIME::Type.new('text/vCard').content_type, 'text/vCard') + assert_equal(MIME::Type.new('application/pkcs7-mime').content_type, 'application/pkcs7-mime') + assert_equal(@zip.content_type, 'x-appl/x-zip'); + end + + def test_encoding + assert_equal(MIME::Type.new('text/plain').encoding, 'quoted-printable') + assert_equal(MIME::Type.new('image/jpeg').encoding, 'base64') + assert_equal(MIME::Type.new('application/x-msword').encoding, 'base64') + assert_equal(MIME::Type.new('text/vCard').encoding, 'quoted-printable') + assert_equal(MIME::Type.new('application/pkcs7-mime').encoding, 'base64') + assert_nothing_raised do + @yaml = MIME::Type.from_array('text/x-yaml', %w(yaml yml), '8bit', + 'linux') + end + assert_equal(@yaml.encoding, '8bit') + assert_nothing_raised { @yaml.encoding = 'base64' } + assert_equal(@yaml.encoding, 'base64') + assert_nothing_raised { @yaml.encoding = :default } + assert_equal(@yaml.encoding, 'quoted-printable') + assert_raises(ArgumentError) { @yaml.encoding = 'binary' } + assert_equal(@zip.encoding, 'base64') + end + + def _test_default_encoding + raise NotImplementedError, 'Need to write test_default_encoding' + end + + def _test_docs + raise NotImplementedError, 'Need to write test_docs' + end + + def _test_docs_equals + raise NotImplementedError, 'Need to write test_docs_equals' + end + + def test_eql? + assert(MIME::Type.new('text/plain').eql?(MIME::Type.new('text/plain'))) + assert(!MIME::Type.new('text/plain').eql?(MIME::Type.new('image/jpeg'))) + assert(!MIME::Type.new('text/plain').eql?('text/plain')) + assert(!MIME::Type.new('text/plain').eql?('image/jpeg')) + end + + def _test_encoding + raise NotImplementedError, 'Need to write test_encoding' + end + + def _test_encoding_equals + raise NotImplementedError, 'Need to write test_encoding_equals' + end + + def test_extensions + assert_nothing_raised do + @yaml = MIME::Type.from_array('text/x-yaml', %w(yaml yml), '8bit', + 'linux') + end + assert_equal(@yaml.extensions, %w(yaml yml)) + assert_nothing_raised { @yaml.extensions = 'yaml' } + assert_equal(@yaml.extensions, ['yaml']) + assert_equal(@zip.extensions.size, 2) + assert_equal(@zip.extensions, ['zip', 'zp']) + end + + def _test_extensions_equals + raise NotImplementedError, 'Need to write test_extensions_equals' + end + + def test_like_eh + assert(MIME::Type.new('text/plain').like?(MIME::Type.new('text/plain'))) + assert(MIME::Type.new('text/plain').like?(MIME::Type.new('text/x-plain'))) + assert(!MIME::Type.new('text/plain').like?(MIME::Type.new('image/jpeg'))) + assert(MIME::Type.new('text/plain').like?('text/plain')) + assert(MIME::Type.new('text/plain').like?('text/x-plain')) + assert(!MIME::Type.new('text/plain').like?('image/jpeg')) + end + + def test_media_type + assert_equal(MIME::Type.new('text/plain').media_type, 'text') + assert_equal(MIME::Type.new('image/jpeg').media_type, 'image') + assert_equal(MIME::Type.new('application/x-msword').media_type, 'application') + assert_equal(MIME::Type.new('text/vCard').media_type, 'text') + assert_equal(MIME::Type.new('application/pkcs7-mime').media_type, 'application') + assert_equal(MIME::Type.new('x-chemical/x-pdb').media_type, 'chemical') + assert_equal(@zip.media_type, 'appl') + end + + def _test_obsolete_eh + raise NotImplementedError, 'Need to write test_obsolete_eh' + end + + def _test_obsolete_equals + raise NotImplementedError, 'Need to write test_obsolete_equals' + end + + def test_platform_eh + assert_nothing_raised do + @yaml = MIME::Type.from_array('text/x-yaml', %w(yaml yml), '8bit', + 'oddbox') + end + assert(!@yaml.platform?) + assert_nothing_raised { @yaml.system = nil } + assert(!@yaml.platform?) + assert_nothing_raised { @yaml.system = /#{RUBY_PLATFORM}/ } + assert(@yaml.platform?) + end + + def test_raw_media_type + assert_equal(MIME::Type.new('text/plain').raw_media_type, 'text') + assert_equal(MIME::Type.new('image/jpeg').raw_media_type, 'image') + assert_equal(MIME::Type.new('application/x-msword').raw_media_type, 'application') + assert_equal(MIME::Type.new('text/vCard').raw_media_type, 'text') + assert_equal(MIME::Type.new('application/pkcs7-mime').raw_media_type, 'application') + + assert_equal(MIME::Type.new('x-chemical/x-pdb').raw_media_type, 'x-chemical') + assert_equal(@zip.raw_media_type, 'x-appl') + end + + def test_raw_sub_type + assert_equal(MIME::Type.new('text/plain').raw_sub_type, 'plain') + assert_equal(MIME::Type.new('image/jpeg').raw_sub_type, 'jpeg') + assert_equal(MIME::Type.new('application/x-msword').raw_sub_type, 'x-msword') + assert_equal(MIME::Type.new('text/vCard').raw_sub_type, 'vCard') + assert_equal(MIME::Type.new('application/pkcs7-mime').raw_sub_type, 'pkcs7-mime') + assert_equal(@zip.raw_sub_type, 'x-zip') + end + + def test_registered_eh + assert(MIME::Type.new('text/plain').registered?) + assert(MIME::Type.new('image/jpeg').registered?) + assert(!MIME::Type.new('application/x-msword').registered?) + assert(MIME::Type.new('text/vCard').registered?) + assert(MIME::Type.new('application/pkcs7-mime').registered?) + assert(!@zip.registered?) + end + + def _test_registered_equals + raise NotImplementedError, 'Need to write test_registered_equals' + end + + def test_signature_eh + assert(!MIME::Type.new('text/plain').signature?) + assert(!MIME::Type.new('image/jpeg').signature?) + assert(!MIME::Type.new('application/x-msword').signature?) + assert(MIME::Type.new('text/vCard').signature?) + assert(MIME::Type.new('application/pkcs7-mime').signature?) + end + + def test_simplified + assert_equal(MIME::Type.new('text/plain').simplified, 'text/plain') + assert_equal(MIME::Type.new('image/jpeg').simplified, 'image/jpeg') + assert_equal(MIME::Type.new('application/x-msword').simplified, 'application/msword') + assert_equal(MIME::Type.new('text/vCard').simplified, 'text/vcard') + assert_equal(MIME::Type.new('application/pkcs7-mime').simplified, 'application/pkcs7-mime') + assert_equal(MIME::Type.new('x-chemical/x-pdb').simplified, 'chemical/pdb') + end + + def test_sub_type + assert_equal(MIME::Type.new('text/plain').sub_type, 'plain') + assert_equal(MIME::Type.new('image/jpeg').sub_type, 'jpeg') + assert_equal(MIME::Type.new('application/x-msword').sub_type, 'msword') + assert_equal(MIME::Type.new('text/vCard').sub_type, 'vcard') + assert_equal(MIME::Type.new('application/pkcs7-mime').sub_type, 'pkcs7-mime') + assert_equal(@zip.sub_type, 'zip') + end + + def test_system_equals + assert_nothing_raised do + @yaml = MIME::Type.from_array('text/x-yaml', %w(yaml yml), '8bit', + 'linux') + end + assert_equal(@yaml.system, %r{linux}) + assert_nothing_raised { @yaml.system = /win32/ } + assert_equal(@yaml.system, %r{win32}) + assert_nothing_raised { @yaml.system = nil } + assert_nil(@yaml.system) + end + + def test_system_eh + assert_nothing_raised do + @yaml = MIME::Type.from_array('text/x-yaml', %w(yaml yml), '8bit', + 'linux') + end + assert(@yaml.system?) + assert_nothing_raised { @yaml.system = nil } + assert(!@yaml.system?) + end + + def test_to_a + assert_nothing_raised do + @yaml = MIME::Type.from_array('text/x-yaml', %w(yaml yml), '8bit', + 'linux') + end + assert_equal(@yaml.to_a, ['text/x-yaml', %w(yaml yml), '8bit', + /linux/, nil, nil, nil, false]) + end + + def test_to_hash + assert_nothing_raised do + @yaml = MIME::Type.from_array('text/x-yaml', %w(yaml yml), '8bit', + 'linux') + end + assert_equal(@yaml.to_hash, + { 'Content-Type' => 'text/x-yaml', + 'Content-Transfer-Encoding' => '8bit', + 'Extensions' => %w(yaml yml), + 'System' => /linux/, + 'Registered' => false, + 'URL' => nil, + 'Obsolete' => nil, + 'Docs' => nil }) + end + + def test_to_s + assert_equal("#{MIME::Type.new('text/plain')}", 'text/plain') + end + + def test_class_constructors + assert_not_nil(@zip) + yaml = MIME::Type.new('text/x-yaml') do |y| + y.extensions = %w(yaml yml) + y.encoding = '8bit' + y.system = 'linux' + end + assert_instance_of(MIME::Type, yaml) + assert_raises(MIME::InvalidContentType) { MIME::Type.new('apps') } + assert_raises(MIME::InvalidContentType) { MIME::Type.new(nil) } + end + + def _test_to_str + raise NotImplementedError, 'Need to write test_to_str' + end + + def _test_url + raise NotImplementedError, 'Need to write test_url' + end + + def _test_url_equals + raise NotImplementedError, 'Need to write test_url_equals' + end + + def _test_urls + raise NotImplementedError, 'Need to write test_urls' + end + + def __test_use_instead + raise NotImplementedError, 'Need to write test_use_instead' + end + end +end diff --git a/vendor/gems/mime-types-1.16/test/test_mime_types.rb b/vendor/gems/mime-types-1.16/test/test_mime_types.rb new file mode 100644 index 0000000..e9d7015 --- /dev/null +++ b/vendor/gems/mime-types-1.16/test/test_mime_types.rb @@ -0,0 +1,122 @@ +#! /usr/bin/env ruby +#-- +# MIME::Types +# A Ruby implementation of a MIME Types information library. Based in spirit +# on the Perl MIME::Types information library by Mark Overmeer. +# http://rubyforge.org/projects/mime-types/ +# +# Licensed under the Ruby disjunctive licence with the GNU GPL or the Perl +# Artistic licence. See Licence.txt for more information. +# +# Copyright 2003 - 2009 Austin Ziegler +#++ +$LOAD_PATH.unshift("#{File.dirname(__FILE__)}/../lib") if __FILE__ == $0 + +require 'mime/types' +require 'test/unit' unless defined? $ZENTEST and $ZENTEST + +module TestMIME + class TestTypes < Test::Unit::TestCase #:nodoc: + def test_class_index_1 + text_plain = MIME::Type.new('text/plain') do |t| + t.encoding = '8bit' + t.extensions = %w(asc txt c cc h hh cpp hpp dat hlp) + end + text_plain_vms = MIME::Type.new('text/plain') do |t| + t.encoding = '8bit' + t.extensions = %w(doc) + t.system = 'vms' + end + + assert_equal(MIME::Types['text/plain'], [text_plain, text_plain_vms]) + end + + def test_class_index_2 + tst_bmp = MIME::Types["image/x-bmp"] + + MIME::Types["image/vnd.wap.wbmp"] + MIME::Types["image/x-win-bmp"] + + assert_equal(tst_bmp.sort, MIME::Types[/bmp$/].sort) + + assert_nothing_raised { + MIME::Types['image/bmp'][0].system = RUBY_PLATFORM + } + + assert_equal([MIME::Type.from_array('image/x-bmp', ['bmp'])], + MIME::Types[/bmp$/, { :platform => true }]) + end + + def test_class_index_3 + assert(MIME::Types['text/vnd.fly', { :complete => true }].empty?) + assert(!MIME::Types['text/plain', { :complete => true} ].empty?) + end + + def _test_class_index_extensions + raise NotImplementedError, 'Need to write test_class_index_extensions' + end + + def _test_class_add + assert_nothing_raised do + @eruby = MIME::Type.new("application/x-eruby") do |t| + t.extensions = "rhtml" + t.encoding = "8bit" + end + + MIME::Types.add(@eruby) + end + + assert_equal(MIME::Types['application/x-eruby'], [@eruby]) + end + + def _test_class_add_type_variant + raise NotImplementedError, 'Need to write test_class_add_type_variant' + end + + def test_class_type_for + assert_equal(MIME::Types.type_for('xml').sort, [ MIME::Types['text/xml'], MIME::Types['application/xml'] ].sort) + assert_equal(MIME::Types.type_for('gif'), MIME::Types['image/gif']) + assert_nothing_raised do + MIME::Types['image/gif'][0].system = RUBY_PLATFORM + end + assert_equal(MIME::Types.type_for('gif', true), MIME::Types['image/gif']) + assert(MIME::Types.type_for('zzz').empty?) + end + + def test_class_of + assert_equal(MIME::Types.of('xml').sort, [ MIME::Types['text/xml'], MIME::Types['application/xml'] ].sort) + assert_equal(MIME::Types.of('gif'), MIME::Types['image/gif']) + assert_nothing_raised do + MIME::Types['image/gif'][0].system = RUBY_PLATFORM + end + assert_equal(MIME::Types.of('gif', true), MIME::Types['image/gif']) + assert(MIME::Types.of('zzz').empty?) + end + + def _test_add + raise NotImplementedError, 'Need to write test_add' + end + + def _test_add_type_variant + raise NotImplementedError, 'Need to write test_add_type_variant' + end + + def _test_data_version + raise NotImplementedError, 'Need to write test_data_version' + end + + def _test_index + raise NotImplementedError, 'Need to write test_index' + end + + def _test_index_extensions + raise NotImplementedError, 'Need to write test_index_extensions' + end + + def _test_of + raise NotImplementedError, 'Need to write test_of' + end + + def _test_type_for + raise NotImplementedError, 'Need to write test_type_for' + end + end +end diff --git a/vendor/gems/xml-simple-1.0.12/.specification b/vendor/gems/xml-simple-1.0.12/.specification new file mode 100644 index 0000000..591efa3 --- /dev/null +++ b/vendor/gems/xml-simple-1.0.12/.specification @@ -0,0 +1,56 @@ +--- !ruby/object:Gem::Specification +name: xml-simple +version: !ruby/object:Gem::Version + version: 1.0.12 +platform: ruby +authors: +- Maik Schmidt +autorequire: +bindir: bin +cert_chain: [] + +date: 2009-02-26 00:00:00 -05:00 +default_executable: +dependencies: [] + +description: +email: contact@maik-schmidt.de +executables: [] + +extensions: [] + +extra_rdoc_files: [] + +files: +- lib/xmlsimple.rb +has_rdoc: true +homepage: http://xml-simple.rubyforge.org +licenses: [] + +post_install_message: +rdoc_options: [] + +require_paths: +- bin +- lib +required_ruby_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: "0" + version: +required_rubygems_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: "0" + version: +requirements: [] + +rubyforge_project: xml-simple +rubygems_version: 1.3.4 +signing_key: +specification_version: 2 +summary: A simple API for XML processing. +test_files: [] + diff --git a/vendor/gems/xml-simple-1.0.12/lib/xmlsimple.rb b/vendor/gems/xml-simple-1.0.12/lib/xmlsimple.rb new file mode 100755 index 0000000..757533d --- /dev/null +++ b/vendor/gems/xml-simple-1.0.12/lib/xmlsimple.rb @@ -0,0 +1,1028 @@ +# = XmlSimple +# +# Author:: Maik Schmidt +# Copyright:: Copyright (c) 2003-2009 Maik Schmidt +# License:: Distributes under the same terms as Ruby. +# +require 'rexml/document' +require 'stringio' + +# Easy API to maintain XML (especially configuration files). +class XmlSimple + include REXML + + @@VERSION = '1.0.12' + + # A simple cache for XML documents that were already transformed + # by xml_in. + class Cache + # Creates and initializes a new Cache object. + def initialize + @mem_share_cache = {} + @mem_copy_cache = {} + end + + # Saves a data structure into a file. + # + # data:: + # Data structure to be saved. + # filename:: + # Name of the file belonging to the data structure. + def save_storable(data, filename) + cache_file = get_cache_filename(filename) + File.open(cache_file, "w+") { |f| Marshal.dump(data, f) } + end + + # Restores a data structure from a file. If restoring the data + # structure failed for any reason, nil will be returned. + # + # filename:: + # Name of the file belonging to the data structure. + def restore_storable(filename) + cache_file = get_cache_filename(filename) + return nil unless File::exist?(cache_file) + return nil unless File::mtime(cache_file).to_i > File::mtime(filename).to_i + data = nil + File.open(cache_file) { |f| data = Marshal.load(f) } + data + end + + # Saves a data structure in a shared memory cache. + # + # data:: + # Data structure to be saved. + # filename:: + # Name of the file belonging to the data structure. + def save_mem_share(data, filename) + @mem_share_cache[filename] = [Time::now.to_i, data] + end + + # Restores a data structure from a shared memory cache. You + # should consider these elements as "read only". If restoring + # the data structure failed for any reason, nil will be + # returned. + # + # filename:: + # Name of the file belonging to the data structure. + def restore_mem_share(filename) + get_from_memory_cache(filename, @mem_share_cache) + end + + # Copies a data structure to a memory cache. + # + # data:: + # Data structure to be copied. + # filename:: + # Name of the file belonging to the data structure. + def save_mem_copy(data, filename) + @mem_share_cache[filename] = [Time::now.to_i, Marshal.dump(data)] + end + + # Restores a data structure from a memory cache. If restoring + # the data structure failed for any reason, nil will be + # returned. + # + # filename:: + # Name of the file belonging to the data structure. + def restore_mem_copy(filename) + data = get_from_memory_cache(filename, @mem_share_cache) + data = Marshal.load(data) unless data.nil? + data + end + + private + + # Returns the "cache filename" belonging to a filename, i.e. + # the extension '.xml' in the original filename will be replaced + # by '.stor'. If filename does not have this extension, '.stor' + # will be appended. + # + # filename:: + # Filename to get "cache filename" for. + def get_cache_filename(filename) + filename.sub(/(\.xml)?$/, '.stor') + end + + # Returns a cache entry from a memory cache belonging to a + # certain filename. If no entry could be found for any reason, + # nil will be returned. + # + # filename:: + # Name of the file the cache entry belongs to. + # cache:: + # Memory cache to get entry from. + def get_from_memory_cache(filename, cache) + return nil unless cache[filename] + return nil unless cache[filename][0] > File::mtime(filename).to_i + return cache[filename][1] + end + end + + # Create a "global" cache. + @@cache = Cache.new + + # Creates and intializes a new XmlSimple object. + # + # defaults:: + # Default values for options. + def initialize(defaults = nil) + unless defaults.nil? || defaults.instance_of?(Hash) + raise ArgumentError, "Options have to be a Hash." + end + @default_options = normalize_option_names(defaults, (KNOWN_OPTIONS['in'] + KNOWN_OPTIONS['out']).uniq) + @options = Hash.new + @_var_values = nil + end + + # Converts an XML document in the same way as the Perl module XML::Simple. + # + # string:: + # XML source. Could be one of the following: + # + # - nil: Tries to load and parse '.xml'. + # - filename: Tries to load and parse filename. + # - IO object: Reads from object until EOF is detected and parses result. + # - XML string: Parses string. + # + # options:: + # Options to be used. + def xml_in(string = nil, options = nil) + handle_options('in', options) + + # If no XML string or filename was supplied look for scriptname.xml. + if string.nil? + string = File::basename($0).dup + string.sub!(/\.[^.]+$/, '') + string += '.xml' + + directory = File::dirname($0) + @options['searchpath'].unshift(directory) unless directory.nil? + end + + if string.instance_of?(String) + if string =~ /<.*?>/m + @doc = parse(string) + elsif string == '-' + @doc = parse($stdin.read) + else + filename = find_xml_file(string, @options['searchpath']) + + if @options.has_key?('cache') + @options['cache'].each { |scheme| + case(scheme) + when 'storable' + content = @@cache.restore_storable(filename) + when 'mem_share' + content = @@cache.restore_mem_share(filename) + when 'mem_copy' + content = @@cache.restore_mem_copy(filename) + else + raise ArgumentError, "Unsupported caching scheme: <#{scheme}>." + end + return content if content + } + end + + @doc = load_xml_file(filename) + end + elsif string.kind_of?(IO) || string.kind_of?(StringIO) || string.kind_of?(Zlib::GzipReader) + @doc = parse(string.read) + else + raise ArgumentError, "Could not parse object of type: <#{string.type}>." + end + + result = collapse(@doc.root) + result = @options['keeproot'] ? merge({}, @doc.root.name, result) : result + put_into_cache(result, filename) + result + end + + # This is the functional version of the instance method xml_in. + def XmlSimple.xml_in(string = nil, options = nil) + xml_simple = XmlSimple.new + xml_simple.xml_in(string, options) + end + + # Converts a data structure into an XML document. + # + # ref:: + # Reference to data structure to be converted into XML. + # options:: + # Options to be used. + def xml_out(ref, options = nil) + handle_options('out', options) + if ref.instance_of?(Array) + ref = { @options['anonymoustag'] => ref } + end + + if @options['keeproot'] + keys = ref.keys + if keys.size == 1 + ref = ref[keys[0]] + @options['rootname'] = keys[0] + end + elsif @options['rootname'] == '' + if ref.instance_of?(Hash) + refsave = ref + ref = {} + refsave.each { |key, value| + if !scalar(value) + ref[key] = value + else + ref[key] = [ value.to_s ] + end + } + end + end + + @ancestors = [] + xml = value_to_xml(ref, @options['rootname'], '') + @ancestors = nil + + if @options['xmldeclaration'] + xml = @options['xmldeclaration'] + "\n" + xml + end + + if @options.has_key?('outputfile') + if @options['outputfile'].kind_of?(IO) + return @options['outputfile'].write(xml) + else + File.open(@options['outputfile'], "w") { |file| file.write(xml) } + end + end + xml + end + + # This is the functional version of the instance method xml_out. + def XmlSimple.xml_out(hash, options = nil) + xml_simple = XmlSimple.new + xml_simple.xml_out(hash, options) + end + + private + + # Declare options that are valid for xml_in and xml_out. + KNOWN_OPTIONS = { + 'in' => %w( + keyattr keeproot forcecontent contentkey noattr + searchpath forcearray suppressempty anonymoustag + cache grouptags normalisespace normalizespace + variables varattr keytosymbol attrprefix + ), + 'out' => %w( + keyattr keeproot contentkey noattr rootname + xmldeclaration outputfile noescape suppressempty + anonymoustag indent grouptags noindent attrprefix + ) + } + + # Define some reasonable defaults. + DEF_KEY_ATTRIBUTES = [] + DEF_ROOT_NAME = 'opt' + DEF_CONTENT_KEY = 'content' + DEF_XML_DECLARATION = "" + DEF_ANONYMOUS_TAG = 'anon' + DEF_FORCE_ARRAY = true + DEF_INDENTATION = ' ' + DEF_KEY_TO_SYMBOL = false + + # Normalizes option names in a hash, i.e., turns all + # characters to lower case and removes all underscores. + # Additionally, this method checks, if an unknown option + # was used and raises an according exception. + # + # options:: + # Hash to be normalized. + # known_options:: + # List of known options. + def normalize_option_names(options, known_options) + return nil if options.nil? + result = Hash.new + options.each { |key, value| + lkey = key.downcase + lkey.gsub!(/_/, '') + if !known_options.member?(lkey) + raise ArgumentError, "Unrecognised option: #{lkey}." + end + result[lkey] = value + } + result + end + + # Merges a set of options with the default options. + # + # direction:: + # 'in': If options should be handled for xml_in. + # 'out': If options should be handled for xml_out. + # options:: + # Options to be merged with the default options. + def handle_options(direction, options) + @options = options || Hash.new + + raise ArgumentError, "Options must be a Hash!" unless @options.instance_of?(Hash) + + unless KNOWN_OPTIONS.has_key?(direction) + raise ArgumentError, "Unknown direction: <#{direction}>." + end + + known_options = KNOWN_OPTIONS[direction] + @options = normalize_option_names(@options, known_options) + + unless @default_options.nil? + known_options.each { |option| + unless @options.has_key?(option) + if @default_options.has_key?(option) + @options[option] = @default_options[option] + end + end + } + end + + unless @options.has_key?('noattr') + @options['noattr'] = false + end + + if @options.has_key?('rootname') + @options['rootname'] = '' if @options['rootname'].nil? + else + @options['rootname'] = DEF_ROOT_NAME + end + + if @options.has_key?('xmldeclaration') && @options['xmldeclaration'] == true + @options['xmldeclaration'] = DEF_XML_DECLARATION + end + + @options['keytosymbol'] = DEF_KEY_TO_SYMBOL unless @options.has_key?('keytosymbol') + + if @options.has_key?('contentkey') + if @options['contentkey'] =~ /^-(.*)$/ + @options['contentkey'] = $1 + @options['collapseagain'] = true + end + else + @options['contentkey'] = DEF_CONTENT_KEY + end + + unless @options.has_key?('normalisespace') + @options['normalisespace'] = @options['normalizespace'] + end + @options['normalisespace'] = 0 if @options['normalisespace'].nil? + + if @options.has_key?('searchpath') + unless @options['searchpath'].instance_of?(Array) + @options['searchpath'] = [ @options['searchpath'] ] + end + else + @options['searchpath'] = [] + end + + if @options.has_key?('cache') && scalar(@options['cache']) + @options['cache'] = [ @options['cache'] ] + end + + @options['anonymoustag'] = DEF_ANONYMOUS_TAG unless @options.has_key?('anonymoustag') + + if !@options.has_key?('indent') || @options['indent'].nil? + @options['indent'] = DEF_INDENTATION + end + + @options['indent'] = '' if @options.has_key?('noindent') + + # Special cleanup for 'keyattr' which could be an array or + # a hash or left to default to array. + if @options.has_key?('keyattr') + if !scalar(@options['keyattr']) + # Convert keyattr => { elem => '+attr' } + # to keyattr => { elem => ['attr', '+'] } + if @options['keyattr'].instance_of?(Hash) + @options['keyattr'].each { |key, value| + if value =~ /^([-+])?(.*)$/ + @options['keyattr'][key] = [$2, $1 ? $1 : ''] + end + } + elsif !@options['keyattr'].instance_of?(Array) + raise ArgumentError, "'keyattr' must be String, Hash, or Array!" + end + else + @options['keyattr'] = [ @options['keyattr'] ] + end + else + @options['keyattr'] = DEF_KEY_ATTRIBUTES + end + + if @options.has_key?('forcearray') + if @options['forcearray'].instance_of?(Regexp) + @options['forcearray'] = [ @options['forcearray'] ] + end + + if @options['forcearray'].instance_of?(Array) + force_list = @options['forcearray'] + unless force_list.empty? + @options['forcearray'] = {} + force_list.each { |tag| + if tag.instance_of?(Regexp) + unless @options['forcearray']['_regex'].instance_of?(Array) + @options['forcearray']['_regex'] = [] + end + @options['forcearray']['_regex'] << tag + else + @options['forcearray'][tag] = true + end + } + else + @options['forcearray'] = false + end + else + @options['forcearray'] = @options['forcearray'] ? true : false + end + else + @options['forcearray'] = DEF_FORCE_ARRAY + end + + if @options.has_key?('grouptags') && !@options['grouptags'].instance_of?(Hash) + raise ArgumentError, "Illegal value for 'GroupTags' option - expected a Hash." + end + + if @options.has_key?('variables') && !@options['variables'].instance_of?(Hash) + raise ArgumentError, "Illegal value for 'Variables' option - expected a Hash." + end + + if @options.has_key?('variables') + @_var_values = @options['variables'] + elsif @options.has_key?('varattr') + @_var_values = {} + end + end + + # Actually converts an XML document element into a data structure. + # + # element:: + # The document element to be collapsed. + def collapse(element) + result = @options['noattr'] ? {} : get_attributes(element) + + if @options['normalisespace'] == 2 + result.each { |k, v| result[k] = normalise_space(v) } + end + + if element.has_elements? + element.each_element { |child| + value = collapse(child) + if empty(value) && (element.attributes.empty? || @options['noattr']) + next if @options.has_key?('suppressempty') && @options['suppressempty'] == true + end + result = merge(result, child.name, value) + } + if has_mixed_content?(element) + # normalisespace? + content = element.texts.map { |x| x.to_s } + content = content[0] if content.size == 1 + result[@options['contentkey']] = content + end + elsif element.has_text? # i.e. it has only text. + return collapse_text_node(result, element) + end + + # Turn Arrays into Hashes if key fields present. + count = fold_arrays(result) + + # Disintermediate grouped tags. + if @options.has_key?('grouptags') + result.each { |key, value| + next unless (value.instance_of?(Hash) && (value.size == 1)) + child_key, child_value = value.to_a[0] + if @options['grouptags'][key] == child_key + result[key] = child_value + end + } + end + + # Fold Hashes containing a single anonymous Array up into just the Array. + if count == 1 + anonymoustag = @options['anonymoustag'] + if result.has_key?(anonymoustag) && result[anonymoustag].instance_of?(Array) + return result[anonymoustag] + end + end + + if result.empty? && @options.has_key?('suppressempty') + return @options['suppressempty'] == '' ? '' : nil + end + + result + end + + # Collapses a text node and merges it with an existing Hash, if + # possible. + # Thanks to Curtis Schofield for reporting a subtle bug. + # + # hash:: + # Hash to merge text node value with, if possible. + # element:: + # Text node to be collapsed. + def collapse_text_node(hash, element) + value = node_to_text(element) + if empty(value) && !element.has_attributes? + return {} + end + + if element.has_attributes? && !@options['noattr'] + return merge(hash, @options['contentkey'], value) + else + if @options['forcecontent'] + return merge(hash, @options['contentkey'], value) + else + return value + end + end + end + + # Folds all arrays in a Hash. + # + # hash:: + # Hash to be folded. + def fold_arrays(hash) + fold_amount = 0 + keyattr = @options['keyattr'] + if (keyattr.instance_of?(Array) || keyattr.instance_of?(Hash)) + hash.each { |key, value| + if value.instance_of?(Array) + if keyattr.instance_of?(Array) + hash[key] = fold_array(value) + else + hash[key] = fold_array_by_name(key, value) + end + fold_amount += 1 + end + } + end + fold_amount + end + + # Folds an Array to a Hash, if possible. Folding happens + # according to the content of keyattr, which has to be + # an array. + # + # array:: + # Array to be folded. + def fold_array(array) + hash = Hash.new + array.each { |x| + return array unless x.instance_of?(Hash) + key_matched = false + @options['keyattr'].each { |key| + if x.has_key?(key) + key_matched = true + value = x[key] + return array if value.instance_of?(Hash) || value.instance_of?(Array) + value = normalise_space(value) if @options['normalisespace'] == 1 + x.delete(key) + hash[value] = x + break + end + } + return array unless key_matched + } + hash = collapse_content(hash) if @options['collapseagain'] + hash + end + + # Folds an Array to a Hash, if possible. Folding happens + # according to the content of keyattr, which has to be + # a Hash. + # + # name:: + # Name of the attribute to be folded upon. + # array:: + # Array to be folded. + def fold_array_by_name(name, array) + return array unless @options['keyattr'].has_key?(name) + key, flag = @options['keyattr'][name] + + hash = Hash.new + array.each { |x| + if x.instance_of?(Hash) && x.has_key?(key) + value = x[key] + return array if value.instance_of?(Hash) || value.instance_of?(Array) + value = normalise_space(value) if @options['normalisespace'] == 1 + hash[value] = x + hash[value]["-#{key}"] = hash[value][key] if flag == '-' + hash[value].delete(key) unless flag == '+' + else + $stderr.puts("Warning: <#{name}> element has no '#{key}' attribute.") + return array + end + } + hash = collapse_content(hash) if @options['collapseagain'] + hash + end + + # Tries to collapse a Hash even more ;-) + # + # hash:: + # Hash to be collapsed again. + def collapse_content(hash) + content_key = @options['contentkey'] + hash.each_value { |value| + return hash unless value.instance_of?(Hash) && value.size == 1 && value.has_key?(content_key) + hash.each_key { |key| hash[key] = hash[key][content_key] } + } + hash + end + + # Adds a new key/value pair to an existing Hash. If the key to be added + # does already exist and the existing value associated with key is not + # an Array, it will be converted into an Array. Then the new value is + # appended to that Array. + # + # hash:: + # Hash to add key/value pair to. + # key:: + # Key to be added. + # value:: + # Value to be associated with key. + def merge(hash, key, value) + if value.instance_of?(String) + value = normalise_space(value) if @options['normalisespace'] == 2 + + # do variable substitutions + unless @_var_values.nil? || @_var_values.empty? + value.gsub!(/\$\{(\w+)\}/) { |x| get_var($1) } + end + + # look for variable definitions + if @options.has_key?('varattr') + varattr = @options['varattr'] + if hash.has_key?(varattr) + set_var(hash[varattr], value) + end + end + end + + #patch for converting keys to symbols + if @options.has_key?('keytosymbol') + if @options['keytosymbol'] == true + key = key.to_s.downcase.to_sym + end + end + + if hash.has_key?(key) + if hash[key].instance_of?(Array) + hash[key] << value + else + hash[key] = [ hash[key], value ] + end + elsif value.instance_of?(Array) # Handle anonymous arrays. + hash[key] = [ value ] + else + if force_array?(key) + hash[key] = [ value ] + else + hash[key] = value + end + end + hash + end + + # Checks, if the 'forcearray' option has to be used for + # a certain key. + def force_array?(key) + return false if key == @options['contentkey'] + return true if @options['forcearray'] == true + forcearray = @options['forcearray'] + if forcearray.instance_of?(Hash) + return true if forcearray.has_key?(key) + return false unless forcearray.has_key?('_regex') + forcearray['_regex'].each { |x| return true if key =~ x } + end + return false + end + + # Converts the attributes array of a document node into a Hash. + # Returns an empty Hash, if node has no attributes. + # + # node:: + # Document node to extract attributes from. + def get_attributes(node) + attributes = {} + if @options['attrprefix'] + node.attributes.each { |n,v| attributes["@" + n] = v } + else + node.attributes.each { |n,v| attributes[n] = v } + end + attributes + end + + # Determines, if a document element has mixed content. + # + # element:: + # Document element to be checked. + def has_mixed_content?(element) + if element.has_text? && element.has_elements? + return true if element.texts.join('') !~ /^\s*$/s + end + false + end + + # Called when a variable definition is encountered in the XML. + # A variable definition looks like + # value + # where attrname matches the varattr setting. + def set_var(name, value) + @_var_values[name] = value + end + + # Called during variable substitution to get the value for the + # named variable. + def get_var(name) + if @_var_values.has_key?(name) + return @_var_values[name] + else + return "${#{name}}" + end + end + + # Recurses through a data structure building up and returning an + # XML representation of that structure as a string. + # + # ref:: + # Reference to the data structure to be encoded. + # name:: + # The XML tag name to be used for this item. + # indent:: + # A string of spaces for use as the current indent level. + def value_to_xml(ref, name, indent) + named = !name.nil? && name != '' + nl = @options.has_key?('noindent') ? '' : "\n" + + if !scalar(ref) + if @ancestors.member?(ref) + raise ArgumentError, "Circular data structures not supported!" + end + @ancestors << ref + else + if named + return [indent, '<', name, '>', @options['noescape'] ? ref.to_s : escape_value(ref.to_s), '', nl].join('') + else + return ref.to_s + nl + end + end + + # Unfold hash to array if possible. + if ref.instance_of?(Hash) && !ref.empty? && !@options['keyattr'].empty? && indent != '' + ref = hash_to_array(name, ref) + end + + result = [] + if ref.instance_of?(Hash) + # Reintermediate grouped values if applicable. + if @options.has_key?('grouptags') + ref.each { |key, value| + if @options['grouptags'].has_key?(key) + ref[key] = { @options['grouptags'][key] => value } + end + } + end + + nested = [] + text_content = nil + if named + result << indent << '<' << name + end + + if !ref.empty? + ref.each { |key, value| + next if !key.nil? && key[0, 1] == '-' + if value.nil? + unless @options.has_key?('suppressempty') && @options['suppressempty'].nil? + raise ArgumentError, "Use of uninitialized value!" + end + value = {} + end + + # Check for the '@' attribute prefix to allow separation of attributes and elements + if @options['noattr'] || + (@options['attrprefix'] && !(key =~ /^@(.*)/)) || + !scalar(value) + nested << value_to_xml(value, key, indent + @options['indent']) + else + value = value.to_s + value = escape_value(value) unless @options['noescape'] + if key == @options['contentkey'] + text_content = value + else + result << ' ' << ($1||key) << '="' << value << '"' + end + end + } + else + text_content = '' + end + + if !nested.empty? || !text_content.nil? + if named + result << '>' + if !text_content.nil? + result << text_content + nested[0].sub!(/^\s+/, '') if !nested.empty? + else + result << nl + end + if !nested.empty? + result << nested << indent + end + result << '' << nl + else + result << nested + end + else + result << ' />' << nl + end + elsif ref.instance_of?(Array) + ref.each { |value| + if scalar(value) + result << indent << '<' << name << '>' + result << (@options['noescape'] ? value.to_s : escape_value(value.to_s)) + result << '' << nl + elsif value.instance_of?(Hash) + result << value_to_xml(value, name, indent) + else + result << indent << '<' << name << '>' << nl + result << value_to_xml(value, @options['anonymoustag'], indent + @options['indent']) + result << indent << '' << nl + end + } + else + # Probably, this is obsolete. + raise ArgumentError, "Can't encode a value of type: #{ref.type}." + end + @ancestors.pop if !scalar(ref) + result.join('') + end + + # Checks, if a certain value is a "scalar" value. Whatever + # that will be in Ruby ... ;-) + # + # value:: + # Value to be checked. + def scalar(value) + return false if value.instance_of?(Hash) || value.instance_of?(Array) + return true + end + + # Attempts to unfold a hash of hashes into an array of hashes. Returns + # a reference to th array on success or the original hash, if unfolding + # is not possible. + # + # parent:: + # + # hashref:: + # Reference to the hash to be unfolded. + def hash_to_array(parent, hashref) + arrayref = [] + hashref.each { |key, value| + return hashref unless value.instance_of?(Hash) + + if @options['keyattr'].instance_of?(Hash) + return hashref unless @options['keyattr'].has_key?(parent) + arrayref << { @options['keyattr'][parent][0] => key }.update(value) + else + arrayref << { @options['keyattr'][0] => key }.update(value) + end + } + arrayref + end + + # Replaces XML markup characters by their external entities. + # + # data:: + # The string to be escaped. + def escape_value(data) + Text::normalize(data) + end + + # Removes leading and trailing whitespace and sequences of + # whitespaces from a string. + # + # text:: + # String to be normalised. + def normalise_space(text) + text.strip.gsub(/\s\s+/, ' ') + end + + # Checks, if an object is nil, an empty String or an empty Hash. + # Thanks to Norbert Gawor for a bugfix. + # + # value:: + # Value to be checked for emptyness. + def empty(value) + case value + when Hash + return value.empty? + when String + return value !~ /\S/m + else + return value.nil? + end + end + + # Converts a document node into a String. + # If the node could not be converted into a String + # for any reason, default will be returned. + # + # node:: + # Document node to be converted. + # default:: + # Value to be returned, if node could not be converted. + def node_to_text(node, default = nil) + if node.instance_of?(REXML::Element) + node.texts.map { |t| t.value }.join('') + elsif node.instance_of?(REXML::Attribute) + node.value.nil? ? default : node.value.strip + elsif node.instance_of?(REXML::Text) + node.value.strip + else + default + end + end + + # Parses an XML string and returns the according document. + # + # xml_string:: + # XML string to be parsed. + # + # The following exception may be raised: + # + # REXML::ParseException:: + # If the specified file is not wellformed. + def parse(xml_string) + Document.new(xml_string) + end + + # Searches in a list of paths for a certain file. Returns + # the full path to the file, if it could be found. Otherwise, + # an exception will be raised. + # + # filename:: + # Name of the file to search for. + # searchpath:: + # List of paths to search in. + def find_xml_file(file, searchpath) + filename = File::basename(file) + + if filename != file + return file if File::file?(file) + else + searchpath.each { |path| + full_path = File::join(path, filename) + return full_path if File::file?(full_path) + } + end + + if searchpath.empty? + return file if File::file?(file) + raise ArgumentError, "File does not exist: #{file}." + end + raise ArgumentError, "Could not find <#{filename}> in <#{searchpath.join(':')}>" + end + + # Loads and parses an XML configuration file. + # + # filename:: + # Name of the configuration file to be loaded. + # + # The following exceptions may be raised: + # + # Errno::ENOENT:: + # If the specified file does not exist. + # REXML::ParseException:: + # If the specified file is not wellformed. + def load_xml_file(filename) + parse(IO::read(filename)) + end + + # Caches the data belonging to a certain file. + # + # data:: + # Data to be cached. + # filename:: + # Name of file the data was read from. + def put_into_cache(data, filename) + if @options.has_key?('cache') + @options['cache'].each { |scheme| + case(scheme) + when 'storable' + @@cache.save_storable(data, filename) + when 'mem_share' + @@cache.save_mem_share(data, filename) + when 'mem_copy' + @@cache.save_mem_copy(data, filename) + else + raise ArgumentError, "Unsupported caching scheme: <#{scheme}>." + end + } + end + end +end + +# vim:sw=2 -- libgit2 0.21.2