From 91b740afa8dff24b895cc35d59918de4fc60275f Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Sun, 30 May 2010 14:35:18 +0200 Subject: [PATCH 01/80] fixing issue with Tilt::VERSION check: '0.10' < '0.8' --- lib/sinatra/base.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/sinatra/base.rb b/lib/sinatra/base.rb index 59b622f37a..2e51da274c 100644 --- a/lib/sinatra/base.rb +++ b/lib/sinatra/base.rb @@ -8,7 +8,8 @@ # require tilt if available; fall back on bundled version. begin require 'tilt' - if Tilt::VERSION < '0.8' + major, minor = Tilt::VERSION.split('.').map { |e| e.to_i } + if major < 0 and minor < 8 warn "WARN: sinatra requires tilt >= 0.8; you have #{Tilt::VERSION}. " + "loading bundled version..." Object.send :remove_const, :Tilt From cf3c218a6c519688c1d44677030a4524fe588701 Mon Sep 17 00:00:00 2001 From: Pedro Menezes Date: Sun, 29 Aug 2010 21:56:44 -0300 Subject: [PATCH 02/80] Adding scss support through specific command --- README.rdoc | 26 ++++++++++++++++++ lib/sinatra/base.rb | 5 ++++ lib/sinatra/tilt.rb | 25 +++++++++++++++++ sinatra.gemspec | 3 +- test/scss_test.rb | 64 +++++++++++++++++++++++++++++++++++++++++++ test/views/hello.scss | 3 ++ 6 files changed, 125 insertions(+), 1 deletion(-) create mode 100644 test/scss_test.rb create mode 100644 test/views/hello.scss diff --git a/README.rdoc b/README.rdoc index 762c5a9442..44afa407d8 100644 --- a/README.rdoc +++ b/README.rdoc @@ -203,6 +203,32 @@ and overridden on an individual basis. sass :stylesheet, :style => :expanded # overridden end +=== Scss Templates + +The sass gem/library is required to render Scss templates: + + ## You'll need to require haml or sass in your app + require 'sass' + + get '/stylesheet.css' do + content_type 'text/css', :charset => 'utf-8' + scss :stylesheet + end + +Renders ./views/stylesheet.scss. + +{Scss' options}[http://sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html#options] +can be set globally through Sinatra's configurations, +see {Options and Configurations}[http://www.sinatrarb.com/configuration.html], +and overridden on an individual basis. + + set :scss, {:style => :compact } # default Scss style is :nested + + get '/stylesheet.css' do + content_type 'text/css', :charset => 'utf-8' + scss :stylesheet, :style => :expanded # overridden + end + === Less Templates The less gem/library is required to render Less templates: diff --git a/lib/sinatra/base.rb b/lib/sinatra/base.rb index d2d6654933..ea52212580 100644 --- a/lib/sinatra/base.rb +++ b/lib/sinatra/base.rb @@ -317,6 +317,11 @@ def sass(template, options={}, locals={}) render :sass, template, options, locals end + def scss(template, options={}, locals={}) + options[:layout] = false + render :scss, template, options, locals + end + def less(template, options={}, locals={}) options[:layout] = false render :less, template, options, locals diff --git a/lib/sinatra/tilt.rb b/lib/sinatra/tilt.rb index 58adbe297a..fe05e584ef 100644 --- a/lib/sinatra/tilt.rb +++ b/lib/sinatra/tilt.rb @@ -514,6 +514,31 @@ def sass_options register 'sass', SassTemplate + # Scss template implementation. + # + # Scss templates do not support object scopes, locals, or yield. + class ScssTemplate < Template + def initialize_engine + return if defined? ::Sass::Engine + require_template_library 'sass' + end + + def prepare + @engine = ::Sass::Engine.new(data, scss_options) + end + + def evaluate(scope, locals, &block) + @output ||= @engine.render + end + + private + def scss_options + options.merge(:filename => eval_file, :line => line, :syntax => :scss) + end + end + register 'scss', ScssTemplate + + # Lessscss template implementation. See: # http://lesscss.org/ # diff --git a/sinatra.gemspec b/sinatra.gemspec index 754b3db34a..6ee84a7101 100644 --- a/sinatra.gemspec +++ b/sinatra.gemspec @@ -4,7 +4,7 @@ Gem::Specification.new do |s| s.name = 'sinatra' s.version = '1.0' - s.date = '2010-03-23' + s.date = '2010-08-29' s.description = "Classy web-development dressed in a DSL" s.summary = "Classy web-development dressed in a DSL" @@ -17,6 +17,7 @@ Gem::Specification.new do |s| AUTHORS CHANGES LICENSE + README.de.rdoc README.jp.rdoc README.rdoc Rakefile diff --git a/test/scss_test.rb b/test/scss_test.rb new file mode 100644 index 0000000000..7fccb8f24d --- /dev/null +++ b/test/scss_test.rb @@ -0,0 +1,64 @@ +require File.dirname(__FILE__) + '/helper' + +begin +require 'sass' + +class ScssTest < Test::Unit::TestCase + def scss_app(&block) + mock_app { + set :views, File.dirname(__FILE__) + '/views' + get '/', &block + } + get '/' + end + + it 'renders inline Scss strings' do + scss_app { scss "#scss {\n background-color: white; }\n" } + assert ok? + assert_equal "#scss {\n background-color: white; }\n", body + end + + it 'renders .scss files in views path' do + scss_app { scss :hello } + assert ok? + assert_equal "#scss {\n background-color: white; }\n", body + end + + it 'ignores the layout option' do + scss_app { scss :hello, :layout => :layout2 } + assert ok? + assert_equal "#scss {\n background-color: white; }\n", body + end + + it "raises error if template not found" do + mock_app { + get('/') { scss :no_such_template } + } + assert_raise(Errno::ENOENT) { get('/') } + end + + it "passes scss options to the scss engine" do + scss_app { + scss "#scss {\n background-color: white;\n color: black\n}", + :style => :compact + } + assert ok? + assert_equal "#scss { background-color: white; color: black; }\n", body + end + + it "passes default scss options to the scss engine" do + mock_app { + set :scss, {:style => :compact} # default scss style is :nested + get '/' do + scss "#scss {\n background-color: white;\n color: black;\n}" + end + } + get '/' + assert ok? + assert_equal "#scss { background-color: white; color: black; }\n", body + end +end + +rescue + warn "#{$!.to_s}: skipping scss tests" +end diff --git a/test/views/hello.scss b/test/views/hello.scss new file mode 100644 index 0000000000..87d1200faf --- /dev/null +++ b/test/views/hello.scss @@ -0,0 +1,3 @@ +#scss { + background-color: white +} \ No newline at end of file From 9cf834af67965ea97261e8ed6f6d6ac79314bd34 Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Wed, 1 Sep 2010 11:00:50 +0200 Subject: [PATCH 03/80] Allow DateTime instances to be passed to last_modified. Makes sure the value is always a string, so Rack::Lint will not complain. Fixes GH #16 --- lib/sinatra/base.rb | 3 ++- test/helpers_test.rb | 63 ++++++++++++++++++++++++-------------------- 2 files changed, 36 insertions(+), 30 deletions(-) diff --git a/lib/sinatra/base.rb b/lib/sinatra/base.rb index ea52212580..1d7d32ff20 100644 --- a/lib/sinatra/base.rb +++ b/lib/sinatra/base.rb @@ -249,8 +249,9 @@ def expires(amount, *values) def last_modified(time) return unless time time = time.to_time if time.respond_to?(:to_time) + time = Time.parse time.strftime('%FT%T%:z') if time.respond_to?(:strftime) time = time.httpdate if time.respond_to?(:httpdate) - response['Last-Modified'] = time + response['Last-Modified'] = time.to_s halt 304 if time == request.env['HTTP_IF_MODIFIED_SINCE'] time end diff --git a/test/helpers_test.rb b/test/helpers_test.rb index 2671ec95da..1a096a71e1 100644 --- a/test/helpers_test.rb +++ b/test/helpers_test.rb @@ -1,4 +1,5 @@ require File.dirname(__FILE__) + '/helper' +require 'date' class HelpersTest < Test::Unit::TestCase def test_default @@ -423,42 +424,46 @@ def send_file_app(opts={}) end describe 'last_modified' do - setup do - now = Time.now - mock_app { - get '/' do - body { 'Hello World' } - last_modified now - 'Boo!' - end - } - @now = now - end + it 'ignores nil' do + mock_app do + get '/' do last_modified nil; 200; end + end - it 'sets the Last-Modified header to a valid RFC 2616 date value' do get '/' - assert_equal @now.httpdate, response['Last-Modified'] + assert ! response['Last-Modified'] end - it 'returns a body when conditional get misses' do - get '/' - assert_equal 200, status - assert_equal 'Boo!', body - end + [Time, DateTime].each do |klass| + describe "with #{klass.name}" do + setup do + now = klass.now + mock_app do + get '/' do + body { 'Hello World' } + last_modified now + 'Boo!' + end + end + @now = Time.parse now.to_s + end - it 'halts when a conditional GET matches' do - get '/', {}, { 'HTTP_IF_MODIFIED_SINCE' => @now.httpdate } - assert_equal 304, status - assert_equal '', body - end + it 'sets the Last-Modified header to a valid RFC 2616 date value' do + get '/' + assert_equal @now.httpdate, response['Last-Modified'] + end - it 'ignores nil' do - mock_app { - get '/' do last_modified nil; 200; end - } + it 'returns a body when conditional get misses' do + get '/' + assert_equal 200, status + assert_equal 'Boo!', body + end - get '/' - assert ! response['Last-Modified'] + it 'halts when a conditional GET matches' do + get '/', {}, { 'HTTP_IF_MODIFIED_SINCE' => @now.httpdate } + assert_equal 304, status + assert_equal '', body + end + end end end From 108162b343a18278694398ee4414c435ea28f560 Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Wed, 1 Sep 2010 11:18:16 +0200 Subject: [PATCH 04/80] Upgrade bundled Tilt, fixes GH #40 --- lib/sinatra/base.rb | 5 +- lib/sinatra/tilt.rb | 205 ++++++++++++++++++++++---------------------- 2 files changed, 105 insertions(+), 105 deletions(-) diff --git a/lib/sinatra/base.rb b/lib/sinatra/base.rb index 1d7d32ff20..22a48a3893 100644 --- a/lib/sinatra/base.rb +++ b/lib/sinatra/base.rb @@ -8,9 +8,8 @@ # require tilt if available; fall back on bundled version. begin require 'tilt' - major, minor = Tilt::VERSION.split('.').map { |e| e.to_i } - if major < 0 and minor < 8 - warn "WARN: sinatra requires tilt >= 0.8; you have #{Tilt::VERSION}. " + + if (Tilt::VERSION.split('.').map { |e| e.to_i } <=> [1,0,2]) < 0 + warn "WARN: sinatra requires tilt >= 1.0.2; you have #{Tilt::VERSION}. " + "loading bundled version..." Object.send :remove_const, :Tilt raise LoadError diff --git a/lib/sinatra/tilt.rb b/lib/sinatra/tilt.rb index fe05e584ef..c4668bfd6f 100644 --- a/lib/sinatra/tilt.rb +++ b/lib/sinatra/tilt.rb @@ -1,7 +1,7 @@ require 'digest/md5' module Tilt - VERSION = '0.8' + VERSION = '1.0.1' @template_mappings = {} @@ -16,6 +16,11 @@ def self.register(ext, template_class) mappings[ext.downcase] = template_class end + # Returns true when a template exists on an exact match of the provided file extension + def self.registered?(ext) + mappings.key?(ext.downcase) + end + # Create a new template for the given file using the file's extension # to determine the the template mapping. def self.new(file, line=nil, options={}, &block) @@ -29,20 +34,12 @@ def self.new(file, line=nil, options={}, &block) # Lookup a template class for the given filename or file # extension. Return nil when no implementation is found. def self.[](file) - if @template_mappings.key?(pattern = file.to_s.downcase) - @template_mappings[pattern] - elsif @template_mappings.key?(pattern = File.basename(pattern)) - @template_mappings[pattern] - else - while !pattern.empty? - if @template_mappings.key?(pattern) - return @template_mappings[pattern] - else - pattern = pattern.sub(/^[^.]*\.?/, '') - end - end - nil + pattern = file.to_s.downcase + unless registered?(pattern) + pattern = File.basename(pattern) + pattern.sub!(/^[^.]*\.?/, '') until (pattern.empty? || registered?(pattern)) end + @template_mappings[pattern] end # Mixin allowing template compilation on scope objects. @@ -62,7 +59,7 @@ def __tilt__ end # Base class for template implementations. Subclasses must implement - # the #prepare method and one of the #evaluate or #template_source + # the #prepare method and one of the #evaluate or #precompiled_template # methods. class Template # Template source; loaded from a file or given directly. @@ -100,7 +97,7 @@ def initialize(file=nil, line=1, options={}, &block) case when arg.respond_to?(:to_str) ; @file = arg.to_str when arg.respond_to?(:to_int) ; @line = arg.to_int - when arg.respond_to?(:to_hash) ; @options = arg.to_hash + when arg.respond_to?(:to_hash) ; @options = arg.to_hash.dup else raise TypeError end end @@ -285,13 +282,18 @@ def generate_compiled_method_name(locals_keys) def compile_template_method(method_name, locals) source, offset = precompiled(locals) - offset += 1 - CompileSite.module_eval <<-RUBY, eval_file, line - offset + offset += 5 + CompileSite.class_eval <<-RUBY, eval_file, line - offset def #{method_name}(locals) - #{source} + Thread.current[:tilt_vars] = [self, locals] + class << self + this, locals = Thread.current[:tilt_vars] + this.instance_eval do + #{source} + end + end end RUBY - ObjectSpace.define_finalizer self, Template.compiled_template_method_remover(CompileSite, method_name) end @@ -353,18 +355,29 @@ def precompiled_template(locals) # ERB template implementation. See: # http://www.ruby-doc.org/stdlib/libdoc/erb/rdoc/classes/ERB.html class ERBTemplate < Template + @@default_output_variable = '_erbout' + + def self.default_output_variable + @@default_output_variable + end + + def self.default_output_variable=(name) + @@default_output_variable = name + end + def initialize_engine return if defined? ::ERB require_template_library 'erb' end def prepare - @outvar = (options[:outvar] || '_erbout').to_s + @outvar = options[:outvar] || self.class.default_output_variable @engine = ::ERB.new(data, options[:safe], options[:trim], @outvar) end def precompiled_template(locals) - @engine.src + source = @engine.src + source end def precompiled_preamble(locals) @@ -399,6 +412,16 @@ def precompiled(locals) # Erubis template implementation. See: # http://www.kuwata-lab.com/erubis/ + # + # ErubisTemplate supports the following additional options, which are not + # passed down to the Erubis engine: + # + # :engine_class allows you to specify a custom engine class to use + # instead of the default (which is ::Erubis::Eruby). + # + # :escape_html when true, ::Erubis::EscapedEruby will be used as + # the engine class instead of the default. All content + # within <%= %> blocks will be automatically html escaped. class ErubisTemplate < ERBTemplate def initialize_engine return if defined? ::Erubis @@ -407,8 +430,10 @@ def initialize_engine def prepare @options.merge!(:preamble => false, :postamble => false) - @outvar = (options.delete(:outvar) || '_erbout').to_s - @engine = ::Erubis::Eruby.new(data, options) + @outvar = options.delete(:outvar) || self.class.default_output_variable + engine_class = options.delete(:engine_class) + engine_class = ::Erubis::EscapedEruby if options.delete(:escape_html) + @engine = (engine_class || ::Erubis::Eruby).new(data, options) end def precompiled_preamble(locals) @@ -508,37 +533,20 @@ def evaluate(scope, locals, &block) private def sass_options - options.merge(:filename => eval_file, :line => line) + options.merge(:filename => eval_file, :line => line, :syntax => :sass) end end register 'sass', SassTemplate - - # Scss template implementation. - # - # Scss templates do not support object scopes, locals, or yield. - class ScssTemplate < Template - def initialize_engine - return if defined? ::Sass::Engine - require_template_library 'sass' - end - - def prepare - @engine = ::Sass::Engine.new(data, scss_options) - end - - def evaluate(scope, locals, &block) - @output ||= @engine.render - end - + # Sass's new .scss type template implementation. + class ScssTemplate < SassTemplate private - def scss_options + def sass_options options.merge(:filename => eval_file, :line => line, :syntax => :scss) end end register 'scss', ScssTemplate - # Lessscss template implementation. See: # http://lesscss.org/ # @@ -560,6 +568,33 @@ def evaluate(scope, locals, &block) register 'less', LessTemplate + # Nokogiri template implementation. See: + # http://nokogiri.org/ + class NokogiriTemplate < Template + def initialize_engine + return if defined?(::Nokogiri) + require_template_library 'nokogiri' + end + + def prepare; end + + def evaluate(scope, locals, &block) + xml = ::Nokogiri::XML::Builder.new + if data.respond_to?(:to_str) + locals[:xml] = xml + super(scope, locals, &block) + elsif data.kind_of?(Proc) + data.call(xml) + end + xml.to_xml + end + + def precompiled_template(locals) + data.to_str + end + end + register 'nokogiri', NokogiriTemplate + # Builder template implementation. See: # http://builder.rubyforge.org/ class BuilderTemplate < Template @@ -676,55 +711,6 @@ def evaluate(scope, locals, &block) register 'textile', RedClothTemplate - # Mustache is written and maintained by Chris Wanstrath. See: - # http://github.com/defunkt/mustache - # - # When a scope argument is provided to MustacheTemplate#render, the - # instance variables are copied from the scope object to the Mustache - # view. - class MustacheTemplate < Template - attr_reader :engine - - def initialize_engine - return if defined? ::Mustache - require_template_library 'mustache' - end - - def prepare - Mustache.view_namespace = options[:namespace] - Mustache.view_path = options[:view_path] || options[:mustaches] - @engine = options[:view] || Mustache.view_class(name) - options.each do |key, value| - next if %w[view view_path namespace mustaches].include?(key.to_s) - @engine.send("#{key}=", value) if @engine.respond_to? "#{key}=" - end - end - - def evaluate(scope=nil, locals={}, &block) - instance = @engine.new - - # copy instance variables from scope to the view - scope.instance_variables.each do |name| - instance.instance_variable_set(name, scope.instance_variable_get(name)) - end - - # locals get added to the view's context - locals.each do |local, value| - instance[local] = value - end - - # if we're passed a block it's a subview. Sticking it in yield - # lets us use {{yield}} in layout.html to render the actual page. - instance[:yield] = block.call if block - - instance.template = data unless instance.compiled? - - instance.to_html - end - end - register 'mustache', MustacheTemplate - - # RDoc template. See: # http://rdoc.rubyforge.org/ # @@ -751,21 +737,36 @@ def evaluate(scope, locals, &block) register 'rdoc', RDocTemplate - # CoffeeScript info: - # http://jashkenas.github.com/coffee-script/ - class CoffeeTemplate < Template + # Radius Template + # http://github.com/jlong/radius/ + class RadiusTemplate < Template def initialize_engine - return if defined? ::CoffeeScript - require_template_library 'coffee-script' + return if defined? ::Radius + require_template_library 'radius' end def prepare - @output = nil end def evaluate(scope, locals, &block) - @output ||= ::CoffeeScript::compile(data, options) + context = Class.new(Radius::Context).new + context.define_tag("yield") do + block.call + end + locals.each do |tag, value| + context.define_tag(tag) do + value + end + end + (class << context; self; end).class_eval do + define_method :tag_missing do |tag, attr| + scope.__send__(tag) # any way to support attr as args? + end + end + options = {:tag_prefix => 'r'}.merge(@options) + parser = Radius::Parser.new(context, options) + parser.parse(data) end end - register 'coffee', CoffeeTemplate + register 'radius', RadiusTemplate end From 0b032a0c3ecb8855e080a0e4d1d904a7271090a4 Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Wed, 1 Sep 2010 11:47:10 +0200 Subject: [PATCH 05/80] Always respect :type parameter for send_file. Fixes GH #42 --- lib/sinatra/base.rb | 1 + test/helpers_test.rb | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/lib/sinatra/base.rb b/lib/sinatra/base.rb index 22a48a3893..7172a174e3 100644 --- a/lib/sinatra/base.rb +++ b/lib/sinatra/base.rb @@ -158,6 +158,7 @@ def send_file(path, opts={}) last_modified stat.mtime content_type mime_type(opts[:type]) || + opts[:type] || mime_type(File.extname(path)) || response['Content-Type'] || 'application/octet-stream' diff --git a/test/helpers_test.rb b/test/helpers_test.rb index 1a096a71e1..647a1dce6d 100644 --- a/test/helpers_test.rb +++ b/test/helpers_test.rb @@ -345,6 +345,18 @@ def send_file_app(opts={}) assert_equal 'text/plain', response['Content-Type'] end + it 'sets the Content-Type response header if type option is set to a file extesion' do + send_file_app :type => 'html' + get '/file.txt' + assert_equal 'text/html', response['Content-Type'] + end + + it 'sets the Content-Type response header if type option is set to a mime type' do + send_file_app :type => 'application/octet-stream' + get '/file.txt' + assert_equal 'application/octet-stream', response['Content-Type'] + end + it 'sets the Content-Length response header' do send_file_app get '/file.txt' From 507972ee64772ffc79a6e54cf84c613f4fe49be6 Mon Sep 17 00:00:00 2001 From: Jean-Philippe Garcia Ballester Date: Mon, 30 Aug 2010 11:00:30 +0200 Subject: [PATCH 06/80] Fix redirect method MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to RFC 2616 section 14.30, for the “Location” header, “the field value consists of a single absolute URI” If a non absolute URI is given, the URI is reconstructed using the request. Tests are modified accordingly. --- lib/sinatra/base.rb | 8 ++++++++ test/filter_test.rb | 4 ++-- test/helpers_test.rb | 6 +++--- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/lib/sinatra/base.rb b/lib/sinatra/base.rb index 7172a174e3..6562af00a4 100644 --- a/lib/sinatra/base.rb +++ b/lib/sinatra/base.rb @@ -96,6 +96,14 @@ def block.each ; yield call ; end # Halt processing and redirect to the URI provided. def redirect(uri, *args) + if not uri =~ /^https?:\/\// + # According to RFC 2616 section 14.30, “the field value consists of a single absolute URI” + abs_uri = request.scheme + "://" + abs_uri << request.host + abs_uri << ":#{port}" if request.scheme == "https" and request.port != 443 or request.scheme == "http" and request.port != 80 + abs_uri << uri + uri = abs_uri + end status 302 response['Location'] = uri halt(*args) diff --git a/test/filter_test.rb b/test/filter_test.rb index 4689913cc2..11a3d8e421 100644 --- a/test/filter_test.rb +++ b/test/filter_test.rb @@ -55,7 +55,7 @@ class BeforeFilterTest < Test::Unit::TestCase get '/foo' assert redirect? - assert_equal '/bar', response['Location'] + assert_equal 'http://example.org/bar', response['Location'] assert_equal '', body end @@ -189,7 +189,7 @@ class AfterFilterTest < Test::Unit::TestCase get '/foo' assert redirect? - assert_equal '/bar', response['Location'] + assert_equal 'http://example.org/bar', response['Location'] assert_equal '', body end diff --git a/test/helpers_test.rb b/test/helpers_test.rb index 647a1dce6d..3be2a8390e 100644 --- a/test/helpers_test.rb +++ b/test/helpers_test.rb @@ -58,7 +58,7 @@ def test_default get '/' assert_equal 302, status assert_equal '', body - assert_equal '/foo', response['Location'] + assert_equal 'http://example.org/foo', response['Location'] end it 'uses the code given when specified' do @@ -72,7 +72,7 @@ def test_default get '/' assert_equal 301, status assert_equal '', body - assert_equal '/foo', response['Location'] + assert_equal 'http://example.org/foo', response['Location'] end it 'redirects back to request.referer when passed back' do @@ -85,7 +85,7 @@ def test_default request = Rack::MockRequest.new(@app) response = request.get('/try_redirect', 'HTTP_REFERER' => '/foo') assert_equal 302, response.status - assert_equal '/foo', response['Location'] + assert_equal 'http://example.org/foo', response['Location'] end end From 081c18401826748f14aa261faca698bae494cf3a Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Wed, 1 Sep 2010 12:02:03 +0200 Subject: [PATCH 07/80] Ignore = 1.9.2 ] # add rubinius (and hopefully other VM impls) ignore patterns ... From ba0d18a8c1229bc6d4c8c37399a3f0521d87a43f Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Wed, 1 Sep 2010 12:08:01 +0200 Subject: [PATCH 08/80] increase VERSION, fixes GH #29 --- lib/sinatra/base.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/sinatra/base.rb b/lib/sinatra/base.rb index 465786f99b..8770274e6e 100644 --- a/lib/sinatra/base.rb +++ b/lib/sinatra/base.rb @@ -19,7 +19,7 @@ end module Sinatra - VERSION = '1.0' + VERSION = '1.1' # The request object. See Rack::Request for more info: # http://rack.rubyforge.org/doc/classes/Rack/Request.html From 38ad411abd901389c1ee28deb6f26ddc8bfb1cac Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Wed, 1 Sep 2010 12:12:36 +0200 Subject: [PATCH 09/80] spec checking wether Marshal.dump(params) is possible --- test/request_test.rb | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test/request_test.rb b/test/request_test.rb index f213646cdf..c61a60cb2e 100644 --- a/test/request_test.rb +++ b/test/request_test.rb @@ -30,4 +30,15 @@ class RequestTest < Test::Unit::TestCase request = Sinatra::Request.new('HTTP_X_FORWARDED_PROTO' => 'https') assert request.secure? end + + it 'is possible to marshal params' do + request = Sinatra::Request.new( + 'REQUEST_METHOD' => 'PUT', + 'CONTENT_TYPE' => 'application/x-www-form-urlencoded', + 'rack.input' => StringIO.new('foo=bar') + ) + params = Sinatra::Base.new.send(:indifferent_hash).replace(request.params) + dumped = Marshal.dump(request.params) + assert_equal 'bar', Marshal.load(dumped)['foo'] + end end From a05d1fa87da2a541896f6fd5a8cff64e130c0671 Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Wed, 1 Sep 2010 12:18:22 +0200 Subject: [PATCH 10/80] require stringio in request_test, fixes GH #51 --- test/request_test.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/test/request_test.rb b/test/request_test.rb index c61a60cb2e..784dc975e2 100644 --- a/test/request_test.rb +++ b/test/request_test.rb @@ -1,4 +1,5 @@ require File.dirname(__FILE__) + '/helper' +require 'stringio' class RequestTest < Test::Unit::TestCase it 'responds to #user_agent' do From 4cab214ca67192dfbd20365f166aada3d26c8a41 Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Wed, 1 Sep 2010 12:58:01 +0200 Subject: [PATCH 11/80] Changelog for 1.1 --- CHANGES | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/CHANGES b/CHANGES index 2f5292ee7d..2cb26497c1 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,56 @@ += 1.1 / Not Yet Released + + * Before and after filters now support pattern matching, including the ability + to use captures: "before('/user/:name') { |name| ... }". This avoids manual + path checking. No performance loss if patterns are avoided. (Konstantin Haase) + + * Setting multiple values now no longer relies on #to_hash and therefore accepts + any Enumerable as parameter. (Simon Rozet) + + * It is now possible to access Sinatra's template_cache from the outside. + (Nick Sutterer) + + * It is now possible to render SCSS files with the `scss` method, which behaves + exactly like `sass` except for the different file extension and assuming the + SCSS syntax. (Pedro Menezes, Konstantin Haase) + + * Broken examples for using Erubis and Haml in README have been fixed. + (Nick Sutterer, Doug Ireton, Jason Stewart) + + * Sinatra is now able to use Tilt versions including numbers > 9, as in 0.10. + (Konstantin Haase) + + * The `last_modified` method now also accepts DateTime instances and makes sure + the header will always be set to a string. (Konstantin Haase) + + * `send_file` now always respects the `:type` option if set. Previously it was + discarded if no matching mime type was found, which made it impossible to + directly pass a mime type. (Konstantin Haase) + + * `redirect` always redirects to an absolute URI, even if a relative URI was passed. + Ensures compatibility with RFC 2616 section 14.30. (Jean-Philippe Garcia Ballester) + + * Added ability to handle weighted HTTP_ACCEPT headers. (Davide D'Agostino) + + * README is now available in German. (Bernhard Essl) + + * This release is compatible with Ruby 1.9.2. Sinatra was trying to read + none existent files Ruby added to the call stack. (Konstantin Haase) + + * This release is now usable in combination with Rails 3. When mounting + a Sinatra application under a subpath in Rails 3, the PATH_INFO is not + prefixed with a slash and no routes did match. (José Valim) + + * Sinatra now handles SIGTERM correctly. (Patrick Collison) + + * Fixes an issue with inline templates in modular applications that manually + call `run!`. (Konstantin Haase) + + * It's now possible to use Sinatra with different package management + systems defining a custom require. (Konstantin Haase) + + * Lighthouse has been dropped in favor of GitHub issues. + = 1.0 / 2010-03-23 * It's now possible to register blocks to run after each request using From 1e755d5d9b241b9c9aaf1704f619cd4ca8286c7e Mon Sep 17 00:00:00 2001 From: Anthony Williams Date: Wed, 1 Sep 2010 16:03:50 +0100 Subject: [PATCH 12/80] Fix redirects when using non-standard ports. Commit 507972ee introduced a change to the redirect method in order to be compliant with RFC 2616's requirement that the Location header use an "absoluteURI". However, the change relied on an undefined "port" variable. This commit fixes that omission by using the port from the request. The formatting of the redirect method has also been adjusted to wrap at approx. 78-80 characters and use "&&" and "||", like the rest of Sinatra. Specs included. --- lib/sinatra/base.rb | 17 +++++++++++------ test/helpers_test.rb | 24 ++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/lib/sinatra/base.rb b/lib/sinatra/base.rb index 8770274e6e..6461508b3b 100644 --- a/lib/sinatra/base.rb +++ b/lib/sinatra/base.rb @@ -97,13 +97,18 @@ def block.each ; yield call ; end # Halt processing and redirect to the URI provided. def redirect(uri, *args) if not uri =~ /^https?:\/\// - # According to RFC 2616 section 14.30, “the field value consists of a single absolute URI” - abs_uri = request.scheme + "://" - abs_uri << request.host - abs_uri << ":#{port}" if request.scheme == "https" and request.port != 443 or request.scheme == "http" and request.port != 80 - abs_uri << uri - uri = abs_uri + # According to RFC 2616 section 14.30, "the field value consists of a + # single absolute URI" + abs_uri = "#{request.scheme}://#{request.host}" + + if request.scheme == 'https' && request.port != 443 || + request.scheme == 'http' && request.port != 80 + abs_uri << ":#{request.port}" + end + + uri = (abs_uri << uri) end + status 302 response['Location'] = uri halt(*args) diff --git a/test/helpers_test.rb b/test/helpers_test.rb index 3be2a8390e..5716546139 100644 --- a/test/helpers_test.rb +++ b/test/helpers_test.rb @@ -87,6 +87,30 @@ def test_default assert_equal 302, response.status assert_equal 'http://example.org/foo', response['Location'] end + + it 'redirects using a non-standard HTTP port' do + mock_app { + get '/' do + redirect '/foo' + end + } + + request = Rack::MockRequest.new(@app) + response = request.get('/', 'SERVER_PORT' => '81') + assert_equal 'http://example.org:81/foo', response['Location'] + end + + it 'redirects using a non-standard HTTPS port' do + mock_app { + get '/' do + redirect '/foo' + end + } + + request = Rack::MockRequest.new(@app) + response = request.get('/', 'SERVER_PORT' => '444') + assert_equal 'http://example.org:444/foo', response['Location'] + end end describe 'error' do From f2a1dc05b830ea241e0413a8e6533c6fdcc14e36 Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Wed, 1 Sep 2010 17:19:15 +0200 Subject: [PATCH 13/80] Add Anthony Williams to the change log. --- CHANGES | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 2cb26497c1..3902cb3320 100644 --- a/CHANGES +++ b/CHANGES @@ -28,7 +28,8 @@ directly pass a mime type. (Konstantin Haase) * `redirect` always redirects to an absolute URI, even if a relative URI was passed. - Ensures compatibility with RFC 2616 section 14.30. (Jean-Philippe Garcia Ballester) + Ensures compatibility with RFC 2616 section 14.30. + (Jean-Philippe Garcia Ballester, Anthony Williams) * Added ability to handle weighted HTTP_ACCEPT headers. (Davide D'Agostino) From 8a3aea14a2301a0f56689591dee12ac8966472c7 Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Thu, 2 Sep 2010 07:02:01 +0200 Subject: [PATCH 14/80] add Shota Fukumori to the change log --- CHANGES | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 3902cb3320..a7b3f791ea 100644 --- a/CHANGES +++ b/CHANGES @@ -36,7 +36,8 @@ * README is now available in German. (Bernhard Essl) * This release is compatible with Ruby 1.9.2. Sinatra was trying to read - none existent files Ruby added to the call stack. (Konstantin Haase) + none existent files Ruby added to the call stack. (Shota Fukumori, + Konstantin Haase) * This release is now usable in combination with Rails 3. When mounting a Sinatra application under a subpath in Rails 3, the PATH_INFO is not From bade15e7bc47e7b02b22a0692942e8852b8f6c93 Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Thu, 2 Sep 2010 13:53:01 +0200 Subject: [PATCH 15/80] Make version number conform to SemVer. --- lib/sinatra/base.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/sinatra/base.rb b/lib/sinatra/base.rb index 6461508b3b..398f81449f 100644 --- a/lib/sinatra/base.rb +++ b/lib/sinatra/base.rb @@ -19,7 +19,7 @@ end module Sinatra - VERSION = '1.1' + VERSION = '1.1.0' # The request object. See Rack::Request for more info: # http://rack.rubyforge.org/doc/classes/Rack/Request.html From 33affbf26411a3157d8f38b5dac8f66be878e894 Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Thu, 2 Sep 2010 13:58:23 +0200 Subject: [PATCH 16/80] Add class level #settings for convenience. --- lib/sinatra/base.rb | 11 ++++++++++- test/settings_test.rb | 4 ++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/lib/sinatra/base.rb b/lib/sinatra/base.rb index 398f81449f..b4e00704b3 100644 --- a/lib/sinatra/base.rb +++ b/lib/sinatra/base.rb @@ -445,11 +445,20 @@ def call!(env) [status, header, body] end + # Access settings defined with Base.set. + def self.settings + self + end + # Access settings defined with Base.set. def settings - self.class + self.class.settings end + alias_method :options, :settings + class << self + alias_method :options, :settings + end # Exit the current block, halts any further processing # of the request, and returns the specified response. diff --git a/test/settings_test.rb b/test/settings_test.rb index e643a36ef0..821376c2eb 100644 --- a/test/settings_test.rb +++ b/test/settings_test.rb @@ -115,6 +115,10 @@ def foo=(value) assert_equal :foo, @base.new.settings.environment end + it 'is accessible from class via #settings' do + assert_equal :foo, @base.settings.environment + end + describe 'methodoverride' do it 'is disabled on Base' do assert ! @base.method_override? From 726feeb4ee0e0f0acbe3f68d4065b322ac69ed7c Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Thu, 2 Sep 2010 14:13:36 +0200 Subject: [PATCH 17/80] Documentation for condition. Fixes GH #15. --- README.rdoc | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/README.rdoc b/README.rdoc index 44afa407d8..4b8cb55723 100644 --- a/README.rdoc +++ b/README.rdoc @@ -81,6 +81,8 @@ Or with a block parameter: "Hello, #{c}!" end +=== Conditions + Routes may include a variety of matching conditions, such as the user agent: get '/foo', :agent => /Songbird (\d\.\d)[\d\/]*?/ do @@ -91,6 +93,32 @@ Routes may include a variety of matching conditions, such as the user agent: # Matches non-songbird browsers end +Other available conditions are `host_name` and `provides`: + + get '/', :host_name => /^admin\./ do + "Admin Area, Access denied!" + end + + get '/', :provides => 'html' do + haml :index + end + + get '/', :provides => ['rss', 'atom', 'xml'] do + builder :feed + end + +You can easily define your own conditions: + + set(:probability) { |value| condition { rand <= value } } + + get '/win_a_car', :probability => 0.1 do + "You won!" + end + + get '/win_a_car' do + "Sorry, you lost." + end + == Static Files Static files are served from the ./public directory. You can specify From 78554ffe912e4c1f4f9ae221452dbdb0723f2f3a Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Thu, 2 Sep 2010 14:18:17 +0200 Subject: [PATCH 18/80] Always reload templates on Ruby 1.8.6, avoids memory leak. Fixes GH #45. --- lib/sinatra/base.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/sinatra/base.rb b/lib/sinatra/base.rb index b4e00704b3..faeebd648e 100644 --- a/lib/sinatra/base.rb +++ b/lib/sinatra/base.rb @@ -1107,7 +1107,7 @@ class << self set :app_file, nil set :root, Proc.new { app_file && File.expand_path(File.dirname(app_file)) } set :views, Proc.new { root && File.join(root, 'views') } - set :reload_templates, Proc.new { development? } + set :reload_templates, Proc.new { development? or RUBY_VERSION < '1.8.7' } set :lock, false set :public, Proc.new { root && File.join(root, 'public') } From aaeb564a430f9560dec360d9fc8f10c53743718a Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Thu, 2 Sep 2010 21:37:07 +0200 Subject: [PATCH 19/80] document route return values, fixes GH #23 --- README.rdoc | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/README.rdoc b/README.rdoc index 4b8cb55723..72f351bdf1 100644 --- a/README.rdoc +++ b/README.rdoc @@ -119,6 +119,35 @@ You can easily define your own conditions: "Sorry, you lost." end +=== Return values + +The return value of a route block determines at least the response body passed +on to the HTTP client, or at least the next middleware in the Rack stack. +Most commonly this is a string, as in the above examples. But other values are +also accepted. + +You can return any object that would either be a valid Rack response, Rack body object +or HTTP status code: + +* An Array with three elements: `[status (Fixnum), headers (Hash), response body (responds to #each)]` +* An Array with two elements: `[status (Fixnum), response body (responds to #each)]` +* An object that responds to #each and passes nothing but strings to the given block +* A Fixnum representing the status code + +That way we can for instance easily implement a streaming example: + + class Stream + def each + 100.times { |i| yield "#{i}\n" } + end + end + + get '/' do + Stream.new + rescue StandardError + [500, 'sorry, error'] + end + == Static Files Static files are served from the ./public directory. You can specify From fa527095a2d7b2b51e974637ead311d0908fc06d Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Fri, 3 Sep 2010 07:57:49 +0200 Subject: [PATCH 20/80] fix markup for inline code in README --- README.rdoc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.rdoc b/README.rdoc index 72f351bdf1..8f2c886783 100644 --- a/README.rdoc +++ b/README.rdoc @@ -93,7 +93,7 @@ Routes may include a variety of matching conditions, such as the user agent: # Matches non-songbird browsers end -Other available conditions are `host_name` and `provides`: +Other available conditions are +host_name+ and +provides+: get '/', :host_name => /^admin\./ do "Admin Area, Access denied!" @@ -129,8 +129,8 @@ also accepted. You can return any object that would either be a valid Rack response, Rack body object or HTTP status code: -* An Array with three elements: `[status (Fixnum), headers (Hash), response body (responds to #each)]` -* An Array with two elements: `[status (Fixnum), response body (responds to #each)]` +* An Array with three elements: +[status (Fixnum), headers (Hash), response body (responds to #each)]+ +* An Array with two elements: +[status (Fixnum), response body (responds to #each)]+ * An object that responds to #each and passes nothing but strings to the given block * A Fixnum representing the status code @@ -349,7 +349,7 @@ Templates may be defined at the end of the source file: %div.title Hello world!!!!! NOTE: Inline templates defined in the source file that requires sinatra -are automatically loaded. Call `enable :inline_templates` explicitly if you +are automatically loaded. Call +enable :inline_templates+ explicitly if you have inline templates in other source files. === Named Templates From 626f68904b355641e613592f5076cd041a3a3691 Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Fri, 3 Sep 2010 08:00:27 +0200 Subject: [PATCH 21/80] fix markup for multi word inline code in README --- README.rdoc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.rdoc b/README.rdoc index 8f2c886783..1e3d8df0a4 100644 --- a/README.rdoc +++ b/README.rdoc @@ -129,9 +129,9 @@ also accepted. You can return any object that would either be a valid Rack response, Rack body object or HTTP status code: -* An Array with three elements: +[status (Fixnum), headers (Hash), response body (responds to #each)]+ -* An Array with two elements: +[status (Fixnum), response body (responds to #each)]+ -* An object that responds to #each and passes nothing but strings to the given block +* An Array with three elements: [status (Fixnum), headers (Hash), response body (responds to #each)] +* An Array with two elements: [status (Fixnum), response body (responds to #each)] +* An object that responds to +#each+ and passes nothing but strings to the given block * A Fixnum representing the status code That way we can for instance easily implement a streaming example: From 9c73cf7d7e50a3ba5a45376afaace1e70ac8b968 Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Fri, 3 Sep 2010 08:01:23 +0200 Subject: [PATCH 22/80] fix markup for multi word inline code in README, take two (apparently # does not match \w) --- README.rdoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rdoc b/README.rdoc index 1e3d8df0a4..11e9568239 100644 --- a/README.rdoc +++ b/README.rdoc @@ -131,7 +131,7 @@ or HTTP status code: * An Array with three elements: [status (Fixnum), headers (Hash), response body (responds to #each)] * An Array with two elements: [status (Fixnum), response body (responds to #each)] -* An object that responds to +#each+ and passes nothing but strings to the given block +* An object that responds to #each and passes nothing but strings to the given block * A Fixnum representing the status code That way we can for instance easily implement a streaming example: From 12c11578f52b46b6aa3330f8875ce8128820185f Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Fri, 3 Sep 2010 08:37:03 +0200 Subject: [PATCH 23/80] Add latest commits to the change log. --- CHANGES | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGES b/CHANGES index a7b3f791ea..de7cdd3da3 100644 --- a/CHANGES +++ b/CHANGES @@ -51,6 +51,14 @@ * It's now possible to use Sinatra with different package management systems defining a custom require. (Konstantin Haase) + * You can now use #settings from class level for convenience. + (Konstantin Haase) + + * Prevents a memory leak on 1.8.6 is production mode. Note, however, that this + is due to a bug in 1.8.6 and request will have the additional overhead of + parsing templates again on that version. It is recommended to use at least + Ruby 1.8.7. (Konstantin Haase) + * Lighthouse has been dropped in favor of GitHub issues. = 1.0 / 2010-03-23 From 2233cdbf683c3a54351a24dce3be2e5875cb2a76 Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Fri, 3 Sep 2010 08:49:30 +0200 Subject: [PATCH 24/80] Allow ampersands in URL parameters, fixes GH #37. --- lib/sinatra/base.rb | 2 +- test/routing_test.rb | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/sinatra/base.rb b/lib/sinatra/base.rb index faeebd648e..272dbe045a 100644 --- a/lib/sinatra/base.rb +++ b/lib/sinatra/base.rb @@ -932,7 +932,7 @@ def compile(path) Regexp.escape(match) else keys << $2[1..-1] - "([^/?&#]+)" + "([^/?#]+)" end end [/^#{pattern}$/, keys] diff --git a/test/routing_test.rb b/test/routing_test.rb index 93948066a2..956f5b64bd 100644 --- a/test/routing_test.rb +++ b/test/routing_test.rb @@ -356,6 +356,18 @@ class RoutingTest < Test::Unit::TestCase assert_equal 'looks good', body end + it "matches paths that include ampersands" do + mock_app { + get '/:name' do + 'looks good' + end + } + + get '/foo&bar' + assert ok? + assert_equal 'looks good', body + end + it "URL decodes named parameters and splats" do mock_app { get '/:foo/*' do From 739b6ba01244ed56d14fbd00ce5e85dcbb786b1c Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Fri, 3 Sep 2010 23:19:20 +0200 Subject: [PATCH 25/80] make URL params available in filters with pattern. Previously this was the only way to access parameters from pattern: before /:name do |name| ... end Now it is also possible to use params: before /:name do do_something params[:name] end --- lib/sinatra/base.rb | 100 ++++++++++++++++++++++++-------------------- test/filter_test.rb | 13 ++++++ 2 files changed, 68 insertions(+), 45 deletions(-) diff --git a/lib/sinatra/base.rb b/lib/sinatra/base.rb index 272dbe045a..76b47c63d0 100644 --- a/lib/sinatra/base.rb +++ b/lib/sinatra/base.rb @@ -494,51 +494,19 @@ def filter!(type, base = self.class) # Run routes defined on the class and all superclasses. def route!(base=self.class, pass_block=nil) if routes = base.routes[@request.request_method] - original_params = @params - - path = unescape(@request.path_info) - path = "/" if path.empty? - routes.each do |pattern, keys, conditions, block| - if match = pattern.match(path) - values = match.captures.to_a - params = - if keys.any? - keys.zip(values).inject({}) do |hash,(k,v)| - if k == 'splat' - (hash[k] ||= []) << v - else - hash[k] = v - end - hash - end - elsif values.any? - {'captures' => values} - else - {} - end - @params = original_params.merge(params) - @block_params = values - - pass_block = catch(:pass) do - conditions.each { |cond| - throw :pass if instance_eval(&cond) == false } - route_eval(&block) - end + pass_block = process_route(pattern, keys, conditions) do + route_eval(&block) end end - - @params = original_params end # Run routes defined in superclass. if base.superclass.respond_to?(:routes) - route! base.superclass, pass_block - return + return route!(base.superclass, pass_block) end route_eval(&pass_block) if pass_block - route_missing end @@ -547,6 +515,46 @@ def route_eval(&block) throw :halt, instance_eval(&block) end + # If the current request matches pattern and conditions, fill params + # with keys and call the given block. + # Revert params afterwards. + # + # Returns pass block. + def process_route(pattern, keys, conditions) + @original_params ||= @params + @path ||= begin + path = unescape(@request.path_info) + path.empty? ? "/" : path + end + if match = pattern.match(@path) + values = match.captures.to_a + params = + if keys.any? + keys.zip(values).inject({}) do |hash,(k,v)| + if k == 'splat' + (hash[k] ||= []) << v + else + hash[k] = v + end + hash + end + elsif values.any? + {'captures' => values} + else + {} + end + @params = @original_params.merge(params) + @block_params = values + catch(:pass) do + conditions.each { |cond| + throw :pass if instance_eval(&cond) == false } + yield + end + end + ensure + @params = @original_params + end + # No matching route was found or all routes passed. The default # implementation is to forward the request downstream when running # as middleware (@app is non-nil); when no downstream app is set, raise @@ -828,11 +836,9 @@ def after(path = nil, &block) # add a filter def add_filter(type, path = nil, &block) return filters[type] << block unless path - block, pattern = compile!(type, path, block) + block, *arguments = compile!(type, path, block) add_filter(type) do - next unless match = pattern.match(request.path_info) - @block_params = match.captures.to_a - instance_eval(&block) + process_route(*arguments) { instance_eval(&block) } end end @@ -896,8 +902,7 @@ def route(verb, path, options={}, &block) host_name(options.delete(:host)) if options.key?(:host) options.each { |option, args| send(option, *args) } - block, pattern, keys = compile! verb, path, block - conditions, @conditions = @conditions, [] + block, pattern, keys, conditions = compile! verb, path, block invoke_hook(:route_added, verb, path, block) (@routes[verb] ||= []). @@ -910,12 +915,17 @@ def invoke_hook(name, *args) def compile!(verb, path, block) method_name = "#{verb} #{path}" + define_method(method_name, &block) - unbound_method = instance_method method_name + unbound_method = instance_method method_name + pattern, keys = compile(path) + conditions, @conditions = @conditions, [] remove_method method_name - [block.arity != 0 ? - proc { unbound_method.bind(self).call(*@block_params) } : - proc { unbound_method.bind(self).call }, *compile(path)] + + [ block.arity != 0 ? + proc { unbound_method.bind(self).call(*@block_params) } : + proc { unbound_method.bind(self).call }, + pattern, keys, conditions ] end def compile(path) diff --git a/test/filter_test.rb b/test/filter_test.rb index 11a3d8e421..d9adb10e46 100644 --- a/test/filter_test.rb +++ b/test/filter_test.rb @@ -264,4 +264,17 @@ class AfterFilterTest < Test::Unit::TestCase get '/foo/bar' assert_equal subpath, 'bar' end + + it 'is possible to access url params from the route param' do + ran = false + mock_app do + get('/foo/*') { } + before('/foo/:sub') do + assert_equal params[:sub], 'bar' + ran = true + end + end + get '/foo/bar' + assert ran + end end From 0dd5749d8ef4144b5f779a1a197b6bfe300acdc4 Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Mon, 6 Sep 2010 17:53:15 +0200 Subject: [PATCH 26/80] Avoid segfault on 1.9.2p0 when running all tests together. Fixes GH #52. --- Rakefile | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/Rakefile b/Rakefile index 7a6a245d28..664f73ecf6 100644 --- a/Rakefile +++ b/Rakefile @@ -13,10 +13,20 @@ end # SPECS =============================================================== -Rake::TestTask.new(:test) do |t| - t.test_files = FileList['test/*_test.rb'] - t.ruby_opts = ['-rubygems'] if defined? Gem - t.ruby_opts << '-I.' +if !ENV['NO_TEST_FIX'] and RUBY_VERSION == '1.9.2' and RUBY_PATCHLEVEL == 0 + # Avoids seg fault + task(:test) do + files = Dir.glob('test/*_test.rb') + files.delete 'test/settings_test.rb' + sh "testrb #{files.join ' '}" + sh "testrb test/settings_test.rb" + end +else + Rake::TestTask.new(:test) do |t| + t.test_files = FileList['test/*_test.rb'] + t.ruby_opts = ['-rubygems'] if defined? Gem + t.ruby_opts << '-I.' + end end # Rcov ================================================================ From 7289cd905c6b36b5c669fbbce1cc2bb00ddb0a39 Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Tue, 7 Sep 2010 09:43:31 +0200 Subject: [PATCH 27/80] Better handling of encodings in Ruby 1.9, defaults to UTF-8. Fixes GH #27, GH #38 and params issue that came up in IRC. --- lib/sinatra/base.rb | 27 +++++++++++++++++++++++++++ test/settings_test.rb | 8 ++++++++ test/views/ascii.haml | 2 ++ 3 files changed, 37 insertions(+) create mode 100644 test/views/ascii.haml diff --git a/lib/sinatra/base.rb b/lib/sinatra/base.rb index 76b47c63d0..3f5abd0ddd 100644 --- a/lib/sinatra/base.rb +++ b/lib/sinatra/base.rb @@ -428,6 +428,7 @@ def call!(env) @response = Response.new @params = indifferent_params(@request.params) template_cache.clear if settings.reload_templates + force_encoding(@params) invoke { dispatch! } invoke { error_block!(response.status) } @@ -1093,6 +1094,31 @@ def caller_locations end end + attr_reader :default_encoding + def default_encoding=(value) + return false unless defined? Encoding + case value + when :utf8, true + self.default_encoding = "UTF-8" + when false + @default_encoding = value + else + Encoding.default_external = value + @default_encoding = Encoding.default_external + end + end + + def force_encoding(data, encoding = default_encoding) + return if !encoding or data == self + if data.respond_to? :force_encoding + data.force_encoding(encoding) + elsif data.respond_to? :each_value + data.each_value { |v| force_encoding(v, encoding) } + elsif data.respond_to? :each + data.each { |v| force_encoding(v, encoding) } + end + end + reset! set :environment, (ENV['RACK_ENV'] || :development).to_sym @@ -1118,6 +1144,7 @@ class << self set :root, Proc.new { app_file && File.expand_path(File.dirname(app_file)) } set :views, Proc.new { root && File.join(root, 'views') } set :reload_templates, Proc.new { development? or RUBY_VERSION < '1.8.7' } + set :default_encoding, 'UTF-8' set :lock, false set :public, Proc.new { root && File.join(root, 'public') } diff --git a/test/settings_test.rb b/test/settings_test.rb index 821376c2eb..4b640ff398 100644 --- a/test/settings_test.rb +++ b/test/settings_test.rb @@ -1,3 +1,4 @@ +# encoding: UTF-8 require File.dirname(__FILE__) + '/helper' class SettingsTest < Test::Unit::TestCase @@ -374,4 +375,11 @@ def foo=(value) assert ! @application.lock? end end + + describe 'default_encoding' do + it 'allows unicode strings in ascii templates per default (1.9)' do + @base.set :views, File.dirname(__FILE__) + "/views" + @base.new.haml(:ascii, {}, :value => "åkej") + end + end end diff --git a/test/views/ascii.haml b/test/views/ascii.haml new file mode 100644 index 0000000000..f410b319ad --- /dev/null +++ b/test/views/ascii.haml @@ -0,0 +1,2 @@ +This file has no unicode in it! += value From e4e9e659e118b87de01c9a3ee211e8388fed68ac Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Tue, 7 Sep 2010 10:01:00 +0200 Subject: [PATCH 28/80] Default default_internal encoding to default_external encoding. --- lib/sinatra/base.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/sinatra/base.rb b/lib/sinatra/base.rb index 3f5abd0ddd..63bd57df46 100644 --- a/lib/sinatra/base.rb +++ b/lib/sinatra/base.rb @@ -1104,6 +1104,7 @@ def default_encoding=(value) @default_encoding = value else Encoding.default_external = value + Encoding.default_internal ||= Encoding.default_external @default_encoding = Encoding.default_external end end From 8465d492aa75a73ecbfbda267e785683da6f3f1b Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Tue, 7 Sep 2010 10:23:28 +0200 Subject: [PATCH 29/80] Avoid `require "rubygems"` and `sudo` in README. --- README.de.rdoc | 5 ++--- README.jp.rdoc | 5 ++--- README.rdoc | 5 ++--- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/README.de.rdoc b/README.de.rdoc index 00450e0181..df57cf339c 100644 --- a/README.de.rdoc +++ b/README.de.rdoc @@ -3,7 +3,6 @@ Sinatra ist eine DSL für die rasche Erstellung von Web Anwendungen in Ruby mit minimalen Aufwand: # myapp.rb - require 'rubygems' require 'sinatra' get '/' do 'Hallo Welt!' @@ -11,8 +10,8 @@ Sinatra ist eine DSL für die rasche Erstellung von Web Anwendungen in Ruby mit Das gem installieren und starten: - sudo gem install sinatra - ruby myapp.rb + gem install sinatra + ruby -rubygems myapp.rb Aufrufen unter: http://localhost:4567 diff --git a/README.jp.rdoc b/README.jp.rdoc index 99b2d6859a..2b68099f57 100644 --- a/README.jp.rdoc +++ b/README.jp.rdoc @@ -3,7 +3,6 @@ SinatraはRubyで下記のような最小労力で手早くウェブアプリケーションを作成するためのDSLです。 # myapp.rb - require 'rubygems' require 'sinatra' get '/' do 'Hello world!' @@ -11,8 +10,8 @@ SinatraはRubyで下記のような最小労力で手早くウェブアプリケ gemをインストールして動かしてみる。 - sudo gem install sinatra - ruby myapp.rb + gem install sinatra + ruby -rubygems myapp.rb http://localhost:4567 を見る。 diff --git a/README.rdoc b/README.rdoc index 11e9568239..593f4ff368 100644 --- a/README.rdoc +++ b/README.rdoc @@ -4,7 +4,6 @@ Sinatra is a DSL for quickly creating web applications in Ruby with minimal effort: # myapp.rb - require 'rubygems' require 'sinatra' get '/' do 'Hello world!' @@ -12,8 +11,8 @@ effort: Install the gem and run with: - sudo gem install sinatra - ruby myapp.rb + gem install sinatra + ruby -rubygems myapp.rb View at: http://localhost:4567 From 6e00c57b224efedec0917696a872a1b658887c51 Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Tue, 7 Sep 2010 14:52:00 +0200 Subject: [PATCH 30/80] Fix streaming example. --- README.rdoc | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.rdoc b/README.rdoc index 593f4ff368..0b56fd0545 100644 --- a/README.rdoc +++ b/README.rdoc @@ -142,9 +142,11 @@ That way we can for instance easily implement a streaming example: end get '/' do - Stream.new - rescue StandardError - [500, 'sorry, error'] + begin + Stream.new + rescue StandardError + [500, 'sorry, error'] + end end == Static Files From 8fbd9c210546e7a43bee64b6d159b663e28bf331 Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Tue, 7 Sep 2010 14:53:43 +0200 Subject: [PATCH 31/80] Simplify streaming example. --- README.rdoc | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/README.rdoc b/README.rdoc index 0b56fd0545..addc0c3673 100644 --- a/README.rdoc +++ b/README.rdoc @@ -141,13 +141,7 @@ That way we can for instance easily implement a streaming example: end end - get '/' do - begin - Stream.new - rescue StandardError - [500, 'sorry, error'] - end - end + get('/') { Stream.new } == Static Files From c37db5cd8448084e1771c55b81854a939eabfbdf Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Tue, 7 Sep 2010 18:18:35 +0200 Subject: [PATCH 32/80] minor adjustments and fixes for the readme examples --- README.rdoc | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.rdoc b/README.rdoc index addc0c3673..803867beb2 100644 --- a/README.rdoc +++ b/README.rdoc @@ -5,6 +5,7 @@ effort: # myapp.rb require 'sinatra' + get '/' do 'Hello world!' end @@ -184,10 +185,10 @@ can be set globally through Sinatra's configurations, see {Options and Configurations}[http://www.sinatrarb.com/configuration.html], and overridden on an individual basis. - set :haml, {:format => :html5 } # default Haml format is :xhtml + set :haml, :format => :html5 # default Haml format is :xhtml get '/' do - haml :index, :haml_options => {:format => :html4 } # overridden + haml :index, :format => :html4 # overridden end @@ -248,7 +249,7 @@ can be set globally through Sinatra's configurations, see {Options and Configurations}[http://www.sinatrarb.com/configuration.html], and overridden on an individual basis. - set :sass, {:style => :compact } # default Sass style is :nested + set :sass, :style => :compact # default Sass style is :nested get '/stylesheet.css' do content_type 'text/css', :charset => 'utf-8' @@ -274,7 +275,7 @@ can be set globally through Sinatra's configurations, see {Options and Configurations}[http://www.sinatrarb.com/configuration.html], and overridden on an individual basis. - set :scss, {:style => :compact } # default Scss style is :nested + set :scss, :style => :compact # default Scss style is :nested get '/stylesheet.css' do content_type 'text/css', :charset => 'utf-8' From 08a62a2da3a84814cd2e89f60542302886c49772 Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Thu, 9 Sep 2010 08:43:07 +0200 Subject: [PATCH 33/80] Avoid misleading default_encoding setting and just rely on Encoding.default_external. --- lib/sinatra/base.rb | 48 ++++++++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/lib/sinatra/base.rb b/lib/sinatra/base.rb index 63bd57df46..e2f8c3b8da 100644 --- a/lib/sinatra/base.rb +++ b/lib/sinatra/base.rb @@ -1094,32 +1094,33 @@ def caller_locations end end - attr_reader :default_encoding - def default_encoding=(value) - return false unless defined? Encoding - case value - when :utf8, true - self.default_encoding = "UTF-8" - when false - @default_encoding = value - else - Encoding.default_external = value - Encoding.default_internal ||= Encoding.default_external - @default_encoding = Encoding.default_external - end - end - - def force_encoding(data, encoding = default_encoding) - return if !encoding or data == self - if data.respond_to? :force_encoding - data.force_encoding(encoding) - elsif data.respond_to? :each_value - data.each_value { |v| force_encoding(v, encoding) } - elsif data.respond_to? :each - data.each { |v| force_encoding(v, encoding) } + # Fixes encoding issues by + # * defaulting to UTF-8 + # * casting params to Encoding.default_external + # + # The latter might not be necessary if Rack handles it one day. + # Keep an eye on Rack's LH #100. + if defined? Encoding + if Encoding.default_external.to_s =~ /^ASCII/ + Encoding.default_external = "UTF-8" + end + Encoding.default_internal ||= Encoding.default_external + + def force_encoding(data) + return if data != self + if data.respond_to? :force_encoding + data.force_encoding(Encoding.default_external) + elsif data.respond_to? :each_value + data.each_value { |v| force_encoding(v) } + elsif data.respond_to? :each + data.each { |v| force_encoding(v) } + end end + else + def force_encoding(*) end end + reset! set :environment, (ENV['RACK_ENV'] || :development).to_sym @@ -1145,7 +1146,6 @@ class << self set :root, Proc.new { app_file && File.expand_path(File.dirname(app_file)) } set :views, Proc.new { root && File.join(root, 'views') } set :reload_templates, Proc.new { development? or RUBY_VERSION < '1.8.7' } - set :default_encoding, 'UTF-8' set :lock, false set :public, Proc.new { root && File.join(root, 'public') } From c6d061483ded19327df9d3c38fd6bfb240b526e0 Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Fri, 10 Sep 2010 16:33:45 +0200 Subject: [PATCH 34/80] Minor README improvements. --- README.rdoc | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/README.rdoc b/README.rdoc index 803867beb2..c0ca9e7c23 100644 --- a/README.rdoc +++ b/README.rdoc @@ -126,8 +126,8 @@ on to the HTTP client, or at least the next middleware in the Rack stack. Most commonly this is a string, as in the above examples. But other values are also accepted. -You can return any object that would either be a valid Rack response, Rack body object -or HTTP status code: +You can return any object that would either be a valid Rack response, Rack +body object or HTTP status code: * An Array with three elements: [status (Fixnum), headers (Hash), response body (responds to #each)] * An Array with two elements: [status (Fixnum), response body (responds to #each)] @@ -328,7 +328,6 @@ other templates. Templates may be defined at the end of the source file: - require 'rubygems' require 'sinatra' get '/' do @@ -388,9 +387,9 @@ route handlers and templates: == Filters -Before filters are evaluated before each request within the context of the -request and can modify the request and response. Instance variables set in -filters are accessible by routes and templates: +Before filters are evaluated before each request within the same context as +the routes will be and can modify the request and response. Instance variables +set in filters are accessible by routes and templates: before do @note = 'Hi!' @@ -402,16 +401,16 @@ filters are accessible by routes and templates: params[:splat] #=> 'bar/baz' end -After filter are evaluated after each request within the context of the -request and can also modify the request and response. Instance variables -set in before filters and routes are accessible by after filters: +After filter are evaluated after each request within the same context and can +also modify the request and response. Instance variables set in before filters +and routes are accessible by after filters: after do puts response.status end -Filters optionally taking a pattern, causing them to be evaluated only if the request -path matches that pattern: +Filters optionally taking a pattern, causing them to be evaluated only if the +request path matches that pattern: before '/protected/*' do authenticate! @@ -427,19 +426,19 @@ To immediately stop a request within a filter or route use: halt -You can also specify the status when halting ... +You can also specify the status when halting: halt 410 -Or the body ... +Or the body: halt 'this will be the body' -Or both ... +Or both: halt 401, 'go away!' -With headers ... +With headers: halt 402, {'Content-Type' => 'text/plain'}, 'revenge' @@ -484,8 +483,8 @@ Run when the environment is set to either :production or == Error handling Error handlers run within the same context as routes and before filters, which -means you get all the goodies it has to offer, like haml, erb, -halt, etc. +means you get all the goodies it has to offer, like haml, +erb, halt, etc. === Not Found @@ -493,7 +492,7 @@ When a Sinatra::NotFound exception is raised, or the response's status code is 404, the not_found handler is invoked: not_found do - 'This is nowhere to be found' + 'This is nowhere to be found.' end === Error From b7b43f70b3a8806f414f6ba57e9583bd7fc19a51 Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Fri, 10 Sep 2010 16:34:41 +0200 Subject: [PATCH 35/80] German README: Improve language and bring up to speed with English version. --- README.de.rdoc | 394 +++++++++++++++++++++++++++++++------------------ 1 file changed, 248 insertions(+), 146 deletions(-) diff --git a/README.de.rdoc b/README.de.rdoc index df57cf339c..92a0922cb5 100644 --- a/README.de.rdoc +++ b/README.de.rdoc @@ -1,6 +1,7 @@ = Sinatra -Sinatra ist eine DSL für die rasche Erstellung von Web Anwendungen in Ruby mit minimalen Aufwand: +Sinatra ist eine DSL das schnelles Erstellen von Webanwendungen in Ruby mit +minimalen Aufwand ermöglicht: # myapp.rb require 'sinatra' @@ -8,17 +9,16 @@ Sinatra ist eine DSL für die rasche Erstellung von Web Anwendungen in Ruby mit 'Hallo Welt!' end -Das gem installieren und starten: +Eingach via rubygems installieren und starten: gem install sinatra ruby -rubygems myapp.rb -Aufrufen unter: http://localhost:4567 +Die Seite kann nun unter http://localhost:4567 betrachtet werden. == Routen -In Sinatra ist eine Route eine HTTP Methode gebunden an ein URL Muster. -Jede Route besitzt einen Block: +In Sinatra ist eine Route definiert durch eine HTTP Methode und ein URL Muster. Jeder dieser Routen wird ein Ruby block zugeordnnet. get '/' do .. zeig etwas .. @@ -36,11 +36,11 @@ Jede Route besitzt einen Block: .. entferne etwas .. end -Die Routen werden in der Reihenfolge angesprochen, wie sie definiert sind. -Das erste Route-Muster das mit dem Request übereinstimmt wird ausgeführt. +Die Routen werden in der Reihenfolge durchlaufen, in der sie definiert sind. +Das erste Routemuster, das mit dem Request übereinstimmt, wird ausgeführt. -Die Muster der Routen können über benannte Parameter erreicht werden mit dem -params Hash: +Die Muster der Routen können benannte Parameter beinhalten, die über den +params Hash zugänglich gemacht werden: get '/hallo/:name' do # passt auf "GET /hallo/foo" und "GET /hallo/bar" @@ -48,13 +48,13 @@ Die Muster der Routen können über benannte Parameter erreicht werden mit dem "Hallo #{params[:name]}!" end -Oder auch über benannte Parameter: +Man kann auf diese auch mit Blockparametern zugreifen: get '/hallo/:name' do |n| "Hallo #{n}!" end -Routen-Muster können auch mit splat (oder Wildcards) Parametern über das +Routenmuster können auch mit Splat- oder Wildcardparametern über das params[:splat] Array angesprochen werden. get '/sag/*/zu/*' do @@ -67,56 +67,113 @@ Routen-Muster können auch mit splat (oder Wildcards) Parametern über das params[:splat] # => ["pfad/zu/datei", "xml"] end -Routen mit Regular Expressions: +Routen mit regulären Ausdrücken sind auch möglich: get %r{/hallo/([\w]+)} do "Hallo, #{params[:captures].first}!" end -Oder mit einem Block-Parameter: +Un auch hier kann man Blockparameter nutzen: get %r{/hallo/([\w]+)} do |c| "Hallo, #{c}!" end -Routen können eine Vielzahl von zutreffenden Bedingungen haben, möglich wäre -ein User Agent: +=== Bedingungen + +An Routen können eine Vielzahl von Bedingungen angehängt werden, die erfüllt +sein müssen, damit der Block ausgeführt wird. möglich wäre etwa eine +Einschränkung des User Agents: get '/foo', :agent => /Songbird (\d\.\d)[\d\/]*?/ do - "Du verwendest Songbird version #{params[:agent][0]}" + "Du verwendest Songbird Version #{params[:agent][0]}" end get '/foo' do # passt auf andere Browser end +Andere mitgelieferte Bedingungen sind +host_name+ und +provides+: + + get '/', :host_name => /^admin\./ do + "Adminbereich, Zugriff verweigert!" + end + + get '/', :provides => 'html' do + haml :index + end + + get '/', :provides => ['rss', 'atom', 'xml'] do + builder :feed + end + +Man kann auch relativ einfach eigene Bedingungen hinzufügen: + + set(:probability) { |value| condition { rand <= value } } + + get '/auto_gewinnen', :probability => 0.1 do + "Du hast gewonnen!" + end + + get '/auto_gewinnen' do + "Tut mir leid, verloren." + end + +=== Rückgabewerte + +Durch den Rückgabewert eines Routenblocks wird mindestens der Response Body +festgelegt, der an den HTTP Client, bzw die nächste Rack Middleware +weitergegeben wird. Im Normalfall handelt es sich hierbei um einen String, wie +in den vorrangehenden Beispielen zu sehen. Es werden alledings auch andere +Werte akzeptiert. + +Man kann jedes Object zurückgeben, bei dem es sich entweder um einenen validen +Rack-Rückgabewert, einen validen Rack-Body oder einen HTTP Status Code +handelt: + +* Ein Array mit drei Elementen: [Status (Fixnum), Headers (Hash), Response Body (hört auf #each)] +* Ein Array mit zwei Elementen: [Status (Fixnum), Response Body (hört auf #each)] +* Ein Objekt, das auf #each hört und den an diese Methode übergebenen Block nur mit Strings als Übergabewerte aufruft. +* Ein Fixnum, das den Status Code festlegt. + +Damit lässt sich relativ einfach Streaming implementieren: + + class Stream + def each + 100.times { |i| yield "#{i}\n" } + end + end + + get('/') { Stream.new } + == Statische Dateien -Statische Dateien werden vom Ordner ./public geliefert. Es ist -möglich einen anderen Ort zu definieren, wenn die :public Option -gesetzt wird: +Statische Dateien werden aus dem ./public Ordner ausgeliefert. Es ist +möglich einen anderen Ort zu definieren, indem man die :public Option +setzt: set :public, File.dirname(__FILE__) + '/static' -Anmerkung: Der public Ordner ist nicht über die URL aufrufbar. Die Datei -./public/css/style.css wird gefunden unter -http://example.com/css/style.css. +Zu beachten ist, dass der Ordnername public nicht Teil der URL ist. Die Datei +./public/css/style.css ist unter +http://example.com/css/style.css zu finden. -== Ansichten / Templates +== Views / Templates -Es wird davon ausgegangen das Templates sich im Ordner ./views -befinden. Um einen anderen Ordner zu definieren: +Standardmäßig wird davon ausgegangen, dass sich Templates sich im +./views Ordner befinden. Man kann jedoch einen anderen Ordner +festlegen: set :views, File.dirname(__FILE__) + '/templates' -Eine wichtige Sache die man sich merken sollte, ist das man immer um auf -Templates zu verweisen Symbole verwenden sollte, auch dann wenn sich ein -Template in einen Unterordner befindet (in diesen Fall :'subdir/template'). -Rendering-Methoden rendern jede Zeichenkette direkt. +Eine wichtige Sache, die man sich hierbei merken sollte, ist das man immer mit +Symbols auf Templates verweisen sollte, auch wenn sich ein Template in einen +Unterordner befindet (in diesen Fall :'subdir/template'). +Renderingmethoden rendern jeden String direkt. === Haml Templates -Die haml gem/Bibliothek wird benötigt um HAML Templates rendern zu können: +Das haml gem wird benötigt um HAML-Templates rendern zu können: ## haml muss eingebunden werden require 'haml' @@ -125,17 +182,17 @@ Die haml gem/Bibliothek wird benötigt um HAML Templates rendern zu können: haml :index end -gerendert wird ./views/index.haml. +Dieser Code rendert ./views/index.haml. {Hamls Optionen}[http://haml-lang.com/docs/yardoc/file.HAML_REFERENCE.html#options] -können global durch die Sinatra Konfiguration gesetzt werden, +können global durch die Sinatrakonfiguration gesetzt werden, siehe {Optionen und Konfiguration}[http://www.sinatrarb.com/configuration.html], und individuell überschrieben werden. - set :haml, {:format => :html5 } # Standard Haml Format ist :xhtml + set :haml, :format => :html5 # Standard Haml-Format ist :xhtml get '/' do - haml :index, :haml_options => {:format => :html4 } # überschrieben + haml :index, :format => :html4 # überschrieben end @@ -148,11 +205,11 @@ und individuell überschrieben werden. erb :index end -gerendert wird ./views/index.erb +Dieser Code rendert ./views/index.erb. === Erubis -Die erubis gem/Bibliothek wird benötigt um erubis Templates rendern zu können: +Das erubis gem wird benötigt um Erubis-Templates rendern zu können: ## erbubis muss eingebunden werden require 'erubis' @@ -161,11 +218,11 @@ Die erubis gem/Bibliothek wird benötigt um erubis Templates rendern zu können: erubis :index end -gerendert wird ./views/index.erubis +Dieser Code rendert ./views/index.erubis. === Builder Templates -Die buidler gem/Bibliothek wird benötigt um builder Templates rendern zu können: +Das buidler gem wird benötigt um Builder-Templates rendern zu können: ## builder muss eingebunden werden require 'builder' @@ -175,13 +232,13 @@ Die buidler gem/Bibliothek wird benötigt um builder Templates rendern zu könne builder :index end -gerendert wird ./views/index.builder. +Dieser Code rendert ./views/index.builder. === Sass Templates -Die sass gem/Bibliothek wird benötigt um sass Templates rendern zu können: +Das haml gem wird benötigt um SASS-Templates rendern zu können: - ## haml order sass müssen eingebunden werden + ## sass muss eingebunden werden require 'sass' get '/stylesheet.css' do @@ -189,53 +246,92 @@ Die sass gem/Bibliothek wird benötigt um sass Templates rendern zu können: sass :stylesheet end -gerendert wird ./views/stylesheet.sass. +Dieser Code rendert ./views/stylesheet.sass. {Sass Optionen}[http://sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html#options] -können global durch die Sinatra Konfiguration gesetzt werden, +können global durch die Sinatra-Konfiguration gesetzt werden, siehe {Optionen und Konfiguration}[http://www.sinatrarb.com/configuration.html], und individuell überschrieben werden. - set :sass, {:style => :compact } # Standard Sass style ist :nested + set :sass, :style => :compact # Standard Sass-Style ist :nested get '/stylesheet.css' do content_type 'text/css', :charset => 'utf-8' sass :stylesheet, :style => :expanded # überschrieben end +=== Scss Templates + +Das haml gem wird benötigt um SCSS-Templates rendern zu können: + + ## sass muss eingebunden werden + require 'sass' + + get '/stylesheet.css' do + content_type 'text/css', :charset => 'utf-8' + scss :stylesheet + end + +Dieser Code rendert ./views/stylesheet.scss. + +{Scss Optionen}[http://sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html#options] +können global durch die Sinatra-Konfiguration gesetzt werden, +siehe {Optionen und Konfiguration}[http://www.sinatrarb.com/configuration.html], +und individuell überschrieben werden. + + set :scss, :style => :compact # Standard Scss-Style ist :nested + + get '/stylesheet.css' do + content_type 'text/css', :charset => 'utf-8' + scss :stylesheet, :style => :expanded # überschrieben + end + +=== Less Templates + +Das less gem wird benötigt um Less-Templates rendern zu können: + + ## less muss eingebunden werden + require 'less' + + get '/stylesheet.css' do + content_type 'text/css', :charset => 'utf-8' + less :stylesheet + end + +Dieser Code rendert ./views/stylesheet.less. + === Inline Templates get '/' do haml '%div.title Hallo Welt' end -Rendert die inline Template Zeichenkette. +Rendert den Inline Template String. === Auf Variablen in Templates zugreifen -Templates werden im selben Kontext ausgewertet wie Routen. Instanz Variablen in -Routen sind auch direkt im Template ansprechbar: +Templates werden im selben Kontext ausgeführt wie Routen. Instanzvariablen in +Routen sind auch direkt im Template verfügbar: get '/:id' do @foo = Foo.find(params[:id]) haml '%h1= @foo.name' end -Oder durch ein explizites Hash von lokalen Variablen: +Oder durch einen expliziten Hash von lokalen Variablen: get '/:id' do foo = Foo.find(params[:id]) haml '%h1= foo.name', :locals => { :foo => foo } end -Wird typischerweise bei Verwendung von Subtemplates (partials) in anderen +Dies wird typischerweise bei Verwendung von Subtemplates (partials) in anderen Templates eingesetzt. -=== Inline Templates +=== Inline-Templates -Templates können am Ende der Datei definiert werden: +Templates können auch am Ende der Datei definiert werden: - require 'rubygems' require 'sinatra' get '/' do @@ -251,13 +347,14 @@ Templates können am Ende der Datei definiert werden: @@ index %div.title Hallo Welt!!!!! -Anmerkung: Inline Templates die in der Quelldatei definiert sind die Sinatra -braucht werden automatisch geladen. Um andere Inline Templates in anderen -Quelldateien aufzurufen muss `enable :inline_templates` explizit verwendet werden. +Anmerkung: Inline-Templates die in der Datei definiert sind die require +'sinatra' aufruft werden automatisch geladen. Um andere Inline-Templates +in anderen Dateien aufzurufen muss enable :inline_templates explizit +verwendet werden. === Benannte Templates -Templates können auch mit der top-level template Methode definiert +Templates können auch mit der Top-Level template-Methode definiert werden: template :layout do @@ -281,8 +378,8 @@ verwendet. Durch :layout => false kann das Ausführen verhindert werden == Helfer -Am top-level werden durch die helpers Methode, Helfer Methoden -definiert bevor die Routen und Templates definiert werden: +Durch die Top-Level helpers-Methode, Helfer-Methoden +definiert, die in Routen und Templates verwendet werden können: helpers do def bar(name) @@ -296,9 +393,9 @@ definiert bevor die Routen und Templates definiert werden: == Filter -"Before" Filter werden immer vor jedem Request ausgeführt. Der Request kann so -Request und Antwort ändern. Gesetzte Instanz Variablen in Filtern können in -Routen und Templates verwendet werden: +Before-Filter werden immer vor jedem Request in dem selben Kontext wie danach +die Routen ausgeführt. So kann man etwa Request und Antwort ändern. Gesetzte +Instanzvariablen in Filtern können in Routen und Templates verwendet werden: before do @note = 'Hi!' @@ -310,46 +407,56 @@ Routen und Templates verwendet werden: params[:splat] #=> 'bar/baz' end -"After" Filter werden nach jedem Request ausgeführt, können auch Request und -Antwort ändern. Gesetzte Instanz Variablen in before Filtern können in after Filter -verwendet werden: +After-Filter werden nach jedem Request im selben Kontext ausgeführt, und +können ebenfalls Request und Antwort ändern. In Before-Filtern gesetzte +Instanzvariablen können in After-Filterm verwendet werden: after do puts response.status end +Filter können optional auch mit einem Pattern ausgestattet werden, welches auf den Request-Pfad passen müssen, damit der Filter ausgeführt wird: + + before '/protected/*' do + authenticate! + end + + after '/create/:slug' do |slug| + session[:last_slug] = slug + end + == Anhalten Zum sofortigen stoppen eines Request in einen Filter oder einer Route: halt -Der Status kann beim stoppen auch angegeben werden ... +Der Status kann beim stoppen auch angegeben werden: halt 410 -Oder den Rumpf ... +Oder auch den Response-Body: - halt 'Hier steht der Rumpf' + halt 'Hier steht der Body' -Oder beides ... +Oder beides: halt 401, 'verschwinde!' -Mit Headers ... +Sogar mit Headers: halt 402, {'Content-Type' => 'text/plain'}, 'Rache' == Weiterspringen -Eine Route kann mittels pass zu der nächsten treffenden Route springen: +Eine Route kann mittels pass zu der nächsten passenden Route springen: - get '/rate/:wer' do - pass unless params[:er] == 'Frank' + get '/raten/:wer' do + pass unless params[:wer] == 'Frank' 'Du hast mich!' end - get '/rate/*' do + get '/raten/*' do 'Du hast mich verfehlt!' end @@ -359,46 +466,45 @@ gefunden wird. == Konfiguration -Lauft einmal beim starten in jeder Umgebung: +Wird einmal beim Starten in jedweder Umgebung ausgeführt: configure do ... end -Lauft nur wenn die Umgebung (RACK_ENV environment variable) auf :production -gesetzt ist: +Läuft nurm wenn die Umgebung (RACK_ENV Umgebungsvariable) auf +:production gesetzt ist: configure :production do ... end -Lauft nur wenn die Umgebung auf :production oder auf :test +Läuft nur wenn die Umgebung auf :production oder auf :test gesetzt ist: configure :production, :test do ... end -== Fehler-Behandlung +== Fehlerbehandlung -Error Handler laufen im selben Kontext wie Routen und before Filter, was bedeutet -es können alle Goodies wie haml, erb, halt, etc. -verwendet werden. +Error Handler laufen im selben Kontext wie Routen und Filter, was bedeutet, +dass alle Goodies wie haml, erb, halt, etc. +verwendet werden können. === Nicht gefunden -Wenn eine Sinatra::NotFound Exception geworfen wird oder ein Statuscode -ist 404, wird der not_found Handler ausgeführt: +Wenn eine Sinatra::NotFound Exception geworfen wird oder der Statuscode 404ist, wird der not_found Handler ausgeführt: not_found do - 'Das ist nirgendwo' + 'Kann nirgendwo gefunden werden.' end === Fehler Der +error+ Handler wird immer ausgeführt wenn eine Exception in einem Routenblock -oder in einen Filter geworfen wurde. Das Exception Objekt kann über die -sinatra.error Rack Variable angesprochen werden: +oder in einen Filter geworfen wurde. Doe Exception kann über die +sinatra.error Rack-Variable angesprochen werden: error do 'Entschuldige es gab einen hässlichen Fehler - ' + env['sinatra.error'].name @@ -420,7 +526,7 @@ Bekommt man dieses: Was passiert ist... etwas schlechtes -Alternativ kann ein Fehler Handler vor dem Status Code definiert werden: +Alternativ kann ein Error Handler auch für Statuscode definiert werden: error 403 do 'Zugriff verboten' @@ -430,37 +536,36 @@ Alternativ kann ein Fehler Handler vor dem Status Code definiert werden: 403 end -Oder ein Bereich: +Oder ein Statuscode-Bereich: error 400..510 do 'Boom' end -Sinatra verwendet verschiedene not_found und error +Sinatra setzt verschiedene not_found und error Handler in der Development Umgebung. -== Mime Typen +== Mime-Types -Wenn mit send_file oder statische Dateien verwendet wird, kann es -sein das Sinatra den Mime-Typ nicht versteht. Registriert wird mit +mime_type+ -per Datei Endung: +Wenn send_file oder statische Dateien verwendet werden, kann es +vorkommen, dass Sinatra den Mime-Typ nicht kennt. Registriert wird dieser mit ++mime_type+ per Dateiendung: mime_type :foo, 'text/foo' -Es kann auch der +content_type+ Helfer verwendet werden: +Es kann aber auch der +content_type+ Helfer verwendet werden: content_type :foo == Rack Middleware -Sinatra baut auf Rack[http://rack.rubyforge.org/], einen minimalen Standard -Interface für Ruby Web Frameworks. Eines der am meisten interessantesten -Fähigkeiten für Entwickler ist der Support von "Middleware" Komponenten die -zwischen dem Server und der Anwendung überwacht laufen und/oder den HTTP -Request/Antwort manipulieren können. +Sinatra baut auf Rack[http://rack.rubyforge.org/], einen minimales Standardinterface für Ruby Webframeworks. Eines der ainteressantesten +Features für Entwickler ist der Support von Middleware, die +zwischen den Server und die Anwendung geschaltet wird und so HTTP +Request und/order Antwort überwachen und/oder manipulieren kann. -Sinatra macht das bauen von Rack middleware pipelines sicher via der top-level -+use+ Methode: +Sinatra macht das erstellen von Middleware-Verkettungen mit der Top-Level +Methode +use+ zu einem Kinderspiel: require 'sinatra' require 'my_custom_middleware' @@ -472,24 +577,24 @@ Sinatra macht das bauen von Rack middleware pipelines sicher via der top-level 'Hallo Welt' end -Die Semantik von +use+ sind identisch mit den Definierten von +Die Semantik von +use+ mit identisch mit der gleichnamigen Methode der Rack::Builder[http://rack.rubyforge.org/doc/classes/Rack/Builder.html] DSL -(meistens verwendet von rackup Dateien). Als Beispiel, die +use+ Methode -akzeptiert mehrere/verschiedene Argumente genauso wie Blöcke: +(meist verwendet in Rackup-Dateien). Ein Beispiel dafür ist, dass die ++use+-Methode mehrere/verschiedene Argumente und auch Blöcke entgegen nimmt: use Rack::Auth::Basic do |username, password| username == 'admin' && password == 'secret' end -Rack hat eine Vielzahl von Standard Middleware Komponenten für Logging, Debugging, -URL Routing, Authentifizierung und Session Verarbeitung. -Sinatra verwendet viele von diesen Komponenten automatisch passierend auf der -Konfiguration, so muss man nicht +use+ explizit verwenden. +Rack bietet eine Vielzahl von Standard Middleware für Logging, Debugging, +URL-Routing, Authentifizierung und Session-Verarbeitung. +Sinatra verwendet viele von diesen Komponenten automatisch, abhängig von der +Konfiguration, so muss man häufig +use+ nicht explizit verwenden. == Testen -Sinatra Tests können mit dem Rack-basierten Test Framework geschrieben werden. -{Rack::Test}[http://gitrdoc.com/brynary/rack-test] wird empfohlen: +Sinatra Tests können mit jedem auf Rack aufbauendem Test Framework geschrieben +werden. {Rack::Test}[http://gitrdoc.com/brynary/rack-test] wird empfohlen: require 'my_sinatra_app' require 'rack/test' @@ -518,17 +623,16 @@ Sinatra Tests können mit dem Rack-basierten Test Framework geschrieben werden. end Anmerkung: Das eingebaute Sinatra::Test Modul und die Sinatra::TestHarness -Klasse werden nach Version 0.9.2 nicht mehr unterstützt. +Klasse werden seit Version 0.9.2 nicht mehr unterstützt. == Sinatra::Base - Middleware, Bibliotheken, und modulare Anwendungen -Die Definition einer Anwendung vom top-level weg funktioniert gut für -Micro-Anwendungen hat aber Nachteile wenn man wiederverwendbare Komponenten -wie Rack Middleware, Rails metal, einfache Bibliotheken mit Server Komponenten +Das Definitieren einer Top-Level Anwendung funktioniert gut für +Microanwendungen, hat aber Nachteile wenn man wiederverwendbare Komponenten +wie Middleware, Rails Metal, einfache Bibliotheken mit Server Komponenten oder auch Sinatra Erweiterungen bauen will. -Das top-level DSL belastet den Objekt-Namespace und setzt einen Style einer -Micro-Anwendung voraus (ein einzelne Anwendungs-Datei, ./public und ./views -Ordner, Logging, Exception Detail Seite, etc). Genau hier kommt Sinatra::Base +Die Top-Level DSL belastet den Objekt-Namespace und setzt einen Microanwendungsstil voraus (ein einzelne Anwendungsdatei, ./public und ./views +Ordner, Logging, Exception-Detail-Seite, usw). Genau hier kommt Sinatra::Base in das Spiel: require 'sinatra/base' @@ -542,39 +646,37 @@ in das Spiel: end end -Die MyApp Klasse ist eine unabhängige Rack Komponente die als Rack Middleware, -Rack Anwendung oder als Rails metal verwendet werden kann. -Verwendet wird sie mit +use+ oder +run+ von einer rackup +config.ru+ Datei oder -als Server Komponente als Bibliothek: +Die MyApp-Klasse ist eine unabhängige Rack-Komponente die als Middleware, +Endpunkt oder via Rails Metal verwendet werden kann. Verwendet wird sie durch ++use+ oder +run+ von einer Rackup +config.ru+ Datei oder als Serverkomponente +einer Bibliothek: MyApp.run! :host => 'localhost', :port => 9090 -Die Methoden von der Sinatra::Base Subklasse sind genau die selben wie die -über die top-level DSL möglichen. Die meisten top-level Anwendungen können zu -Sinatra::Base Komponenten mit 2 Veränderungen konvertiert werden: +Die Methoden der Sinatra::Base-Subklasse sind genau die selben wie die +der Top-Level DSL. Die meisten Top-Level Anwendungen können mit nur zwei Veränderungen zu Sinatra::Base-Komponenten konvertiert werden: -* Die Datei sollte +sinatra/base+ und nicht aus +sinatra+; importieren - ansonsten werden alle von Sinatra's DSL Methoden im Namespace importiert. -* Alle Anwendungs Routen, Error Handler, Filter und Optionen in eine SubKlasse - von Sinatra::Base. +* Die Datei sollte require 'sinatra/base' anstelle von + require 'sinatra/base' aufrufen, ansonsten werden alle von + Sinatra's DSL Methoden in den Top-Level- Namespace importiert. +* Alle Routen, Error Handler, Filter und Optionen der Applikation müssen in + einer Subklass von Sinatra::Base definiert werden. -+Sinatra::Base+ ist ein leeres Blatt. Die meisten Optionen sind per Standard -deaktiviert, das betrifft auch dem eingebauten Server. Siehe {Optionen und Konfiguration}[http://sinatra.github.com/configuration.html] für Details an möglichen Optionen. ++Sinatra::Base+ ist ein unbeschriebense Blatt. Die meisten Optionen sind per default deaktiviert, das betrifft auch den eingebauten Server. Siehe {Optionen und Konfiguration}[http://sinatra.github.com/configuration.html] für Details über möglichen Optionen. -SIDEBAR: Sinatras top-level DSL ist implementiert als einfaches Delegations -System. Die +Sinatra::Application+ Klasse -- ein paar spezielle SubKlassen von -Sinatra::Base -- enthält alle :get, :put, :post, :delete, :before, -:error, :not_found, :configure und :set Meldungen gesendet durch den top-level. -Schaue am besten im Code nach: hier im {Sinatra::Delegator mixin}[http://github.com/sinatra/sinatra/blob/master/lib/sinatra/base.rb#L1064] - {inkludieren im Haupt-Namespace}[http://github.com/sinatra/sinatra/blob/master/lib/sinatra/main.rb#L25]. +SIDEBAR: Sinatras Top-Level DSL-Methoden sind als einfache Delegationen +implementiert. Die Sinatra::Application-Klasse -- eine spezielle Subklasse von +Sinatra::Base -- erhält alle :get, :put, :post, :delete, :before, :error, +:not_found, :configure und :set Meldungen die vom Top-Level aus gesendet +werden. Schau am besten im Code nach: Hier ist {Sinatra::Delegator mixin}[http://github.com/sinatra/sinatra/blob/master/lib/sinatra/base.rb#L1064] definiert und wird in den {globalen Namespace eingebunden}[http://github.com/sinatra/sinatra/blob/master/lib/sinatra/main.rb#L25]. == Kommandozeile -Sinatra Anwendungen können gestartet werden: +Sinatra Anwendungen können direkt gestartet werden: ruby myapp.rb [-h] [-x] [-e ENVIRONMENT] [-p PORT] [-h HOST] [-s HANDLER] -Optionen sind: +Die Optionen sind: -h # Hilfe -p # Den Port setzen (Standard ist 4567) @@ -583,17 +685,17 @@ Optionen sind: -s # rack server/handler setzen (Standard ist thin) -x # mutex lock einschalten (Standard ist off) -== Am neuesten Stand (The Bleeding Edge) +== Der neueste Stand (The Bleeding Edge) -Um auf den neuesten Stand von Sinatras Code zu sein, kann ein lokaler Clone +Um auf den neuesten Stand von Sinatras Code zu sein, kann eine lokale Kopie angelegt werden. Gestartet wird in der Anwendung mit dem sinatra/lib -Ordner und dem LOAD_PATH: +Ordner im LOAD_PATH: cd myapp git clone git://github.com/sinatra/sinatra.git ruby -Isinatra/lib myapp.rb -Alternativ kann sinatra/lib Ordner zum LOAD_PATH in +Alternativ kann der sinatra/lib Ordner zum LOAD_PATH in der Anwendung hinzugefügt werden: $LOAD_PATH.unshift File.dirname(__FILE__) + '/sinatra/lib' @@ -604,7 +706,7 @@ der Anwendung hinzugefügt werden: "Ich laufe mit Version " + Sinatra::VERSION end -Um Sinatra Code in Zukunft zu aktualisieren: +Um Sinatra Code in der Zukunft zu aktualisieren: cd myproject/sinatra git pull @@ -613,8 +715,8 @@ Um Sinatra Code in Zukunft zu aktualisieren: * {Projekt Website}[http://sinatra.github.com/] - Ergänzende Dokumentation, News und Links zu anderen Ressourcen. -* {helfen}[http://sinatra.github.com/contributing.html] - Einen Fehler gefunden? Brauchst du Hilfe? Hast du einen Patch? -* {Issue tracker}[http://github.com/sinatra/sinatra/issues] +* {Hilfe beisteuern}[http://sinatra.github.com/contributing.html] - Einen Fehler gefunden? Brauchst du Hilfe? Hast du einen Patch? +* {Issue Tracker}[http://github.com/sinatra/sinatra/issues] * {Twitter}[http://twitter.com/sinatra] -* {Mailing List}[http://groups.google.com/group/sinatrarb] +* {Mailingliste}[http://groups.google.com/group/sinatrarb] * {IRC: #sinatra}[irc://chat.freenode.net/#sinatra] auf http://freenode.net From e51bb77bd04c7e8013d70ad200bee2d671cb35cb Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Fri, 10 Sep 2010 16:41:34 +0200 Subject: [PATCH 36/80] Update CHANGES. --- CHANGES | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index de7cdd3da3..0d67f5c5f6 100644 --- a/CHANGES +++ b/CHANGES @@ -33,12 +33,16 @@ * Added ability to handle weighted HTTP_ACCEPT headers. (Davide D'Agostino) - * README is now available in German. (Bernhard Essl) + * README is now available in German. (Bernhard Essl, Konstantin Haase) * This release is compatible with Ruby 1.9.2. Sinatra was trying to read none existent files Ruby added to the call stack. (Shota Fukumori, Konstantin Haase) + * Better handling of Encodings in 1.9, defaults encoding to UTF-8 and + respects Encoding.default_internal and Encoding.default_external. + (Konstantin Haase) + * This release is now usable in combination with Rails 3. When mounting a Sinatra application under a subpath in Rails 3, the PATH_INFO is not prefixed with a slash and no routes did match. (José Valim) From fa15c4d4eead42ae7c315a5d12027e051d66c363 Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Fri, 10 Sep 2010 16:42:40 +0200 Subject: [PATCH 37/80] Readjust latest changelog entries. --- CHANGES | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/CHANGES b/CHANGES index 0d67f5c5f6..f77d6db42a 100644 --- a/CHANGES +++ b/CHANGES @@ -1,18 +1,19 @@ = 1.1 / Not Yet Released - * Before and after filters now support pattern matching, including the ability - to use captures: "before('/user/:name') { |name| ... }". This avoids manual - path checking. No performance loss if patterns are avoided. (Konstantin Haase) + * Before and after filters now support pattern matching, including the + ability to use captures: "before('/user/:name') { |name| ... }". This + avoids manual path checking. No performance loss if patterns are avoided. + (Konstantin Haase) - * Setting multiple values now no longer relies on #to_hash and therefore accepts - any Enumerable as parameter. (Simon Rozet) + * Setting multiple values now no longer relies on #to_hash and therefore + accepts any Enumerable as parameter. (Simon Rozet) * It is now possible to access Sinatra's template_cache from the outside. (Nick Sutterer) - * It is now possible to render SCSS files with the `scss` method, which behaves - exactly like `sass` except for the different file extension and assuming the - SCSS syntax. (Pedro Menezes, Konstantin Haase) + * It is now possible to render SCSS files with the `scss` method, which + behaves exactly like `sass` except for the different file extension and + assuming the SCSS syntax. (Pedro Menezes, Konstantin Haase) * Broken examples for using Erubis and Haml in README have been fixed. (Nick Sutterer, Doug Ireton, Jason Stewart) @@ -20,16 +21,16 @@ * Sinatra is now able to use Tilt versions including numbers > 9, as in 0.10. (Konstantin Haase) - * The `last_modified` method now also accepts DateTime instances and makes sure - the header will always be set to a string. (Konstantin Haase) + * The `last_modified` method now also accepts DateTime instances and makes + sure the header will always be set to a string. (Konstantin Haase) - * `send_file` now always respects the `:type` option if set. Previously it was - discarded if no matching mime type was found, which made it impossible to - directly pass a mime type. (Konstantin Haase) + * `send_file` now always respects the `:type` option if set. Previously it + was discarded if no matching mime type was found, which made it impossible + to directly pass a mime type. (Konstantin Haase) - * `redirect` always redirects to an absolute URI, even if a relative URI was passed. - Ensures compatibility with RFC 2616 section 14.30. - (Jean-Philippe Garcia Ballester, Anthony Williams) + * `redirect` always redirects to an absolute URI, even if a relative URI was + passed. Ensures compatibility with RFC 2616 section 14.30. (Jean-Philippe + Garcia Ballester, Anthony Williams) * Added ability to handle weighted HTTP_ACCEPT headers. (Davide D'Agostino) @@ -58,10 +59,10 @@ * You can now use #settings from class level for convenience. (Konstantin Haase) - * Prevents a memory leak on 1.8.6 is production mode. Note, however, that this - is due to a bug in 1.8.6 and request will have the additional overhead of - parsing templates again on that version. It is recommended to use at least - Ruby 1.8.7. (Konstantin Haase) + * Prevents a memory leak on 1.8.6 is production mode. Note, however, that + this is due to a bug in 1.8.6 and request will have the additional overhead + of parsing templates again on that version. It is recommended to use at + least Ruby 1.8.7. (Konstantin Haase) * Lighthouse has been dropped in favor of GitHub issues. From 5a5b9e5ae8ef7ee66f1290db61d719580eafbb2d Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Fri, 10 Sep 2010 16:46:39 +0200 Subject: [PATCH 38/80] Update year in LICENSE --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 145fdff527..7e38bc494c 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2007, 2008, 2009 Blake Mizerany +Copyright (c) 2007, 2008, 2009, 2010 Blake Mizerany Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation From 222d2cb6d9d5b0bc8b2dfbd6965aaace7cecfb8e Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Fri, 10 Sep 2010 16:49:25 +0200 Subject: [PATCH 39/80] Add German and Japanese README to RDoc files in gemspec. --- sinatra.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sinatra.gemspec b/sinatra.gemspec index 6ee84a7101..ce11d0b2e5 100644 --- a/sinatra.gemspec +++ b/sinatra.gemspec @@ -77,7 +77,7 @@ Gem::Specification.new do |s| s.test_files = s.files.select {|path| path =~ /^test\/.*_test.rb/} - s.extra_rdoc_files = %w[README.rdoc LICENSE] + s.extra_rdoc_files = %w[README.rdoc README.de.rdoc README.jp.rdoc LICENSE] s.add_dependency 'rack', '>= 1.0' s.add_development_dependency 'shotgun', '>= 0.6', '< 1.0' s.add_development_dependency 'rack-test', '>= 0.3.0' From ea1426f38b46ee8bfeac17d9b38bfb29abe7aef4 Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Fri, 10 Sep 2010 18:21:30 +0200 Subject: [PATCH 40/80] Proper encoding test. --- test/encoding_test.rb | 18 ++++++++++++++++++ test/settings_test.rb | 8 -------- test/views/utf8.haml | 2 ++ 3 files changed, 20 insertions(+), 8 deletions(-) create mode 100644 test/encoding_test.rb create mode 100644 test/views/utf8.haml diff --git a/test/encoding_test.rb b/test/encoding_test.rb new file mode 100644 index 0000000000..b0dfec5c70 --- /dev/null +++ b/test/encoding_test.rb @@ -0,0 +1,18 @@ +# encoding: UTF-8 +require File.dirname(__FILE__) + '/helper' + +class BaseTest < Test::Unit::TestCase + setup do + @base = Sinatra.new(Sinatra::Base) + @base.set :views, File.dirname(__FILE__) + "/views" + end + + it 'allows unicode strings in ascii templates per default (1.9)' do + @base.new.haml(:ascii, {}, :value => "åkej") + end + + it 'allows ascii strings in unicode templates per default (1.9)' do + next unless defined? Encoding + @base.new.haml(:utf8, {}, :value => "Some Lyrics".encode("ASCII")) + end +end \ No newline at end of file diff --git a/test/settings_test.rb b/test/settings_test.rb index 4b640ff398..821376c2eb 100644 --- a/test/settings_test.rb +++ b/test/settings_test.rb @@ -1,4 +1,3 @@ -# encoding: UTF-8 require File.dirname(__FILE__) + '/helper' class SettingsTest < Test::Unit::TestCase @@ -375,11 +374,4 @@ def foo=(value) assert ! @application.lock? end end - - describe 'default_encoding' do - it 'allows unicode strings in ascii templates per default (1.9)' do - @base.set :views, File.dirname(__FILE__) + "/views" - @base.new.haml(:ascii, {}, :value => "åkej") - end - end end diff --git a/test/views/utf8.haml b/test/views/utf8.haml new file mode 100644 index 0000000000..16fac31dd2 --- /dev/null +++ b/test/views/utf8.haml @@ -0,0 +1,2 @@ +%h1= value +Ingen vill veta var du köpt din tröja. From 99680c7f88613a544019d70a24217ee5ba26d02a Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Fri, 10 Sep 2010 21:34:30 +0200 Subject: [PATCH 41/80] unbundle Tilt --- lib/sinatra/base.rb | 14 +- lib/sinatra/tilt.rb | 772 -------------------------------------------- sinatra.gemspec | 3 +- 3 files changed, 3 insertions(+), 786 deletions(-) delete mode 100644 lib/sinatra/tilt.rb diff --git a/lib/sinatra/base.rb b/lib/sinatra/base.rb index e2f8c3b8da..3ec9c5922a 100644 --- a/lib/sinatra/base.rb +++ b/lib/sinatra/base.rb @@ -4,19 +4,7 @@ require 'rack' require 'rack/builder' require 'sinatra/showexceptions' - -# require tilt if available; fall back on bundled version. -begin - require 'tilt' - if (Tilt::VERSION.split('.').map { |e| e.to_i } <=> [1,0,2]) < 0 - warn "WARN: sinatra requires tilt >= 1.0.2; you have #{Tilt::VERSION}. " + - "loading bundled version..." - Object.send :remove_const, :Tilt - raise LoadError - end -rescue LoadError - require 'sinatra/tilt' -end +require 'tilt' module Sinatra VERSION = '1.1.0' diff --git a/lib/sinatra/tilt.rb b/lib/sinatra/tilt.rb deleted file mode 100644 index c4668bfd6f..0000000000 --- a/lib/sinatra/tilt.rb +++ /dev/null @@ -1,772 +0,0 @@ -require 'digest/md5' - -module Tilt - VERSION = '1.0.1' - - @template_mappings = {} - - # Hash of template path pattern => template implementation class mappings. - def self.mappings - @template_mappings - end - - # Register a template implementation by file extension. - def self.register(ext, template_class) - ext = ext.to_s.sub(/^\./, '') - mappings[ext.downcase] = template_class - end - - # Returns true when a template exists on an exact match of the provided file extension - def self.registered?(ext) - mappings.key?(ext.downcase) - end - - # Create a new template for the given file using the file's extension - # to determine the the template mapping. - def self.new(file, line=nil, options={}, &block) - if template_class = self[file] - template_class.new(file, line, options, &block) - else - fail "No template engine registered for #{File.basename(file)}" - end - end - - # Lookup a template class for the given filename or file - # extension. Return nil when no implementation is found. - def self.[](file) - pattern = file.to_s.downcase - unless registered?(pattern) - pattern = File.basename(pattern) - pattern.sub!(/^[^.]*\.?/, '') until (pattern.empty? || registered?(pattern)) - end - @template_mappings[pattern] - end - - # Mixin allowing template compilation on scope objects. - # - # Including this module in scope objects passed to Template#render - # causes template source to be compiled to methods the first time they're - # used. This can yield significant (5x-10x) performance increases for - # templates that support it (ERB, Erubis, Builder). - # - # It's also possible (though not recommended) to include this module in - # Object to enable template compilation globally. The downside is that - # the template methods will polute the global namespace and could lead to - # unexpected behavior. - module CompileSite - def __tilt__ - end - end - - # Base class for template implementations. Subclasses must implement - # the #prepare method and one of the #evaluate or #precompiled_template - # methods. - class Template - # Template source; loaded from a file or given directly. - attr_reader :data - - # The name of the file where the template data was loaded from. - attr_reader :file - - # The line number in #file where template data was loaded from. - attr_reader :line - - # A Hash of template engine specific options. This is passed directly - # to the underlying engine and is not used by the generic template - # interface. - attr_reader :options - - # Used to determine if this class's initialize_engine method has - # been called yet. - @engine_initialized = false - class << self - attr_accessor :engine_initialized - alias engine_initialized? engine_initialized - end - - # Create a new template with the file, line, and options specified. By - # default, template data is read from the file. When a block is given, - # it should read template data and return as a String. When file is nil, - # a block is required. - # - # All arguments are optional. - def initialize(file=nil, line=1, options={}, &block) - @file, @line, @options = nil, 1, {} - - [options, line, file].compact.each do |arg| - case - when arg.respond_to?(:to_str) ; @file = arg.to_str - when arg.respond_to?(:to_int) ; @line = arg.to_int - when arg.respond_to?(:to_hash) ; @options = arg.to_hash.dup - else raise TypeError - end - end - - raise ArgumentError, "file or block required" if (@file || block).nil? - - # call the initialize_engine method if this is the very first time - # an instance of this class has been created. - if !self.class.engine_initialized? - initialize_engine - self.class.engine_initialized = true - end - - # used to generate unique method names for template compilation - @stamp = (Time.now.to_f * 10000).to_i - @compiled_method_names = {} - - # load template data and prepare - @reader = block || lambda { |t| File.read(@file) } - @data = @reader.call(self) - prepare - end - - # Render the template in the given scope with the locals specified. If a - # block is given, it is typically available within the template via - # +yield+. - def render(scope=Object.new, locals={}, &block) - evaluate scope, locals || {}, &block - end - - # The basename of the template file. - def basename(suffix='') - File.basename(file, suffix) if file - end - - # The template file's basename with all extensions chomped off. - def name - basename.split('.', 2).first if basename - end - - # The filename used in backtraces to describe the template. - def eval_file - file || '(__TEMPLATE__)' - end - - protected - # Called once and only once for each template subclass the first time - # the template class is initialized. This should be used to require the - # underlying template library and perform any initial setup. - def initialize_engine - end - - # Like Kernel::require but issues a warning urging a manual require when - # running under a threaded environment. - def require_template_library(name) - if Thread.list.size > 1 - warn "WARN: tilt autoloading '#{name}' in a non thread-safe way; " + - "explicit require '#{name}' suggested." - end - require name - end - - # Do whatever preparation is necessary to setup the underlying template - # engine. Called immediately after template data is loaded. Instance - # variables set in this method are available when #evaluate is called. - # - # Subclasses must provide an implementation of this method. - def prepare - if respond_to?(:compile!) - # backward compat with tilt < 0.6; just in case - warn 'Tilt::Template#compile! is deprecated; implement #prepare instead.' - compile! - else - raise NotImplementedError - end - end - - # Process the template and return the result. When the scope mixes in - # the Tilt::CompileSite module, the template is compiled to a method and - # reused given identical locals keys. When the scope object - # does not mix in the CompileSite module, the template source is - # evaluated with instance_eval. In any case, template executation - # is guaranteed to be performed in the scope object with the locals - # specified and with support for yielding to the block. - def evaluate(scope, locals, &block) - if scope.respond_to?(:__tilt__) - method_name = compiled_method_name(locals.keys) - if scope.respond_to?(method_name) - scope.send(method_name, locals, &block) - else - compile_template_method(method_name, locals) - scope.send(method_name, locals, &block) - end - else - evaluate_source(scope, locals, &block) - end - end - - # Generates all template source by combining the preamble, template, and - # postamble and returns a two-tuple of the form: [source, offset], where - # source is the string containing (Ruby) source code for the template and - # offset is the integer line offset where line reporting should begin. - # - # Template subclasses may override this method when they need complete - # control over source generation or want to adjust the default line - # offset. In most cases, overriding the #precompiled_template method is - # easier and more appropriate. - def precompiled(locals) - preamble = precompiled_preamble(locals) - parts = [ - preamble, - precompiled_template(locals), - precompiled_postamble(locals) - ] - [parts.join("\n"), preamble.count("\n") + 1] - end - - # A string containing the (Ruby) source code for the template. The - # default Template#evaluate implementation requires either this method - # or the #precompiled method be overridden. When defined, the base - # Template guarantees correct file/line handling, locals support, custom - # scopes, and support for template compilation when the scope object - # allows it. - def precompiled_template(locals) - raise NotImplementedError - end - - # Generates preamble code for initializing template state, and performing - # locals assignment. The default implementation performs locals - # assignment only. Lines included in the preamble are subtracted from the - # source line offset, so adding code to the preamble does not effect line - # reporting in Kernel::caller and backtraces. - def precompiled_preamble(locals) - locals.map { |k,v| "#{k} = locals[:#{k}]" }.join("\n") - end - - # Generates postamble code for the precompiled template source. The - # string returned from this method is appended to the precompiled - # template source. - def precompiled_postamble(locals) - '' - end - - # The unique compiled method name for the locals keys provided. - def compiled_method_name(locals_keys) - @compiled_method_names[locals_keys] ||= - generate_compiled_method_name(locals_keys) - end - - private - # Evaluate the template source in the context of the scope object. - def evaluate_source(scope, locals, &block) - source, offset = precompiled(locals) - scope.instance_eval(source, eval_file, line - offset) - end - - # JRuby doesn't allow Object#instance_eval to yield to the block it's - # closed over. This is by design and (ostensibly) something that will - # change in MRI, though no current MRI version tested (1.8.6 - 1.9.2) - # exhibits the behavior. More info here: - # - # http://jira.codehaus.org/browse/JRUBY-2599 - # - # Additionally, JRuby's eval line reporting is off by one compared to - # all MRI versions tested. - # - # We redefine evaluate_source to work around both issues. - if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby' - undef evaluate_source - def evaluate_source(scope, locals, &block) - source, offset = precompiled(locals) - file, lineno = eval_file, (line - offset) - 1 - scope.instance_eval { Kernel::eval(source, binding, file, lineno) } - end - end - - def generate_compiled_method_name(locals_keys) - parts = [object_id, @stamp] + locals_keys.map { |k| k.to_s }.sort - digest = Digest::MD5.hexdigest(parts.join(':')) - "__tilt_#{digest}" - end - - def compile_template_method(method_name, locals) - source, offset = precompiled(locals) - offset += 5 - CompileSite.class_eval <<-RUBY, eval_file, line - offset - def #{method_name}(locals) - Thread.current[:tilt_vars] = [self, locals] - class << self - this, locals = Thread.current[:tilt_vars] - this.instance_eval do - #{source} - end - end - end - RUBY - ObjectSpace.define_finalizer self, - Template.compiled_template_method_remover(CompileSite, method_name) - end - - def self.compiled_template_method_remover(site, method_name) - proc { |oid| garbage_collect_compiled_template_method(site, method_name) } - end - - def self.garbage_collect_compiled_template_method(site, method_name) - site.module_eval do - begin - remove_method(method_name) - rescue NameError - # method was already removed (ruby >= 1.9) - end - end - end - end - - # Extremely simple template cache implementation. Calling applications - # create a Tilt::Cache instance and use #fetch with any set of hashable - # arguments (such as those to Tilt.new): - # cache = Tilt::Cache.new - # cache.fetch(path, line, options) { Tilt.new(path, line, options) } - # - # Subsequent invocations return the already loaded template object. - class Cache - def initialize - @cache = {} - end - - def fetch(*key) - @cache[key] ||= yield - end - - def clear - @cache = {} - end - end - - - # Template Implementations ================================================ - - - # The template source is evaluated as a Ruby string. The #{} interpolation - # syntax can be used to generated dynamic output. - class StringTemplate < Template - def prepare - @code = "%Q{#{data}}" - end - - def precompiled_template(locals) - @code - end - end - register 'str', StringTemplate - - - # ERB template implementation. See: - # http://www.ruby-doc.org/stdlib/libdoc/erb/rdoc/classes/ERB.html - class ERBTemplate < Template - @@default_output_variable = '_erbout' - - def self.default_output_variable - @@default_output_variable - end - - def self.default_output_variable=(name) - @@default_output_variable = name - end - - def initialize_engine - return if defined? ::ERB - require_template_library 'erb' - end - - def prepare - @outvar = options[:outvar] || self.class.default_output_variable - @engine = ::ERB.new(data, options[:safe], options[:trim], @outvar) - end - - def precompiled_template(locals) - source = @engine.src - source - end - - def precompiled_preamble(locals) - <<-RUBY - begin - __original_outvar = #{@outvar} if defined?(#{@outvar}) - #{super} - RUBY - end - - def precompiled_postamble(locals) - <<-RUBY - #{super} - ensure - #{@outvar} = __original_outvar - end - RUBY - end - - # ERB generates a line to specify the character coding of the generated - # source in 1.9. Account for this in the line offset. - if RUBY_VERSION >= '1.9.0' - def precompiled(locals) - source, offset = super - [source, offset + 1] - end - end - end - - %w[erb rhtml].each { |ext| register ext, ERBTemplate } - - - # Erubis template implementation. See: - # http://www.kuwata-lab.com/erubis/ - # - # ErubisTemplate supports the following additional options, which are not - # passed down to the Erubis engine: - # - # :engine_class allows you to specify a custom engine class to use - # instead of the default (which is ::Erubis::Eruby). - # - # :escape_html when true, ::Erubis::EscapedEruby will be used as - # the engine class instead of the default. All content - # within <%= %> blocks will be automatically html escaped. - class ErubisTemplate < ERBTemplate - def initialize_engine - return if defined? ::Erubis - require_template_library 'erubis' - end - - def prepare - @options.merge!(:preamble => false, :postamble => false) - @outvar = options.delete(:outvar) || self.class.default_output_variable - engine_class = options.delete(:engine_class) - engine_class = ::Erubis::EscapedEruby if options.delete(:escape_html) - @engine = (engine_class || ::Erubis::Eruby).new(data, options) - end - - def precompiled_preamble(locals) - [super, "#{@outvar} = _buf = ''"].join("\n") - end - - def precompiled_postamble(locals) - ["_buf", super].join("\n") - end - - # Erubis doesn't have ERB's line-off-by-one under 1.9 problem. - # Override and adjust back. - if RUBY_VERSION >= '1.9.0' - def precompiled(locals) - source, offset = super - [source, offset - 1] - end - end - end - register 'erubis', ErubisTemplate - - - # Haml template implementation. See: - # http://haml.hamptoncatlin.com/ - class HamlTemplate < Template - def initialize_engine - return if defined? ::Haml::Engine - require_template_library 'haml' - end - - def prepare - options = @options.merge(:filename => eval_file, :line => line) - @engine = ::Haml::Engine.new(data, options) - end - - def evaluate(scope, locals, &block) - if @engine.respond_to?(:precompiled_method_return_value, true) - super - else - @engine.render(scope, locals, &block) - end - end - - # Precompiled Haml source. Taken from the precompiled_with_ambles - # method in Haml::Precompiler: - # http://github.com/nex3/haml/blob/master/lib/haml/precompiler.rb#L111-126 - def precompiled_template(locals) - @engine.precompiled - end - - def precompiled_preamble(locals) - local_assigns = super - @engine.instance_eval do - <<-RUBY - begin - extend Haml::Helpers - _hamlout = @haml_buffer = Haml::Buffer.new(@haml_buffer, #{options_for_buffer.inspect}) - _erbout = _hamlout.buffer - __in_erb_template = true - _haml_locals = locals - #{local_assigns} - RUBY - end - end - - def precompiled_postamble(locals) - @engine.instance_eval do - <<-RUBY - #{precompiled_method_return_value} - ensure - @haml_buffer = @haml_buffer.upper - end - RUBY - end - end - end - register 'haml', HamlTemplate - - - # Sass template implementation. See: - # http://haml.hamptoncatlin.com/ - # - # Sass templates do not support object scopes, locals, or yield. - class SassTemplate < Template - def initialize_engine - return if defined? ::Sass::Engine - require_template_library 'sass' - end - - def prepare - @engine = ::Sass::Engine.new(data, sass_options) - end - - def evaluate(scope, locals, &block) - @output ||= @engine.render - end - - private - def sass_options - options.merge(:filename => eval_file, :line => line, :syntax => :sass) - end - end - register 'sass', SassTemplate - - # Sass's new .scss type template implementation. - class ScssTemplate < SassTemplate - private - def sass_options - options.merge(:filename => eval_file, :line => line, :syntax => :scss) - end - end - register 'scss', ScssTemplate - - # Lessscss template implementation. See: - # http://lesscss.org/ - # - # Less templates do not support object scopes, locals, or yield. - class LessTemplate < Template - def initialize_engine - return if defined? ::Less::Engine - require_template_library 'less' - end - - def prepare - @engine = ::Less::Engine.new(data) - end - - def evaluate(scope, locals, &block) - @engine.to_css - end - end - register 'less', LessTemplate - - - # Nokogiri template implementation. See: - # http://nokogiri.org/ - class NokogiriTemplate < Template - def initialize_engine - return if defined?(::Nokogiri) - require_template_library 'nokogiri' - end - - def prepare; end - - def evaluate(scope, locals, &block) - xml = ::Nokogiri::XML::Builder.new - if data.respond_to?(:to_str) - locals[:xml] = xml - super(scope, locals, &block) - elsif data.kind_of?(Proc) - data.call(xml) - end - xml.to_xml - end - - def precompiled_template(locals) - data.to_str - end - end - register 'nokogiri', NokogiriTemplate - - # Builder template implementation. See: - # http://builder.rubyforge.org/ - class BuilderTemplate < Template - def initialize_engine - return if defined?(::Builder) - require_template_library 'builder' - end - - def prepare - end - - def evaluate(scope, locals, &block) - xml = ::Builder::XmlMarkup.new(:indent => 2) - if data.respond_to?(:to_str) - locals[:xml] = xml - super(scope, locals, &block) - elsif data.kind_of?(Proc) - data.call(xml) - end - xml.target! - end - - def precompiled_template(locals) - data.to_str - end - end - register 'builder', BuilderTemplate - - - # Liquid template implementation. See: - # http://liquid.rubyforge.org/ - # - # Liquid is designed to be a *safe* template system and threfore - # does not provide direct access to execuatable scopes. In order to - # support a +scope+, the +scope+ must be able to represent itself - # as a hash by responding to #to_h. If the +scope+ does not respond - # to #to_h it will be ignored. - # - # LiquidTemplate does not support yield blocks. - # - # It's suggested that your program require 'liquid' at load - # time when using this template engine. - class LiquidTemplate < Template - def initialize_engine - return if defined? ::Liquid::Template - require_template_library 'liquid' - end - - def prepare - @engine = ::Liquid::Template.parse(data) - end - - def evaluate(scope, locals, &block) - locals = locals.inject({}){ |h,(k,v)| h[k.to_s] = v ; h } - if scope.respond_to?(:to_h) - scope = scope.to_h.inject({}){ |h,(k,v)| h[k.to_s] = v ; h } - locals = scope.merge(locals) - end - locals['yield'] = block.nil? ? '' : yield - locals['content'] = locals['yield'] - @engine.render(locals) - end - end - register 'liquid', LiquidTemplate - - - # Discount Markdown implementation. See: - # http://github.com/rtomayko/rdiscount - # - # RDiscount is a simple text filter. It does not support +scope+ or - # +locals+. The +:smart+ and +:filter_html+ options may be set true - # to enable those flags on the underlying RDiscount object. - class RDiscountTemplate < Template - def flags - [:smart, :filter_html].select { |flag| options[flag] } - end - - def initialize_engine - return if defined? ::RDiscount - require_template_library 'rdiscount' - end - - def prepare - @engine = RDiscount.new(data, *flags) - @output = nil - end - - def evaluate(scope, locals, &block) - @output ||= @engine.to_html - end - end - register 'markdown', RDiscountTemplate - register 'mkd', RDiscountTemplate - register 'md', RDiscountTemplate - - - # RedCloth implementation. See: - # http://redcloth.org/ - class RedClothTemplate < Template - def initialize_engine - return if defined? ::RedCloth - require_template_library 'redcloth' - end - - def prepare - @engine = RedCloth.new(data) - @output = nil - end - - def evaluate(scope, locals, &block) - @output ||= @engine.to_html - end - end - register 'textile', RedClothTemplate - - - # RDoc template. See: - # http://rdoc.rubyforge.org/ - # - # It's suggested that your program require 'rdoc/markup' and - # 'rdoc/markup/to_html' at load time when using this template - # engine. - class RDocTemplate < Template - def initialize_engine - return if defined?(::RDoc::Markup) - require_template_library 'rdoc/markup' - require_template_library 'rdoc/markup/to_html' - end - - def prepare - markup = RDoc::Markup::ToHtml.new - @engine = markup.convert(data) - @output = nil - end - - def evaluate(scope, locals, &block) - @output ||= @engine.to_s - end - end - register 'rdoc', RDocTemplate - - - # Radius Template - # http://github.com/jlong/radius/ - class RadiusTemplate < Template - def initialize_engine - return if defined? ::Radius - require_template_library 'radius' - end - - def prepare - end - - def evaluate(scope, locals, &block) - context = Class.new(Radius::Context).new - context.define_tag("yield") do - block.call - end - locals.each do |tag, value| - context.define_tag(tag) do - value - end - end - (class << context; self; end).class_eval do - define_method :tag_missing do |tag, attr| - scope.__send__(tag) # any way to support attr as args? - end - end - options = {:tag_prefix => 'r'}.merge(@options) - parser = Radius::Parser.new(context, options) - parser.parse(data) - end - end - register 'radius', RadiusTemplate -end diff --git a/sinatra.gemspec b/sinatra.gemspec index ce11d0b2e5..0635965e30 100644 --- a/sinatra.gemspec +++ b/sinatra.gemspec @@ -78,7 +78,8 @@ Gem::Specification.new do |s| s.test_files = s.files.select {|path| path =~ /^test\/.*_test.rb/} s.extra_rdoc_files = %w[README.rdoc README.de.rdoc README.jp.rdoc LICENSE] - s.add_dependency 'rack', '>= 1.0' + s.add_dependency 'rack', '>= 1.0' + s.add_dependency 'tilt', '~> 1.1' s.add_development_dependency 'shotgun', '>= 0.6', '< 1.0' s.add_development_dependency 'rack-test', '>= 0.3.0' s.add_development_dependency 'haml' From 28a3a350bba42f911be6d144ca7af45776c778a7 Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Sat, 11 Sep 2010 13:53:49 +0200 Subject: [PATCH 42/80] Add liquid helper method. Tilt supports liquid for quite some time now, but it was not as easy to use as haml or erb, and not documented. Tests and documentation (English and German) included. --- README.de.rdoc | 18 ++++++++++++ README.rdoc | 18 ++++++++++++ lib/sinatra/base.rb | 4 +++ sinatra.gemspec | 1 + test/liquid_test.rb | 58 +++++++++++++++++++++++++++++++++++++++ test/views/hello.liquid | 1 + test/views/layout2.liquid | 2 ++ 7 files changed, 102 insertions(+) create mode 100644 test/liquid_test.rb create mode 100644 test/views/hello.liquid create mode 100644 test/views/layout2.liquid diff --git a/README.de.rdoc b/README.de.rdoc index 92a0922cb5..50d0022aaa 100644 --- a/README.de.rdoc +++ b/README.de.rdoc @@ -300,6 +300,24 @@ Das less gem wird benötigt um Less-Templates rendern zu können: Dieser Code rendert ./views/stylesheet.less. +=== Liquid Templates + +Das liquid gem wird benötigt um Liquid-Templates rendern zu können: + + ## liquid muss eingebunden werden + require 'liquid' + + get '/' do + liquid :index + end + +Dieser Code rendert ./views/index.liquid. + +Da man aus Liquid-Templates heraus keine Methoden (abgesehen von +yield+) +aufrufen kann, will man nahezu in allen Fällen +locals+ übergeben: + + liquid :index, :locals => { :key => 'value' } + === Inline Templates get '/' do diff --git a/README.rdoc b/README.rdoc index c0ca9e7c23..92fecbe36a 100644 --- a/README.rdoc +++ b/README.rdoc @@ -296,6 +296,24 @@ The less gem/library is required to render Less templates: Renders ./views/stylesheet.less. +=== Liquid Templates + +The liquid gem/library is required to render Liquid templates: + + ## You'll need to require liquid in your app + require 'liquid' + + get '/' do + liquid :index + end + +Renders ./views/index.liquid. + +Since you cannot call Ruby methods (except for +yield+) from a Liquid +template, you almost always want to pass locals to it: + + liquid :index, :locals => { :key => 'value' } + === Inline Templates get '/' do diff --git a/lib/sinatra/base.rb b/lib/sinatra/base.rb index 3ec9c5922a..44c91158ea 100644 --- a/lib/sinatra/base.rb +++ b/lib/sinatra/base.rb @@ -335,6 +335,10 @@ def builder(template=nil, options={}, locals={}, &block) render :builder, template, options, locals end + def liquid(template, options={}, locals={}) + render :liquid, template, options, locals + end + private def render(engine, data, options={}, locals={}, &block) # merge app-level options diff --git a/sinatra.gemspec b/sinatra.gemspec index 0635965e30..dd8a52e0c1 100644 --- a/sinatra.gemspec +++ b/sinatra.gemspec @@ -86,6 +86,7 @@ Gem::Specification.new do |s| s.add_development_dependency 'builder' s.add_development_dependency 'erubis' s.add_development_dependency 'less' + s.add_development_dependency 'liquid' s.has_rdoc = true s.homepage = "http://sinatra.rubyforge.org" diff --git a/test/liquid_test.rb b/test/liquid_test.rb new file mode 100644 index 0000000000..3315bd1697 --- /dev/null +++ b/test/liquid_test.rb @@ -0,0 +1,58 @@ +require File.dirname(__FILE__) + '/helper' + +begin +require 'liquid' + +class LiquidTest < Test::Unit::TestCase + def liquid_app(&block) + mock_app do + set :views, File.dirname(__FILE__) + '/views' + get '/', &block + end + get '/' + end + + it 'renders inline liquid strings' do + liquid_app { liquid '

