Created
October 15, 2009 19:59
-
-
Save Sutto/211235 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
BEFORE TEMPLATE | |
<%= content_for_layout %> | |
AFTER TEMPLATE |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
BEFORE TEMPLATE TWO | |
= content_for_layout | |
AFTER TEMPLATE TWO |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
BEFORE TEMPLATE THREE | |
<%= yield %> | |
AFTER TEMPLATE THREE |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
BEFORE TEMPLATE FOUR | |
= yield | |
AFTER TEMPLATE FOUR |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<%= @request.request_method %> to <%= @request.path_info %> with <%= @request.query_string %> - <%= content_for_layout %> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<%= request.request_method %> to <%= request.path_info %> with <%= request.query_string %> - <%= content_for_layout %> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'rack' | |
# sudo gem install tilt --source http://gemcutter.org/ | |
require 'tilt' | |
# A simple Rack middleware which makes it super easy | |
# have a shared layout rendering system used across | |
# different rails applications. | |
# | |
# Examples: | |
# | |
# use Rack::LayoutWrapper, :template_dir => "required-path-to-template", | |
# :cache_listings => true, # Optional option to cache listings | |
# :default => "some-default-layout-name" # Optional default layout name | |
# | |
# Released under the MIT License | |
module Rack | |
class LayoutWrapper | |
class BodyWrapper | |
def initialize(before, original_body, after) | |
@before = before | |
@after = after | |
@original_body = original_body | |
end | |
def each | |
yield @before | |
@original_body.each { |c| yield c } | |
yield @after | |
end | |
end | |
@@template_path_cache = {} | |
PLACEHOLDER_CONTENTS = 'TEMPLATE-STUFF-GOES-HERE'.freeze | |
PLACEHOLDER_ENV_KEY = 'x-rack.layout_wrapper'.freeze | |
attr_accessor :template_dir, :default_layout | |
def initialize(app, opts = {}) | |
@app = app | |
@render_layout = false | |
@layouts = nil | |
@layout_name = nil | |
@template_dir = opts[:template_dir] | |
@cache_listings = opts[:cache_listings] || true | |
@default_layout = opts[:default] | |
if @template_dir.to_s.strip == "" || !::File.directory?(@template_dir) | |
raise ArgumentError, "You need to provide a valid :template_dir directory" | |
end | |
end | |
def call(env) | |
available_layouts if @layouts.nil? && @cache_listings | |
dup._call(env) | |
end | |
def _call(env) | |
env[PLACEHOLDER_ENV_KEY] = self | |
status, headers, body = @app.call(env) | |
@request = (env['rack.request'] || Rack::Request.new(env)) | |
headers, body = wrap_with_layout(headers, body) if @render_layout | |
[status, headers, body] | |
end | |
def render_layout? | |
@render_layout | |
end | |
def layout_name=(value) | |
@render_layout = !value.nil? | |
@layout_name = value | |
end | |
def render_layout=(value) | |
@render_layout = value | |
end | |
def layout_name | |
@layout_name ||= @default_layout | |
end | |
def available_layouts | |
@layouts ||= begin | |
layouts = Dir[::File.join(@template_dir, "**", "*")].select { |i| ::File.file?(i) } | |
layouts.map { |l| l.gsub(/^#{Regexp.escape(@template_dir)}\/(.*)\..*/, '\1') }.uniq | |
end | |
end | |
def request | |
@request | |
end | |
def full_template_path | |
default = @@template_path_cache[layout_name] | |
return default if default && ::File.exist?(default) | |
@@template_path_cache[@layout_name] = Dir[::File.join(@template_dir, "#{@layout_name}.*")].select { |i| ::File.file?(i) }.first | |
end | |
protected | |
def render_layout_parts | |
template_path = full_template_path | |
return "", "" if template_path.nil? || !::File.exist?(template_path) | |
locals = {:content_for_layout => PLACEHOLDER_CONTENTS, :request => @request} | |
template = Tilt.new(full_template_path).render(self, locals) { PLACEHOLDER_CONTENTS } | |
before_template, after_template = template.split(PLACEHOLDER_CONTENTS, 2) | |
before_template ||= "" | |
after_template ||= "" | |
return before_template, after_template | |
end | |
def wrap_with_layout(headers, body) | |
header_hash = Rack::Utils::HeaderHash.new(headers) | |
content_length = header_hash['Content-Length'].to_i | |
before_layout, after_layout = render_layout_parts | |
content_length += Rack::Utils.bytesize(before_layout) | |
content_length += Rack::Utils.bytesize(after_layout) | |
header_hash['Content-Length'] = content_length.to_s | |
body_wrapper = BodyWrapper.new(before_layout, body, after_layout) | |
return header_hash, body_wrapper | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'rubygems' | |
require 'test/unit' | |
require 'redgreen' if RUBY_VERSION < '1.9' | |
require 'rack' | |
require 'erb' | |
require 'haml' | |
require File.join(File.dirname(__FILE__), "layout_wrapper") | |
class APIVersionMapperTest < Test::Unit::TestCase | |
def setup | |
@example_app = proc do |env| | |
req = Rack::Request.new(env) | |
headers = { | |
"Content-Type" => "text/html", | |
"Content-Length" => "8" | |
} | |
env[Rack::LayoutWrapper::PLACEHOLDER_ENV_KEY].layout_name = req.GET["layout_name"] if req.GET["layout_name"] | |
env[Rack::LayoutWrapper::PLACEHOLDER_ENV_KEY].render_layout = true if req.GET["render_layout"] | |
[200, headers, ["FROM APP"]] | |
end | |
@app = Rack::LayoutWrapper.new(@example_app, :template_dir => File.join(File.dirname(__FILE__), "layouts")) | |
end | |
def test_no_layouts | |
assert [email protected]_layout? | |
get '/' | |
assert_valid_response "FROM APP" | |
end | |
def test_simple_erb_layout | |
get '/?layout_name=example_a' | |
assert_valid_response "BEFORE TEMPLATE\nFROM APP\nAFTER TEMPLATE" | |
end | |
def test_simple_haml_layout | |
get '/?layout_name=example_b' | |
assert_valid_response "BEFORE TEMPLATE TWO\nFROM APP\nAFTER TEMPLATE TWO\n" | |
end | |
def test_yielded_erb_layout | |
get '/?layout_name=example_c' | |
assert_valid_response "BEFORE TEMPLATE THREE\nFROM APP\nAFTER TEMPLATE THREE" | |
end | |
def test_yielded_haml_layout | |
get '/?layout_name=example_d' | |
assert_valid_response "BEFORE TEMPLATE FOUR\nFROM APP\nAFTER TEMPLATE FOUR\n" | |
end | |
def test_default_layouts | |
@app.default_layout = "example_a" | |
get '/?render_layout=true' | |
assert_valid_response "BEFORE TEMPLATE\nFROM APP\nAFTER TEMPLATE" | |
# Now, override the default | |
get '/?layout_name=example_c' | |
assert_valid_response "BEFORE TEMPLATE THREE\nFROM APP\nAFTER TEMPLATE THREE" | |
end | |
def test_template_scope | |
get '/?layout_name=example_e' | |
assert_valid_response "GET to / with layout_name=example_e - FROM APP\n" | |
end | |
def test_template_request | |
get '/?layout_name=example_f' | |
assert_valid_response "GET to / with layout_name=example_f - FROM APP\n" | |
end | |
protected | |
def get(path, env = {}) | |
@response = Rack::MockRequest.new(@app).get(path, env) | |
end | |
def assert_valid_response(body) | |
assert @response.ok?, "the response should be ok (was #{@response.status} instead)" | |
content_length = Rack::Utils.bytesize(body) | |
real_content_length = @response["Content-Length"].to_i | |
assert_equal content_length, real_content_length, "Expected a Content-Length of #{content_length}, got #{real_content_length}" | |
assert_equal body, @response.body, "Incorrect body in response" | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment