mirror of
https://github.com/sinatra/sinatra
synced 2023-03-27 23:18:01 -04:00
Merge branch 'master' into content-for
This commit is contained in:
commit
36d2a73c9b
28 changed files with 1835 additions and 0 deletions
2
sinatra-contrib/Gemfile
Normal file
2
sinatra-contrib/Gemfile
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
source "http://rubygems.org" unless ENV['QUICK']
|
||||||
|
gemspec
|
1
sinatra-contrib/README.md
Normal file
1
sinatra-contrib/README.md
Normal file
|
@ -0,0 +1 @@
|
||||||
|
TODO: More imports/rewrites, documentation.
|
40
sinatra-contrib/lib/sinatra/config_file.rb
Normal file
40
sinatra-contrib/lib/sinatra/config_file.rb
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
require 'sinatra/base'
|
||||||
|
require 'yaml'
|
||||||
|
|
||||||
|
module Sinatra
|
||||||
|
module ConfigFile
|
||||||
|
def self.registered(base)
|
||||||
|
base.set :environments, %w[test production development]
|
||||||
|
end
|
||||||
|
|
||||||
|
def config_file(*paths)
|
||||||
|
Dir.chdir(root || '.') do
|
||||||
|
paths.each do |pattern|
|
||||||
|
Dir.glob(pattern) do |file|
|
||||||
|
$stderr.puts "loading config file '#{file}'" if logging?
|
||||||
|
yaml = config_for_env(YAML.load_file(file)) || {}
|
||||||
|
yaml.each_pair do |key, value|
|
||||||
|
for_env = config_for_env(value)
|
||||||
|
set key, for_env unless value and for_env.nil? and respond_to? key
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def config_for_env(hash)
|
||||||
|
if hash.respond_to? :keys and hash.keys.all? { |k| environments.include? k.to_s }
|
||||||
|
hash = hash[environment.to_s] || hash[environment.to_sym]
|
||||||
|
end
|
||||||
|
|
||||||
|
if hash.respond_to? :to_hash
|
||||||
|
indifferent_hash = Hash.new {|hash,key| hash[key.to_s] if Symbol === key }
|
||||||
|
indifferent_hash.merge hash.to_hash
|
||||||
|
else
|
||||||
|
hash
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
29
sinatra-contrib/lib/sinatra/contrib.rb
Normal file
29
sinatra-contrib/lib/sinatra/contrib.rb
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
require 'sinatra/contrib/setup'
|
||||||
|
|
||||||
|
module Sinatra
|
||||||
|
module Contrib
|
||||||
|
##
|
||||||
|
# Common middleware that doesn't bring run time overhead if not used
|
||||||
|
# or breaks if external dependencies are missing. Will extend
|
||||||
|
# Sinatra::Application by default.
|
||||||
|
module Common
|
||||||
|
register :ConfigFile
|
||||||
|
register :Decompile
|
||||||
|
register :Namespace
|
||||||
|
register :RespondWith
|
||||||
|
helpers :LinkHeader
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Other extensions you don't want to be loaded unless needed.
|
||||||
|
module Custom
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Stuff that aren't Sinatra extensions, technically.
|
||||||
|
autoload :Extension
|
||||||
|
autoload :TestHelpers
|
||||||
|
end
|
||||||
|
|
||||||
|
register Sinatra::Contrib::Common
|
||||||
|
end
|
46
sinatra-contrib/lib/sinatra/contrib/setup.rb
Normal file
46
sinatra-contrib/lib/sinatra/contrib/setup.rb
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
require 'sinatra/base'
|
||||||
|
require 'sinatra/contrib/version'
|
||||||
|
require 'backports'
|
||||||
|
|
||||||
|
module Sinatra
|
||||||
|
module Contrib
|
||||||
|
module Loader
|
||||||
|
def extensions
|
||||||
|
@extensions ||= {:helpers => [], :register => []}
|
||||||
|
end
|
||||||
|
|
||||||
|
def register(name, path = nil)
|
||||||
|
autoload name, path, :register
|
||||||
|
end
|
||||||
|
|
||||||
|
def helpers(name, path = nil)
|
||||||
|
autoload name, path, :helpers
|
||||||
|
end
|
||||||
|
|
||||||
|
def autoload(name, path = nil, method = nil)
|
||||||
|
path ||= "sinatra/#{name.to_s.underscore}"
|
||||||
|
extensions[method] << name if method
|
||||||
|
Sinatra.autoload(name, path)
|
||||||
|
end
|
||||||
|
|
||||||
|
def registered(base)
|
||||||
|
@extensions.each do |meth, list|
|
||||||
|
base.send(meth, *list.map { |n| Sinatra.const_get n })
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
module Common
|
||||||
|
extend Loader
|
||||||
|
end
|
||||||
|
|
||||||
|
module Custom
|
||||||
|
extend Loader
|
||||||
|
end
|
||||||
|
|
||||||
|
extend Loader
|
||||||
|
def self.registered(base)
|
||||||
|
base.register Common, Custom
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
44
sinatra-contrib/lib/sinatra/contrib/version.rb
Normal file
44
sinatra-contrib/lib/sinatra/contrib/version.rb
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
module Sinatra
|
||||||
|
module Contrib
|
||||||
|
def self.version
|
||||||
|
VERSION
|
||||||
|
end
|
||||||
|
|
||||||
|
module VERSION
|
||||||
|
extend Comparable
|
||||||
|
|
||||||
|
MAJOR = 1
|
||||||
|
MINOR = 2
|
||||||
|
TINY = 0
|
||||||
|
SIGNATURE = [MAJOR, MINOR, TINY]
|
||||||
|
STRING = SIGNATURE.join '.'
|
||||||
|
|
||||||
|
def self.major; MAJOR end
|
||||||
|
def self.minor; MINOR end
|
||||||
|
def self.tiny; TINY end
|
||||||
|
def self.to_s; STRING end
|
||||||
|
|
||||||
|
def self.hash
|
||||||
|
STRING.hash
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.<=>(other)
|
||||||
|
other = other.split('.').map { |i| i.to_i } if other.respond_to? :split
|
||||||
|
SIGNATURE <=> Array(other)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.inspect
|
||||||
|
STRING.inspect
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.respond_to?(meth, *)
|
||||||
|
meth.to_s !~ /^__|^to_str$/ and STRING.respond_to? meth unless super
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.method_missing(meth, *args, &block)
|
||||||
|
return super unless STRING.respond_to?(meth)
|
||||||
|
STRING.send(meth, *args, &block)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
61
sinatra-contrib/lib/sinatra/decompile.rb
Normal file
61
sinatra-contrib/lib/sinatra/decompile.rb
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
require 'sinatra/base'
|
||||||
|
require 'backports'
|
||||||
|
|
||||||
|
module Sinatra
|
||||||
|
##
|
||||||
|
# Can be used as extension or stand-alone:
|
||||||
|
#
|
||||||
|
# Sinatra::Decompile.decompile(...)
|
||||||
|
module Decompile
|
||||||
|
extend self
|
||||||
|
|
||||||
|
##
|
||||||
|
# Regenerates a string pattern for a given route
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
#
|
||||||
|
# class Sinatra::Application
|
||||||
|
# routes.each do |verb, list|
|
||||||
|
# puts "#{verb}:"
|
||||||
|
# list.each do |data|
|
||||||
|
# puts "\t" << decompile(data)
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# Will return the internal Regexp if unable to reconstruct the pattern,
|
||||||
|
# which likely indicates that a Regexp was used in the first place.
|
||||||
|
#
|
||||||
|
# You can also use this to check whether you could actually use a string
|
||||||
|
# pattern instead of your regexp:
|
||||||
|
#
|
||||||
|
# decompile /^/foo$/ # => '/foo'
|
||||||
|
def decompile(pattern, keys = nil, *)
|
||||||
|
# Everything in here is basically just the reverse of
|
||||||
|
# Sinatra::Base#compile
|
||||||
|
pattern, keys = pattern if pattern.respond_to? :to_ary
|
||||||
|
keys, str = keys.try(:dup), pattern.inspect
|
||||||
|
return pattern unless str.start_with? '/' and str.end_with? '/'
|
||||||
|
str.gsub! /^\/\^?|\$?\/$/, ''
|
||||||
|
return pattern if str =~ /^[\.\+]/
|
||||||
|
str.gsub! /\([^\(]*\)/ do |part|
|
||||||
|
case part
|
||||||
|
when '(.*?)'
|
||||||
|
return pattern if keys.shift != 'splat'
|
||||||
|
'*'
|
||||||
|
when '([^\/?#]+)'
|
||||||
|
return pattern if keys.empty?
|
||||||
|
":" << keys.shift
|
||||||
|
else
|
||||||
|
return pattern
|
||||||
|
end
|
||||||
|
end
|
||||||
|
str.gsub /(.)([\.\+\(\)\/])/ do
|
||||||
|
return pattern if $1 != "\\"
|
||||||
|
$2
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
register Decompile
|
||||||
|
end
|
50
sinatra-contrib/lib/sinatra/extension.rb
Normal file
50
sinatra-contrib/lib/sinatra/extension.rb
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
require 'sinatra/base'
|
||||||
|
require 'backports/basic_object' unless defined? BasicObject
|
||||||
|
|
||||||
|
module Sinatra
|
||||||
|
module Extension
|
||||||
|
def self.new(&block)
|
||||||
|
ext = Module.new.extend(self)
|
||||||
|
ext.class_eval(&block)
|
||||||
|
ext
|
||||||
|
end
|
||||||
|
|
||||||
|
def settings
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
def configure(*args, &block)
|
||||||
|
record(:configure, *args) { |c| c.instance_exec(c, &block) }
|
||||||
|
end
|
||||||
|
|
||||||
|
def registered(base = nil, &block)
|
||||||
|
base ? replay(base) : record(:class_eval, &block)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def record(method, *args, &block)
|
||||||
|
recorded_methods << [method, args, block]
|
||||||
|
end
|
||||||
|
|
||||||
|
def replay(object)
|
||||||
|
recorded_methods.each { |m, a, b| object.send(m, *a, &b) }
|
||||||
|
end
|
||||||
|
|
||||||
|
def recorded_methods
|
||||||
|
@recorded_methods ||= []
|
||||||
|
end
|
||||||
|
|
||||||
|
def method_missing(method, *args, &block)
|
||||||
|
return super unless Sinatra::Base.respond_to? method
|
||||||
|
record(method, *args, &block)
|
||||||
|
DontCall.new(method)
|
||||||
|
end
|
||||||
|
|
||||||
|
class DontCall < BasicObject
|
||||||
|
def initialize(method) @method = method end
|
||||||
|
def method_missing(*) fail "not supposed to use result of #@method!" end
|
||||||
|
def inspect; "#<#{self.class}: #{@method}>" end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
84
sinatra-contrib/lib/sinatra/link_header.rb
Normal file
84
sinatra-contrib/lib/sinatra/link_header.rb
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
require 'sinatra/base'
|
||||||
|
|
||||||
|
module Sinatra
|
||||||
|
##
|
||||||
|
# Helper methods for generating Link HTTP headers and HTML tags.
|
||||||
|
module LinkHeader
|
||||||
|
##
|
||||||
|
# Set Link HTTP header and returns HTML tags for telling the browser to
|
||||||
|
# prefetch given resources (only supported by Opera and Firefox at the
|
||||||
|
# moment).
|
||||||
|
def prefetch(*urls)
|
||||||
|
link(:prefetch, *urls)
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Sets Link HTTP header and returns HTML tags for using stylesheets.
|
||||||
|
def stylesheet(*urls)
|
||||||
|
urls << {} unless urls.last.respond_to? :to_hash
|
||||||
|
urls.last[:type] ||= mime_type(:css)
|
||||||
|
link(:stylesheet, *urls)
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Sets Link HTTP header and returns corresponding HTML tags.
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
#
|
||||||
|
# # Sets header:
|
||||||
|
# # Link: </foo>; rel="next"
|
||||||
|
# # Returns String:
|
||||||
|
# # '<link href="/foo" rel="next" />'
|
||||||
|
# link '/foo', :rel => :next
|
||||||
|
#
|
||||||
|
# # Multiple URLs
|
||||||
|
# link :stylesheet, '/a.css', '/b.css'
|
||||||
|
def link(*urls)
|
||||||
|
opts = urls.last.respond_to?(:to_hash) ? urls.pop : {}
|
||||||
|
opts[:rel] = urls.shift unless urls.first.respond_to? :to_str
|
||||||
|
options = opts.map { |k, v| " #{k}=#{v.to_s.inspect}" }
|
||||||
|
html_pattern = "<link href=\"%s\"#{options.join} />"
|
||||||
|
http_pattern = ["<%s>", *options].join ";"
|
||||||
|
link = (response["Link"] ||= "")
|
||||||
|
|
||||||
|
urls.map do |url|
|
||||||
|
link << "\n" unless link.empty?
|
||||||
|
link << (http_pattern % url)
|
||||||
|
html_pattern % url
|
||||||
|
end.join "\n"
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Takes the current value of th Link header(s) and generates HTML tags
|
||||||
|
# from it.
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
#
|
||||||
|
# get '/' do
|
||||||
|
# # You can of course use fancy helpers like #link, #stylesheet
|
||||||
|
# # or #prefetch
|
||||||
|
# response["Link"] = '</foo>; rel="next"'
|
||||||
|
# haml :some_page
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# __END__
|
||||||
|
#
|
||||||
|
# @@ layout
|
||||||
|
# %head= link_headers
|
||||||
|
# %body= yield
|
||||||
|
def link_headers
|
||||||
|
yield if block_given?
|
||||||
|
return "" unless response.include? "Link"
|
||||||
|
response["Link"].lines.map do |line|
|
||||||
|
url, *opts = line.split(';').map(&:strip)
|
||||||
|
"<link href=\"#{url[1..-2]}\" #{opts.join " "} />"
|
||||||
|
end.join "\n"
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.registered(base)
|
||||||
|
puts "WARNING: #{self} is a helpers module, not an extension."
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
helpers LinkHeader
|
||||||
|
end
|
137
sinatra-contrib/lib/sinatra/namespace.rb
Normal file
137
sinatra-contrib/lib/sinatra/namespace.rb
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
require 'sinatra/base'
|
||||||
|
require 'sinatra/decompile'
|
||||||
|
|
||||||
|
module Sinatra
|
||||||
|
module Namespace
|
||||||
|
def self.new(base, pattern, conditions = {}, &block)
|
||||||
|
Module.new do
|
||||||
|
extend NamespacedMethods
|
||||||
|
@base, @extensions = base, []
|
||||||
|
@pattern, @conditions = compile(pattern, conditions)
|
||||||
|
|
||||||
|
namespace = self
|
||||||
|
before { extend namespace }
|
||||||
|
define_method(:error_block!) do |*keys|
|
||||||
|
if block = keys.inject(nil) { |b,k| b ||= namespace.errors[k] }
|
||||||
|
instance_eval(&block)
|
||||||
|
else
|
||||||
|
super(*keys)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class_eval(&block)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
module SharedMethods
|
||||||
|
def namespace(pattern, conditions = {}, &block)
|
||||||
|
Sinatra::Namespace.new(self, pattern, conditions, &block)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
module NamespacedMethods
|
||||||
|
include SharedMethods
|
||||||
|
include Sinatra::Decompile
|
||||||
|
attr_reader :base
|
||||||
|
|
||||||
|
def self.prefixed(*names)
|
||||||
|
names.each { |n| define_method(n) { |*a, &b| prefixed(n, *a, &b) }}
|
||||||
|
end
|
||||||
|
|
||||||
|
prefixed :before, :after, :delete, :get, :head, :options, :patch, :post, :put
|
||||||
|
|
||||||
|
def helpers(*extensions, &block)
|
||||||
|
class_eval(&block) if block_given?
|
||||||
|
include(*extensions) if extensions.any?
|
||||||
|
end
|
||||||
|
|
||||||
|
def register(*extensions, &block)
|
||||||
|
extensions << Module.new(&block) if block_given?
|
||||||
|
@extensions += extensions
|
||||||
|
extensions.each do |extension|
|
||||||
|
extend extension
|
||||||
|
extension.registered(self) if extension.respond_to?(:registered)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def invoke_hook(name, *args)
|
||||||
|
@extensions.each { |e| e.send(name, *args) if e.respond_to?(name) }
|
||||||
|
end
|
||||||
|
|
||||||
|
def errors
|
||||||
|
@errors ||= {}
|
||||||
|
end
|
||||||
|
|
||||||
|
def not_found(&block)
|
||||||
|
error(404, &block)
|
||||||
|
end
|
||||||
|
|
||||||
|
def error(codes = Exception, &block)
|
||||||
|
[*codes].each { |c| errors[c] = block }
|
||||||
|
end
|
||||||
|
|
||||||
|
def respond_to(*args)
|
||||||
|
return @conditions[:provides] || base.respond_to if args.empty?
|
||||||
|
@conditions[:provides] = args
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def app
|
||||||
|
base.respond_to?(:base) ? base.base : base
|
||||||
|
end
|
||||||
|
|
||||||
|
def compile(pattern, conditions, default_pattern = nil)
|
||||||
|
if pattern.respond_to? :to_hash
|
||||||
|
conditions = conditions.merge pattern.to_hash
|
||||||
|
pattern = nil
|
||||||
|
end
|
||||||
|
base_pattern, base_conditions = @pattern, @conditions
|
||||||
|
pattern ||= default_pattern
|
||||||
|
base_pattern ||= base.pattern if base.respond_to? :pattern
|
||||||
|
base_conditions ||= base.conditions if base.respond_to? :conditions
|
||||||
|
[ prefixed_path(base_pattern, pattern),
|
||||||
|
(base_conditions || {}).merge(conditions) ]
|
||||||
|
end
|
||||||
|
|
||||||
|
def prefixed_path(a, b)
|
||||||
|
return a || b || // unless a and b
|
||||||
|
a, b = decompile(a), decompile(b) unless a.class == b.class
|
||||||
|
a, b = regexpify(a), regexpify(b) unless a.class == b.class
|
||||||
|
path = a.class.new "#{a}#{b}"
|
||||||
|
path = /^#{path}$/ if path.is_a? Regexp and base == app
|
||||||
|
path
|
||||||
|
end
|
||||||
|
|
||||||
|
def regexpify(pattern)
|
||||||
|
pattern = Sinatra::Base.send(:compile, pattern).first.inspect
|
||||||
|
pattern.gsub! /^\/(\^|\\A)?|(\$|\\Z)?\/$/, ''
|
||||||
|
Regexp.new pattern
|
||||||
|
end
|
||||||
|
|
||||||
|
def prefixed(method, pattern = nil, conditions = {}, &block)
|
||||||
|
default = '*' if method == :before or method == :after
|
||||||
|
pattern, conditions = compile pattern, conditions, default
|
||||||
|
result = base.send(method, pattern, conditions, &block)
|
||||||
|
invoke_hook :route_added, method.to_s.upcase, pattern, block
|
||||||
|
result
|
||||||
|
end
|
||||||
|
|
||||||
|
def method_missing(meth, *args, &block)
|
||||||
|
return super if args.any? or block or not base.respond_to? meth
|
||||||
|
base.send meth
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
module BaseMethods
|
||||||
|
include SharedMethods
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.extend_object(base)
|
||||||
|
base.extend BaseMethods
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
register Sinatra::Namespace
|
||||||
|
Delegator.delegate :namespace
|
||||||
|
end
|
154
sinatra-contrib/lib/sinatra/respond_with.rb
Normal file
154
sinatra-contrib/lib/sinatra/respond_with.rb
Normal file
|
@ -0,0 +1,154 @@
|
||||||
|
require 'sinatra/base'
|
||||||
|
require 'json' unless String.method_defined? :to_json
|
||||||
|
|
||||||
|
module Sinatra
|
||||||
|
module RespondWith
|
||||||
|
class Format
|
||||||
|
def initialize(app)
|
||||||
|
@app, @map, @generic, @default = app, {}, {}, nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def on(type, &block)
|
||||||
|
@app.settings.mime_types(type).each do |mime|
|
||||||
|
case mime
|
||||||
|
when '*/*' then @default = block
|
||||||
|
when /^([^\/]+)\/\*$/ then @generic[$1] = block
|
||||||
|
else @map[mime] = block
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def finish
|
||||||
|
yield self if block_given?
|
||||||
|
mime_type = @app.content_type ||
|
||||||
|
@app.request.preferred_type(@map.keys) ||
|
||||||
|
@app.request.preferred_type ||
|
||||||
|
'text/html'
|
||||||
|
type = mime_type.split(/\s*;\s*/, 2).first
|
||||||
|
handlers = [@map[type], @generic[type[/^[^\/]+/]], @default].compact
|
||||||
|
handlers.each do |block|
|
||||||
|
if result = block.call(type)
|
||||||
|
@app.content_type mime_type
|
||||||
|
@app.halt result
|
||||||
|
end
|
||||||
|
end
|
||||||
|
@app.halt 406
|
||||||
|
end
|
||||||
|
|
||||||
|
def method_missing(meth, *args, &block)
|
||||||
|
return super if args.any? or block.nil? or not @app.mime_type(meth)
|
||||||
|
on(meth, &block)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
module Helpers
|
||||||
|
def respond_with(template, object = nil, &block)
|
||||||
|
object, template = template, nil unless Symbol === template
|
||||||
|
format = Format.new(self)
|
||||||
|
format.on "*/*" do |type|
|
||||||
|
exts = settings.ext_map[type]
|
||||||
|
exts << :xml if type.end_with? '+xml'
|
||||||
|
if template
|
||||||
|
args = template_cache.fetch(type, template) { template_for(template, exts) }
|
||||||
|
if args.any?
|
||||||
|
locals = { :object => object }
|
||||||
|
locals.merge! object.to_hash if object.respond_to? :to_hash
|
||||||
|
args << { :locals => locals }
|
||||||
|
halt send(*args)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if object
|
||||||
|
exts.each do |ext|
|
||||||
|
next unless meth = "to_#{ext}" and object.respond_to? meth
|
||||||
|
halt(*object.send(meth))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
false
|
||||||
|
end
|
||||||
|
format.finish(&block)
|
||||||
|
end
|
||||||
|
|
||||||
|
def respond_to(&block)
|
||||||
|
Format.new(self).finish(&block)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def template_for(name, exts)
|
||||||
|
# in production this is cached, so don't worry to much about runtime
|
||||||
|
possible = []
|
||||||
|
settings.template_engines[:all].each do |engine|
|
||||||
|
exts.each { |ext| possible << [engine, "#{name}.#{ext}"] }
|
||||||
|
end
|
||||||
|
exts.each do |ext|
|
||||||
|
settings.template_engines[ext].each { |e| possible << [e, name] }
|
||||||
|
end
|
||||||
|
possible.each do |engine, template|
|
||||||
|
find_template(settings.views, template, Tilt[engine]) do |file|
|
||||||
|
next unless File.exist? file
|
||||||
|
return settings.rendering_method(engine) << template.to_sym
|
||||||
|
end
|
||||||
|
end
|
||||||
|
[] # nil or false would not be cached
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
attr_accessor :ext_map
|
||||||
|
|
||||||
|
def remap_extensions
|
||||||
|
ext_map.clear
|
||||||
|
Rack::Mime::MIME_TYPES.each { |e,t| ext_map[t] << e[1..-1].to_sym }
|
||||||
|
ext_map['text/javascript'] << 'js'
|
||||||
|
ext_map['text/xml'] << 'xml'
|
||||||
|
end
|
||||||
|
|
||||||
|
def mime_type(*)
|
||||||
|
result = super
|
||||||
|
remap_extensions
|
||||||
|
result
|
||||||
|
end
|
||||||
|
|
||||||
|
def respond_to(*formats)
|
||||||
|
if formats.any?
|
||||||
|
@respond_to ||= []
|
||||||
|
@respond_to.concat formats
|
||||||
|
elsif @respond_to.nil? and superclass.respond_to? :respond_to
|
||||||
|
superclass.respond_to
|
||||||
|
else
|
||||||
|
@respond_to
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def rendering_method(engine)
|
||||||
|
return [engine] if Sinatra::Templates.method_defined? engine
|
||||||
|
return [:mab] if engine.to_sym == :markaby
|
||||||
|
[:render, :engine]
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def compile!(verb, path, block, options = {})
|
||||||
|
options[:provides] ||= respond_to if respond_to
|
||||||
|
super
|
||||||
|
end
|
||||||
|
|
||||||
|
ENGINES = {
|
||||||
|
:css => [:less, :sass, :scss],
|
||||||
|
:xml => [:builder, :nokogiri],
|
||||||
|
:js => [:coffee],
|
||||||
|
:html => [:erb, :erubis, :haml, :slim, :liquid, :radius, :mab, :markdown,
|
||||||
|
:textile, :rdoc],
|
||||||
|
:all => Sinatra::Templates.instance_methods.map(&:to_sym) + [:mab] -
|
||||||
|
[:find_template, :markaby]
|
||||||
|
}
|
||||||
|
|
||||||
|
ENGINES.default = []
|
||||||
|
|
||||||
|
def self.registered(base)
|
||||||
|
base.ext_map = Hash.new { |h,k| h[k] = [] }
|
||||||
|
base.set :template_engines, ENGINES.dup
|
||||||
|
base.remap_extensions
|
||||||
|
base.helpers Helpers
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
86
sinatra-contrib/lib/sinatra/test_helpers.rb
Normal file
86
sinatra-contrib/lib/sinatra/test_helpers.rb
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
require 'sinatra/base'
|
||||||
|
require 'rack/test'
|
||||||
|
require 'rack'
|
||||||
|
require 'forwardable'
|
||||||
|
|
||||||
|
module Sinatra
|
||||||
|
Base.set :environment, :test
|
||||||
|
|
||||||
|
module TestHelpers
|
||||||
|
class Session < Rack::Test::Session
|
||||||
|
def global_env
|
||||||
|
@global_env ||= {}
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def default_env
|
||||||
|
super.merge global_env
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
include Rack::Test::Methods
|
||||||
|
extend Forwardable
|
||||||
|
attr_accessor :settings
|
||||||
|
|
||||||
|
def_delegators :last_response, :body, :headers, :status, :errors
|
||||||
|
def_delegators :app, :configure, :set, :enable, :disable, :use, :helpers, :register
|
||||||
|
def_delegators :current_session, :env_for
|
||||||
|
|
||||||
|
def mock_app(base = Sinatra::Base, &block)
|
||||||
|
inner = nil
|
||||||
|
@app = Sinatra.new(base) do
|
||||||
|
inner = self
|
||||||
|
class_eval(&block)
|
||||||
|
end
|
||||||
|
@settings = inner
|
||||||
|
app
|
||||||
|
end
|
||||||
|
|
||||||
|
def app=(base)
|
||||||
|
@app = base
|
||||||
|
end
|
||||||
|
|
||||||
|
alias set_app app=
|
||||||
|
|
||||||
|
def app
|
||||||
|
@app ||= Class.new Sinatra::Base
|
||||||
|
Rack::Lint.new @app
|
||||||
|
end
|
||||||
|
|
||||||
|
unless method_defined? :options
|
||||||
|
def options(uri, params = {}, env = {}, &block)
|
||||||
|
env = env_for(uri, env.merge(:method => "OPTIONS", :params => params))
|
||||||
|
current_session.send(:process_request, uri, env, &block)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
unless method_defined? :patch
|
||||||
|
def patch(uri, params = {}, env = {}, &block)
|
||||||
|
env = env_for(uri, env.merge(:method => "PATCH", :params => params))
|
||||||
|
current_session.send(:process_request, uri, env, &block)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def last_request?
|
||||||
|
last_request
|
||||||
|
true
|
||||||
|
rescue Rack::Test::Error
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
def session
|
||||||
|
return {} unless last_request?
|
||||||
|
raise Rack::Test:Error, "session not enabled for app" unless last_env["rack.session"] or app.session?
|
||||||
|
last_request.session
|
||||||
|
end
|
||||||
|
|
||||||
|
def last_env
|
||||||
|
last_request.env
|
||||||
|
end
|
||||||
|
|
||||||
|
def build_rack_test_session(name) # :nodoc:
|
||||||
|
Session.new rack_mock_session(name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
17
sinatra-contrib/sinatra-contrib.gemspec
Normal file
17
sinatra-contrib/sinatra-contrib.gemspec
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
Gem::Specification.new do |s|
|
||||||
|
s.name = "sinatra-contrib"
|
||||||
|
s.version = "1.2.0"
|
||||||
|
s.description = "Collection of useful Sinatra extensions"
|
||||||
|
s.authors = ["Konstantin Haase"]
|
||||||
|
s.email = "konstantin.mailinglists@googlemail.com"
|
||||||
|
s.files = Dir["**/*.{rb,md}"] << "LICENSE"
|
||||||
|
s.has_rdoc = 'yard'
|
||||||
|
s.homepage = "http://github.com/rkh/#{s.name}"
|
||||||
|
s.require_paths = ["lib"]
|
||||||
|
s.summary = s.description
|
||||||
|
|
||||||
|
s.add_dependency "sinatra", "~> 1.2.2"
|
||||||
|
s.add_dependency "backports", ">= 2.0"
|
||||||
|
|
||||||
|
s.add_development_dependency "rspec", "~> 2.3"
|
||||||
|
end
|
6
sinatra-contrib/spec/config_file/key_value.yml
Normal file
6
sinatra-contrib/spec/config_file/key_value.yml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
foo: bar
|
||||||
|
something: 42
|
||||||
|
nested:
|
||||||
|
a: 1
|
||||||
|
b: 2
|
4
sinatra-contrib/spec/config_file/missing_env.yml
Normal file
4
sinatra-contrib/spec/config_file/missing_env.yml
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
foo:
|
||||||
|
production: 10
|
||||||
|
development: 20
|
7
sinatra-contrib/spec/config_file/with_envs.yml
Normal file
7
sinatra-contrib/spec/config_file/with_envs.yml
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
---
|
||||||
|
development:
|
||||||
|
foo: development
|
||||||
|
production:
|
||||||
|
foo: production
|
||||||
|
test:
|
||||||
|
foo: test
|
11
sinatra-contrib/spec/config_file/with_nested_envs.yml
Normal file
11
sinatra-contrib/spec/config_file/with_nested_envs.yml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
---
|
||||||
|
database:
|
||||||
|
production:
|
||||||
|
adapter: postgresql
|
||||||
|
database: foo_production
|
||||||
|
development:
|
||||||
|
adapter: sqlite
|
||||||
|
database: db/development.db
|
||||||
|
test:
|
||||||
|
adapter: sqlite
|
||||||
|
database: db/test.db
|
44
sinatra-contrib/spec/config_file_spec.rb
Normal file
44
sinatra-contrib/spec/config_file_spec.rb
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
require 'backports'
|
||||||
|
require_relative 'spec_helper'
|
||||||
|
|
||||||
|
describe Sinatra::ConfigFile do
|
||||||
|
def config_file(*args, &block)
|
||||||
|
mock_app do
|
||||||
|
register Sinatra::ConfigFile
|
||||||
|
set :root, File.expand_path('../config_file', __FILE__)
|
||||||
|
instance_eval(&block) if block
|
||||||
|
config_file(*args)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should set options from a simple config_file' do
|
||||||
|
config_file 'key_value.yml'
|
||||||
|
settings.foo.should == 'bar'
|
||||||
|
settings.something.should == 42
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should create indifferent hashes' do
|
||||||
|
config_file 'key_value.yml'
|
||||||
|
settings.nested['a'].should == 1
|
||||||
|
settings.nested[:a].should == 1
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should recognize env specific settings per file' do
|
||||||
|
config_file 'with_envs.yml'
|
||||||
|
settings.foo.should == 'test'
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should recognize env specific settings per setting' do
|
||||||
|
config_file 'with_nested_envs.yml'
|
||||||
|
settings.database[:adapter].should == 'sqlite'
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should not set present values to nil if the current env is missing' do
|
||||||
|
# first let's check the test is actually working properly
|
||||||
|
config_file('missing_env.yml') { set :foo => 42, :environment => :production }
|
||||||
|
settings.foo.should == 10
|
||||||
|
# now test it
|
||||||
|
config_file('missing_env.yml') { set :foo => 42, :environment => :test }
|
||||||
|
settings.foo.should == 42
|
||||||
|
end
|
||||||
|
end
|
42
sinatra-contrib/spec/decompile_spec.rb
Normal file
42
sinatra-contrib/spec/decompile_spec.rb
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
require 'backports'
|
||||||
|
require_relative 'spec_helper'
|
||||||
|
|
||||||
|
RSpec::Matchers.define :decompile do |path|
|
||||||
|
match do |app|
|
||||||
|
@compiled, @keys = app.send :compile, path
|
||||||
|
@decompiled = app.decompile(@compiled, @keys)
|
||||||
|
@decompiled.should == path
|
||||||
|
end
|
||||||
|
|
||||||
|
failure_message_for_should do |app|
|
||||||
|
values = [app, @compiled, @keys, path, @decompiled].map(&:inspect)
|
||||||
|
"expected %s to decompile %s with %s to %s, but was %s" % values
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe Sinatra::Decompile do
|
||||||
|
subject { Sinatra::Application }
|
||||||
|
it { should decompile("") }
|
||||||
|
it { should decompile("/") }
|
||||||
|
it { should decompile("/?") }
|
||||||
|
it { should decompile("/foo") }
|
||||||
|
it { should decompile("/:name") }
|
||||||
|
it { should decompile("/:name?") }
|
||||||
|
it { should decompile("/:foo/:bar") }
|
||||||
|
it { should decompile("/page/:id/edit") }
|
||||||
|
it { should decompile("/hello/*") }
|
||||||
|
it { should decompile("/*/foo/*") }
|
||||||
|
it { should decompile("*") }
|
||||||
|
it { should decompile(":name.:format") }
|
||||||
|
it { should decompile(/./) }
|
||||||
|
it { should decompile(/f(oo)/) }
|
||||||
|
it { should decompile(/ba+r/) }
|
||||||
|
|
||||||
|
it 'just returns strings' do
|
||||||
|
subject.decompile('/foo').should == '/foo'
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'just decompile simple regexps without keys' do
|
||||||
|
subject.decompile(%r{/foo}).should == '/foo'
|
||||||
|
end
|
||||||
|
end
|
33
sinatra-contrib/spec/extension_spec.rb
Normal file
33
sinatra-contrib/spec/extension_spec.rb
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
require 'backports'
|
||||||
|
require_relative 'spec_helper'
|
||||||
|
|
||||||
|
describe Sinatra::Extension do
|
||||||
|
module ExampleExtension
|
||||||
|
extend Sinatra::Extension
|
||||||
|
|
||||||
|
set :foo, :bar
|
||||||
|
settings.set :bar, :blah
|
||||||
|
|
||||||
|
configure :test, :production do
|
||||||
|
set :reload_stuff, false
|
||||||
|
end
|
||||||
|
|
||||||
|
configure :development do
|
||||||
|
set :reload_stuff, true
|
||||||
|
end
|
||||||
|
|
||||||
|
get '/' do
|
||||||
|
"from extension, yay"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
before { mock_app { register ExampleExtension }}
|
||||||
|
|
||||||
|
it('allows using set') { settings.foo.should == :bar }
|
||||||
|
it('implements configure') { settings.reload_stuff.should be_false }
|
||||||
|
|
||||||
|
it 'allows defing routes' do
|
||||||
|
get('/').should be_ok
|
||||||
|
body.should == "from extension, yay"
|
||||||
|
end
|
||||||
|
end
|
100
sinatra-contrib/spec/link_header_spec.rb
Normal file
100
sinatra-contrib/spec/link_header_spec.rb
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
require 'backports'
|
||||||
|
require_relative 'spec_helper'
|
||||||
|
|
||||||
|
describe Sinatra::LinkHeader do
|
||||||
|
before do
|
||||||
|
mock_app do
|
||||||
|
helpers Sinatra::LinkHeader
|
||||||
|
before('/') { link 'something', :rel => 'from-filter', :foo => :bar }
|
||||||
|
|
||||||
|
get '/' do
|
||||||
|
link :something, 'booyah'
|
||||||
|
end
|
||||||
|
|
||||||
|
get '/style' do
|
||||||
|
stylesheet '/style.css'
|
||||||
|
end
|
||||||
|
|
||||||
|
get '/prefetch' do
|
||||||
|
prefetch '/foo'
|
||||||
|
end
|
||||||
|
|
||||||
|
get '/link_headers' do
|
||||||
|
response['Link'] = "<foo> ;bar=\"baz\""
|
||||||
|
stylesheet '/style.css'
|
||||||
|
prefetch '/foo'
|
||||||
|
link_headers
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe :link do
|
||||||
|
it "sets link headers" do
|
||||||
|
get '/'
|
||||||
|
headers['Link'].lines.should include('<booyah>; rel="something"')
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns link html tags" do
|
||||||
|
get '/'
|
||||||
|
body.should == '<link href="booyah" rel="something" />'
|
||||||
|
end
|
||||||
|
|
||||||
|
it "takes an options hash" do
|
||||||
|
get '/'
|
||||||
|
elements = ["<something>", "foo=\"bar\"", "rel=\"from-filter\""]
|
||||||
|
headers['Link'].lines.first.strip.split('; ').sort.should == elements
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe :stylesheet do
|
||||||
|
it 'sets link headers' do
|
||||||
|
get '/style'
|
||||||
|
headers['Link'].should match(%r{^</style\.css>;})
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'sets type to text/css' do
|
||||||
|
get '/style'
|
||||||
|
headers['Link'].should include('type="text/css"')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'sets rel to stylesheet' do
|
||||||
|
get '/style'
|
||||||
|
headers['Link'].should include('rel="stylesheet"')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns html tag' do
|
||||||
|
get '/style'
|
||||||
|
body.should match(%r{^<link href="/style\.css"})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe :prefetch do
|
||||||
|
it 'sets link headers' do
|
||||||
|
get '/prefetch'
|
||||||
|
headers['Link'].should match(%r{^</foo>;})
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'sets rel to prefetch' do
|
||||||
|
get '/prefetch'
|
||||||
|
headers['Link'].should include('rel="prefetch"')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns html tag' do
|
||||||
|
get '/prefetch'
|
||||||
|
body.should == '<link href="/foo" rel="prefetch" />'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe :link_headers do
|
||||||
|
it 'generates html for all link headers' do
|
||||||
|
get '/link_headers'
|
||||||
|
body.should include('<link href="/foo" rel="prefetch" />')
|
||||||
|
body.should include('<link href="/style.css" ')
|
||||||
|
end
|
||||||
|
|
||||||
|
it "respects Link headers not generated on its own" do
|
||||||
|
get '/link_headers'
|
||||||
|
body.should include('<link href="foo" bar="baz" />')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
546
sinatra-contrib/spec/namespace_spec.rb
Normal file
546
sinatra-contrib/spec/namespace_spec.rb
Normal file
|
@ -0,0 +1,546 @@
|
||||||
|
require 'backports'
|
||||||
|
require_relative 'spec_helper'
|
||||||
|
|
||||||
|
describe Sinatra::Namespace do
|
||||||
|
verbs = [:get, :head, :post, :put, :delete, :options]
|
||||||
|
verbs << :patch if Sinatra::VERSION >= '1.3'
|
||||||
|
|
||||||
|
def mock_app(&block)
|
||||||
|
super do
|
||||||
|
register Sinatra::Namespace
|
||||||
|
class_eval(&block)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def namespace(*args, &block)
|
||||||
|
mock_app { namespace(*args, &block) }
|
||||||
|
end
|
||||||
|
|
||||||
|
verbs.each do |verb|
|
||||||
|
describe "HTTP #{verb.to_s.upcase}" do
|
||||||
|
describe 'pattern generation' do
|
||||||
|
it "should add routes including prefix to the base app" do
|
||||||
|
namespace("/foo") { send(verb, "/bar") { "baz" }}
|
||||||
|
send(verb, "/foo/bar").should be_ok
|
||||||
|
body.should == "baz" unless verb == :head
|
||||||
|
send(verb, "/foo/baz").should_not be_ok
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should allows adding routes with no path" do
|
||||||
|
namespace("/foo") { send(verb) { "bar" } }
|
||||||
|
send(verb, "/foo").should be_ok
|
||||||
|
body.should == "bar" unless verb == :head
|
||||||
|
end
|
||||||
|
|
||||||
|
it "allows unsing regular expressions" do
|
||||||
|
namespace("/foo") { send(verb, /\/\d\d/) { "bar" }}
|
||||||
|
send(verb, "/foo/12").should be_ok
|
||||||
|
body.should == "bar" unless verb == :head
|
||||||
|
send(verb, "/foo/123").should_not be_ok
|
||||||
|
end
|
||||||
|
|
||||||
|
it "allows using regular expressions for the prefix" do
|
||||||
|
namespace(/\/\d\d/) { send(verb, /\/\d\d/) { "foo" }}
|
||||||
|
send(verb, "/23/12").should be_ok
|
||||||
|
body.should == "foo" unless verb == :head
|
||||||
|
send(verb, "/123/12").should_not be_ok
|
||||||
|
end
|
||||||
|
|
||||||
|
it "sets params correctly from namespace" do
|
||||||
|
namespace("/:foo") { send(verb, "/bar") { params[:foo] }}
|
||||||
|
send(verb, "/foo/bar").should be_ok
|
||||||
|
body.should == "foo" unless verb == :head
|
||||||
|
send(verb, "/foo/baz").should_not be_ok
|
||||||
|
send(verb, "/fox/bar").should be_ok
|
||||||
|
body.should == "fox" unless verb == :head
|
||||||
|
end
|
||||||
|
|
||||||
|
it "sets params correctly from route" do
|
||||||
|
namespace("/foo") { send(verb, "/:bar") { params[:bar] }}
|
||||||
|
send(verb, "/foo/bar").should be_ok
|
||||||
|
body.should == "bar" unless verb == :head
|
||||||
|
send(verb, "/foo/baz").should be_ok
|
||||||
|
body.should == "baz" unless verb == :head
|
||||||
|
end
|
||||||
|
|
||||||
|
it "allows splats to be combined from namespace and route" do
|
||||||
|
namespace("/*") { send(verb, "/*") { params[:splat].join " - " }}
|
||||||
|
send(verb, '/foo/bar').should be_ok
|
||||||
|
body.should == "foo - bar" unless verb == :head
|
||||||
|
end
|
||||||
|
|
||||||
|
it "sets params correctly from namespace if simple regexp is used for route" do
|
||||||
|
namespace("/:foo") { send(verb, %r{/bar}) { params[:foo] }}
|
||||||
|
send(verb, "/foo/bar").should be_ok
|
||||||
|
body.should == "foo" unless verb == :head
|
||||||
|
send(verb, "/foo/baz").should_not be_ok
|
||||||
|
send(verb, "/fox/bar").should be_ok
|
||||||
|
body.should == "fox" unless verb == :head
|
||||||
|
end
|
||||||
|
|
||||||
|
it "sets params correctly from route if simple regexp is used for namespace" do
|
||||||
|
namespace(%r{/foo}) { send(verb, "/:bar") { params[:bar] }}
|
||||||
|
send(verb, "/foo/bar").should be_ok
|
||||||
|
body.should == "bar" unless verb == :head
|
||||||
|
send(verb, "/foo/baz").should be_ok
|
||||||
|
body.should == "baz" unless verb == :head
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'allows defining routes without a pattern' do
|
||||||
|
namespace(%r{/foo}) { send(verb) { 'bar' } }
|
||||||
|
send(verb, '/foo').should be_ok
|
||||||
|
body.should == 'bar' unless verb == :head
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'conditions' do
|
||||||
|
it 'allows using conditions for namespaces' do
|
||||||
|
mock_app do
|
||||||
|
namespace(:host_name => 'example.com') { send(verb) { 'yes' }}
|
||||||
|
send(verb, '/') { 'no' }
|
||||||
|
end
|
||||||
|
send(verb, '/', {}, 'HTTP_HOST' => 'example.com')
|
||||||
|
last_response.should be_ok
|
||||||
|
body.should == 'yes' unless verb == :head
|
||||||
|
send(verb, '/', {}, 'HTTP_HOST' => 'example.org')
|
||||||
|
last_response.should be_ok
|
||||||
|
body.should == 'no' unless verb == :head
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'allows using conditions for before filters' do
|
||||||
|
namespace '/foo' do
|
||||||
|
before(:host_name => 'example.com') { @yes = "yes" }
|
||||||
|
send(verb) { @yes || "no" }
|
||||||
|
end
|
||||||
|
send(verb, '/foo', {}, 'HTTP_HOST' => 'example.com')
|
||||||
|
last_response.should be_ok
|
||||||
|
body.should == 'yes' unless verb == :head
|
||||||
|
send(verb, '/foo', {}, 'HTTP_HOST' => 'example.org')
|
||||||
|
last_response.should be_ok
|
||||||
|
body.should == 'no' unless verb == :head
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'allows using conditions for after filters' do
|
||||||
|
ran = false
|
||||||
|
namespace '/foo' do
|
||||||
|
before(:host_name => 'example.com') { ran = true }
|
||||||
|
send(verb) { "ok" }
|
||||||
|
end
|
||||||
|
send(verb, '/foo', {}, 'HTTP_HOST' => 'example.org')
|
||||||
|
ran.should be_false
|
||||||
|
send(verb, '/foo', {}, 'HTTP_HOST' => 'example.com')
|
||||||
|
ran.should be_true
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'allows using conditions for routes' do
|
||||||
|
namespace '/foo' do
|
||||||
|
send(verb, :host_name => 'example.com') { "ok" }
|
||||||
|
end
|
||||||
|
send(verb, '/foo', {}, 'HTTP_HOST' => 'example.com').should be_ok
|
||||||
|
send(verb, '/foo', {}, 'HTTP_HOST' => 'example.org').should_not be_ok
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'allows using conditions for before filters and the namespace' do
|
||||||
|
ran = false
|
||||||
|
namespace '/', :provides => :txt do
|
||||||
|
before(:host_name => 'example.com') { ran = true }
|
||||||
|
send(verb) { "ok" }
|
||||||
|
end
|
||||||
|
send(verb, '/', {}, 'HTTP_HOST' => 'example.org', 'HTTP_ACCEPT' => 'text/plain')
|
||||||
|
ran.should be_false
|
||||||
|
send(verb, '/', {}, 'HTTP_HOST' => 'example.com', 'HTTP_ACCEPT' => 'text/html')
|
||||||
|
ran.should be_false
|
||||||
|
send(verb, '/', {}, 'HTTP_HOST' => 'example.com', 'HTTP_ACCEPT' => 'text/plain')
|
||||||
|
ran.should be_true
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'allows using conditions for routes and the namespace' do
|
||||||
|
namespace '/foo', :host_name => 'example.com' do
|
||||||
|
send(verb, :provides => :txt) { "ok" }
|
||||||
|
end
|
||||||
|
send(verb, '/foo', {}, 'HTTP_HOST' => 'example.com', 'HTTP_ACCEPT' => 'text/plain').should be_ok
|
||||||
|
send(verb, '/foo', {}, 'HTTP_HOST' => 'example.com', 'HTTP_ACCEPT' => 'text/html').should_not be_ok
|
||||||
|
send(verb, '/foo', {}, 'HTTP_HOST' => 'example.org', 'HTTP_ACCEPT' => 'text/plain').should_not be_ok
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'allows combining conditions with a prefix for namespaces' do
|
||||||
|
namespace '/', :host_name => 'example.com' do
|
||||||
|
send(verb) { "ok" }
|
||||||
|
end
|
||||||
|
send(verb, '/', {}, 'HTTP_HOST' => 'example.com').should be_ok
|
||||||
|
send(verb, '/', {}, 'HTTP_HOST' => 'example.org').should_not be_ok
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'allows combining conditions with a prefix for before filters' do
|
||||||
|
ran = false
|
||||||
|
namespace :provides => :txt do
|
||||||
|
before('/foo', :host_name => 'example.com') { ran = true }
|
||||||
|
send(verb, '/*') { "ok" }
|
||||||
|
end
|
||||||
|
send(verb, '/foo', {}, 'HTTP_HOST' => 'example.org', 'HTTP_ACCEPT' => 'text/plain')
|
||||||
|
ran.should be_false
|
||||||
|
send(verb, '/foo', {}, 'HTTP_HOST' => 'example.com', 'HTTP_ACCEPT' => 'text/html')
|
||||||
|
ran.should be_false
|
||||||
|
send(verb, '/bar', {}, 'HTTP_HOST' => 'example.com', 'HTTP_ACCEPT' => 'text/plain')
|
||||||
|
ran.should be_false
|
||||||
|
send(verb, '/foo', {}, 'HTTP_HOST' => 'example.com', 'HTTP_ACCEPT' => 'text/plain')
|
||||||
|
ran.should be_true
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'allows combining conditions with a prefix for after filters' do
|
||||||
|
ran = false
|
||||||
|
namespace :provides => :txt do
|
||||||
|
after('/foo', :host_name => 'example.com') { ran = true }
|
||||||
|
send(verb, '/*') { "ok" }
|
||||||
|
end
|
||||||
|
send(verb, '/foo', {}, 'HTTP_HOST' => 'example.org', 'HTTP_ACCEPT' => 'text/plain')
|
||||||
|
ran.should be_false
|
||||||
|
send(verb, '/foo', {}, 'HTTP_HOST' => 'example.com', 'HTTP_ACCEPT' => 'text/html')
|
||||||
|
ran.should be_false
|
||||||
|
send(verb, '/bar', {}, 'HTTP_HOST' => 'example.com', 'HTTP_ACCEPT' => 'text/plain')
|
||||||
|
ran.should be_false
|
||||||
|
send(verb, '/foo', {}, 'HTTP_HOST' => 'example.com', 'HTTP_ACCEPT' => 'text/plain')
|
||||||
|
ran.should be_true
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'allows combining conditions with a prefix for routes' do
|
||||||
|
namespace :host_name => 'example.com' do
|
||||||
|
send(verb, '/foo', :provides => :txt) { "ok" }
|
||||||
|
end
|
||||||
|
send(verb, '/foo', {}, 'HTTP_HOST' => 'example.com', 'HTTP_ACCEPT' => 'text/plain').should be_ok
|
||||||
|
send(verb, '/foo', {}, 'HTTP_HOST' => 'example.com', 'HTTP_ACCEPT' => 'text/html').should_not be_ok
|
||||||
|
send(verb, '/foo', {}, 'HTTP_HOST' => 'example.org', 'HTTP_ACCEPT' => 'text/plain').should_not be_ok
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'allows combining conditions with a prefix for filters and the namespace' do
|
||||||
|
ran = false
|
||||||
|
namespace '/f', :provides => :txt do
|
||||||
|
before('oo', :host_name => 'example.com') { ran = true }
|
||||||
|
send(verb, '/*') { "ok" }
|
||||||
|
end
|
||||||
|
send(verb, '/foo', {}, 'HTTP_HOST' => 'example.org', 'HTTP_ACCEPT' => 'text/plain')
|
||||||
|
ran.should be_false
|
||||||
|
send(verb, '/foo', {}, 'HTTP_HOST' => 'example.com', 'HTTP_ACCEPT' => 'text/html')
|
||||||
|
ran.should be_false
|
||||||
|
send(verb, '/far', {}, 'HTTP_HOST' => 'example.com', 'HTTP_ACCEPT' => 'text/plain')
|
||||||
|
ran.should be_false
|
||||||
|
send(verb, '/foo', {}, 'HTTP_HOST' => 'example.com', 'HTTP_ACCEPT' => 'text/plain')
|
||||||
|
ran.should be_true
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'allows combining conditions with a prefix for routes and the namespace' do
|
||||||
|
namespace '/f', :host_name => 'example.com' do
|
||||||
|
send(verb, 'oo', :provides => :txt) { "ok" }
|
||||||
|
end
|
||||||
|
send(verb, '/foo', {}, 'HTTP_HOST' => 'example.com', 'HTTP_ACCEPT' => 'text/plain').should be_ok
|
||||||
|
send(verb, '/foo', {}, 'HTTP_HOST' => 'example.com', 'HTTP_ACCEPT' => 'text/html').should_not be_ok
|
||||||
|
send(verb, '/foo', {}, 'HTTP_HOST' => 'example.org', 'HTTP_ACCEPT' => 'text/plain').should_not be_ok
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'filters' do
|
||||||
|
it 'should trigger before filters for namespaces' do
|
||||||
|
ran = false
|
||||||
|
namespace('/foo') { before { ran = true }}
|
||||||
|
send(verb, '/foo')
|
||||||
|
ran.should be_true
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should trigger after filters for namespaces' do
|
||||||
|
ran = false
|
||||||
|
namespace('/foo') { after { ran = true }}
|
||||||
|
send(verb, '/foo')
|
||||||
|
ran.should be_true
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should not trigger before filter for different namespaces' do
|
||||||
|
ran = false
|
||||||
|
namespace('/foo') { before { ran = true }}
|
||||||
|
send(verb, '/fox')
|
||||||
|
ran.should be_false
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should not trigger after filter for different namespaces' do
|
||||||
|
ran = false
|
||||||
|
namespace('/foo') { after { ran = true }}
|
||||||
|
send(verb, '/fox')
|
||||||
|
ran.should be_false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'helpers' do
|
||||||
|
it "allows defining helpers with the helpers method" do
|
||||||
|
namespace '/foo' do
|
||||||
|
helpers do
|
||||||
|
def magic
|
||||||
|
42
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
send verb, '/bar' do
|
||||||
|
magic.to_s
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
send(verb, '/foo/bar').should be_ok
|
||||||
|
body.should == '42' unless verb == :head
|
||||||
|
end
|
||||||
|
|
||||||
|
it "allows defining helpers without the helpers method" do
|
||||||
|
namespace '/foo' do
|
||||||
|
def magic
|
||||||
|
42
|
||||||
|
end
|
||||||
|
|
||||||
|
send verb, '/bar' do
|
||||||
|
magic.to_s
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
send(verb, '/foo/bar').should be_ok
|
||||||
|
body.should == '42' unless verb == :head
|
||||||
|
end
|
||||||
|
|
||||||
|
it "allows using helper mixins with the helpers method" do
|
||||||
|
mixin = Module.new do
|
||||||
|
def magic
|
||||||
|
42
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
namespace '/foo' do
|
||||||
|
helpers mixin
|
||||||
|
send verb, '/bar' do
|
||||||
|
magic.to_s
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
send(verb, '/foo/bar').should be_ok
|
||||||
|
body.should == '42' unless verb == :head
|
||||||
|
end
|
||||||
|
|
||||||
|
it "makes helpers defined inside a namespace not available to routes outside that namespace" do
|
||||||
|
mock_app do
|
||||||
|
namespace '/foo' do
|
||||||
|
def magic
|
||||||
|
42
|
||||||
|
end
|
||||||
|
|
||||||
|
send verb, '/bar' do
|
||||||
|
magic.to_s
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
send verb, '/' do
|
||||||
|
magic.to_s
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
proc { send verb, '/' }.should raise_error(NameError)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "makes helper mixins used inside a namespace not available to routes outside that namespace" do
|
||||||
|
mixin = Module.new do
|
||||||
|
def magic
|
||||||
|
42
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
mock_app do
|
||||||
|
namespace '/foo' do
|
||||||
|
helpers mixin
|
||||||
|
send verb, '/bar' do
|
||||||
|
magic.to_s
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
send verb, '/' do
|
||||||
|
magic.to_s
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
proc { send verb, '/' }.should raise_error(NameError)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "allows accessing helpers defined outside the namespace" do
|
||||||
|
mock_app do
|
||||||
|
helpers do
|
||||||
|
def magic
|
||||||
|
42
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
namespace '/foo' do
|
||||||
|
send verb, '/bar' do
|
||||||
|
magic.to_s
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
send(verb, '/foo/bar').should be_ok
|
||||||
|
body.should == '42' unless verb == :head
|
||||||
|
end
|
||||||
|
|
||||||
|
it "allows calling super in helpers overwritten inside a namespace" do
|
||||||
|
mock_app do
|
||||||
|
helpers do
|
||||||
|
def magic
|
||||||
|
42
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
namespace '/foo' do
|
||||||
|
def magic
|
||||||
|
super - 19
|
||||||
|
end
|
||||||
|
|
||||||
|
send verb, '/bar' do
|
||||||
|
magic.to_s
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
send(verb, '/foo/bar').should be_ok
|
||||||
|
body.should == '23' unless verb == :head
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'nesting' do
|
||||||
|
it 'routes to nested namespaces' do
|
||||||
|
namespace '/foo' do
|
||||||
|
namespace '/bar' do
|
||||||
|
send(verb, '/baz') { 'OKAY!!11!'}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
send(verb, '/foo/bar/baz').should be_ok
|
||||||
|
body.should == 'OKAY!!11!' unless verb == :head
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'exposes helpers to nested namespaces' do
|
||||||
|
namespace '/foo' do
|
||||||
|
helpers do
|
||||||
|
def magic
|
||||||
|
42
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
namespace '/bar' do
|
||||||
|
send verb, '/baz' do
|
||||||
|
magic.to_s
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
send(verb, '/foo/bar/baz').should be_ok
|
||||||
|
body.should == '42' unless verb == :head
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not use helpers of nested namespaces outside that namespace' do
|
||||||
|
namespace '/foo' do
|
||||||
|
namespace '/bar' do
|
||||||
|
def magic
|
||||||
|
42
|
||||||
|
end
|
||||||
|
|
||||||
|
send verb, '/baz' do
|
||||||
|
magic.to_s
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
send verb do
|
||||||
|
magic.to_s
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
proc { send verb, '/foo' }.should raise_error(NameError)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'sets params correctly' do
|
||||||
|
namespace('/:a') { namespace('/:b') { send(verb) { params[:a] }}}
|
||||||
|
send(verb, '/foo/bar').should be_ok
|
||||||
|
body.should == 'foo' unless verb == :head
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'error handlers' do
|
||||||
|
it "should allow custom error handlers with not found" do
|
||||||
|
namespace('/de') do
|
||||||
|
not_found { 'nicht gefunden' }
|
||||||
|
end
|
||||||
|
send(verb, '/foo').status.should == 404
|
||||||
|
last_response.body.should_not == 'nicht gefunden' unless verb == :head
|
||||||
|
get('/en/foo').status.should == 404
|
||||||
|
last_response.body.should_not == 'nicht gefunden' unless verb == :head
|
||||||
|
get('/de/foo').status.should == 404
|
||||||
|
last_response.body.should == 'nicht gefunden' unless verb == :head
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should allow custom error handlers with error" do
|
||||||
|
namespace('/de') do
|
||||||
|
error(404) { 'nicht gefunden' }
|
||||||
|
end
|
||||||
|
send(verb, '/foo').status.should == 404
|
||||||
|
last_response.body.should_not == 'nicht gefunden' unless verb == :head
|
||||||
|
get('/en/foo').status.should == 404
|
||||||
|
last_response.body.should_not == 'nicht gefunden' unless verb == :head
|
||||||
|
get('/de/foo').status.should == 404
|
||||||
|
last_response.body.should == 'nicht gefunden' unless verb == :head
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'templates' do
|
||||||
|
it "allows to define nested templates"
|
||||||
|
it "allows to define nested layouts"
|
||||||
|
it "allows setting a different views directory"
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'extensions' do
|
||||||
|
it 'allows read access to settings' do
|
||||||
|
value = nil
|
||||||
|
mock_app do
|
||||||
|
set :foo, 42
|
||||||
|
namespace '/foo' do
|
||||||
|
value = foo
|
||||||
|
end
|
||||||
|
end
|
||||||
|
value.should == 42
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'allows registering extensions for a namespace only' do
|
||||||
|
a = b = nil
|
||||||
|
extension = Module.new { define_method(:views) { "CUSTOM!!!" } }
|
||||||
|
mock_app do
|
||||||
|
namespace '/' do
|
||||||
|
register extension
|
||||||
|
a = views
|
||||||
|
end
|
||||||
|
b = views
|
||||||
|
end
|
||||||
|
a.should == 'CUSTOM!!!'
|
||||||
|
b.should_not == 'CUSTOM!!!'
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'triggers route_added hook' do
|
||||||
|
route = nil
|
||||||
|
extension = Module.new
|
||||||
|
extension.singleton_class.class_eval do
|
||||||
|
define_method(:route_added) { |*r| route = r }
|
||||||
|
end
|
||||||
|
mock_app do
|
||||||
|
namespace '/f' do
|
||||||
|
register extension
|
||||||
|
get('oo') { }
|
||||||
|
end
|
||||||
|
get('/bar') { }
|
||||||
|
end
|
||||||
|
route[1].should == '/foo'
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'prevents changing app global settings' do
|
||||||
|
proc { namespace('/') { set :foo, :bar }}.should raise_error
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
1
sinatra-contrib/spec/respond_with/bar.erb
Normal file
1
sinatra-contrib/spec/respond_with/bar.erb
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Girl! I wanna take you to a ... bar!
|
1
sinatra-contrib/spec/respond_with/bar.json.erb
Normal file
1
sinatra-contrib/spec/respond_with/bar.json.erb
Normal file
|
@ -0,0 +1 @@
|
||||||
|
json!
|
1
sinatra-contrib/spec/respond_with/foo.html.erb
Normal file
1
sinatra-contrib/spec/respond_with/foo.html.erb
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Hello <%= name %>!
|
2
sinatra-contrib/spec/respond_with/not_html.sass
Normal file
2
sinatra-contrib/spec/respond_with/not_html.sass
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
body
|
||||||
|
color: red
|
280
sinatra-contrib/spec/respond_with_spec.rb
Normal file
280
sinatra-contrib/spec/respond_with_spec.rb
Normal file
|
@ -0,0 +1,280 @@
|
||||||
|
require 'backports'
|
||||||
|
require_relative 'spec_helper'
|
||||||
|
|
||||||
|
describe Sinatra::RespondWith do
|
||||||
|
def provides(*args)
|
||||||
|
@provides = args
|
||||||
|
end
|
||||||
|
|
||||||
|
def respond_app(&block)
|
||||||
|
types = @provides
|
||||||
|
mock_app do
|
||||||
|
set :app_file, __FILE__
|
||||||
|
set :views, root + '/respond_with'
|
||||||
|
register Sinatra::RespondWith
|
||||||
|
respond_to(*types) if types
|
||||||
|
class_eval(&block)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def respond_to(*args, &block)
|
||||||
|
respond_app { get('/') { respond_to(*args, &block) } }
|
||||||
|
end
|
||||||
|
|
||||||
|
def respond_with(*args, &block)
|
||||||
|
respond_app { get('/') { respond_with(*args, &block) } }
|
||||||
|
end
|
||||||
|
|
||||||
|
def req(*types)
|
||||||
|
p = types.shift if types.first.is_a? String and types.first.start_with? '/'
|
||||||
|
accept = types.map { |t| Sinatra::Base.mime_type(t).to_s }.join ','
|
||||||
|
get (p || '/'), {}, 'HTTP_ACCEPT' => accept
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "Helpers#respond_to" do
|
||||||
|
it 'allows defining handlers by file extensions' do
|
||||||
|
respond_to do |format|
|
||||||
|
format.html { "html!" }
|
||||||
|
format.json { "json!" }
|
||||||
|
end
|
||||||
|
|
||||||
|
req(:html).body.should == "html!"
|
||||||
|
req(:json).body.should == "json!"
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'respects quality' do
|
||||||
|
respond_to do |format|
|
||||||
|
format.html { "html!" }
|
||||||
|
format.json { "json!" }
|
||||||
|
end
|
||||||
|
|
||||||
|
req("text/html;q=0.7, application/json;q=0.3").body.should == "html!"
|
||||||
|
req("text/html;q=0.3, application/json;q=0.7").body.should == "json!"
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'allows using mime types' do
|
||||||
|
respond_to do |format|
|
||||||
|
format.on('text/html') { "html!" }
|
||||||
|
format.json { "json!" }
|
||||||
|
end
|
||||||
|
|
||||||
|
req(:html).body.should == "html!"
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'allows using wildcards in format matchers' do
|
||||||
|
respond_to do |format|
|
||||||
|
format.on('text/*') { "text!" }
|
||||||
|
format.json { "json!" }
|
||||||
|
end
|
||||||
|
|
||||||
|
req(:html).body.should == "text!"
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'allows using catch all wildcards in format matchers' do
|
||||||
|
respond_to do |format|
|
||||||
|
format.on('*/*') { "anything!" }
|
||||||
|
format.json { "json!" }
|
||||||
|
end
|
||||||
|
|
||||||
|
req(:html).body.should == "anything!"
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'prefers concret over generic' do
|
||||||
|
respond_to do |format|
|
||||||
|
format.on('text/*') { "text!" }
|
||||||
|
format.on('*/*') { "anything!" }
|
||||||
|
format.json { "json!" }
|
||||||
|
end
|
||||||
|
|
||||||
|
req(:json).body.should == "json!"
|
||||||
|
req(:html).body.should == "text!"
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not set up default handlers' do
|
||||||
|
respond_to
|
||||||
|
req.should_not be_ok
|
||||||
|
status.should == 406
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "Helpers#respond_with" do
|
||||||
|
describe "matching" do
|
||||||
|
it 'allows defining handlers by file extensions' do
|
||||||
|
respond_with(:ignore) do |format|
|
||||||
|
format.html { "html!" }
|
||||||
|
format.json { "json!" }
|
||||||
|
end
|
||||||
|
|
||||||
|
req(:html).body.should == "html!"
|
||||||
|
req(:json).body.should == "json!"
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'respects quality' do
|
||||||
|
respond_with(:ignore) do |format|
|
||||||
|
format.html { "html!" }
|
||||||
|
format.json { "json!" }
|
||||||
|
end
|
||||||
|
|
||||||
|
req("text/html;q=0.7, application/json;q=0.3").body.should == "html!"
|
||||||
|
req("text/html;q=0.3, application/json;q=0.7").body.should == "json!"
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'allows using mime types' do
|
||||||
|
respond_with(:ignore) do |format|
|
||||||
|
format.on('text/html') { "html!" }
|
||||||
|
format.json { "json!" }
|
||||||
|
end
|
||||||
|
|
||||||
|
req(:html).body.should == "html!"
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'allows using wildcards in format matchers' do
|
||||||
|
respond_with(:ignore) do |format|
|
||||||
|
format.on('text/*') { "text!" }
|
||||||
|
format.json { "json!" }
|
||||||
|
end
|
||||||
|
|
||||||
|
req(:html).body.should == "text!"
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'allows using catch all wildcards in format matchers' do
|
||||||
|
respond_with(:ignore) do |format|
|
||||||
|
format.on('*/*') { "anything!" }
|
||||||
|
format.json { "json!" }
|
||||||
|
end
|
||||||
|
|
||||||
|
req(:html).body.should == "anything!"
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'prefers concret over generic' do
|
||||||
|
respond_with(:ignore) do |format|
|
||||||
|
format.on('text/*') { "text!" }
|
||||||
|
format.on('*/*') { "anything!" }
|
||||||
|
format.json { "json!" }
|
||||||
|
end
|
||||||
|
|
||||||
|
req(:json).body.should == "json!"
|
||||||
|
req(:html).body.should == "text!"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "default behavior" do
|
||||||
|
it 'converts objects to json out of the box' do
|
||||||
|
respond_with 'a' => 'b'
|
||||||
|
req(:json).body.should == {'a' => 'b'}.to_json
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'handles multiple routes correctly' do
|
||||||
|
respond_app do
|
||||||
|
get('/') { respond_with 'a' => 'b' }
|
||||||
|
get('/:name') { respond_with 'a' => params[:name] }
|
||||||
|
end
|
||||||
|
req('/', :json).body.should == {'a' => 'b'}.to_json
|
||||||
|
req('/b', :json).body.should == {'a' => 'b'}.to_json
|
||||||
|
req('/c', :json).body.should == {'a' => 'c'}.to_json
|
||||||
|
end
|
||||||
|
|
||||||
|
it "calls to_EXT if available" do
|
||||||
|
respond_with Struct.new(:to_pdf).new("hello")
|
||||||
|
req(:pdf).body.should == "hello"
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'results in a 406 if format cannot be produced' do
|
||||||
|
respond_with({})
|
||||||
|
req(:html).should_not be_ok
|
||||||
|
status.should == 406
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'templates' do
|
||||||
|
it 'looks for templates with name.target.engine' do
|
||||||
|
respond_with :foo, :name => 'World'
|
||||||
|
req(:html).should be_ok
|
||||||
|
body.should == "Hello World!"
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'looks for templates with name.engine for specific engines' do
|
||||||
|
respond_with :bar
|
||||||
|
req(:html).should be_ok
|
||||||
|
body.should == "Girl! I wanna take you to a ... bar!"
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not use name.engine for engines producing other formats' do
|
||||||
|
respond_with :not_html
|
||||||
|
req(:html).should_not be_ok
|
||||||
|
status.should == 406
|
||||||
|
body.should be_empty
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'falls back to to_EXT if no template is found' do
|
||||||
|
respond_with :foo, :name => 'World'
|
||||||
|
req(:json).should be_ok
|
||||||
|
body.should == {:name => 'World'}.to_json
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'favors templates over to_EXT' do
|
||||||
|
respond_with :bar, :name => 'World'
|
||||||
|
req(:json).should be_ok
|
||||||
|
body.should == 'json!'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'customizing' do
|
||||||
|
it 'allows customizing' do
|
||||||
|
respond_with(:foo, :name => 'World') { |f| f.html { 'html!' }}
|
||||||
|
req(:html).should be_ok
|
||||||
|
body.should == "html!"
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'falls back to default behavior if none matches' do
|
||||||
|
respond_with(:foo, :name => 'World') { |f| f.json { 'json!' }}
|
||||||
|
req(:html).should be_ok
|
||||||
|
body.should == "Hello World!"
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'favors generic rule over default behavior' do
|
||||||
|
respond_with(:foo, :name => 'World') { |f| f.on('*/*') { 'generic!' }}
|
||||||
|
req(:html).should be_ok
|
||||||
|
body.should == "generic!"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe :respond_to do
|
||||||
|
it 'acts as global provides condition' do
|
||||||
|
respond_app do
|
||||||
|
respond_to :json, :html
|
||||||
|
get('/a') { 'ok' }
|
||||||
|
get('/b') { 'ok' }
|
||||||
|
end
|
||||||
|
|
||||||
|
req('/b', :xml).should_not be_ok
|
||||||
|
req('/b', :html).should be_ok
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'still allows provides' do
|
||||||
|
respond_app do
|
||||||
|
respond_to :json, :html
|
||||||
|
get('/a') { 'ok' }
|
||||||
|
get('/b', :provides => :json) { 'ok' }
|
||||||
|
end
|
||||||
|
|
||||||
|
req('/b', :html).should_not be_ok
|
||||||
|
req('/b', :json).should be_ok
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'plays well with namespaces' do
|
||||||
|
respond_app do
|
||||||
|
register Sinatra::Namespace
|
||||||
|
namespace '/a' do
|
||||||
|
respond_to :json
|
||||||
|
get { 'json' }
|
||||||
|
end
|
||||||
|
get('/b') { 'anything' }
|
||||||
|
end
|
||||||
|
|
||||||
|
req('/a', :html).should_not be_ok
|
||||||
|
req('/b', :html).should be_ok
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
6
sinatra-contrib/spec/spec_helper.rb
Normal file
6
sinatra-contrib/spec/spec_helper.rb
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
require 'sinatra/contrib'
|
||||||
|
|
||||||
|
RSpec.configure do |config|
|
||||||
|
config.expect_with :rspec, :stdlib
|
||||||
|
config.include Sinatra::TestHelpers
|
||||||
|
end
|
Loading…
Add table
Reference in a new issue