pax_global_header00006660000000000000000000000064121673332000014507gustar00rootroot0000000000000052 comment=42b32353e30d2fff8fa8b1975993ba1052619b26 ruby-rack-accept-0.4.5/000077500000000000000000000000001216733320000146515ustar00rootroot00000000000000ruby-rack-accept-0.4.5/CHANGES000066400000000000000000000013041216733320000156420ustar00rootroot00000000000000## 0.4.3 / July 29, 2010 * Added support for Ruby 1.9.2 ## 0.4 / April 5, 2010 * Added support for media type queries with multiple range parameters * Changed Rack::Accept::Header#sort method to return only single values and moved previous functionality to Rack::Accept::Header#sort_with_qvalues ## 0.3 / April 3, 2010 * Enhanced Rack middleware component to be able to automatically send a 406 response when the request is not acceptable ## 0.2 / April 2, 2010 * Moved all common header methods into Rack::Accept::Header module * Many improvements to the documentation ## 0.1.1 / April 1, 2010 * Whoops, forgot to require Rack. :] ## 0.1 / April 1, 2010 * Initial release ruby-rack-accept-0.4.5/README.md000066400000000000000000000124611216733320000161340ustar00rootroot00000000000000# Rack::Accept **Rack::Accept** is a suite of tools for Ruby/Rack applications that eases the complexity of building and interpreting the Accept* family of [HTTP request headers][rfc]. Some features of the library are: * Strict adherence to [RFC 2616][rfc], specifically [section 14][rfc-sec14] * Full support for the [Accept][rfc-sec14-1], [Accept-Charset][rfc-sec14-2], [Accept-Encoding][rfc-sec14-3], and [Accept-Language][rfc-sec14-4] HTTP request headers * May be used as [Rack][rack] middleware or standalone * A comprehensive [test suite][test] that covers many edge cases [rfc]: http://www.w3.org/Protocols/rfc2616/rfc2616.html [rfc-sec14]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html [rfc-sec14-1]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1 [rfc-sec14-2]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.2 [rfc-sec14-3]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3 [rfc-sec14-4]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4 [rack]: http://rack.rubyforge.org/ [test]: http://github.com/mjijackson/rack-accept/tree/master/test/ ## Installation **Using [RubyGems](http://rubygems.org/):** $ sudo gem install rack-accept **From a local copy:** $ git clone git://github.com/mjijackson/rack-accept.git $ cd rack-accept $ rake package && sudo rake install ## Usage **Rack::Accept** implements the Rack middleware interface and may be used with any Rack-based application. Simply insert the `Rack::Accept` module in your Rack middleware pipeline and access the `Rack::Accept::Request` object in the `rack-accept.request` environment key, as in the following example. ```ruby require 'rack/accept' use Rack::Accept app = lambda do |env| accept = env['rack-accept.request'] response = Rack::Response.new if accept.media_type?('text/html') response['Content-Type'] = 'text/html' response.write "

Hello. You accept text/html!

