sinatra-contrib-1.4.7/0000755000004100000410000000000012706205017014652 5ustar www-datawww-datasinatra-contrib-1.4.7/Rakefile0000644000004100000410000000402212706205017016315 0ustar www-datawww-data$LOAD_PATH.unshift File.expand_path('../lib', __FILE__) require 'open-uri' require 'yaml' require 'sinatra/contrib/version' desc "run specs" task(:spec) { ruby '-S rspec spec -cw' } task(:test => :spec) task(:default => :spec) namespace :doc do task :readmes do Dir.glob 'lib/sinatra/*.rb' do |file| excluded_files = %w[lib/sinatra/contrib.rb lib/sinatra/capture.rb lib/sinatra/engine_tracking.rb] next if excluded_files.include?(file) doc = File.read(file)[/^module Sinatra(\n)+( #[^\n]*\n)*/m].scan(/^ *#(?!#) ?(.*)\n/).join("\n") file = "doc/#{file[4..-4].tr("/_", "-")}.rdoc" Dir.mkdir "doc" unless File.directory? "doc" puts "writing #{file}" File.open(file, "w") { |f| f << doc } end end task :all => [:readmes] end desc "generate documentation" task :doc => 'doc:all' desc "generate gemspec" task 'sinatra-contrib.gemspec' do content = File.read 'sinatra-contrib.gemspec' fields = { :authors => `git shortlog -sn`.scan(/[^\d\s].*/), :email => `git shortlog -sne`.scan(/[^<]+@[^>]+/), :files => `git ls-files`.split("\n").reject { |f| f =~ /^(\.|Gemfile)/ } } fields.each do |field, values| updated = " s.#{field} = [" updated << values.map { |v| "\n %p" % v }.join(',') updated << "\n ]" content.sub!(/ s\.#{field} = \[\n( .*\n)* \]/, updated) end File.open('sinatra-contrib.gemspec', 'w') { |f| f << content } end task :gemspec => 'sinatra-contrib.gemspec' task :release => :gemspec do sh <<-SH rm -Rf sinatra-contrib*.gem && gem build sinatra-contrib.gemspec && gem install sinatra-contrib*.gem --local && gem push sinatra-contrib*.gem && git commit --allow-empty -a -m '#{Sinatra::Contrib::VERSION} release' && git tag -s v#{Sinatra::Contrib::VERSION} -m '#{Sinatra::Contrib::VERSION} release' && git tag -s #{Sinatra::Contrib::VERSION} -m '#{Sinatra::Contrib::VERSION} release' && git push && (git push origin master || true) && git push --tags && (git push origin --tags || true) SH end sinatra-contrib-1.4.7/ideas.md0000644000004100000410000000175012706205017016264 0ustar www-datawww-data* Extension that does something like this: def build(*) if settings.memcached? use Rack::Cache, :backend => :memcached use Rack::Session::Memcached # ... end super end * `sinatra-smart-cache`: update cache header only if arguments are more restrictive than curent value, set caching headers that way for most helper methods (i.e. `sass` or `send_file`) * Some verbose logging extension: Log what filters, routes, error handlers, templates, and so on is used. * Form helpers, with forms as first class objects that accepts hashes or something, so the form meta data can also be used to expose a JSON API or similar, possibly defining routes (like "Sinatra's Hat"), strictly using the ActiveModel API. * Extend `sinatra-content-for` to support Liquid, Radius, Markaby, Nokogiri and Builder. At least the first two probably involve patching Tilt. * Rewrite of `sinatra-compass`? * Helpers for HTML escaping and such.sinatra-contrib-1.4.7/spec/0000755000004100000410000000000012706205017015604 5ustar www-datawww-datasinatra-contrib-1.4.7/spec/decompile_spec.rb0000644000004100000410000000240312706205017021103 0ustar www-datawww-datarequire '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("a b") } it { should decompile("a+b") } 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 sinatra-contrib-1.4.7/spec/link_header_spec.rb0000644000004100000410000000440112706205017021407 0ustar www-datawww-datarequire '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'] = " ;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('; rel="something"') end it "returns link html tags" do get '/' body.should == '' end it "takes an options hash" do get '/' elements = ["", "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{^;}) 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{^;}) 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 == '' end end describe :link_headers do it 'generates html for all link headers' do get '/link_headers' body.should include('') body.should include('') end end end sinatra-contrib-1.4.7/spec/spec_helper.rb0000644000004100000410000000022612706205017020422 0ustar www-datawww-dataENV['RACK_ENV'] = 'test' require 'sinatra/contrib' RSpec.configure do |config| config.expect_with :rspec config.include Sinatra::TestHelpers end sinatra-contrib-1.4.7/spec/namespace/0000755000004100000410000000000012706205017017540 5ustar www-datawww-datasinatra-contrib-1.4.7/spec/namespace/nested/0000755000004100000410000000000012706205017021022 5ustar www-datawww-datasinatra-contrib-1.4.7/spec/namespace/nested/foo.erb0000644000004100000410000000000312706205017022270 0ustar www-datawww-dataho sinatra-contrib-1.4.7/spec/namespace/foo.erb0000644000004100000410000000000312706205017021006 0ustar www-datawww-datahi sinatra-contrib-1.4.7/spec/multi_route_spec.rb0000644000004100000410000000231012706205017021507 0ustar www-datawww-datarequire 'spec_helper' describe Sinatra::MultiRoute do it 'does not break normal routing' do mock_app do register Sinatra::MultiRoute get('/') { 'normal' } end get('/').should be_ok body.should be == 'normal' end it 'supports multiple routes' do mock_app do register Sinatra::MultiRoute get('/foo', '/bar') { 'paths' } end get('/foo').should be_ok body.should be == 'paths' get('/bar').should be_ok body.should be == 'paths' end it 'triggers conditions' do count = 0 mock_app do register Sinatra::MultiRoute set(:some_condition) { |_| count += 1 } get('/foo', '/bar', :some_condition => true) { 'paths' } end count.should be == 4 end it 'supports multiple verbs' do mock_app do register Sinatra::MultiRoute route('PUT', 'POST', '/') { 'verb' } end post('/').should be_ok body.should be == 'verb' put('/').should be_ok body.should be == 'verb' end it 'takes symbols as verbs' do mock_app do register Sinatra::MultiRoute route(:get, '/baz') { 'symbol as verb' } end get('/baz').should be_ok body.should be == 'symbol as verb' end end sinatra-contrib-1.4.7/spec/config_file_spec.rb0000644000004100000410000000347512706205017021420 0ustar www-datawww-datarequire '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 render options in ERB tags' do config_file 'key_value.yml.erb' settings.foo.should == "bar" settings.something.should == 42 settings.nested['a'].should == 1 settings.nested[:a].should == 1 settings.nested['b'].should == 2 settings.nested[:b].should == 2 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 it 'should prioritize settings in latter files' do # first let's check the test is actually working properly config_file 'key_value.yml' settings.foo.should == 'bar' # now test it config_file 'key_value_override.yml' settings.foo.should == 'foo' end end sinatra-contrib-1.4.7/spec/streaming_spec.rb0000644000004100000410000002212112706205017021132 0ustar www-datawww-datarequire 'backports' require 'spec_helper' describe Sinatra::Streaming do def stream(&block) rack_middleware = @use out = nil mock_app do rack_middleware.each { |args| use(*args) } helpers Sinatra::Streaming get('/') { out = stream(&block) } end get('/') out end def use(*args) @use << args end before do @use = [] end context 'stream test helper' do it 'runs the given block' do ran = false stream { ran = true } ran.should be true end it 'returns the stream object' do out = stream { } out.should be_a(Sinatra::Helpers::Stream) end it 'fires a request against that stream' do stream { |out| out << "Hello World!" } last_response.should be_ok body.should be == "Hello World!" end it 'passes the stream object to the block' do passed = nil returned = stream { |out| passed = out } passed.should be == returned end end context Sinatra::Streaming::Stream do it 'should extend the stream object' do out = stream { } out.should be_a(Sinatra::Streaming::Stream) end it 'should not extend stream objects of other apps' do out = nil mock_app { get('/') { out = stream { }}} get('/') out.should be_a(Sinatra::Helpers::Stream) out.should_not be_a(Sinatra::Streaming::Stream) end end context 'app' do it 'is the app instance the stream was created from' do out = stream { } out.app.should be_a(Sinatra::Base) end end context 'lineno' do it 'defaults to 0' do stream { }.lineno.should be == 0 end it 'does not increase on write' do stream do |out| out << "many\nlines\n" out.lineno.should be == 0 end end it 'is writable' do out = stream { } out.lineno = 10 out.lineno.should be == 10 end end context 'pos' do it 'defaults to 0' do stream { }.pos.should be == 0 end it 'increases when writing data' do stream do |out| out.pos.should be == 0 out << 'hi' out.pos.should be == 2 end end it 'is writable' do out = stream { } out.pos = 10 out.pos.should be == 10 end it 'aliased to #tell' do out = stream { } out.tell.should be == 0 out.pos = 10 out.tell.should be == 10 end end context 'closed' do it 'returns false while streaming' do stream { |out| out.should_not be_closed } end it 'returns true after streaming' do stream {}.should be_closed end end context 'map!' do it 'applies transformations later' do stream do |out| out.map! { |s| s.upcase } out << 'ok' end body.should be == "OK" end it 'is chainable' do stream do |out| out.map! { |s| s.upcase } out.map! { |s| s.reverse } out << 'ok' end body.should be == "KO" end it 'works with middleware' do middleware = Class.new do def initialize(app) @app = app end def call(env) status, headers, body = @app.call(env) body.map! { |s| s.upcase } [status, headers, body] end end use middleware stream { |out| out << "ok" } body.should be == "OK" end it 'modifies each value separately' do stream do |out| out.map! { |s| s.reverse } out << "ab" << "cd" end body.should be == "badc" end end context 'map' do it 'works with middleware' do middleware = Class.new do def initialize(app) @app = app end def call(env) status, headers, body = @app.call(env) [status, headers, body.map(&:upcase)] end end use middleware stream { |out| out << "ok" } body.should be == "OK" end it 'is chainable' do middleware = Class.new do def initialize(app) @app = app end def call(env) status, headers, body = @app.call(env) [status, headers, body.map(&:upcase).map(&:reverse)] end end use middleware stream { |out| out << "ok" } body.should be == "KO" end it 'can be written as each.map' do middleware = Class.new do def initialize(app) @app = app end def call(env) status, headers, body = @app.call(env) [status, headers, body.each.map(&:upcase)] end end use middleware stream { |out| out << "ok" } body.should be == "OK" end it 'does not modify the original body' do stream do |out| out.map { |s| s.reverse } out << 'ok' end body.should be == 'ok' end end context 'write' do it 'writes to the stream' do stream { |out| out.write 'hi' } body.should be == 'hi' end it 'returns the number of bytes' do stream do |out| out.write('hi').should be == 2 out.write('hello').should be == 5 end end it 'accepts non-string objects' do stream do |out| out.write(12).should be == 2 end end it 'should be aliased to syswrite' do stream { |out| out.syswrite('hi').should be == 2 } body.should be == 'hi' end it 'should be aliased to write_nonblock' do stream { |out| out.write_nonblock('hi').should be == 2 } body.should be == 'hi' end end context 'print' do it 'writes to the stream' do stream { |out| out.print('hi') } body.should be == 'hi' end it 'accepts multiple arguments' do stream { |out| out.print(1, 2, 3, 4) } body.should be == '1234' end it 'returns nil' do stream { |out| out.print('hi').should be_nil } end end context 'printf' do it 'writes to the stream' do stream { |out| out.printf('hi') } body.should be == 'hi' end it 'interpolates the format string' do stream { |out| out.printf("%s: %d", "answer", 42) } body.should be == 'answer: 42' end it 'returns nil' do stream { |out| out.printf('hi').should be_nil } end end context 'putc' do it 'writes the first character of a string' do stream { |out| out.putc('hi') } body.should be == 'h' end it 'writes the character corresponding to an integer' do stream { |out| out.putc(42) } body.should be == '*' end it 'returns nil' do stream { |out| out.putc('hi').should be_nil } end end context 'puts' do it 'writes to the stream' do stream { |out| out.puts('hi') } body.should be == "hi\n" end it 'accepts multiple arguments' do stream { |out| out.puts(1, 2, 3, 4) } body.should be == "1\n2\n3\n4\n" end it 'returns nil' do stream { |out| out.puts('hi').should be_nil } end end context 'close' do it 'sets #closed? to true' do stream do |out| out.close out.should be_closed end end it 'sets #closed_write? to true' do stream do |out| out.should_not be_closed_write out.close out.should be_closed_write end end it 'fires callbacks' do stream do |out| fired = false out.callback { fired = true } out.close fired.should be true end end it 'prevents from further writing' do stream do |out| out.close expect { out << 'hi' }.to raise_error(IOError, 'not opened for writing') end end end context 'close_read' do it 'raises the appropriate exception' do expect { stream { |out| out.close_read }}. to raise_error(IOError, "closing non-duplex IO for reading") end end context 'closed_read?' do it('returns true') { stream { |out| out.should be_closed_read }} end context 'rewind' do it 'resets pos' do stream do |out| out << 'hi' out.rewind out.pos.should be == 0 end end it 'resets lineno' do stream do |out| out.lineno = 10 out.rewind out.lineno.should be == 0 end end end raises = %w[ bytes eof? eof getbyte getc gets read read_nonblock readbyte readchar readline readlines readpartial sysread ungetbyte ungetc ] enum = %w[chars each_line each_byte each_char lines] dummies = %w[flush fsync internal_encoding pid] raises.each do |method| context method do it 'raises the appropriate exception' do expect { stream { |out| out.public_send(method) }}. to raise_error(IOError, "not opened for reading") end end end enum.each do |method| context method do it 'creates an Enumerator' do stream { |out| out.public_send(method).should be_a(Enumerator) } end it 'calling each raises the appropriate exception' do expect { stream { |out| out.public_send(method).each { }}}. to raise_error(IOError, "not opened for reading") end end end dummies.each do |method| context method do it 'returns nil' do stream { |out| out.public_send(method).should be_nil } end end end end sinatra-contrib-1.4.7/spec/respond_with/0000755000004100000410000000000012706205017020311 5ustar www-datawww-datasinatra-contrib-1.4.7/spec/respond_with/bar.json.erb0000644000004100000410000000000512706205017022512 0ustar www-datawww-datajson!sinatra-contrib-1.4.7/spec/respond_with/not_html.sass0000644000004100000410000000002212706205017023022 0ustar www-datawww-databody color: red sinatra-contrib-1.4.7/spec/respond_with/bar.erb0000644000004100000410000000001212706205017021540 0ustar www-datawww-dataguten Tag!sinatra-contrib-1.4.7/spec/respond_with/baz.yajl0000644000004100000410000000001712706205017021744 0ustar www-datawww-datajson = "yajl!" sinatra-contrib-1.4.7/spec/respond_with/foo.html.erb0000644000004100000410000000002212706205017022523 0ustar www-datawww-dataHello <%= name %>!sinatra-contrib-1.4.7/spec/reloader_spec.rb0000644000004100000410000003220112706205017020736 0ustar www-datawww-datarequire 'spec_helper' require 'fileutils' describe Sinatra::Reloader do # Returns the temporary directory. def tmp_dir File.expand_path('../../tmp', __FILE__) end # Returns the path of the Sinatra application file created by # +setup_example_app+. def app_file_path File.join(tmp_dir, "example_app_#{$example_app_counter}.rb") end # Returns the name of the Sinatra application created by # +setup_example_app+: 'ExampleApp1' for the first application, # 'ExampleApp2' fo the second one, and so on... def app_name "ExampleApp#{$example_app_counter}" end # Returns the (constant of the) Sinatra application created by # +setup_example_app+. def app_const Module.const_get(app_name) end # Writes a file with a Sinatra application using the template # located at specs/reloader/app.rb.erb. It expects an # +options+ hash, with an array of strings containing the # application's routes (+:routes+ key), a hash with the inline # template's names as keys and the bodys as values # (+:inline_templates+ key) and an optional application name # (+:name+) otherwise +app_name+ is used. # # It ensures to change the written file's mtime when it already # exists. def write_app_file(options={}) options[:routes] ||= ['get("/foo") { erb :foo }'] options[:inline_templates] ||= nil options[:extensions] ||= [] options[:middlewares] ||= [] options[:filters] ||= [] options[:errors] ||= {} options[:name] ||= app_name options[:enable_reloader] = true unless options[:enable_reloader] === false options[:parent] ||= 'Sinatra::Base' update_file(app_file_path) do |f| template_path = File.expand_path('../reloader/app.rb.erb', __FILE__) template = Tilt.new(template_path, nil, :trim => '<>') f.write template.render(Object.new, options) end end alias update_app_file write_app_file # It calls File.open(path, 'w', &block) all the times # needed to change the file's mtime. def update_file(path, &block) original_mtime = File.exist?(path) ? File.mtime(path) : Time.at(0) new_time = original_mtime + 1 File.open(path, 'w', &block) File.utime(new_time, new_time, path) end # Writes a Sinatra application to a file, requires the file, sets # the new application as the one being tested and enables the # reloader. def setup_example_app(options={}) $example_app_counter ||= 0 $example_app_counter += 1 FileUtils.mkdir_p(tmp_dir) write_app_file(options) $LOADED_FEATURES.delete app_file_path require app_file_path self.app = app_const app_const.enable :reloader end after(:all) { FileUtils.rm_rf(tmp_dir) } describe "default route reloading mechanism" do before(:each) do setup_example_app(:routes => ['get("/foo") { "foo" }']) end it "doesn't mess up the application" do get('/foo').body.should == 'foo' end it "knows when a route has been modified" do update_app_file(:routes => ['get("/foo") { "bar" }']) get('/foo').body.should == 'bar' end it "knows when a route has been added" do update_app_file( :routes => ['get("/foo") { "foo" }', 'get("/bar") { "bar" }'] ) get('/foo').body.should == 'foo' get('/bar').body.should == 'bar' end it "knows when a route has been removed" do update_app_file(:routes => ['get("/bar") { "bar" }']) get('/foo').status.should == 404 end it "doesn't try to reload a removed file" do update_app_file(:routes => ['get("/foo") { "i shall not be reloaded" }']) FileUtils.rm app_file_path get('/foo').body.strip.should == 'foo' end end describe "default inline templates reloading mechanism" do before(:each) do setup_example_app( :routes => ['get("/foo") { erb :foo }'], :inline_templates => { :foo => 'foo' } ) end it "doesn't mess up the application" do get('/foo').body.strip.should == 'foo' end it "reloads inline templates in the app file" do update_app_file( :routes => ['get("/foo") { erb :foo }'], :inline_templates => { :foo => 'bar' } ) get('/foo').body.strip.should == 'bar' end it "reloads inline templates in other file" do setup_example_app(:routes => ['get("/foo") { erb :foo }']) template_file_path = File.join(tmp_dir, 'templates.rb') File.open(template_file_path, 'w') do |f| f.write "__END__\n\n@@foo\nfoo" end require template_file_path app_const.inline_templates= template_file_path get('/foo').body.strip.should == 'foo' update_file(template_file_path) do |f| f.write "__END__\n\n@@foo\nbar" end get('/foo').body.strip.should == 'bar' end end describe "default middleware reloading mechanism" do it "knows when a middleware has been added" do setup_example_app(:routes => ['get("/foo") { "foo" }']) update_app_file( :routes => ['get("/foo") { "foo" }'], :middlewares => [Rack::Head] ) get('/foo') # ...to perform the reload app_const.middleware.should_not be_empty end it "knows when a middleware has been removed" do setup_example_app( :routes => ['get("/foo") { "foo" }'], :middlewares => [Rack::Head] ) update_app_file(:routes => ['get("/foo") { "foo" }']) get('/foo') # ...to perform the reload app_const.middleware.should be_empty end end describe "default filter reloading mechanism" do it "knows when a before filter has been added" do setup_example_app(:routes => ['get("/foo") { "foo" }']) expect { update_app_file( :routes => ['get("/foo") { "foo" }'], :filters => ['before { @hi = "hi" }'] ) get('/foo') # ...to perform the reload }.to change { app_const.filters[:before].size }.by(1) end it "knows when an after filter has been added" do setup_example_app(:routes => ['get("/foo") { "foo" }']) expect { update_app_file( :routes => ['get("/foo") { "foo" }'], :filters => ['after { @bye = "bye" }'] ) get('/foo') # ...to perform the reload }.to change { app_const.filters[:after].size }.by(1) end it "knows when a before filter has been removed" do setup_example_app( :routes => ['get("/foo") { "foo" }'], :filters => ['before { @hi = "hi" }'] ) expect { update_app_file(:routes => ['get("/foo") { "foo" }']) get('/foo') # ...to perform the reload }.to change { app_const.filters[:before].size }.by(-1) end it "knows when an after filter has been removed" do setup_example_app( :routes => ['get("/foo") { "foo" }'], :filters => ['after { @bye = "bye" }'] ) expect { update_app_file(:routes => ['get("/foo") { "foo" }']) get('/foo') # ...to perform the reload }.to change { app_const.filters[:after].size }.by(-1) end end describe "error reloading" do before do setup_example_app( :routes => ['get("/secret") { 403 }'], :errors => { 403 => "'Access forbiden'" } ) end it "doesn't mess up the application" do get('/secret').should be_client_error get('/secret').body.strip.should == 'Access forbiden' end it "knows when a error has been added" do update_app_file(:errors => { 404 => "'Nowhere'" }) get('/nowhere').should be_not_found get('/nowhere').body.should == 'Nowhere' end it "knows when a error has been removed" do update_app_file(:routes => ['get("/secret") { 403 }']) get('/secret').should be_client_error get('/secret').body.should_not == 'Access forbiden' end it "knows when a error has been modified" do update_app_file( :routes => ['get("/secret") { 403 }'], :errors => { 403 => "'What are you doing here?'" } ) get('/secret').should be_client_error get('/secret').body.should == 'What are you doing here?' end end describe "extension reloading" do it "doesn't duplicate routes with every reload" do module ::RouteExtension def self.registered(klass) klass.get('/bar') { 'bar' } end end setup_example_app( :routes => ['get("/foo") { "foo" }'], :extensions => ['RouteExtension'] ) expect { update_app_file( :routes => ['get("/foo") { "foo" }'], :extensions => ['RouteExtension'] ) get('/foo') # ...to perform the reload }.to_not change { app_const.routes['GET'].size } end it "doesn't duplicate middleware with every reload" do module ::MiddlewareExtension def self.registered(klass) klass.use Rack::Head end end setup_example_app( :routes => ['get("/foo") { "foo" }'], :extensions => ['MiddlewareExtension'] ) expect { update_app_file( :routes => ['get("/foo") { "foo" }'], :extensions => ['MiddlewareExtension'] ) get('/foo') # ...to perform the reload }.to_not change { app_const.middleware.size } end it "doesn't duplicate before filters with every reload" do module ::BeforeFilterExtension def self.registered(klass) klass.before { @hi = 'hi' } end end setup_example_app( :routes => ['get("/foo") { "foo" }'], :extensions => ['BeforeFilterExtension'] ) expect { update_app_file( :routes => ['get("/foo") { "foo" }'], :extensions => ['BeforeFilterExtension'] ) get('/foo') # ...to perform the reload }.to_not change { app_const.filters[:before].size } end it "doesn't duplicate after filters with every reload" do module ::AfterFilterExtension def self.registered(klass) klass.after { @bye = 'bye' } end end setup_example_app( :routes => ['get("/foo") { "foo" }'], :extensions => ['AfterFilterExtension'] ) expect { update_app_file( :routes => ['get("/foo") { "foo" }'], :extensions => ['AfterFilterExtension'] ) get('/foo') # ...to perform the reload }.to_not change { app_const.filters[:after].size } end end describe ".dont_reload" do before(:each) do setup_example_app( :routes => ['get("/foo") { erb :foo }'], :inline_templates => { :foo => 'foo' } ) end it "allows to specify a file to stop from being reloaded" do app_const.dont_reload app_file_path update_app_file(:routes => ['get("/foo") { "bar" }']) get('/foo').body.strip.should == 'foo' end it "allows to specify a glob to stop matching files from being reloaded" do app_const.dont_reload '**/*.rb' update_app_file(:routes => ['get("/foo") { "bar" }']) get('/foo').body.strip.should == 'foo' end it "doesn't interfere with other application's reloading policy" do app_const.dont_reload '**/*.rb' setup_example_app(:routes => ['get("/foo") { "foo" }']) update_app_file(:routes => ['get("/foo") { "bar" }']) get('/foo').body.strip.should == 'bar' end end describe ".also_reload" do before(:each) do setup_example_app(:routes => ['get("/foo") { Foo.foo }']) @foo_path = File.join(tmp_dir, 'foo.rb') update_file(@foo_path) do |f| f.write 'class Foo; def self.foo() "foo" end end' end $LOADED_FEATURES.delete @foo_path require @foo_path app_const.also_reload @foo_path end it "allows to specify a file to be reloaded" do get('/foo').body.strip.should == 'foo' update_file(@foo_path) do |f| f.write 'class Foo; def self.foo() "bar" end end' end get('/foo').body.strip.should == 'bar' end it "allows to specify glob to reaload matching files" do get('/foo').body.strip.should == 'foo' update_file(@foo_path) do |f| f.write 'class Foo; def self.foo() "bar" end end' end get('/foo').body.strip.should == 'bar' end it "doesn't try to reload a removed file" do update_file(@foo_path) do |f| f.write 'class Foo; def self.foo() "bar" end end' end FileUtils.rm @foo_path get('/foo').body.strip.should == 'foo' end it "doesn't interfere with other application's reloading policy" do app_const.also_reload '**/*.rb' setup_example_app(:routes => ['get("/foo") { Foo.foo }']) get('/foo').body.strip.should == 'foo' update_file(@foo_path) do |f| f.write 'class Foo; def self.foo() "bar" end end' end get('/foo').body.strip.should == 'foo' end end it "automatically registers the reloader in the subclasses" do class ::Parent < Sinatra::Base register Sinatra::Reloader enable :reloader end setup_example_app( :routes => ['get("/foo") { "foo" }'], :enable_reloader => false, :parent => 'Parent' ) update_app_file( :routes => ['get("/foo") { "bar" }'], :enable_reloader => false, :parent => 'Parent' ) get('/foo').body.should == 'bar' end end sinatra-contrib-1.4.7/spec/capture_spec.rb0000644000004100000410000000367612706205017020622 0ustar www-datawww-data# -*- coding: utf-8 -*- require 'slim' require 'spec_helper' describe Sinatra::Capture do subject do Sinatra.new do enable :inline_templates helpers Sinatra::Capture end.new! end Tilt.prefer Tilt::ERBTemplate extend Forwardable def_delegators :subject, :capture, :capture_later def render(engine, template) subject.send(:render, engine, template.to_sym).strip.gsub(/\s+/, ' ') end shared_examples_for "a template language" do |engine| lang = engine == :erubis ? :erb : engine require "#{engine}" it "captures content" do render(engine, "simple_#{lang}").should == "Say Hello World!" end it "allows nested captures" do render(engine, "nested_#{lang}").should == "Say Hello World!" end end describe('haml') { it_behaves_like "a template language", :haml } describe('slim') { it_behaves_like "a template language", :slim } describe('erubis') { it_behaves_like "a template language", :erubis } describe 'erb' do it_behaves_like "a template language", :erb it "handles utf-8 encoding" do render(:erb, "utf_8").should == "UTF-8 –" end it "handles ISO-8859-1 encoding" do render(:erb, "iso_8859_1").should == "ISO-8859-1 -" end if RUBY_VERSION >= '1.9' end end __END__ @@ simple_erb Say <% a = capture do %>World<% end %> Hello <%= a %>! @@ nested_erb Say <% a = capture do %> <% b = capture do %>World<% end %> <%= b %>! <% end %> Hello <%= a.strip %> @@ simple_slim | Say - a = capture do | World | Hello #{a.strip}! @@ nested_slim | Say - a = capture do - b = capture do | World | #{b.strip}! | Hello #{a.strip} @@ simple_haml Say - a = capture do World Hello #{a.strip}! @@ nested_haml Say - a = capture do - b = capture do World #{b.strip}! Hello #{a.strip} @@ utf_8 <% a = capture do %>–<% end %> UTF-8 <%= a %> @@ iso_8859_1 <% a = capture do %>-<% end %> ISO-8859-1 <%= a.force_encoding("iso-8859-1") %> sinatra-contrib-1.4.7/spec/okjson.rb0000644000004100000410000003263412706205017017444 0ustar www-datawww-data# Copyright 2011 Keith Rarick # # 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. # See https://github.com/kr/okjson for updates. require 'stringio' # Some parts adapted from # http://golang.org/src/pkg/json/decode.go and # http://golang.org/src/pkg/utf8/utf8.go module OkJson extend self # Decodes a json document in string s and # returns the corresponding ruby value. # String s must be valid UTF-8. If you have # a string in some other encoding, convert # it first. # # String values in the resulting structure # will be UTF-8. def decode(s) ts = lex(s) v, ts = textparse(ts) if ts.length > 0 raise Error, 'trailing garbage' end v end # Parses a "json text" in the sense of RFC 4627. # Returns the parsed value and any trailing tokens. # Note: this is almost the same as valparse, # except that it does not accept atomic values. def textparse(ts) if ts.length < 0 raise Error, 'empty' end typ, _, val = ts[0] case typ when '{' then objparse(ts) when '[' then arrparse(ts) else raise Error, "unexpected #{val.inspect}" end end # Parses a "value" in the sense of RFC 4627. # Returns the parsed value and any trailing tokens. def valparse(ts) if ts.length < 0 raise Error, 'empty' end typ, _, val = ts[0] case typ when '{' then objparse(ts) when '[' then arrparse(ts) when :val,:str then [val, ts[1..-1]] else raise Error, "unexpected #{val.inspect}" end end # Parses an "object" in the sense of RFC 4627. # Returns the parsed value and any trailing tokens. def objparse(ts) ts = eat('{', ts) obj = {} if ts[0][0] == '}' return obj, ts[1..-1] end k, v, ts = pairparse(ts) obj[k] = v if ts[0][0] == '}' return obj, ts[1..-1] end loop do ts = eat(',', ts) k, v, ts = pairparse(ts) obj[k] = v if ts[0][0] == '}' return obj, ts[1..-1] end end end # Parses a "member" in the sense of RFC 4627. # Returns the parsed values and any trailing tokens. def pairparse(ts) (typ, _, k), ts = ts[0], ts[1..-1] if typ != :str raise Error, "unexpected #{k.inspect}" end ts = eat(':', ts) v, ts = valparse(ts) [k, v, ts] end # Parses an "array" in the sense of RFC 4627. # Returns the parsed value and any trailing tokens. def arrparse(ts) ts = eat('[', ts) arr = [] if ts[0][0] == ']' return arr, ts[1..-1] end v, ts = valparse(ts) arr << v if ts[0][0] == ']' return arr, ts[1..-1] end loop do ts = eat(',', ts) v, ts = valparse(ts) arr << v if ts[0][0] == ']' return arr, ts[1..-1] end end end def eat(typ, ts) if ts[0][0] != typ raise Error, "expected #{typ} (got #{ts[0].inspect})" end ts[1..-1] end # Sans s and returns a list of json tokens, # excluding white space (as defined in RFC 4627). def lex(s) ts = [] while s.length > 0 typ, lexeme, val = tok(s) if typ == nil raise Error, "invalid character at #{s[0,10].inspect}" end if typ != :space ts << [typ, lexeme, val] end s = s[lexeme.length..-1] end ts end # Scans the first token in s and # returns a 3-element list, or nil # if no such token exists. # # The first list element is one of # '{', '}', ':', ',', '[', ']', # :val, :str, and :space. # # The second element is the lexeme. # # The third element is the value of the # token for :val and :str, otherwise # it is the lexeme. def tok(s) case s[0] when ?{ then ['{', s[0,1], s[0,1]] when ?} then ['}', s[0,1], s[0,1]] when ?: then [':', s[0,1], s[0,1]] when ?, then [',', s[0,1], s[0,1]] when ?[ then ['[', s[0,1], s[0,1]] when ?] then [']', s[0,1], s[0,1]] when ?n then nulltok(s) when ?t then truetok(s) when ?f then falsetok(s) when ?" then strtok(s) when Spc then [:space, s[0,1], s[0,1]] when ?\t then [:space, s[0,1], s[0,1]] when ?\n then [:space, s[0,1], s[0,1]] when ?\r then [:space, s[0,1], s[0,1]] else numtok(s) end end def nulltok(s); s[0,4] == 'null' && [:val, 'null', nil] end def truetok(s); s[0,4] == 'true' && [:val, 'true', true] end def falsetok(s); s[0,5] == 'false' && [:val, 'false', false] end def numtok(s) m = /-?([1-9][0-9]+|[0-9])([.][0-9]+)?([eE][+-]?[0-9]+)?/.match(s) if m && m.begin(0) == 0 if m[3] && !m[2] [:val, m[0], Integer(m[1])*(10**Integer(m[3][1..-1]))] elsif m[2] [:val, m[0], Float(m[0])] else [:val, m[0], Integer(m[0])] end end end def strtok(s) m = /"([^"\\]|\\["\/\\bfnrt]|\\u[0-9a-fA-F]{4})*"/.match(s) if ! m raise Error, "invalid string literal at #{abbrev(s)}" end [:str, m[0], unquote(m[0])] end def abbrev(s) t = s[0,10] p = t['`'] t = t[0,p] if p t = t + '...' if t.length < s.length '`' + t + '`' end # Converts a quoted json string literal q into a UTF-8-encoded string. # The rules are different than for Ruby, so we cannot use eval. # Unquote will raise an error if q contains control characters. def unquote(q) q = q[1...-1] a = q.dup # allocate a big enough string r, w = 0, 0 while r < q.length c = q[r] case true when c == ?\\ r += 1 if r >= q.length raise Error, "string literal ends with a \"\\\": \"#{q}\"" end case q[r] when ?",?\\,?/,?' a[w] = q[r] r += 1 w += 1 when ?b,?f,?n,?r,?t a[w] = Unesc[q[r]] r += 1 w += 1 when ?u r += 1 uchar = begin hexdec4(q[r,4]) rescue RuntimeError => e raise Error, "invalid escape sequence \\u#{q[r,4]}: #{e}" end r += 4 if surrogate? uchar if q.length >= r+6 uchar1 = hexdec4(q[r+2,4]) uchar = subst(uchar, uchar1) if uchar != Ucharerr # A valid pair; consume. r += 6 end end end w += ucharenc(a, w, uchar) else raise Error, "invalid escape char #{q[r]} in \"#{q}\"" end when c == ?", c < Spc raise Error, "invalid character in string literal \"#{q}\"" else # Copy anything else byte-for-byte. # Valid UTF-8 will remain valid UTF-8. # Invalid UTF-8 will remain invalid UTF-8. a[w] = c r += 1 w += 1 end end a[0,w] end # Encodes unicode character u as UTF-8 # bytes in string a at position i. # Returns the number of bytes written. def ucharenc(a, i, u) case true when u <= Uchar1max a[i] = (u & 0xff).chr 1 when u <= Uchar2max a[i+0] = (Utag2 | ((u>>6)&0xff)).chr a[i+1] = (Utagx | (u&Umaskx)).chr 2 when u <= Uchar3max a[i+0] = (Utag3 | ((u>>12)&0xff)).chr a[i+1] = (Utagx | ((u>>6)&Umaskx)).chr a[i+2] = (Utagx | (u&Umaskx)).chr 3 else a[i+0] = (Utag4 | ((u>>18)&0xff)).chr a[i+1] = (Utagx | ((u>>12)&Umaskx)).chr a[i+2] = (Utagx | ((u>>6)&Umaskx)).chr a[i+3] = (Utagx | (u&Umaskx)).chr 4 end end def hexdec4(s) if s.length != 4 raise Error, 'short' end (nibble(s[0])<<12) | (nibble(s[1])<<8) | (nibble(s[2])<<4) | nibble(s[3]) end def subst(u1, u2) if Usurr1 <= u1 && u1 < Usurr2 && Usurr2 <= u2 && u2 < Usurr3 return ((u1-Usurr1)<<10) | (u2-Usurr2) + Usurrself end return Ucharerr end def unsubst(u) if u < Usurrself || u > Umax || surrogate?(u) return Ucharerr, Ucharerr end u -= Usurrself [Usurr1 + ((u>>10)&0x3ff), Usurr2 + (u&0x3ff)] end def surrogate?(u) Usurr1 <= u && u < Usurr3 end def nibble(c) case true when ?0 <= c && c <= ?9 then c.ord - ?0.ord when ?a <= c && c <= ?z then c.ord - ?a.ord + 10 when ?A <= c && c <= ?Z then c.ord - ?A.ord + 10 else raise Error, "invalid hex code #{c}" end end # Encodes x into a json text. It may contain only # Array, Hash, String, Numeric, true, false, nil. # (Note, this list excludes Symbol.) # X itself must be an Array or a Hash. # No other value can be encoded, and an error will # be raised if x contains any other value, such as # Nan, Infinity, Symbol, and Proc, or if a Hash key # is not a String. # Strings contained in x must be valid UTF-8. def encode(x) case x when Hash then objenc(x) when Array then arrenc(x) else raise Error, 'root value must be an Array or a Hash' end end def valenc(x) case x when Hash then objenc(x) when Array then arrenc(x) when String then strenc(x) when Numeric then numenc(x) when true then "true" when false then "false" when nil then "null" else raise Error, "cannot encode #{x.class}: #{x.inspect}" end end def objenc(x) '{' + x.map{|k,v| keyenc(k) + ':' + valenc(v)}.join(',') + '}' end def arrenc(a) '[' + a.map{|x| valenc(x)}.join(',') + ']' end def keyenc(k) case k when String then strenc(k) else raise Error, "Hash key is not a string: #{k.inspect}" end end def strenc(s) t = StringIO.new t.putc(?") r = 0 while r < s.length case s[r] when ?" then t.print('\\"') when ?\\ then t.print('\\\\') when ?\b then t.print('\\b') when ?\f then t.print('\\f') when ?\n then t.print('\\n') when ?\r then t.print('\\r') when ?\t then t.print('\\t') else c = s[r] case true when Spc <= c && c <= ?~ t.putc(c) when true u, size = uchardec(s, r) r += size - 1 # we add one more at the bottom of the loop if u < 0x10000 t.print('\\u') hexenc4(t, u) else u1, u2 = unsubst(u) t.print('\\u') hexenc4(t, u1) t.print('\\u') hexenc4(t, u2) end else # invalid byte; skip it end end r += 1 end t.putc(?") t.string end def hexenc4(t, u) t.putc(Hex[(u>>12)&0xf]) t.putc(Hex[(u>>8)&0xf]) t.putc(Hex[(u>>4)&0xf]) t.putc(Hex[u&0xf]) end def numenc(x) if x.nan? || x.infinite? return 'null' end rescue nil "#{x}" end # Decodes unicode character u from UTF-8 # bytes in string s at position i. # Returns u and the number of bytes read. def uchardec(s, i) n = s.length - i return [Ucharerr, 1] if n < 1 c0 = s[i].ord # 1-byte, 7-bit sequence? if c0 < Utagx return [c0, 1] end # unexpected continuation byte? return [Ucharerr, 1] if c0 < Utag2 # need continuation byte return [Ucharerr, 1] if n < 2 c1 = s[i+1].ord return [Ucharerr, 1] if c1 < Utagx || Utag2 <= c1 # 2-byte, 11-bit sequence? if c0 < Utag3 u = (c0&Umask2)<<6 | (c1&Umaskx) return [Ucharerr, 1] if u <= Uchar1max return [u, 2] end # need second continuation byte return [Ucharerr, 1] if n < 3 c2 = s[i+2].ord return [Ucharerr, 1] if c2 < Utagx || Utag2 <= c2 # 3-byte, 16-bit sequence? if c0 < Utag4 u = (c0&Umask3)<<12 | (c1&Umaskx)<<6 | (c2&Umaskx) return [Ucharerr, 1] if u <= Uchar2max return [u, 3] end # need third continuation byte return [Ucharerr, 1] if n < 4 c3 = s[i+3].ord return [Ucharerr, 1] if c3 < Utagx || Utag2 <= c3 # 4-byte, 21-bit sequence? if c0 < Utag5 u = (c0&Umask4)<<18 | (c1&Umaskx)<<12 | (c2&Umaskx)<<6 | (c3&Umaskx) return [Ucharerr, 1] if u <= Uchar3max return [u, 4] end return [Ucharerr, 1] end class Error < ::StandardError end Utagx = 0x80 # 1000 0000 Utag2 = 0xc0 # 1100 0000 Utag3 = 0xe0 # 1110 0000 Utag4 = 0xf0 # 1111 0000 Utag5 = 0xF8 # 1111 1000 Umaskx = 0x3f # 0011 1111 Umask2 = 0x1f # 0001 1111 Umask3 = 0x0f # 0000 1111 Umask4 = 0x07 # 0000 0111 Uchar1max = (1<<7) - 1 Uchar2max = (1<<11) - 1 Uchar3max = (1<<16) - 1 Ucharerr = 0xFFFD # unicode "replacement char" Usurrself = 0x10000 Usurr1 = 0xd800 Usurr2 = 0xdc00 Usurr3 = 0xe000 Umax = 0x10ffff Spc = ' '[0] Unesc = {?b=>?\b, ?f=>?\f, ?n=>?\n, ?r=>?\r, ?t=>?\t} Hex = '0123456789abcdef' end sinatra-contrib-1.4.7/spec/extension_spec.rb0000644000004100000410000000121412706205017021155 0ustar www-datawww-datarequire '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 sinatra-contrib-1.4.7/spec/reloader/0000755000004100000410000000000012706205017017401 5ustar www-datawww-datasinatra-contrib-1.4.7/spec/reloader/app.rb.erb0000644000004100000410000000127012706205017021255 0ustar www-datawww-dataclass <%= name %> < <%= parent %> <% if enable_reloader %> register Sinatra::Reloader enable :reloader <% end %> <% unless inline_templates.nil? %> enable :inline_templates <% end %> <% extensions.each do |extension| %> register <%= extension %> <% end %> <% middlewares.each do |middleware| %> use <%= middleware %> <% end %> <% filters.each do |filter| %> <%= filter %> <% end %> <% errors.each do |number, code| %> error <%= number %> do <%= code %> end <% end %> <% routes.each do |route| %> <%= route %> <% end %> end <% unless inline_templates.nil? %> __END__ <% inline_templates.each_pair do |name, content| %> @@<%= name %> <%= content %> <% end %> <% end %> sinatra-contrib-1.4.7/spec/content_for/0000755000004100000410000000000012706205017020124 5ustar www-datawww-datasinatra-contrib-1.4.7/spec/content_for/takes_values.erubis0000644000004100000410000000010112706205017024015 0ustar www-datawww-data<% content_for :foo do |a, b| %><%= a %> <%= b %><% end %>sinatra-contrib-1.4.7/spec/content_for/same_key.erb0000644000004100000410000000004612706205017022413 0ustar www-datawww-data<% content_for :foo do %>foo<% end %> sinatra-contrib-1.4.7/spec/content_for/passes_values.haml0000644000004100000410000000003312706205017023640 0ustar www-datawww-data= yield_content :foo, 1, 2 sinatra-contrib-1.4.7/spec/content_for/footer.haml0000644000004100000410000000005412706205017022264 0ustar www-datawww-data- if content_for? :foo = yield_content :foosinatra-contrib-1.4.7/spec/content_for/layout.haml0000644000004100000410000000002512706205017022301 0ustar www-datawww-data= yield_content :foo sinatra-contrib-1.4.7/spec/content_for/different_key.erb0000644000004100000410000000004612706205017023434 0ustar www-datawww-data<% content_for :bar do %>bar<% end %> sinatra-contrib-1.4.7/spec/content_for/passes_values.slim0000644000004100000410000000003412706205017023664 0ustar www-datawww-data== yield_content :foo, 1, 2 sinatra-contrib-1.4.7/spec/content_for/multiple_yields.slim0000644000004100000410000000007712706205017024222 0ustar www-datawww-data= yield_content :foo = yield_content :foo = yield_content :foo sinatra-contrib-1.4.7/spec/content_for/different_key.slim0000644000004100000410000000003612706205017023627 0ustar www-datawww-data- content_for :bar do | bar sinatra-contrib-1.4.7/spec/content_for/same_key.erubis0000644000004100000410000000004612706205017023134 0ustar www-datawww-data<% content_for :foo do %>foo<% end %> sinatra-contrib-1.4.7/spec/content_for/multiple_blocks.haml0000644000004100000410000000017412706205017024161 0ustar www-datawww-data- content_for :foo do foo - content_for :foo do bar - content_for :baz do WON'T RENDER ME - content_for :foo do baz sinatra-contrib-1.4.7/spec/content_for/footer.slim0000644000004100000410000000005412706205017022307 0ustar www-datawww-data- if content_for? :foo = yield_content :foosinatra-contrib-1.4.7/spec/content_for/takes_values.slim0000644000004100000410000000005212706205017023475 0ustar www-datawww-data- content_for :foo do |a, b| i= a = b sinatra-contrib-1.4.7/spec/content_for/layout.slim0000644000004100000410000000002512706205017022324 0ustar www-datawww-data= yield_content :foo sinatra-contrib-1.4.7/spec/content_for/footer.erb0000644000004100000410000000010012706205017022103 0ustar www-datawww-data<% if content_for? :foo %> <%= yield_content :foo %> <% end %>sinatra-contrib-1.4.7/spec/content_for/same_key.haml0000644000004100000410000000003412706205017022561 0ustar www-datawww-data- content_for :foo do foo sinatra-contrib-1.4.7/spec/content_for/takes_values.haml0000644000004100000410000000005212706205017023452 0ustar www-datawww-data- content_for :foo do |a, b| %i= a =b sinatra-contrib-1.4.7/spec/content_for/multiple_blocks.erb0000644000004100000410000000024412706205017024006 0ustar www-datawww-data<% content_for :foo do %>foo<% end %> <% content_for :foo do %>bar<% end %> <% content_for :baz do %>WON'T RENDER ME<% end %> <% content_for :foo do %>baz<% end %> sinatra-contrib-1.4.7/spec/content_for/multiple_yields.erubis0000644000004100000410000000011612706205017024541 0ustar www-datawww-data<%= yield_content :foo %> <%= yield_content :foo %> <%= yield_content :foo %> sinatra-contrib-1.4.7/spec/content_for/multiple_blocks.slim0000644000004100000410000000020412706205017024176 0ustar www-datawww-data- content_for :foo do | foo - content_for :foo do | bar - content_for :baz do | WON'T RENDER ME - content_for :foo do | baz sinatra-contrib-1.4.7/spec/content_for/layout.erb0000644000004100000410000000003112706205017022125 0ustar www-datawww-data<%= yield_content :foo %>sinatra-contrib-1.4.7/spec/content_for/layout.erubis0000644000004100000410000000003112706205017022646 0ustar www-datawww-data<%= yield_content :foo %>sinatra-contrib-1.4.7/spec/content_for/passes_values.erb0000644000004100000410000000003712706205017023473 0ustar www-datawww-data<%= yield_content :foo, 1, 2 %>sinatra-contrib-1.4.7/spec/content_for/same_key.slim0000644000004100000410000000003612706205017022606 0ustar www-datawww-data- content_for :foo do | foo sinatra-contrib-1.4.7/spec/content_for/multiple_yields.haml0000644000004100000410000000007712706205017024177 0ustar www-datawww-data= yield_content :foo = yield_content :foo = yield_content :foo sinatra-contrib-1.4.7/spec/content_for/footer.erubis0000644000004100000410000000010012706205017022624 0ustar www-datawww-data<% if content_for? :foo %> <%= yield_content :foo %> <% end %>sinatra-contrib-1.4.7/spec/content_for/multiple_yields.erb0000644000004100000410000000011612706205017024020 0ustar www-datawww-data<%= yield_content :foo %> <%= yield_content :foo %> <%= yield_content :foo %> sinatra-contrib-1.4.7/spec/content_for/different_key.erubis0000644000004100000410000000004612706205017024155 0ustar www-datawww-data<% content_for :bar do %>bar<% end %> sinatra-contrib-1.4.7/spec/content_for/takes_values.erb0000644000004100000410000000010112706205017023274 0ustar www-datawww-data<% content_for :foo do |a, b| %><%= a %> <%= b %><% end %>sinatra-contrib-1.4.7/spec/content_for/passes_values.erubis0000644000004100000410000000003712706205017024214 0ustar www-datawww-data<%= yield_content :foo, 1, 2 %>sinatra-contrib-1.4.7/spec/content_for/different_key.haml0000644000004100000410000000003412706205017023602 0ustar www-datawww-data- content_for :bar do bar sinatra-contrib-1.4.7/spec/content_for/multiple_blocks.erubis0000644000004100000410000000024412706205017024527 0ustar www-datawww-data<% content_for :foo do %>foo<% end %> <% content_for :foo do %>bar<% end %> <% content_for :baz do %>WON'T RENDER ME<% end %> <% content_for :foo do %>baz<% end %> sinatra-contrib-1.4.7/spec/config_file/0000755000004100000410000000000012706205017020050 5ustar www-datawww-datasinatra-contrib-1.4.7/spec/config_file/with_nested_envs.yml0000644000004100000410000000031012706205017024135 0ustar www-datawww-data--- database: production: adapter: postgresql database: foo_production development: adapter: sqlite database: db/development.db test: adapter: sqlite database: db/test.dbsinatra-contrib-1.4.7/spec/config_file/key_value.yml.erb0000644000004100000410000000011712706205017023325 0ustar www-datawww-data--- foo: <%= "bar" %> something: <%= 42 %> nested: a: <%= 1 %> b: <%= 2 %> sinatra-contrib-1.4.7/spec/config_file/key_value.yml0000644000004100000410000000006112706205017022554 0ustar www-datawww-data--- foo: bar something: 42 nested: a: 1 b: 2 sinatra-contrib-1.4.7/spec/config_file/missing_env.yml0000644000004100000410000000005312706205017023112 0ustar www-datawww-data--- foo: production: 10 development: 20sinatra-contrib-1.4.7/spec/config_file/with_envs.yml0000644000004100000410000000012412706205017022576 0ustar www-datawww-data--- development: foo: development production: foo: production test: foo: test sinatra-contrib-1.4.7/spec/config_file/key_value_override.yml0000644000004100000410000000001512706205017024452 0ustar www-datawww-data--- foo: foo sinatra-contrib-1.4.7/spec/respond_with_spec.rb0000644000004100000410000002027312706205017021654 0ustar www-datawww-datarequire 'multi_json' require 'spec_helper' require 'okjson' 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' OkJson.decode(req(:json).body).should == {'a' => 'b'} end it 'handles multiple routes correctly' do respond_app do get('/') { respond_with 'a' => 'b' } get('/:name') { respond_with 'a' => params[:name] } end OkJson.decode(req('/', :json).body).should == {'a' => 'b'} OkJson.decode(req('/b', :json).body).should == {'a' => 'b'} OkJson.decode(req('/c', :json).body).should == {'a' => 'c'} 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 == "guten Tag!" 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 #json if no template is found' do respond_with :foo, :name => 'World' req(:json).should be_ok OkJson.decode(body).should == {'name' => 'World'} end it 'favors templates over #json' do respond_with :bar, :name => 'World' req(:json).should be_ok body.should == 'json!' end it 'falls back to to_EXT if no template is found' do object = {:name => 'World'} def object.to_pdf; "hi" end respond_with :foo, object req(:pdf).should be_ok body.should == "hi" end unless defined? JRUBY_VERSION it 'uses yajl for json' do respond_with :baz req(:json).should be_ok body.should == "\"yajl!\"" end 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 describe "inherited" do it "registers RespondWith in an inherited app" do app = Sinatra.new do set :app_file, __FILE__ set :views, root + '/respond_with' register Sinatra::RespondWith get '/a' do respond_with :json end end self.app = Sinatra.new(app) req('/a', :json).should_not be_ok 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 sinatra-contrib-1.4.7/spec/namespace_spec.rb0000644000004100000410000005561012706205017021106 0ustar www-datawww-datarequire '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 it 'prefixes the path with the namespace' 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 context 'when namespace is a string' do it 'accepts routes with no path' do namespace('/foo') { send(verb) { 'bar' } } send(verb, '/foo').should be_ok body.should == 'bar' unless verb == :head end it 'accepts the path as a named parameter' 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 'accepts the path as a regular expression' do namespace('/foo') { send(verb, /\/\d\d/) { 'bar' }} send(verb, '/foo/12').should be_ok body.should eq 'bar' unless verb == :head send(verb, '/foo/123').should_not be_ok end end context 'when namespace is a named parameter' do it 'accepts routes with no path' do namespace('/:foo') { send(verb) { 'bar' } } send(verb, '/foo').should be_ok body.should == 'bar' unless verb == :head end it 'sets the parameter correctly' do namespace('/:foo') { send(verb, '/bar') { params[:foo] }} send(verb, '/foo/bar').should be_ok body.should == 'foo' unless verb == :head send(verb, '/fox/bar').should be_ok body.should == 'fox' unless verb == :head send(verb, '/foo/baz').should_not be_ok end it 'accepts the path as a named parameter' 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 'accepts the path as regular expression' do namespace('/:foo') { send(verb, %r{/bar}) { params[:foo] }} send(verb, '/foo/bar').should be_ok body.should == 'foo' unless verb == :head send(verb, '/fox/bar').should be_ok body.should == 'fox' unless verb == :head send(verb, '/foo/baz').should_not be_ok end end context 'when namespace is a regular expression' do it 'accepts routes with no path' do namespace(%r{/foo}) { send(verb) { 'bar' } } send(verb, '/foo').should be_ok body.should == 'bar' unless verb == :head end it 'accepts the path as a named parameter' 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 'accepts the path as a regular expression' 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 end context 'when namespace is a splat' do it 'accepts the path as a splat' do namespace('/*') { send(verb, '/*') { params[:splat].join ' - ' }} send(verb, '/foo/bar').should be_ok body.should == 'foo - bar' unless verb == :head end end describe 'before-filters' do specify 'are triggered' do ran = false namespace('/foo') { before { ran = true }} send(verb, '/foo') ran.should be true end specify 'are not triggered for a different namespace' do ran = false namespace('/foo') { before { ran = true }} send(verb, '/fox') ran.should be false end end describe 'after-filters' do specify 'are triggered' do ran = false namespace('/foo') { after { ran = true }} send(verb, '/foo') ran.should be true end specify 'are not triggered for a different namespace' do ran = false namespace('/foo') { after { ran = true }} send(verb, '/fox') ran.should be false end end describe 'conditions' do context 'when the namespace has no prefix' do specify 'are accepted in the namespace' 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 specify 'are accepted in the route definition' 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 specify 'are accepted in the before-filter' 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 specify 'are accepted in the after-filter' 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 end context 'when the namespace is a string' do specify 'are accepted in the namespace' do namespace '/foo', :host_name => 'example.com' do send(verb) { 'ok' } end send(verb, '/foo', {}, 'HTTP_HOST' => 'example.com').should be_ok send(verb, '/foo', {}, 'HTTP_HOST' => 'example.org').should_not be_ok end specify 'are accepted in the before-filter' 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 specify 'are accepted in the after-filter' 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 specify 'are accepted in the route definition' 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 context 'when the namespace has a condition' do specify 'are accepted in the before-filter' 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 specify 'are accepted in the filters' 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 end end end describe 'helpers' do it 'are defined using 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 'can be defined as normal methods' 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 'can be defined using module mixins' 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 specify 'are unavailable outside the namespace where they are defined' 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 specify 'are unavailable outside the namespace that they are mixed into' 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 specify 'are available to nested namespaces' 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 specify 'can call super from nested definitions' 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 specify 'does not provide access to nested helper methods' 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 'accepts a nested namespace as a named parameter' 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 handling' do it 'can be customized using the not_found block' do namespace('/de') do not_found { 'nicht gefunden' } end send(verb, '/foo').status.should eq 404 last_response.body.should_not eq 'nicht gefunden' unless verb == :head get('/en/foo').status.should eq 404 last_response.body.should_not eq 'nicht gefunden' unless verb == :head get('/de/foo').status.should eq 404 last_response.body.should eq 'nicht gefunden' unless verb == :head end it 'can be customized for specific error codes' do namespace('/de') do error(404) { 'nicht gefunden' } end send(verb, '/foo').status.should eq 404 last_response.body.should_not eq 'nicht gefunden' unless verb == :head get('/en/foo').status.should eq 404 last_response.body.should_not eq 'nicht gefunden' unless verb == :head get('/de/foo').status.should eq 404 last_response.body.should eq 'nicht gefunden' unless verb == :head end it 'falls back to the handler defined in the base app' do mock_app do error(404) { 'not found...' } namespace('/en') do end namespace('/de') do error(404) { 'nicht gefunden' } end end send(verb, '/foo').status.should eq 404 last_response.body.should eq 'not found...' unless verb == :head get('/en/foo').status.should eq 404 last_response.body.should eq 'not found...' unless verb == :head get('/de/foo').status.should eq 404 last_response.body.should eq 'nicht gefunden' unless verb == :head end it 'can be customized for specific Exception classes' do mock_app do class AError < StandardError; end class BError < AError; end error(AError) do body('auth failed') 401 end namespace('/en') do get '/foo' do raise BError end end namespace('/de') do error(AError) do body('methode nicht erlaubt') 406 end get '/foo' do raise BError end end end get('/en/foo').status.should eq 401 last_response.body.should eq 'auth failed' unless verb == :head get('/de/foo').status.should eq 406 last_response.body.should eq 'methode nicht erlaubt' unless verb == :head end it "allows custom error handlers when namespace is declared as /en/:id. Issue #119" do mock_app { class CError < StandardError; end error { raise "should not come here" } namespace('/en/:id') do error(CError) { 201 } get '/?' do raise CError end end } get('/en/1').status.should == 201 end end unless verb == :head describe 'templates' do specify 'default to the base app\'s template' do mock_app do template(:foo) { 'hi' } send(verb, '/') { erb :foo } namespace '/foo' do send(verb) { erb :foo } end end send(verb, '/').body.should eq 'hi' send(verb, '/foo').body.should eq 'hi' end specify 'can be nested' do mock_app do template(:foo) { 'hi' } send(verb, '/') { erb :foo } namespace '/foo' do template(:foo) { 'ho' } send(verb) { erb :foo } end end send(verb, '/').body.should eq 'hi' send(verb, '/foo').body.should eq 'ho' end specify 'can use a custom views directory' do mock_app do set :views, File.expand_path('../namespace', __FILE__) send(verb, '/') { erb :foo } namespace('/foo') do set :views, File.expand_path('../namespace/nested', __FILE__) send(verb) { erb :foo } end end send(verb, '/').body.should eq "hi\n" send(verb, '/foo').body.should eq "ho\n" end specify 'default to the base app\'s layout' do mock_app do layout { 'he said: <%= yield %>' } template(:foo) { 'hi' } send(verb, '/') { erb :foo } namespace '/foo' do template(:foo) { 'ho' } send(verb) { erb :foo } end end send(verb, '/').body.should eq 'he said: hi' send(verb, '/foo').body.should eq 'he said: ho' end specify 'can define nested layouts' do mock_app do layout { 'Hello <%= yield %>!' } template(:foo) { 'World' } send(verb, '/') { erb :foo } namespace '/foo' do layout { 'Hi <%= yield %>!' } send(verb) { erb :foo } end end send(verb, '/').body.should eq 'Hello World!' send(verb, '/foo').body.should eq 'Hi World!' end end end describe 'extensions' do specify 'provide read access to settings' do value = nil mock_app do set :foo, 42 namespace '/foo' do value = foo end end value.should eq 42 end specify 'can be registered within a namespace' 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 eq 'CUSTOM!!!' b.should_not eq 'CUSTOM!!!' end specify 'trigger the 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 eq '/foo' end specify 'prevent app-global settings from being changed' do proc { namespace('/') { set :foo, :bar }}.should raise_error end end end end describe 'settings' do it 'provides access to top-level settings' do mock_app do set :foo, 'ok' namespace '/foo' do get '/bar' do settings.foo end end end get('/foo/bar').status.should == 200 last_response.body.should == 'ok' end it 'uses some repro' do mock_app do set :foo, 42 namespace '/foo' do get '/bar' do #settings.respond_to?(:foo).to_s settings.foo.to_s end end end get('/foo/bar').status.should == 200 last_response.body.should == '42' end it 'allows checking setting existence with respond_to?' do mock_app do set :foo, 42 namespace '/foo' do get '/bar' do settings.respond_to?(:foo).to_s end end end get('/foo/bar').status.should == 200 last_response.body.should == 'true' end end end sinatra-contrib-1.4.7/spec/custom_logger_spec.rb0000644000004100000410000000157712706205017022026 0ustar www-datawww-datarequire 'spec_helper' require 'sinatra/custom_logger' describe Sinatra::CustomLogger do before do rack_logger = @rack_logger = double mock_app do helpers Sinatra::CustomLogger before do env['rack.logger'] = rack_logger end get '/' do logger.info 'Logged message' 'Response' end end end describe '#logger' do it 'falls back to request.logger' do expect(@rack_logger).to receive(:info).with('Logged message') get '/' end context 'logger setting is set' do before do custom_logger = @custom_logger = double @app.class_eval do configure do set :logger, custom_logger end end end it 'calls custom logger' do expect(@custom_logger).to receive(:info).with('Logged message') get '/' end end end end sinatra-contrib-1.4.7/spec/json_spec.rb0000644000004100000410000000627112706205017020122 0ustar www-datawww-datarequire 'multi_json' require 'spec_helper' require 'okjson' shared_examples_for "a json encoder" do |lib, const| before do begin require lib if lib @encoder = eval(const) rescue LoadError pending "unable to load #{lib}" end end it "allows setting :encoder to #{const}" do enc = @encoder mock_app { get('/') { json({'foo' => 'bar'}, :encoder => enc) }} results_in 'foo' => 'bar' end it "allows setting settings.json_encoder to #{const}" do enc = @encoder mock_app do set :json_encoder, enc get('/') { json 'foo' => 'bar' } end results_in 'foo' => 'bar' end end describe Sinatra::JSON do def mock_app(&block) super do class_eval(&block) end end def results_in(obj) OkJson.decode(get('/').body).should == obj end it "encodes objects to json out of the box" do mock_app { get('/') { json :foo => [1, 'bar', nil] } } results_in 'foo' => [1, 'bar', nil] end it "sets the content type to 'application/json'" do mock_app { get('/') { json({}) } } get('/')["Content-Type"].should include("application/json") end it "allows overriding content type with :content_type" do mock_app { get('/') { json({}, :content_type => "foo/bar") } } get('/')["Content-Type"].should == "foo/bar" end it "accepts shorthands for :content_type" do mock_app { get('/') { json({}, :content_type => :js) } } get('/')["Content-Type"].should == "application/javascript;charset=utf-8" end it 'calls generate on :encoder if available' do enc = Object.new def enc.generate(obj) obj.inspect end mock_app { get('/') { json(42, :encoder => enc) }} get('/').body.should == '42' end it 'calls encode on :encoder if available' do enc = Object.new def enc.encode(obj) obj.inspect end mock_app { get('/') { json(42, :encoder => enc) }} get('/').body.should == '42' end it 'sends :encoder as method call if it is a Symbol' do mock_app { get('/') { json(42, :encoder => :inspect) }} get('/').body.should == '42' end it 'calls generate on settings.json_encoder if available' do enc = Object.new def enc.generate(obj) obj.inspect end mock_app do set :json_encoder, enc get('/') { json 42 } end get('/').body.should == '42' end it 'calls encode on settings.json_encode if available' do enc = Object.new def enc.encode(obj) obj.inspect end mock_app do set :json_encoder, enc get('/') { json 42 } end get('/').body.should == '42' end it 'sends settings.json_encode as method call if it is a Symbol' do mock_app do set :json_encoder, :inspect get('/') { json 42 } end get('/').body.should == '42' end describe('Yajl') { it_should_behave_like "a json encoder", "yajl", "Yajl::Encoder" } unless defined? JRUBY_VERSION describe('JSON') { it_should_behave_like "a json encoder", "json", "::JSON" } describe('OkJson') { it_should_behave_like "a json encoder", nil, "OkJson" } describe('to_json') { it_should_behave_like "a json encoder", "json", ":to_json" } describe('without') { it_should_behave_like "a json encoder", nil, "Sinatra::JSON" } end sinatra-contrib-1.4.7/spec/cookies_spec.rb0000644000004100000410000004601312706205017020603 0ustar www-datawww-datarequire 'spec_helper' describe Sinatra::Cookies do def cookie_route(*cookies, &block) result = nil set_cookie(cookies) @cookie_app.get('/') do result = instance_eval(&block) "ok" end get '/', {}, @headers || {} last_response.should be_ok body.should be == "ok" result end def cookies(*set_cookies) cookie_route(*set_cookies) { cookies } end before do app = nil mock_app do helpers Sinatra::Cookies app = self end @cookie_app = app clear_cookies end describe :cookie_route do it 'runs the block' do ran = false cookie_route { ran = true } ran.should be true end it 'returns the block result' do cookie_route { 42 }.should be == 42 end end describe :== do it 'is comparable to hashes' do cookies.should be == {} end it 'is comparable to anything that responds to to_hash' do other = Struct.new(:to_hash).new({}) cookies.should be == other end end describe :[] do it 'allows access to request cookies' do cookies("foo=bar")["foo"].should be == "bar" end it 'takes symbols as keys' do cookies("foo=bar")[:foo].should be == "bar" end it 'returns nil for missing keys' do cookies("foo=bar")['bar'].should be_nil end it 'allows access to response cookies' do cookie_route do response.set_cookie 'foo', 'bar' cookies['foo'] end.should be == 'bar' end it 'favors response cookies over request cookies' do cookie_route('foo=bar') do response.set_cookie 'foo', 'baz' cookies['foo'] end.should be == 'baz' end it 'takes the last value for response cookies' do cookie_route do response.set_cookie 'foo', 'bar' response.set_cookie 'foo', 'baz' cookies['foo'] end.should be == 'baz' end end describe :[]= do it 'sets cookies to httponly' do cookie_route do cookies['foo'] = 'bar' response['Set-Cookie'].lines.detect { |l| l.start_with? 'foo=' } end.should include('HttpOnly') end it 'sets domain to nil if localhost' do @headers = {'HTTP_HOST' => 'localhost'} cookie_route do cookies['foo'] = 'bar' response['Set-Cookie'] end.should_not include("domain") end it 'sets the domain' do cookie_route do cookies['foo'] = 'bar' response['Set-Cookie'].lines.detect { |l| l.start_with? 'foo=' } end.should include('domain=example.org') end it 'sets path to / by default' do cookie_route do cookies['foo'] = 'bar' response['Set-Cookie'].lines.detect { |l| l.start_with? 'foo=' } end.should include('path=/') end it 'sets path to the script_name if app is nested' do cookie_route do request.script_name = '/foo' cookies['foo'] = 'bar' response['Set-Cookie'].lines.detect { |l| l.start_with? 'foo=' } end.should include('path=/foo') end it 'sets a cookie' do cookie_route { cookies['foo'] = 'bar' } cookie_jar['foo'].should be == 'bar' end it 'adds a value to the cookies hash' do cookie_route do cookies['foo'] = 'bar' cookies['foo'] end.should be == 'bar' end end describe :assoc do it 'behaves like Hash#assoc' do cookies('foo=bar').assoc('foo') == ['foo', 'bar'] end end if Hash.method_defined? :assoc describe :clear do it 'removes request cookies from cookies hash' do jar = cookies('foo=bar') jar['foo'].should be == 'bar' jar.clear jar['foo'].should be_nil end it 'removes response cookies from cookies hash' do cookie_route do cookies['foo'] = 'bar' cookies.clear cookies['foo'] end.should be_nil end it 'expires existing cookies' do cookie_route("foo=bar") do cookies.clear response['Set-Cookie'] end.should include("foo=;", "expires=", "1970 00:00:00") end end describe :compare_by_identity? do it { cookies.should_not be_compare_by_identity } end describe :default do it { cookies.default.should be_nil } end describe :default_proc do it { cookies.default_proc.should be_nil } end describe :delete do it 'removes request cookies from cookies hash' do jar = cookies('foo=bar') jar['foo'].should be == 'bar' jar.delete 'foo' jar['foo'].should be_nil end it 'removes response cookies from cookies hash' do cookie_route do cookies['foo'] = 'bar' cookies.delete 'foo' cookies['foo'] end.should be_nil end it 'expires existing cookies' do cookie_route("foo=bar") do cookies.delete 'foo' response['Set-Cookie'] end.should include("foo=;", "expires=", "1970 00:00:00") end it 'honours the app cookie_options' do @cookie_app.class_eval do set :cookie_options, { :path => '/foo', :domain => 'bar.com', :secure => true, :httponly => true } end cookie_header = cookie_route("foo=bar") do cookies.delete 'foo' response['Set-Cookie'] end cookie_header.should include("path=/foo;", "domain=bar.com;", "secure;", "HttpOnly") end it 'does not touch other cookies' do cookie_route("foo=bar", "bar=baz") do cookies.delete 'foo' cookies['bar'] end.should be == 'baz' end it 'returns the previous value for request cookies' do cookie_route("foo=bar") do cookies.delete "foo" end.should be == "bar" end it 'returns the previous value for response cookies' do cookie_route do cookies['foo'] = 'bar' cookies.delete "foo" end.should be == "bar" end it 'returns nil for non-existing cookies' do cookie_route { cookies.delete("foo") }.should be_nil end end describe :delete_if do it 'deletes cookies that match the block' do cookie_route('foo=bar') do cookies['bar'] = 'baz' cookies['baz'] = 'foo' cookies.delete_if { |*a| a.include? 'bar' } cookies.values_at 'foo', 'bar', 'baz' end.should be == [nil, nil, 'foo'] end end describe :each do it 'loops through cookies' do keys = [] foo = nil bar = nil cookie_route('foo=bar', 'bar=baz') do cookies.each do |key, value| foo = value if key == 'foo' bar = value if key == 'bar' keys << key end end keys.sort.should be == ['bar', 'foo'] foo.should be == 'bar' bar.should be == 'baz' end it 'favors response over request cookies' do seen = false cookie_route('foo=bar') do cookies[:foo] = 'baz' cookies.each do |key, value| key.should == 'foo' value.should == 'baz' seen.should == false seen = true end end end it 'does not loop through deleted cookies' do cookie_route('foo=bar') do cookies.delete :foo cookies.each { fail } end end it 'returns an enumerator' do cookie_route('foo=bar') do enum = cookies.each enum.each { |key, value| key.should == 'foo' } end end end describe :each_key do it 'loops through cookies' do keys = [] cookie_route('foo=bar', 'bar=baz') do cookies.each_key do |key| keys << key end end keys.sort.should be == ['bar', 'foo'] end it 'only yields keys once' do seen = false cookie_route('foo=bar') do cookies[:foo] = 'baz' cookies.each_key do |key| seen.should == false seen = true end end end it 'does not loop through deleted cookies' do cookie_route('foo=bar') do cookies.delete :foo cookies.each_key { fail } end end it 'returns an enumerator' do cookie_route('foo=bar') do enum = cookies.each_key enum.each { |key| key.should == 'foo' } end end end describe :each_pair do it 'loops through cookies' do keys = [] foo = nil bar = nil cookie_route('foo=bar', 'bar=baz') do cookies.each_pair do |key, value| foo = value if key == 'foo' bar = value if key == 'bar' keys << key end end keys.sort.should be == ['bar', 'foo'] foo.should be == 'bar' bar.should be == 'baz' end it 'favors response over request cookies' do seen = false cookie_route('foo=bar') do cookies[:foo] = 'baz' cookies.each_pair do |key, value| key.should == 'foo' value.should == 'baz' seen.should == false seen = true end end end it 'does not loop through deleted cookies' do cookie_route('foo=bar') do cookies.delete :foo cookies.each_pair { fail } end end it 'returns an enumerator' do cookie_route('foo=bar') do enum = cookies.each_pair enum.each { |key, value| key.should == 'foo' } end end end describe :each_value do it 'loops through cookies' do values = [] cookie_route('foo=bar', 'bar=baz') do cookies.each_value do |value| values << value end end values.sort.should be == ['bar', 'baz'] end it 'favors response over request cookies' do seen = false cookie_route('foo=bar') do cookies[:foo] = 'baz' cookies.each_value do |value| value.should == 'baz' seen.should == false seen = true end end end it 'does not loop through deleted cookies' do cookie_route('foo=bar') do cookies.delete :foo cookies.each_value { fail } end end it 'returns an enumerator' do cookie_route('foo=bar') do enum = cookies.each_value enum.each { |value| value.should == 'bar' } end end end describe :empty? do it 'returns true if there are no cookies' do cookies.should be_empty end it 'returns false if there are request cookies' do cookies('foo=bar').should_not be_empty end it 'returns false if there are response cookies' do cookie_route do cookies['foo'] = 'bar' cookies.empty? end.should be false end it 'becomes true if response cookies are removed' do cookie_route do cookies['foo'] = 'bar' cookies.delete :foo cookies.empty? end.should be true end it 'becomes true if request cookies are removed' do cookie_route('foo=bar') do cookies.delete :foo cookies.empty? end.should be_truthy end it 'becomes true after clear' do cookie_route('foo=bar', 'bar=baz') do cookies['foo'] = 'bar' cookies.clear cookies.empty? end.should be_truthy end end describe :fetch do it 'returns values from request cookies' do cookies('foo=bar').fetch('foo').should be == 'bar' end it 'returns values from response cookies' do cookie_route do cookies['foo'] = 'bar' cookies.fetch('foo') end.should be == 'bar' end it 'favors response over request cookies' do cookie_route('foo=baz') do cookies['foo'] = 'bar' cookies.fetch('foo') end.should be == 'bar' end it 'raises an exception if key does not exist' do error = if defined? JRUBY_VERSION IndexError else RUBY_VERSION >= '1.9' ? KeyError : IndexError end expect { cookies.fetch('foo') }.to raise_exception(error) end it 'returns the block result if missing' do cookies.fetch('foo') { 'bar' }.should be == 'bar' end end describe :flatten do it { cookies('foo=bar').flatten.should be == {'foo' => 'bar'}.flatten } end if Hash.method_defined? :flatten describe :has_key? do it 'checks request cookies' do cookies('foo=bar').should have_key('foo') end it 'checks response cookies' do jar = cookies jar['foo'] = 'bar' jar.should have_key(:foo) end it 'does not use deleted cookies' do jar = cookies('foo=bar') jar.delete :foo jar.should_not have_key('foo') end end describe :has_value? do it 'checks request cookies' do cookies('foo=bar').should have_value('bar') end it 'checks response cookies' do jar = cookies jar[:foo] = 'bar' jar.should have_value('bar') end it 'does not use deleted cookies' do jar = cookies('foo=bar') jar.delete :foo jar.should_not have_value('bar') end end describe :include? do it 'checks request cookies' do cookies('foo=bar').should include('foo') end it 'checks response cookies' do jar = cookies jar['foo'] = 'bar' jar.should include(:foo) end it 'does not use deleted cookies' do jar = cookies('foo=bar') jar.delete :foo jar.should_not include('foo') end end describe :index do it 'checks request cookies' do cookies('foo=bar').index('bar').should be == 'foo' end it 'checks response cookies' do jar = cookies jar['foo'] = 'bar' jar.index('bar').should be == 'foo' end it 'returns nil when missing' do cookies('foo=bar').index('baz').should be_nil end end if RUBY_VERSION < '1.9' describe :keep_if do it 'removes entries' do jar = cookies('foo=bar', 'bar=baz') jar.keep_if { |*args| args == ['bar', 'baz'] } jar.should be == {'bar' => 'baz'} end end describe :key do it 'checks request cookies' do cookies('foo=bar').key('bar').should be == 'foo' end it 'checks response cookies' do jar = cookies jar['foo'] = 'bar' jar.key('bar').should be == 'foo' end it 'returns nil when missing' do cookies('foo=bar').key('baz').should be_nil end end describe :key? do it 'checks request cookies' do cookies('foo=bar').key?('foo').should be true end it 'checks response cookies' do jar = cookies jar['foo'] = 'bar' jar.key?(:foo).should be true end it 'does not use deleted cookies' do jar = cookies('foo=bar') jar.delete :foo jar.key?('foo').should be false end end describe :keys do it { cookies('foo=bar').keys.should == ['foo'] } end describe :length do it { cookies.length.should == 0 } it { cookies('foo=bar').length.should == 1 } end describe :member? do it 'checks request cookies' do cookies('foo=bar').member?('foo').should be true end it 'checks response cookies' do jar = cookies jar['foo'] = 'bar' jar.member?(:foo).should be true end it 'does not use deleted cookies' do jar = cookies('foo=bar') jar.delete :foo jar.member?('foo').should be false end end describe :merge do it 'is mergable with a hash' do cookies('foo=bar').merge(:bar => :baz).should be == {"foo" => "bar", :bar => :baz} end it 'does not create cookies' do jar = cookies('foo=bar') jar.merge(:bar => 'baz') jar.should_not include(:bar) end it 'takes a block for conflict resolution' do update = {'foo' => 'baz', 'bar' => 'baz'} merged = cookies('foo=bar').merge(update) do |key, old, other| key.should be == 'foo' old.should be == 'bar' other.should be == 'baz' 'foo' end merged['foo'].should be == 'foo' end end describe :merge! do it 'creates cookies' do jar = cookies('foo=bar') jar.merge! :bar => 'baz' jar.should include('bar') end it 'overrides existing values' do jar = cookies('foo=bar') jar.merge! :foo => "baz" jar["foo"].should be == "baz" end it 'takes a block for conflict resolution' do update = {'foo' => 'baz', 'bar' => 'baz'} jar = cookies('foo=bar') jar.merge!(update) do |key, old, other| key.should be == 'foo' old.should be == 'bar' other.should be == 'baz' 'foo' end jar['foo'].should be == 'foo' end end describe :rassoc do it 'behaves like Hash#assoc' do cookies('foo=bar').rassoc('bar') == ['foo', 'bar'] end end if Hash.method_defined? :rassoc describe :reject do it 'removes entries from new hash' do jar = cookies('foo=bar', 'bar=baz') sub = jar.reject { |*args| args == ['bar', 'baz'] } sub.should be == {'foo' => 'bar'} jar['bar'].should be == 'baz' end end describe :reject! do it 'removes entries' do jar = cookies('foo=bar', 'bar=baz') jar.reject! { |*args| args == ['bar', 'baz'] } jar.should be == {'foo' => 'bar'} end end describe :replace do it 'replaces entries' do jar = cookies('foo=bar', 'bar=baz') jar.replace 'foo' => 'baz', 'baz' => 'bar' jar.should be == {'foo' => 'baz', 'baz' => 'bar'} end end describe :select do it 'removes entries from new hash' do jar = cookies('foo=bar', 'bar=baz') sub = jar.select { |*args| args != ['bar', 'baz'] } sub.should be == {'foo' => 'bar'}.select { true } jar['bar'].should be == 'baz' end end describe :select! do it 'removes entries' do jar = cookies('foo=bar', 'bar=baz') jar.select! { |*args| args != ['bar', 'baz'] } jar.should be == {'foo' => 'bar'} end end if Hash.method_defined? :select! describe :shift do it 'removes from the hash' do jar = cookies('foo=bar') jar.shift.should be == ['foo', 'bar'] jar.should_not include('bar') end end describe :size do it { cookies.size.should == 0 } it { cookies('foo=bar').size.should == 1 } end describe :update do it 'creates cookies' do jar = cookies('foo=bar') jar.update :bar => 'baz' jar.should include('bar') end it 'overrides existing values' do jar = cookies('foo=bar') jar.update :foo => "baz" jar["foo"].should be == "baz" end it 'takes a block for conflict resolution' do merge = {'foo' => 'baz', 'bar' => 'baz'} jar = cookies('foo=bar') jar.update(merge) do |key, old, other| key.should be == 'foo' old.should be == 'bar' other.should be == 'baz' 'foo' end jar['foo'].should be == 'foo' end end describe :value? do it 'checks request cookies' do cookies('foo=bar').value?('bar').should be true end it 'checks response cookies' do jar = cookies jar[:foo] = 'bar' jar.value?('bar').should be true end it 'does not use deleted cookies' do jar = cookies('foo=bar') jar.delete :foo jar.value?('bar').should be false end end describe :values do it { cookies('foo=bar', 'bar=baz').values.sort.should be == ['bar', 'baz'] } end describe :values_at do it { cookies('foo=bar', 'bar=baz').values_at('foo').should be == ['bar'] } end end sinatra-contrib-1.4.7/spec/content_for_spec.rb0000644000004100000410000001465112706205017021472 0ustar www-datawww-datarequire 'spec_helper' describe Sinatra::ContentFor do subject do Sinatra.new do helpers Sinatra::ContentFor set :views, File.expand_path("../content_for", __FILE__) end.new! end Tilt.prefer Tilt::ERBTemplate extend Forwardable def_delegators :subject, :content_for, :yield_content def render(engine, template) subject.send(:render, engine, template, :layout => false).gsub(/\s/, '') end describe "without templates" do it 'renders blocks declared with the same key you use when rendering' do content_for(:foo) { "foo" } yield_content(:foo).should == "foo" end it 'renders blocks more than once' do content_for(:foo) { "foo" } 3.times { yield_content(:foo).should == "foo" } end it 'does not render a block with a different key' do content_for(:bar) { "bar" } yield_content(:foo).should be_empty end it 'renders multiple blocks with the same key' do content_for(:foo) { "foo" } content_for(:foo) { "bar" } content_for(:bar) { "WON'T RENDER ME" } content_for(:foo) { "baz" } yield_content(:foo).should == "foobarbaz" end it 'renders multiple blocks more than once' do content_for(:foo) { "foo" } content_for(:foo) { "bar" } content_for(:bar) { "WON'T RENDER ME" } content_for(:foo) { "baz" } 3.times { yield_content(:foo).should == "foobarbaz" } end it 'passes values to the blocks' do content_for(:foo) { |a| a.upcase } yield_content(:foo, 'a').should == "A" yield_content(:foo, 'b').should == "B" end end # TODO: liquid radius markaby builder nokogiri engines = %w[erb erubis haml slim] engines.each do |inner| describe inner.capitalize do before :all do begin require inner rescue LoadError => e pending "Skipping: " << e.message end end describe "with yield_content in Ruby" do it 'renders blocks declared with the same key you use when rendering' do render inner, :same_key yield_content(:foo).strip.should == "foo" end it 'renders blocks more than once' do render inner, :same_key 3.times { yield_content(:foo).strip.should == "foo" } end it 'does not render a block with a different key' do render inner, :different_key yield_content(:foo).should be_empty end it 'renders multiple blocks with the same key' do render inner, :multiple_blocks yield_content(:foo).gsub(/\s/, '').should == "foobarbaz" end it 'renders multiple blocks more than once' do render inner, :multiple_blocks 3.times { yield_content(:foo).gsub(/\s/, '').should == "foobarbaz" } end it 'passes values to the blocks' do render inner, :takes_values yield_content(:foo, 1, 2).gsub(/\s/, '').should == "12" end end describe "with content_for in Ruby" do it 'renders blocks declared with the same key you use when rendering' do content_for(:foo) { "foo" } render(inner, :layout).should == "foo" end it 'renders blocks more than once' do content_for(:foo) { "foo" } render(inner, :multiple_yields).should == "foofoofoo" end it 'does not render a block with a different key' do content_for(:bar) { "foo" } render(inner, :layout).should be_empty end it 'renders multiple blocks with the same key' do content_for(:foo) { "foo" } content_for(:foo) { "bar" } content_for(:bar) { "WON'T RENDER ME" } content_for(:foo) { "baz" } render(inner, :layout).should == "foobarbaz" end it 'renders multiple blocks more than once' do content_for(:foo) { "foo" } content_for(:foo) { "bar" } content_for(:bar) { "WON'T RENDER ME" } content_for(:foo) { "baz" } render(inner, :multiple_yields).should == "foobarbazfoobarbazfoobarbaz" end it 'passes values to the blocks' do content_for(:foo) { |a,b| "#{a}#{b}" } render(inner, :passes_values).should == "12" end end describe "with content_for? in Ruby" do it 'renders block if key is set' do content_for(:foo) { "foot" } render(inner, :footer).should == "foot" end it 'does not render a block if different key' do content_for(:different_key) { "foot" } render(inner, :footer).should be_empty end end engines.each do |outer| describe "with yield_content in #{outer.capitalize}" do def body last_response.body.gsub(/\s/, '') end before :all do begin require outer rescue LoadError => e pending "Skipping: " << e.message end end before do mock_app do helpers Sinatra::ContentFor set inner, :layout_engine => outer set :views, File.expand_path("../content_for", __FILE__) get('/:view') { render(inner, params[:view].to_sym) } get('/:layout/:view') do render inner, params[:view].to_sym, :layout => params[:layout].to_sym end end end it 'renders blocks declared with the same key you use when rendering' do get('/same_key').should be_ok body.should == "foo" end it 'renders blocks more than once' do get('/multiple_yields/same_key').should be_ok body.should == "foofoofoo" end it 'does not render a block with a different key' do get('/different_key').should be_ok body.should be_empty end it 'renders multiple blocks with the same key' do get('/multiple_blocks').should be_ok body.should == "foobarbaz" end it 'renders multiple blocks more than once' do get('/multiple_yields/multiple_blocks').should be_ok body.should == "foobarbazfoobarbazfoobarbaz" end it 'passes values to the blocks' do get('/passes_values/takes_values').should be_ok body.should == "12" end end end end end end sinatra-contrib-1.4.7/lib/0000755000004100000410000000000012706205017015420 5ustar www-datawww-datasinatra-contrib-1.4.7/lib/sinatra/0000755000004100000410000000000012706205017017061 5ustar www-datawww-datasinatra-contrib-1.4.7/lib/sinatra/namespace.rb0000644000004100000410000002050112706205017021340 0ustar www-datawww-datarequire 'backports' require 'sinatra/base' require 'sinatra/decompile' module Sinatra # = Sinatra::Namespace # # Sinatra::Namespace is an extension that adds namespaces to an # application. This namespaces will allow you to share a path prefix for the # routes within the namespace, and define filters, conditions and error # handlers exclusively for them. Besides that, you can also register helpers # and extensions that will be used only within the namespace. # # == Usage # # Once you have loaded the extension (see below), you can use the +namespace+ # method to define namespaces in your application. # # You can define a namespace by a path prefix: # # namespace '/blog' do # get { haml :blog } # get '/:entry_permalink' do # @entry = Entry.find_by_permalink!(params[:entry_permalink]) # haml :entry # end # # # More blog routes... # end # # by a condition: # # namespace :host_name => 'localhost' do # get('/admin/dashboard') { haml :dashboard } # get('/admin/login') { haml :login } # # # More admin routes... # end # # or both: # # namespace '/admin', :host_name => 'localhost' do # get('/dashboard') { haml :dashboard } # get('/login') { haml :login } # post('/login') { login_user } # # # More admin routes... # end # # When you define a filter or an error handler, or register an extension or a # set of helpers within a namespace, they only affect the routes defined in # it. For instance, lets define a before filter to prevent the access of # unauthorized users to the admin section of the application: # # namespace '/admin' do # helpers AdminHelpers # before { authenticate unless request.path_info == '/admin/login' } # # get '/dashboard' do # # Only authenticated users can access here... # haml :dashboard # end # # # More admin routes... # end # # get '/' do # # Any user can access here... # haml :index # end # # Well, they actually also affect the nested namespaces: # # namespace '/admin' do # helpers AdminHelpers # before { authenticate unless request.path_info == '/admin/login' } # # namespace '/users' do # get do # # Only authenticated users can access here... # @users = User.all # haml :users # end # # # More user admin routes... # end # # # More admin routes... # end # # === Classic Application Setup # # To be able to use namespaces in a classic application all you need to do is # require the extension: # # require "sinatra" # require "sinatra/namespace" # # # The rest of your classic application code goes here... # # === Modular Application Setup # # To be able to use namespaces in a modular application all you need to do is # require the extension, and then, register it: # # require "sinatra/base" # require "sinatra/namespace" # # class MyApp < Sinatra::Base # register Sinatra::Namespace # # # The rest of your modular application code goes here... # end # module Namespace def self.new(base, pattern, conditions = {}, &block) Module.new do #quelch uninitialized variable warnings, since these get used by compile method. @pattern, @conditions = nil, nil extend NamespacedMethods include InstanceMethods @base, @extensions, @errors = base, [], {} @pattern, @conditions = compile(pattern, conditions) @templates = Hash.new { |h,k| @base.templates[k] } namespace = self before { extend(@namespace = namespace) } class_eval(&block) end end module InstanceMethods def settings @namespace end def template_cache super.fetch(:nested, @namespace) { Tilt::Cache.new } 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, :templates 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 not_found(&block) error(Sinatra::NotFound, &block) end def errors base.errors.merge(namespace_errors) end def namespace_errors @errors end def error(*codes, &block) args = Sinatra::Base.send(:compile!, "ERROR", regexpify(@pattern), block) codes = codes.map { |c| Array(c) }.flatten codes << Exception if codes.empty? codes.each do |c| errors = @errors[c] ||= [] errors << args end end def respond_to(*args) return @conditions[:provides] || base.respond_to if args.empty? @conditions[:provides] = args end def set(key, value = self, &block) raise ArgumentError, "may not set #{key}" if key != :views return key.each { |k,v| set(k, v) } if block.nil? and value == self block ||= proc { value } singleton_class.send(:define_method, key, &block) end def enable(*opts) opts.each { |key| set(key, true) } end def disable(*opts) opts.each { |key| set(key, false) } end def template(name, &block) filename, line = caller_locations.first templates[name] = [block, filename, line.to_i] end def layout(name=:layout, &block) template name, &block 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(method, *args, &block) base.send(method, *args, &block) end def respond_to?(method, include_private = false) super || base.respond_to?(method, include_private) end end module BaseMethods include SharedMethods end def self.extend_object(base) base.extend BaseMethods end end register Sinatra::Namespace Delegator.delegate :namespace end sinatra-contrib-1.4.7/lib/sinatra/decompile.rb0000644000004100000410000000731312706205017021353 0ustar www-datawww-datarequire 'sinatra/base' require 'backports' require 'uri' module Sinatra # = Sinatra::Decompile # # Sinatra::Decompile is an extension that provides a method, # conveniently called +decompile+, that will generate a String pattern for a # given route. # # == Usage # # === Classic Application # # To use the extension in a classic application all you need to do is require # it: # # require "sinatra" # require "sinatra/decompile" # # # Your classic application code goes here... # # This will add the +decompile+ method to the application/class scope, but # you can also call it as Sinatra::Decompile.decompile. # # === Modular Application # # To use the extension in a modular application you need to require it, and # then, tell the application you will use it: # # require "sinatra/base" # require "sinatra/decompile" # # class MyApp < Sinatra::Base # register Sinatra::Decompile # # # The rest of your modular application code goes here... # end # # This will add the +decompile+ method to the application/class scope. You # can choose not to register the extension, but instead of calling # +decompile+, you will need to call 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 it's 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 # # Sinatra 2.0 will come with a mechanism for this, making this obsolete. 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! /^\/(\^|\\A)?|(\$|\\z)?\/$/, '' str.gsub! encoded(' '), ' ' return pattern if str =~ /^[\.\+]/ str.gsub! '((?:[^\.\/?#%]|(?:%[^2].|%[2][^Ee]))+)', '([^\/?#]+)' str.gsub! '((?:[^\/?#%]|(?:%[^2].|%[2][^Ee]))+)', '([^\/?#]+)' str.gsub! /\([^\(\)]*\)|\([^\(\)]*\([^\(\)]*\)[^\(\)]*\)/ do |part| case part when '(.*?)' return pattern if keys.shift != 'splat' '*' when /^\(\?\:(\\*.)\|%[\w\[\]]+\)$/ $1 when /^\(\?\:(%\d+)\|([^\)]+|\([^\)]+\))\)$/ URI.unescape($1) when '([^\/?#]+)' return pattern if keys.empty? ":" << keys.shift when /^\(\?\:\\?(.)\|/ char = $1 return pattern unless encoded(char) == part Regexp.escape(char) else return pattern end end str.gsub /(.)([\.\+\(\)\/])/ do return pattern if $1 != "\\" $2 end end private def encoded(char) return super if defined? super enc = uri_parser.escape(char) enc = "(?:#{escaped(char, enc).join('|')})" if enc == char enc = "(?:#{enc}|#{encoded('+')})" if char == " " enc end def uri_parser #TODO: Remove check after dropping support for 1.8.7 @_uri_parser ||= defined?(URI::Parser) ? URI::Parser.new : URI end end register Decompile end sinatra-contrib-1.4.7/lib/sinatra/contrib.rb0000644000004100000410000000160112706205017021044 0ustar www-datawww-datarequire '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 :MultiRoute register :Namespace register :RespondWith helpers :Capture helpers :ContentFor helpers :Cookies helpers :EngineTracking helpers :JSON helpers :LinkHeader helpers :Streaming end ## # Other extensions you don't want to be loaded unless needed. module Custom # register :Compass register :Decompile register :Reloader end ## # Stuff that aren't Sinatra extensions, technically. autoload :Extension autoload :TestHelpers end register Sinatra::Contrib::Common end sinatra-contrib-1.4.7/lib/sinatra/reloader.rb0000755000004100000410000003174512706205017021220 0ustar www-datawww-datarequire 'sinatra/base' module Sinatra # = Sinatra::Reloader # # Extension to reload modified files. Useful during development, # since it will automatically require files defining routes, filters, # error handlers and inline templates, with every incoming request, # but only if they have been updated. # # == Usage # # === Classic Application # # To enable the reloader in a classic application all you need to do is # require it: # # require "sinatra" # require "sinatra/reloader" if development? # # # Your classic application code goes here... # # === Modular Application # # To enable the reloader in a modular application all you need to do is # require it, and then, register it: # # require "sinatra/base" # require "sinatra/reloader" # # class MyApp < Sinatra::Base # configure :development do # register Sinatra::Reloader # end # # # Your modular application code goes here... # end # # == Using the Reloader in Other Environments # # By default, the reloader is only enabled for the development # environment. Similar to registering the reloader in a modular # application, a classic application requires manually enabling the # extension for it to be available in a non-development environment. # # require "sinatra" # require "sinatra/reloader" # # configure :production do # enable :reloader # end # # == Changing the Reloading Policy # # You can refine the reloading policy with +also_reload+ and # +dont_reload+, to customize which files should, and should not, be # reloaded, respectively. # # === Classic Application # # Simply call the methods: # # require "sinatra" # require "sinatra/reloader" if development? # # also_reload '/path/to/some/file' # dont_reload '/path/to/other/file' # # # Your classic application code goes here... # # === Modular Application # # Call the methods inside the +configure+ block: # # require "sinatra/base" # require "sinatra/reloader" # # class MyApp < Sinatra::Base # configure :development do # register Sinatra::Reloader # also_reload '/path/to/some/file' # dont_reload '/path/to/other/file' # end # # # Your modular application code goes here... # end # module Reloader # Watches a file so it can tell when it has been updated, and what # elements does it contain. class Watcher # Represents an element of a Sinatra application that may need to # be reloaded. An element could be: # * a route # * a filter # * an error handler # * a middleware # * inline templates # # Its +representation+ attribute is there to allow to identify the # element within an application, that is, to match it with its # Sinatra's internal representation. class Element < Struct.new(:type, :representation) end # Collection of file +Watcher+ that can be associated with a # Sinatra application. That way, we can know which files belong # to a given application and which files have been modified. It # also provides a mechanism to inform a Watcher of the elements # defined in the file being watched and if its changes should be # ignored. class List @app_list_map = Hash.new { |hash, key| hash[key] = new } # Returns the +List+ for the application +app+. def self.for(app) @app_list_map[app] end # Creates a new +List+ instance. def initialize @path_watcher_map = Hash.new do |hash, key| hash[key] = Watcher.new(key) end end # Lets the +Watcher+ for the file located at +path+ know that the # +element+ is defined there, and adds the +Watcher+ to the +List+, # if it isn't already there. def watch(path, element) watcher_for(path).elements << element end # Tells the +Watcher+ for the file located at +path+ to ignore # the file changes, and adds the +Watcher+ to the +List+, if # it isn't already there. def ignore(path) watcher_for(path).ignore end # Adds a +Watcher+ for the file located at +path+ to the # +List+, if it isn't already there. def watcher_for(path) @path_watcher_map[File.expand_path(path)] end alias watch_file watcher_for # Returns an array with all the watchers in the +List+. def watchers @path_watcher_map.values end # Returns an array with all the watchers in the +List+ that # have been updated. def updated watchers.find_all(&:updated?) end end attr_reader :path, :elements, :mtime # Creates a new +Watcher+ instance for the file located at +path+. def initialize(path) @ignore = nil @path, @elements = path, [] update end # Indicates whether or not the file being watched has been modified. def updated? !ignore? && !removed? && mtime != File.mtime(path) end # Updates the mtime of the file being watched. def update @mtime = File.mtime(path) end # Indicates whether or not the file being watched has inline # templates. def inline_templates? elements.any? { |element| element.type == :inline_templates } end # Informs that the modifications to the file being watched # should be ignored. def ignore @ignore = true end # Indicates whether or not the modifications to the file being # watched should be ignored. def ignore? !!@ignore end # Indicates whether or not the file being watched has been removed. def removed? !File.exist?(path) end end MUTEX_FOR_PERFORM = Mutex.new # When the extension is registered it extends the Sinatra application # +klass+ with the modules +BaseMethods+ and +ExtensionMethods+ and # defines a before filter to +perform+ the reload of the modified files. def self.registered(klass) @reloader_loaded_in ||= {} return if @reloader_loaded_in[klass] @reloader_loaded_in[klass] = true klass.extend BaseMethods klass.extend ExtensionMethods klass.set(:reloader) { klass.development? } klass.set(:reload_templates) { klass.reloader? } klass.before do if klass.reloader? MUTEX_FOR_PERFORM.synchronize { Reloader.perform(klass) } end end klass.set(:inline_templates, klass.app_file) if klass == Sinatra::Application end # Reloads the modified files, adding, updating and removing the # needed elements. def self.perform(klass) Watcher::List.for(klass).updated.each do |watcher| klass.set(:inline_templates, watcher.path) if watcher.inline_templates? watcher.elements.each { |element| klass.deactivate(element) } $LOADED_FEATURES.delete(watcher.path) require watcher.path watcher.update end end # Contains the methods defined in Sinatra::Base that are overridden. module BaseMethods # Protects Sinatra::Base.run! from being called more than once. def run!(*args) if settings.reloader? super unless running? else super end end # Does everything Sinatra::Base#route does, but it also tells the # +Watcher::List+ for the Sinatra application to watch the defined # route. # # Note: We are using #compile! so we don't interfere with extensions # changing #route. def compile!(verb, path, block, options = {}) source_location = block.respond_to?(:source_location) ? block.source_location.first : caller_files[1] signature = super watch_element( source_location, :route, { :verb => verb, :signature => signature } ) signature end # Does everything Sinatra::Base#inline_templates= does, but it also # tells the +Watcher::List+ for the Sinatra application to watch the # inline templates in +file+ or the file who made the call to this # method. def inline_templates=(file=nil) file = (file.nil? || file == true) ? (caller_files[1] || File.expand_path($0)) : file watch_element(file, :inline_templates) super end # Does everything Sinatra::Base#use does, but it also tells the # +Watcher::List+ for the Sinatra application to watch the middleware # being used. def use(middleware, *args, &block) path = caller_files[1] || File.expand_path($0) watch_element(path, :middleware, [middleware, args, block]) super end # Does everything Sinatra::Base#add_filter does, but it also tells # the +Watcher::List+ for the Sinatra application to watch the defined # filter. def add_filter(type, path = nil, options = {}, &block) source_location = block.respond_to?(:source_location) ? block.source_location.first : caller_files[1] result = super watch_element(source_location, :"#{type}_filter", filters[type].last) result end # Does everything Sinatra::Base#error does, but it also tells the # +Watcher::List+ for the Sinatra application to watch the defined # error handler. def error(*codes, &block) path = caller_files[1] || File.expand_path($0) result = super codes.each do |c| watch_element(path, :error, :code => c, :handler => @errors[c]) end result end # Does everything Sinatra::Base#register does, but it also lets the # reloader know that an extension is being registered, because the # elements defined in its +registered+ method need a special treatment. def register(*extensions, &block) start_registering_extension result = super stop_registering_extension result end # Does everything Sinatra::Base#register does and then registers the # reloader in the +subclass+. def inherited(subclass) result = super subclass.register Sinatra::Reloader result end end # Contains the methods that the extension adds to the Sinatra application. module ExtensionMethods # Removes the +element+ from the Sinatra application. def deactivate(element) case element.type when :route then verb = element.representation[:verb] signature = element.representation[:signature] (routes[verb] ||= []).delete(signature) when :middleware then @middleware.delete(element.representation) when :before_filter then filters[:before].delete(element.representation) when :after_filter then filters[:after].delete(element.representation) when :error then code = element.representation[:code] handler = element.representation[:handler] @errors.delete(code) if @errors[code] == handler end end # Indicates with a +glob+ which files should be reloaded if they # have been modified. It can be called several times. def also_reload(*glob) Dir[*glob].each { |path| Watcher::List.for(self).watch_file(path) } end # Indicates with a +glob+ which files should not be reloaded even if # they have been modified. It can be called several times. def dont_reload(*glob) Dir[*glob].each { |path| Watcher::List.for(self).ignore(path) } end private # attr_reader :register_path warn on -w (private attribute) def register_path; @register_path ||= nil; end # Indicates an extesion is being registered. def start_registering_extension @register_path = caller_files[2] end # Indicates the extesion has already been registered. def stop_registering_extension @register_path = nil end # Indicates whether or not an extension is being registered. def registering_extension? !register_path.nil? end # Builds a Watcher::Element from +type+ and +representation+ and # tells the Watcher::List for the current application to watch it # in the file located at +path+. # # If an extension is being registered, it also tells the list to # watch it in the file where the extension has been registered. # This prevents the duplication of the elements added by the # extension in its +registered+ method with every reload. def watch_element(path, type, representation=nil) list = Watcher::List.for(self) element = Watcher::Element.new(type, representation) list.watch(path, element) list.watch(register_path, element) if registering_extension? end end end register Reloader Delegator.delegate :also_reload, :dont_reload end sinatra-contrib-1.4.7/lib/sinatra/json.rb0000644000004100000410000000664212706205017020367 0ustar www-datawww-datarequire 'sinatra/base' require 'multi_json' module Sinatra # = Sinatra::JSON # # Sinatra::JSON adds a helper method, called +json+, for (obviously) # json generation. # # == Usage # # === Classic Application # # In a classic application simply require the helper, and start using it: # # require "sinatra" # require "sinatra/json" # # # define a route that uses the helper # get '/' do # json :foo => 'bar' # end # # # The rest of your classic application code goes here... # # === Modular Application # # In a modular application you need to require the helper, and then tell the # application you will use it: # # require "sinatra/base" # require "sinatra/json" # # class MyApp < Sinatra::Base # # # define a route that uses the helper # get '/' do # json :foo => 'bar' # end # # # The rest of your modular application code goes here... # end # # === Encoders # # By default it will try to call +to_json+ on the object, but if it doesn't # respond to that message, it will use its own rather simple encoder. You can # easily change that anyways. To use +JSON+, simply require it: # # require 'json' # # The same goes for Yajl::Encoder: # # require 'yajl' # # For other encoders, besides requiring them, you need to define the # :json_encoder setting. For instance, for the +Whatever+ encoder: # # require 'whatever' # set :json_encoder, Whatever # # To force +json+ to simply call +to_json+ on the object: # # set :json_encoder, :to_json # # Actually, it can call any method: # # set :json_encoder, :my_fancy_json_method # # === Content-Type # # It will automatically set the content type to "application/json". As # usual, you can easily change that, with the :json_content_type # setting: # # set :json_content_type, :js # # === Overriding the Encoder and the Content-Type # # The +json+ helper will also take two options :encoder and # :content_type. The values of this options are the same as the # :json_encoder and :json_content_type settings, # respectively. You can also pass those to the json method: # # get '/' do # json({:foo => 'bar'}, :encoder => :to_json, :content_type => :js) # end # module JSON class << self def encode(object) ::MultiJson.dump(object) end end def json(object, options = {}) content_type resolve_content_type(options) resolve_encoder_action object, resolve_encoder(options) end private def resolve_content_type(options = {}) options[:content_type] || settings.json_content_type end def resolve_encoder(options = {}) options[:json_encoder] || settings.json_encoder end def resolve_encoder_action(object, encoder) [:encode, :generate].each do |method| return encoder.send(method, object) if encoder.respond_to? method end if encoder.is_a? Symbol object.__send__(encoder) else fail "#{encoder} does not respond to #generate nor #encode" end #if end #resolve_encoder_action end #JSON Base.set :json_encoder do ::MultiJson end Base.set :json_content_type, :json # Load the JSON helpers in modular style automatically Base.helpers JSON end sinatra-contrib-1.4.7/lib/sinatra/capture.rb0000644000004100000410000000556212706205017021061 0ustar www-datawww-datarequire 'sinatra/base' require 'sinatra/engine_tracking' require 'backports' module Sinatra # # = Sinatra::Capture # # Extension that enables blocks inside other extensions. # It currently works for erb, slim and haml. # Enables mixing of different template languages. # # Example: # # # in hello_world.erb # # Say # <% a = capture do %>World<% end %> # Hello <%= a %>! # # # in hello_world.slim # # | Say # - a = capture do # | World # | Hello #{a}! # # # in hello_world.haml # # Say # - a = capture do # World # Hello #{a.strip}! # # # You can also use nested blocks. # # Example # # # in hello_world.erb # # Say # <% a = capture do %> # <% b = capture do %>World<% end %> # <%= b %>! # <% end %> # Hello <%= a.strip %> # # # The main advantage of capture is mixing of different template engines. # # Example # # # in mix_me_up.slim # # - two = capture do # - erb "<%= 1 + 1 %>" # | 1 + 1 = #{two} # # == Usage # # === Classic Application # # In a classic application simply require the helpers, and start using them: # # require "sinatra" # require "sinatra/capture" # # # The rest of your classic application code goes here... # # === Modular Application # # In a modular application you need to require the helpers, and then tell # the application you will use them: # # require "sinatra/base" # require "sinatra/capture" # # class MyApp < Sinatra::Base # helpers Sinatra::Capture # # # The rest of your modular application code goes here... # end # module Capture include Sinatra::EngineTracking DUMMIES = { :haml => "!= capture_haml(*args, &block)", :erubis => "<% @capture = yield(*args) %>", :slim => "== yield(*args)" } def capture(*args, &block) @capture = nil if current_engine == :ruby result = block[*args] elsif current_engine == :erb || current_engine == :slim @_out_buf, _buf_was = '', @_out_buf block[*args] result = eval('@_out_buf', block.binding) @_out_buf = _buf_was else buffer = eval '_buf if defined?(_buf)', block.binding old_buffer = buffer.dup if buffer dummy = DUMMIES.fetch(current_engine) options = { :layout => false, :locals => {:args => args, :block => block }} buffer.try :clear result = render(current_engine, dummy, options, &block) end result.strip.empty? && @capture ? @capture : result ensure buffer.try :replace, old_buffer end def capture_later(&block) engine = current_engine proc { |*a| with_engine(engine) { @capture = capture(*a, &block) }} end end helpers Capture end sinatra-contrib-1.4.7/lib/sinatra/respond_with.rb0000644000004100000410000001775412706205017022131 0ustar www-datawww-datarequire 'sinatra/json' require 'sinatra/base' $KCODE = "UTF-8" unless RUBY_VERSION > "1.9.0" module Sinatra # # = Sinatra::RespondWith # # These extensions let Sinatra automatically choose what template to render or # action to perform depending on the request's Accept header. # # Example: # # # Without Sinatra::RespondWith # get '/' do # data = { :name => 'example' } # request.accept.each do |type| # case type.to_s # when 'text/html' # halt haml(:index, :locals => data) # when 'text/json' # halt data.to_json # when 'application/atom+xml' # halt nokogiri(:'index.atom', :locals => data) # when 'application/xml', 'text/xml' # halt nokogiri(:'index.xml', :locals => data) # when 'text/plain' # halt 'just an example' # end # end # error 406 # end # # # With Sinatra::RespondWith # get '/' do # respond_with :index, :name => 'example' do |f| # f.txt { 'just an example' } # end # end # # Both helper methods +respond_to+ and +respond_with+ let you define custom # handlers like the one above for +text/plain+. +respond_with+ additionally # takes a template name and/or an object to offer the following default # behavior: # # * If a template name is given, search for a template called # +name.format.engine+ (+index.xml.nokogiri+ in the above example). # * If a template name is given, search for a templated called +name.engine+ # for engines known to result in the requested format (+index.haml+). # * If a file extension associated with the mime type is known to Sinatra, and # the object responds to +to_extension+, call that method and use the result # (+data.to_json+). # # == Security # # Since methods are triggered based on client input, this can lead to security # issues (but not as severe as those might appear in the first place: keep in # mind that only known file extensions are used). You should limit # the possible formats you serve. # # This is possible with the +provides+ condition: # # get '/', :provides => [:html, :json, :xml, :atom] do # respond_with :index, :name => 'example' # end # # However, since you have to set +provides+ for every route, this extension # adds an app global (class method) `respond_to`, that lets you define content # types for all routes: # # respond_to :html, :json, :xml, :atom # get('/a') { respond_with :index, :name => 'a' } # get('/b') { respond_with :index, :name => 'b' } # # == Custom Types # # Use the +on+ method for defining actions for custom types: # # get '/' do # respond_to do |f| # f.xml { nokogiri :index } # f.on('application/custom') { custom_action } # f.on('text/*') { data.to_s } # f.on('*/*') { "matches everything" } # end # end # # Definition order does not matter. 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(method, *args, &block) return super if args.any? or block.nil? or not @app.mime_type(method) on(method, &block) end end module Helpers include Sinatra::JSON 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 renderer = args.first options = args[1..-1] + [{:locals => locals}] halt send(renderer, *options) end end if object exts.each do |ext| halt json(object) if ext == :json next unless object.respond_to? method = "to_#{ext}" halt(*object.send(method)) 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 too 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| # not exactly like Tilt[engine], but does not trigger a require if Tilt.respond_to?(:mappings) klass = Tilt.mappings[Tilt.normalize(engine)].first else klass = Tilt[engine] end find_template(settings.views, template, klass) 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 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 def self.jrubyify(engs) not_supported = [:markdown] engs.keys.each do |key| engs[key].collect! { |eng| (eng == :yajl) ? :json_pure : eng } engs[key].delete_if { |eng| not_supported.include?(eng) } end engs end def self.engines 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]), :json => [:yajl], } engines.default = [] (defined? JRUBY_VERSION) ? jrubyify(engines) : engines end def self.registered(base) base.set :ext_map, Hash.new { |h,k| h[k] = [] } base.set :template_engines, engines base.remap_extensions base.helpers Helpers end end register RespondWith Delegator.delegate :respond_to end sinatra-contrib-1.4.7/lib/sinatra/test_helpers.rb0000644000004100000410000000370412706205017022113 0ustar www-datawww-datarequire '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_delegators :rack_mock_session, :cookie_jar 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 sinatra-contrib-1.4.7/lib/sinatra/multi_route.rb0000644000004100000410000000416612706205017021765 0ustar www-datawww-datarequire 'sinatra/base' module Sinatra # = Sinatra::MultiRoute # # Create multiple routes with one statement. # # == Usage # # Use this extension to create a handler for multiple routes: # # get '/foo', '/bar' do # # ... # end # # Or for multiple verbs: # # route :get, :post, '/' do # # ... # end # # Or for multiple verbs and multiple routes: # # route :get, :post, ['/foo', '/bar'] do # # ... # end # # Or even for custom verbs: # # route 'LIST', '/' do # # ... # end # # === Classic Application # # To use the extension in a classic application all you need to do is require # it: # # require "sinatra" # require "sinatra/multi_route" # # # Your classic application code goes here... # # === Modular Application # # To use the extension in a modular application you need to require it, and # then, tell the application you will use it: # # require "sinatra/base" # require "sinatra/multi_route" # # class MyApp < Sinatra::Base # register Sinatra::MultiRoute # # # The rest of your modular application code goes here... # end # module MultiRoute def head(*args, &block) super(*route_args(args), &block) end def delete(*args, &block) super(*route_args(args), &block) end def get(*args, &block) super(*route_args(args), &block) end def options(*args, &block) super(*route_args(args), &block) end def patch(*args, &block) super(*route_args(args), &block) end def post(*args, &block) super(*route_args(args), &block) end def put(*args, &block) super(*route_args(args), &block) end def route(*args, &block) options = Hash === args.last ? args.pop : {} routes = [*args.pop] args.each do |verb| verb = verb.to_s.upcase if Symbol === verb routes.each do |route| super(verb, route, options, &block) end end end private def route_args(args) options = Hash === args.last ? args.pop : {} [args, options] end end register MultiRoute end sinatra-contrib-1.4.7/lib/sinatra/contrib/0000755000004100000410000000000012706205017020521 5ustar www-datawww-datasinatra-contrib-1.4.7/lib/sinatra/contrib/version.rb0000644000004100000410000000050612706205017022534 0ustar www-datawww-datamodule Sinatra module Contrib def self.version VERSION end SIGNATURE = [1, 4, 7] VERSION = SIGNATURE.join('.') VERSION.extend Comparable def VERSION.<=>(other) other = other.split('.').map { |i| i.to_i } if other.respond_to? :split SIGNATURE <=> Array(other) end end end sinatra-contrib-1.4.7/lib/sinatra/contrib/all.rb0000644000004100000410000000010112706205017021606 0ustar www-datawww-datarequire 'sinatra/contrib' Sinatra.register Sinatra::Contrib::All sinatra-contrib-1.4.7/lib/sinatra/contrib/setup.rb0000644000004100000410000000213512706205017022207 0ustar www-datawww-datarequire '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 |method, list| list = list.map { |name| Sinatra.const_get name } base.send(method, *list) unless base == ::Sinatra::Application end end end module Common extend Loader end module Custom extend Loader end module All def self.registered(base) base.register Common, Custom end end extend Loader def self.registered(base) base.register Common, Custom end end end sinatra-contrib-1.4.7/lib/sinatra/link_header.rb0000644000004100000410000000723412706205017021661 0ustar www-datawww-datarequire 'sinatra/base' module Sinatra # = Sinatra::LinkHeader # # Sinatra::LinkHeader adds a set of helper methods to generate link # HTML tags and their corresponding Link HTTP headers. # # == Usage # # Once you had set up the helpers in your application (see below), you will # be able to call the following methods from inside your route handlers, # filters and templates: # # +prefetch+:: # Sets the Link HTTP headers and returns HTML tags to prefetch the given # resources. # # +stylesheet+:: # Sets the Link HTTP headers and returns HTML tags to use the given # stylesheets. # # +link+:: # Sets the Link HTTP headers and returns the corresponding HTML tags # for the given resources. # # +link_headers+:: # Returns the corresponding HTML tags for the current Link HTTP headers. # # === Classic Application # # In a classic application simply require the helpers, and start using them: # # require "sinatra" # require "sinatra/link_header" # # # The rest of your classic application code goes here... # # === Modular Application # # In a modular application you need to require the helpers, and then tell # the application you will use them: # # require "sinatra/base" # require "sinatra/link_header" # # class MyApp < Sinatra::Base # helpers Sinatra::LinkHeader # # # The rest of your modular application code goes here... # end # module LinkHeader ## # Sets 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: ; rel="next" # # Returns String: # # '' # 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 = "" 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"] = '; 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) "" end.join "\n" end def self.registered(base) puts "WARNING: #{self} is a helpers module, not an extension." end end helpers LinkHeader end sinatra-contrib-1.4.7/lib/sinatra/engine_tracking.rb0000644000004100000410000000270012706205017022534 0ustar www-datawww-datarequire 'sinatra/base' module Sinatra module EngineTracking attr_reader :current_engine def erb? @current_engine == :erb end def erubis? @current_engine == :erubis or erb? && Tilt[:erb] == Tilt::ErubisTemplate end def haml? @current_engine == :haml end def sass? @current_engine == :sass end def scss? @current_engine == :scss end def less? @current_engine == :less end def builder? @current_engine == :builder end def liquid? @current_engine == :liquid end def markdown? @current_engine == :markdown end def textile? @current_engine == :textile end def rdoc? @current_engine == :rdoc end def radius? @current_engine == :radius end def markaby? @current_engine == :markaby end def coffee? @current_engine == :coffee end def nokogiri? @current_engine == :nokogiri end def slim? @current_engine == :slim end def creole? @current_engine == :creole end def initialize(*) @current_engine = :ruby super end def with_engine(engine) @current_engine, engine_was = engine.to_sym, @current_engine yield ensure @current_engine = engine_was end private def render(engine, *) with_engine(engine) { super } end end helpers EngineTracking end sinatra-contrib-1.4.7/lib/sinatra/config_file.rb0000644000004100000410000001234212706205017021654 0ustar www-datawww-datarequire 'sinatra/base' require 'yaml' require 'erb' module Sinatra # = Sinatra::ConfigFile # # Sinatra::ConfigFile is an extension that allows you to load the # application's configuration from YAML files. It automatically detects if # the files contains specific environment settings and it will use the # corresponding to the current one. # # You can access those options through +settings+ within the application. If # you try to get the value for a setting that hasn't been defined in the # config file for the current environment, you will get whatever it was set # to in the application. # # == Usage # # Once you have written your configurations to a YAML file you can tell the # extension to load them. See below for more information about how these # files are interpreted. # # For the examples, lets assume the following config.yml file: # # greeting: Welcome to my file configurable application # # === Classic Application # # require "sinatra" # require "sinatra/config_file" # # config_file 'path/to/config.yml' # # get '/' do # @greeting = settings.greeting # haml :index # end # # # The rest of your classic application code goes here... # # === Modular Application # # require "sinatra/base" # require "sinatra/config_file" # # class MyApp < Sinatra::Base # register Sinatra::ConfigFile # # config_file 'path/to/config.yml' # # get '/' do # @greeting = settings.greeting # haml :index # end # # # The rest of your modular application code goes here... # end # # === Config File Format # # In its most simple form this file is just a key-value list: # # foo: bar # something: 42 # nested: # a: 1 # b: 2 # # But it also can provide specific environment configuration. There are two # ways to do that: at the file level and at the settings level. # # At the settings level (e.g. in 'path/to/config.yml'): # # development: # foo: development # bar: bar # test: # foo: test # bar: bar # production: # foo: production # bar: bar # # Or at the file level: # # foo: # development: development # test: test # production: production # bar: bar # # In either case, settings.foo will return the environment name, and # settings.bar will return "bar". # # Be aware that if you have a different environment, besides development, # test and production, you will also need to adjust the +environments+ # setting, otherwise the settings will not load. For instance, when # you also have a staging environment: # # set :environments, %w{development test production staging} # # If you wish to provide defaults that may be shared among all the environments, # this can be done by using one of the existing environments as the default using # the YAML alias, and then overwriting values in the other environments: # # development: &common_settings # foo: 'foo' # bar: 'bar' # # production: # <<: *common_settings # bar: 'baz' # override the default value # module ConfigFile # When the extension is registered sets the +environments+ setting to the # traditional environments: development, test and production. def self.registered(base) base.set :environments, %w[test production development] end # Loads the configuration from the YAML files whose +paths+ are passed as # arguments, filtering the settings for the current environment. Note that # these +paths+ can actually be globs. 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? document = IO.read(file) document = ERB.new(document).result if file.split('.').include?('erb') yaml = config_for_env(YAML.load(document)) || {} 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 # Given a +hash+ with some application configuration it returns the # settings applicable to the current environment. Note that this can only # be done when all the keys of +hash+ are environment names included in the # +environments+ setting (which is an Array of Strings). Also, the # returned config is a indifferently accessible Hash, which means that you # can get its values using Strings or Symbols as keys. 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 register ConfigFile Delegator.delegate :config_file end sinatra-contrib-1.4.7/lib/sinatra/content_for.rb0000644000004100000410000000732012706205017021730 0ustar www-datawww-datarequire 'sinatra/base' require 'sinatra/capture' module Sinatra # = Sinatra::ContentFor # # Sinatra::ContentFor is a set of helpers that allows you to capture # blocks inside views to be rendered later during the request. The most # common use is to populate different parts of your layout from your view. # # The currently supported engines are: Erb, Erubis, Haml and Slim. # # == Usage # # You call +content_for+, generally from a view, to capture a block of markup # giving it an identifier: # # # index.erb # <% content_for :some_key do %> # ... # <% end %> # # Then, you call +yield_content+ with that identifier, generally from a # layout, to render the captured block: # # # layout.erb # <%= yield_content :some_key %> # # === Classic Application # # To use the helpers in a classic application all you need to do is require # them: # # require "sinatra" # require "sinatra/content_for" # # # Your classic application code goes here... # # === Modular Application # # To use the helpers in a modular application you need to require them, and # then, tell the application you will use them: # # require "sinatra/base" # require "sinatra/content_for" # # class MyApp < Sinatra::Base # helpers Sinatra::ContentFor # # # The rest of your modular application code goes here... # end # # == And How Is This Useful? # # For example, some of your views might need a few javascript tags and # stylesheets, but you don't want to force this files in all your pages. # Then you can put <%= yield_content :scripts_and_styles %> on your # layout, inside the tag, and each view can call content_for # setting the appropriate set of tags that should be added to the layout. # module ContentFor include Capture # Capture a block of content to be rendered later. For example: # # <% content_for :head do %> # # <% end %> # # You can call +content_for+ multiple times with the same key # (in the example +:head+), and when you render the blocks for # that key all of them will be rendered, in the same order you # captured them. # # Your blocks can also receive values, which are passed to them # by yield_content def content_for(key, &block) content_blocks[key.to_sym] << capture_later(&block) end # Check if a block of content with the given key was defined. For # example: # # <% content_for :head do %> # # <% end %> # # <% if content_for? :head %> # content "head" was defined. # <% end %> def content_for?(key) content_blocks[key.to_sym].any? end # Render the captured blocks for a given key. For example: # # # Example # <%= yield_content :head %> # # # Would render everything you declared with content_for # :head before closing the tag. # # You can also pass values to the content blocks by passing them # as arguments after the key: # # <%= yield_content :head, 1, 2 %> # # Would pass 1 and 2 to all the blocks registered # for :head. def yield_content(key, *args) content_blocks[key.to_sym].map { |b| capture(*args, &b) }.join end private def content_blocks @content_blocks ||= Hash.new {|h,k| h[k] = [] } end end helpers ContentFor end sinatra-contrib-1.4.7/lib/sinatra/custom_logger.rb0000644000004100000410000000265112706205017022263 0ustar www-datawww-datamodule Sinatra # = Sinatra::CustomLogger # # CustomLogger extension allows you to define your own logger instance # using +logger+ setting. That logger then will be available # as #logger helper method in your routes and views. # # == Usage # # === Classic Application # # To define your custom logger instance in a classic application: # # require 'sinatra' # require 'sinatra/custom_logger' # require 'logger' # # set :logger, Logger.new(STDOUT) # # get '/' do # logger.info 'Some message' # STDOUT logger is used # # Other code... # end # # === Modular Application # # The same for modular application: # # require 'sinatra/base' # require 'sinatra/custom_logger' # require 'logger' # # class MyApp < Sinatra::Base # helpers Sinatra::CustomLogger # # configure :development, :production do # logger = Logger.new(File.open("#{root}/log/#{environment}.log", 'a')) # logger.level = Logger::DEBUG if development? # set :logger, logger # end # # get '/' do # logger.info 'Some message' # File-based logger is used # # Other code... # end # end # module CustomLogger def logger if settings.respond_to?(:logger) settings.logger else request.logger end end end helpers CustomLogger end sinatra-contrib-1.4.7/lib/sinatra/streaming.rb0000644000004100000410000001317212706205017021403 0ustar www-datawww-datarequire 'sinatra/base' require 'backports' module Sinatra # = Sinatra::Streaming # # Sinatra 1.3 introduced the +stream+ helper. This addon improves the # streaming API by making the stream object imitate an IO object, turning # it into a real Deferrable and making the body play nicer with middleware # unaware of streaming. # # == IO-like behavior # # This is useful when passing the stream object to a library expecting an # IO or StringIO object. # # get '/' do # stream do |out| # out.puts "Hello World!", "How are you?" # out.write "Written #{out.pos} bytes so far!\n" # out.putc(65) unless out.closed? # out.flush # end # end # # == Better Middleware Handling # # Blocks passed to #map! or #map will actually be applied when streaming # takes place (as you might have suspected, #map! applies modifications # to the current body, while #map creates a new one): # # class StupidMiddleware # def initialize(app) @app = app end # # def call(env) # status, headers, body = @app.call(env) # body.map! { |e| e.upcase } # [status, headers, body] # end # end # # use StupidMiddleware # # get '/' do # stream do |out| # out.puts "still" # sleep 1 # out.puts "streaming" # end # end # # Even works if #each is used to generate an Enumerator: # # def call(env) # status, headers, body = @app.call(env) # body = body.each.map { |s| s.upcase } # [status, headers, body] # end # # Note that both examples violate the Rack specification. # # == Setup # # In a classic application: # # require "sinatra" # require "sinatra/streaming" # # In a modular application: # # require "sinatra/base" # require "sinatra/streaming" # # class MyApp < Sinatra::Base # helpers Sinatra::Streaming # end module Streaming def stream(*) stream = super stream.extend Stream stream.app = self env['async.close'].callback { stream.close } if env.key? 'async.close' stream end module Stream attr_accessor :app, :lineno, :pos, :transformer, :closed alias tell pos alias closed? closed def self.extended(obj) obj.closed, obj.lineno, obj.pos = false, 0, 0 obj.callback { obj.closed = true } obj.errback { obj.closed = true } end def <<(data) raise IOError, 'not opened for writing' if closed? data = data.to_s data = @transformer[data] if @transformer @pos += data.bytesize super(data) end def each # that way body.each.map { ... } works return self unless block_given? super end def map(&block) # dup would not copy the mixin clone.map!(&block) end def map!(&block) if @transformer inner, outer = @transformer, block block = proc { |value| outer[inner[value]] } end @transformer = block self end def write(data) self << data data.to_s.bytesize end alias syswrite write alias write_nonblock write def print(*args) args.each { |arg| self << arg } nil end def printf(format, *args) print(format.to_s % args) end def putc(c) print c.chr end def puts(*args) args.each { |arg| self << "#{arg}\n" } nil end def close_read raise IOError, "closing non-duplex IO for reading" end def closed_read? true end def closed_write? closed? end def external_encoding Encoding.find settings.default_encoding rescue NameError settings.default_encoding end def closed? @closed end def settings app.settings end def rewind @pos = @lineno = 0 end def not_open_for_reading(*) raise IOError, "not opened for reading" end alias bytes not_open_for_reading alias eof? not_open_for_reading alias eof not_open_for_reading alias getbyte not_open_for_reading alias getc not_open_for_reading alias gets not_open_for_reading alias read not_open_for_reading alias read_nonblock not_open_for_reading alias readbyte not_open_for_reading alias readchar not_open_for_reading alias readline not_open_for_reading alias readlines not_open_for_reading alias readpartial not_open_for_reading alias sysread not_open_for_reading alias ungetbyte not_open_for_reading alias ungetc not_open_for_reading private :not_open_for_reading def enum_not_open_for_reading(*) not_open_for_reading if block_given? enum_for(:not_open_for_reading) end alias chars enum_not_open_for_reading alias each_line enum_not_open_for_reading alias each_byte enum_not_open_for_reading alias each_char enum_not_open_for_reading alias lines enum_not_open_for_reading undef enum_not_open_for_reading def dummy(*) end alias flush dummy alias fsync dummy alias internal_encoding dummy alias pid dummy undef dummy def seek(*) 0 end alias sysseek seek def sync true end def tty? false end alias isatty tty? end end helpers Streaming end sinatra-contrib-1.4.7/lib/sinatra/extension.rb0000644000004100000410000000510412706205017021422 0ustar www-datawww-datarequire 'sinatra/base' require 'backports/basic_object' unless defined? BasicObject module Sinatra # = Sinatra::Extension # # Sinatra::Extension is a mixin that provides some syntactic sugar # for your extensions. It allows you to call almost any # Sinatra::Base method directly inside your extension # module. This means you can use +get+ to define a route, +before+ # to define a before filter, +set+ to define a setting and so on. # # Is important to be aware that this mixin remembers the method calls you # make, and then, when your extension is registered, replays them on the # Sinatra application that has been extended. In order to do that, it # defines a registered method, so, if your extension defines one # too, remember to call +super+. # # == Usage # # Just require the mixin and extend your extension with it: # # require 'sinatra/extension' # # module MyExtension # extend Sinatra::Extension # # # set some settings for development # configure :development do # set :reload_stuff, true # end # # # define a route # get '/' do # 'Hello World' # end # # # The rest of your extension code goes here... # end # # You can also create an extension with the +new+ method: # # MyExtension = Sinatra::Extension.new do # # Your extension code goes here... # end # # This is useful when you just want to pass a block to # Sinatra::Base.register. 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 sinatra-contrib-1.4.7/lib/sinatra/cookies.rb0000644000004100000410000001560212706205017021046 0ustar www-datawww-datarequire 'sinatra/base' require 'backports' module Sinatra # = Sinatra::Cookies # # Easy way to deal with cookies # # == Usage # # Allows you to read cookies: # # get '/' do # "value: #{cookies[:something]}" # end # # And of course to write cookies: # # get '/set' do # cookies[:something] = 'foobar' # redirect to('/') # end # # And generally behaves like a hash: # # get '/demo' do # cookies.merge! 'foo' => 'bar', 'bar' => 'baz' # cookies.keep_if { |key, value| key.start_with? 'b' } # foo, bar = cookies.values_at 'foo', 'bar' # "size: #{cookies.length}" # end # # === Classic Application # # In a classic application simply require the helpers, and start using them: # # require "sinatra" # require "sinatra/cookies" # # # The rest of your classic application code goes here... # # === Modular Application # # In a modular application you need to require the helpers, and then tell # the application to use them: # # require "sinatra/base" # require "sinatra/cookies" # # class MyApp < Sinatra::Base # helpers Sinatra::Cookies # # # The rest of your modular application code goes here... # end # module Cookies class Jar include Enumerable attr_reader :options def initialize(app) @response_string = nil @response_hash = {} @response = app.response @request = app.request @deleted = [] @options = { :path => @request.script_name.to_s.empty? ? '/' : @request.script_name, :domain => @request.host == 'localhost' ? nil : @request.host, :secure => @request.secure?, :httponly => true } if app.settings.respond_to? :cookie_options @options.merge! app.settings.cookie_options end end def ==(other) other.respond_to? :to_hash and to_hash == other.to_hash end def [](key) response_cookies[key.to_s] || request_cookies[key.to_s] end def []=(key, value) @response.set_cookie key.to_s, @options.merge(:value => value) end def assoc(key) to_hash.assoc(key.to_s) end if Hash.method_defined? :assoc def clear each_key { |k| delete(k) } end def compare_by_identity? false end def default nil end alias default_proc default def delete(key) result = self[key] @response.delete_cookie(key.to_s, @options) result end def delete_if return enum_for(__method__) unless block_given? each { |k, v| delete(k) if yield(k, v) } self end def each(&block) return enum_for(__method__) unless block_given? to_hash.each(&block) end def each_key(&block) return enum_for(__method__) unless block_given? to_hash.each_key(&block) end alias each_pair each def each_value(&block) return enum_for(__method__) unless block_given? to_hash.each_value(&block) end def empty? to_hash.empty? end def fetch(key, &block) response_cookies.fetch(key.to_s) do request_cookies.fetch(key.to_s, &block) end end def flatten to_hash.flatten end if Hash.method_defined? :flatten def has_key?(key) response_cookies.has_key? key.to_s or request_cookies.has_key? key.to_s end def has_value?(value) response_cookies.has_value? value or request_cookies.has_value? value end def hash to_hash.hash end alias include? has_key? alias member? has_key? def index(value) warn "Hash#index is deprecated; use Hash#key" if RUBY_VERSION > '1.9' key(value) end def inspect "<##{self.class}: #{to_hash.inspect[1..-2]}>" end def invert to_hash.invert end if Hash.method_defined? :invert def keep_if return enum_for(__method__) unless block_given? delete_if { |*a| not yield(*a) } end def key(value) to_hash.key(value) end alias key? has_key? def keys to_hash.keys end def length to_hash.length end def merge(other, &block) to_hash.merge(other, &block) end def merge!(other) other.each_pair do |key, value| if block_given? and include? key self[key] = yield(key.to_s, self[key], value) else self[key] = value end end end def rassoc(value) to_hash.rassoc(value) end def rehash response_cookies.rehash request_cookies.rehash self end def reject(&block) return enum_for(__method__) unless block_given? to_hash.reject(&block) end alias reject! delete_if def replace(other) select! { |k, v| other.include?(k) or other.include?(k.to_s) } merge! other end def select(&block) return enum_for(__method__) unless block_given? to_hash.select(&block) end alias select! keep_if if Hash.method_defined? :select! def shift key, value = to_hash.shift delete(key) [key, value] end alias size length def sort(&block) to_hash.sort(&block) end if Hash.method_defined? :sort alias store []= def to_hash request_cookies.merge(response_cookies) end def to_a to_hash.to_a end def to_s to_hash.to_s end alias update merge! alias value? has_value? def values to_hash.values end def values_at(*list) list.map { |k| self[k] } end private def warn(message) super "#{caller.first[/^[^:]:\d+:/]} warning: #{message}" end def deleted parse_response @deleted end def response_cookies parse_response @response_hash end def parse_response string = @response['Set-Cookie'] return if @response_string == string hash = {} string.each_line do |line| key, value = line.split(';', 2).first.to_s.split('=', 2) next if key.nil? key = Rack::Utils.unescape(key) if line =~ /expires=Thu, 01[-\s]Jan[-\s]1970/ @deleted << key else @deleted.delete key hash[key] = value end end @response_hash.replace hash @response_string = string end def request_cookies @request.cookies.reject { |key, value| deleted.include? key } end end def cookies @cookies ||= Jar.new(self) end end helpers Cookies end sinatra-contrib-1.4.7/sinatra-contrib.gemspec0000644000004100000410000001576612706205017021335 0ustar www-datawww-data# -*- encoding: utf-8 -*- # Run `rake sinatra-contrib.gemspec` to update the gemspec. require File.expand_path('../lib/sinatra/contrib/version', __FILE__) Gem::Specification.new do |s| s.name = "sinatra-contrib" s.version = Sinatra::Contrib::VERSION s.description = "Collection of useful Sinatra extensions" s.homepage = "http://github.com/sinatra/sinatra-contrib" s.license = "MIT" s.summary = s.description # generated from git shortlog -sn s.authors = [ "Konstantin Haase", "Zachary Scott", "Gabriel Andretta", "Trevor Bramble", "Katrina Owen", "Ashley Williams", "Nicolas Sanguinetti", "Hrvoje Šimić", "Masahiro Fujiwara", "Rafael Magana", "Vipul A M", "ashley williams", "Jack Chu", "Sumeet Singh", "Ilya Shindyapin", "lest", "Jake Worth", "Kashyap", "Matt Lyon", "Matthew Conway", "Meck", "Michi Huber", "Nic Benders", "Patricio Mac Adden", "Reed Lipman", "Samy Dindane", "Sergey Nartimov", "Thibaut Sacreste", "Uchio KONDO", "Will Bailey", "mono", "Adrian Pacała", "undr", "Aish", "Alexey Chernenkov", "Andrew Crump", "Anton Davydov", "Bo Jeanes", "David Asabina", "Eliot Shepard", "Eric Marden", "Gray Manley", "Guillaume Bouteille", "Jamie Hodge", "Julie Ng", "Koichi Sasada", "Kyle Lacy", "Lars Vonk", "Martin Frost", "Mathieu Allaire" ] # generated from git shortlog -sne s.email = [ "konstantin.mailinglists@googlemail.com", "ohhgabriel@gmail.com", "inbox@trevorbramble.com", "e@zzak.io", "zachary@zacharyscott.net", "katrina.owen@gmail.com", "ashley@bocoup.com", "contacto@nicolassanguinetti.info", "shime.ferovac@gmail.com", "raf.magana@gmail.com", "m-fujiwara@axsh.net", "vipulnsward@gmail.com", "konstantin.haase@gmail.com", "jack@jackchu.com", "ashley666ashley@gmail.com", "ilya@shindyapin.com", "just.lest@gmail.com", "kashyap.kmbc@gmail.com", "ortuna@gmail.com", "tbramble@chef.io", "jworth@prevailhs.com", "mail@zzak.io", "nic@newrelic.com", "patriciomacadden@gmail.com", "rmlipman@gmail.com", "samy@dindane.com", "just.lest@gmail.com", "thibaut.sacreste@gmail.com", "udzura@udzura.jp", "will.bailey@gmail.com", "mono@mono0x.net", "altpacala@gmail.com", "undr@yandex.ru", "aisha.fenton@visfleet.com", "laise@pisem.net", "andrew.crump@ieee.org", "antondavydov.o@gmail.com", "me@bjeanes.com", "david@supr.nu", "eshepard@slower.net", "eric.marden@gmail.com", "g.manley@tukaiz.com", "duffman@via.ecp.fr", "jamiehodge@me.com", "uxjulie@gmail.com", "ko1@atdot.net", "kylewlacy@me.com", "lars.vonk@gmail.com", "blame@kth.se", "mathieuallaire@gmail.com", "matt@flowerpowered.com", "himself@mattonrails.com", "yesmeck@gmail.com", "michi.huber@gmail.com" ] # generated from git ls-files s.files = [ "LICENSE", "README.md", "Rakefile", "ideas.md", "lib/sinatra/capture.rb", "lib/sinatra/config_file.rb", "lib/sinatra/content_for.rb", "lib/sinatra/contrib.rb", "lib/sinatra/contrib/all.rb", "lib/sinatra/contrib/setup.rb", "lib/sinatra/contrib/version.rb", "lib/sinatra/cookies.rb", "lib/sinatra/custom_logger.rb", "lib/sinatra/decompile.rb", "lib/sinatra/engine_tracking.rb", "lib/sinatra/extension.rb", "lib/sinatra/json.rb", "lib/sinatra/link_header.rb", "lib/sinatra/multi_route.rb", "lib/sinatra/namespace.rb", "lib/sinatra/reloader.rb", "lib/sinatra/respond_with.rb", "lib/sinatra/streaming.rb", "lib/sinatra/test_helpers.rb", "sinatra-contrib.gemspec", "spec/capture_spec.rb", "spec/config_file/key_value.yml", "spec/config_file/key_value.yml.erb", "spec/config_file/key_value_override.yml", "spec/config_file/missing_env.yml", "spec/config_file/with_envs.yml", "spec/config_file/with_nested_envs.yml", "spec/config_file_spec.rb", "spec/content_for/different_key.erb", "spec/content_for/different_key.erubis", "spec/content_for/different_key.haml", "spec/content_for/different_key.slim", "spec/content_for/footer.erb", "spec/content_for/footer.erubis", "spec/content_for/footer.haml", "spec/content_for/footer.slim", "spec/content_for/layout.erb", "spec/content_for/layout.erubis", "spec/content_for/layout.haml", "spec/content_for/layout.slim", "spec/content_for/multiple_blocks.erb", "spec/content_for/multiple_blocks.erubis", "spec/content_for/multiple_blocks.haml", "spec/content_for/multiple_blocks.slim", "spec/content_for/multiple_yields.erb", "spec/content_for/multiple_yields.erubis", "spec/content_for/multiple_yields.haml", "spec/content_for/multiple_yields.slim", "spec/content_for/passes_values.erb", "spec/content_for/passes_values.erubis", "spec/content_for/passes_values.haml", "spec/content_for/passes_values.slim", "spec/content_for/same_key.erb", "spec/content_for/same_key.erubis", "spec/content_for/same_key.haml", "spec/content_for/same_key.slim", "spec/content_for/takes_values.erb", "spec/content_for/takes_values.erubis", "spec/content_for/takes_values.haml", "spec/content_for/takes_values.slim", "spec/content_for_spec.rb", "spec/cookies_spec.rb", "spec/custom_logger_spec.rb", "spec/decompile_spec.rb", "spec/extension_spec.rb", "spec/json_spec.rb", "spec/link_header_spec.rb", "spec/multi_route_spec.rb", "spec/namespace/foo.erb", "spec/namespace/nested/foo.erb", "spec/namespace_spec.rb", "spec/okjson.rb", "spec/reloader/app.rb.erb", "spec/reloader_spec.rb", "spec/respond_with/bar.erb", "spec/respond_with/bar.json.erb", "spec/respond_with/baz.yajl", "spec/respond_with/foo.html.erb", "spec/respond_with/not_html.sass", "spec/respond_with_spec.rb", "spec/spec_helper.rb", "spec/streaming_spec.rb" ] s.add_dependency "sinatra", "~> 1.4.0" s.add_dependency "backports", ">= 2.0" s.add_dependency "tilt", ">= 1.3", "< 3" s.add_dependency "rack-test" s.add_dependency "rack-protection" s.add_dependency "multi_json" s.add_development_dependency "rspec", "~> 2.3" s.add_development_dependency "haml" s.add_development_dependency "erubis" s.add_development_dependency "slim" s.add_development_dependency "less" s.add_development_dependency "sass" s.add_development_dependency "builder" s.add_development_dependency "liquid" s.add_development_dependency "redcarpet" s.add_development_dependency "RedCloth" s.add_development_dependency "asciidoctor" s.add_development_dependency "radius" s.add_development_dependency "coffee-script" s.add_development_dependency "nokogiri" s.add_development_dependency "creole" s.add_development_dependency "wikicloth" s.add_development_dependency "markaby" s.add_development_dependency "rake", "< 11" end sinatra-contrib-1.4.7/LICENSE0000644000004100000410000000213712706205017015662 0ustar www-datawww-dataThe MIT License (MIT) Copyright (c) 2008-2011 Nicolas Sanguinetti, entp.com, Konstantin Haase 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. sinatra-contrib-1.4.7/README.md0000644000004100000410000000724012706205017016134 0ustar www-datawww-data[![Build Status](https://secure.travis-ci.org/sinatra/sinatra-contrib.png)](http://travis-ci.org/sinatra/sinatra-contrib) Collection of common Sinatra extensions, semi-officially supported. # Goals * For every future Sinatra release, have at least one fully compatible release * High code quality, high test coverage * Include plugins people usually ask for a lot # Included extensions ## Common Extensions These are common extension which will not add significant overhead or change any behavior of already existing APIs. They do not add any dependencies not already installed with this gem. Currently included: * `sinatra/capture`: Let's you capture the content of blocks in templates. * `sinatra/config_file`: Allows loading configuration from yaml files. * `sinatra/content_for`: Adds Rails-style `content_for` helpers to Haml, Erb, Erubis and Slim. * `sinatra/cookies`: A `cookies` helper for reading and writing cookies. * `sinatra/engine_tracking`: Adds methods like `haml?` that allow helper methods to check whether they are called from within a template. * `sinatra/json`: Adds a `#json` helper method to return JSON documents. * `sinatra/link_header`: Helpers for generating `link` HTML tags and corresponding `Link` HTTP headers. Adds `link`, `stylesheet` and `prefetch` helper methods. * `sinatra/multi_route`: Adds ability to define one route block for multiple routes and multiple or custom HTTP verbs. * `sinatra/namespace`: Adds namespace support to Sinatra. * `sinatra/respond_with`: Choose action and/or template automatically depending on the incoming request. Adds helpers `respond_to` and `respond_with`. * `sinatra/custom_logger`: This extension allows you to define your own logger instance using +logger+ setting. That logger then will be available as #logger helper method in your routes and views. ## Custom Extensions These extensions may add additional dependencies and enhance the behavior of the existing APIs. Currently included: * `sinatra/decompile`: Recreates path patterns from Sinatra's internal data structures (used by other extensions). * `sinatra/reloader`: Automatically reloads Ruby files on code changes. ## Other Tools * `sinatra/extension`: Mixin for writing your own Sinatra extensions. * `sinatra/test_helpers`: Helper methods to ease testing your Sinatra application. Partly extracted from Sinatra. Testing framework agnostic # Installation Add `gem 'sinatra-contrib'` to *Gemfile*, then execute `bundle install`. If you don't use Bundler, install the gem manually by executing `gem install sinatra-contrib` in your command line. # Usage ## Classic Style A single extension (example: sinatra-content-for): ``` ruby require 'sinatra' require 'sinatra/content_for' ``` Common extensions: ``` ruby require 'sinatra' require 'sinatra/contrib' ``` All extensions: ``` ruby require 'sinatra' require 'sinatra/contrib/all' ``` ## Modular Style A single extension (example: sinatra-content-for): ``` ruby require 'sinatra/base' require 'sinatra/content_for' require 'sinatra/namespace' class MyApp < Sinatra::Base # Note: Some modules are extensions, some helpers, see the specific # documentation or the source helpers Sinatra::ContentFor register Sinatra::Namespace end ``` Common extensions: ``` ruby require 'sinatra/base' require 'sinatra/contrib' class MyApp < Sinatra::Base register Sinatra::Contrib end ``` All extensions: ``` ruby require 'sinatra/base' require 'sinatra/contrib/all' class MyApp < Sinatra::Base register Sinatra::Contrib end ``` ## Documentation For more info check the [official docs](http://www.sinatrarb.com/contrib/) and [api docs](http://rubydoc.info/gems/sinatra-contrib/1.4.0/frames).