1
0
Fork 0
mirror of https://github.com/sinatra/sinatra synced 2023-03-27 23:18:01 -04:00
This commit is contained in:
Blake Mizerany 2007-10-04 15:40:12 -07:00
parent 756b8e5de9
commit 85e1a4c944
15 changed files with 383 additions and 104 deletions

22
LICENSE Normal file
View file

@ -0,0 +1,22 @@
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 file

@ -1,7 +1,4 @@
CHANGELOG CHANGELOG
RakeFile
README
x.rb
examples/hello/hello.rb examples/hello/hello.rb
examples/hello/views/hello.erb examples/hello/views/hello.erb
examples/todo/todo.rb examples/todo/todo.rb
@ -9,8 +6,15 @@ files/default_index.erb
files/error.erb files/error.erb
files/logo.png files/logo.png
files/not_found.erb files/not_found.erb
lib/sinatra.rb lib/sinatra/context/renderer.rb
lib/sinatra/context.rb lib/sinatra/context.rb
lib/sinatra/core_ext/array.rb
lib/sinatra/core_ext/class.rb
lib/sinatra/core_ext/hash.rb
lib/sinatra/core_ext/kernel.rb
lib/sinatra/core_ext/metaid.rb
lib/sinatra/core_ext/module.rb
lib/sinatra/core_ext/symbol.rb
lib/sinatra/dispatcher.rb lib/sinatra/dispatcher.rb
lib/sinatra/dsl.rb lib/sinatra/dsl.rb
lib/sinatra/environment.rb lib/sinatra/environment.rb
@ -19,29 +23,28 @@ lib/sinatra/irb.rb
lib/sinatra/loader.rb lib/sinatra/loader.rb
lib/sinatra/logger.rb lib/sinatra/logger.rb
lib/sinatra/options.rb lib/sinatra/options.rb
lib/sinatra/pretty_url.rb lib/sinatra/rack_ext/request.rb
lib/sinatra/route.rb lib/sinatra/route.rb
lib/sinatra/server.rb lib/sinatra/server.rb
lib/sinatra/sessions.rb
lib/sinatra/test_methods.rb lib/sinatra/test_methods.rb
lib/sinatra/context/renderer.rb lib/sinatra.rb
lib/sinatra/core_ext/array.rb LICENSE
lib/sinatra/core_ext/class.rb RakeFile
lib/sinatra/core_ext/hash.rb README
lib/sinatra/core_ext/kernel.rb sinatra.gemspec
lib/sinatra/core_ext/metaid.rb site/index.htm
lib/sinatra/core_ext/module.rb
lib/sinatra/core_ext/symbol.rb
site/index.html site/index.html
site/logo.png site/logo.png
test/helper.rb test/helper.rb
test/sinatra/dispatcher_test.rb test/sinatra/dispatcher_test.rb
test/sinatra/event_test.rb test/sinatra/event_test.rb
test/sinatra/pretty_url_test.rb
test/sinatra/renderer_test.rb test/sinatra/renderer_test.rb
test/sinatra/request_test.rb
test/sinatra/route_test.rb test/sinatra/route_test.rb
test/sinatra/static_files/foo.txt
test/sinatra/static_files_test.rb test/sinatra/static_files_test.rb
test/sinatra/url_test.rb test/sinatra/url_test.rb
test/sinatra/static_files/foo.txt
vendor/erb/init.rb vendor/erb/init.rb
vendor/erb/lib/erb.rb vendor/erb/lib/erb.rb
vendor/haml/init.rb vendor/haml/init.rb

74
README
View file