" else response['Content-Type'] = 'text/plain' response.write "Apparently you don't accept text/html. Too bad." end response.finish end run app ``` **Rack::Accept** can also construct automatic [406][406] responses if you set up the types of media, character sets, encoding, or languages your server is able to serve ahead of time. If you pass a configuration block to your `use` statement it will yield the `Rack::Accept::Context` object that is used for that invocation. [406]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.7 ```ruby require 'rack/accept' use(Rack::Accept) do |context| # We only ever serve content in English or Japanese from this site, so if # the user doesn't accept either of these we will respond with a 406. context.languages = %w< en jp > end app = ... run app ``` **Note:** _You should think carefully before using Rack::Accept in this way. Many user agents are careless about the types of Accept headers they send, and depend on apps not being too picky. Instead of automatically sending a 406, you should probably only send one when absolutely necessary._ Additionally, **Rack::Accept** may be used outside of a Rack context to provide any Ruby app the ability to construct and interpret Accept headers. ```ruby require 'rack/accept' mtype = Rack::Accept::MediaType.new mtype.qvalues = { 'text/html' => 1, 'text/*' => 0.8, '*/*' => 0.5 } mtype.to_s # => "Accept: text/html, text/*;q=0.8, */*;q=0.5" cset = Rack::Accept::Charset.new('unicode-1-1, iso-8859-5;q=0.8') cset.best_of(%w< iso-8859-5 unicode-1-1 >) # => "unicode-1-1" cset.accept?('iso-8859-1') # => true ``` The very last line in this example may look like a mistake to someone not familiar with the intricacies of [the spec][rfc-sec14-3], but it's actually correct. It just puts emphasis on the convenience of using this library so you don't have to worry about these kinds of details. ## Four-letter Words - Spec: [http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html][rfc-sec14] - Code: [http://github.com/mjijackson/rack-accept][code] - Bugs: [http://github.com/mjijackson/rack-accept/issues][bugs] - Docs: [http://mjijackson.github.com/rack-accept][docs] [code]: http://github.com/mjijackson/rack-accept [bugs]: http://github.com/mjijackson/rack-accept/issues [docs]: http://mjijackson.github.com/rack-accept ## License Copyright 2012 Michael Jackson Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. The software is provided "as is", without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and noninfringement. In no event shall the authors or copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with the software or the use or other dealings in the software. ruby-rack-accept-0.4.5/Rakefile000066400000000000000000000031771216733320000163260ustar00rootroot00000000000000require 'rake/clean' require 'rake/testtask' task :default => :test # TESTS ####################################################################### Rake::TestTask.new(:test) do |t| t.test_files = FileList['test/*_test.rb'] end # DOCS ######################################################################## desc "Generate API documentation" task :api => FileList['lib/**/*.rb'] do |t| output_dir = ENV['OUTPUT_DIR'] || 'api' rm_rf output_dir sh((<<-SH).gsub(/[\s\n]+/, ' ').strip) hanna --op #{output_dir} --promiscuous --charset utf8 --fmt html --inline-source --line-numbers --accessor option_accessor=RW --main Rack::Accept --title 'Rack::Accept API Documentation' #{t.prerequisites.join(' ')} SH end CLEAN.include 'api' # PACKAGING & INSTALLATION #################################################### if defined?(Gem) $spec = eval("#{File.read('rack-accept.gemspec')}") directory 'dist' def package(ext='') "dist/#{$spec.name}-#{$spec.version}" + ext end file package('.gem') => %w< dist > + $spec.files do |f| sh "gem build rack-accept.gemspec" mv File.basename(f.name), f.name end file package('.tar.gz') => %w< dist > + $spec.files do |f| sh "git archive --format=tar HEAD | gzip > #{f.name}" end desc "Build packages" task :package => %w< .gem .tar.gz >.map {|e| package(e) } desc "Build and install as local gem" task :install => package('.gem') do |t| sh "gem install #{package('.gem')}" end desc "Upload gem to rubygems.org" task :release => package('.gem') do |t| sh "gem push #{package('.gem')}" end end CLOBBER.include 'dist' ruby-rack-accept-0.4.5/lib/000077500000000000000000000000001216733320000154175ustar00rootroot00000000000000ruby-rack-accept-0.4.5/lib/rack/000077500000000000000000000000001216733320000163375ustar00rootroot00000000000000ruby-rack-accept-0.4.5/lib/rack/accept.rb000066400000000000000000000011331216733320000201210ustar00rootroot00000000000000require 'rack' # HTTP Accept* for Ruby/Rack. # # http://mjijackson.com/rack-accept module Rack::Accept # Enables Rack::Accept to be used as a Rack middleware. def self.new(app, &block) Context.new(app, &block) end autoload :Charset, 'rack/accept/charset' autoload :Context, 'rack/accept/context' autoload :Encoding, 'rack/accept/encoding' autoload :Header, 'rack/accept/header' autoload :Language, 'rack/accept/language' autoload :MediaType, 'rack/accept/media_type' autoload :Request, 'rack/accept/request' autoload :Response, 'rack/accept/response' end ruby-rack-accept-0.4.5/lib/rack/accept/000077500000000000000000000000001216733320000175765ustar00rootroot00000000000000ruby-rack-accept-0.4.5/lib/rack/accept/charset.rb000066400000000000000000000017361216733320000215630ustar00rootroot00000000000000module Rack::Accept # Represents an HTTP Accept-Charset header according to the HTTP 1.1 # specification, and provides several convenience methods for determining # acceptable character sets. # # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.2 class Charset include Header # The name of this header. def name 'Accept-Charset' end # Determines the quality factor (qvalue) of the given +charset+. def qvalue(charset) m = matches(charset) if m.empty? charset == 'iso-8859-1' ? 1 : 0 else normalize_qvalue(@qvalues[m.first]) end end # Returns an array of character sets from this header that match the given # +charset+, ordered by precedence. def matches(charset) values.select {|v| v == charset || v == '*' }.sort {|a, b| # "*" gets least precedence, any others should be equal. a == '*' ? 1 : (b == '*' ? -1 : 0) } end end end ruby-rack-accept-0.4.5/lib/rack/accept/context.rb000066400000000000000000000035421216733320000216130ustar00rootroot00000000000000module Rack::Accept # Implements the Rack middleware interface. class Context # This error is raised when the server is not able to provide an acceptable # response. class AcceptError < StandardError; end attr_reader :app def initialize(app) @app = app @checks = {} @check_headers = [] yield self if block_given? end # Inserts a new Rack::Accept::Request object into the environment before # handing the request to the app immediately downstream. def call(env) request = env['rack-accept.request'] ||= Request.new(env) check!(request) unless @checks.empty? @app.call(env) rescue AcceptError response = Response.new response.not_acceptable! response.finish end # Defines the types of media this server is able to serve. def media_types=(media_types) add_check(:media_type, media_types) end # Defines the character sets this server is able to serve. def charsets=(charsets) add_check(:charset, charsets) end # Defines the types of encodings this server is able to serve. def encodings=(encodings) add_check(:encoding, encodings) end # Defines the languages this server is able to serve. def languages=(languages) add_check(:language, languages) end private def add_check(header_name, values) @checks[header_name] ||= [] @checks[header_name].concat(values) @check_headers << header_name unless @check_headers.include?(header_name) end # Raises an AcceptError if this server is not able to serve an acceptable # response. def check!(request) @check_headers.each do |header_name| values = @checks[header_name] header = request.send(header_name) raise AcceptError unless values.any? {|v| header.accept?(v) } end end end end ruby-rack-accept-0.4.5/lib/rack/accept/encoding.rb000066400000000000000000000017441216733320000217170ustar00rootroot00000000000000module Rack::Accept # Represents an HTTP Accept-Encoding header according to the HTTP 1.1 # specification, and provides several convenience methods for determining # acceptable content encodings. # # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3 class Encoding include Header # The name of this header. def name 'Accept-Encoding' end # Determines the quality factor (qvalue) of the given +encoding+. def qvalue(encoding) m = matches(encoding) if m.empty? encoding == 'identity' ? 1 : 0 else normalize_qvalue(@qvalues[m.first]) end end # Returns an array of encodings from this header that match the given # +encoding+, ordered by precedence. def matches(encoding) values.select {|v| v == encoding || v == '*' }.sort {|a, b| # "*" gets least precedence, any others should be equal. a == '*' ? 1 : (b == '*' ? -1 : 0) } end end end ruby-rack-accept-0.4.5/lib/rack/accept/header.rb000066400000000000000000000121011216733320000213460ustar00rootroot00000000000000module Rack::Accept # Contains methods that are useful for working with Accept-style HTTP # headers. The MediaType, Charset, Encoding, and Language classes all mixin # this module. module Header # Parses the value of an Accept-style request header into a hash of # acceptable values and their respective quality factors (qvalues). The # +join+ method may be used on the resulting hash to obtain a header # string that is the semantic equivalent of the one provided. def parse(header) qvalues = {} header.to_s.split(/,\s*/).each do |part| m = /^([^\s,]+?)(?:\s*;\s*q\s*=\s*(\d+(?:\.\d+)?))?$/.match(part) if m qvalues[m[1].downcase] = normalize_qvalue((m[2] || 1).to_f) else raise "Invalid header value: #{part.inspect}" end end qvalues end module_function :parse # Returns a string suitable for use as the value of an Accept-style HTTP # header from the map of acceptable values to their respective quality # factors (qvalues). The +parse+ method may be used on the resulting string # to obtain a hash that is the equivalent of the one provided. def join(qvalues) qvalues.map {|k, v| k + (v == 1 ? '' : ";q=#{v}") }.join(', ') end module_function :join # Parses a media type string into its relevant pieces. The return value # will be an array with three values: 1) the content type, 2) the content # subtype, and 3) the media type parameters. An empty array is returned if # no match can be made. def parse_media_type(media_type) m = media_type.to_s.match(/^([a-z*]+)\/([a-z0-9*\-.+]+)(?:;([a-z0-9=;]+))?$/) m ? [m[1], m[2], m[3] || ''] : [] end module_function :parse_media_type # Parses a string of media type range parameters into a hash of parameters # to their respective values. def parse_range_params(params) params.split(';').inject({}) do |m, p| k, v = p.split('=', 2) m[k] = v if v m end end module_function :parse_range_params # Converts 1.0 and 0.0 qvalues to 1 and 0 respectively. Used to maintain # consistency across qvalue methods. def normalize_qvalue(q) (q == 1 || q == 0) && q.is_a?(Float) ? q.to_i : q end module_function :normalize_qvalue module PublicInstanceMethods # A table of all values of this header to their respective quality # factors (qvalues). attr_accessor :qvalues def initialize(header='') @qvalues = parse(header) end # The name of this header. Should be overridden in classes that mixin # this module. def name '' end # Returns the quality factor (qvalue) of the given +value+. Should be # overridden in classes that mixin this module. def qvalue(value) 1 end # Returns the value of this header as a string. def value join(@qvalues) end # Returns an array of all values of this header, in no particular order. def values @qvalues.keys end # Determines if the given +value+ is acceptable (does not have a qvalue # of 0) according to this header. def accept?(value) qvalue(value) != 0 end # Returns a copy of the given +values+ array, sorted by quality factor # (qvalue). Each element of the returned array is itself an array # containing two objects: 1) the value's qvalue and 2) the original # value. # # It is important to note that this sort is a "stable sort". In other # words, the order of the original values is preserved so long as the # qvalue for each is the same. This expectation can be useful when # trying to determine which of a variety of options has the highest # qvalue. If the user prefers using one option over another (for any # number of reasons), he should put it first in +values+. He may then # use the first result with confidence that it is both most acceptable # to the client and most convenient for him as well. def sort_with_qvalues(values, keep_unacceptables=true) qvalues = {} values.each do |v| q = qvalue(v) if q != 0 || keep_unacceptables qvalues[q] ||= [] qvalues[q] << v end end order = qvalues.keys.sort.reverse order.inject([]) {|m, q| m.concat(qvalues[q].map {|v| [q, v] }) } end # Sorts the given +values+ according to the qvalue of each while # preserving the original order. See #sort_with_qvalues for more # information on exactly how the sort is performed. def sort(values, keep_unacceptables=false) sort_with_qvalues(values, keep_unacceptables).map {|q, v| v } end # A shortcut for retrieving the first result of #sort. def best_of(values, keep_unacceptables=false) sort(values, keep_unacceptables).first end # Returns a string representation of this header. def to_s [name, value].join(': ') end end include PublicInstanceMethods end end ruby-rack-accept-0.4.5/lib/rack/accept/language.rb000066400000000000000000000022051216733320000217050ustar00rootroot00000000000000module Rack::Accept # Represents an HTTP Accept-Language header according to the HTTP 1.1 # specification, and provides several convenience methods for determining # acceptable content languages. # # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4 class Language include Header attr_writer :first_level_match # The name of this header. def name 'Accept-Language' end # Determines the quality factor (qvalue) of the given +language+. def qvalue(language) return 1 if @qvalues.empty? m = matches(language) return 0 if m.empty? normalize_qvalue(@qvalues[m.first]) end # Returns an array of languages from this header that match the given # +language+, ordered by precedence. def matches(language) values.select {|v| v = v.match(/^(.+?)-/) ? $1 : v if @first_level_match v == language || v == '*' || (language.match(/^(.+?)-/) && v == $1) }.sort {|a, b| # "*" gets least precedence, any others are compared based on length. a == '*' ? -1 : (b == '*' ? 1 : a.length <=> b.length) }.reverse end end end ruby-rack-accept-0.4.5/lib/rack/accept/media_type.rb000066400000000000000000000033331216733320000222450ustar00rootroot00000000000000module Rack::Accept # Represents an HTTP Accept header according to the HTTP 1.1 specification, # and provides several convenience methods for determining acceptable media # types. # # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1 class MediaType include Header # The name of this header. def name 'Accept' end # Determines the quality factor (qvalue) of the given +media_type+. def qvalue(media_type) return 1 if @qvalues.empty? m = matches(media_type) return 0 if m.empty? normalize_qvalue(@qvalues[m.first]) end # Returns an array of media types from this header that match the given # +media_type+, ordered by precedence. def matches(media_type) type, subtype, params = parse_media_type(media_type) values.select {|v| if v == media_type || v == '*/*' true else t, s, p = parse_media_type(v) t == type && (s == '*' || s == subtype) && (p == '' || params_match?(params, p)) end }.sort_by {|v| # Most specific gets precedence. v.length }.reverse end private def initialize(header) # Strip accept-extension for now. We may want to do something with this # later if people actually start to use it. header = header.to_s.split(/,\s*/).map {|part| part.sub(/(;\s*q\s*=\s*[\d.]+).*$/, '\1') }.join(', ') super(header) end # Returns true if all parameters and values in +match+ are also present in # +params+. def params_match?(params, match) return true if params == match parsed = parse_range_params(params) parsed == parsed.merge(parse_range_params(match)) end end end ruby-rack-accept-0.4.5/lib/rack/accept/request.rb000066400000000000000000000060441216733320000216170ustar00rootroot00000000000000require 'rack/request' module Rack::Accept # A container class for convenience methods when Rack::Accept is used on the # request level as Rack middleware. Instances of this class also manage a # lightweight cache of various header instances to speed up execution. class Request < Rack::Request attr_reader :env def initialize(env) @env = env end # Provides access to the Rack::Accept::MediaType instance for this request. def media_type @media_type ||= MediaType.new(env['HTTP_ACCEPT']) end # Provides access to the Rack::Accept::Charset instance for this request. def charset @charset ||= Charset.new(env['HTTP_ACCEPT_CHARSET']) end # Provides access to the Rack::Accept::Encoding instance for this request. def encoding @encoding ||= Encoding.new(env['HTTP_ACCEPT_ENCODING']) end # Provides access to the Rack::Accept::Language instance for this request. def language @language ||= Language.new(env['HTTP_ACCEPT_LANGUAGE']) end # Returns true if the +Accept+ request header indicates the given media # type is acceptable, false otherwise. def media_type?(value) media_type.accept?(value) end # Returns true if the +Accept-Charset+ request header indicates the given # character set is acceptable, false otherwise. def charset?(value) charset.accept?(value) end # Returns true if the +Accept-Encoding+ request header indicates the given # encoding is acceptable, false otherwise. def encoding?(value) encoding.accept?(value) end # Returns true if the +Accept-Language+ request header indicates the given # language is acceptable, false otherwise. def language?(value) language.accept?(value) end # Determines the best media type to use in a response from the given media # types, if any is acceptable. For more information on how this value is # determined, see the documentation for # Rack::Accept::Header::PublicInstanceMethods#sort. def best_media_type(values) media_type.best_of(values) end # Determines the best character set to use in a response from the given # character sets, if any is acceptable. For more information on how this # value is determined, see the documentation for # Rack::Accept::Header::PublicInstanceMethods#sort. def best_charset(values) charset.best_of(values) end # Determines the best encoding to use in a response from the given # encodings, if any is acceptable. For more information on how this value # is determined, see the documentation for # Rack::Accept::Header::PublicInstanceMethods#sort. def best_encoding(values) encoding.best_of(values) end # Determines the best language to use in a response from the given # languages, if any is acceptable. For more information on how this value # is determined, see the documentation for # Rack::Accept::Header::PublicInstanceMethods#sort. def best_language(values) language.best_of(values) end end end ruby-rack-accept-0.4.5/lib/rack/accept/response.rb000066400000000000000000000011461216733320000217630ustar00rootroot00000000000000require 'rack/response' module Rack::Accept # The base class for responses issued by Rack::Accept. class Response < Rack::Response # Marks this response as being unacceptable and clears the response body. # # Note: The HTTP spec advises servers to respond with an "entity" that # describes acceptable parameters, but it fails to go into detail about its # implementation. Thus, it is up to the user of this library to create such # an entity if one is desired. def not_acceptable! self.status = 406 self.body = [] header['Content-Length'] = '0' end end end ruby-rack-accept-0.4.5/lib/rack/accept/version.rb000066400000000000000000000002631216733320000216110ustar00rootroot00000000000000module Rack module Accept VERSION = [0, 4, 5] # Returns the current version of Rack::Accept as a string. def self.version VERSION.join('.') end end end ruby-rack-accept-0.4.5/metadata.yml000066400000000000000000000045101216733320000171540ustar00rootroot00000000000000--- !ruby/object:Gem::Specification name: rack-accept version: !ruby/object:Gem::Version version: 0.4.5 prerelease: platform: ruby authors: - Michael Jackson autorequire: bindir: bin cert_chain: [] date: 2012-06-15 00:00:00.000000000Z dependencies: - !ruby/object:Gem::Dependency name: rack requirement: &70171348874000 !ruby/object:Gem::Requirement none: false requirements: - - ! '>=' - !ruby/object:Gem::Version version: '0.4' type: :runtime prerelease: false version_requirements: *70171348874000 - !ruby/object:Gem::Dependency name: rake requirement: &70171348873580 !ruby/object:Gem::Requirement none: false requirements: - - ! '>=' - !ruby/object:Gem::Version version: '0' type: :development prerelease: false version_requirements: *70171348873580 description: HTTP Accept, Accept-Charset, Accept-Encoding, and Accept-Language for Ruby/Rack email: mjijackson@gmail.com executables: [] extensions: [] extra_rdoc_files: - CHANGES - README.md files: - lib/rack/accept/charset.rb - lib/rack/accept/context.rb - lib/rack/accept/encoding.rb - lib/rack/accept/header.rb - lib/rack/accept/language.rb - lib/rack/accept/media_type.rb - lib/rack/accept/request.rb - lib/rack/accept/response.rb - lib/rack/accept/version.rb - lib/rack/accept.rb - test/charset_test.rb - test/context_test.rb - test/encoding_test.rb - test/header_test.rb - test/helper.rb - test/language_test.rb - test/media_type_test.rb - test/request_test.rb - CHANGES - rack-accept.gemspec - Rakefile - README.md homepage: http://mjijackson.github.com/rack-accept licenses: [] post_install_message: rdoc_options: - --line-numbers - --inline-source - --title - Rack::Accept - --main - Rack::Accept require_paths: - lib required_ruby_version: !ruby/object:Gem::Requirement none: false requirements: - - ! '>=' - !ruby/object:Gem::Version version: '0' required_rubygems_version: !ruby/object:Gem::Requirement none: false requirements: - - ! '>=' - !ruby/object:Gem::Version version: '0' requirements: [] rubyforge_project: rubygems_version: 1.8.17 signing_key: specification_version: 3 summary: HTTP Accept* for Ruby/Rack test_files: - test/charset_test.rb - test/context_test.rb - test/encoding_test.rb - test/header_test.rb - test/language_test.rb - test/media_type_test.rb - test/request_test.rb ruby-rack-accept-0.4.5/rack-accept.gemspec000066400000000000000000000017161216733320000204000ustar00rootroot00000000000000$LOAD_PATH.unshift(File.expand_path('../lib', __FILE__)) require 'rack/accept/version' Gem::Specification.new do |s| s.name = 'rack-accept' s.version = Rack::Accept.version s.date = Time.now.strftime('%Y-%m-%d') s.summary = 'HTTP Accept* for Ruby/Rack' s.description = 'HTTP Accept, Accept-Charset, Accept-Encoding, and Accept-Language for Ruby/Rack' s.author = 'Michael Jackson' s.email = 'mjijackson@gmail.com' s.require_paths = %w< lib > s.files = Dir['doc/**/*'] + Dir['lib/**/*.rb'] + Dir['test/*.rb'] + %w< CHANGES rack-accept.gemspec Rakefile README.md > s.test_files = s.files.select {|path| path =~ /^test\/.*_test.rb/ } s.add_dependency('rack', '>= 0.4') s.add_development_dependency('rake') s.has_rdoc = true s.rdoc_options = %w< --line-numbers --inline-source --title Rack::Accept --main Rack::Accept > s.extra_rdoc_files = %w< CHANGES README.md > s.homepage = 'http://mjijackson.github.com/rack-accept' end ruby-rack-accept-0.4.5/test/000077500000000000000000000000001216733320000156305ustar00rootroot00000000000000ruby-rack-accept-0.4.5/test/charset_test.rb000066400000000000000000000024661216733320000206550ustar00rootroot00000000000000require File.expand_path('../helper', __FILE__) class CharsetTest < Test::Unit::TestCase C = Rack::Accept::Charset def test_qvalue c = C.new('') assert_equal(0, c.qvalue('unicode-1-1')) assert_equal(1, c.qvalue('iso-8859-1')) c = C.new('unicode-1-1') assert_equal(1, c.qvalue('unicode-1-1')) assert_equal(0, c.qvalue('iso-8859-5')) assert_equal(1, c.qvalue('iso-8859-1')) c = C.new('unicode-1-1, *;q=0.5') assert_equal(1, c.qvalue('unicode-1-1')) assert_equal(0.5, c.qvalue('iso-8859-5')) assert_equal(0.5, c.qvalue('iso-8859-1')) c = C.new('iso-8859-1;q=0, *;q=0.5') assert_equal(0.5, c.qvalue('iso-8859-5')) assert_equal(0, c.qvalue('iso-8859-1')) c = C.new('*;q=0') assert_equal(0, c.qvalue('iso-8859-1')) end def test_matches c = C.new('iso-8859-1, iso-8859-5, *') assert_equal(%w{*}, c.matches('')) assert_equal(%w{iso-8859-1 *}, c.matches('iso-8859-1')) assert_equal(%w{*}, c.matches('unicode-1-1')) end def test_best_of c = C.new('iso-8859-5, unicode-1-1;q=0.8') assert_equal('iso-8859-5', c.best_of(%w< iso-8859-5 unicode-1-1 >)) assert_equal('iso-8859-5', c.best_of(%w< iso-8859-5 utf-8 >)) assert_equal('iso-8859-1', c.best_of(%w< iso-8859-1 utf-8 >)) assert_equal(nil, c.best_of(%w< utf-8 >)) end end ruby-rack-accept-0.4.5/test/context_test.rb000066400000000000000000000030521216733320000207000ustar00rootroot00000000000000require File.expand_path('../helper', __FILE__) class ContextTest < Test::Unit::TestCase def media_types; Proc.new {|c| c.media_types = %w< text/html > } end def charsets; Proc.new {|c| c.charsets = %w< iso-8859-5 > } end def encodings; Proc.new {|c| c.encodings = %w< gzip > } end def languages; Proc.new {|c| c.languages = %w< en > } end def test_empty request assert_equal(200, status) end def test_media_types request('HTTP_ACCEPT' => 'text/html') assert_equal(200, status) request('HTTP_ACCEPT' => 'text/html', &media_types) assert_equal(200, status) request('HTTP_ACCEPT' => 'text/plain', &media_types) assert_equal(406, status) end def test_charsets request('HTTP_ACCEPT_CHARSET' => 'iso-8859-5') assert_equal(200, status) request('HTTP_ACCEPT_CHARSET' => 'iso-8859-5', &charsets) assert_equal(200, status) request('HTTP_ACCEPT_CHARSET' => 'unicode-1-1', &charsets) assert_equal(406, status) end def test_encodings request('HTTP_ACCEPT_ENCODING' => 'gzip') assert_equal(200, status) request('HTTP_ACCEPT_ENCODING' => 'gzip', &encodings) assert_equal(200, status) request('HTTP_ACCEPT_ENCODING' => 'compress', &encodings) assert_equal(406, status) end def test_languages request('HTTP_ACCEPT_LANGUAGE' => 'en') assert_equal(200, status) request('HTTP_ACCEPT_LANGUAGE' => 'en', &languages) assert_equal(200, status) request('HTTP_ACCEPT_LANGUAGE' => 'jp', &languages) assert_equal(406, status) end end ruby-rack-accept-0.4.5/test/encoding_test.rb000066400000000000000000000014141216733320000210020ustar00rootroot00000000000000require File.expand_path('../helper', __FILE__) class EncodingTest < Test::Unit::TestCase E = Rack::Accept::Encoding def test_qvalue e = E.new('') assert_equal(0, e.qvalue('gzip')) assert_equal(1, e.qvalue('identity')) e = E.new('gzip, *;q=0.5') assert_equal(1, e.qvalue('gzip')) assert_equal(0.5, e.qvalue('identity')) end def test_matches e = E.new('gzip, identity, *') assert_equal(%w{*}, e.matches('')) assert_equal(%w{gzip *}, e.matches('gzip')) assert_equal(%w{*}, e.matches('compress')) end def test_best_of e = E.new('gzip, compress') assert_equal('gzip', e.best_of(%w< gzip compress >)) assert_equal('identity', e.best_of(%w< identity compress >)) assert_equal(nil, e.best_of(%w< zip >)) end end ruby-rack-accept-0.4.5/test/header_test.rb000066400000000000000000000044111216733320000204440ustar00rootroot00000000000000require File.expand_path('../helper', __FILE__) class HeaderTest < Test::Unit::TestCase H = Rack::Accept::Header def test_parse_and_join # Accept header = 'text/plain; q=0.5, text/html, text/html;level=2, text/html;level=1;q=0.3, text/x-c, image/*; q=0.2' expect = { 'text/plain' => 0.5, 'text/html' => 1, 'text/html;level=2' => 1, 'text/html;level=1' => 0.3, 'text/x-c' => 1, 'image/*' => 0.2 } assert_equal(expect, H.parse(header)) assert_equal(expect, H.parse(H.join(expect))) # Accept-Charset header = 'iso-8859-5, unicode-1-1;q=0.8' expect = { 'iso-8859-5' => 1, 'unicode-1-1' => 0.8 } assert_equal(expect, H.parse(header)) assert_equal(expect, H.parse(H.join(expect))) # Accept-Encoding header = 'gzip;q=1.0, identity; q=0.5, *;q=0' expect = { 'gzip' => 1, 'identity' => 0.5, '*' => 0 } assert_equal(expect, H.parse(header)) assert_equal(expect, H.parse(H.join(expect))) # Accept-Language header = 'da, en-gb;q=0.8, en;q=0.7' expect = { 'da' => 1, 'en-gb' => 0.8, 'en' => 0.7 } assert_equal(expect, H.parse(header)) assert_equal(expect, H.parse(H.join(expect))) end def test_parse_media_type assert_equal([], H.parse_media_type('')) assert_equal(['*', '*', ''], H.parse_media_type('*/*')) assert_equal(['text', '*', ''], H.parse_media_type('text/*')) assert_equal(['text', 'html', ''], H.parse_media_type('text/html')) assert_equal(['text', 'html', 'level=1'], H.parse_media_type('text/html;level=1')) assert_equal(['text', 'html', 'level=1;answer=42'], H.parse_media_type('text/html;level=1;answer=42')) assert_equal(['text', 'x-dvi', ''], H.parse_media_type('text/x-dvi')) end def test_parse_range_params assert_equal({}, H.parse_range_params('')) assert_equal({}, H.parse_range_params('a')) assert_equal({'a' => 'a'}, H.parse_range_params('a=a')) assert_equal({'a' => 'a', 'b' => 'b'}, H.parse_range_params('a=a;b=b')) end def test_normalize_qvalue assert_equal(1, H.normalize_qvalue(1.0)) assert_equal(0, H.normalize_qvalue(0.0)) assert_equal(1, H.normalize_qvalue(1)) assert_equal(0, H.normalize_qvalue(0)) assert_equal(0.5, H.normalize_qvalue(0.5)) end end ruby-rack-accept-0.4.5/test/helper.rb000066400000000000000000000015651216733320000174430ustar00rootroot00000000000000ENV['RACK_ENV'] = 'test' begin require 'rack' rescue LoadError require 'rubygems' require 'rack' end testdir = File.dirname(__FILE__) $LOAD_PATH.unshift(testdir) unless $LOAD_PATH.include?(testdir) libdir = File.dirname(File.dirname(__FILE__)) + '/lib' $LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir) require 'test/unit' require 'rack/accept' class Test::Unit::TestCase attr_reader :context attr_reader :response def status @response && @response.status end def request(env={}, method='GET', uri='/') @context = Rack::Accept.new(fake_app) yield @context if block_given? mock_request = Rack::MockRequest.new(@context) @response = mock_request.request(method.to_s.upcase, uri, env) @response end def fake_app(status=200, headers={}, body=[]) lambda {|env| Rack::Response.new(body, status, headers).finish } end end ruby-rack-accept-0.4.5/test/language_test.rb000066400000000000000000000024251216733320000210020ustar00rootroot00000000000000require File.expand_path('../helper', __FILE__) class LanguageTest < Test::Unit::TestCase L = Rack::Accept::Language def test_qvalue l = L.new('') assert_equal(1, l.qvalue('en')) l = L.new('en;q=0.5, en-gb') assert_equal(0.5, l.qvalue('en')) assert_equal(1, l.qvalue('en-gb')) assert_equal(0, l.qvalue('da')) end def test_matches l = L.new('da, *, en') assert_equal(%w{*}, l.matches('')) assert_equal(%w{da *}, l.matches('da')) assert_equal(%w{en *}, l.matches('en')) assert_equal(%w{en *}, l.matches('en-gb')) assert_equal(%w{*}, l.matches('eng')) l = L.new('en, en-gb') assert_equal(%w{en-gb en}, l.matches('en-gb')) end def test_best_of l = L.new('en;q=0.5, en-gb') assert_equal('en-gb', l.best_of(%w< en en-gb >)) assert_equal('en', l.best_of(%w< en da >)) assert_equal('en-us', l.best_of(%w< en-us en-au >)) assert_equal(nil, l.best_of(%w< da >)) l = L.new('en;q=0.5, en-GB, fr-CA') assert_equal('en-gb', l.best_of(%w< en en-gb >)) l = L.new('en-gb;q=0.5, EN, fr-CA') assert_equal('en', l.best_of(%w< en en-gb >)) l = L.new('en;q=0.5, fr-ca') assert_equal('en', l.best_of(%w< en fr >)) l.first_level_match = true assert_equal('fr', l.best_of(%w< en fr >)) end end ruby-rack-accept-0.4.5/test/media_type_test.rb000066400000000000000000000041741216733320000213420ustar00rootroot00000000000000require File.expand_path('../helper', __FILE__) class MediaTypeTest < Test::Unit::TestCase M = Rack::Accept::MediaType def test_qvalue m = M.new('text/html, text/*;q=0.3, */*;q=0.5') assert_equal(0.5, m.qvalue('image/png')) assert_equal(0.3, m.qvalue('text/plain')) assert_equal(1, m.qvalue('text/html')) m = M.new('text/html') assert_equal(0, m.qvalue('image/png')) m = M.new('') assert_equal(1, m.qvalue('text/html')) end def test_matches m = M.new('text/*, text/html, text/html;level=1, */*') assert_equal(%w{*/*}, m.matches('')) assert_equal(%w{*/*}, m.matches('image/jpeg')) assert_equal(%w{text/* */*}, m.matches('text/plain')) assert_equal(%w{text/html text/* */*}, m.matches('text/html')) assert_equal(%w{text/html;level=1 text/html text/* */*}, m.matches('text/html;level=1')) assert_equal(%w{text/html;level=1 text/html text/* */*}, m.matches('text/html;level=1;answer=42')) end def test_best_of m = M.new('text/*;q=0.5, text/html') assert_equal('text/html', m.best_of(%w< text/plain text/html >)) assert_equal('text/plain', m.best_of(%w< text/plain image/png >)) assert_equal('text/plain', m.best_of(%w< text/plain text/javascript >)) assert_equal(nil, m.best_of(%w< image/png >)) m = M.new('text/*') assert_equal('text/html', m.best_of(%w< text/html text/xml >)) assert_equal('text/xml', m.best_of(%w< text/xml text/html >)) m = M.new('TEXT/*') assert_equal('text/html', m.best_of(%w< text/html text/xml >)) assert_equal('text/xml', m.best_of(%w< text/xml text/html >)) end def test_extension m = M.new('text/*;q=0.5;a=42') assert_equal(0.5, m.qvalue('text/plain')) end def test_vendored_types m = M.new("application/vnd.ms-excel") assert_equal(nil, m.best_of(%w< application/vnd.ms-powerpoint >)) m = M.new("application/vnd.api-v1+json") assert_equal(false, m.accept?("application/vnd.api-v2+json")) v1, v2 = "application/vnd.api-v1+json", "application/vnd.api-v2+json" m = M.new("#{v1},#{v2}") assert_equal(v1, m.best_of([v1, v2])) assert_equal(v2, m.best_of([v2, v1])) end end ruby-rack-accept-0.4.5/test/request_test.rb000066400000000000000000000033141216733320000207050ustar00rootroot00000000000000require File.expand_path('../helper', __FILE__) class RequestTest < Test::Unit::TestCase R = Rack::Accept::Request def test_media_type r = R.new('HTTP_ACCEPT' => 'text/*;q=0, text/html') assert(r.media_type?('text/html')) assert(r.media_type?('text/html;level=1')) assert(!r.media_type?('text/plain')) assert(!r.media_type?('image/png')) request = R.new('HTTP_ACCEPT' => '*/*') assert(request.media_type?('image/png')) end def test_charset r = R.new('HTTP_ACCEPT_CHARSET' => 'iso-8859-5, unicode-1-1;q=0.8') assert(r.charset?('iso-8859-5')) assert(r.charset?('unicode-1-1')) assert(r.charset?('iso-8859-1')) assert(!r.charset?('utf-8')) r = R.new('HTTP_ACCEPT_CHARSET' => 'iso-8859-1;q=0') assert(!r.charset?('iso-8859-1')) end def test_encoding r = R.new('HTTP_ACCEPT_ENCODING' => '') assert(r.encoding?('identity')) assert(!r.encoding?('gzip')) r = R.new('HTTP_ACCEPT_ENCODING' => 'gzip') assert(r.encoding?('identity')) assert(r.encoding?('gzip')) assert(!r.encoding?('compress')) r = R.new('HTTP_ACCEPT_ENCODING' => 'gzip;q=0, *') assert(r.encoding?('compress')) assert(r.encoding?('identity')) assert(!r.encoding?('gzip')) r = R.new('HTTP_ACCEPT_ENCODING' => 'identity;q=0') assert(!r.encoding?('identity')) r = R.new('HTTP_ACCEPT_ENCODING' => '*;q=0') assert(!r.encoding?('identity')) end def test_language request = R.new({}) assert(request.language?('en')) assert(request.language?('da')) request = R.new('HTTP_ACCEPT_LANGUAGE' => 'en;q=0.5, en-gb') assert(request.language?('en')) assert(request.language?('en-gb')) assert(!request.language?('da')) end end