Hiya

' } + assert ok? + assert_equal "

Hiya

", body + end + + it 'renders .liquid files in views path' do + liquid_app { liquid :hello } + assert ok? + assert_equal "

Hello From Liquid

\n", body + end + + it "renders with inline layouts" do + mock_app do + layout { "

THIS. IS. {{ yield }}

" } + get('/') { liquid 'SPARTA' } + end + get '/' + assert ok? + assert_equal "

THIS. IS. SPARTA

", body + end + + it "renders with file layouts" do + liquid_app { liquid 'Hello World', :layout => :layout2 } + assert ok? + assert_equal "

Liquid Layout!

\n

Hello World

\n", body + end + + it "raises error if template not found" do + mock_app { get('/') { liquid :no_such_template } } + assert_raise(Errno::ENOENT) { get('/') } + end + + it "allows passing locals" do + liquid_app do + liquid '{{ value }}', :locals => { :value => 'foo' } + end + assert ok? + assert_equal 'foo', body + end +end +rescue + warn "#{$!.to_s}: skipping liquid tests" +end diff --git a/test/views/hello.liquid b/test/views/hello.liquid new file mode 100644 index 0000000000..00d7d26cc4 --- /dev/null +++ b/test/views/hello.liquid @@ -0,0 +1 @@ +

