├── .gitignore ├── Gemfile ├── LICENSE ├── README.md ├── Rakefile ├── brochure.gemspec ├── config.ru.example ├── lib ├── brochure.rb └── brochure │ ├── application.rb │ ├── context.rb │ ├── errors.rb │ ├── failsafe.rb │ ├── static.rb │ └── template.rb └── test ├── fixtures ├── custom404 │ └── templates │ │ └── 404.html.erb └── default │ ├── helpers │ └── link_helper.rb │ ├── public │ └── screen.css │ ├── templates │ ├── _layout.html.erb │ ├── blog.html.erb │ ├── blog │ │ └── 2010.html.erb │ ├── doctype.html.haml │ ├── engineless.html │ ├── error.html.erb │ ├── haml_with_layout.html.haml │ ├── hello.html.str │ ├── hello.js.erb │ ├── help │ │ ├── index.html.erb │ │ ├── partial_error.html.erb │ │ └── search.html.erb │ ├── index.html.erb │ ├── shared │ │ └── _head.html.erb │ └── signup.html.erb │ └── vendor │ └── plugins │ └── common │ └── templates │ ├── common.html.erb │ └── shared │ └── _footer.html.erb └── test_brochure.rb /.gitignore: -------------------------------------------------------------------------------- 1 | Gemfile.lock 2 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source :rubygems 2 | gemspec 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010 Sam Stephenson 2 | Copyright (c) 2010 Josh Peek 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining 5 | a copy of this software and associated documentation files (the 6 | "Software"), to deal in the Software without restriction, including 7 | without limitation the rights to use, copy, modify, merge, publish, 8 | distribute, sublicense, and/or sell copies of the Software, and to 9 | permit persons to whom the Software is furnished to do so, subject to 10 | the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 19 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 20 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Brochure 3 | 4 | Brochure is a Rack application for serving static sites with ERB 5 | templates (or any of the many [template languages supported by 6 | Tilt](http://github.com/rtomayko/tilt/blob/master/TEMPLATES.md#readme)). 7 | It's the good parts of PHP wrapped up in a little Ruby package — 8 | perfect for serving the marketing site for your Rails app. 9 | 10 | 11 | Sample application structure: 12 | 13 | templates/ 14 | help/ 15 | index.html.erb 16 | index.html.erb 17 | shared/ 18 | _header.html.erb 19 | _footer.html.erb 20 | signup.html.erb 21 | config.ru 22 | public/ 23 | ... 24 | 25 | Sample `config.ru`: 26 | 27 | require "brochure" 28 | root = File.dirname(__FILE__) 29 | run Brochure.app(root) 30 | 31 | 32 | ## Automatic URL mapping 33 | 34 | URLs are automatically mapped to template names: 35 | 36 | * `/` → `templates/index.html.erb` 37 | * `/signup` → `templates/signup.html.erb` 38 | * `/help/` → `templates/help/index.html.erb` 39 | 40 | 41 | ## Partials and helpers 42 | 43 | Templates can render partials. A partial is denoted by a leading 44 | underscore in its filename. So `<%= render "shared/header" %>` will 45 | render `templates/shared/_header.html.erb` inline. 46 | 47 | Partials can `<%= yield %>` back to the templates that render 48 | them. You can use this technique to extract common header and footer 49 | markup into a single layout file, for example. 50 | 51 | Templates have access to the Rack environment via the `env` method and 52 | to the Brochure application via the `application` 53 | method. Additionally, a `Rack::Request` wrapper around the Rack 54 | environment is available via the `request` method. 55 | 56 | You can print HTML-escaped strings in your templates with the `h` 57 | helper. 58 | 59 | 60 | ## Custom helper methods and instance variables 61 | 62 | You can make additional helper methods and instance variables 63 | available to your templates. Helper methods live in Ruby modules and 64 | can be included with the `:helpers` option to `Brochure.app`: 65 | 66 | module AssetHelper 67 | def asset_path(filename) 68 | local_path = File.join(application.asset_root, filename) 69 | digest = Digest::MD5.hexdigest(IO.read(local_path)) 70 | "/#{filename}?#{digest}" 71 | end 72 | end 73 | 74 | run Brochure.app(root, :helpers => [AssetHelper]) 75 | 76 | Similarly, instance variables can be defined with the `:assigns` 77 | option: 78 | 79 | run Brochure.app(root, :assigns => { :domain => "37signals.com" }) 80 | 81 | 82 | ## Tilt template options 83 | 84 | You can specify global [Tilt template 85 | options](https://github.com/rtomayko/tilt/blob/master/TEMPLATES.md#readme) 86 | on a per-engine basis with `:template_options`: 87 | 88 | run Brochure.app(root, :template_options => { 89 | ".haml" => { :format => :html5 }, 90 | ".md" => { :smart => true } 91 | }) 92 | 93 | 94 | # Installation 95 | 96 | $ gem install brochure 97 | 98 | Requires [Hike](http://github.com/sstephenson/hike), 99 | [Rack](http://rack.rubyforge.org/), and 100 | [Tilt](http://github.com/rtomayko/tilt). 101 | 102 | 103 | # License 104 | 105 | Copyright (c) 2010 Sam Stephenson and Josh Peek. 106 | 107 | Released under the MIT license. See `LICENSE` for details. 108 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "rake/testtask" 2 | 3 | task :default => :test 4 | 5 | Rake::TestTask.new do |t| 6 | t.verbose = true 7 | end 8 | -------------------------------------------------------------------------------- /brochure.gemspec: -------------------------------------------------------------------------------- 1 | spec = Gem::Specification.new do |s| 2 | s.name = "brochure" 3 | s.version = "0.5.4" 4 | s.platform = Gem::Platform::RUBY 5 | s.authors = ["Sam Stephenson", "Josh Peek"] 6 | s.email = ["sstephenson@gmail.com", "josh@joshpeek.com"] 7 | s.homepage = "http://github.com/sstephenson/brochure" 8 | s.summary = "Rack + ERB static sites" 9 | s.description = "A Rack application for serving static sites with ERB templates." 10 | s.files = Dir["lib/**/*.rb", "README.md", "LICENSE"] 11 | s.require_path = "lib" 12 | 13 | s.add_dependency "hike", "~> 1.0" 14 | s.add_dependency "rack", "~> 1.0" 15 | s.add_dependency "tilt", "~> 1.1" 16 | 17 | s.add_development_dependency "rake" 18 | s.add_development_dependency "rack-test" 19 | s.add_development_dependency "haml" 20 | end 21 | -------------------------------------------------------------------------------- /config.ru.example: -------------------------------------------------------------------------------- 1 | require "brochure" 2 | run Brochure.app(File.dirname(__FILE__)) 3 | -------------------------------------------------------------------------------- /lib/brochure.rb: -------------------------------------------------------------------------------- 1 | require "hike" 2 | require "rack" 3 | require "tilt" 4 | 5 | module Brochure 6 | VERSION = "0.5.4" 7 | 8 | autoload :Application, "brochure/application" 9 | autoload :CaptureNotSupported, "brochure/errors" 10 | autoload :Context, "brochure/context" 11 | autoload :Failsafe, "brochure/failsafe" 12 | autoload :Static, "brochure/static" 13 | autoload :Template, "brochure/template" 14 | autoload :TemplateNotFound, "brochure/errors" 15 | 16 | def self.app(root, options = {}) 17 | app = Application.new(root, options) 18 | yield app if block_given? 19 | app = Static.new(app, app.asset_root) 20 | 21 | if development? 22 | app = Rack::ShowExceptions.new(app) 23 | else 24 | app = Failsafe.new(app) 25 | end 26 | 27 | app 28 | end 29 | 30 | def self.development? 31 | ENV["RACK_ENV"] == "development" 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /lib/brochure/application.rb: -------------------------------------------------------------------------------- 1 | module Brochure 2 | class Application 3 | attr_reader :app_root, :template_root, :asset_root, :plugin_root, :assigns, :template_options 4 | 5 | def initialize(root, options = {}) 6 | @app_root = File.expand_path(root) 7 | @template_root = File.join(@app_root, "templates") 8 | @asset_root = File.join(@app_root, "public") 9 | @plugin_root = File.join(@app_root, "vendor", "plugins") 10 | 11 | @assigns = options[:assigns] || {} 12 | @template_options = options[:template_options] || {} 13 | helpers.push(*options[:helpers]) if options[:helpers] 14 | initialize_plugins 15 | end 16 | 17 | def initialize_plugins 18 | plugins.each do |plugin_root| 19 | template_trail.paths.push(File.join(plugin_root, "templates")) 20 | end 21 | end 22 | 23 | def template_trail 24 | @template_trail ||= Hike::Trail.new(app_root).tap do |trail| 25 | trail.extensions.replace(Tilt.mappings.keys.sort) 26 | trail.paths.push(template_root) 27 | end 28 | end 29 | 30 | def context_class 31 | @context_class ||= Context.for(helpers) 32 | end 33 | 34 | def templates 35 | @templates ||= {} 36 | end 37 | 38 | def helpers 39 | @helpers ||= [] 40 | end 41 | 42 | def plugins 43 | @plugins ||= Dir[File.join(plugin_root, "*")].select do |entry| 44 | File.directory?(entry) 45 | end 46 | end 47 | 48 | def call(env) 49 | if forbidden?(env["PATH_INFO"]) 50 | forbidden 51 | elsif template = find_template(env["PATH_INFO"]) 52 | success template.render(env), template.content_type 53 | else 54 | not_found(env) 55 | end 56 | end 57 | 58 | def forbidden?(path) 59 | path[".."] || File.basename(path)[/^_/] 60 | end 61 | 62 | def find_template(logical_path, format_extension = ".html") 63 | if template_path = find_template_path(logical_path, format_extension) 64 | template_for(template_path) 65 | end 66 | end 67 | 68 | def find_partial(logical_path, format_extension = ".html") 69 | if template_path = find_partial_path(logical_path, format_extension) 70 | template_for(template_path) 71 | end 72 | end 73 | 74 | def find_template_path(logical_path, format_extension) 75 | template_trail.find( 76 | logical_path, 77 | logical_path + format_extension, 78 | File.join(logical_path, "index" + format_extension) 79 | ) 80 | end 81 | 82 | def find_partial_path(logical_path, format_extension) 83 | dirname, basename = File.split(logical_path) 84 | if dirname == "." 85 | partial_path = "_" + basename 86 | else 87 | partial_path = File.join(dirname, "_" + basename) 88 | end 89 | template_trail.find(partial_path, partial_path + format_extension) 90 | end 91 | 92 | def template_for(template_path) 93 | if Brochure.development? 94 | Template.new(self, template_path) 95 | else 96 | templates[template_path] ||= Template.new(self, template_path) 97 | end 98 | end 99 | 100 | def context_for(template, env) 101 | context_class.new(self, template, env, assigns) 102 | end 103 | 104 | def respond_with(status, body, content_type = "text/html; charset=utf-8") 105 | headers = { 106 | "Content-Type" => content_type, 107 | "Content-Length" => Rack::Utils.bytesize(body).to_s 108 | } 109 | [status, headers, [body]] 110 | end 111 | 112 | def success(body, content_type) 113 | respond_with 200, body, content_type 114 | end 115 | 116 | def not_found(env) 117 | if template = find_template("404") 118 | respond_with 404, template.render(env) 119 | else 120 | default_not_found 121 | end 122 | end 123 | 124 | def default_not_found 125 | respond_with 404, <<-HTML 126 | 127 | Not Found 128 |

404 Not Found

129 | HTML 130 | end 131 | 132 | def forbidden 133 | respond_with 403, "Forbidden" 134 | end 135 | end 136 | end 137 | -------------------------------------------------------------------------------- /lib/brochure/context.rb: -------------------------------------------------------------------------------- 1 | module Brochure 2 | class Context 3 | def self.for(helpers) 4 | context = Class.new(self) 5 | context.send(:include, *helpers) if helpers.any? 6 | context 7 | end 8 | 9 | attr_reader :application, :template, :env 10 | 11 | def initialize(application, template, env, assigns = {}) 12 | @application = application 13 | @template = template 14 | @env = env 15 | 16 | load_assigns(assigns) 17 | end 18 | 19 | def load_assigns(assigns) 20 | assigns.each do |name, value| 21 | instance_variable_set("@#{name}", value) 22 | end 23 | end 24 | 25 | def request 26 | @request ||= Rack::Request.new(env) 27 | end 28 | 29 | def h(html) 30 | Rack::Utils.escape_html(html) 31 | end 32 | 33 | def render(logical_path, locals = {}, &block) 34 | if partial = application.find_partial(logical_path, template.format_extension) 35 | if block_given? 36 | concat partial.render(env, locals) { capture(&block) } 37 | else 38 | partial.render(env, locals) 39 | end 40 | else 41 | raise TemplateNotFound, "no such template '#{logical_path}'" 42 | end 43 | end 44 | 45 | def engine_name 46 | template.engine_extension[1..-1] 47 | end 48 | 49 | def concat(str) 50 | if respond_to?(method = "#{engine_name}_concat") 51 | send(method, str) 52 | elsif @_out_buf 53 | @_out_buf << str 54 | else 55 | raise CaptureNotSupported, "no capture support for #{engine_name} templates" 56 | end 57 | end 58 | 59 | def capture(&block) 60 | if respond_to?(method = "capture_#{engine_name}") 61 | send(method, &block) 62 | elsif @_out_buf 63 | begin 64 | buf = "" 65 | old_buf, @_out_buf = @_out_buf, buf 66 | yield 67 | buf 68 | ensure 69 | @_out_buf = old_buf 70 | end 71 | else 72 | raise CaptureNotSupported, "no capture support for #{engine_name} templates" 73 | end 74 | end 75 | end 76 | end 77 | -------------------------------------------------------------------------------- /lib/brochure/errors.rb: -------------------------------------------------------------------------------- 1 | module Brochure 2 | class Error < ::StandardError; end 3 | class TemplateNotFound < Error; end 4 | class CaptureNotSupported < Error; end 5 | end 6 | -------------------------------------------------------------------------------- /lib/brochure/failsafe.rb: -------------------------------------------------------------------------------- 1 | module Brochure 2 | class Failsafe 3 | def initialize(app) 4 | @app = app 5 | end 6 | 7 | def call(env) 8 | @app.call(env) 9 | rescue Exception => exception 10 | backtrace = ["#{exception.class.name}: #{exception}", *exception.backtrace].join("\n ") 11 | env["rack.errors"].puts(backtrace) 12 | env["rack.errors"].flush 13 | 14 | body = <<-HTML 15 | 16 | Internal Server Error 17 |

500 Internal Server Error

18 | HTML 19 | 20 | [500, 21 | { "Content-Type" => "text/html, charset=utf-8", 22 | "Content-Length" => Rack::Utils.bytesize(body).to_s }, 23 | [body]] 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/brochure/static.rb: -------------------------------------------------------------------------------- 1 | require 'rack/file' 2 | 3 | module Brochure 4 | class Static 5 | def initialize(app, dir) 6 | @file = Rack::File.new(dir) 7 | @app = app 8 | end 9 | 10 | def call(env) 11 | status, headers, body = @file.call(env) 12 | if status > 400 13 | @app.call(env) 14 | else 15 | [status, headers, body] 16 | end 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/brochure/template.rb: -------------------------------------------------------------------------------- 1 | module Brochure 2 | class Template 3 | attr_reader :app, :path 4 | 5 | def initialize(app, path) 6 | @app = app 7 | @path = path 8 | end 9 | 10 | def template 11 | if engine_extension 12 | options = app.template_options[engine_extension] || nil 13 | @template ||= Tilt.new(path, options, :outvar => '@_out_buf') 14 | end 15 | end 16 | 17 | def basename 18 | @basename ||= File.basename(path) 19 | end 20 | 21 | def extensions 22 | @extensions ||= basename.scan(/\.[^.]+/) 23 | end 24 | 25 | def format_extension 26 | extensions.first 27 | end 28 | 29 | def engine_extension 30 | extensions[1] 31 | end 32 | 33 | def content_type 34 | @content_type ||= begin 35 | type = Rack::Mime.mime_type(format_extension) 36 | type[/^text/] ? "#{type}; charset=utf-8" : type 37 | end 38 | end 39 | 40 | def render(env, locals = {}, &block) 41 | if template 42 | template.render(app.context_for(self, env), locals, &block) 43 | else 44 | File.read(path) 45 | end 46 | end 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /test/fixtures/custom404/templates/404.html.erb: -------------------------------------------------------------------------------- 1 | 2 | Not Found 3 | 4 |

Oops, that isn't right.

5 |

You may have typed the URL incorrectly.

6 | 7 | 8 | -------------------------------------------------------------------------------- /test/fixtures/default/helpers/link_helper.rb: -------------------------------------------------------------------------------- 1 | module LinkHelper 2 | def link_to(title, url) 3 | "#{title}" 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /test/fixtures/default/public/screen.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: "Helvetica"; 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/default/templates/_layout.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%= title %> 5 | 6 | 7 | <%= yield %> 8 | 9 | 10 | -------------------------------------------------------------------------------- /test/fixtures/default/templates/blog.html.erb: -------------------------------------------------------------------------------- 1 | <% render "layout", :title => "Blog" do %> 2 |

Latest

3 | <% end %> 4 | -------------------------------------------------------------------------------- /test/fixtures/default/templates/blog/2010.html.erb: -------------------------------------------------------------------------------- 1 | <% render "layout", :title => "Blog - 2010" do %> 2 |

Posts from 2010

3 | <% end %> 4 | -------------------------------------------------------------------------------- /test/fixtures/default/templates/doctype.html.haml: -------------------------------------------------------------------------------- 1 | !!! 2 | -------------------------------------------------------------------------------- /test/fixtures/default/templates/engineless.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Engineless <%= template %> 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /test/fixtures/default/templates/error.html.erb: -------------------------------------------------------------------------------- 1 | <% raise "foo" %> 2 | -------------------------------------------------------------------------------- /test/fixtures/default/templates/haml_with_layout.html.haml: -------------------------------------------------------------------------------- 1 | - render "layout", :title => "Blog" do 2 | %h1 Latest 3 | = "foo" 4 | -------------------------------------------------------------------------------- /test/fixtures/default/templates/hello.html.str: -------------------------------------------------------------------------------- 1 |

Hello #{request.params["name"]}

2 | Home 3 | -------------------------------------------------------------------------------- /test/fixtures/default/templates/hello.js.erb: -------------------------------------------------------------------------------- 1 | var domain = "<%= @domain %>"; 2 | -------------------------------------------------------------------------------- /test/fixtures/default/templates/help/index.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | <%= render "shared/head", :title => "Help" %> 4 | 5 |

Help

6 | <%= link_to "Home", "/" %> 7 | <%= render "shared/footer" %> 8 | 9 | 10 | -------------------------------------------------------------------------------- /test/fixtures/default/templates/help/partial_error.html.erb: -------------------------------------------------------------------------------- 1 | <%= render "shared/missing" %> 2 | -------------------------------------------------------------------------------- /test/fixtures/default/templates/help/search.html.erb: -------------------------------------------------------------------------------- 1 |

Search for "<%= request.params['term'] %>"

2 | -------------------------------------------------------------------------------- /test/fixtures/default/templates/index.html.erb: -------------------------------------------------------------------------------- 1 |

Welcome to Zombocom

2 | -------------------------------------------------------------------------------- /test/fixtures/default/templates/shared/_head.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | <%= title %> 4 | 5 | 6 | -------------------------------------------------------------------------------- /test/fixtures/default/templates/signup.html.erb: -------------------------------------------------------------------------------- 1 |

Sign up

2 | -------------------------------------------------------------------------------- /test/fixtures/default/vendor/plugins/common/templates/common.html.erb: -------------------------------------------------------------------------------- 1 | Common template 2 | -------------------------------------------------------------------------------- /test/fixtures/default/vendor/plugins/common/templates/shared/_footer.html.erb: -------------------------------------------------------------------------------- 1 |
Footer
2 | -------------------------------------------------------------------------------- /test/test_brochure.rb: -------------------------------------------------------------------------------- 1 | require "brochure" 2 | require "rack/test" 3 | require "test/unit" 4 | 5 | ENV['RACK_ENV'] = 'test' 6 | 7 | require File.expand_path("../fixtures/default/helpers/link_helper", __FILE__) 8 | 9 | class BrochureTest < Test::Unit::TestCase 10 | include Rack::Test::Methods 11 | 12 | def app(name = :default, options = {}, &blk) 13 | @app ||= Brochure.app( 14 | File.dirname(__FILE__) + "/fixtures/#{name}", 15 | :helpers => [LinkHelper], 16 | :assigns => { :domain => "37signals.com" }, 17 | :template_options => options[:template_options], 18 | &blk 19 | ) 20 | end 21 | 22 | def test_templates_are_rendered_when_present 23 | get "/signup" 24 | assert last_response.ok? 25 | assert_equal "

Sign up

", last_response.body.strip 26 | assert_equal "text/html; charset=utf-8", last_response.content_type 27 | end 28 | 29 | def test_index_templates_are_rendered_for_directories 30 | get "/" 31 | assert last_response.ok? 32 | assert_equal "

Welcome to Zombocom

", last_response.body.strip 33 | end 34 | 35 | def test_extensions_are_ignored 36 | get "/signup.html" 37 | assert last_response.ok? 38 | assert_equal "

Sign up

", last_response.body.strip 39 | end 40 | 41 | def test_partials_are_not_publicly_accessible 42 | get "/shared/_head" 43 | assert last_response.forbidden? 44 | end 45 | 46 | def test_static_asset 47 | get "/screen.css" 48 | assert last_response.ok? 49 | end 50 | 51 | def test_404_is_returned_when_a_template_is_not_present 52 | get "/nonexistent" 53 | assert last_response.not_found? 54 | assert_match %r{

404 Not Found

}, last_response.body 55 | end 56 | 57 | def test_404_is_returned_for_a_directory_when_an_index_template_is_not_present 58 | get "/shared" 59 | assert last_response.not_found? 60 | end 61 | 62 | def test_custom_404_is_returned_when_a_404_html_template_is_not_present 63 | app :custom404 64 | 65 | get "/nonexistent" 66 | assert last_response.not_found? 67 | assert_match %r{

Oops, that isn't right.

}, last_response.body #' 68 | end 69 | 70 | def test_500_is_returned_when_a_template_raises_an_exception 71 | get "/error" 72 | assert last_response.server_error? 73 | end 74 | 75 | def test_403_is_returned_when_path_is_outside_root 76 | get "/../passwd" 77 | assert_equal 403, last_response.status 78 | end 79 | 80 | def test_template_has_access_to_request 81 | get "/help/search?term=export" 82 | assert last_response.body["

Search for \"export\"

"] 83 | end 84 | 85 | def test_partials_can_be_rendered_from_templates 86 | get "/help" 87 | assert last_response.body["Help"] 88 | end 89 | 90 | def test_helpers_are_available_to_templates 91 | get "/help" 92 | assert last_response.body["Home"] 93 | end 94 | 95 | def test_missing_partial_raises_an_error 96 | get "/help/partial_error" 97 | assert last_response.server_error? 98 | end 99 | 100 | def test_render_layout_with_block 101 | get "/blog" 102 | assert last_response.body["Blog"] 103 | assert last_response.body["

Latest

"] 104 | 105 | get "/blog/2010" 106 | assert last_response.body["Blog - 2010"] 107 | assert last_response.body["

Posts from 2010

"] 108 | end 109 | 110 | def test_using_other_tilt_template_types 111 | get "/hello?name=Sam" 112 | assert last_response.body["

Hello Sam

"] 113 | end 114 | 115 | def test_block_configured 116 | app do |a| 117 | path = '../custom404/templates' 118 | a.template_trail.paths.push path 119 | end 120 | get '/404' 121 | assert last_response.ok? 122 | end 123 | 124 | def test_templates_in_plugins 125 | get "/common" 126 | assert last_response.body["Common template"] 127 | end 128 | 129 | def test_rendering_partials_in_plugins 130 | get "/help" 131 | assert last_response.body["
Footer
"] 132 | end 133 | 134 | def test_assigns_are_available_in_templates 135 | get "/hello" 136 | assert last_response.body['Home'] 137 | end 138 | 139 | def test_alternate_template_format 140 | get "/hello.js" 141 | assert last_response.body['var domain = "37signals.com";'] 142 | assert_equal "application/javascript", last_response.content_type 143 | end 144 | 145 | def test_engineless_templates 146 | get "/engineless" 147 | assert last_response.ok? 148 | assert last_response.body["Engineless <%= template %>"] 149 | end 150 | 151 | def test_haml_with_layout 152 | get "/haml_with_layout" 153 | assert last_response.body["Blog"] 154 | assert last_response.body["

Latest

"] 155 | end 156 | 157 | def test_haml_with_xhtml_format 158 | app :default, :template_options => { ".haml" => { :format => :xhtml } } 159 | get "/doctype" 160 | assert last_response.body["\n"] 161 | end 162 | 163 | def test_haml_with_html5_format 164 | app :default, :template_options => { ".haml" => { :format => :html5 } } 165 | get "/doctype" 166 | assert last_response.body["\n"] 167 | end 168 | end 169 | --------------------------------------------------------------------------------