@ -1,43 +1,57 @@
Copyright (c) <year> <copyright holders> Sinatra (C) 2007 By Blake Mizerany
Permission is hereby granted, free of charge, to any person = Classy web-development dressed in a DSL
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 == Install!
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, sudo gem install sinatra -y
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.
== Use!
Using Sinatra is EASY! I'm going to move quick. I'll let you drool at your own pace.
WARNING: Keep a fresh pair of underwear nearby. You may need them when you see this. - Create a file called lyrics.rb (or any name you like)
Get running without a file! - Add
require 'rubygems'
require 'sinatra'
ruby -e 'require "sinatra"' # HIT http://localhost:4567/ and BAM! - Run (yes, with just ruby)
% ruby lyrics.rb
== Sinata has taken the stage on port 4567!
now create a file called test.rb (or whatever you fancy) and enter this: - Take a moment and view the default page http://localhost:4567. Go ahead and bask in it's glory.
require 'sinatra' * Notice:
* It didn't create any page to show you that default page (just a cool thing to see, that's all)
* There was nothing generated other than a log file
* Sinatra is a really cool name for a web-framework that's a DSL
get '/' do - Modify lyrics.rb by adding:
body 'Hello World!'
end
now run 'ruby test.rb' and refresh your browser get '/' do
'Hello World'
end
- Refresh (no need to restart Sinatra):
Oh yeah! http://localhost:4567
- Modify again (then refresh):
get '/' do
<<-HTML
<form action='/' method="POST">
<input type="text" name="name" />
<input type="submit" value="Say my name!" />
</form>
HTML
end
post '/' do
"Hello #{params[:name] || 'World'}!"
end
- Homework:
Use the Sinatra::Erb::EventContext or Sinatra::Haml::EventContext to do the same. Do them inline and as template files.

View file

@ -20,6 +20,7 @@ begin
p.email = "blake.mizerany@gmail.com" p.email = "blake.mizerany@gmail.com"
p.test_pattern = 'test/**/*_test.rb' p.test_pattern = 'test/**/*_test.rb'
p.include_rakefile = true p.include_rakefile = true
p.rdoc_pattern = ['README', 'LICENSE'] << Dir.glob('lib/**/*.rb') << Dir.glob('vendor/**/*.rb')
end end
rescue LoadError rescue LoadError

View file

@ -9,22 +9,33 @@ module Sinatra
include Sinatra::Renderer include Sinatra::Renderer
def initialize(request) def initialize(request) #:nodoc:
@request = request @request = request
@headers = {} @headers = {}
end end
# Sets or returns the status
def status(value = nil) def status(value = nil)
@status = value if value @status = value if value
@status || 200 @status || 200
end end
# Sets or returns the body
# *Usage*
# body 'test'
# or
# body do
# 'test'
# end
# both are the same
#
def body(value = nil, &block) def body(value = nil, &block)
@body = value if value @body = value if value
@body = block.call if block @body = block.call if block
@body @body
end end
# Renders an exception to +body+ and sets status to 500
def error(value = nil) def error(value = nil)
if value if value
status 500 status 500
@ -34,12 +45,14 @@ module Sinatra
@error @error
end end
# This allows for: # Sets or returns response headers
# header 'Content-Type' => 'text/html' #
# header 'Foo' => 'Bar' # *Usage*
# header 'Content-Type' => 'text/html'
# header 'Foo' => 'Bar'
# or # or
# headers 'Content-Type' => 'text/html', # headers 'Content-Type' => 'text/html',
# 'Foo' => 'Bar' # 'Foo' => 'Bar'
# #
# Whatever blows your hair back # Whatever blows your hair back
def headers(value = nil) def headers(value = nil)
@ -48,21 +61,24 @@ module Sinatra
end end
alias :header :headers alias :header :headers
# Returns a Hash of session data. Keys are symbolized
def session def session
request.env['rack.session'] request.env['rack.session']
end end
# Returns a Hash of params. Keys are symbolized
def params def params
@params ||= @request.params.symbolize_keys @params ||= @request.params.symbolize_keys
end end
# Redirect to a url
def redirect(path) def redirect(path)
logger.info "Redirecting to: #{path}" logger.info "Redirecting to: #{path}"
status 302 status 302
header 'Location' => path header 'Location' => path
end end
def log_event def log_event #:nodoc:
logger.info "#{request.request_method} #{request.path_info} | Status: #{status} | Params: #{params.inspect}" logger.info "#{request.request_method} #{request.path_info} | Status: #{status} | Params: #{params.inspect}"
logger.exception(error) if error logger.exception(error) if error
end end