Hello From Liquid

diff --git a/test/views/layout2.liquid b/test/views/layout2.liquid new file mode 100644 index 0000000000..4b64d4d4d6 --- /dev/null +++ b/test/views/layout2.liquid @@ -0,0 +1,2 @@ +

Liquid Layout!

+

{{ yield }}

From 4db91f3c3cccc01c473b0877076d78030260e52e Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Sat, 11 Sep 2010 13:56:10 +0200 Subject: [PATCH 43/80] German README: Adjust Template section headings ("XYZ Template" -> "XYZ-Template"). --- README.de.rdoc | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/README.de.rdoc b/README.de.rdoc index 50d0022aaa..ab5976f2c4 100644 --- a/README.de.rdoc +++ b/README.de.rdoc @@ -171,9 +171,9 @@ Symbols auf Templates verweisen sollte, auch wenn sich ein Template in einen Unterordner befindet (in diesen Fall :'subdir/template'). Renderingmethoden rendern jeden String direkt. -=== Haml Templates +=== Haml-Templates -Das haml gem wird benötigt um HAML-Templates rendern zu können: +Das haml gem wird benötigt um Haml-Templates rendern zu können: ## haml muss eingebunden werden require 'haml' @@ -195,8 +195,7 @@ und individuell überschrieben werden. haml :index, :format => :html4 # überschrieben end - -=== Erb Templates +=== Erb-Templates ## erb muss eingebunden werden require 'erb' @@ -220,7 +219,7 @@ Das erubis gem wird benötigt um Erubis-Templates rendern zu können: Dieser Code rendert ./views/index.erubis. -=== Builder Templates +=== Builder-Templates Das buidler gem wird benötigt um Builder-Templates rendern zu können: @@ -234,7 +233,7 @@ Das buidler gem wird benötigt um Builder-Templates rendern zu können: Dieser Code rendert ./views/index.builder. -=== Sass Templates +=== Sass-Templates Das haml gem wird benötigt um SASS-Templates rendern zu können: @@ -260,7 +259,7 @@ und individuell überschrieben werden. sass :stylesheet, :style => :expanded # überschrieben end -=== Scss Templates +=== Scss-Templates Das haml gem wird benötigt um SCSS-Templates rendern zu können: @@ -286,7 +285,7 @@ und individuell überschrieben werden. scss :stylesheet, :style => :expanded # überschrieben end -=== Less Templates +=== Less-Templates Das less gem wird benötigt um Less-Templates rendern zu können: @@ -300,7 +299,7 @@ Das less gem wird benötigt um Less-Templates rendern zu können: Dieser Code rendert ./views/stylesheet.less. -=== Liquid Templates +=== Liquid-Templates Das liquid gem wird benötigt um Liquid-Templates rendern zu können: @@ -318,13 +317,13 @@ aufrufen kann, will man nahezu in allen Fällen +locals+ übergeben: liquid :index, :locals => { :key => 'value' } -=== Inline Templates +=== Inline-Templates get '/' do haml '%div.title Hallo Welt' end -Rendert den Inline Template String. +Rendert den Inline-Template-String. === Auf Variablen in Templates zugreifen From 970169b1fb001fbd9245c48f5d83c4321fb58387 Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Sat, 11 Sep 2010 14:34:41 +0200 Subject: [PATCH 44/80] Add markdown helper method. Tilt supports markdown for quite some time now, but it was not as easy to use as haml or erb, and not documented. Tests and documentation (English and German) included. --- README.de.rdoc | 26 ++++++++++++++++++++++++++ README.rdoc | 23 +++++++++++++++++++++++ lib/sinatra/base.rb | 9 +++++++++ sinatra.gemspec | 1 + test/markdown_test.rb | 34 ++++++++++++++++++++++++++++++++++ test/views/hello.md | 1 + 6 files changed, 94 insertions(+) create mode 100644 test/markdown_test.rb create mode 100644 test/views/hello.md diff --git a/README.de.rdoc b/README.de.rdoc index ab5976f2c4..f03a7825a1 100644 --- a/README.de.rdoc +++ b/README.de.rdoc @@ -317,6 +317,32 @@ aufrufen kann, will man nahezu in allen Fällen +locals+ übergeben: liquid :index, :locals => { :key => 'value' } +=== Markdown-Templates + +Das rdiscount gem wird benötigt um Markdown-Templates rendern zu können: + + ## rdiscount muss eingebunden werden + require "rdiscount" + + get '/' do + markdown :index + end + +Dieser Code rendert ./views/index.markdown (+md+ und +mkd+ sind +ebenfalls zulässige Dateiendungen). + +Da es weder möglich ist Methoden aufzurufen, noch +locals+ zu übergeben, ist +es am sinnvollsten Markdown in Kombination mit einer anderen Template-Engine +zu nutzen: + + erb :overview, :locals => { :text => markdown(:introduction) } + +Es ist auch möglich die +markdown+ Methode aus anderen Templates heraus +aufzurufen: + + %h1 Hallo von Haml! + %p= markdown(:greetings) + === Inline-Templates get '/' do diff --git a/README.rdoc b/README.rdoc index 92fecbe36a..b8e489a272 100644 --- a/README.rdoc +++ b/README.rdoc @@ -314,6 +314,29 @@ template, you almost always want to pass locals to it: liquid :index, :locals => { :key => 'value' } +=== Markdown Templates + +The rdiscount gem/library is required to render Markdown templates: + + ## You'll need to require rdiscount in your app + require "rdiscount" + + get '/' do + markdown :index + end + +Renders ./views/index.markdown (+md+ and +mkd+ are also valid file +extensions). + +It is not possible to call methods from markdown, nor to pass locals to it. You therefore will usually use it in combination with another rendering engine: + + erb :overview, :locals => { :text => markdown(:introduction) } + +Note that you may also call the markdown method from within other templates: + + %h1 Hello From Haml! + %p= markdown(:greetings) + === Inline Templates get '/' do diff --git a/lib/sinatra/base.rb b/lib/sinatra/base.rb index 44c91158ea..714d142818 100644 --- a/lib/sinatra/base.rb +++ b/lib/sinatra/base.rb @@ -339,6 +339,10 @@ def liquid(template, options={}, locals={}) render :liquid, template, options, locals end + def markdown(template, options={}, locals={}) + render :markdown, template, options, locals + end + private def render(engine, data, options={}, locals={}, &block) # merge app-level options @@ -379,6 +383,11 @@ def compile_template(engine, data, options, views) template.new(path, line.to_i, options) { body } else path = ::File.join(views, "#{data}.#{engine}") + Tilt.mappings.each do |ext, klass| + break if File.exists?(path) + next unless klass == template + path = ::File.join(views, "#{data}.#{ext}") + end template.new(path, 1, options) end when data.is_a?(Proc) || data.is_a?(String) diff --git a/sinatra.gemspec b/sinatra.gemspec index dd8a52e0c1..0ada0b2ece 100644 --- a/sinatra.gemspec +++ b/sinatra.gemspec @@ -87,6 +87,7 @@ Gem::Specification.new do |s| s.add_development_dependency 'erubis' s.add_development_dependency 'less' s.add_development_dependency 'liquid' + s.add_development_dependency 'rdiscount' s.has_rdoc = true s.homepage = "http://sinatra.rubyforge.org" diff --git a/test/markdown_test.rb b/test/markdown_test.rb new file mode 100644 index 0000000000..e157d18b28 --- /dev/null +++ b/test/markdown_test.rb @@ -0,0 +1,34 @@ +require File.dirname(__FILE__) + '/helper' + +begin +require 'rdiscount' + +class MarkdownTest < Test::Unit::TestCase + def markdown_app(&block) + mock_app do + set :views, File.dirname(__FILE__) + '/views' + get '/', &block + end + get '/' + end + + it 'renders inline markdown strings' do + markdown_app { markdown '# Hiya' } + assert ok? + assert_equal "

