Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Styles revamping.

  • Loading branch information...
commit 72589623918ffbc55239953e6b3caac0a616cb13 1 parent 2e0e664
@djanowski djanowski authored
Showing with 208 additions and 4,190 deletions.
  1. +50 −40 irclogger.rb
  2. +0 −110 public/styles.css
  3. +0 −6 vendor/sinatra/.gitignore
  4. +0 −3  vendor/sinatra/.gitmodules
  5. +0 −2  vendor/sinatra/CHANGELOG
  6. +0 −22 vendor/sinatra/LICENSE
  7. +0 −122 vendor/sinatra/Manifest
  8. +0 −464 vendor/sinatra/README.rdoc
  9. +0 −14 vendor/sinatra/Rakefile
  10. BIN  vendor/sinatra/images/404.png
  11. BIN  vendor/sinatra/images/500.png
  12. +0 −1,506 vendor/sinatra/lib/sinatra.rb
  13. +0 −53 vendor/sinatra/lib/sinatra/test/methods.rb
  14. +0 −10 vendor/sinatra/lib/sinatra/test/spec.rb
  15. +0 −13 vendor/sinatra/lib/sinatra/test/unit.rb
  16. +0 −287 vendor/sinatra/test/app_test.rb
  17. +0 −287 vendor/sinatra/test/application_test.rb
  18. +0 −101 vendor/sinatra/test/builder_test.rb
  19. +0 −67 vendor/sinatra/test/custom_error_test.rb
  20. +0 −41 vendor/sinatra/test/diddy_test.rb
  21. +0 −136 vendor/sinatra/test/erb_test.rb
  22. +0 −15 vendor/sinatra/test/event_context_test.rb
  23. +0 −50 vendor/sinatra/test/events_test.rb
  24. +0 −233 vendor/sinatra/test/haml_test.rb
  25. +0 −4 vendor/sinatra/test/helper.rb
  26. +0 −61 vendor/sinatra/test/mapped_error_test.rb
  27. +0 −66 vendor/sinatra/test/pipeline_test.rb
  28. +0 −1  vendor/sinatra/test/public/foo.xml
  29. +0 −16 vendor/sinatra/test/rest_test.rb
  30. +0 −57 vendor/sinatra/test/sass_test.rb
  31. +0 −39 vendor/sinatra/test/sessions_test.rb
  32. +0 −118 vendor/sinatra/test/streaming_test.rb
  33. +0 −19 vendor/sinatra/test/sym_params_test.rb
  34. +0 −30 vendor/sinatra/test/template_test.rb
  35. +0 −48 vendor/sinatra/test/use_in_file_templates_test.rb
  36. +0 −1  vendor/sinatra/test/views/foo.builder
  37. +0 −1  vendor/sinatra/test/views/foo.erb
  38. +0 −1  vendor/sinatra/test/views/foo.haml
  39. +0 −2  vendor/sinatra/test/views/foo.sass
  40. +0 −2  vendor/sinatra/test/views/foo_layout.erb
  41. +0 −2  vendor/sinatra/test/views/foo_layout.haml
  42. +0 −1  vendor/sinatra/test/views/layout_test/foo.builder
  43. +0 −1  vendor/sinatra/test/views/layout_test/foo.erb
  44. +0 −1  vendor/sinatra/test/views/layout_test/foo.haml
  45. +0 −2  vendor/sinatra/test/views/layout_test/foo.sass
  46. +0 −3  vendor/sinatra/test/views/layout_test/layout.builder
  47. +0 −1  vendor/sinatra/test/views/layout_test/layout.erb
  48. +0 −1  vendor/sinatra/test/views/layout_test/layout.haml
  49. +0 −2  vendor/sinatra/test/views/layout_test/layout.sass
  50. +0 −1  vendor/sinatra/test/views/no_layout/no_layout.builder
  51. +0 −1  vendor/sinatra/test/views/no_layout/no_layout.haml
  52. +15 −0 views/channel.haml
  53. +0 −16 views/index.erb
  54. +15 −0 views/index.haml
  55. +0 −50 views/layout.erb
  56. +36 −0 views/layout.haml
  57. +0 −60 views/log.erb
  58. +92 −0 views/styles.sass
