Rack::Utils contains a grab-bag of useful methods for writing web applications adopted from all kinds of Ruby libraries.
Every standard HTTP code mapped to the appropriate message. Generated with:
curl -s http://www.iana.org/assignments/http-status-codes | \ ruby -ane 'm = /^(\d{3}) +(\S[^\[(]+)/.match($_) and puts " #{m[1]} => \x27#{m[2].strip}x27,"'
Responses with HTTP status codes that should not have an entity body
# File lib/rack/utils.rb, line 121 def build_nested_query(value, prefix = nil) case value when Array value.map { |v| build_nested_query(v, "#{prefix}[]") }.join("&") when Hash value.map { |k, v| build_nested_query(v, prefix ? "#{prefix}[#{escape(k)}]" : escape(k)) }.join("&") when String raise ArgumentError, "value must be a Hash" if prefix.nil? "#{prefix}=#{escape(value)}" else prefix end end
# File lib/rack/utils.rb, line 110 def build_query(params) params.map { |k, v| if v.class == Array build_query(v.map { |x| [k, x] }) else "#{escape(k)}=#{escape(v)}" end }.join("&") end
Parses the “Range:” header, if present, into an array of Range objects. Returns nil if the header is missing or syntactically invalid. Returns an empty array if none of the ranges are satisfiable.
# File lib/rack/utils.rb, line 277 def byte_ranges(env, size) # See <http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35> http_range = env['HTTP_RANGE'] return nil unless http_range ranges = [] http_range.split(/,\s*/).each do |range_spec| matches = range_spec.match(/bytes=(\d*)-(\d*)/) return nil unless matches r0,r1 = matches[1], matches[2] if r0.empty? return nil if r1.empty? # suffix-byte-range-spec, represents trailing suffix of file r0 = [size - r1.to_i, 0].max r1 = size - 1 else r0 = r0.to_i if r1.empty? r1 = size - 1 else r1 = r1.to_i return nil if r1 < r0 # backwards range is syntactically invalid r1 = size-1 if r1 >= size end end ranges << (r0..r1) if r0 <= r1 end ranges end
# File lib/rack/utils.rb, line 248 def bytesize(string) string.bytesize end
URI escapes. (CGI style space to +)
# File lib/rack/utils.rb, line 21 def escape(s) URI.encode_www_form_component(s) end
Escape ampersands, brackets and quotes to their HTML/XML entities.
# File lib/rack/utils.rb, line 151 def escape_html(string) string.to_s.gsub(ESCAPE_HTML_PATTERN){|c| ESCAPE_HTML[c] } end
Like URI escaping, but with %20 instead of +. Strictly speaking this is true URI escaping.
# File lib/rack/utils.rb, line 28 def escape_path(s) escape(s).gsub('+', '%20') end
# File lib/rack/utils.rb, line 78 def normalize_params(params, name, v = nil) name =~ %(\A[\[\]]*([^\[\]]+)\]*) k = $1 || '' after = $' || '' return if k.empty? if after == "" params[k] = v elsif after == "[]" params[k] ||= [] raise TypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array) params[k] << v elsif after =~ %(^\[\]\[([^\[\]]+)\]$) || after =~ %(^\[\](.+)$) child_key = $1 params[k] ||= [] raise TypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array) if params[k].last.is_a?(Hash) && !params[k].last.key?(child_key) normalize_params(params[k].last, child_key, v) else params[k] << normalize_params({}, child_key, v) end else params[k] ||= {} raise TypeError, "expected Hash (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Hash) params[k] = normalize_params(params[k], after, v) end return params end
# File lib/rack/utils.rb, line 66 def parse_nested_query(qs, d = nil) params = {} (qs || '').split(d ? /[#{d}] */ : DEFAULT_SEP).each do |p| k, v = p.split('=', 2).map { |s| unescape(s) } normalize_params(params, k, v) end return params end
Stolen from Mongrel, with some small modifications: Parses a query string by breaking it up at the ‘&’ and ‘;’ characters. You can also use this to parse cookies by changing the characters used in the second parameter (which defaults to ‘&;’).
# File lib/rack/utils.rb, line 46 def parse_query(qs, d = nil) params = {} (qs || '').split(d ? /[#{d}] */ : DEFAULT_SEP).each do |p| k, v = p.split('=', 2).map { |x| unescape(x) } if cur = params[k] if cur.class == Array params[k] << v else params[k] = [cur, v] end else params[k] = v end end return params end
Modified version of stdlib time.rb Time#rfc2822 to use ‘%d-%b-%Y’ instead of ‘% %b %Y’. It assumes that the time is in GMT to comply to the RFC 2109.
NOTE: I’m not sure the RFC says it requires GMT, but is ambigous enough that I’m certain someone implemented only that option. Do not use %a and %b from Time.strptime, it would use localized names for weekday and month.
# File lib/rack/utils.rb, line 267 def rfc2822(time) wday = Time::RFC2822_DAY_NAME[time.wday] mon = Time::RFC2822_MONTH_NAME[time.mon - 1] time.strftime("#{wday}, %d-#{mon}-%Y %H:%M:%S GMT") end
# File lib/rack/utils.rb, line 156 def select_best_encoding(available_encodings, accept_encoding) # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html expanded_accept_encoding = accept_encoding.map { |m, q| if m == "*" (available_encodings - accept_encoding.map { |m2, _| m2 }).map { |m2| [m2, q] } else [[m, q]] end }.inject([]) { |mem, list| mem + list } encoding_candidates = expanded_accept_encoding.sort_by { |_, q| -q }.map { |m, _| m } unless encoding_candidates.include?("identity") encoding_candidates.push("identity") end expanded_accept_encoding.find_all { |m, q| q == 0.0 }.each { |m, _| encoding_candidates.delete(m) } return (encoding_candidates & available_encodings)[0] end
Generated with the Darkfish Rdoc Generator 2.