Hiya

\n", body + end + + it 'renders .markdown files in views path' do + markdown_app { markdown :hello } + assert ok? + assert_equal "

Hello From Markdown

\n", body + end + + it "raises error if template not found" do + mock_app { get('/') { markdown :no_such_template } } + assert_raise(Errno::ENOENT) { get('/') } + end +end +rescue + warn "#{$!.to_s}: skipping markdown tests" +end diff --git a/test/views/hello.md b/test/views/hello.md new file mode 100644 index 0000000000..31dc9cc293 --- /dev/null +++ b/test/views/hello.md @@ -0,0 +1 @@ +# Hello From Markdown \ No newline at end of file From b464e024a8f90df169fbe46bd7b5877aedc5b17c Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Sat, 11 Sep 2010 14:52:55 +0200 Subject: [PATCH 45/80] Add textile helper method. Tilt supports textile for quite some time now, but it was not as easy to use as haml or erb, and not documented. Tests and documentation (English and German) included. --- README.de.rdoc | 25 +++++++++++++++++++++++++ README.rdoc | 22 ++++++++++++++++++++++ lib/sinatra/base.rb | 4 ++++ sinatra.gemspec | 1 + test/textile_test.rb | 34 ++++++++++++++++++++++++++++++++++ test/views/hello.textile | 1 + 6 files changed, 87 insertions(+) create mode 100644 test/textile_test.rb create mode 100644 test/views/hello.textile diff --git a/README.de.rdoc b/README.de.rdoc index f03a7825a1..96ff7ab0c3 100644 --- a/README.de.rdoc +++ b/README.de.rdoc @@ -343,6 +343,31 @@ aufzurufen: %h1 Hallo von Haml! %p= markdown(:greetings) +=== Textile-Templates + +Das RedCloth gem wird benötigt um Textile-Templates rendern zu können: + + ## redcloth muss eingebunden werden + require "redcloth" + + get '/' do + textile :index + end + +Dieser Code rendert ./views/index.textile. + +Da es weder möglich ist Methoden aufzurufen, noch +locals+ zu übergeben, ist +es am sinnvollsten Textile in Kombination mit einer anderen Template-Engine +zu nutzen: + + erb :overview, :locals => { :text => textile(:introduction) } + +Es ist auch möglich die +textile+ Methode aus anderen Templates heraus +aufzurufen: + + %h1 Hallo von Haml! + %p= textile(:greetings) + === Inline-Templates get '/' do diff --git a/README.rdoc b/README.rdoc index b8e489a272..f79dbac5ee 100644 --- a/README.rdoc +++ b/README.rdoc @@ -337,6 +337,28 @@ Note that you may also call the markdown method from within other templates: %h1 Hello From Haml! %p= markdown(:greetings) +=== Textile Templates + +The RedCloth gem/library is required to render Textile templates: + + ## You'll need to require rdiscount in your app + require "redcloth" + + get '/' do + textile :index + end + +Renders ./views/index.textile. + +It is not possible to call methods from textile, nor to pass locals to it. You therefore will usually use it in combination with another rendering engine: + + erb :overview, :locals => { :text => textile(:introduction) } + +Note that you may also call the textile method from within other templates: + + %h1 Hello From Haml! + %p= textile(:greetings) + === Inline Templates get '/' do diff --git a/lib/sinatra/base.rb b/lib/sinatra/base.rb index 714d142818..ff66bfebc6 100644 --- a/lib/sinatra/base.rb +++ b/lib/sinatra/base.rb @@ -343,6 +343,10 @@ def markdown(template, options={}, locals={}) render :markdown, template, options, locals end + def textile(template, options={}, locals={}) + render :textile, template, options, locals + end + private def render(engine, data, options={}, locals={}, &block) # merge app-level options diff --git a/sinatra.gemspec b/sinatra.gemspec index 0ada0b2ece..908f006e4d 100644 --- a/sinatra.gemspec +++ b/sinatra.gemspec @@ -88,6 +88,7 @@ Gem::Specification.new do |s| s.add_development_dependency 'less' s.add_development_dependency 'liquid' s.add_development_dependency 'rdiscount' + s.add_development_dependency 'RedCloth' s.has_rdoc = true s.homepage = "http://sinatra.rubyforge.org" diff --git a/test/textile_test.rb b/test/textile_test.rb new file mode 100644 index 0000000000..38df1d2e5a --- /dev/null +++ b/test/textile_test.rb @@ -0,0 +1,34 @@ +require File.dirname(__FILE__) + '/helper' + +begin +require 'redcloth' + +class TextileTest < Test::Unit::TestCase + def textile_app(&block) + mock_app do + set :views, File.dirname(__FILE__) + '/views' + get '/', &block + end + get '/' + end + + it 'renders inline textile strings' do + textile_app { textile 'h1. Hiya' } + assert ok? + assert_equal "

