Skip to content

Commit

Permalink
add template support to namespaces
Browse files Browse the repository at this point in the history
  • Loading branch information
rkh committed Mar 29, 2011
1 parent 10a2734 commit aaf66d7
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 19 deletions.
2 changes: 0 additions & 2 deletions ideas.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,3 @@

* Extend `sinatra-content-for` to support Liquid, Radius, Markaby, Nokogiri and
Builder. At least the first two probably involve patching Tilt.

* Have `sinatra-namespace` support namespace local templates.
63 changes: 49 additions & 14 deletions lib/sinatra/namespace.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
require 'backports'
require 'sinatra/base'
require 'sinatra/decompile'

Expand All @@ -6,23 +7,34 @@ module Namespace
def self.new(base, pattern, conditions = {}, &block)
Module.new do
extend NamespacedMethods
@base, @extensions = base, []
include InstanceMethods
@base, @extensions = base, []
@pattern, @conditions = compile(pattern, conditions)

@templates = Hash.new { |h,k| @base.templates[k] }
namespace = self
before { extend namespace }
define_method(:error_block!) do |*keys|
if block = keys.inject(nil) { |b,k| b ||= namespace.errors[k] }
instance_eval(&block)
else
super(*keys)
end
end

before { extend(@namespace = namespace) }
class_eval(&block)
end
end

module InstanceMethods
def settings
@namespace
end

def template_cache
super.fetch(:nested, @namespace) { Tilt::Cache.new }
end

def error_block!(*keys)
if block = keys.inject(nil) { |b,k| b ||= @namespace.errors[k] }
instance_eval(&block)
else
super
end
end
end

module SharedMethods
def namespace(pattern, conditions = {}, &block)
Sinatra::Namespace.new(self, pattern, conditions, &block)
Expand All @@ -32,7 +44,7 @@ def namespace(pattern, conditions = {}, &block)
module NamespacedMethods
include SharedMethods
include Sinatra::Decompile
attr_reader :base
attr_reader :base, :templates

def self.prefixed(*names)
names.each { |n| define_method(n) { |*a, &b| prefixed(n, *a, &b) }}
Expand Down Expand Up @@ -75,6 +87,30 @@ def respond_to(*args)
@conditions[:provides] = args
end

def set(key, value = self, &block)
raise ArgumentError, "may not set #{key}" if key != :views
return key.each { |k,v| set(k, v) } if block.nil? and value == self
block ||= proc { value }
singleton_class.send(:define_method, key, &block)
end

def enable(*opts)
opts.each { |key| set(key, true) }
end

def disable(*opts)
opts.each { |key| set(key, false) }
end

def template(name, &block)
filename, line = caller_locations.first
templates[name] = [block, filename, line.to_i]
end

def layout(name=:layout, &block)
template name, &block
end

private

def app
Expand Down Expand Up @@ -118,8 +154,7 @@ def prefixed(method, pattern = nil, conditions = {}, &block)
end

def method_missing(meth, *args, &block)
return super if args.any? or block or not base.respond_to? meth
base.send meth
base.send(meth, *args, &block)
end
end

Expand Down
1 change: 1 addition & 0 deletions spec/namespace/foo.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
hi
1 change: 1 addition & 0 deletions spec/namespace/nested/foo.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ho
83 changes: 80 additions & 3 deletions spec/namespace_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -490,9 +490,86 @@ def magic
end

describe 'templates' do
it "allows to define nested templates"
it "allows to define nested layouts"
it "allows setting a different views directory"
it "allows using templates from the base" do
mock_app do
template(:foo) { 'hi' }
send(verb, '/') { erb :foo }
namespace '/foo' do
send(verb) { erb :foo }
end
end

if verb != :head
send(verb, '/').body.should == "hi"
send(verb, '/foo').body.should == "hi"
end
end

it "allows to define nested templates" do
mock_app do
template(:foo) { 'hi' }
send(verb, '/') { erb :foo }
namespace '/foo' do
template(:foo) { 'ho' }
send(verb) { erb :foo }
end
end

if verb != :head
send(verb, '/').body.should == "hi"
send(verb, '/foo').body.should == "ho"
end
end

it "allows to define nested layouts" do
mock_app do
layout { 'Hello <%= yield %>!' }
template(:foo) { 'World' }
send(verb, '/') { erb :foo }
namespace '/foo' do
layout { 'Hi <%= yield %>!' }
send(verb) { erb :foo }
end
end

if verb != :head
send(verb, '/').body.should == "Hello World!"
send(verb, '/foo').body.should == "Hi World!"
end
end

it "allows using templates from the base" do
mock_app do
layout { "he said: <%= yield %>" }
template(:foo) { 'hi' }
send(verb, '/') { erb :foo }
namespace '/foo' do
template(:foo) { 'ho' }
send(verb) { erb :foo }
end
end

if verb != :head
send(verb, '/').body.should == "he said: hi"
send(verb, '/foo').body.should == "he said: ho"
end
end

it "allows setting a different views directory" do
mock_app do
set :views, File.expand_path('../namespace', __FILE__)
send(verb, '/') { erb :foo }
namespace('/foo') do
set :views, File.expand_path('../namespace/nested', __FILE__)
send(verb) { erb :foo }
end
end

if verb != :head
send(verb, '/').body.should == "hi\n"
send(verb, '/foo').body.should == "ho\n"
end
end
end

describe 'extensions' do
Expand Down

0 comments on commit aaf66d7

Please sign in to comment.