View file

@ -1,7 +1,12 @@
Layouts = Hash.new Layouts = Hash.new # :nodoc:
module Sinatra module Sinatra
# The magic or rendering happens here. This is included in Sinatra::EventContext on load.
#
# These methods are the foundation for Sinatra::Erb and Sinatra::Haml and allow you to quickly
# create custom wrappers for your favorite rendering engines outside of erb and haml.
module Renderer module Renderer
DEFAULT_OPTIONS = { DEFAULT_OPTIONS = {
@ -9,6 +14,38 @@ module Sinatra
:layout => :layout :layout => :layout
} }
# Renders templates from a string or file and handles their layouts:
#
# Example:
# module MyRenderer
# def my(template, options, &layout)
# render(template, :my, options, &layout)
# end
#
# def render_my(template)
# template.capitalize # It capitalizes templates!!!!! WOW!
# end
# end
# Sinatra::EventContext.send :include, MyRenderer
#
# get '/' do
# my "something"
# end
#
# get_it '/' # => 'Something'
#
# The second method is named render_extname. render will call this dynamicly
#
# paramaters:
# * +template+ If String, renders the string. If Symbol, reads from file with the basename of the Symbol; uses +renderer+ for extension.
# * +renderer+ A symbol defining the render_ method to call and the extension append to +template+ when looking in the +views_directory+
# * +options+ An optional Hash of options (see next section)
#
# options:
# * +:views_directory+ Allows you to override the default 'views' directory an look for the template in another
# * +:layout+ Which layout to use (see Sinatra::Dsl). false to force a render with no layout. Defaults to :default layout
#
def render(template, renderer, options = {}) def render(template, renderer, options = {})
options = DEFAULT_OPTIONS.merge(options) options = DEFAULT_OPTIONS.merge(options)

View file

@ -1,3 +1,6 @@
# Compliments to why for this:
# http://whytheluckystiff.net/articles/seeingMetaclassesClearly.html
class Object class Object
# The hidden singleton lurks behind everyone # The hidden singleton lurks behind everyone
def metaclass; class << self; self; end; end def metaclass; class << self; self; end; end

View file