Hiya

", body + end + + it 'renders .textile files in views path' do + textile_app { textile :hello } + assert ok? + assert_equal "

Hello From Textile

", body + end + + it "raises error if template not found" do + mock_app { get('/') { textile :no_such_template } } + assert_raise(Errno::ENOENT) { get('/') } + end +end +rescue + warn "#{$!.to_s}: skipping textile tests" +end diff --git a/test/views/hello.textile b/test/views/hello.textile new file mode 100644 index 0000000000..02686a69c1 --- /dev/null +++ b/test/views/hello.textile @@ -0,0 +1 @@ +h1. Hello From Textile From c248dbac9db09f726f11d4877b49c80025961da0 Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Sun, 12 Sep 2010 13:59:00 +0200 Subject: [PATCH 46/80] Add rdoc helper method. Tilt supports RDoc for quite some time now, but it was not as easy to use as haml or erb, and not documented. Tests and documentation (English and German) included. --- README.de.rdoc | 25 +++++++++++++++++++++++++ README.rdoc | 22 ++++++++++++++++++++++ lib/sinatra/base.rb | 4 ++++ test/rdoc_test.rb | 34 ++++++++++++++++++++++++++++++++++ test/views/hello.rdoc | 1 + 5 files changed, 86 insertions(+) create mode 100644 test/rdoc_test.rb create mode 100644 test/views/hello.rdoc diff --git a/README.de.rdoc b/README.de.rdoc index 96ff7ab0c3..980fdf4174 100644 --- a/README.de.rdoc +++ b/README.de.rdoc @@ -368,6 +368,31 @@ aufzurufen: %h1 Hallo von Haml! %p= textile(:greetings) +=== RDoc-Templates + +Das rdoc gem wird benötigt um RDoc-Templates rendern zu können: + + ## redcloth muss eingebunden werden + require "rdoc" + + get '/' do + rdoc :index + end + +Dieser Code rendert ./views/index.rdoc. + +Da es weder möglich ist Methoden aufzurufen, noch +locals+ zu übergeben, ist +es am sinnvollsten RDoc in Kombination mit einer anderen Template-Engine +zu nutzen: + + erb :overview, :locals => { :text => rdoc(:introduction) } + +Es ist auch möglich die +rdoc+ Methode aus anderen Templates heraus +aufzurufen: + + %h1 Hallo von Haml! + %p= rdoc(:greetings) + === Inline-Templates get '/' do diff --git a/README.rdoc b/README.rdoc index f79dbac5ee..3b8e62069e 100644 --- a/README.rdoc +++ b/README.rdoc @@ -359,6 +359,28 @@ Note that you may also call the textile method from within other templates: %h1 Hello From Haml! %p= textile(:greetings) +=== RDoc Templates + +The RDoc gem/library is required to render RDoc templates: + + ## You'll need to require rdiscount in your app + require "rdoc" + + get '/' do + rdoc :index + end + +Renders ./views/index.rdoc. + +It is not possible to call methods from rdoc, nor to pass locals to it. You therefore will usually use it in combination with another rendering engine: + + erb :overview, :locals => { :text => rdoc(:introduction) } + +Note that you may also call the rdoc method from within other templates: + + %h1 Hello From Haml! + %p= rdoc(:greetings) + === Inline Templates get '/' do diff --git a/lib/sinatra/base.rb b/lib/sinatra/base.rb index ff66bfebc6..807bc0e31b 100644 --- a/lib/sinatra/base.rb +++ b/lib/sinatra/base.rb @@ -347,6 +347,10 @@ def textile(template, options={}, locals={}) render :textile, template, options, locals end + def rdoc(template, options={}, locals={}) + render :rdoc, template, options, locals + end + private def render(engine, data, options={}, locals={}, &block) # merge app-level options diff --git a/test/rdoc_test.rb b/test/rdoc_test.rb new file mode 100644 index 0000000000..8762e3dba2 --- /dev/null +++ b/test/rdoc_test.rb @@ -0,0 +1,34 @@ +require File.dirname(__FILE__) + '/helper' + +begin +require 'rdoc' + +class RdocTest < Test::Unit::TestCase + def rdoc_app(&block) + mock_app do + set :views, File.dirname(__FILE__) + '/views' + get '/', &block + end + get '/' + end + + it 'renders inline rdoc strings' do + rdoc_app { rdoc '= Hiya' } + assert ok? + assert_equal "

Hiya

\n", body + end + + it 'renders .rdoc files in views path' do + rdoc_app { rdoc :hello } + assert ok? + assert_equal "

Hello From RDoc

\n", body + end + + it "raises error if template not found" do + mock_app { get('/') { rdoc :no_such_template } } + assert_raise(Errno::ENOENT) { get('/') } + end +end +rescue + warn "#{$!.to_s}: skipping rdoc tests" +end diff --git a/test/views/hello.rdoc b/test/views/hello.rdoc new file mode 100644 index 0000000000..cfba71486e --- /dev/null +++ b/test/views/hello.rdoc @@ -0,0 +1 @@ += Hello From RDoc From 7cb94f2d3f9e52e6d75ef1cf036f91064f8fc600 Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Sun, 12 Sep 2010 15:14:45 +0200 Subject: [PATCH 47/80] Add radius helper method. Tilt supports radius for quite some time now, but it was not as easy to use as haml or erb, and not documented. Tests and documentation (English and German) included. --- README.de.rdoc | 18 ++++++++++++ README.rdoc | 18 ++++++++++++ lib/sinatra/base.rb | 4 +++ test/radius_test.rb | 59 +++++++++++++++++++++++++++++++++++++++ test/views/hello.radius | 1 + test/views/layout2.radius | 2 ++ 6 files changed, 102 insertions(+) create mode 100644 test/radius_test.rb create mode 100644 test/views/hello.radius create mode 100644 test/views/layout2.radius diff --git a/README.de.rdoc b/README.de.rdoc index 980fdf4174..5d833294cc 100644 --- a/README.de.rdoc +++ b/README.de.rdoc @@ -393,6 +393,24 @@ aufzurufen: %h1 Hallo von Haml! %p= rdoc(:greetings) +=== Radius-Templates + +Das radius gem wird benötigt um Radius-Templates rendern zu können: + + ## radius muss eingebunden werden + require 'radius' + + get '/' do + radius :index + end + +Dieser Code rendert ./views/index.radius. + +Da man aus Radius-Templates heraus keine Methoden (abgesehen von +yield+) +aufrufen kann, will man nahezu in allen Fällen +locals+ übergeben: + + radius :index, :locals => { :key => 'value' } + === Inline-Templates get '/' do diff --git a/README.rdoc b/README.rdoc index 3b8e62069e..1c63ad2a5d 100644 --- a/README.rdoc +++ b/README.rdoc @@ -381,6 +381,24 @@ Note that you may also call the rdoc method from within other templates: %h1 Hello From Haml! %p= rdoc(:greetings) +=== Radius Templates + +The radius gem/library is required to render Radius templates: + + ## You'll need to require radius in your app + require 'radius' + + get '/' do + radius :index + end + +Renders ./views/index.radius. + +Since you cannot call Ruby methods (except for +yield+) from a Radius +template, you almost always want to pass locals to it: + + radius :index, :locals => { :key => 'value' } + === Inline Templates get '/' do diff --git a/lib/sinatra/base.rb b/lib/sinatra/base.rb index 807bc0e31b..126a375ba5 100644 --- a/lib/sinatra/base.rb +++ b/lib/sinatra/base.rb @@ -351,6 +351,10 @@ def rdoc(template, options={}, locals={}) render :rdoc, template, options, locals end + def radius(template, options={}, locals={}) + render :radius, template, options, locals + end + private def render(engine, data, options={}, locals={}, &block) # merge app-level options diff --git a/test/radius_test.rb b/test/radius_test.rb new file mode 100644 index 0000000000..7f43928a53 --- /dev/null +++ b/test/radius_test.rb @@ -0,0 +1,59 @@ +require File.dirname(__FILE__) + '/helper' + +begin +fail 'Radius broken on 1.9.' if RUBY_VERSION >= '1.9.1' +require 'radius' + +class RadiusTest < Test::Unit::TestCase + def radius_app(&block) + mock_app do + set :views, File.dirname(__FILE__) + '/views' + get '/', &block + end + get '/' + end + + it 'renders inline radius strings' do + radius_app { radius '

Hiya

' } + assert ok? + assert_equal "

Hiya

", body + end + + it 'renders .radius files in views path' do + radius_app { radius :hello } + assert ok? + assert_equal "

Hello From Radius

\n", body + end + + it "renders with inline layouts" do + mock_app do + layout { "

THIS. IS.

" } + get('/') { radius 'SPARTA' } + end + get '/' + assert ok? + assert_equal "

THIS. IS. SPARTA

", body + end + + it "renders with file layouts" do + radius_app { radius 'Hello World', :layout => :layout2 } + assert ok? + assert_equal "

Radius Layout!

\n

Hello World

\n", body + end + + it "raises error if template not found" do + mock_app { get('/') { radius :no_such_template } } + assert_raise(Errno::ENOENT) { get('/') } + end + + it "allows passing locals" do + radius_app do + radius '', :locals => { :value => 'foo' } + end + assert ok? + assert_equal 'foo', body + end +end +rescue + warn "#{$!.to_s}: skipping radius tests" +end diff --git a/test/views/hello.radius b/test/views/hello.radius new file mode 100644 index 0000000000..98e35f37e1 --- /dev/null +++ b/test/views/hello.radius @@ -0,0 +1 @@ +

Hello From Radius

diff --git a/test/views/layout2.radius b/test/views/layout2.radius new file mode 100644 index 0000000000..57a06c04e4 --- /dev/null +++ b/test/views/layout2.radius @@ -0,0 +1,2 @@ +

Radius Layout!

+

From f4f03edd414cd6cdf9e7c6bdb990bea5b403ef15 Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Sun, 12 Sep 2010 15:22:24 +0200 Subject: [PATCH 48/80] Make sure test layout is removed to avoid failures. --- test/templates_test.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/test/templates_test.rb b/test/templates_test.rb index ea4438e1bd..04b89584ef 100644 --- a/test/templates_test.rb +++ b/test/templates_test.rb @@ -1,4 +1,5 @@ require File.dirname(__FILE__) + '/helper' +File.delete(File.dirname(__FILE__) + '/views/layout.test') rescue nil class TestTemplate < Tilt::Template def prepare From 8718419b5b86e511147fb9feedc3f3a58b0445fc Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Sun, 12 Sep 2010 15:28:43 +0200 Subject: [PATCH 49/80] Avoid another segfault on 1.9.2 when running `rake test`. --- Rakefile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Rakefile b/Rakefile index 664f73ecf6..afa31bf951 100644 --- a/Rakefile +++ b/Rakefile @@ -16,10 +16,10 @@ end if !ENV['NO_TEST_FIX'] and RUBY_VERSION == '1.9.2' and RUBY_PATCHLEVEL == 0 # Avoids seg fault task(:test) do - files = Dir.glob('test/*_test.rb') - files.delete 'test/settings_test.rb' - sh "testrb #{files.join ' '}" - sh "testrb test/settings_test.rb" + second_run = %w[test/settings_test.rb test/rdoc_test.rb] + first_run = Dir.glob('test/*_test.rb') - second_run + sh "testrb #{first_run.join ' '}" + sh "testrb #{second_run.join ' '}" end else Rake::TestTask.new(:test) do |t| From 8ce74b3ad271bdf072845dcb17eadf4ee3569a6b Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Sun, 12 Sep 2010 17:00:33 +0200 Subject: [PATCH 50/80] Add markaby helper method. Tilt supports Markaby for quite some time now, but it was not as easy to use as haml or erb, and not documented. Tests and documentation (English and German) included. --- README.de.rdoc | 13 ++++++++++ README.rdoc | 13 ++++++++++ Rakefile | 5 ++-- lib/sinatra/base.rb | 4 +++ test/hello.mab | 1 + test/markaby_test.rb | 58 ++++++++++++++++++++++++++++++++++++++++++ test/views/hello.mab | 1 + test/views/layout2.mab | 2 ++ 8 files changed, 94 insertions(+), 3 deletions(-) create mode 100644 test/hello.mab create mode 100644 test/markaby_test.rb create mode 100644 test/views/hello.mab create mode 100644 test/views/layout2.mab diff --git a/README.de.rdoc b/README.de.rdoc index 5d833294cc..1fd7e77811 100644 --- a/README.de.rdoc +++ b/README.de.rdoc @@ -411,6 +411,19 @@ aufrufen kann, will man nahezu in allen Fällen +locals+ übergeben: radius :index, :locals => { :key => 'value' } +=== Markaby-Templates + +Das markaby gem wird benötigt um Markaby-Templates rendern zu können: + + ## markaby muss eingebunden werden + require 'markaby' + + get '/' do + markaby :index + end + +Dieser Code rendert ./views/index.mab. + === Inline-Templates get '/' do diff --git a/README.rdoc b/README.rdoc index 1c63ad2a5d..5000aab0a2 100644 --- a/README.rdoc +++ b/README.rdoc @@ -399,6 +399,19 @@ template, you almost always want to pass locals to it: radius :index, :locals => { :key => 'value' } +=== Markaby Templates + +The markaby gem/library is required to render Markaby templates: + + ## You'll need to require markaby in your app + require 'markaby' + + get '/' do + markaby :index + end + +Renders ./views/index.mab. + === Inline Templates get '/' do diff --git a/Rakefile b/Rakefile index afa31bf951..db31ab9c7d 100644 --- a/Rakefile +++ b/Rakefile @@ -16,10 +16,9 @@ end if !ENV['NO_TEST_FIX'] and RUBY_VERSION == '1.9.2' and RUBY_PATCHLEVEL == 0 # Avoids seg fault task(:test) do - second_run = %w[test/settings_test.rb test/rdoc_test.rb] + second_run = %w[settings rdoc markaby].map { |l| "test/#{l}_test.rb" } first_run = Dir.glob('test/*_test.rb') - second_run - sh "testrb #{first_run.join ' '}" - sh "testrb #{second_run.join ' '}" + [first_run, second_run].each { |f| sh "testrb #{f.join ' '}" } end else Rake::TestTask.new(:test) do |t| diff --git a/lib/sinatra/base.rb b/lib/sinatra/base.rb index 126a375ba5..bcd8c5b2d1 100644 --- a/lib/sinatra/base.rb +++ b/lib/sinatra/base.rb @@ -355,6 +355,10 @@ def radius(template, options={}, locals={}) render :radius, template, options, locals end + def markaby(template, options={}, locals={}) + render :mab, template, options, locals + end + private def render(engine, data, options={}, locals={}, &block) # merge app-level options diff --git a/test/hello.mab b/test/hello.mab new file mode 100644 index 0000000000..6e0c9cbf01 --- /dev/null +++ b/test/hello.mab @@ -0,0 +1 @@ +h1 "Hello From Markaby" diff --git a/test/markaby_test.rb b/test/markaby_test.rb new file mode 100644 index 0000000000..8f09ab6bba --- /dev/null +++ b/test/markaby_test.rb @@ -0,0 +1,58 @@ +require File.dirname(__FILE__) + '/helper' + +begin +require 'markaby' + +class MarkabyTest < Test::Unit::TestCase + def markaby_app(&block) + mock_app do + set :views, File.dirname(__FILE__) + '/views' + get '/', &block + end + get '/' + end + + it 'renders inline markaby strings' do + markaby_app { markaby 'h1 "Hiya"' } + assert ok? + assert_equal "

Hiya

", body + end + + it 'renders .markaby files in views path' do + markaby_app { markaby :hello } + assert ok? + assert_equal "

Hello From Markaby

", body + end + + it "renders with inline layouts" do + mock_app do + layout { 'h1 { text "THIS. IS. "; yield }' } + get('/') { markaby 'em "SPARTA"' } + end + get '/' + assert ok? + assert_equal "

THIS. IS. SPARTA

", body + end + + it "renders with file layouts" do + markaby_app { markaby 'text "Hello World"', :layout => :layout2 } + assert ok? + assert_equal "

Markaby Layout!

Hello World