View
90 irclogger.rb
@@ -1,31 +1,15 @@
-require 'rubygems'
# Wire up the paths
Dir[File.dirname(__FILE__) + '/vendor/*/lib'].each { |d| $:.unshift d }
$:.unshift File.dirname(__FILE__) + '/lib'
-## Rack 0.9.1 fix ##############
-#require 'rack/file'
-#class Rack::File
-# MIME_TYPES = Hash.new { |hash, key|
-# Rack::Mime::MIME_TYPES[".#{key}"] }
-#end
-
-## Ruby 1.9.1 fixes
-if RUBY_VERSION > '1.9'
- ## String#each needs to be aliased
- class String
- alias_method :each, :each_line
- end
-
- ## Ruby 1.9.1 also works best with thin
- require 'thin'
-end
-
require 'sinatra'
require 'date'
require 'helpers'
require 'partials'
+# set :raise_errors, true
+# set :show_exceptions, true
+
## DB ###########################
require 'sequel'
DB = Sequel.connect 'mysql://root@localhost/irclogs'
@@ -37,7 +21,7 @@ def message_type
end
def msg?
- ! nick.blank?
+ ! nick.empty?
end
def info?
@@ -45,6 +29,8 @@ def info?
end
end
+class NilClass; def empty?; true; end; end
+
## Helpers ###########################
helpers do
@@ -52,16 +38,52 @@ def info?
include Sinatra::Partials
include Rack::Utils
alias_method :h, :escape_html
+
+ def status_line(text)
+ text.sub(/#.*$/, "")
+ end
+
+ def calendar(channel, date)
+ cal = `cal #{date.month} #{date.year}`
+
+ cal.gsub!(/\b(\d{1,2})\b/) do
+ d = date.strftime("%Y-%m-#{$1.rjust 2, "0"}")
+ current = "current" if date.to_s == d
+
+ %Q{<a class="#{current}" href="/#{channel}/#{d}">#{$1}</a>}
+ end
+
+ next_date = date >> 1
+ prev_date = date >> -1
+
+ %Q{<a href="/#{channel}/#{prev_date}">&lt;</a>#{date.strftime("%B %Y").center(18)}<a href="/#{channel}/#{next_date}">&gt;</a>\n#{cal.split("\n")[1..-1].join("\n")}}
+ end
+
+ def log_entry(message, &block)
+ if message.msg?
+ haml_tag(:span, :class => "msg", &block)
+ else
+ yield
+ end
+ end
end
## Web ##########################
-get '/' do
+before do
@channels = DB["SELECT channel FROM irclog GROUP BY channel"].inject([]) { |arr, row|
arr << row[:channel] if (row[:channel] =~ /^#/ && row[:channel] != "#datamapper http://datamapper.")
arr
}
+end
+
+get '/' do
+ @date = Date.today
+ haml :index
+end
- erb :index
+get "/styles.css" do
+ response["Content-Type"] = "text/css"
+ sass :styles
end
get '/:channel' do
@@ -74,33 +96,21 @@ def info?
end
get '/:channel/:date' do
- @channel = params[:channel]
- @date = params[:date]
+ @date = params[:date].empty? ? Date.today : Date.parse(params[:date])
- begin
- @base = Date.parse(@date)
- rescue
- redirect "/#{@channel}/#{relative_day(@date)}"
- end
+ @channel = params[:channel]
- @day_before = (@base - 1)
- @day_after = (@base + 1)
+ @day_before = (@date - 1)
+ @day_after = (@date + 1)
- @begin = Time.local(@base.year, @base.month, @base.day)
+ @begin = Time.local(@date.year, @date.month, @date.day)
@end = Time.local(@day_after.year, @day_after.month, @day_after.day)
@messages = Message.filter(:timestamp > @begin.to_i).
filter(:timestamp < @end.to_i).
filter(:channel => "##{@channel}").
order(:timestamp)
- @urls = @messages.inject([]) do |arr, m|
- matches = m.line.scan IRCLogger::Helpers::AUTO_LINK_RE
- matches.each { |match| arr << (match[1] + match[2]) }
- arr
- end
-
-
- erb :log
+ haml :channel
end
## Monkey Patching #############
View
110 public/styles.css
@@ -1,110 +0,0 @@
-/*
- * #443A38 - Dark
- * #936158 - Dusty Pink
- * #DC9767 - Orange
- * #EDCB91 - Yellow
- * #F4EBC3 - Light Yellow
- */
-
-* {
- margin: 0;
- padding: 0;
-}
-
-body {
- font-weight: normal;
- font-size: 14px;
- background-color: #d0d0d0;
- font-family: georgia, times, serif;
-}
-
-#header {
- margin-top: 20px;
- color: #547980;
- padding-left: 20px;
-}
-#dates {
- padding-left: 20px;
-}
-
-#links {
- width: 35%;
-}
-#links a {
- font-size: 14px;
-}
-
-#log {
- float: left;
- padding-left: 20px;
- width: 55%;
-}
-
-#links {
- float: right;
- margin-top: 50px;
- padding-right: 30px;
-}
-#links ul {
- list-style: none;
-}
-
-#log ol {
- list-style: none;
-}
-
-div.me {
- font-style: italic;
-}
-
-div.nick {
- width: 120px;
- float: left;
- font-weight: bold;
- clear:both;
-}
-
-div.logline {
- width: 600px;
- left: 100px;
- float: right;
- margin-bottom: 10px;
-}
-
-div.time {
- font-size: larger;
- padding: 10px 0;
- clear: both;
- left: 100px;
- color: #547980;
-}
-
-
-/* Hash marks for permalinking to a certain point in the log */
-.permalink,
-.permalink:active,
-.permalink:visited {
- color: #594F4F;
- text-decoration: none;
-}
-
-/* Google Custom Search */
-#searchControl {
- margin-top: 15px;
- background-color: #ddd;
- color: black;
- font-family: Arial, sans-serif;
- font-size: 13px;
-}
-
-/* Highlight -siteSearch in results area */
-
-/* bold the section header */
-.gsc-resultsRoot-siteSearch .gsc-title {
- font-weight : bold;
-}
-
-.gsc-resultsRoot-siteSearch .gsc-keeper {
- background-image : url('http://www.google.com/uds/css/darkgrey_check.gif');
- font-weight : bold;
-}
View
6 vendor/sinatra/.gitignore
@@ -1,6 +0,0 @@
-doc
-pkg
-*.log
-.DS_Store
-Manifest
-x.rb
View
3  vendor/sinatra/.gitmodules
@@ -1,3 +0,0 @@
-[submodule "vendor/rack"]
- path = vendor/rack
- url = git://github.com/chneukirchen/rack-mirror.git
View
2  vendor/sinatra/CHANGELOG
@@ -1,2 +0,0 @@
-v0.2.1 File upload fix and minor tweaks
-v0.2.0 Released!
View
22 vendor/sinatra/LICENSE
@@ -1,22 +0,0 @@
-Copyright (c) 2007 Blake Mizerany
-
-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.
View
122 vendor/sinatra/Manifest
@@ -1,122 +0,0 @@
-CHANGELOG
-images/404.png
-images/500.png
-lib/sinatra/test/methods.rb
-lib/sinatra/test/spec.rb
-lib/sinatra/test/unit.rb
-lib/sinatra.rb
-LICENSE
-Manifest
-Rakefile
-README.rdoc
-test/app_test.rb
-test/application_test.rb
-test/builder_test.rb
-test/custom_error_test.rb
-test/diddy_test.rb
-test/erb_test.rb
-test/event_context_test.rb
-test/events_test.rb
-test/haml_test.rb
-test/helper.rb
-test/mapped_error_test.rb
-test/public/foo.xml
-test/rest_test.rb
-test/sass_test.rb
-test/sessions_test.rb
-test/streaming_test.rb
-test/sym_params_test.rb
-test/template_test.rb
-test/use_in_file_templates_test.rb
-test/views/foo.builder
-test/views/foo.erb
-test/views/foo.haml
-test/views/foo.sass
-test/views/foo_layout.erb
-test/views/foo_layout.haml
-test/views/layout_test/foo.builder
-test/views/layout_test/foo.erb
-test/views/layout_test/foo.haml
-test/views/layout_test/foo.sass
-test/views/layout_test/layout.builder
-test/views/layout_test/layout.erb
-test/views/layout_test/layout.haml
-test/views/layout_test/layout.sass
-test/views/no_layout/no_layout.builder
-test/views/no_layout/no_layout.haml
-vendor/rack/AUTHORS
-vendor/rack/bin/rackup
-vendor/rack/contrib/rack_logo.svg
-vendor/rack/COPYING
-vendor/rack/example/lobster.ru
-vendor/rack/example/protectedlobster.rb
-vendor/rack/example/protectedlobster.ru
-vendor/rack/KNOWN-ISSUES
-vendor/rack/lib/rack/adapter/camping.rb
-vendor/rack/lib/rack/auth/abstract/handler.rb
-vendor/rack/lib/rack/auth/abstract/request.rb
-vendor/rack/lib/rack/auth/basic.rb
-vendor/rack/lib/rack/auth/digest/md5.rb
-vendor/rack/lib/rack/auth/digest/nonce.rb
-vendor/rack/lib/rack/auth/digest/params.rb
-vendor/rack/lib/rack/auth/digest/request.rb
-vendor/rack/lib/rack/auth/openid.rb
-vendor/rack/lib/rack/builder.rb
-vendor/rack/lib/rack/cascade.rb
-vendor/rack/lib/rack/commonlogger.rb
-vendor/rack/lib/rack/file.rb
-vendor/rack/lib/rack/handler/cgi.rb
-vendor/rack/lib/rack/handler/fastcgi.rb
-vendor/rack/lib/rack/handler/lsws.rb
-vendor/rack/lib/rack/handler/mongrel.rb
-vendor/rack/lib/rack/handler/scgi.rb
-vendor/rack/lib/rack/handler/webrick.rb
-vendor/rack/lib/rack/lint.rb
-vendor/rack/lib/rack/lobster.rb
-vendor/rack/lib/rack/mock.rb
-vendor/rack/lib/rack/recursive.rb
-vendor/rack/lib/rack/reloader.rb
-vendor/rack/lib/rack/request.rb
-vendor/rack/lib/rack/response.rb
-vendor/rack/lib/rack/session/abstract/id.rb
-vendor/rack/lib/rack/session/cookie.rb
-vendor/rack/lib/rack/session/memcache.rb
-vendor/rack/lib/rack/session/pool.rb
-vendor/rack/lib/rack/showexceptions.rb
-vendor/rack/lib/rack/showstatus.rb
-vendor/rack/lib/rack/static.rb
-vendor/rack/lib/rack/urlmap.rb
-vendor/rack/lib/rack/utils.rb
-vendor/rack/lib/rack.rb
-vendor/rack/Rakefile
-vendor/rack/README
-vendor/rack/test/cgi/lighttpd.conf
-vendor/rack/test/cgi/test
-vendor/rack/test/cgi/test.fcgi
-vendor/rack/test/cgi/test.ru
-vendor/rack/test/spec_rack_auth_basic.rb
-vendor/rack/test/spec_rack_auth_digest.rb
-vendor/rack/test/spec_rack_builder.rb
-vendor/rack/test/spec_rack_camping.rb
-vendor/rack/test/spec_rack_cascade.rb
-vendor/rack/test/spec_rack_cgi.rb
-vendor/rack/test/spec_rack_commonlogger.rb
-vendor/rack/test/spec_rack_fastcgi.rb
-vendor/rack/test/spec_rack_file.rb
-vendor/rack/test/spec_rack_lint.rb
-vendor/rack/test/spec_rack_lobster.rb
-vendor/rack/test/spec_rack_mock.rb
-vendor/rack/test/spec_rack_mongrel.rb
-vendor/rack/test/spec_rack_recursive.rb
-vendor/rack/test/spec_rack_request.rb
-vendor/rack/test/spec_rack_response.rb
-vendor/rack/test/spec_rack_session_cookie.rb
-vendor/rack/test/spec_rack_session_memcache.rb
-vendor/rack/test/spec_rack_session_pool.rb
-vendor/rack/test/spec_rack_showexceptions.rb
-vendor/rack/test/spec_rack_showstatus.rb
-vendor/rack/test/spec_rack_static.rb
-vendor/rack/test/spec_rack_urlmap.rb
-vendor/rack/test/spec_rack_utils.rb
-vendor/rack/test/spec_rack_webrick.rb
-vendor/rack/test/testrequest.rb
View
464 vendor/sinatra/README.rdoc
@@ -1,464 +0,0 @@
-= Sinatra
-
-Sinatra a DSL for quickly creating web-applications in Ruby with minimal effort.
-
-= Sample app:
-
- # myapp.rb
-
- require 'rubygems'
- require 'sinatra'
-
- get '/' do
- 'Hello world!'
- end
-
-Ruby this as <tt>ruby myapp.rb</tt> and view at <tt>http://localhost:4567</tt>
-
-= RESTful
-
- get '/' do
- .. show things ..
- end
-
- post '/' do
- .. create something ..
- end
-
- put '/' do
- .. update something ..
- end
-
- delete '/' do
- .. annihilate something ..
- end
-
- head '/' do
-
- end
-
-NOTE: <tt>put</tt> and <tt>delete</tt> are triggered when a <tt>_method</tt> param is set to PUT or DELETE and the HTTP_REQUEST_METHOD is a POST
-
-= Routes
-
-NOTE: Routes are looked up in order of declaration
-
-Simple
-
- get '/hi' do
- ...
- end
-
-With params
-
- get '/:name' do
- # matches /sinatra and the like and sets params[:name]
- end
-
-Splat'n
-
- get '/say/*/to/*' do
- # matches /say/hello/to/world
- params["splat"] # => ["hello", "world"]
- end
-
- get '/download/*.*' do
- # matches /download/path/to/file.xml
- params["splat"] # => ["path/to/file", "xml"]
- end
-
-Get an agent!
-
- get '/foo', :agent => /Songbird (\d\.\d)[\d\/]*?/ do
- "You're using Songbird version #{params[:agent][0]}"
- end
-
- get '/foo' do
- # matches non-songbird browsers
- end
-
-= Static files
-
-Put all of your static content in the ./public directory
-
- root
- \ public
-
-If a file exists that maps to the REQUEST_PATH then it is served and the request end; Sinatra will look for and event that matches the path otherwise
-
-= Views (if you need MVC)
-
-All file-based views are looked up in:
-
- root
- | - views/
-
-
-== Templates
-
-=== Haml
-
- get '/' do
- haml :index
- end
-
-This will render <tt>./views/index.haml</tt>
-
-=== Sass
- get '/stylesheet.css' do
- content_type 'text/css', :charset => 'utf-8'
- sass :stylesheet
- end
-
-This will render <tt>./views/stylesheet.sass</tt>
-
-=== Inline
-
- get '/' do
- haml '%div.title Hello World'
- end
-
-This will render the inlined template string
-
-=== Accessing Variables
-
-Templates are rendered in the context the current Sinatra::EventContext. This means you get all instance/class variables and methods it has access to.
-
- get '/:id' do
- @foo = Foo.find(params[:id])
- haml '%h1== @foo.name'
- end
-
-Send local objects like:
-
- get '/:id' do
- localvar = Foo.find(params[:id])
- haml '%h1== localvar.name', :locals => { :localvar => localvar }
- end
-
-This is more ideal for rendering templates as partials from within templates
-
-== In file templates
-
-This one is cool:
-
- get '/' do
- haml :index
- end
-
- use_in_file_templates!
-
- __END__
-
- @@ layout
- X
- = yield
- X
-
- @@ index
- %div.title Hello world!!!!!
-
-Try it!
-
-= You can do this too but it's not as cool
-
- template :layout do
- "X\n=yield\nX"
- end
-
- template :index do
- '%div.title Hello World!'
- end
-
- get '/' do
- haml :index
- end
-
-=== Erb
-
-This works like Haml except you use <tt>erb</tt> instead of <tt>haml</tt>
-
-=== Sass
-
-This works like Haml except you use <tt>sass</tt> instead of <tt>haml</tt>. It's also a good idea to add <tt>content_type 'text/css', :charset => 'utf-8'</tt> before your call to <tt>sass</tt> so Sinatra returns the proper content type header with the file.
-
-=== Builder
-
-See Sinatra::Builder
-
-= Helpers
-
-It is ill-advised to create helpers on (main). Use the handy <tt>helpers</tt> to install helper methods on Sinatra::EventContext for use inside events and templates.
-
-Example:
-
- helpers do
-
- def bar(name)
- "#{name}bar"
- end
-
- end
-
- get '/:name' do
- bar(params[:name])
- end
-
-= Before filters
-
-These are run in Sinatra::EventContext
-
- before do
- .. this code will run before each event ..
- end
-
-= Halt!
-
-To immediately stop a request during a before filter or event use:
-
- throw :halt
-
-=== Variations
-
-Set the body to the result of a helper method
-
- throw :halt, :helper_method
-
-Set the body to the result of a helper method after sending it parameters from the local scope
-
- throw :halt, [:helper_method, foo, bar]
-
-Set the body to a simple string
-
- throw :halt, 'this will be the body'
-
-Set status then the body
-
- throw :halt, [401, 'go away!']
-
-Set the status then call a helper method with params from local scope
-
- throw :halt, [401, [:helper_method, foo, bar]]
-
-Run a proc inside the Sinatra::EventContext instance and set the body to the result
-
- throw :halt, lambda { puts 'In a proc!'; 'I just wrote to $stdout!' }
-
-Create you own to_result
-
- class MyResultObject
- def to_result(event_context, *args)
- event_context.body = 'This will be the body!
- end
- end
-
- get '/' do
- throw :halt, MyResultObject.new
- end
-
-Get the gist? If you want more fun with this then checkout <tt>to_result</tt> on Array, Symbol, Fixnum, NilClass.
-
-= Configuration & Re-loading
-
-Sinatra supports multiple environments and re-loading. Re-loading happens on every request when in :development. Wrap your configurations in <tt>configure</tt> (i.e. Database connections, Constants, etc.) to protect them from re-loading and to only work in certain environments.
-
-All environments:
-
- configure do
-
- end
-
-Production
-
- configure :production do
-
- end
-
-Two at a time:
-
- configure :production, :test do
-
- end
-
-This is also really nifty for error handling.
-
-= Error handling
-
-=== Not Found
-
-Remember: These are run inside the Sinatra::EventContext which means you get all the goodies is has to offer (i.e. haml, erb, :halt, etc.)
-
-Whenever NotFound is raised this will be called
-
- not_found do
- 'This is nowhere to be found'
- end
-
-=== Error
-
-By default +error+ will catch Sinatra::ServerError
-
-Sinatra will pass you the error via the 'sinatra.error' in request.env
-
- error do
- 'Sorry there was a nasty error - ' + request.env['sinatra.error'].name
- end
-
-Custom error mapping:
-
- error MyCustomError do
- 'So what happened was...' + request.env['sinatra.error'].message
- end
-
-then if this happens:
-
- get '/' do
- raise MyCustomError, 'something bad'
- end
-
-you gets this:
-
- So what happened was... something bad
-
-one guess what this does ;)
-
- not_found do
- 'I have no clue what you're looking for'
- end
-
-Try it!
-
-
-Because Sinatra give you a default <tt>not_found</tt> and <tt>error</tt> do :production that are secure. If you want to customize only for :production but want to keep the friendly helper screens for :development then do this:
-
- configure :production do
-
- not_found do
- "We're so sorry, but we don't what this is"
- end
-
- error do
- "Something really nasty happened. We're on it!"
- end
-
- end
-
-= Mime types
-
-When using send_file or static files you may have mime types Sinatra doesn't understand. Use +mime+ in those cases.
-
- mime :foo, 'text/foo'
-
-= Using Rack Middleware
-
-Sinatra rides on Rack[http://rack.rubyforge.org/], a minimal standard interface for Ruby web frameworks. One of Rack's most interesting capabilities for application developers is support for "middleware" -- components that sit between the server and your application monitoring and/or manipulating the HTTP request/response to provide various types of common functionality. What's more, middleware is portable between web frameworks, so middleware components developed under, e.g., Merb, can be used with Sinatra and vice versa.
-
-Sinatra makes building Rack middleware pipelines a cinch via a top-level +use+ method:
-
- require 'sinatra'
- require 'my_custom_middleware'
-
- use Rack::Lint
- use MyCustomMiddleware
-
- get '/hello' do
- 'Hello World'
- end
-
-The semantics of +use+ are identical to those defined for the Rack::Builder[http://rack.rubyforge.org/doc/classes/Rack/Builder.html] DSL (most frequently used from rackup files). For example, the +use+ method accepts multiple/variable args as well as blocks:
-
- use Rack::Auth::Basic do |username, password|
- username == 'admin' && password == 'secret'
- end
-
-Rack is distributed with a variety of standard middleware for logging, debugging, URL routing, authentication, and session handling. Sinatra uses many of of these components automatically based on configuration so you typically don't have to +use+ them explicitly.
-
-= Testing
-
-=== Methods
-
- get_it path, params
- get_it path, params.merge(:env => { 'HTTP_HOST' => 'www.sinatrarb.com' }) or
- get_it path, params.merge(:env => { :host => 'www.sinatrarb.com' })
-
-RESTful:
-
- post_it '/foo', '<myxml></myxml>', 'HTTP_ACCEPT' => 'application/xml'
-
-also works with:
-
- get_it, post_it, put_it, delete_it, head_it
-
-=== Test/Unit
-
- require 'my_sinatra_app'
- require 'sinatra/test/unit'
-
- class MyAppTest < Test::Unit::TestCase
-
- def test_my_default
- get_it '/'
- assert_equal 'My Default Page!', @response.body
- end
-
- def test_with_agent
- get_it '/', :agent => 'Songbird'
- assert_equal 'You're in Songbird!', @response.body
- end
-
- ...
-
- end
-
-=== Test/Spec
-
- require 'my_sinatra_app'
- require 'sinatra/test/spec'
-
- context 'My app'
-
- should "show a default page" do
- get_it '/'
- should.be.ok
- body.should.equal 'My Default Page!'
- end
- ...
-
- end
-
-== Test helpers
-
-See Sinatra::Test::Methods
-
-= Irb
-
-This will be back in soon
-
-= Command line
-
-Run your sinatra file like:
-
- ruby myapp.rb [options]
-
-Options are:
-
- -h # help
- -p # set the port (default is 4567)
- -e # set the environment (default is development)
- -x # turn on the mutex lock (default is off)
-
-= Contribute
-
- cd where/you/keep/your/projects
- git clone git://github.com/bmizerany/sinatra.git
- cd your_project
- ln -s ../sinatra/
-
-at the top of your sinatra.rb file
-
- $:.unshift File.dirname(__FILE__) + '/sinatra/lib'
- require 'sinatra'
-
- get '/about' do
- "I'm running on Version " + Sinatra::Version.combined
- end
View
14 vendor/sinatra/Rakefile
@@ -1,14 +0,0 @@
-require 'rubygems'
-require 'echoe'
-
-task :default => :test
-
-Echoe.new("sinatra") do |p|
- p.author = "Blake Mizerany"
- p.summary = "Classy web-development dressed in a DSL"
- p.url = "http://www.sinatrarb.com"
- p.docs_host = "sinatrarb.com:/var/www/blakemizerany.com/public/docs/"
- p.dependencies = ["mongrel >=1.0.1"]
- p.install_message = "*** Be sure to checkout the site for helpful tips! sinatrarb.com ***"
- p.include_rakefile = true
-end
View
BIN  vendor/sinatra/images/404.png
Deleted file not rendered
View
BIN  vendor/sinatra/images/500.png
Deleted file not rendered
View
1,506 vendor/sinatra/lib/sinatra.rb
@@ -1,1506 +0,0 @@
-Dir[File.dirname(__FILE__) + "/../vendor/*"].each do |l|
- $:.unshift "#{File.expand_path(l)}/lib"
-end
-
-require 'rack'
-
-require 'rubygems'
-require 'time'
-require 'ostruct'
-require "uri"
-
-if ENV['SWIFT']
- require 'swiftcore/swiftiplied_mongrel'
- puts "Using Swiftiplied Mongrel"
-elsif ENV['EVENT']
- require 'swiftcore/evented_mongrel'
- puts "Using Evented Mongrel"
-end
-
-class Class
- def dslify_writer(*syms)
- syms.each do |sym|
- class_eval <<-end_eval
- def #{sym}(v=nil)
- self.send "#{sym}=", v if v
- v
- end
- end_eval
- end
- end
-end
-
-module Rack #:nodoc:
-
- class Request #:nodoc:
-
- # Set of request method names allowed via the _method parameter hack. By default,
- # all request methods defined in RFC2616 are included, with the exception of
- # TRACE and CONNECT.
- POST_TUNNEL_METHODS_ALLOWED = %w( PUT DELETE OPTIONS HEAD )
-
- # Return the HTTP request method with support for method tunneling using the POST
- # _method parameter hack. If the real request method is POST and a _method param is
- # given and the value is one defined in +POST_TUNNEL_METHODS_ALLOWED+, return the value
- # of the _method param instead.
- def request_method
- if post_tunnel_method_hack?
- params['_method'].upcase
- else
- @env['REQUEST_METHOD']
- end
- end
-
- def user_agent
- @env['HTTP_USER_AGENT']
- end
-
- private
-
- # Return truthfully if and only if the following conditions are met: 1.) the
- # *actual* request method is POST, 2.) the request content-type is one of
- # 'application/x-www-form-urlencoded' or 'multipart/form-data', 3.) there is a
- # "_method" parameter in the POST body (not in the query string), and 4.) the
- # method parameter is one of the verbs listed in the POST_TUNNEL_METHODS_ALLOWED
- # list.
- def post_tunnel_method_hack?
- @env['REQUEST_METHOD'] == 'POST' &&
- POST_TUNNEL_METHODS_ALLOWED.include?(self.POST.fetch('_method', '').upcase)
- end
-
- end
-
- module Utils
- extend self
- end
-
-end
-
-module Sinatra
- extend self
-
- module Version
- MAJOR = '0'
- MINOR = '2'
- REVISION = '3'
- def self.combined
- [MAJOR, MINOR, REVISION].join('.')
- end
- end
-
- class NotFound < RuntimeError; end
- class ServerError < RuntimeError; end
-
- Result = Struct.new(:block, :params, :status) unless defined?(Result)
-
- def options
- application.options
- end
-
- def application
- @app ||= Application.new
- end
-
- def application=(app)
- @app = app
- end
-
- def port
- application.options.port
- end
-
- def env
- application.options.env
- end
-
- # Deprecated: use application instead of build_application.
- alias :build_application :application
-
- def server
- options.server ||= defined?(Rack::Handler::Thin) ? "thin" : "mongrel"
-
- # Convert the server into the actual handler name
- handler = options.server.capitalize
-
- # If the convenience conversion didn't get us anything,
- # fall back to what the user actually set.
- handler = options.server unless Rack::Handler.const_defined?(handler)
-
- @server ||= eval("Rack::Handler::#{handler}")
- end
-
- def run
- begin
- puts "== Sinatra has taken the stage on port #{port} for #{env} with backup by #{server.name}"
- require 'pp'
- server.run(application, :Port => port) do |server|
- trap(:INT) do
- server.stop
- puts "\n== Sinatra has ended his set (crowd applauds)"
- end
- end
- rescue Errno::EADDRINUSE => e
- puts "== Someone is already performing on port #{port}!"
- end
- end
-
- class Event
-
- URI_CHAR = '[^/?:,&#\.]'.freeze unless defined?(URI_CHAR)
- PARAM = /(:(#{URI_CHAR}+)|\*)/.freeze unless defined?(PARAM)
- SPLAT = /(.*?)/
- attr_reader :path, :block, :param_keys, :pattern, :options
-
- def initialize(path, options = {}, &b)
- @path = URI.encode(path)
- @block = b
- @param_keys = []
- @options = options
- splats = 0
- regex = @path.to_s.gsub(PARAM) do |match|
- if match == "*"
- @param_keys << "_splat_#{splats}"
- splats += 1
- SPLAT.to_s
- else
- @param_keys << $2
- "(#{URI_CHAR}+)"
- end
- end
-
- @pattern = /^#{regex}$/
- end
-
- def invoke(request)
- params = {}
- if agent = options[:agent]
- return unless request.user_agent =~ agent
- params[:agent] = $~[1..-1]
- end
- if host = options[:host]
- return unless host === request.host
- end
- return unless pattern =~ request.path_info.squeeze('/')
- params.merge!(param_keys.zip($~.captures.map(&:from_param)).to_hash)
- splats = params.select { |k, v| k =~ /^_splat_\d+$/ }.sort.map(&:last)
- unless splats.empty?
- params.delete_if { |k, v| k =~ /^_splat_\d+$/ }
- params["splat"] = splats
- end
- Result.new(block, params, 200)
- end
-
- end
-
- class Error
-
- attr_reader :code, :block
-
- def initialize(code, &b)
- @code, @block = code, b
- end
-
- def invoke(request)
- Result.new(block, {}, 404)
- end
-
- end
-
- class Static
-
- def invoke(request)
- return unless File.file?(
- Sinatra.application.options.public + request.path_info.http_unescape
- )
- Result.new(block, {}, 200)
- end
-
- def block
- Proc.new do
- send_file Sinatra.application.options.public + request.path_info.http_unescape,
- :disposition => nil
- end
- end
-
- end
-
- # Adapted from actionpack
- # Methods for sending files and streams to the browser instead of rendering.
- module Streaming
- DEFAULT_SEND_FILE_OPTIONS = {
- :type => 'application/octet-stream'.freeze,
- :disposition => 'attachment'.freeze,
- :stream => true,
- :buffer_size => 4096
- }.freeze
-
- class MissingFile < RuntimeError; end
-
- class FileStreamer
-
- attr_reader :path, :options
-
- def initialize(path, options)
- @path, @options = path, options
- end
-
- def to_result(cx, *args)
- self
- end
-
- def each
- File.open(path, 'rb') do |file|
- while buf = file.read(options[:buffer_size])
- yield buf
- end
- end
- end
-
- end
-
- protected
- # Sends the file by streaming it 4096 bytes at a time. This way the
- # whole file doesn't need to be read into memory at once. This makes
- # it feasible to send even large files.
- #
- # Be careful to sanitize the path parameter if it coming from a web
- # page. send_file(params[:path]) allows a malicious user to
- # download any file on your server.
- #
- # Options:
- # * <tt>:filename</tt> - suggests a filename for the browser to use.
- # Defaults to File.basename(path).
- # * <tt>:type</tt> - specifies an HTTP content type.
- # Defaults to 'application/octet-stream'.
- # * <tt>:disposition</tt> - specifies whether the file will be shown inline or downloaded.
- # Valid values are 'inline' and 'attachment' (default). When set to nil, the
- # Content-Disposition and Content-Transfer-Encoding headers are omitted entirely.
- # * <tt>:stream</tt> - whether to send the file to the user agent as it is read (true)
- # or to read the entire file before sending (false). Defaults to true.
- # * <tt>:buffer_size</tt> - specifies size (in bytes) of the buffer used to stream the file.
- # Defaults to 4096.
- # * <tt>:status</tt> - specifies the status code to send with the response. Defaults to '200 OK'.
- # * <tt>:last_modified</tt> - an optional RFC 2616 formatted date value (See Time#httpdate)
- # indicating the last modified time of the file. If the request includes an
- # If-Modified-Since header that matches this value exactly, a 304 Not Modified response
- # is sent instead of the file. Defaults to the file's last modified
- # time.
- #
- # The default Content-Type and Content-Disposition headers are
- # set to download arbitrary binary files in as many browsers as
- # possible. IE versions 4, 5, 5.5, and 6 are all known to have
- # a variety of quirks (especially when downloading over SSL).
- #
- # Simple download:
- # send_file '/path/to.zip'
- #
- # Show a JPEG in the browser:
- # send_file '/path/to.jpeg', :type => 'image/jpeg', :disposition => 'inline'
- #
- # Show a 404 page in the browser:
- # send_file '/path/to/404.html, :type => 'text/html; charset=utf-8', :status => 404
- #
- # Read about the other Content-* HTTP headers if you'd like to
- # provide the user with more information (such as Content-Description).
- # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.11
- #
- # Also be aware that the document may be cached by proxies and browsers.
- # The Pragma and Cache-Control headers declare how the file may be cached
- # by intermediaries. They default to require clients to validate with
- # the server before releasing cached responses. See
- # http://www.mnot.net/cache_docs/ for an overview of web caching and
- # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9
- # for the Cache-Control header spec.
- def send_file(path, options = {}) #:doc:
- raise MissingFile, "Cannot read file #{path}" unless File.file?(path) and File.readable?(path)
-
- options[:length] ||= File.size(path)
- options[:filename] ||= File.basename(path)
- options[:type] ||= Rack::File::MIME_TYPES[File.extname(options[:filename])[1..-1]] || 'text/plain'
- options[:last_modified] ||= File.mtime(path).httpdate
- send_file_headers! options
-
- if options[:stream]
- throw :halt, [options[:status] || 200, FileStreamer.new(path, options)]
- else
- File.open(path, 'rb') { |file| throw :halt, [options[:status] || 200, file.read] }
- end
- end
-
- # Send binary data to the user as a file download. May set content type, apparent file name,
- # and specify whether to show data inline or download as an attachment.
- #
- # Options:
- # * <tt>:filename</tt> - Suggests a filename for the browser to use.
- # * <tt>:type</tt> - specifies an HTTP content type.
- # Defaults to 'application/octet-stream'.
- # * <tt>:disposition</tt> - specifies whether the file will be shown inline or downloaded.
- # Valid values are 'inline' and 'attachment' (default).
- # * <tt>:status</tt> - specifies the status code to send with the response. Defaults to '200 OK'.
- # * <tt>:last_modified</tt> - an optional RFC 2616 formatted date value (See Time#httpdate)
- # indicating the last modified time of the response entity. If the request includes an
- # If-Modified-Since header that matches this value exactly, a 304 Not Modified response
- # is sent instead of the data.
- #
- # Generic data download:
- # send_data buffer
- #
- # Download a dynamically-generated tarball:
- # send_data generate_tgz('dir'), :filename => 'dir.tgz'
- #
- # Display an image Active Record in the browser:
- # send_data image.data, :type => image.content_type, :disposition => 'inline'
- #
- # See +send_file+ for more information on HTTP Content-* headers and caching.
- def send_data(data, options = {}) #:doc:
- send_file_headers! options.merge(:length => data.size)
- throw :halt, [options[:status] || 200, data]
- end
-
- private
- def send_file_headers!(options)
- options = DEFAULT_SEND_FILE_OPTIONS.merge(options)
- [:length, :type, :disposition].each do |arg|
- raise ArgumentError, ":#{arg} option required" unless options.key?(arg)
- end
-
- # Send a "304 Not Modified" if the last_modified option is provided and matches
- # the If-Modified-Since request header value.
- if last_modified = options[:last_modified]
- header 'Last-Modified' => last_modified
- throw :halt, [ 304, '' ] if last_modified == request.env['HTTP_IF_MODIFIED_SINCE']
- end
-
- headers(
- 'Content-Length' => options[:length].to_s,
- 'Content-Type' => options[:type].strip # fixes a problem with extra '\r' with some browsers
- )
-
- # Omit Content-Disposition and Content-Transfer-Encoding headers if
- # the :disposition option set to nil.
- if !options[:disposition].nil?
- disposition = options[:disposition].dup || 'attachment'
- disposition <<= %(; filename="#{options[:filename]}") if options[:filename]
- headers 'Content-Disposition' => disposition, 'Content-Transfer-Encoding' => 'binary'
- end
-
- # Fix a problem with IE 6.0 on opening downloaded files:
- # If Cache-Control: no-cache is set (which Rails does by default),
- # IE removes the file it just downloaded from its cache immediately
- # after it displays the "open/save" dialog, which means that if you
- # hit "open" the file isn't there anymore when the application that
- # is called for handling the download is run, so let's workaround that
- header('Cache-Control' => 'private') if headers['Cache-Control'] == 'no-cache'
- end
- end
-
-
- # Helper methods for building various aspects of the HTTP response.
- module ResponseHelpers
-
- # Immediately halt response execution by redirecting to the resource
- # specified. The +path+ argument may be an absolute URL or a path
- # relative to the site root. Additional arguments are passed to the
- # halt.
- #
- # With no integer status code, a '302 Temporary Redirect' response is
- # sent. To send a permanent redirect, pass an explicit status code of
- # 301:
- #
- # redirect '/somewhere/else', 301
- #
- # NOTE: No attempt is made to rewrite the path based on application
- # context. The 'Location' response header is set verbatim to the value
- # provided.
- def redirect(path, *args)
- status(302)
- header 'Location' => path
- throw :halt, *args
- end
-
- # Access or modify response headers. With no argument, return the
- # underlying headers Hash. With a Hash argument, add or overwrite
- # existing response headers with the values provided:
- #
- # headers 'Content-Type' => "text/html;charset=utf-8",
- # 'Last-Modified' => Time.now.httpdate,
- # 'X-UA-Compatible' => 'IE=edge'
- #
- # This method also available in singular form (#header).
- def headers(header = nil)
- @response.headers.merge!(header) if header
- @response.headers
- end
- alias :header :headers
-
- # Set the content type of the response body (HTTP 'Content-Type' header).
- #
- # The +type+ argument may be an internet media type (e.g., 'text/html',
- # 'application/xml+atom', 'image/png') or a Symbol key into the
- # Rack::File::MIME_TYPES table.
- #
- # Media type parameters, such as "charset", may also be specified using the
- # optional hash argument:
- #
- # get '/foo.html' do
- # content_type 'text/html', :charset => 'utf-8'
- # "<h1>Hello World</h1>"
- # end
- #
- def content_type(type, params={})
- type = Rack::File::MIME_TYPES[type.to_s] if type.kind_of?(Symbol)
- fail "Invalid or undefined media_type: #{type}" if type.nil?
- if params.any?
- params = params.collect { |kv| "%s=%s" % kv }.join(', ')
- type = [ type, params ].join(";")
- end
- response.header['Content-Type'] = type
- end
-
- # Set the last modified time of the resource (HTTP 'Last-Modified' header)
- # and halt if conditional GET matches. The +time+ argument is a Time,
- # DateTime, or other object that responds to +to_time+.
- #
- # When the current request includes an 'If-Modified-Since' header that
- # matches the time specified, execution is immediately halted with a
- # '304 Not Modified' response.
- #
- # Calling this method before perfoming heavy processing (e.g., lengthy
- # database queries, template rendering, complex logic) can dramatically
- # increase overall throughput with caching clients.
- def last_modified(time)
- time = time.to_time if time.respond_to?(:to_time)
- time = time.httpdate if time.respond_to?(:httpdate)
- response.header['Last-Modified'] = time
- throw :halt, 304 if time == request.env['HTTP_IF_MODIFIED_SINCE']
- time
- end
-
- # Set the response entity tag (HTTP 'ETag' header) and halt if conditional
- # GET matches. The +value+ argument is an identifier that uniquely
- # identifies the current version of the resource. The +strength+ argument
- # indicates whether the etag should be used as a :strong (default) or :weak
- # cache validator.
- #
- # When the current request includes an 'If-None-Match' header with a
- # matching etag, execution is immediately halted. If the request method is
- # GET or HEAD, a '304 Not Modified' response is sent. For all other request
- # methods, a '412 Precondition Failed' response is sent.
- #
- # Calling this method before perfoming heavy processing (e.g., lengthy
- # database queries, template rendering, complex logic) can dramatically
- # increase overall throughput with caching clients.
- #
- # ==== See Also
- # {RFC2616: ETag}[http://w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.19],
- # ResponseHelpers#last_modified
- def entity_tag(value, strength=:strong)
- value =
- case strength
- when :strong then '"%s"' % value
- when :weak then 'W/"%s"' % value
- else raise TypeError, "strength must be one of :strong or :weak"
- end
- response.header['ETag'] = value
-
- # Check for If-None-Match request header and halt if match is found.
- etags = (request.env['HTTP_IF_NONE_MATCH'] || '').split(/\s*,\s*/)
- if etags.include?(value) || etags.include?('*')
- # GET/HEAD requests: send Not Modified response
- throw :halt, 304 if request.get? || request.head?
- # Other requests: send Precondition Failed response
- throw :halt, 412
- end
- end
-
- alias :etag :entity_tag
-
- end
-
- module RenderingHelpers
-
- def render(renderer, template, options={})
- m = method("render_#{renderer}")
- result = m.call(resolve_template(renderer, template, options), options)
- if layout = determine_layout(renderer, template, options)
- result = m.call(resolve_template(renderer, layout, options), options) { result }
- end
- result
- end
-
- def determine_layout(renderer, template, options)
- return if options[:layout] == false
- layout_from_options = options[:layout] || :layout
- resolve_template(renderer, layout_from_options, options, false)
- end
-
- private
-
- def resolve_template(renderer, template, options, scream = true)
- case template
- when String
- template
- when Proc
- template.call
- when Symbol
- if proc = templates[template]
- resolve_template(renderer, proc, options, scream)
- else
- read_template_file(renderer, template, options, scream)
- end
- else
- nil
- end
- end
-
- def read_template_file(renderer, template, options, scream = true)
- path = File.join(
- options[:views_directory] || Sinatra.application.options.views,
- "#{template}.#{renderer}"
- )
- unless File.exists?(path)
- raise Errno::ENOENT.new(path) if scream
- nil
- else
- File.read(path)
- end
- end
-
- def templates
- Sinatra.application.templates
- end
-
- end
-
- module Erb
-
- def erb(content, options={})
- require 'erb'
- render(:erb, content, options)
- end
-
- private
-
- def render_erb(content, options = {})
- locals_opt = options.delete(:locals) || {}
-
- locals_code = ""
- locals_hash = {}
- locals_opt.each do |key, value|
- locals_code << "#{key} = locals_hash[:#{key}]\n"
- locals_hash[:"#{key}"] = value
- end
-
- body = ::ERB.new(content).src
- eval("#{locals_code}#{body}", binding)
- end
-
- end
-
- module Haml
-
- def haml(content, options={})
- require 'haml'
- render(:haml, content, options)
- end
-
- private
-
- def render_haml(content, options = {}, &b)
- haml_options = (options[:options] || {}).merge(Sinatra.options.haml || {})
- ::Haml::Engine.new(content, haml_options).render(options[:scope] || self, options[:locals] || {}, &b)
- end
-
- end
-
- # Generate valid CSS using Sass (part of Haml)
- #
- # Sass templates can be in external files with <tt>.sass</tt> extension or can use Sinatra's
- # in_file_templates. In either case, the file can be rendered by passing the name of
- # the template to the +sass+ method as a symbol.
- #
- # Unlike Haml, Sass does not support a layout file, so the +sass+ method will ignore both
- # the default <tt>layout.sass</tt> file and any parameters passed in as <tt>:layout</tt> in
- # the options hash.
- #
- # === Sass Template Files
- #
- # Sass templates can be stored in separate files with a <tt>.sass</tt>
- # extension under the view path.
- #
- # Example:
- # get '/stylesheet.css' do
- # header 'Content-Type' => 'text/css; charset=utf-8'
- # sass :stylesheet
- # end
- #
- # The "views/stylesheet.sass" file might contain the following:
- #
- # body
- # #admin
- # :background-color #CCC
- # #main
- # :background-color #000
- # #form
- # :border-color #AAA
- # :border-width 10px
- #
- # And yields the following output:
- #
- # body #admin {
- # background-color: #CCC; }
- # body #main {
- # background-color: #000; }
- #
- # #form {
- # border-color: #AAA;
- # border-width: 10px; }
- #
- #
- # NOTE: Haml must be installed or a LoadError will be raised the first time an
- # attempt is made to render a Sass template.
- #
- # See http://haml.hamptoncatlin.com/docs/rdoc/classes/Sass.html for comprehensive documentation on Sass.
-
-
- module Sass
-
- def sass(content, options = {})
- require 'sass'
-
- # Sass doesn't support a layout, so we override any possible layout here
- options[:layout] = false
-
- render(:sass, content, options)
- end
-
- private
-
- def render_sass(content, options = {})
- ::Sass::Engine.new(content).render
- end
- end
-
- # Generating conservative XML content using Builder templates.
- #
- # Builder templates can be inline by passing a block to the builder method, or in
- # external files with +.builder+ extension by passing the name of the template
- # to the +builder+ method as a Symbol.
- #
- # === Inline Rendering
- #
- # If the builder method is given a block, the block is called directly with an
- # +XmlMarkup+ instance and the result is returned as String:
- # get '/who.xml' do
- # builder do |xml|
- # xml.instruct!
- # xml.person do
- # xml.name "Francis Albert Sinatra",
- # :aka => "Frank Sinatra"
- # xml.email 'frank@capitolrecords.com'
- # end
- # end
- # end
- #
- # Yields the following XML:
- # <?xml version='1.0' encoding='UTF-8'?>
- # <person>
- # <name aka='Frank Sinatra'>Francis Albert Sinatra</name>
- # <email>Frank Sinatra</email>
- # </person>
- #
- # === Builder Template Files
- #
- # Builder templates can be stored in separate files with a +.builder+
- # extension under the view path. An +XmlMarkup+ object named +xml+ is automatically
- # made available to template.
- #
- # Example:
- # get '/bio.xml' do
- # builder :bio
- # end
- #
- # The "views/bio.builder" file might contain the following:
- # xml.instruct! :xml, :version => '1.1'
- # xml.person do
- # xml.name "Francis Albert Sinatra"
- # xml.aka "Frank Sinatra"
- # xml.aka "Ol' Blue Eyes"
- # xml.aka "The Chairman of the Board"
- # xml.born 'date' => '1915-12-12' do
- # xml.text! "Hoboken, New Jersey, U.S.A."
- # end
- # xml.died 'age' => 82
- # end
- #
- # And yields the following output:
- # <?xml version='1.1' encoding='UTF-8'?>
- # <person>
- # <name>Francis Albert Sinatra</name>
- # <aka>Frank Sinatra</aka>
- # <aka>Ol&apos; Blue Eyes</aka>
- # <aka>The Chairman of the Board</aka>
- # <born date='1915-12-12'>Hoboken, New Jersey, U.S.A.</born>
- # <died age='82' />
- # </person>
- #
- # NOTE: Builder must be installed or a LoadError will be raised the first time an
- # attempt is made to render a builder template.
- #
- # See http://builder.rubyforge.org/ for comprehensive documentation on Builder.
- module Builder
-
- def builder(content=nil, options={}, &block)
- options, content = content, nil if content.is_a?(Hash)
- content = Proc.new { block } if content.nil?
- render(:builder, content, options)
- end
-
- private
-
- def render_builder(content, options = {}, &b)
- require 'builder'
- xml = ::Builder::XmlMarkup.new(:indent => 2)
- case content
- when String
- eval(content, binding, '<BUILDER>', 1)
- when Proc
- content.call(xml)
- end
- xml.target!
- end
-
- end
-
- class EventContext
-
- include ResponseHelpers
- include Streaming
- include RenderingHelpers
- include Erb
- include Haml
- include Builder
- include Sass
-
- attr_accessor :request, :response
-
- dslify_writer :status, :body
-
- def initialize(request, response, route_params)
- @request = request
- @response = response
- @route_params = route_params
- @response.body = nil
- end
-
- def params
- @params ||= begin
- h = Hash.new {|h,k| h[k.to_s] if Symbol === k}
- h.merge(@route_params.merge(@request.params))
- end
- end
-
- def data
- @data ||= params.keys.first
- end
-
- def stop(*args)
- throw :halt, args
- end
-
- def complete(returned)
- @response.body || returned
- end
-
- def session
- request.env['rack.session'] ||= {}
- end
-
- private
-
- def method_missing(name, *args, &b)
- @response.send(name, *args, &b)
- end
-
- end
-
-
- # The Application class represents the top-level working area of a
- # Sinatra app. It provides the DSL for defining various aspects of the
- # application and implements a Rack compatible interface for dispatching
- # requests.
- #
- # Many of the instance methods defined in this class (#get, #post,
- # #put, #delete, #layout, #before, #error, #not_found, etc.) are
- # available at top-level scope. When invoked from top-level, the
- # messages are forwarded to the "default application" (accessible
- # at Sinatra::application).
- class Application
-
- # Hash of event handlers with request method keys and
- # arrays of potential handlers as values.
- attr_reader :events
-
- # Hash of error handlers with error status codes as keys and
- # handlers as values.
- attr_reader :errors
-
- # Hash of template name mappings.
- attr_reader :templates
-
- # Hash of filters with event name keys (:before) and arrays of
- # handlers as values.
- attr_reader :filters
-
- # Array of objects to clear during reload. The objects in this array
- # must respond to :clear.
- attr_reader :clearables
-
- # Object including open attribute methods for modifying Application
- # configuration.
- attr_reader :options
-
- # List of methods available from top-level scope. When invoked from
- # top-level the method is forwarded to the default application
- # (Sinatra::application).
- FORWARD_METHODS = %w[
- get put post delete head template layout before error not_found
- configures configure set set_options set_option enable disable use
- ]
-
- # Create a new Application with a default configuration taken
- # from the default_options Hash.
- #
- # NOTE: A default Application is automatically created the first
- # time any of Sinatra's DSL related methods is invoked so there
- # is typically no need to create an instance explicitly. See
- # Sinatra::application for more information.
- def initialize
- @reloading = false
- @clearables = [
- @events = Hash.new { |hash, key| hash[key] = [] },
- @errors = Hash.new,
- @filters = Hash.new { |hash, key| hash[key] = [] },
- @templates = Hash.new,
- @middleware = []
- ]
- @options = OpenStruct.new(self.class.default_options)
- load_default_configuration!
- end
-
- # Hash of default application configuration options. When a new
- # Application is created, the #options object takes its initial values
- # from here.
- #
- # Changes to the default_options Hash effect only Application objects
- # created after the changes are made. For this reason, modifications to
- # the default_options Hash typically occur at the very beginning of a
- # file, before any DSL related functions are invoked.
- def self.default_options
- return @default_options unless @default_options.nil?
- root = File.expand_path(File.dirname($0))
- @default_options = {
- :run => true,
- :port => 4567,
- :env => :development,
- :root => root,
- :views => root + '/views',
- :public => root + '/public',
- :sessions => false,
- :logging => true,
- :app_file => $0,
- :raise_errors => false
- }
- load_default_options_from_command_line!
- @default_options
- end
-
- # Search ARGV for command line arguments and update the
- # Sinatra::default_options Hash accordingly. This method is
- # invoked the first time the default_options Hash is accessed.
- # NOTE: Ignores --name so unit/spec tests can run individually
- def self.load_default_options_from_command_line! #:nodoc:
- require 'optparse'
- OptionParser.new do |op|
- op.on('-p port') { |port| default_options[:port] = port }
- op.on('-e env') { |env| default_options[:env] = env }
- op.on('-x') { default_options[:mutex] = true }
- op.on('-s server') { |server| default_options[:server] = server }
- end.parse!(ARGV.dup.select { |o| o !~ /--name/ })
- end
-
- # Determine whether the application is in the process of being
- # reloaded.
- def reloading?
- @reloading == true
- end
-
- # Yield to the block for configuration if the current environment
- # matches any included in the +envs+ list. Always yield to the block
- # when no environment is specified.
- #
- # NOTE: configuration blocks are not executed during reloads.
- def configures(*envs, &b)
- return if reloading?
- yield self if envs.empty? || envs.include?(options.env)
- end
-
- alias :configure :configures
-
- # When both +option+ and +value+ arguments are provided, set the option
- # specified. With a single Hash argument, set all options specified in
- # Hash. Options are available via the Application#options object.
- #
- # Setting individual options:
- # set :port, 80
- # set :env, :production
- # set :views, '/path/to/views'
- #
- # Setting multiple options:
- # set :port => 80,
- # :env => :production,
- # :views => '/path/to/views'
- #
- def set(option, value=self)
- if value == self && option.kind_of?(Hash)
- option.each { |key,val| set(key, val) }
- else
- options.send("#{option}=", value)
- end
- end
-
- alias :set_option :set
- alias :set_options :set
-
- # Enable the options specified by setting their values to true. For
- # example, to enable sessions and logging:
- # enable :sessions, :logging
- def enable(*opts)
- opts.each { |key| set(key, true) }
- end
-
- # Disable the options specified by setting their values to false. For
- # example, to disable logging and automatic run:
- # disable :logging, :run
- def disable(*opts)
- opts.each { |key| set(key, false) }
- end
-
- # Define an event handler for the given request method and path
- # spec. The block is executed when a request matches the method
- # and spec.
- #
- # NOTE: The #get, #post, #put, and #delete helper methods should
- # be used to define events when possible.
- def event(method, path, options = {}, &b)
- events[method].push(Event.new(path, options, &b)).last
- end
-
- # Define an event handler for GET requests.
- def get(path, options={}, &b)
- event(:get, path, options, &b)
- end
-
- # Define an event handler for POST requests.
- def post(path, options={}, &b)
- event(:post, path, options, &b)
- end
-
- # Define an event handler for HEAD requests.
- def head(path, options={}, &b)
- event(:head, path, options, &b)
- end
-
- # Define an event handler for PUT requests.
- #
- # NOTE: PUT events are triggered when the HTTP request method is
- # PUT and also when the request method is POST and the body includes a
- # "_method" parameter set to "PUT".
- def put(path, options={}, &b)
- event(:put, path, options, &b)
- end
-
- # Define an event handler for DELETE requests.
- #
- # NOTE: DELETE events are triggered when the HTTP request method is
- # DELETE and also when the request method is POST and the body includes a
- # "_method" parameter set to "DELETE".
- def delete(path, options={}, &b)
- event(:delete, path, options, &b)
- end
-
- # Visits and invokes each handler registered for the +request_method+ in
- # definition order until a Result response is produced. If no handler
- # responds with a Result, the NotFound error handler is invoked.
- #
- # When the request_method is "HEAD" and no valid Result is produced by
- # the set of handlers registered for HEAD requests, an attempt is made to
- # invoke the GET handlers to generate the response before resorting to the
- # default error handler.
- def lookup(request)
- method = request.request_method.downcase.to_sym
- events[method].eject(&[:invoke, request]) ||
- (events[:get].eject(&[:invoke, request]) if method == :head) ||
- errors[NotFound].invoke(request)
- end
-
-
- # Define a named template. The template may be referenced from
- # event handlers by passing the name as a Symbol to rendering
- # methods. The block is executed each time the template is rendered
- # and the resulting object is passed to the template handler.
- #
- # The following example defines a HAML template named hello and
- # invokes it from an event handler:
- #
- # template :hello do
- # "h1 Hello World!"
- # end
- #
- # get '/' do
- # haml :hello
- # end
- #
- def template(name, &b)
- templates[name] = b
- end
-
- # Define a layout template.
- def layout(name=:layout, &b)
- template(name, &b)
- end
-
- # Define a custom error handler for the exception class +type+. The block
- # is invoked when the specified exception type is raised from an error
- # handler and can manipulate the response as needed:
- #
- # error MyCustomError do
- # status 500
- # 'So what happened was...' + request.env['sinatra.error'].message
- # end
- #
- # The Sinatra::ServerError handler is used by default when an exception
- # occurs and no matching error handler is found.
- def error(type=ServerError, options = {}, &b)
- errors[type] = Error.new(type, &b)
- end
-
- # Define a custom error handler for '404 Not Found' responses. This is a
- # shorthand for:
- # error NotFound do
- # ..
- # end
- def not_found(options={}, &b)
- error NotFound, options, &b
- end
-
- # Define a request filter. When <tt>type</tt> is <tt>:before</tt>, execute the
- # block in the context of each request before matching event handlers.
- def filter(type, &b)
- filters[type] << b
- end
-
- # Invoke the block in the context of each request before invoking
- # matching event handlers.
- def before(&b)
- filter :before, &b
- end
-
- def development?
- options.env == :development
- end
-
- # Clear all events, templates, filters, and error handlers
- # and then reload the application source file. This occurs
- # automatically before each request is processed in development.
- def reload!
- clearables.each(&:clear)
- load_default_configuration!
- @pipeline = nil
- @reloading = true
- Kernel.load Sinatra.options.app_file
- @reloading = false
- end
-
- # Determine whether the application is in the process of being
- # reloaded.
- def reloading?
- @reloading == true
- end
-
- # Mutex instance used for thread synchronization.
- def mutex
- @@mutex ||= Mutex.new
- end
-
- # Yield to the block with thread synchronization
- def run_safely
- if options.mutex
- mutex.synchronize { yield }
- else
- yield
- end
- end
-
- # Add a piece of Rack middleware to the pipeline leading to the
- # application.
- def use(klass, *args, &block)
- fail "#{klass} must respond to 'new'" unless klass.respond_to?(:new)
- @pipeline = nil
- @middleware.push([ klass, args, block ]).last
- end
-
- private
-
- # Rack middleware derived from current state of application options.
- # These components are plumbed in at the very beginning of the
- # pipeline.
- def optional_middleware
- [
- ([ Rack::CommonLogger, [], nil ] if options.logging),
- ([ Rack::Session::Cookie, [], nil ] if options.sessions)
- ].compact
- end
-
- # Rack middleware explicitly added to the application with #use. These
- # components are plumbed into the pipeline downstream from
- # #optional_middle.
- def explicit_middleware
- @middleware
- end
-
- # All Rack middleware used to construct the pipeline.
- def middleware
- optional_middleware + explicit_middleware
- end
-
- public
-
- # An assembled pipeline of Rack middleware that leads eventually to
- # the Application#invoke method. The pipeline is built upon first
- # access. Defining new middleware with Application#use or manipulating
- # application options may cause the pipeline to be rebuilt.
- def pipeline
- @pipeline ||=
- middleware.inject(method(:dispatch)) do |app,(klass,args,block)|
- klass.new(app, *args, &block)
- end
- end
-
- # Rack compatible request invocation interface.
- def call(env)
- reload! if development?
- pipeline.call(env)
- end
-
- # Request invocation handler - called at the end of the Rack pipeline
- # for each request.
- #
- # 1. Create Rack::Request, Rack::Response helper objects.
- # 2. Lookup event handler based on request method and path.
- # 3. Create new EventContext to house event handler evaluation.
- # 4. Invoke each #before filter in context of EventContext object.
- # 5. Invoke event handler in context of EventContext object.
- # 6. Return response to Rack.
- #
- # See the Rack specification for detailed information on the
- # +env+ argument and return value.
- def dispatch(env)
- request = Rack::Request.new(env)
- result = lookup(request)
- context = EventContext.new(request, Rack::Response.new, result.params)
- context.status(result.status)
- begin
- returned = run_safely do
- catch(:halt) do
- filters[:before].each { |f| context.instance_eval(&f) }
- [:complete, context.instance_eval(&result.block)]
- end
- end
- body = returned.to_result(context)
- rescue => e
- request.env['sinatra.error'] = e
- context.status(500)
- result = (errors[e.class] || errors[ServerError]).invoke(request)
- returned = run_safely do
- catch(:halt) do
- [:complete, context.instance_eval(&result.block)]
- end
- end
- body = returned.to_result(context)
- end
- body = '' unless body.respond_to?(:each)
- body = '' if request.request_method.upcase == 'HEAD'
- context.body = body.kind_of?(String) ? [*body] : body
- context.finish
- end
-
- # Called immediately after the application is initialized or reloaded to
- # register default events, templates, and error handlers.
- def load_default_configuration!
-
- # The static event is always executed first.
- events[:get] << Static.new
-
- # Default configuration for all environments.
- configure do
- error do
- raise request.env['sinatra.error'] if Sinatra.options.raise_errors
- '<h1>Internal Server Error</h1>'
- end
- not_found { '<h1>Not Found</h1>'}
- end
-
- configures :development do
-
- get '/sinatra_custom_images/:image.png' do
- File.read(File.dirname(__FILE__) + "/../images/#{params[:image]}.png")
- end
-
- not_found do
- %Q(
- <style>
- body {
- text-align: center;
- color: #888;
- font-family: Arial;
- font-size: 22px;
- margin: 20px;
- }
- #content {
- margin: 0 auto;
- width: 500px;
- text-align: left;
- }
- </style>
- <html>
- <body>
- <h2>Sinatra doesn't know this diddy.</h2>
- <img src='/sinatra_custom_images/404.png'></img>
- <div id="content">
- Try this:
-<pre>#{request.request_method.downcase} "#{request.path_info}" do
- .. do something ..
-end<pre>
- </div>
- </body>
- </html>
- )
- end
-
- error do
- @error = request.env['sinatra.error']
- %Q(
- <html>
- <body>
- <style type="text/css" media="screen">
- body {
- font-family: Verdana;
- color: #333;
- }
-
- #content {
- width: 700px;
- margin-left: 20px;
- }
-
- #content h1 {
- width: 99%;
- color: #1D6B8D;
- font-weight: bold;
- }
-
- #stacktrace {
- margin-top: -20px;
- }
-
- #stacktrace pre {
- font-size: 12px;
- border-left: 2px solid #ddd;
- padding-left: 10px;
- }
-
- #stacktrace img {
- margin-top: 10px;
- }
- </style>
- <div id="content">
- <img src="/sinatra_custom_images/500.png" />
- <div class="info">
- Params: <pre>#{params.inspect}
- </div>
- <div id="stacktrace">
- <h1>#{Rack::Utils.escape_html(@error.class.name + ' - ' + @error.message)}</h1>
- <pre><code>#{Rack::Utils.escape_html(@error.backtrace.join("\n"))}</code></pre>
- </div>
- </body>
- </html>
- )
- end
- end
- end
-
- private :load_default_configuration!
-
- end
-
-end
-
-# Delegate DSLish methods to the currently active Sinatra::Application
-# instance.
-Sinatra::Application::FORWARD_METHODS.each do |method|
- eval(<<-EOS, binding, '(__DSL__)', 1)
- def #{method}(*args, &b)
- Sinatra.application.#{method}(*args, &b)
- end
- EOS
-end
-
-def helpers(&b)
- Sinatra::EventContext.class_eval(&b)
-end
-
-def use_in_file_templates!
- require 'stringio'
- templates = IO.read(caller.first.split(':').first).split('__FILE__').last
- data = StringIO.new(templates)
- current_template = nil
- data.each do |line|
- if line =~ /^@@\s?(.*)/
- current_template = $1.to_sym
- Sinatra.application.templates[current_template] = ''
- elsif current_template
- Sinatra.application.templates[current_template] << line
- end
- end
-end
-
-def mime(ext, type)
- Rack::File::MIME_TYPES[ext.to_s] = type
-end
-
-### Misc Core Extensions
-
-module Kernel
-
- def silence_warnings
- old_verbose, $VERBOSE = $VERBOSE, nil
- yield
- ensure
- $VERBOSE = old_verbose
- end
-
-end
-
-class String
-
- # Converts +self+ to an escaped URI parameter value
- # 'Foo Bar'.to_param # => 'Foo%20Bar'
- def to_param
- Rack::Utils.escape(self)
- end
- alias :http_escape :to_param
-
- # Converts +self+ from an escaped URI parameter value
- # 'Foo%20Bar'.from_param # => 'Foo Bar'
- def from_param
- Rack::Utils.unescape(self)
- end
- alias :http_unescape :from_param
-
-end
-
-class Hash
-
- def to_params
- map { |k,v| "#{k}=#{URI.escape(v)}" }.join('&')
- end
-
- def symbolize_keys
- self.inject({}) { |h,(k,v)| h[k.to_sym] = v; h }
- end
-
- def pass(*keys)
- reject { |k,v| !keys.include?(k) }
- end
-
-end
-
-class Symbol
-
- def to_proc
- Proc.new { |*args| args.shift.__send__(self, *args) }
- end
-
-end
-
-class Array
-
- def to_hash
- self.inject({}) { |h, (k, v)| h[k] = v; h }
- end
-
- def to_proc
- Proc.new { |*args| args.shift.__send__(self[0], *(args + self[1..-1])) }
- end
-
-end
-
-module Enumerable
-
- def eject(&block)
- find { |e| result = block[e] and break result }
- end
-
-end
-
-### Core Extension results for throw :halt
-
-class Proc
- def to_result(cx, *args)
- cx.instance_eval(&self)
- args.shift.to_result(cx, *args)
- end
-end
-
-class String
- def to_result(cx, *args)
- args.shift.to_result(cx, *args)
- self
- end
-end
-
-class Array
- def to_result(cx, *args)
- self.shift.to_result(cx, *self)
- end
-end
-
-class Symbol
- def to_result(cx, *args)
- cx.send(self, *args)
- end
-end
-
-class Fixnum
- def to_result(cx, *args)
- cx.status self
- args.shift.to_result(cx, *args)
- end
-end
-
-class NilClass
- def to_result(cx, *args)
- ''
- end
-end
-
-at_exit do
- raise $! if $!
- if Sinatra.application.options.run
- Sinatra.run
- end
-end
-
-mime :xml, 'application/xml'
-mime :js, 'application/javascript'
View
53 vendor/sinatra/lib/sinatra/test/methods.rb
@@ -1,53 +0,0 @@
-module Sinatra
-
- module Test
-
- module Methods
-
- def easy_env_map
- {
- :accept => "HTTP_ACCEPT",
- :agent => "HTTP_USER_AGENT",
- :host => "HTTP_HOST",
- :session => "HTTP_COOKIE",
- :cookies => "HTTP_COOKIE"
- }
- end
-
- def session(data, key = 'rack.session')
- data = data.from_params if data.respond_to?(:from_params)
- "#{Rack::Utils.escape(key)}=#{[Marshal.dump(data)].pack("m*")}"
- end
-
- def map_easys(params)
- easy_env_map.inject(params.dup) do |m, (from, to)|
- m[to] = m.delete(from) if m.has_key?(from); m
- end
- end
-
- %w(get head post put delete).each do |m|
- define_method("#{m}_it") do |path, *args|
- env, input = if args.size == 2
- [args.last, args.first]
- elsif args.size == 1
- data = args.first
- data.is_a?(Hash) ? [map_easys(data.delete(:env) || {}), data.to_params] : [nil, data]
- end
- @request = Rack::MockRequest.new(Sinatra.build_application)
- @response = @request.request(m.upcase, path, {:input => input}.merge(env || {}))
- end
- end
-
- def follow!
- get_it(@response.location)
- end
-
- def method_missing(name, *args)
- @response.send(name, *args) rescue super
- end
-
- end
-
- end
-
-end
View
10 vendor/sinatra/lib/sinatra/test/spec.rb
@@ -1,10 +0,0 @@
-require File.dirname(__FILE__) + '/unit'
-require 'test/spec'
-
-class Test::Unit::TestCase
-
- def should
- @response.should
- end
-
-end
View
13 vendor/sinatra/lib/sinatra/test/unit.rb
@@ -1,13 +0,0 @@
-require 'test/unit'
-require File.dirname(__FILE__) + '/methods'
-
-Test::Unit::TestCase.send(:include, Sinatra::Test::Methods)
-
-Sinatra::Application.default_options.merge!(
- :env => :test,
- :run => false,
- :raise_errors => true,
- :logging => false
-)
-
-Sinatra.application = nil
View
287 vendor/sinatra/test/app_test.rb
@@ -1,287 +0,0 @@
-require File.dirname(__FILE__) + '/helper'
-
-context "Sinatra" do
-
- setup do
- Sinatra.application = nil
- end
-
- specify "should put all DSL methods on (main)" do
- object = Object.new
- Sinatra::Application::FORWARD_METHODS.each do |method|
- object.private_methods.should.include(method)
- end
- end
-
- specify "should handle result of nil" do
- get '/' do
- nil
- end
-
- get_it '/'
- should.be.ok
- body.should == ''
- end
-
- specify "handles events" do
- get '/:name' do
- 'Hello ' + params["name"]
- end
-
- get_it '/Blake'
-
- should.be.ok
- body.should.equal 'Hello Blake'
- end
-
-
- specify "handles splats" do
- get '/hi/*' do
- params["splat"].kind_of?(Array).should.equal true
- params["splat"].first
- end
-
- get_it '/hi/Blake'
-
- should.be.ok
- body.should.equal 'Blake'
- end
-
- specify "handles multiple splats" do
- get '/say/*/to/*' do
- params["splat"].join(' ')
- end
-
- get_it '/say/hello/to/world'
-
- should.be.ok
- body.should.equal 'hello world'
- end
-
- specify "allow empty splats" do
- get '/say/*/to*/*' do
- params["splat"].join(' ')
- end
-
- get_it '/say/hello/to/world'
-
- should.be.ok
- body.should.equal 'hello world' # second splat is empty
-
- get_it '/say/hello/tomy/world'
-
- should.be.ok
- body.should.equal 'hello my world'
- end
-
- specify "gives access to underlying response header Hash" do
- get '/' do
- header['X-Test'] = 'Is this thing on?'
- headers 'X-Test2' => 'Foo', 'X-Test3' => 'Bar'
- ''
- end
-
- get_it '/'
- should.be.ok
- headers.should.include 'X-Test'
- headers['X-Test'].should.equal 'Is this thing on?'
- headers.should.include 'X-Test3'
- headers['X-Test3'].should.equal 'Bar'
- end
-
- specify "follows redirects" do
- get '/' do
- redirect '/blake'
- end
-
- get '/blake' do
- 'Mizerany'
- end
-
- get_it '/'
- should.be.redirection
- body.should.equal ''
-
- follow!
- should.be.ok
- body.should.equal 'Mizerany'
- end
-
- specify "renders a body with a redirect&q