@ -1,42 +1,163 @@
module Kernel
%w( get post put delete ).each do |verb| module Sinatra
eval <<-end_eval
def #{verb}(path, &block)
Sinatra::Event.new(:#{verb}, path, &block)
end
end_eval
end
def after_attend(filter_name = nil, &block)
Sinatra::Event.after_attend(filter_name, &block)
end
def helpers(&block)
Sinatra::EventContext.class_eval(&block)
end
def static(path, root) module Dsl
Sinatra::StaticEvent.new(path, root)
end # Define an Event that responds to a +path+ on GET method
#
%w(test development production).each do |env| # The +path+ can be a template (i.e. '/:foo/bar/:baz'). When recognized, it will add <tt>:foo</tt> and <tt>:baz</tt> to +params+ with their values.
module_eval <<-end_eval #
def #{env} # Example:
yield if Sinatra::Options.environment == :#{env} # # Going RESTful
end #
end_eval # get '/' do
end # .. show stuff ..
# end
def layout(name = :layout, options = {}) #
Layouts[name] = unless block_given? # post '/' do
File.read("%s/%s" % [options[:views_directory] || 'views', name]) # .. add stuff ..
else # redirect '/'
yield # end
#
# put '/:id' do
# .. update params[:id] ..
# redirect '/'
# end
#
# delete '/:id' do
# .. delete params[:id] ..
# redirect '/'
# end
#
# BIG NOTE: PUT and DELETE are trigged when POSTing to their paths with a <tt>_method</tt> param whose value is PUT or DELETE
#
def get(path, &block)
Sinatra::Event.new(:get, path, &block)
end
# Same as get but responds to POST
def post(path, &block)
Sinatra::Event.new(:post, path, &block)
end
# Same as get but responds to PUT
#
# BIG NOTE: PUT and DELETE are trigged when POSTing to their paths with a <tt>_method</tt> param whose value is PUT or DELETE
def put(path, &block)
Sinatra::Event.new(:put, path, &block)
end
# Same as get but responds to DELETE
#
# BIG NOTE: PUT and DELETE are trigged when POSTing to their paths with a <tt>_method</tt> param whose value is PUT or DELETE
def delete(path, &block)
Sinatra::Event.new(:delete, path, &block)
end
# Run given block after each Event's execution
# Example:
# after_attend do
# logger.debug "After event attend!"
# end
def after_attend(filter_name = nil, &block)
Sinatra::Event.after_attend(filter_name, &block)
end end
end
def sessions(on_off) # Add methods to each event for use during execution
Sinatra::Session::Cookie.use = on_off #
# Example:
# helpers do
# def foo
# 'foo!'
# end
# end
#
# get '/bar' do
# foo
# end
#
# get_it '/bar' # => 'foo!'
#
def helpers(&block)
Sinatra::EventContext.class_eval(&block)
end
# Maps a path to a physical directory containing static files
#
# Example:
# static '/p', 'public'
#
def static(path, root)
Sinatra::StaticEvent.new(path, root)
end
# Execute block if in development mode (Used for configuration)
def development
yield if Sinatra::Options.environment == :development
end
# Execute block if in production mode (Used for configuration)
def production
yield if Sinatra::Options.environment == :production
end
# Execute block if in test mode (Used for configuration)
def test
yield if Sinatra::Options.environment == :test
end
# Define named layouts (default name is <tt>:layout</tt>)
#
# Examples:
# # Default layout in Erb
# layout do
# '-- <%= yield %> --'
# end
#
# # Named layout in Haml
# layout :for_haml do
# '== XXXX #{yield} XXXX'
# end
#
# # Loads layout named <tt>:"foo.erb"</tt> from file (default behaviour if block is omitted)
# layout 'foo.erb' # looks for foo.erb. This is odd an is being re-thought
#
# def layout(name = :layout, options = {})
# Layouts[name] = unless block_given?
# File.read("%s/%s" % [options[:views_directory] || 'views', name])
# else
# yield
# end
# end
#
# Cool trick:
#
# # Send a one-time layout to renderer method
# get '/cooltrick' do
# erb 'wicked' do
# 'Cool <%= yield %> Trick'
# end
# end
#
# get_it '/cooltrick' # => 'Cool wicked Trick'
#
def layout(name = :layout, options = {})
Layouts[name] = unless block_given?
File.read("%s/%s" % [options[:views_directory] || 'views', name])
else
yield
end
end
# Turn sessions <tt>:on</tt> or <tt>:off</tt>
#
# NOTE: There is currently no way to turn it on or off per Event... patches anyone?)
def sessions(on_off)
Sinatra::Session::Cookie.use = on_off
end
end end
end end
include Sinatra::Dsl

View file

@ -1,6 +1,6 @@
module Sinatra module Sinatra
module EventManager module EventManager # :nodoc:
extend self extend self
def reset! def reset!
@ -39,7 +39,7 @@ module Sinatra
end end
class Event class Event # :nodoc:
cattr_accessor :logger cattr_accessor :logger
cattr_accessor :after_filters cattr_accessor :after_filters
@ -88,7 +88,7 @@ module Sinatra
end end
class StaticEvent < Event class StaticEvent < Event # :nodoc:
def initialize(path, root, register = true) def initialize(path, root, register = true)
@root = root @root = root

View file

@ -1,17 +1,24 @@
module Sinatra module Sinatra
# Sinatra Irb is entered via <tt>ruby myapp.rb -c</tt> (replace myapp.rb with your app filename)
#
# Be sure to also check out Sinatra::TestMethods for more cool stuff when your in Irb
#
module Irb module Irb
extend self extend self
# taken from merb # taken from merb
def start! def start! #:nodoc:
Object.send(:include, TestMethods) # added to allow post_to in console Object.send(:include, TestMethods) # added to allow post_to in console
Object.class_eval do Object.class_eval do
# Reload all Sinatra and App specific files
def reload! def reload!
Loader.reload! Loader.reload!
end end
# Show the +body+ with result info in your text editor!!! Great Job!
def show!(editor = nil) def show!(editor = nil)
editor = editor || ENV['EDITOR'] editor = editor || ENV['EDITOR']
IO.popen(editor, 'w') do |f| IO.popen(editor, 'w') do |f|
@ -24,7 +31,7 @@ module Sinatra
end end
alias :mate :show! alias :mate :show!
def result_info def result_info #:nodoc:
info = <<-end_info info = <<-end_info
# Status: #{status} # Status: #{status}
# Headers: #{headers.inspect} # Headers: #{headers.inspect}

View file

@ -1,6 +1,6 @@
module Rack module Rack #:nodoc:
class Request class Request #:nodoc:
def request_method def request_method
if @env['REQUEST_METHOD'] == 'POST' && %w(PUT DELETE).include?(params['_method']) if @env['REQUEST_METHOD'] == 'POST' && %w(PUT DELETE).include?(params['_method'])

View file

@ -1,9 +1,20 @@
require 'uri' require 'uri'
module Sinatra module Sinatra
# These methods are for integration testing without an internet connection. They are available in Test::Unit::TestCase and when in Irb.
module TestMethods module TestMethods
# get_it, post_it, put_it, delete_it
# Executes the method and returns the result of the body
#
# options:
# +:params+ a hash of name parameters
#
# Example:
# get_it '/', :name => 'Blake' # => 'Hello Blake!'
#
%w(get post put delete).each do |verb| %w(get post put delete).each do |verb|
module_eval <<-end_eval module_eval <<-end_eval
def #{verb}_it(path, params = {}) def #{verb}_it(path, params = {})

View file

@ -1,6 +1,6 @@
require File.dirname(__FILE__) + '/../helper' require File.dirname(__FILE__) + '/../helper'
class Sinatra::EventContext class Sinatra::EventContext # :nodoc:
def render_foo(template) def render_foo(template)
require 'erb' require 'erb'

26
vendor/erb/lib/erb.rb vendored
View file

@ -1,14 +1,36 @@
module Sinatra module Sinatra
module Erb module Erb # :nodoc:
module EventContext module EventContext
# Renders raw erb in within the events context.
#
# This can be use to if you already have the template on hand and don't
# need a layout. This is speedier than using erb
#
def render_erb(content) def render_erb(content)
require 'erb' require 'erb'
body ERB.new(content).result(binding) body ERB.new(content).result(binding)
end end
# Renders erb within an event.
#
# Inline example:
#
# get '/foo' do
# erb 'The time is <%= Time.now %>'
# end
#
# Template example:
#
# get '/foo' do
# erb :foo #=> reads and renders view/foo.erb
# end
#
# For options, see Sinatra::Renderer
#
# See also: Sinatra::Renderer
def erb(template, options = {}, &layout) def erb(template, options = {}, &layout)
render(template, :erb, options, &layout) render(template, :erb, options, &layout)
end end

View file

@ -1,14 +1,36 @@
module Sinatra module Sinatra
module Haml module Haml # :nodoc:
module EventContext module EventContext
def render_haml(content) # Renders raw haml in within the events context.
#
# This can be use to if you already have the template on hand and don't
# need a layout. This is speedier than using haml
#
def render_haml(template)
require 'haml' require 'haml'
body ::Haml::Engine.new(content).render(self) body ::Haml::Engine.new(template).render(self)
end end
# Renders Haml within an event.
#
# Inline example:
#
# get '/foo' do
# haml '== The time is #{Time.now}'
# end
#
# Template example:
#
# get '/foo' do
# haml :foo #=> reads and renders view/foo.haml
# end
#
# For options, see Sinatra::Renderer
#
# See also: Sinatra::Renderer
def haml(template, options = {}, &layout) def haml(template, options = {}, &layout)
render(template, :haml, options, &layout) render(template, :haml, options, &layout)
end end