", body + end + + it "raises error if template not found" do + mock_app { get('/') { markaby :no_such_template } } + assert_raise(Errno::ENOENT) { get('/') } + end + + it "allows passing locals" do + markaby_app do + markaby 'text value', :locals => { :value => 'foo' } + end + assert ok? + assert_equal 'foo', body + end +end +rescue + warn "#{$!.to_s}: skipping markaby tests" +end diff --git a/test/views/hello.mab b/test/views/hello.mab new file mode 100644 index 0000000000..6e0c9cbf01 --- /dev/null +++ b/test/views/hello.mab @@ -0,0 +1 @@ +h1 "Hello From Markaby" diff --git a/test/views/layout2.mab b/test/views/layout2.mab new file mode 100644 index 0000000000..01da339bff --- /dev/null +++ b/test/views/layout2.mab @@ -0,0 +1,2 @@ +h1 "Markaby Layout!" +p { yield } From f58d015b6d3512ba739d6bea1722262a3431a9a8 Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Sun, 12 Sep 2010 23:09:10 +0200 Subject: [PATCH 51/80] Add coffee helper method. Tilt supports CoffeeScript again, but it was not as easy to use as sass or scss, and not documented. Tests and documentation (English and German) included. --- README.de.rdoc | 13 +++++++++ README.rdoc | 15 ++++++++++ lib/sinatra/base.rb | 5 ++++ test/coffee_test.rb | 64 +++++++++++++++++++++++++++++++++++++++++ test/views/hello.coffee | 1 + 5 files changed, 98 insertions(+) create mode 100644 test/coffee_test.rb create mode 100644 test/views/hello.coffee diff --git a/README.de.rdoc b/README.de.rdoc index 1fd7e77811..2226d16b0f 100644 --- a/README.de.rdoc +++ b/README.de.rdoc @@ -424,6 +424,19 @@ Das markaby gem wird benötigt um Markaby-Templates rendern zu können: Dieser Code rendert ./views/index.mab. +=== CoffeScript-Templates + +Das coffee-script gem und das `coffee`-Programm werden benötigt um CoffeScript-Templates rendern zu können: + + ## coffee-script muss eingebunden werden + require 'coffee-script' + + get '/application.js' do + coffee :application + end + +Dieser Code rendert ./views/application.coffee. + === Inline-Templates get '/' do diff --git a/README.rdoc b/README.rdoc index 5000aab0a2..a03ed1a3e9 100644 --- a/README.rdoc +++ b/README.rdoc @@ -412,6 +412,21 @@ The markaby gem/library is required to render Markaby templates: Renders ./views/index.mab. +=== CoffeeScript Templates + +The coffee-script gem/library and the `coffee` binary are required to render +CoffeeScript templates: + + ## You'll need to require coffee-script in your app + require 'coffee-script' + + get '/application.js' do + content_type 'text/javascript', :charset => 'utf-8' + coffee :application + end + +Renders ./views/application.coffee. + === Inline Templates get '/' do diff --git a/lib/sinatra/base.rb b/lib/sinatra/base.rb index bcd8c5b2d1..7c6885081e 100644 --- a/lib/sinatra/base.rb +++ b/lib/sinatra/base.rb @@ -359,6 +359,11 @@ def markaby(template, options={}, locals={}) render :mab, template, options, locals end + def coffee(template, options={}, locals={}) + options[:layout] = false + render :coffee, template, options, locals + end + private def render(engine, data, options={}, locals={}, &block) # merge app-level options diff --git a/test/coffee_test.rb b/test/coffee_test.rb new file mode 100644 index 0000000000..c71f9d2586 --- /dev/null +++ b/test/coffee_test.rb @@ -0,0 +1,64 @@ +require File.dirname(__FILE__) + '/helper' + +begin +require 'coffee-script' + +class CoffeeTest < Test::Unit::TestCase + def coffee_app(&block) + mock_app { + set :views, File.dirname(__FILE__) + '/views' + get '/', &block + } + get '/' + end + + it 'renders inline Coffee strings' do + coffee_app { coffee "alert 'Aye!'\n" } + assert ok? + assert_equal "(function() {\n alert('Aye!');\n})();\n", body + end + + it 'renders .coffee files in views path' do + coffee_app { coffee :hello } + assert ok? + assert_equal "(function() {\n alert(\"Aye!\");\n})();\n", body + end + + it 'ignores the layout option' do + coffee_app { coffee :hello, :layout => :layout2 } + assert ok? + assert_equal "(function() {\n alert(\"Aye!\");\n})();\n", body + end + + it "raises error if template not found" do + mock_app { + get('/') { coffee :no_such_template } + } + assert_raise(Errno::ENOENT) { get('/') } + end + + it "passes coffee options to the coffee engine" do + coffee_app { + coffee "alert 'Aye!'\n", + :no_wrap => true + } + assert ok? + assert_equal "alert('Aye!');", body + end + + it "passes default coffee options to the coffee engine" do + mock_app { + set :coffee, :no_wrap => true # default coffee style is :nested + get '/' do + coffee "alert 'Aye!'\n" + end + } + get '/' + assert ok? + assert_equal "alert('Aye!');", body + end +end + +rescue + warn "#{$!.to_s}: skipping coffee tests" +end diff --git a/test/views/hello.coffee b/test/views/hello.coffee new file mode 100644 index 0000000000..b21e7d632d --- /dev/null +++ b/test/views/hello.coffee @@ -0,0 +1 @@ +alert "Aye!" From a852f97f52e74c488efe5d9b35952a92aca525bb Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Sun, 12 Sep 2010 23:11:22 +0200 Subject: [PATCH 52/80] Add content_type line to German CoffeeScript example. --- README.de.rdoc | 1 + 1 file changed, 1 insertion(+) diff --git a/README.de.rdoc b/README.de.rdoc index 2226d16b0f..828296458e 100644 --- a/README.de.rdoc +++ b/README.de.rdoc @@ -432,6 +432,7 @@ Das coffee-script gem und das `coffee`-Programm werden benötigt um CoffeScript- require 'coffee-script' get '/application.js' do + content_type 'text/javascript', :charset => 'utf-8' coffee :application end From beea69953b43a971bfeda7e41b5e2af6d1c84670 Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Sun, 12 Sep 2010 23:21:48 +0200 Subject: [PATCH 53/80] Skip rdiscount tests on JRuby. --- test/markdown_test.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/test/markdown_test.rb b/test/markdown_test.rb index e157d18b28..f1f12f1daa 100644 --- a/test/markdown_test.rb +++ b/test/markdown_test.rb @@ -1,6 +1,7 @@ require File.dirname(__FILE__) + '/helper' begin +fail "rdiscount not available" if defined? JRuby require 'rdiscount' class MarkdownTest < Test::Unit::TestCase From a5991f512b25ca89f66b0f5338cf4d4cb78f77fd Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Mon, 13 Sep 2010 23:15:35 +0200 Subject: [PATCH 54/80] Regenerate gemspec. Fixes rkh/sinatra issue #3. --- sinatra.gemspec | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/sinatra.gemspec b/sinatra.gemspec index 908f006e4d..ebb3bd4802 100644 --- a/sinatra.gemspec +++ b/sinatra.gemspec @@ -3,8 +3,8 @@ Gem::Specification.new do |s| s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.name = 'sinatra' - s.version = '1.0' - s.date = '2010-08-29' + s.version = '1.1.0' + s.date = '2010-09-13' s.description = "Classy web-development dressed in a DSL" s.summary = "Classy web-development dressed in a DSL" @@ -27,33 +27,43 @@ Gem::Specification.new do |s| lib/sinatra/images/500.png lib/sinatra/main.rb lib/sinatra/showexceptions.rb - lib/sinatra/tilt.rb sinatra.gemspec test/base_test.rb test/builder_test.rb + test/coffee_test.rb test/contest.rb + test/encoding_test.rb test/erb_test.rb test/erubis_test.rb test/extensions_test.rb test/filter_test.rb test/haml_test.rb + test/hello.mab test/helper.rb test/helpers_test.rb test/less_test.rb + test/liquid_test.rb test/mapped_error_test.rb + test/markaby_test.rb + test/markdown_test.rb test/middleware_test.rb test/public/favicon.ico + test/radius_test.rb + test/rdoc_test.rb test/request_test.rb test/response_test.rb test/result_test.rb test/route_added_hook_test.rb test/routing_test.rb test/sass_test.rb + test/scss_test.rb test/server_test.rb test/settings_test.rb test/sinatra_test.rb test/static_test.rb test/templates_test.rb + test/textile_test.rb + test/views/ascii.haml test/views/error.builder test/views/error.erb test/views/error.erubis @@ -61,17 +71,29 @@ Gem::Specification.new do |s| test/views/error.sass test/views/foo/hello.test test/views/hello.builder + test/views/hello.coffee test/views/hello.erb test/views/hello.erubis test/views/hello.haml test/views/hello.less + test/views/hello.liquid + test/views/hello.mab + test/views/hello.md + test/views/hello.radius + test/views/hello.rdoc test/views/hello.sass + test/views/hello.scss test/views/hello.test + test/views/hello.textile test/views/layout2.builder test/views/layout2.erb test/views/layout2.erubis test/views/layout2.haml + test/views/layout2.liquid + test/views/layout2.mab + test/views/layout2.radius test/views/layout2.test + test/views/utf8.haml ] # = MANIFEST = From 68d2d7b7e1077a79cab60a65f009678d32118e07 Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Mon, 13 Sep 2010 23:45:21 +0200 Subject: [PATCH 55/80] Fix params encoding. --- lib/sinatra/base.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/sinatra/base.rb b/lib/sinatra/base.rb index 7c6885081e..b564e3df68 100644 --- a/lib/sinatra/base.rb +++ b/lib/sinatra/base.rb @@ -1129,7 +1129,7 @@ def caller_locations Encoding.default_internal ||= Encoding.default_external def force_encoding(data) - return if data != self + return if data == self if data.respond_to? :force_encoding data.force_encoding(Encoding.default_external) elsif data.respond_to? :each_value From 3475f505caa34aceee6bb576fc91d532eb22160c Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Thu, 16 Sep 2010 14:44:37 +0200 Subject: [PATCH 56/80] Allow spaces after inline template names. Fixes GH #61. --- lib/sinatra/base.rb | 2 +- test/templates_test.rb | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/sinatra/base.rb b/lib/sinatra/base.rb index b564e3df68..007c6a3a96 100644 --- a/lib/sinatra/base.rb +++ b/lib/sinatra/base.rb @@ -824,7 +824,7 @@ def inline_templates=(file=nil) template = nil data.each_line do |line| lines += 1 - if line =~ /^@@\s*(.*)/ + if line =~ /^@@\s*(\S*)/ template = '' templates[$1.to_sym] = [template, file, lines] elsif template diff --git a/test/templates_test.rb b/test/templates_test.rb index 04b89584ef..a32277f3c8 100644 --- a/test/templates_test.rb +++ b/test/templates_test.rb @@ -84,6 +84,11 @@ def with_default_layout assert_equal "X\n= yield\nX\n", @app.templates[:layout][0] end + it 'ignores spaces after names of inline templates' do + mock_app { enable :inline_templates } + assert_equal "There's a space after 'bar'!\n\n", @app.templates[:bar][0] + end + it 'loads templates from given source file' do mock_app { set :inline_templates, __FILE__ } assert_equal "this is foo\n\n", @app.templates[:foo][0] @@ -154,6 +159,9 @@ def with_default_layout @@ foo this is foo +@@ bar +There's a space after 'bar'! + @@ layout X = yield From f96c6ef7d83b85ac5aefc489b60100ebecf47829 Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Sun, 19 Sep 2010 13:23:06 +0200 Subject: [PATCH 57/80] Add latest changes to changelog. --- CHANGES | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index f77d6db42a..32ef621444 100644 --- a/CHANGES +++ b/CHANGES @@ -15,6 +15,10 @@ behaves exactly like `sass` except for the different file extension and assuming the SCSS syntax. (Pedro Menezes, Konstantin Haase) + * Added `liquid`, `markdown`, `textile`, `rdoc`, `radius`, `markaby`, and + `coffee` rendering methods for rendering Liquid, Markdown, Textile, RDoc, + Radius, Markaby and CoffeeScript templates (Konstantin Haase) + * Broken examples for using Erubis and Haml in README have been fixed. (Nick Sutterer, Doug Ireton, Jason Stewart) @@ -40,7 +44,7 @@ none existent files Ruby added to the call stack. (Shota Fukumori, Konstantin Haase) - * Better handling of Encodings in 1.9, defaults encoding to UTF-8 and + * Better handling of encodings in 1.9, defaults params encoding to UTF-8 and respects Encoding.default_internal and Encoding.default_external. (Konstantin Haase) @@ -53,10 +57,12 @@ * Fixes an issue with inline templates in modular applications that manually call `run!`. (Konstantin Haase) + * Spaces after inline template names are now ignored (Konstantin Haase) + * It's now possible to use Sinatra with different package management systems defining a custom require. (Konstantin Haase) - * You can now use #settings from class level for convenience. + * You can now use #settings method from class and top level for convenience. (Konstantin Haase) * Prevents a memory leak on 1.8.6 is production mode. Note, however, that @@ -66,6 +72,9 @@ * Lighthouse has been dropped in favor of GitHub issues. + * Tilt is now a dependency and therefore no longer ships bundled with + Sinatra. (Ryan Tomayko, Konstantin Haase) + = 1.0 / 2010-03-23 * It's now possible to register blocks to run after each request using From 7f2b234ffee4115c6608aac2c0412da9e22aad42 Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Sun, 19 Sep 2010 13:26:26 +0200 Subject: [PATCH 58/80] Allow spaces in inline template names (still ignoring trailing spaces). --- lib/sinatra/base.rb | 2 +- test/templates_test.rb | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/sinatra/base.rb b/lib/sinatra/base.rb index 007c6a3a96..71a395804e 100644 --- a/lib/sinatra/base.rb +++ b/lib/sinatra/base.rb @@ -824,7 +824,7 @@ def inline_templates=(file=nil) template = nil data.each_line do |line| lines += 1 - if line =~ /^@@\s*(\S*)/ + if line =~ /^@@\s*(.*\S)\s*$/ template = '' templates[$1.to_sym] = [template, file, lines] elsif template diff --git a/test/templates_test.rb b/test/templates_test.rb index a32277f3c8..3eb7fd342f 100644 --- a/test/templates_test.rb +++ b/test/templates_test.rb @@ -87,6 +87,7 @@ def with_default_layout it 'ignores spaces after names of inline templates' do mock_app { enable :inline_templates } assert_equal "There's a space after 'bar'!\n\n", @app.templates[:bar][0] + assert_equal "this is not foo\n\n", @app.templates[:"foo bar"][0] end it 'loads templates from given source file' do @@ -162,6 +163,9 @@ def with_default_layout @@ bar There's a space after 'bar'! +@@ foo bar +this is not foo + @@ layout X = yield From 996cf5b0c54a683f54bc602cea1899e6ac4a0dce Mon Sep 17 00:00:00 2001 From: Steve Shreeve Date: Wed, 28 Jul 2010 10:27:47 -0400 Subject: [PATCH 59/80] correct the HTTP status code range --- lib/sinatra/base.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/sinatra/base.rb b/lib/sinatra/base.rb index 71a395804e..d06c0c3241 100644 --- a/lib/sinatra/base.rb +++ b/lib/sinatra/base.rb @@ -644,7 +644,7 @@ def invoke(&block) end when res.respond_to?(:each) @response.body = res - when (100...599) === res + when (100..599) === res @response.status = res end From c3f167f72810c6ab5105daf42e71a76493ba9489 Mon Sep 17 00:00:00 2001 From: Eric Marden Date: Mon, 26 Jul 2010 23:32:00 -0500 Subject: [PATCH 60/80] MOD: docs for testing forgot to require test/unit --- README.rdoc | 1 + 1 file changed, 1 insertion(+) diff --git a/README.rdoc b/README.rdoc index a03ed1a3e9..c5f1301b00 100644 --- a/README.rdoc +++ b/README.rdoc @@ -724,6 +724,7 @@ or framework. {Rack::Test}[http://gitrdoc.com/brynary/rack-test] is recommended: require 'my_sinatra_app' + require 'test/unit' require 'rack/test' class MyAppTest < Test::Unit::TestCase From 1cf1c76c8914962936536e596b36b14acf433972 Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Sun, 19 Sep 2010 23:11:20 +0200 Subject: [PATCH 61/80] Credit Steve Shreeve and Eric Marden in CHANGES. --- CHANGES | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index 32ef621444..38118e072d 100644 --- a/CHANGES +++ b/CHANGES @@ -19,8 +19,8 @@ `coffee` rendering methods for rendering Liquid, Markdown, Textile, RDoc, Radius, Markaby and CoffeeScript templates (Konstantin Haase) - * Broken examples for using Erubis and Haml in README have been fixed. - (Nick Sutterer, Doug Ireton, Jason Stewart) + * Broken examples for using Erubis, Haml and Test::Unit in README have been + fixed. (Nick Sutterer, Doug Ireton, Jason Stewart, Eric Marden) * Sinatra is now able to use Tilt versions including numbers > 9, as in 0.10. (Konstantin Haase) @@ -70,6 +70,8 @@ of parsing templates again on that version. It is recommended to use at least Ruby 1.8.7. (Konstantin Haase) + * 599 now is a legal status code. (Steve Shreeve) + * Lighthouse has been dropped in favor of GitHub issues. * Tilt is now a dependency and therefore no longer ships bundled with From f8e0a34a4448e58fdfe46bfe7631bb7cb316b7a6 Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Mon, 20 Sep 2010 12:09:33 +0200 Subject: [PATCH 62/80] Set outvar for any template engine (so it is usable from liquid, textile and markdown). Also, only set it if not present, allowing to override it. --- lib/sinatra/base.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/sinatra/base.rb b/lib/sinatra/base.rb index d06c0c3241..d87aaf4629 100644 --- a/lib/sinatra/base.rb +++ b/lib/sinatra/base.rb @@ -301,12 +301,10 @@ module Templates include Tilt::CompileSite def erb(template, options={}, locals={}) - options[:outvar] = '@_out_buf' render :erb, template, options, locals end def erubis(template, options={}, locals={}) - options[:outvar] = '@_out_buf' render :erubis, template, options, locals end @@ -368,6 +366,7 @@ def coffee(template, options={}, locals={}) def render(engine, data, options={}, locals={}, &block) # merge app-level options options = settings.send(engine).merge(options) if settings.respond_to?(engine) + options[:outvar] ||= '@_out_buf' # extract generic options locals = options.delete(:locals) || locals || {} From 94022d62652d2a903b325b0f13dc7a2c3316ed32 Mon Sep 17 00:00:00 2001 From: Gabriel Andretta Date: Tue, 21 Sep 2010 08:45:05 +0200 Subject: [PATCH 63/80] Add README's spanish translation. Signed-off-by: Konstantin Haase --- README.es.rdoc | 664 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 664 insertions(+) create mode 100644 README.es.rdoc diff --git a/README.es.rdoc b/README.es.rdoc new file mode 100644 index 0000000000..4305943375 --- /dev/null +++ b/README.es.rdoc @@ -0,0 +1,664 @@ += Sinatra + +Sinatra es un DSL (Lenguaje Específico de Dominio) para crear +aplicaciones web rápidamente en Ruby con un mínimo esfuerzo: + + # miapp.rb + require 'rubygems' + require 'sinatra' + get '/' do + 'Hola mundo!' + end + +Instalá la gem (gema) y ejecutá la aplicación con: + + sudo gem install sinatra + ruby miapp.rb + +Podés verla en: http://localhost:4567 + +== Rutas + +En Sinatra, una ruta es un método HTTP apareado con un patrón de una URL. +Cada ruta es asociada con un bloque: + + get '/' do + .. mostrar algo .. + end + + post '/' do + .. crear algo .. + end + + put '/' do + .. actualizar algo .. + end + + delete '/' do + .. aniquilar algo .. + end + +Las rutas son comparadas en el orden en el que son definidas. La primer ruta +que coincide con la petición es invocada. + +Los patrones de las rutas pueden incluir parámetros nombrados, accesibles a +través de el hash params: + + get '/hola/:nombre' do + # coincide con "GET /hola/foo" y "GET /hola/bar" + # params[:nombre] es 'foo' o 'bar' respectivamente + "Hola #{params[:nombre]}!" + end + +También podés acceder a los parámetros nombrados usando parámetros de bloque: + + get '/hola/:nombre' do |n| + "Hola #{n}!" + end + +Los patrones de ruta también pueden incluir parámetros splat o wildcard +(plaf o comodín), accesibles a través del arreglo +params[:splat]. + + get '/decir/*/al/*' do + # coincide con /decir/hola/al/mundo + params[:splat] # => ["hola", "mundo"] + end + + get '/descargar/*.*' do + # coincide con /descargar/path/al/archivo.xml + params[:splat] # => ["path/al/archivo", "xml"] + end + +Búsqueda de rutas con Expresiones Regulares: + + get %r{/hola/([\w]+)} do + "Hola, #{params[:captures].first}!" + end + +O con un parámetro de bloque: + + get %r{/hola/([\w]+)} do |c| + "Hola, #{c}!" + end + +Las rutas pueden incluir una variedad de condiciones de selección, como por +ejemplo el user agent (agente de usuario): + + get '/foo', :agent => /Songbird (\d\.\d)[\d\/]*?/ do + "Estás usando la versión #{params[:agent][0]} de Songbird" + end + + get '/foo' do + # Coincide con browsers que no sean songbird + end + +== Archivos estáticos + +Los archivos estáticos son servidos desde el directorio público +./public. Podés especificar una ubicación diferente ajustando la +opción :public: + + set :public, File.dirname(__FILE__) + '/estaticos' + +Notá que el nombre del directorio público no está incluido en la URL. Por +ejemplo, el archivo ./public/css/style.css se accede a través de +http://ejemplo.com/css/style.css. + +== Vistas / Plantillas + +Se asume que las plantillas (templates) están ubicadas directamente +bajo el directorio ./views. Para usar un directorio de vistas +(views) diferente: + + set :views, File.dirname(__FILE__) + '/plantillas' + +Es importante acordarse que siempre tenés que referenciar a las plantillas con +símbolos, incluso cuando se encuentran en un subdirectorio (en este caso tenés +que usar :'subdir/plantilla'). Los métodos de renderización van a +renderizar directamente cualquier string que reciban como argumento. + +=== Plantillas Haml + +La gem/librería haml es necesaria para para renderizar plantillas HAML: + + ## Vas a necesitar requerir haml en tu app + require 'haml' + + get '/' do + haml :index + end + +Renderiza ./views/index.haml. +{Haml's options}[http://haml-lang.com/docs/yardoc/file.HAML_REFERENCE.html#options] + +Las {opciones de Haml}[http://haml-lang.com/docs/yardoc/file.HAML_REFERENCE.html#options] +pueden ser ajustadas globalmente a través de las configuraciones de Sinatra, +ver {Opciones y Configuraciones}[http://www.sinatrarb.com/configuration.html], +y reemplazadas individualmente. + + set :haml, { :format => :html5 } # el formato por defecto de Haml es :xhtml + + get '/' do + haml :index, :haml_options => { :format => :html4 } # reemplazado + end + +=== Plantillas Erb + + ## Vas a necesitar requerir erb en tu app + require 'erb' + + get '/' do + erb :index + end + +Renderiza ./views/index.erb + +=== Erubis + +La gem/librería erubis es necesaria para renderizar plantillas erubis: + + ## Vas a necesitar requerir erubis en tu app + require 'erubis' + + get '/' do + erubis :index + end + +Renderiza ./views/index.erubis + +=== Plantillas Builder + +La gem/librería builder es necesaria para renderizar plantillas builder: + + ## Vas a necesitar requerir builder en tu app + require 'builder' + + get '/' do + content_type 'application/xml', :charset => 'utf-8' + builder :index + end + +Renderiza ./views/index.builder. + +=== Plantillas Sass + +La gem/librería sass es necesaria para renderizar plantillas Sass: + + ## Vas a necesitar requerir haml o sass en tu app + require 'sass' + + get '/stylesheet.css' do + content_type 'text/css', :charset => 'utf-8' + sass :stylesheet + end + +Renderiza ./views/stylesheet.sass. + +Las {opciones de Sass}[http://sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html#options] +pueden ser ajustadas globalmente a través de las configuraciones de Sinatra, +ver {Opciones y Configuraciones}[http://www.sinatrarb.com/configuration.html], +y reemplazadas individualmente. + + set :sass, { :style => :compact } # el estilo por defecto de Sass es :nested + + get '/stylesheet.css' do + content_type 'text/css', :charset => 'utf-8' + sass :stylesheet, :style => :expanded # reemplazado + end + +=== Plantillas Less + +La gem/librería less es necesaria para renderizar plantillas Less: + + ## Vas a necesitar requerir less en tu app + require 'less' + + get '/stylesheet.css' do + content_type 'text/css', :charset => 'utf-8' + less :stylesheet + end + +Renderiza ./views/stylesheet.less. + +=== Plantillas Inline + + get '/' do + haml '%div.titulo Hola Mundo' + end + +Renderiza el template contenido en el string. + +=== Accediendo a Variables en Plantillas + +Las plantillas son evaluadas dentro del mismo contexto que los manejadores de +ruta (route handlers). Las variables de instancia asignadas en los +manejadores de ruta son accesibles directamente por las plantillas: + + get '/:id' do + @foo = Foo.find(params[:id]) + haml '%h1= @foo.nombre' + end + +O es posible especificar un Hash explícito de variables locales: + + get '/:id' do + foo = Foo.find(params[:id]) + haml '%h1= foo.nombre', :locals => { :foo => foo } + end + +Esto es usado típicamente cuando se renderizan plantillas como parciales desde +adentro de otras plantillas. + +=== Plantillas Inline + +Las plantillas pueden ser definidas al final del archivo fuente: + + require 'rubygems' + require 'sinatra' + + get '/' do + haml :index + end + + __END__ + + @@ layout + %html + = yield + + @@ index + %div.titulo Hola mundo!!!!! + +NOTA: únicamente las plantillas inline definidas en el archivo fuente que +requiere sinatra son cargadas automáticamente. Llamá +`enable :inline_templates` explícitamente si tenés plantillas inline en otros +archivos fuente. + +=== Plantillas Nombradas + +Las plantillas también pueden ser definidas usando el método top-level +template: + + template :layout do + "%html\n =yield\n" + end + + template :index do + '%div.titulo Hola Mundo!' + end + + get '/' do + haml :index + end + +Si existe una plantilla con el nombre "layout", va a ser usada cada vez que +una plantilla es renderizada. Podés desactivar los layouts pasando +:layout => false. + + get '/' do + haml :index, :layout => !request.xhr? + end + +== Ayudantes (Helpers) + +Usá el método top-level helpers para definir métodos ayudantes que +pueden ser utilizados dentro de los manejadores de rutas y las plantillas: + + helpers do + def bar(nombre) + "#{nombre}bar" + end + end + + get '/:nombre' do + bar(params[:nombre]) + end + +== Filtros (Filters) + +Los Before Filters son evaluados antes de cada petición dentro del contexto de +la petición y pueden modificar la petición y la respuesta. Las variables de +instancia asignadas en los filtros son accesibles por las rutas y las +plantillas: + + before do + @nota = 'Hey!' + request.path_info = '/foo/bar/baz' + end + + get '/foo/*' do + @nota #=> 'Hey!' + params[:splat] #=> 'bar/baz' + end + +Los After Filter son evaluados después de cada petición dentro del contexto de +la petición y también pueden modificar la petición y la respuesta. Las +variables de instancia asignadas en before filters y rutas son accesibles por +los after filters: + + after do + puts response.status + end + +Los filtros aceptan un patrón opcional, que cuando está presente causa que los +mismos sean evaluados únicamente si el path de la petición coincide con ese +patrón: + + before '/protegido/*' do + autenticar! + end + + after '/crear/:slug' do |slug| + session[:ultimo_slug] = slug + end + +== Interrumpiendo (Halting) + +Para detener inmediatamente una petición dentro de un filtro o una ruta usá: + + halt + +También podés especificar el estado ... + + halt 410 + +O el cuerpo ... + + halt 'esto va a ser el cuerpo' + +O los dos ... + + halt 401, 'salí de acá!' + +Con encabezados ... + + halt 402, { 'Content-Type' => 'text/plain' }, 'venganza' + +== Pasando + +Una ruta puede pasarle el procesamiento a la siguiente ruta que coincida con +la petición usando pass: + + get '/adivina/:quien' do + pass unless params[:quien] == 'Franco' + 'Adivinaste!' + end + + get '/adivina/*' do + 'Erraste!' + end + +Se sale inmediatamente del bloque de la ruta y se le pasa el control a la +siguiente ruta que coincida. Si no coincide ninguna ruta, se devuelve un 404. + +== Configuración + +Ejecutar una vez, en el inicio, en cualquier entorno: + + configure do + ... + end + +Ejecutar únicamente cuando el entorno (la variable de entorno RACK_ENV) es +:production: + + configure :production do + ... + end + +Ejecutar cuando el entorno es :production o :test: + + configure :production, :test do + ... + end + +== Manejo de errores + +Los manejadores de errores se ejecutan dentro del mismo contexto que las rutas +y los before filters, lo que significa que podés usar, por ejemplo, +haml, erb, halt, etc. + +=== No encontrado (Not Found) + +Cuando se eleva una excepción Sinatra::NotFound, o el código de +estado de la respuesta es 404, el manejador not_found es invocado: + + not_found do + 'No existo' + end + +=== Error + +El manejador +error+ es invocado cada vez que una excepción es elevada +desde un bloque de ruta o un filtro. El objeto de la excepción se puede +obtener de la variable Rack sinatra.error: + + error do + 'Disculpá, ocurrió un error horrible - ' + env['sinatra.error'].name + end + +Errores personalizados: + + error MiErrorPersonalizado do + 'Lo que pasó fue...' request.env['sinatra.error'].message + end + +Entonces, si pasa esto: + + get '/' do + raise MiErrorPersonalizado, 'algo malo' + end + +Obtenés esto: + + Lo que pasó fue... algo malo + +También, podés instalar un manejador de errores para un código de estado: + + error 403 do + 'Acceso prohibido' + end + + get '/secreto' do + 403 + end + +O un rango: + + error 400..510 do + 'Boom' + end + +Sinatra instala manejadores not_found y error especiales +cuando se ejecuta dentro del entorno de desarrollo "development". + +== Tipos Mime + +Cuando usás send_file o archivos estáticos tal vez tengas tipos mime +que Sinatra no entiende. Usá +mime_type+ para registrarlos a través de la +extensión de archivo: + + mime_type :foo, 'text/foo' + +También lo podés usar con el ayudante +content_type+: + + content_type :foo + +== Rack Middleware + +Sinatra corre sobre Rack[http://rack.rubyforge.org/], una interfaz minimalista +que es un estándar para frameworks webs escritos en Ruby. Una de las +capacidades más interesantes de Rack para los desarrolladores de aplicaciones +es el soporte de "middleware" -- componentes que se ubican entre el servidor y +tu aplicación, supervisando y/o manipulando la petición/respuesta HTTP para +proporcionar varios tipos de funcionalidades comunes. + +Sinatra hace muy sencillo construir tuberías de Rack middleware a través del +método top-level +use+: + + require 'sinatra' + require 'mi_middleware_personalizado' + + use Rack::Lint + use MiMiddlewarePersonalizado + + get '/hola' do + 'Hola Mundo' + end + +Las semánticas de +use+ son idénticas a las definidas para el DSL +Rack::Builder[http://rack.rubyforge.org/doc/classes/Rack/Builder.html] +(más frecuentemente usadas desde archivos rackup). Por ejemplo, el método ++use+ acepta argumentos múltiples/variables así como bloques: + + use Rack::Auth::Basic do |nombre_de_usuario, password| + nombre_de_usuario == 'admin' && password == 'secreto' + end + +Rack es distribuido con una variedad de middleware estándar para logging, +debugging, enrutamiento URL, autenticación, y manejo de sesiones. Sinatra +usa muchos de estos componentes automáticamente de acuerdo a su configuración +para que típicamente no tengas que usarlas (con +use+) explícitamente. + +== Testing + +Los test para las aplicaciones Sinatra pueden ser escritos utilizando +cualquier framework o librería de testing basada en Rack. Se recomienda +usar {Rack::Test}[http://gitrdoc.com/brynary/rack-test]: + + require 'mi_app_sinatra' + require 'rack/test' + + class MiAppTest < Test::Unit::TestCase + include Rack::Test::Methods + + def app + Sinatra::Application + end + + def test_por_defecto + get '/' + assert_equal 'Hola Mundo!', last_response.body + end + + def test_con_parametros + get '/saludar', :name => 'Franco' + assert_equal 'Hola Franco!', last_response.body + end + + def test_con_entorno_rack + get '/', {}, 'HTTP_USER_AGENT' => 'Songbird' + assert_equal "Estás usando Songbird!", last_response.body + end + end + +NOTA: El módulo Sinatra::Test y la clase Sinatra::TestHarness están +deprecados a partir de la versión 0.9.2. + +== Sinatra::Base - Middleware, Librerías, y Aplicaciones Modulares + +Definir tu aplicación en el top-level funciona bien para micro-aplicaciones +pero trae inconvenientes considerables a la hora de construir componentes +reusables como Rack middleware, Rails metal, simple librerías con un +componente de servidor, o incluso extensiones de Sinatra. El DSL de top-level +contamina el espacio de nombres de Object y asume una configuración apropiada +para micro-aplicaciones (por ejemplo, un único archivo de aplicación, los +directorios ./public y ./views, logging, página con detalles de excepción, +etc.). Ahí es donde Sinatra::Base entra en el juego: + + + require 'sinatra/base' + + class MiApp < Sinatra::Base + set :sessions, true + set :foo, 'bar' + + get '/' do + 'Hola Mundo!' + end + end + +La clase MiApp es un componente Rack independiente que puede actuar como Rack +middleware, una aplicación Rack, o Rails metal. Podés usar (con +use+) o +ejecutar (con +run+) esta clase desde un archivo rackup +config.ru+; o, +controlar un componente de servidor provisto como una librería: + + MiApp.run! :host => 'localhost', :port => 9090 + +Las subclases de Sinatra::Base tienen disponibles exactamente los mismos +métodos que los provistos por el DSL de top-level. La mayoría de las +aplicaciones top-level se pueden convertir en componentes Sinatra::Base con +dos modificaciones: + +* Tu archivo debe requerir +sinatra/base+ en lugar de +sinatra+; de otra + manera, todos los métodos del DSL de sinatra son importados dentro del + espacio de nombres principal. +* Poné las rutas, manejadores de errores, filtros y opciones de tu aplicación + en una subclase de Sinatra::Base. + ++Sinatra::Base+ es una pizarra en blanco. La mayoría de las opciones están +desactivadas por defecto, incluyendo el servidor incorporado. Mirá +{Opciones y Configuraciones}[http://sinatra.github.com/configuration.html] +para detalles sobre las opciones disponibles y su comportamiento. + +NOTA AL MÁRGEN: el DSL de top-level de Sinatra es implementado usando un +simple sistema de delegación. La clase +Sinatra::Application+ -- una subclase +especial de Sinatra::Base -- recive todos los mensajes :get, :put, :post, +:delete, :before, :error, :not_found, :configure y :set enviados al top-level. +Pegale una mirada al código: acá está el +{Sinatra::Delegator mixin}[http://github.com/sinatra/sinatra/blob/ceac46f0bc129a6e994a06100aa854f606fe5992/lib/sinatra/base.rb#L1128] +que es {incluido en el espacio de nombres principal}[http://github.com/sinatra/sinatra/blob/ceac46f0bc129a6e994a06100aa854f606fe5992/lib/sinatra/main.rb#L28] + +== Línea de comandos + +Las aplicaciones Sinatra pueden ser ejecutadas directamente: + + ruby miapp.rb [-h] [-x] [-e ENTORNO] [-p PUERTO] [-o HOST] [-s MANEJADOR] + +Las opciones son: + + -h # ayuda + -p # asigna el puerto (4567 es usado por defecto) + -o # asigna el host (0.0.0.0 es usado por defecto) + -e # asigna el entorno (development es usado por defecto) + -s # especifica el servidor/manejador rack (thin es usado por defecto) + -x # activa el mutex lock (está desactivado por defecto) + +== A la vanguardia + +Si querés usar el código de Sinatra más reciente, cloná el repositorio +localmente y ejecutá tu aplicación, asegurándote que el directorio +sinatra/lib esté en el LOAD_PATH: + + cd miapp + git clone git://github.com/sinatra/sinatra.git + ruby -Isinatra/lib miapp.rb + +Otra opción consiste en agregar el directorio sinatra/lib al +LOAD_PATH dentro de tu aplicación: + + $LOAD_PATH.unshift File.dirname(__FILE__) + '/sinatra/lib' + require 'rubygems' + require 'sinatra' + + get '/acerca-de' do + "Estoy usando la versión " + Sinatra::VERSION + end + +Para actualizar el código fuente de Sinatra en el futuro: + + cd miproyecto/sinatra + git pull + +== Más + +* {Sito web del proyecto}[http://www.sinatrarb.com/] - Documentación + adicional, noticias, y enlaces a otros recursos. +* {Contribuyendo}[http://www.sinatrarb.com/contributing] - ¿Encontraste un + error?. ¿Necesitás ayuda?. ¿Tenés un parche?. +* {Lighthouse}[http://sinatra.lighthouseapp.com] - Seguimiento de problemas y + planeamiento de lanzamientos. +* {Twitter}[http://twitter.com/sinatra] +* {Lista de Correo}[http://groups.google.com/group/sinatrarb/topics] +* {IRC: #sinatra}[irc://chat.freenode.net/#sinatra] en http://freenode.net From 945a1f8dbd6657e419f3a33cbf516eb296fa44ee Mon Sep 17 00:00:00 2001 From: mig Date: Tue, 21 Sep 2010 08:46:20 +0200 Subject: [PATCH 64/80] Add README's French translation. Signed-off-by: Konstantin Haase --- README.fr.rdoc | 653 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 653 insertions(+) create mode 100644 README.fr.rdoc diff --git a/README.fr.rdoc b/README.fr.rdoc new file mode 100644 index 0000000000..c7decf7301 --- /dev/null +++ b/README.fr.rdoc @@ -0,0 +1,653 @@ += Sinatra + +Sinatra est une DSL pour créer rapidement des applications web en Ruby et sans +effort: + + # mon_application.rb + require 'rubygems' + require 'sinatra' + get '/' do + 'Bonjour Monde!' + end + +Installez le Gem et lancez avec: + + sudo gem install sinatra + ruby mon_application.rb + +Voir ici: http://localhost:4567 + +== Routes + +Dans Sinatra, une route est une méthode HTTP couplée à un pattern URL. +Chaque route est associée à un bloc: + + get '/' do + .. montrer quelque chose .. + end + + post '/' do + .. créer quelque chose .. + end + + put '/' do + .. changer quelque chose .. + end + + delete '/' do + .. effacer quelque chose .. + end + +Les routes sont comparées dans l'ordre où elles ont été définies. La première route qui +correspond à la requête est invoquée. + +Les patterns peuvent inclure des paramètres, accessibles par l'intermédiaire du +hash params: + + get '/bonjour/:nom' do + # répond aux requêtes "GET /bonjour/foo" et "GET /bonjour/bar" + # params[:nom] est 'foo' ou 'bar' + "Bonjour #{params[:nom]}!" + end + +Vous pouvez aussi les nommer directement dans les paramètres du bloc comme ceci: + + get '/bonjour/:nom' do |n| + "Bonjour #{n}!" + end + +Une route peut contenir un splat (caractère joker), accessible par l'intermédiaire de +la liste params[:splat]. + + get '/dire/*/a/*' do + # répondrait à /dire/bonjour/a/monde + params[:splat] # => ["bonjour", "monde"] + end + + get '/telecharger/*.*' do + # répondrait à /telecharger/chemin/vers/fichier.xml + params[:splat] # => ["chemin/vers/fichier", "xml"] + end + +Une route peut s'exprimer avec une Expression Régulière: + + get %r{/bonjour/([\w]+)} do + "Bonjour, #{params[:captures].first}!" + end + +Là aussi on peut utiliser les paramètres de bloc: + + get %r{/bonjour/([\w]+)} do |c| + "Bonjour, #{c}!" + end + +On peut ajouter d'autres paramètres à une route, comme par exemple le "user agent": + + get '/foo', :agent => /Songbird (\d\.\d)[\d\/]*?/ do + "Vous utilisez Songbird version #{params[:agent][0]}" + end + + get '/foo' do + # Correspond à tous les autres navigateurs + end + +== Fichiers Statiques + +Par défaut, les fichiers statiques sont considérés dans le dossier ./public. +Vous pouvez changer ce dossier pour un autre nom grace à l'option :public: + + set :public, File.dirname(__FILE__) + '/statique' + +Notez que le nom du dossier publique n'est pas inclus dans l'URL. Un fichier sous +./public/css/style.css est appelé avec l'URL: http://exemple.com/css/style.css. + +== Vues / Templates + +Par défaut, les templates sont cherchés dans le dossier ./views. +Pour utiliser un autre dossier, il faut le déclarer: + + set :views, File.dirname(__FILE__) + '/templates' + +Il est important de noter que les templates sont toujours référencés +sous forme de symboles. Même s'il s'agit d'un sous-répertoire. Dans ce +cas, il faut le déclarer de cette façon: :'sous_repertoire/template'. +Si c'est du texte qui est passé à une méthode de rendu, elle considère le +texte comme le contenu du template. + +=== Templates Haml + +Le Gem HAML est nécessaire pour utiliser la function de rendu haml: + + ## Charger le Gem HAML + require 'haml' + + get '/' do + haml :index + end + +Utilisera le template: ./views/index.haml. + +{Les options de Haml}[http://haml-lang.com/docs/yardoc/file.HAML_REFERENCE.html#options] +peuvent se manipuler directement avec la configuration de Sinatra, +voir {Options et Configuration}[http://www.sinatrarb.com/configuration.html] +qui supportent aussi la réécriture (surcharge) comme dans cet exemple. + + set :haml, {:format => :html5 } # le format par défaut dans Haml est :xhtml + + get '/' do + haml :index, :haml_options => {:format => :html4 } # surcharge + end + + +=== Templates Erb + + ## Charger la bibliothèque Erb + require 'erb' + + get '/' do + erb :index + end + +Utilisera le template: ./views/index.erb + +=== Erubis + +Le Gem Erubis est nécessaire pour utiliser la function de rendu erubis: + + ## Charger la bibliothèque Erubis + require 'erubis' + + get '/' do + erubis :index + end + +Utilisera le template: ./views/index.erubis + +=== Templates Builder + +Le Gem Builder est nécessaire pour utiliser la function de rendu builder: + + ## Charger la bibliothèque Builder + require 'builder' + + get '/' do + content_type 'application/xml', :charset => 'utf-8' + builder :index + end + +Utilisera le template: ./views/index.builder. + +=== Templates Sass + +Le Gem Sass est nécessaire pour utiliser la function de rendu sass: + + ## Charger la bibliothèque Haml ou Sass + require 'sass' + + get '/stylesheet.css' do + content_type 'text/css', :charset => 'utf-8' + sass :stylesheet + end + +Utilisera le template: ./views/stylesheet.sass. + +{Les options de Sass}[http://sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html#options] +peuvent se manipuler directement avec la configuration de Sinatra, +voir {Options et Configuration}[http://www.sinatrarb.com/configuration.html] +qui supportent aussi la réécriture (surcharge) comme dans cet exemple. + + set :sass, {:style => :compact } # le style par défaut dans Sass est :nested + + get '/stylesheet.css' do + content_type 'text/css', :charset => 'utf-8' + sass :stylesheet, :style => :expanded # surcharge + end + +=== Templates Less + +Le Gem Less est nécessaire pour utiliser la function de rendu less: + + ## Charger la bibliothèque Less + require 'less' + + get '/stylesheet.css' do + content_type 'text/css', :charset => 'utf-8' + less :stylesheet + end + +Utilisera le template: ./views/stylesheet.less. + +=== Templates sans fichier + + get '/' do + haml '%div.title Bonjour Monde' + end + +Utilisera le texte passé en argument pour générer la page, au lieu d'aller +chercher le texte dans un fichier. + +=== Accéder aux variables dans un Template + +Un template est évalué dans le même contexte que l'endroit d'où il a été +appelé (gestionnaire de route). Les variables d'instance déclarées dans le +gestionnaire de route sont directement accessible dans le template: + + get '/:id' do + @foo = Foo.find(params[:id]) + haml '%h1= @foo.nom' + end + +Alternativement, on peut passer un Hash contenant des variables locales: + + get '/:id' do + foo = Foo.find(params[:id]) + haml '%h1= foo.nom', :locals => { :foo => foo } + end + +Ceci est généralement utilisé lorsque l'on veut utiliser un template comme partiel +(depuis un autre template) et qu'il est donc nécessaire d'adapter les noms de variables. + +=== Templates dans le fichier source + +Des templates peuvent être définis dans le fichier source comme ceci: + + require 'rubygems' + require 'sinatra' + + get '/' do + haml :index + end + + __END__ + + @@ layout + %html + = yield + + @@ index + %div.title Bonjour Monde!!!!! + +NOTE: Les templates dans un fichier source qui contient le chargement de Sinatra +sont automatiquements chargés. Si vous avez des templates dans d'autres fichiers source, +il faut explicitement le déclarer: `enable :inline_templates` . + +=== La methode Template + +Les templates peuvent aussi être définis grâce à la méthode de haut niveau template: + + template :layout do + "%html\n =yield\n" + end + + template :index do + '%div.title Bonjour Monde!' + end + + get '/' do + haml :index + end + +Si un template nommé "layout" existe, il sera utilisé à chaque fois qu'un template +sera affiché. Vous pouvez désactivez le layout à tout moment en passant +:layout => false. + + get '/' do + haml :index, :layout => !request.xhr? + end + +== Helpers + +Utilisez la méthode de haut niveau helpers pour définir des routines qui +seront accessibles des vos gestionnaires de route et dans vos templates: + + helpers do + def bar(nom) + "#{nom}bar" + end + end + + get '/:nom' do + bar(params[:nom]) + end + +== Filtres + +Un filtre before est évalué avant n'importe quelle requête, dans le +contexte de celle-ci, et peut modifier la requête ou la réponse. Les variables +d'instance déclarées dans le filtre sont accessibles au gestionnaire de route +et au template: + + before do + @note = 'Coucou!' + request.path_info = '/foo/bar/baz' + end + + get '/foo/*' do + @note #=> 'Coucou!' + params[:splat] #=> 'bar/baz' + end + +Un filtre after est évalué après chaque requête, dans le contexte +de celle-ci et de la réponse. Toutes les variables d'instance déclarées dans +un filtre before, le gestionnaire de route sont accessibles dans +le filtre after: + + after do + puts response.status + end + +En option, on peut passer un pattern au filtre, ce qui le rend actif uniquement +si la requête correspond au pattern en question: + + before '/secret/*' do + authentification! + end + + after '/faire/:travail' do |travail| + session[:dernier_travail] = travail + end + +== Halt + +Pour arrêter immediatement la requête dans un filtre ou un gestionnaire de route: + + halt + +Vous pouvez aussi passer le code status ... + + halt 410 + +Ou le texte ... + + halt 'Ceci est le texte' + +Ou les deux ... + + halt 401, 'Partez!' + +Ainsi que les headers ... + + halt 402, {'Content-Type' => 'text/plain'}, 'revanche' + +== Passer + +Une route peut passer son tour avec pass: + + get '/devine/:qui' do + pass unless params[:qui] == 'Frank' + "Tu m'as eu!" + end + + get '/devine/*' do + 'Manqué!' + end + +On sort donc immédiatement de ce gestionnaire et on continue à chercher +dans les pattern suivant, le prochain qui correspond à la requête. +Si aucun des patterns suivant ne correspond, un code 404 est retourné. + +== Configuration + +Lancé une seule fois au démarrage de tous les environnements: + + configure do + ... + end + +Lancé si l'environnement (variable RACK_ENV environment) est défini comme +:production: + + configure :production do + ... + end + +Lancé si l'environnement est :production ou +:test: + + configure :production, :test do + ... + end + +== Gérer les erreurs + +Les gestionnaires d'erreur tournent dans le même contexte que les routes ou les +filtres, ce qui veut dire que vous avez accès (entre autres) aux bons vieux +haml, erb, halt, etc. + +=== Pas Trouvé + +Quand une exception Sinatra::NotFound est soulevée, ou que le code status +est 404, le gestionnaire not_found est invoqué: + + not_found do + 'Pas moyen de trouver ce que vous cherchez' + end + +=== Erreur + +Le gestionnaire +error+ est invoqué à chaque fois qu'une exception est soulevée dans une route +ou un filtre. L'objet exception est accessible via la variable Rack sinatra.error: + + error do + 'Désolé mais une méchante erreur est survenue - ' + env['sinatra.error'].name + end + +Erreur sur mesure: + + error MonErreurSurMesure do + 'Donc il est arrivé ceci...' + request.env['sinatra.error'].message + end + +Donc si ceci arrive: + + get '/' do + raise MonErreurSurMesure, 'quelque chose de mal' + end + +Vous obtenez ça: + + Donc il est arrivé ceci... quelque chose de mal + +Alternativement, vous pouvez avoir un gestionnaire d'erreur associé à un code particulier: + + error 403 do + 'Accès interdit' + end + + get '/secret' do + 403 + end + +Ou un interval: + + error 400..510 do + 'Boom' + end + +Sinatra installe pour vous quelques gestionnaires not_found et error +génériques lorsque vous êtes en environnement development. + +== Types Mime + +Quand vous utilisez send_file et que le fichier possède une extension que Sinatra +ne reconnaît pas, utilisez +mime_type+ pour le déclarer: + + mime_type :foo, 'text/foo' + +Vous pouvez aussi l'utiliser avec +content_type+: + + content_type :foo + +== Les Middlewares Rack + +Sinatra tourne avec Rack[http://rack.rubyforge.org/], une interface standard +et minimale pour les web frameworks Ruby. Un des points forts de Rack est le +support de ce que l'on appelle des "middlewares" -- composant qui vient se situer +entre le serveur et votre application, et dont le but est de visualiser/manipuler la +requête/réponse HTTP, et d'offrir divers fonctionnalités classiques. + +Sinatra permet de construire facilement des middlewares Rack via la méthode de +haut niveau +use+: + + require 'sinatra' + require 'mon_middleware_perso' + + use Rack::Lint + use MonMiddlewarePerso + + get '/bonjour' do + 'Bonjour Monde' + end + +La sémantique de +use+ est identique à celle définie dans la DSL de +Rack::Builder[http://rack.rubyforge.org/doc/classes/Rack/Builder.html] +(le plus souvent utilisée dans un fichier rackup). Par exemple, la méthode +use+ +accepte divers arguments ainsi que des blocs: + + use Rack::Auth::Basic do |login, password| + login == 'admin' && password == 'secret' + end + +Rack est distribué avec une bonne variété de middlewares standards pour les logs, +debuguer, faire du routage URL, de l'authentification, gérer des sessions. +Sinatra utilise beaucoup de ces composants automatiquement via la configuration, +donc pour ceux-ci vous n'aurez pas à utiliser la méthode +use+. + +== Tester + +Les tests pour Sinatra peuvent être écrit avec n'importe quelle bibliothèque +basée sur Rack. {Rack::Test}[http://gitrdoc.com/brynary/rack-test] est +recommandé: + + require 'mon_application_sinatra' + require 'rack/test' + + class MonTest < Test::Unit::TestCase + include Rack::Test::Methods + + def app + Sinatra::Application + end + + def test_ma_racine + get '/' + assert_equal 'Bonjour Monde!', last_response.body + end + + def test_avec_des_parametres + get '/rencontrer', :name => 'Frank' + assert_equal 'Salut Frank!', last_response.body + end + + def test_avec_rack_env + get '/', {}, 'HTTP_USER_AGENT' => 'Songbird' + assert_equal "Vous utilisez Songbird!", last_response.body + end + end + +NOTE: Le module intégré Sinatra::Test et la classe Sinatra::TestHarness +sont désapprouvés depuis la version 0.9.2 . + +== Sinatra::Base - Les Middlewares, les Bibliothèques, et les Applications Modulaires + +Définir votre application au niveau supérieure fonctionne bien pour les +micro-applications, mais peut s'avérer moins pratique lorsqu'il s'agit +de créer des composants réutilisables comme des middlewares Rack, faire +du Rails metal, ou de simples bibliothèques avec un composant serveur, ou +même une extension pour Sinatra. La DSL de haut niveau pollue l'espace de noms +et part sur une configuration adaptée à une micro-application (un fichier unique +pour l'application, les dossiers ./public et ./views, les logs, pages d'erreur, etc.). +C'est là que Sinatra::Base entre en jeu: + + require 'sinatra/base' + + class MonApplication < Sinatra::Base + set :sessions, true + set :foo, 'bar' + + get '/' do + 'Bonjour Monde!' + end + end + +La classe MonApplication est un composant Rack indépendant qui peut agir +comme un middleware Rack, une application Rack, ou Rails metal. vous pouvez +donc le lancer avec +run+ ou l'utiliser avec +use+ dans un fichier rackup; +ou contrôler un composant de serveur sous forme de bibliothèque: + + MonApplication.run! :host => 'localhost', :port => 9090 + +Les méthodes disponibles dans Sinatra::Base sont exactement identiques à +celles disponibles dans la DSL de haut-niveau. La plupart des applications +de haut niveau peuvent être converties en composant Sinatra::Base avec +deux modifications: + +* Votre fichier doit charger +sinatra/base+ au lieu de +sinatra+; + autrement, toutes les méthodes de la DSL seront chargées dans l'espace + de noms. +* Mettre vos gestionnaires de route, vos gestionnaires d'erreur, vos filtres + et options dans une sous-classe de Sinatra::Base. + ++Sinatra::Base+ est plutôt épuré. La plupart des options sont désactivées par défaut, +ceci inclus le serveur. Voir {Options et Configuration}[http://sinatra.github.com/configuration.html] +pour plus de détails sur les options et leur comportement. + +SIDEBAR: La DSL de Sinatra est implémentée en utilisant un simple système +de délégation. La class +Sinatra::Application+ -- une sous-classe spéciale de +Sinatra::Base -- reçoit tous les messages :get, :put, :post, :delete, :before, +:error, :not_found, :configure, et :set envoyés en haut niveau. Jetez un oeil +pour vous faire une idée: voici le mixin {Sinatra::Delegator}[http://github.com/sinatra/sinatra/blob/ceac46f0bc129a6e994a06100aa854f606fe5992/lib/sinatra/base.rb#L1128] +qui est {inclus dans l'espace de noms principal}[http://github.com/sinatra/sinatra/blob/ceac46f0bc129a6e994a06100aa854f606fe5992/lib/sinatra/main.rb#L28] + +== Ligne de commande + +Les applications en Sinatra peuvent être lancées directement: + + ruby mon_application.rb [-h] [-x] [-e ENVIRONNEMENT] [-p PORT] [-o HOTE] [-s SERVEUR] + +Options are: + + -h # aide + -p # déclare le port (4567 par défaut) + -o # déclare l'hôte (0.0.0.0 par défaut) + -e # déclare l'environnement (+development+ par défaut) + -s # déclare le serveur/gestionnaire à utiliser (thin par défaut) + -x # active le mutex lock (off par défaut) + +== Essuyer les plâtres + +Si vous voulez utiliser la toute dernière version de Sinatra, créez un +clone local et lancez votre application avec le dossier sinatra/lib +dans votre LOAD_PATH: + + cd mon_application + git clone git://github.com/sinatra/sinatra.git + ruby -Isinatra/lib mon_application.rb + +Alternativement, vous pouvez ajoutez le dossier sinatra/lib à votre +LOAD_PATH à l'intérieure de votre application: + + $LOAD_PATH.unshift File.dirname(__FILE__) + '/sinatra/lib' + require 'rubygems' + require 'sinatra' + + get '/a_propos' do + "J'utilise la version " + Sinatra::VERSION + end + +Pour mettre à jour les sources de Sinatra par la suite: + + cd mon_projet/sinatra + git pull + +== Mais encore + +* {Site internet}[http://www.sinatrarb.com/] - Plus de documentation, + de news, et des liens vers d'autres ressources. +* {Contribuer}[http://www.sinatrarb.com/contributing] - Vous avez trouvé un bug? Besoin + d'aide? Vous avez un patch? +* {Lighthouse}[http://sinatra.lighthouseapp.com] - Ici on traque les problèmes et on + planifie les prochaines versions. +* {Twitter}[http://twitter.com/sinatra] +* {Mailing List}[http://groups.google.com/group/sinatrarb/topics] +* {IRC: #sinatra}[irc://chat.freenode.net/#sinatra] sur http://freenode.net From 3a25d9878b19a66c2f189a99e9849bde8981b991 Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Tue, 21 Sep 2010 08:57:26 +0200 Subject: [PATCH 65/80] Adjust examples in French and Spanish READMEs. --- README.es.rdoc | 11 +++++------ README.fr.rdoc | 11 +++++------ 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/README.es.rdoc b/README.es.rdoc index 4305943375..554de3973f 100644 --- a/README.es.rdoc +++ b/README.es.rdoc @@ -4,7 +4,6 @@ Sinatra es un DSL (Lenguaje Específico de Dominio) para crear aplicaciones web rápidamente en Ruby con un mínimo esfuerzo: # miapp.rb - require 'rubygems' require 'sinatra' get '/' do 'Hola mundo!' @@ -12,8 +11,8 @@ aplicaciones web rápidamente en Ruby con un mínimo esfuerzo: Instalá la gem (gema) y ejecutá la aplicación con: - sudo gem install sinatra - ruby miapp.rb + gem install sinatra + ruby -rubygems miapp.rb Podés verla en: http://localhost:4567 @@ -137,10 +136,10 @@ pueden ser ajustadas globalmente a través de las configuraciones de Sinatra, ver {Opciones y Configuraciones}[http://www.sinatrarb.com/configuration.html], y reemplazadas individualmente. - set :haml, { :format => :html5 } # el formato por defecto de Haml es :xhtml + set :haml, :format => :html5 # el formato por defecto de Haml es :xhtml get '/' do - haml :index, :haml_options => { :format => :html4 } # reemplazado + haml :index, :format => :html4 # reemplazado end === Plantillas Erb @@ -200,7 +199,7 @@ pueden ser ajustadas globalmente a través de las configuraciones de Sinatra, ver {Opciones y Configuraciones}[http://www.sinatrarb.com/configuration.html], y reemplazadas individualmente. - set :sass, { :style => :compact } # el estilo por defecto de Sass es :nested + set :sass, :style => :compact # el estilo por defecto de Sass es :nested get '/stylesheet.css' do content_type 'text/css', :charset => 'utf-8' diff --git a/README.fr.rdoc b/README.fr.rdoc index c7decf7301..e7098a88cb 100644 --- a/README.fr.rdoc +++ b/README.fr.rdoc @@ -4,7 +4,6 @@ Sinatra est une DSL pour créer rapidement des applications web en Ruby et sans effort: # mon_application.rb - require 'rubygems' require 'sinatra' get '/' do 'Bonjour Monde!' @@ -12,8 +11,8 @@ effort: Installez le Gem et lancez avec: - sudo gem install sinatra - ruby mon_application.rb + gem install sinatra + ruby -rubygems mon_application.rb Voir ici: http://localhost:4567 @@ -132,10 +131,10 @@ peuvent se manipuler directement avec la configuration de Sinatra, voir {Options et Configuration}[http://www.sinatrarb.com/configuration.html] qui supportent aussi la réécriture (surcharge) comme dans cet exemple. - set :haml, {:format => :html5 } # le format par défaut dans Haml est :xhtml + set :haml, :format => :html5 # le format par défaut dans Haml est :xhtml get '/' do - haml :index, :haml_options => {:format => :html4 } # surcharge + haml :index, :format => :html4 # surcharge end @@ -196,7 +195,7 @@ peuvent se manipuler directement avec la configuration de Sinatra, voir {Options et Configuration}[http://www.sinatrarb.com/configuration.html] qui supportent aussi la réécriture (surcharge) comme dans cet exemple. - set :sass, {:style => :compact } # le style par défaut dans Sass est :nested + set :sass, :style => :compact # le style par défaut dans Sass est :nested get '/stylesheet.css' do content_type 'text/css', :charset => 'utf-8' From da346eb1b6e1bc193c860e62b9d20e07a8c5d2d8 Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Tue, 21 Sep 2010 09:31:44 +0200 Subject: [PATCH 66/80] Reorganize CHANGES and add Spanish/French READMEs to it. --- CHANGES | 67 +++++++++++++++++++++++++++++---------------------------- 1 file changed, 34 insertions(+), 33 deletions(-) diff --git a/CHANGES b/CHANGES index 38118e072d..20ff4ae6f7 100644 --- a/CHANGES +++ b/CHANGES @@ -5,12 +5,6 @@ avoids manual path checking. No performance loss if patterns are avoided. (Konstantin Haase) - * Setting multiple values now no longer relies on #to_hash and therefore - accepts any Enumerable as parameter. (Simon Rozet) - - * It is now possible to access Sinatra's template_cache from the outside. - (Nick Sutterer) - * It is now possible to render SCSS files with the `scss` method, which behaves exactly like `sass` except for the different file extension and assuming the SCSS syntax. (Pedro Menezes, Konstantin Haase) @@ -19,38 +13,55 @@ `coffee` rendering methods for rendering Liquid, Markdown, Textile, RDoc, Radius, Markaby and CoffeeScript templates (Konstantin Haase) - * Broken examples for using Erubis, Haml and Test::Unit in README have been - fixed. (Nick Sutterer, Doug Ireton, Jason Stewart, Eric Marden) - - * Sinatra is now able to use Tilt versions including numbers > 9, as in 0.10. + * You can now use #settings method from class and top level for convenience. (Konstantin Haase) - * The `last_modified` method now also accepts DateTime instances and makes - sure the header will always be set to a string. (Konstantin Haase) + * Setting multiple values now no longer relies on #to_hash and therefore + accepts any Enumerable as parameter. (Simon Rozet) - * `send_file` now always respects the `:type` option if set. Previously it - was discarded if no matching mime type was found, which made it impossible - to directly pass a mime type. (Konstantin Haase) + * README is now available in French (Mickael Riga), German (Bernhard Essl, + Konstantin Haase) and Spanish (Gabriel Andretta). - * `redirect` always redirects to an absolute URI, even if a relative URI was - passed. Ensures compatibility with RFC 2616 section 14.30. (Jean-Philippe - Garcia Ballester, Anthony Williams) + * It is now possible to access Sinatra's template_cache from the outside. + (Nick Sutterer) - * Added ability to handle weighted HTTP_ACCEPT headers. (Davide D'Agostino) + * The `last_modified` method now also accepts DateTime instances and makes + sure the header will always be set to a string. (Konstantin Haase) - * README is now available in German. (Bernhard Essl, Konstantin Haase) + * 599 now is a legal status code. (Steve Shreeve) * This release is compatible with Ruby 1.9.2. Sinatra was trying to read none existent files Ruby added to the call stack. (Shota Fukumori, Konstantin Haase) + * Prevents a memory leak on 1.8.6 is production mode. Note, however, that + this is due to a bug in 1.8.6 and request will have the additional overhead + of parsing templates again on that version. It is recommended to use at + least Ruby 1.8.7. (Konstantin Haase) + + * Sinatra is now usable in combination with Rails 3. When mounting a Sinatra + application under a subpath in Rails 3, the PATH_INFO is not prefixed with + a slash and no routes did match. (José Valim) + * Better handling of encodings in 1.9, defaults params encoding to UTF-8 and respects Encoding.default_internal and Encoding.default_external. (Konstantin Haase) - * This release is now usable in combination with Rails 3. When mounting - a Sinatra application under a subpath in Rails 3, the PATH_INFO is not - prefixed with a slash and no routes did match. (José Valim) + * Added ability to handle weighted HTTP_ACCEPT headers. (Davide D'Agostino) + + * `send_file` now always respects the `:type` option if set. Previously it + was discarded if no matching mime type was found, which made it impossible + to directly pass a mime type. (Konstantin Haase) + + * `redirect` always redirects to an absolute URI, even if a relative URI was + passed. Ensures compatibility with RFC 2616 section 14.30. (Jean-Philippe + Garcia Ballester, Anthony Williams) + + * Broken examples for using Erubis, Haml and Test::Unit in README have been + fixed. (Nick Sutterer, Doug Ireton, Jason Stewart, Eric Marden) + + * Sinatra is now able to use Tilt versions including numbers > 9, as in 0.10. + (Konstantin Haase) * Sinatra now handles SIGTERM correctly. (Patrick Collison) @@ -62,16 +73,6 @@ * It's now possible to use Sinatra with different package management systems defining a custom require. (Konstantin Haase) - * You can now use #settings method from class and top level for convenience. - (Konstantin Haase) - - * Prevents a memory leak on 1.8.6 is production mode. Note, however, that - this is due to a bug in 1.8.6 and request will have the additional overhead - of parsing templates again on that version. It is recommended to use at - least Ruby 1.8.7. (Konstantin Haase) - - * 599 now is a legal status code. (Steve Shreeve) - * Lighthouse has been dropped in favor of GitHub issues. * Tilt is now a dependency and therefore no longer ships bundled with From 250b65400369eac71557ab45e17d38f17a0b13d8 Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Tue, 21 Sep 2010 10:02:20 +0200 Subject: [PATCH 67/80] Include new translations in RDoc. --- sinatra.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sinatra.gemspec b/sinatra.gemspec index ebb3bd4802..20bdd935d3 100644 --- a/sinatra.gemspec +++ b/sinatra.gemspec @@ -99,7 +99,7 @@ Gem::Specification.new do |s| s.test_files = s.files.select {|path| path =~ /^test\/.*_test.rb/} - s.extra_rdoc_files = %w[README.rdoc README.de.rdoc README.jp.rdoc LICENSE] + s.extra_rdoc_files = %w[README.rdoc README.de.rdoc README.jp.rdoc README.fr.rdoc README.es.rdoc LICENSE] s.add_dependency 'rack', '>= 1.0' s.add_dependency 'tilt', '~> 1.1' s.add_development_dependency 'shotgun', '>= 0.6', '< 1.0' From 52d4311315c8048f64798daead54d6ef7b054afb Mon Sep 17 00:00:00 2001 From: Gabriel Andretta Date: Mon, 20 Sep 2010 23:13:40 -0300 Subject: [PATCH 68/80] improves Spanish README --- README.es.rdoc | 79 +++++++++++++++++++++++--------------------------- 1 file changed, 37 insertions(+), 42 deletions(-) diff --git a/README.es.rdoc b/README.es.rdoc index 554de3973f..bf5bcfdf79 100644 --- a/README.es.rdoc +++ b/README.es.rdoc @@ -1,7 +1,7 @@ = Sinatra -Sinatra es un DSL (Lenguaje Específico de Dominio) para crear -aplicaciones web rápidamente en Ruby con un mínimo esfuerzo: +Sinatra es un DSL para crear aplicaciones web rápidamente en Ruby con un mínimo +esfuerzo: # miapp.rb require 'sinatra' @@ -9,7 +9,7 @@ aplicaciones web rápidamente en Ruby con un mínimo esfuerzo: 'Hola mundo!' end -Instalá la gem (gema) y ejecutá la aplicación con: +Instalá la gem y ejecutá la aplicación con: gem install sinatra ruby -rubygems miapp.rb @@ -18,8 +18,8 @@ Podés verla en: http://localhost:4567 == Rutas -En Sinatra, una ruta es un método HTTP apareado con un patrón de una URL. -Cada ruta es asociada con un bloque: +En Sinatra, una ruta está compuesta por un método HTTP y un patrón de una URL. +Cada ruta se asocia con un bloque: get '/' do .. mostrar algo .. @@ -45,7 +45,7 @@ través de el hash params: get '/hola/:nombre' do # coincide con "GET /hola/foo" y "GET /hola/bar" - # params[:nombre] es 'foo' o 'bar' respectivamente + # params[:nombre] es 'foo' o 'bar' "Hola #{params[:nombre]}!" end @@ -55,9 +55,8 @@ También podés acceder a los parámetros nombrados usando parámetros de bloque "Hola #{n}!" end -Los patrones de ruta también pueden incluir parámetros splat o wildcard -(plaf o comodín), accesibles a través del arreglo -params[:splat]. +Los patrones de ruta también pueden incluir parámetros splat (o wildcard), +accesibles a través del arreglo params[:splat]. get '/decir/*/al/*' do # coincide con /decir/hola/al/mundo @@ -69,7 +68,7 @@ Los patrones de ruta también pueden incluir parámetros splat o wildcard params[:splat] # => ["path/al/archivo", "xml"] end -Búsqueda de rutas con Expresiones Regulares: +Rutas con Expresiones Regulares: get %r{/hola/([\w]+)} do "Hola, #{params[:captures].first}!" @@ -82,10 +81,10 @@ O con un parámetro de bloque: end Las rutas pueden incluir una variedad de condiciones de selección, como por -ejemplo el user agent (agente de usuario): +ejemplo el user agent: get '/foo', :agent => /Songbird (\d\.\d)[\d\/]*?/ do - "Estás usando la versión #{params[:agent][0]} de Songbird" + "Estás usando la versión de Songbird #{params[:agent][0]}" end get '/foo' do @@ -106,16 +105,15 @@ ejemplo, el archivo ./public/css/style.css se accede a través de == Vistas / Plantillas -Se asume que las plantillas (templates) están ubicadas directamente -bajo el directorio ./views. Para usar un directorio de vistas -(views) diferente: +Se asume que las plantillas están ubicadas directamente bajo el directorio +./views. Para usar un directorio de vistas diferente: set :views, File.dirname(__FILE__) + '/plantillas' Es importante acordarse que siempre tenés que referenciar a las plantillas con símbolos, incluso cuando se encuentran en un subdirectorio (en este caso tenés que usar :'subdir/plantilla'). Los métodos de renderización van a -renderizar directamente cualquier string que reciban como argumento. +renderizar directamente cualquier string que se les pase como argumento. === Plantillas Haml @@ -129,7 +127,6 @@ La gem/librería haml es necesaria para para renderizar plantillas HAML: end Renderiza ./views/index.haml. -{Haml's options}[http://haml-lang.com/docs/yardoc/file.HAML_REFERENCE.html#options] Las {opciones de Haml}[http://haml-lang.com/docs/yardoc/file.HAML_REFERENCE.html#options] pueden ser ajustadas globalmente a través de las configuraciones de Sinatra, @@ -231,15 +228,15 @@ Renderiza el template contenido en el string. === Accediendo a Variables en Plantillas Las plantillas son evaluadas dentro del mismo contexto que los manejadores de -ruta (route handlers). Las variables de instancia asignadas en los -manejadores de ruta son accesibles directamente por las plantillas: +ruta. Las variables de instancia asignadas en los manejadores de ruta son +accesibles directamente por las plantillas: get '/:id' do @foo = Foo.find(params[:id]) haml '%h1= @foo.nombre' end -O es posible especificar un Hash explícito de variables locales: +O es posible especificar un Hash de variables locales explícitamente: get '/:id' do foo = Foo.find(params[:id]) @@ -299,7 +296,7 @@ una plantilla es renderizada. Podés desactivar los layouts pasando haml :index, :layout => !request.xhr? end -== Ayudantes (Helpers) +== Ayudantes Usá el método top-level helpers para definir métodos ayudantes que pueden ser utilizados dentro de los manejadores de rutas y las plantillas: @@ -314,9 +311,9 @@ pueden ser utilizados dentro de los manejadores de rutas y las plantillas: bar(params[:nombre]) end -== Filtros (Filters) +== Filtros -Los Before Filters son evaluados antes de cada petición dentro del contexto de +Los filtros before son evaluados antes de cada petición dentro del contexto de la petición y pueden modificar la petición y la respuesta. Las variables de instancia asignadas en los filtros son accesibles por las rutas y las plantillas: @@ -331,10 +328,10 @@ plantillas: params[:splat] #=> 'bar/baz' end -Los After Filter son evaluados después de cada petición dentro del contexto de -la petición y también pueden modificar la petición y la respuesta. Las -variables de instancia asignadas en before filters y rutas son accesibles por -los after filters: +Los filtros After son evaluados después de cada petición dentro del contexto de +la petición y también pueden modificar la petición y la respuesta. Las +variables de instancia asignadas en los filtros before y rutas son accesibles +por los filtros after: after do puts response.status @@ -352,7 +349,7 @@ patrón: session[:ultimo_slug] = slug end -== Interrumpiendo (Halting) +== Interrupción Para detener inmediatamente una petición dentro de un filtro o una ruta usá: @@ -374,7 +371,7 @@ Con encabezados ... halt 402, { 'Content-Type' => 'text/plain' }, 'venganza' -== Pasando +== Paso Una ruta puede pasarle el procesamiento a la siguiente ruta que coincida con la petición usando pass: @@ -415,7 +412,7 @@ Ejecutar cuando el entorno es :production o :test: == Manejo de errores Los manejadores de errores se ejecutan dentro del mismo contexto que las rutas -y los before filters, lo que significa que podés usar, por ejemplo, +y los filtros before, lo que significa que podés usar, por ejemplo, haml, erb, halt, etc. === No encontrado (Not Found) @@ -507,9 +504,9 @@ método top-level +use+: end Las semánticas de +use+ son idénticas a las definidas para el DSL -Rack::Builder[http://rack.rubyforge.org/doc/classes/Rack/Builder.html] -(más frecuentemente usadas desde archivos rackup). Por ejemplo, el método -+use+ acepta argumentos múltiples/variables así como bloques: +Rack::Builder[http://rack.rubyforge.org/doc/classes/Rack/Builder.html] (más +frecuentemente usado desde archivos rackup). Por ejemplo, el método +use+ +acepta argumentos múltiples/variables así como bloques: use Rack::Auth::Basic do |nombre_de_usuario, password| nombre_de_usuario == 'admin' && password == 'secreto' @@ -520,11 +517,11 @@ debugging, enrutamiento URL, autenticación, y manejo de sesiones. Sinatra usa muchos de estos componentes automáticamente de acuerdo a su configuración para que típicamente no tengas que usarlas (con +use+) explícitamente. -== Testing +== Pruebas -Los test para las aplicaciones Sinatra pueden ser escritos utilizando -cualquier framework o librería de testing basada en Rack. Se recomienda -usar {Rack::Test}[http://gitrdoc.com/brynary/rack-test]: +Las pruebas para las aplicaciones Sinatra pueden ser escritas utilizando +cualquier framework o librería de pruebas basada en Rack. Se recomienda usar +{Rack::Test}[http://gitrdoc.com/brynary/rack-test]: require 'mi_app_sinatra' require 'rack/test' @@ -536,14 +533,14 @@ usar {Rack::Test}[http://gitrdoc.com/brynary/rack-test]: Sinatra::Application end - def test_por_defecto + def test_mi_defecto get '/' assert_equal 'Hola Mundo!', last_response.body end def test_con_parametros get '/saludar', :name => 'Franco' - assert_equal 'Hola Franco!', last_response.body + assert_equal 'Hola Frank!', last_response.body end def test_con_entorno_rack @@ -566,7 +563,6 @@ para micro-aplicaciones (por ejemplo, un único archivo de aplicación, los directorios ./public y ./views, logging, página con detalles de excepción, etc.). Ahí es donde Sinatra::Base entra en el juego: - require 'sinatra/base' class MiApp < Sinatra::Base @@ -656,8 +652,7 @@ Para actualizar el código fuente de Sinatra en el futuro: adicional, noticias, y enlaces a otros recursos. * {Contribuyendo}[http://www.sinatrarb.com/contributing] - ¿Encontraste un error?. ¿Necesitás ayuda?. ¿Tenés un parche?. -* {Lighthouse}[http://sinatra.lighthouseapp.com] - Seguimiento de problemas y - planeamiento de lanzamientos. +* {Issue tracker}[http://sinatra.lighthouseapp.com] * {Twitter}[http://twitter.com/sinatra] * {Lista de Correo}[http://groups.google.com/group/sinatrarb/topics] * {IRC: #sinatra}[irc://chat.freenode.net/#sinatra] en http://freenode.net From c2705a9ff74885249033e28a6c57f9bb88fd156b Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Wed, 22 Sep 2010 07:41:01 +0200 Subject: [PATCH 69/80] Include all READMEs in documentation generated by rake task. --- Rakefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Rakefile b/Rakefile index db31ab9c7d..d828e26b09 100644 --- a/Rakefile +++ b/Rakefile @@ -47,11 +47,11 @@ task 'doc' => ['doc:api'] task 'doc:api' => ['doc/api/index.html'] -file 'doc/api/index.html' => FileList['lib/**/*.rb','README.rdoc'] do |f| +file 'doc/api/index.html' => FileList['lib/**/*.rb', 'README.*'] do |f| require 'rbconfig' hanna = RbConfig::CONFIG['ruby_install_name'].sub('ruby', 'hanna') rb_files = f.prerequisites - sh((<<-end).gsub(/\s+/, ' ')) + sh(<<-end.gsub(/\s+/, ' ')) #{hanna} --charset utf8 --fmt html From 130335bc4b41c5e0c5883fcad6769325bab65185 Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Wed, 22 Sep 2010 07:43:28 +0200 Subject: [PATCH 70/80] Fix grammar in German README. --- README.de.rdoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.de.rdoc b/README.de.rdoc index 828296458e..46017275df 100644 --- a/README.de.rdoc +++ b/README.de.rdoc @@ -1,7 +1,7 @@ = Sinatra -Sinatra ist eine DSL das schnelles Erstellen von Webanwendungen in Ruby mit -minimalen Aufwand ermöglicht: +Sinatra ist eine DSL, die das schnelles Erstellen von Webanwendungen in Ruby +mit minimalen Aufwand ermöglicht: # myapp.rb require 'sinatra' From 1abac3217f4f8ad0ba693951145862e16c053e8a Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Wed, 22 Sep 2010 07:50:09 +0200 Subject: [PATCH 71/80] Add note to all README translations saying: 'Important Note: This document is just a translation of the English version and might not be up to date.' French translation by Gabriel Andretta (ohhgabriel) Spanish translation by michelc Japanese translation by Kiko Uehara (kiko) Japanese translation also includes 'Please refer to original English document for the most up to date information.' More translations and discussion: http://gist.github.com/589498 --- README.de.rdoc | 1 + README.es.rdoc | 1 + README.fr.rdoc | 1 + README.jp.rdoc | 1 + 4 files changed, 4 insertions(+) diff --git a/README.de.rdoc b/README.de.rdoc index 46017275df..d656ae06c9 100644 --- a/README.de.rdoc +++ b/README.de.rdoc @@ -1,4 +1,5 @@ = Sinatra +Wichtig: Dieses Dokument ist eine Übersetzung aus dem Englischen und unter Umständen nicht auf dem aktuellsten Stand. Sinatra ist eine DSL, die das schnelles Erstellen von Webanwendungen in Ruby mit minimalen Aufwand ermöglicht: diff --git a/README.es.rdoc b/README.es.rdoc index bf5bcfdf79..449f2549a0 100644 --- a/README.es.rdoc +++ b/README.es.rdoc @@ -1,4 +1,5 @@ = Sinatra +Atención: Este documento es una traducción de la versión en inglés y puede estar desactualizado. Sinatra es un DSL para crear aplicaciones web rápidamente en Ruby con un mínimo esfuerzo: diff --git a/README.fr.rdoc b/README.fr.rdoc index e7098a88cb..f4bae56ea7 100644 --- a/README.fr.rdoc +++ b/README.fr.rdoc @@ -1,4 +1,5 @@ = Sinatra +Attention: Ce document correspond à la traduction de la version anglaise et il n'est peut être plus à jour. Sinatra est une DSL pour créer rapidement des applications web en Ruby et sans effort: diff --git a/README.jp.rdoc b/README.jp.rdoc index 2b68099f57..ad8250a156 100644 --- a/README.jp.rdoc +++ b/README.jp.rdoc @@ -1,4 +1,5 @@ = Sinatra +注) 本文書は英語から翻訳したものであり、その内容が最新でない場合もあります。最新の情報はオリジナルの英語版を参照して下さい。 SinatraはRubyで下記のような最小労力で手早くウェブアプリケーションを作成するためのDSLです。 From ac4d015ee4bfd8ad7252ddb930a8898333475f39 Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Wed, 22 Sep 2010 09:25:45 +0200 Subject: [PATCH 72/80] increase inline documentation --- lib/sinatra/base.rb | 19 +++++++++++++++++-- lib/sinatra/showexceptions.rb | 10 +++++++++- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/lib/sinatra/base.rb b/lib/sinatra/base.rb index d87aaf4629..4e5dfc4e4c 100644 --- a/lib/sinatra/base.rb +++ b/lib/sinatra/base.rb @@ -17,6 +17,8 @@ def accept @env['HTTP_ACCEPT'].to_s.split(',').map { |a| a.split(';')[0].strip } end + # Whether or not the web server (or a reverse proxy in front of it) is + # using SSL to communicate with the client. def secure? (@env['HTTP_X_FORWARDED_PROTO'] || @env['rack.url_scheme']) == 'https' end @@ -443,7 +445,7 @@ def call(env) attr_accessor :env, :request, :response, :params - def call!(env) + def call!(env) # :nodoc: @env = env @request = Request.new(env) @response = Response.new @@ -613,6 +615,7 @@ def indifferent_params(params) end end + # Creates a Hash with indifferent access. def indifferent_hash Hash.new {|hash,key| hash[key.to_s] if Symbol === key } end @@ -663,6 +666,7 @@ def dispatch! filter! :after unless env['sinatra.static_file'] end + # Special treatment for 404s in order to play nice with cascades. def handle_not_found!(boom) @env['sinatra.error'] = boom @response.status = 404 @@ -671,6 +675,7 @@ def handle_not_found!(boom) error_block! boom.class, NotFound end + # Error handling during requests. def handle_exception!(boom) @env['sinatra.error'] = boom @@ -712,6 +717,8 @@ def dump_errors!(boom) class << self attr_reader :routes, :filters, :templates, :errors + # Removes all routes, filters, middleware and extension hooks from the + # current class (not routes/filters/... defined by its superclass). def reset! @conditions = [] @routes = {} @@ -871,10 +878,13 @@ def condition(&block) end private + # Condition for matching host name. Parameter might be String or Regexp. def host_name(pattern) condition { pattern === request.host } end + # Condition for matching user agent. Parameter should be Regexp. + # Will set params[:agent]. def user_agent(pattern) condition { if request.user_agent =~ pattern @@ -887,6 +897,7 @@ def user_agent(pattern) end alias_method :agent, :user_agent + # Condition for matching mimetypes. Accepts file extensions. def provides(*types) types = [types] unless types.kind_of? Array types.map!{|t| mime_type(t)} @@ -985,6 +996,8 @@ def helpers(*extensions, &block) include(*extensions) if extensions.any? end + # Register an extension. Alternatively take a block from which an + # extension will be created and registered on the fly. def register(*extensions, &block) extensions << Module.new(&block) if block_given? @extensions += extensions @@ -1089,7 +1102,7 @@ def metadef(message, &block) end public - CALLERS_TO_IGNORE = [ + CALLERS_TO_IGNORE = [ # :nodoc: /\/sinatra(\/(base|main|showexceptions))?\.rb$/, # all sinatra code /lib\/tilt.*\.rb$/, # all tilt code /\(.*\)/, # generated code @@ -1108,6 +1121,8 @@ def caller_files map { |file,line| file } end + # Like caller_files, but containing Arrays rather than strings with the + # first element being the file, and the second being the line. def caller_locations caller(1). map { |line| line.split(/:(?=\d|in )/)[0,2] }. diff --git a/lib/sinatra/showexceptions.rb b/lib/sinatra/showexceptions.rb index 86738c97b0..50aa59e25b 100644 --- a/lib/sinatra/showexceptions.rb +++ b/lib/sinatra/showexceptions.rb @@ -1,12 +1,20 @@ require 'rack/showexceptions' module Sinatra + # Sinatra::ShowExceptions catches all exceptions raised from the app it + # wraps. It shows a useful backtrace with the sourcefile and clickable + # context, the whole Rack environment and the request data. + # + # Be careful when you use this on public-facing sites as it could reveal + # information helpful to attackers. class ShowExceptions < Rack::ShowExceptions def initialize(app) @app = app @template = ERB.new(TEMPLATE) end + private + def frame_class(frame) if frame.filename =~ /lib\/sinatra.*\.rb/ "framework" @@ -18,7 +26,7 @@ def frame_class(frame) end end -TEMPLATE = < From 3e0c8cb77d94d1cea6e1a22adc1a1ad337f7e68a Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Wed, 22 Sep 2010 09:26:42 +0200 Subject: [PATCH 73/80] Remove before_filters and after_filters method (no longer in use). --- lib/sinatra/base.rb | 8 -------- 1 file changed, 8 deletions(-) diff --git a/lib/sinatra/base.rb b/lib/sinatra/base.rb index 4e5dfc4e4c..5cfeb8029a 100644 --- a/lib/sinatra/base.rb +++ b/lib/sinatra/base.rb @@ -735,14 +735,6 @@ def reset! end end - def before_filters - @filters[:before] - end - - def after_filters - @filters[:after] - end - # Extension modules registered on this class and all superclasses. def extensions if superclass.respond_to?(:extensions) From fd5df5a6db65a69830de70e08a6c7924ae3517d4 Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Wed, 22 Sep 2010 09:28:09 +0200 Subject: [PATCH 74/80] Remove uneccessary line from provieds, use do ... end for multiline conditions. --- lib/sinatra/base.rb | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/sinatra/base.rb b/lib/sinatra/base.rb index 5cfeb8029a..24422b4cbb 100644 --- a/lib/sinatra/base.rb +++ b/lib/sinatra/base.rb @@ -878,23 +878,22 @@ def host_name(pattern) # Condition for matching user agent. Parameter should be Regexp. # Will set params[:agent]. def user_agent(pattern) - condition { + condition do if request.user_agent =~ pattern @params[:agent] = $~[1..-1] true else false end - } + end end alias_method :agent, :user_agent # Condition for matching mimetypes. Accepts file extensions. def provides(*types) - types = [types] unless types.kind_of? Array - types.map!{|t| mime_type(t)} + types.map! { |t| mime_type(t) } - condition { + condition do matching_types = (request.accept & types) unless matching_types.empty? response.headers['Content-Type'] = matching_types.first @@ -902,7 +901,7 @@ def provides(*types) else false end - } + end end public From 125573777d48ed48fe7c476f58c5bdfa7a4323a8 Mon Sep 17 00:00:00 2001 From: Michel Date: Thu, 23 Sep 2010 04:46:03 -0700 Subject: [PATCH 75/80] Added a transition to better explain that templates must be defined with symbols. Signed-off-by: Konstantin Haase --- README.rdoc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.rdoc b/README.rdoc index c5f1301b00..785eb89aec 100644 --- a/README.rdoc +++ b/README.rdoc @@ -164,8 +164,9 @@ directory. To use a different views directory: One important thing to remember is that you always have to reference templates with symbols, even if they're in a subdirectory (in this -case use :'subdir/template'). Rendering methods will render -any strings passed to them directly. +case use :'subdir/template'). You must use a symbol because +otherwise rendering methods will render any strings passed to them +directly. === Haml Templates From 3f21c3f438ef38566cd4b504275a7c06f699451f Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Fri, 24 Sep 2010 01:28:03 +0200 Subject: [PATCH 76/80] Document different scopes/bindings. --- README.rdoc | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/README.rdoc b/README.rdoc index 785eb89aec..77377c4501 100644 --- a/README.rdoc +++ b/README.rdoc @@ -804,6 +804,86 @@ top-level. Have a look at the code for yourself: here's the {Sinatra::Delegator mixin}[http://github.com/sinatra/sinatra/blob/ceac46f0bc129a6e994a06100aa854f606fe5992/lib/sinatra/base.rb#L1128] being {included into the main namespace}[http://github.com/sinatra/sinatra/blob/ceac46f0bc129a6e994a06100aa854f606fe5992/lib/sinatra/main.rb#L28] +== Scopes and Binding + +The scope you are currently in determines what methods and variables are available. + +=== Application/Class Scope + +Every Sinatra application corresponds to a subclass of Sinatra::Base. If you +are using the top level DSL (require 'sinatra'), then this class is +Sinatra::Application, otherwise it is the subclass you created explicitly. At +class level you have methods like `get` or `before`, but you cannot access the +`request` object or the `session`, as there only is a single application class +for all requests. + +Options created via `set` are methods at class level: + + class MyApp << Sinatra::Base + # Hey, I'm in the application scope! + set :foo, 42 + foo # => 42 + + get '/foo' do + # Hey, I'm no longer in the application scope! + end + end + +You have the application scope binding inside + +* Your application class body +* Methods defined by extensions +* The block passed to `helpers` +* Procs/blocks used as value for `set` + +You can reach the scope object (the class) like this: + +* The object passed to configure blocks (configure { |c| ... }) +* `settings` from within request scope + +=== Request/Instance Scope + +For every incoming request a new instance of your application class is created +and all handler blocks run in that scope. From within this scope you can +access the `request` or `session` object and call methods like `erb` or +`haml`. You can access the application scope from within the request scope via +the `settings` helper. + + class MyApp << Sinatra::Base + # Hey, I'm in the application scope! + get '/define_route/:name' do + # Request scope for '/define_route/:name' + @value = 42 + + settings.get("/#{params[:name]}") do + # Request scope for "/#{params[:name]}" + @value # => nil (not the same request) + end + + "Route defined!" + end + end + +You have the request scope binding inside + +* get/head/post/put/delete blocks +* before/after filters +* helper methods +* templates/views + +=== Delegation Scope + +The delegation scope just forwards methods to the class scope. However, it +does not behave 100% like the class scope, as you do not have the classes +bindings: Only methods explicitly marked for delegation are available and you +do not share variables with the class scope (read: you have a different +`self`). + +You have the delegate scope binding inside + +* The top level binding, if you did require "sinatra" +* A object extended with the `Sinatra::Delegator` mixin + == Command line Sinatra applications can be run directly: From 6233ce4390fdbaba07d6890e29839505a24b6639 Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Fri, 24 Sep 2010 10:15:53 +0200 Subject: [PATCH 77/80] Proof-reading Scopes section --- README.rdoc | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/README.rdoc b/README.rdoc index 77377c4501..60de32d74b 100644 --- a/README.rdoc +++ b/README.rdoc @@ -806,7 +806,8 @@ being {included into the main namespace}[http://github.com/sinatra/sinatra/blob/ == Scopes and Binding -The scope you are currently in determines what methods and variables are available. +The scope you are currently in determines what methods and variables are +available. === Application/Class Scope @@ -829,7 +830,7 @@ Options created via `set` are methods at class level: end end -You have the application scope binding inside +You have the application scope binding inside: * Your application class body * Methods defined by extensions @@ -838,16 +839,16 @@ You have the application scope binding inside You can reach the scope object (the class) like this: -* The object passed to configure blocks (configure { |c| ... }) +* Via the object passed to configure blocks (configure { |c| ... }) * `settings` from within request scope === Request/Instance Scope -For every incoming request a new instance of your application class is created -and all handler blocks run in that scope. From within this scope you can -access the `request` or `session` object and call methods like `erb` or -`haml`. You can access the application scope from within the request scope via -the `settings` helper. +For every incoming request, a new instance of your application class is +created and all handler blocks run in that scope. From within this scope you +can access the `request` and `session` object or call rendering methods like +`erb` or `haml`. You can access the application scope from within the request +scope via the `settings` helper: class MyApp << Sinatra::Base # Hey, I'm in the application scope! @@ -864,7 +865,7 @@ the `settings` helper. end end -You have the request scope binding inside +You have the request scope binding inside: * get/head/post/put/delete blocks * before/after filters @@ -874,15 +875,16 @@ You have the request scope binding inside === Delegation Scope The delegation scope just forwards methods to the class scope. However, it -does not behave 100% like the class scope, as you do not have the classes -bindings: Only methods explicitly marked for delegation are available and you -do not share variables with the class scope (read: you have a different -`self`). +does not behave 100% like the class scope, as you do not have the class' +binding: Only methods explicitly marked for delegation are available and you +do not share variables/state with the class scope (read: you have a different +`self`). You can explicitly add method delegations by calling +Sinatra::Delegator.delegate :method_name. -You have the delegate scope binding inside +You have the delegate scope binding inside: * The top level binding, if you did require "sinatra" -* A object extended with the `Sinatra::Delegator` mixin +* An object extended with the `Sinatra::Delegator` mixin == Command line From 2e2e59590d8920dcb1b552602a06117788b0c102 Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Fri, 24 Sep 2010 10:21:40 +0200 Subject: [PATCH 78/80] Remove Sinatra::Delegator sidebar (now covered by Scopes), keep "Have a look at the code for yourself" paragraph, but move it into Scopes. --- README.rdoc | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/README.rdoc b/README.rdoc index 60de32d74b..6b1af072a7 100644 --- a/README.rdoc +++ b/README.rdoc @@ -796,14 +796,6 @@ Sinatra::Base components with two modifications: including the built-in server. See {Options and Configuration}[http://sinatra.github.com/configuration.html] for details on available options and their behavior. -SIDEBAR: Sinatra's top-level DSL is implemented using a simple delegation -system. The +Sinatra::Application+ class -- a special subclass of -Sinatra::Base -- receives all :get, :put, :post, :delete, :before, -:error, :not_found, :configure, and :set messages sent to the -top-level. Have a look at the code for yourself: here's the -{Sinatra::Delegator mixin}[http://github.com/sinatra/sinatra/blob/ceac46f0bc129a6e994a06100aa854f606fe5992/lib/sinatra/base.rb#L1128] -being {included into the main namespace}[http://github.com/sinatra/sinatra/blob/ceac46f0bc129a6e994a06100aa854f606fe5992/lib/sinatra/main.rb#L28] - == Scopes and Binding The scope you are currently in determines what methods and variables are @@ -886,6 +878,10 @@ You have the delegate scope binding inside: * The top level binding, if you did require "sinatra" * An object extended with the `Sinatra::Delegator` mixin +Have a look at the code for yourself: here's the +{Sinatra::Delegator mixin}[http://github.com/sinatra/sinatra/blob/ceac46f0bc129a6e994a06100aa854f606fe5992/lib/sinatra/base.rb#L1128] +being {included into the main namespace}[http://github.com/sinatra/sinatra/blob/ceac46f0bc129a6e994a06100aa854f606fe5992/lib/sinatra/main.rb#L28] + == Command line Sinatra applications can be run directly: From 5e8d5015e2efdfe249562e8b734db6cf58824854 Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Fri, 24 Sep 2010 10:37:53 +0200 Subject: [PATCH 79/80] Added "Using Sinatra as middleware" section, fixes GH #19. --- README.rdoc | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/README.rdoc b/README.rdoc index 6b1af072a7..238f0360f7 100644 --- a/README.rdoc +++ b/README.rdoc @@ -796,6 +796,42 @@ Sinatra::Base components with two modifications: including the built-in server. See {Options and Configuration}[http://sinatra.github.com/configuration.html] for details on available options and their behavior. +=== Using Sinatra as middleware + +Not only is Sinatra able to use other Rack middleware, any Sinatra application +can in turn be added in front of any Rack endpoint as middleware itself. This +endpoint could be another Sinatra application, or any other Rack-based +application (Rails/Ramaze/Camping/...). + + require 'sinatra/base' + + class LoginScreen < Sinatra::Base + enable :session + + get('/login') { haml :login } + + post('/login') do + if params[:name] = 'admin' and params[:password] = 'admin' + session['user_name'] = params[:name] + else + redirect '/login' + end + end + end + + class MyApp < Sinatra::Base + # middleware will run before filters + use LoginScreen + + before do + unless session['user_name'] + halt "Access denied, please login." + end + end + + get('/') { "Hello #{session['user_name']}." } + end + == Scopes and Binding The scope you are currently in determines what methods and variables are From 69791f71285c84e217efc061eee15ef32ffca515 Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Fri, 24 Sep 2010 19:33:51 +0200 Subject: [PATCH 80/80] Minor README adjustments. --- README.rdoc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.rdoc b/README.rdoc index 238f0360f7..b6d00b1101 100644 --- a/README.rdoc +++ b/README.rdoc @@ -757,7 +757,7 @@ are deprecated as of the 0.9.2 release. == Sinatra::Base - Middleware, Libraries, and Modular Apps Defining your app at the top-level works well for micro-apps but has -considerable drawbacks when building reuseable components such as Rack +considerable drawbacks when building reusable components such as Rack middleware, Rails metal, simple libraries with a server component, or even Sinatra extensions. The top-level DSL pollutes the Object namespace and assumes a micro-app style configuration (e.g., a single application @@ -796,7 +796,7 @@ Sinatra::Base components with two modifications: including the built-in server. See {Options and Configuration}[http://sinatra.github.com/configuration.html] for details on available options and their behavior. -=== Using Sinatra as middleware +=== Using Sinatra as Middleware Not only is Sinatra able to use other Rack middleware, any Sinatra application can in turn be added in front of any Rack endpoint as middleware itself. This @@ -916,7 +916,7 @@ You have the delegate scope binding inside: Have a look at the code for yourself: here's the {Sinatra::Delegator mixin}[http://github.com/sinatra/sinatra/blob/ceac46f0bc129a6e994a06100aa854f606fe5992/lib/sinatra/base.rb#L1128] -being {included into the main namespace}[http://github.com/sinatra/sinatra/blob/ceac46f0bc129a6e994a06100aa854f606fe5992/lib/sinatra/main.rb#L28] +being {included into the main namespace}[http://github.com/sinatra/sinatra/blob/ceac46f0bc129a6e994a06100aa854f606fe5992/lib/sinatra/main.rb#L28]. == Command line