template-inheritance 0.0.0.0.0.0.0.0.1 → 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +9 -0
- data/README.textile +18 -1
- data/example/base.html.haml +31 -0
- data/example/example.rb +16 -0
- data/example/shared/google_analytics.html.haml +8 -0
- data/example/site/_wtf.html.haml +3 -0
- data/example/site/base.html.haml +38 -0
- data/example/site/post.html.haml +17 -0
- data/lib/template-inheritance.rb +118 -0
- data/lib/template-inheritance/exts/haml.rb +33 -0
- data/lib/template-inheritance/exts/tilt.rb +29 -0
- data/lib/template-inheritance/helpers.rb +144 -0
- data/spec/spec.opts +5 -0
- data/spec/spec_helper.rb +51 -0
- data/spec/stubs/block/block.html.haml +2 -0
- data/spec/stubs/block/blocks.html.haml +5 -0
- data/spec/stubs/block/error.html.haml +2 -0
- data/spec/stubs/block/getter.html.haml +3 -0
- data/spec/stubs/block/value.html.haml +1 -0
- data/spec/stubs/context_id.html.haml +1 -0
- data/spec/stubs/enhance_block/basic.html.haml +5 -0
- data/spec/stubs/enhance_block/error.html.haml +3 -0
- data/spec/stubs/enhance_block/name_error.html.haml +1 -0
- data/spec/stubs/enhance_block/nil.html.haml +3 -0
- data/spec/stubs/enhance_block/standalone.html.haml +2 -0
- data/spec/stubs/extend_block/basic.html.haml +5 -0
- data/spec/stubs/extend_block/error.html.haml +5 -0
- data/spec/stubs/extend_block/error2.html.haml +1 -0
- data/spec/stubs/extend_block/name_error.html.haml +1 -0
- data/spec/stubs/extend_block/nil.html.haml +4 -0
- data/spec/stubs/includes/base.html.haml +1 -0
- data/spec/stubs/includes/basic.html.haml +1 -0
- data/spec/stubs/includes/includes.html.haml +7 -0
- data/spec/stubs/includes/integration.html.haml +4 -0
- data/spec/stubs/includes/integration2.html.haml +4 -0
- data/spec/stubs/index.html.haml +1 -0
- data/spec/stubs/inheritance/basic/base.html.haml +2 -0
- data/spec/stubs/inheritance/basic/index.html.haml +2 -0
- data/spec/stubs/inheritance/capture/haml/base.html.haml +2 -0
- data/spec/stubs/inheritance/capture/haml/index.html.haml +3 -0
- data/spec/stubs/library.html.haml +3 -0
- data/spec/stubs/test.html.haml +1 -0
- data/spec/stubs/variables.html.haml +1 -0
- data/spec/templates/exts/haml_spec.rb +0 -0
- data/spec/templates/exts/tilt_spec.rb +0 -0
- data/spec/templates/helpers_spec.rb +139 -0
- data/spec/templates/template_spec.rb +61 -0
- data/template-inheritance.gemspec +42 -0
- metadata +66 -10
data/.gitignore
ADDED
data/README.textile
CHANGED
@@ -1,3 +1,20 @@
|
|
1
1
|
h1. About
|
2
2
|
|
3
|
-
|
3
|
+
This is a framework-agnostic template inheritance engine from Rango.
|
4
|
+
|
5
|
+
h1. Usage
|
6
|
+
|
7
|
+
<pre>
|
8
|
+
require "template-inheritance"
|
9
|
+
|
10
|
+
TemplateInheritance::Template.paths << File.expand_path("..", __FILE__)
|
11
|
+
|
12
|
+
template = TemplateInheritance::Template.new("site/post.html.haml")
|
13
|
+
template.render(post: post)
|
14
|
+
</pre>
|
15
|
+
|
16
|
+
h1. Haml Extensions
|
17
|
+
|
18
|
+
<pre>
|
19
|
+
Tilt::HamlTemplate.options[:default_attributes] = {form: {method: "post"}}
|
20
|
+
</pre>
|
@@ -0,0 +1,31 @@
|
|
1
|
+
!!! 5
|
2
|
+
|
3
|
+
/ Why should be poor templates so low-level?
|
4
|
+
/ Even they deserve their Kernel#require!
|
5
|
+
- includes "shared/google_analytics.html"
|
6
|
+
|
7
|
+
%html
|
8
|
+
%head
|
9
|
+
/ This is a great fun, the content for the block value
|
10
|
+
/ can be just anything, not just some dumb string.
|
11
|
+
/ The default value is there just in case that no
|
12
|
+
/ scripts would be provided in children templates.
|
13
|
+
- block(:scripts, Array.new).each do |script|
|
14
|
+
%script{src: "/scripts/#{script}"}
|
15
|
+
%body
|
16
|
+
/ Just to make sure that the default value for
|
17
|
+
/ method works (as defined in example.rb).
|
18
|
+
%form{action: "/search"}
|
19
|
+
|
20
|
+
= block(:google_analytics)
|
21
|
+
= block(:main) do
|
22
|
+
/ This is default content for the 'main' block.
|
23
|
+
/ In case that we wouldn't fill in the block in
|
24
|
+
/ any of child templates, the default content
|
25
|
+
/ would be rendered. If we fill in the block,
|
26
|
+
/ the default content is ignored.
|
27
|
+
|
28
|
+
/ We can use either block(name, content)
|
29
|
+
/ or block(name) { content }.
|
30
|
+
|
31
|
+
No content yet.
|
data/example/example.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
$LOAD_PATH << File.expand_path("../../lib", __FILE__)
|
4
|
+
|
5
|
+
require "ostruct"
|
6
|
+
require "template-inheritance"
|
7
|
+
|
8
|
+
TemplateInheritance::Template.paths << File.expand_path("..", __FILE__)
|
9
|
+
|
10
|
+
post = OpenStruct.new(title: "My First Post!", content: "This is my first post!")
|
11
|
+
|
12
|
+
# setup
|
13
|
+
Tilt::HamlTemplate.options[:default_attributes] = {form: {method: "post"}}
|
14
|
+
|
15
|
+
template = TemplateInheritance::Template.new("site/post.html.haml")
|
16
|
+
puts "", template.render(post: post)
|
@@ -0,0 +1,38 @@
|
|
1
|
+
/ Extends means "use this template as a layout". Of course
|
2
|
+
/ as this is template inheritance, the layout can have its
|
3
|
+
/ own layout which can have its own layout etc, there's no
|
4
|
+
/ restriction.
|
5
|
+
|
6
|
+
/ The right fun starts with conditional extends, for example
|
7
|
+
/ extends "base.html" unless request.ajax? (works in Rango)
|
8
|
+
/ So in case of a XHR request it will just render current
|
9
|
+
/ template without any layouting. Then if you use code
|
10
|
+
/ "= block(:main)" instead of "- block(:main)", it will
|
11
|
+
/ actually returns result of the block(:main) into the
|
12
|
+
/ template. This way you are agnostic to XHR/non-XHR
|
13
|
+
/ requests, simply return wrapped HTML or just the
|
14
|
+
/ required chunk depending on the type of request.
|
15
|
+
- extends "base.html" # unless request.ajax?
|
16
|
+
|
17
|
+
/ Oh yeah you can access the value and modify it as you like!
|
18
|
+
- block(:scripts).unshift("mootools-core.js")
|
19
|
+
|
20
|
+
/ You might thing that the following solution might work
|
21
|
+
/ as well, but it won't, because in this case it'd be the
|
22
|
+
/ default value and default value is used just in case it
|
23
|
+
/ won't get any value in the child template.
|
24
|
+
/- block(:scripts, block(:scripts) + ["mootools-core"])
|
25
|
+
|
26
|
+
/ However there's the extend_block helper which
|
27
|
+
/ will rewrite the value from the child template:
|
28
|
+
/- extend_block(:scripts, block(:scripts) + ["mootools-core"])
|
29
|
+
|
30
|
+
/ Another one is enhance_block which does the same,
|
31
|
+
/ but unlike the extend_block helper, it doesn't
|
32
|
+
/ fail in case the block isn't defined yet.
|
33
|
+
|
34
|
+
/ And you can also clear block using clear_block(name).
|
35
|
+
|
36
|
+
- block(:main) do
|
37
|
+
%h1 Welcome to my cool blog!
|
38
|
+
= block(:post)
|
@@ -0,0 +1,17 @@
|
|
1
|
+
/ Paths
|
2
|
+
|
3
|
+
/ ./base.html: Find template base.html in the same directory as current template.
|
4
|
+
/ ../base.html: Find template base.html in parent directory of current template.
|
5
|
+
|
6
|
+
/ base.html: find template base.html in TemplateInheritance::Template.paths array.
|
7
|
+
/ The lookup works same as Ruby's require lookup, it's basically
|
8
|
+
/ TemplateInheritance::Template.paths.find { |dir| File.join(dir, template_name) }
|
9
|
+
- extends "./base.html"
|
10
|
+
|
11
|
+
- block(:scripts, ["post.js"])
|
12
|
+
|
13
|
+
- block(:post) do
|
14
|
+
%h2= post.title
|
15
|
+
%p= post.content
|
16
|
+
|
17
|
+
= partial "./wtf", local: true
|
@@ -0,0 +1,118 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "template-inheritance/exts/tilt"
|
4
|
+
require "template-inheritance/helpers"
|
5
|
+
|
6
|
+
module TemplateInheritance
|
7
|
+
@@development = true
|
8
|
+
def self.development=(boolean)
|
9
|
+
@@development = boolean
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.development?
|
13
|
+
@@development
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.logger
|
17
|
+
@@logger ||= SimpleLogger.new
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.logger=(logger)
|
21
|
+
@@logger = logger
|
22
|
+
end
|
23
|
+
|
24
|
+
class TemplateNotFound < StandardError
|
25
|
+
def initialize(message = "Template not found")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class SimpleLogger
|
30
|
+
def log(message)
|
31
|
+
puts "~ #{message}"
|
32
|
+
end
|
33
|
+
|
34
|
+
alias_method :info, :log
|
35
|
+
alias_method :debug, :log
|
36
|
+
end
|
37
|
+
|
38
|
+
class Template
|
39
|
+
def self.paths
|
40
|
+
@@paths ||= Array.new
|
41
|
+
end
|
42
|
+
|
43
|
+
# template -> supertemplate is the same relationship as class -> superclass
|
44
|
+
# @since 0.0.2
|
45
|
+
attr_accessor :path, :scope, :supertemplate, :context
|
46
|
+
|
47
|
+
# @since 0.0.2
|
48
|
+
attr_writer :blocks
|
49
|
+
def blocks
|
50
|
+
@blocks ||= Hash.new
|
51
|
+
end
|
52
|
+
|
53
|
+
# @since 0.0.2
|
54
|
+
def initialize(path, scope = Object.new)
|
55
|
+
self.path = path#[scope.class.template_prefix.chomp("/"), template].join("/")
|
56
|
+
self.scope = scope
|
57
|
+
self.scope.extend(TemplateHelpers)
|
58
|
+
# this enables template caching
|
59
|
+
unless TemplateInheritance.development?
|
60
|
+
self.scope.extend(Tilt::CompileSite)
|
61
|
+
end
|
62
|
+
self.scope.template = self
|
63
|
+
end
|
64
|
+
|
65
|
+
# @since 0.0.2
|
66
|
+
def fullpath
|
67
|
+
@fullpath ||= begin
|
68
|
+
if self.path.match(/^(\/|\.)/) # /foo or ./foo
|
69
|
+
Dir[self.path, "#{self.path}.*"].find {|file| !File.directory?(file)}
|
70
|
+
else
|
71
|
+
self.find_in_paths
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def adapter
|
77
|
+
snake_case(self.template.class.name.split("::").last).sub("_template", "")
|
78
|
+
end
|
79
|
+
|
80
|
+
def extension # haml, erb ...
|
81
|
+
File.extname(path)[1..-1]
|
82
|
+
end
|
83
|
+
|
84
|
+
def template(options = Hash.new)
|
85
|
+
@template ||= Tilt.new(self.fullpath, nil, options)
|
86
|
+
end
|
87
|
+
|
88
|
+
# @since 0.0.2
|
89
|
+
def render(context = Hash.new)
|
90
|
+
raise TemplateNotFound.new("Template #{self.path} wasn't found in these paths: #{self.class.paths.inspect}") if self.fullpath.nil?
|
91
|
+
TemplateInheritance.logger.info("Rendering template #{self.path} with context keys #{context.keys.inspect}")
|
92
|
+
self.scope.context = self.context = context # so we can access context in the scope object as well
|
93
|
+
value = self.template.render(self.scope, context)
|
94
|
+
TemplateInheritance.logger.debug("Available blocks: #{self.blocks.keys.inspect}")
|
95
|
+
if self.supertemplate
|
96
|
+
TemplateInheritance.logger.debug("Extends call: #{self.supertemplate}")
|
97
|
+
supertemplate = self.class.new(self.supertemplate, self.scope)
|
98
|
+
supertemplate.blocks = self.blocks
|
99
|
+
return supertemplate.render(context)
|
100
|
+
end
|
101
|
+
value
|
102
|
+
end
|
103
|
+
|
104
|
+
protected
|
105
|
+
def find_in_paths
|
106
|
+
self.class.paths.each do |directory|
|
107
|
+
path = File.join(directory, self.path)
|
108
|
+
return Dir[path, "#{path}.*"].find {|file| !File.directory?(file)}
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def snake_case(string)
|
113
|
+
return string.downcase if string =~ /^[A-Z]+$/
|
114
|
+
string.gsub(/([A-Z]+)(?=[A-Z][a-z]?)|\B[A-Z]/, '_\&') =~ /_*(.*)/
|
115
|
+
return $+.downcase
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# Option default_attributes
|
4
|
+
#
|
5
|
+
# A hash of default attributes for tags (`{tag => {attribute => default_value}}`).
|
6
|
+
# Attributes of each tag will reverse merged with his default attributes, so you
|
7
|
+
# don't have to write over and over that script tag has attribute `type` with value
|
8
|
+
# `text/javascript`. For example, `%script` compiles to `<script type="text/javascript"></script>`.
|
9
|
+
# Defaults to `{script: {type: "text/javascript"}, form: {method: "POST"}}`
|
10
|
+
|
11
|
+
module Haml
|
12
|
+
module Precompiler
|
13
|
+
alias_method :__prerender_tag__, :prerender_tag
|
14
|
+
def prerender_tag(name, self_close, attributes)
|
15
|
+
# merge given attributes with default attributes from options
|
16
|
+
defaults = Tilt::HamlTemplate.options[:default_attributes][name.to_sym]
|
17
|
+
attributes = defaults.merge(attributes) if defaults
|
18
|
+
__prerender_tag__(name, self_close, attributes)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class Buffer
|
23
|
+
alias_method :__open_tag__, :open_tag
|
24
|
+
def open_tag(name, self_closing, try_one_line, preserve_tag, escape_html, class_id,
|
25
|
+
nuke_outer_whitespace, nuke_inner_whitespace, obj_ref, content, *attributes_hashes)
|
26
|
+
defaults = Tilt::HamlTemplate.options[:default_attributes][name.to_sym]
|
27
|
+
attributes_hashes.unshift(defaults) if defaults
|
28
|
+
|
29
|
+
__open_tag__(name, self_closing, try_one_line, preserve_tag, escape_html, class_id,
|
30
|
+
nuke_outer_whitespace, nuke_inner_whitespace, obj_ref, content, *attributes_hashes)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "tilt"
|
4
|
+
|
5
|
+
module TemplateInheritance
|
6
|
+
module TiltExtensions
|
7
|
+
# Tilt::HamlTemplate.options[:default_attributes] = {script: {type: "text/javascript"}, form: {method: "POST"}}
|
8
|
+
module Haml
|
9
|
+
def self.included(klass)
|
10
|
+
klass.send(:remove_method, :initialize_engine)
|
11
|
+
def klass.options
|
12
|
+
@options ||= Hash.new
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize_engine
|
17
|
+
require_template_library 'haml' unless defined? ::Haml::Engine
|
18
|
+
require "template-inheritance/exts/haml" if self.class.options[:default_attributes]
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize(*args)
|
22
|
+
super
|
23
|
+
self.options.merge!(self.class.options)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
Tilt::HamlTemplate.send(:include, TemplateInheritance::TiltExtensions::Haml)
|
@@ -0,0 +1,144 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module TemplateInheritance
|
4
|
+
SubtemplateNotFound = Class.new(StandardError)
|
5
|
+
|
6
|
+
module TemplateHelpers
|
7
|
+
def self.extended(scope)
|
8
|
+
class << scope
|
9
|
+
attr_accessor :template
|
10
|
+
attr_accessor :context
|
11
|
+
# @example Capture being used in a .html.erb page:
|
12
|
+
# <% @foo = capture do %>
|
13
|
+
# <p>Some Foo content!</p>
|
14
|
+
# <% end %>
|
15
|
+
#
|
16
|
+
# @params [*args] Arguments to pass to the block.
|
17
|
+
# @params [&block] The template block to call.
|
18
|
+
# @return [String] The output of the block.
|
19
|
+
# @api private
|
20
|
+
def capture(*args, &block)
|
21
|
+
capture_method = "capture_#{self.template.adapter}"
|
22
|
+
if self.respond_to?(capture_method) # tilt doesn't support @_out_buf for haml
|
23
|
+
self.send("capture_#{self.template.adapter}", *args, &block)
|
24
|
+
else
|
25
|
+
# @_out_buf comes from tilt
|
26
|
+
unless self.instance_variable_defined?("@_out_buf")
|
27
|
+
raise "Adapter #{self.template.adapter} doesn't support capturing"
|
28
|
+
end
|
29
|
+
_old_buf, @_out_buf = @_out_buf, ""
|
30
|
+
block.call(*args)
|
31
|
+
@_out_buf = _old_buf.chomp.strip
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def concat(string)
|
36
|
+
concat_method = "concat_#{self.template.adapter}"
|
37
|
+
if self.respond_to?(concat_method) # tilt doesn't support @_out_buf for haml
|
38
|
+
self.send("concat_#{self.template.adapter}", string)
|
39
|
+
else
|
40
|
+
# @_out_buf comes from tilt
|
41
|
+
unless self.instance_variable_defined?("@_out_buf")
|
42
|
+
raise "Adapter #{self.template.adapter} doesn't support concating"
|
43
|
+
end
|
44
|
+
@_out_buf << string
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.included(scope_class)
|
51
|
+
scope_class.class_eval { attr_accessor :template }
|
52
|
+
end
|
53
|
+
|
54
|
+
# post/show.html: it's block is the block we like to see in output
|
55
|
+
# post/base.html
|
56
|
+
# base.html: here it will be rendered, so we need block to returns the correct block code
|
57
|
+
# @since 0.0.2
|
58
|
+
# @version 0.2
|
59
|
+
def block(name, value = nil, &block)
|
60
|
+
raise ArgumentError, "Block has to have a name!" if name.nil?
|
61
|
+
raise ArgumentError, "You have to provide value or block, not both of them!" if value && block
|
62
|
+
self.template.blocks[name] ||= block ? self.template.scope.capture(&block) : value
|
63
|
+
return self.template.blocks[name]
|
64
|
+
end
|
65
|
+
|
66
|
+
# - extend_block(:head) do
|
67
|
+
# = pupu :lighter, syntax: "html", theme: "standard"
|
68
|
+
# = block(:head)
|
69
|
+
def extend_block(name, value = nil, &block)
|
70
|
+
unless self.template.blocks[name]
|
71
|
+
raise NameError, "Block #{name.inspect} wasn't defined yet, you can't extend it!"
|
72
|
+
end
|
73
|
+
self.enhance_block(name, value, &block)
|
74
|
+
end
|
75
|
+
|
76
|
+
def enhance_block(name, value = nil, &block)
|
77
|
+
raise ArgumentError, "Block has to have a name!" if name.nil?
|
78
|
+
raise ArgumentError, "You have to provide value or block, not both of them!" if value && block
|
79
|
+
value = self.template.scope.capture(&block) if value.nil? && block
|
80
|
+
self.template.blocks[name] = value if value
|
81
|
+
return self.template.blocks[name]
|
82
|
+
end
|
83
|
+
|
84
|
+
# Clears default content of given block.
|
85
|
+
#
|
86
|
+
# @example
|
87
|
+
# clear_block(:flyout)
|
88
|
+
def clear_block(name)
|
89
|
+
raise ArgumentError, "You need to specify name of block to clear." if name.nil?
|
90
|
+
self.template.blocks[name] = String.new
|
91
|
+
end
|
92
|
+
|
93
|
+
# Low-level rendering method for templates.
|
94
|
+
#
|
95
|
+
# @since 0.2
|
96
|
+
# @example
|
97
|
+
# render "base.html"
|
98
|
+
# render "./base.html"
|
99
|
+
# render "../base.html"
|
100
|
+
def render(path, context = Hash.new)
|
101
|
+
full_path = normalize_template_path(path)
|
102
|
+
original_template = self.template
|
103
|
+
template = TemplateInheritance::Template.new(full_path, self) # self is scope
|
104
|
+
self.template = original_template
|
105
|
+
return template.render(context)
|
106
|
+
rescue Exceptions::TemplateNotFound # FIXME: this doesn't work
|
107
|
+
raise SubtemplateNotFound, "Template #{path} doesn't exist in #{full_path}"
|
108
|
+
end
|
109
|
+
|
110
|
+
# partial "products/list"
|
111
|
+
# @since 0.0.2
|
112
|
+
# @version 0.2.1
|
113
|
+
def partial(template, extra_context = Hash.new)
|
114
|
+
# NOTE: we can't use File.split because it normalize the path,
|
115
|
+
# so "./base.html" will be the same as "base.html", but it shouldn't be
|
116
|
+
*path, basename = template.split("/")
|
117
|
+
render File.join(path.join("/"), "_#{basename}"), self.template.context.merge(extra_context)
|
118
|
+
end
|
119
|
+
|
120
|
+
# @since 0.2
|
121
|
+
def includes(template, context = Hash.new)
|
122
|
+
render normalize_template_path(template), context
|
123
|
+
return true
|
124
|
+
end
|
125
|
+
|
126
|
+
# extends "base.html"
|
127
|
+
# @since 0.0.2
|
128
|
+
def extends(path)
|
129
|
+
# we can't just create a new template, because it has to do it after it reads the whole file
|
130
|
+
self.template.supertemplate = normalize_template_path(path)
|
131
|
+
end
|
132
|
+
|
133
|
+
# @since 0.2
|
134
|
+
def normalize_template_path(template)
|
135
|
+
if template.start_with?("./")
|
136
|
+
File.expand_path(File.join(File.dirname(self.template.fullpath), template))
|
137
|
+
elsif template.start_with?("../")
|
138
|
+
File.expand_path(File.join(File.dirname(self.template.fullpath), "..", template))
|
139
|
+
else
|
140
|
+
template
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
data/spec/spec.opts
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
SPEC_ROOT = File.dirname(__FILE__)
|
4
|
+
STUBS_ROOT = File.join(SPEC_ROOT, "stubs")
|
5
|
+
|
6
|
+
$:.unshift(File.join(SPEC_ROOT, "..", "lib"))
|
7
|
+
|
8
|
+
begin
|
9
|
+
# Require the preresolved locked set of gems.
|
10
|
+
require File.expand_path("../.bundle/environment", __FILE__)
|
11
|
+
rescue LoadError
|
12
|
+
# Fallback on doing the resolve at runtime.
|
13
|
+
require "rubygems"
|
14
|
+
require "bundler"
|
15
|
+
Bundler.setup
|
16
|
+
end
|
17
|
+
|
18
|
+
require "spec" # so you can run ruby spec/rango/whatever_spec.rb
|
19
|
+
|
20
|
+
require "rango"
|
21
|
+
require "logger"
|
22
|
+
Rango.logger = Logger.new("/dev/null")
|
23
|
+
|
24
|
+
class RecursiveOpenStruct < OpenStruct
|
25
|
+
def initialize(attributes = Hash.new)
|
26
|
+
attributes.each do |key, value|
|
27
|
+
if value.is_a?(Hash)
|
28
|
+
attributes[key] = OpenStruct.new(value)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
super(attributes)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
module Spec
|
36
|
+
module Matchers
|
37
|
+
def match(expected)
|
38
|
+
Matcher.new(:match, expected) do |expected|
|
39
|
+
match do |actual|
|
40
|
+
actual.match(expected)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
Spec::Runner.configure do |config|
|
48
|
+
config.before(:all) do
|
49
|
+
Rango.environment = "test"
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
= block(:one, "a value")
|
@@ -0,0 +1 @@
|
|
1
|
+
= self.object_id
|
@@ -0,0 +1 @@
|
|
1
|
+
- enhance_block(nil)
|
@@ -0,0 +1 @@
|
|
1
|
+
= extend_block(:content, "foo")
|
@@ -0,0 +1 @@
|
|
1
|
+
- extend_block(nil)
|
@@ -0,0 +1 @@
|
|
1
|
+
== #{block(:title)} by #{block(:author)}
|
@@ -0,0 +1 @@
|
|
1
|
+
= includes "library.html"
|
@@ -0,0 +1 @@
|
|
1
|
+
= title
|
@@ -0,0 +1 @@
|
|
1
|
+
%html
|
@@ -0,0 +1 @@
|
|
1
|
+
= title
|
File without changes
|
File without changes
|
@@ -0,0 +1,139 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require_relative "../../spec_helper"
|
4
|
+
require "rango/templates/helpers"
|
5
|
+
|
6
|
+
Rango::Template.template_paths.clear.push(File.join(STUBS_ROOT, "templates"))
|
7
|
+
|
8
|
+
describe Rango::TemplateHelpers do
|
9
|
+
include Rango::TemplateHelpers
|
10
|
+
describe "#partial" do
|
11
|
+
it "should work" do
|
12
|
+
pending "This can't work because self.template doesn't exist, we have to use render mixin"
|
13
|
+
partial "basic.html"
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should share context with the parent template" do
|
17
|
+
pending
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should be able to specify additional context which isn't propagated to the parent template" do
|
21
|
+
pending
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "#render" do
|
26
|
+
it "should consider 'path.html' as a path relative to Template.template_paths" do
|
27
|
+
pending
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should consider './path.html' as a path relative to the current template" do
|
31
|
+
pending
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should look for '../path.html' in the parent directory of directory with current template" do
|
35
|
+
pending
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe "#includes" do
|
40
|
+
require "rango/mixins/render"
|
41
|
+
it "should return true" do
|
42
|
+
Rango::RenderMixin.render("includes/basic.html").strip.should eql("true")
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should work with blocks" do
|
46
|
+
Rango::RenderMixin.render("includes/includes.html")
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should work with extends" do
|
50
|
+
output = Rango::RenderMixin.render("includes/integration.html")
|
51
|
+
output.strip.should eql("Greeting by Jakub Stastny")
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should work with extends" do
|
55
|
+
output = Rango::RenderMixin.render("includes/integration2.html")
|
56
|
+
output.strip.should eql("Greeting by Jakub Stastny")
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe "extends" do
|
61
|
+
end
|
62
|
+
|
63
|
+
describe "block" do
|
64
|
+
it "should raise argument error if name isn't specified" do
|
65
|
+
-> { block(nil) }.should raise_error(ArgumentError)
|
66
|
+
end
|
67
|
+
|
68
|
+
it "should work as a setter if a value is provided" do
|
69
|
+
output = Rango::RenderMixin.render("block/value.html")
|
70
|
+
output.strip.should eql("a value")
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should work as a setter if a block is provided" do
|
74
|
+
output = Rango::RenderMixin.render("block/block.html")
|
75
|
+
output.strip.should eql("a block")
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should work as a getter" do
|
79
|
+
output = Rango::RenderMixin.render("block/getter.html")
|
80
|
+
output.strip.should eql("Hello World!")
|
81
|
+
end
|
82
|
+
|
83
|
+
it "should raise argument error if both value and block is provided" do
|
84
|
+
-> { Rango::RenderMixin.render("block/error.html") }.should raise_error(ArgumentError)
|
85
|
+
end
|
86
|
+
|
87
|
+
it "should store the first non-nil value" do
|
88
|
+
output = Rango::RenderMixin.render("block/blocks.html")
|
89
|
+
output.strip.should eql("first")
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
describe "extend_block" do
|
94
|
+
it "should raise argument error if name isn't specified" do
|
95
|
+
-> { Rango::RenderMixin.render("extend_block/name_error.html") }.should raise_error(NameError)
|
96
|
+
end
|
97
|
+
|
98
|
+
it "should raise argument error if both value and block is provided" do
|
99
|
+
-> { Rango::RenderMixin.render("extend_block/error.html") }.should raise_error(ArgumentError)
|
100
|
+
end
|
101
|
+
|
102
|
+
it "should raise argument error if block of given name doesn't exist so far" do
|
103
|
+
-> { Rango::RenderMixin.render("extend_block/error2.html") }.should raise_error
|
104
|
+
end
|
105
|
+
|
106
|
+
it "should work with super()-like inheritance" do
|
107
|
+
Rango::RenderMixin.render("extend_block/basic.html")
|
108
|
+
end
|
109
|
+
|
110
|
+
it "should do nothing if the value or block is nil" do
|
111
|
+
output = Rango::RenderMixin.render("extend_block/nil.html")
|
112
|
+
output.strip.should eql("Original")
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
describe "enhance_block" do
|
117
|
+
it "should work even if the block isn't defined so far" do
|
118
|
+
output = Rango::RenderMixin.render("enhance_block/standalone.html")
|
119
|
+
output.strip.should eql("Hello World!")
|
120
|
+
end
|
121
|
+
|
122
|
+
it "should not raise argument error if name isn't specified" do
|
123
|
+
-> { Rango::RenderMixin.render("enhance_block/name_error.html") }.should_not raise_error(NameError)
|
124
|
+
end
|
125
|
+
|
126
|
+
it "should raise argument error if both value and block is provided" do
|
127
|
+
-> { Rango::RenderMixin.render("enhance_block/error.html") }.should raise_error(ArgumentError)
|
128
|
+
end
|
129
|
+
|
130
|
+
it "should work with super()-like inheritance" do
|
131
|
+
Rango::RenderMixin.render("enhance_block/basic.html")
|
132
|
+
end
|
133
|
+
|
134
|
+
it "should do nothing if the value or block is nil" do
|
135
|
+
output = Rango::RenderMixin.render("enhance_block/nil.html")
|
136
|
+
output.strip.should eql("Original")
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require_relative "../../spec_helper"
|
4
|
+
require "rango/templates/template"
|
5
|
+
|
6
|
+
Rango::Template.template_paths.clear.push(File.join(STUBS_ROOT, "templates"))
|
7
|
+
|
8
|
+
describe Rango::Template do
|
9
|
+
describe "#initialize" do
|
10
|
+
it "should take path as a first argument" do
|
11
|
+
template = Rango::Template.new("test.html")
|
12
|
+
template.path.should eql("test.html")
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should take scope as an optional second argument" do
|
16
|
+
scope = Object.new
|
17
|
+
template = Rango::Template.new("test.html", scope)
|
18
|
+
template.scope.should eql(scope)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "#fullpath" do
|
23
|
+
it "should find" do
|
24
|
+
template = Rango::Template.new("test.html")
|
25
|
+
fullpath = File.join(STUBS_ROOT, "templates", "test.html.haml")
|
26
|
+
template.fullpath.should eql(fullpath)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "#render" do
|
31
|
+
it "should raise TemplateNotFound if template can't be found" do
|
32
|
+
template = Rango::Template.new("idonotexist.html")
|
33
|
+
-> { template.render }.should raise_error(Rango::Exceptions::TemplateNotFound)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should render" do
|
37
|
+
template = Rango::Template.new("test.html")
|
38
|
+
template.render.should eql("<html></html>\n")
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should have template inheritance" do
|
42
|
+
template = Rango::Template.new("inheritance/basic/index.html")
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should capture haml" do
|
46
|
+
template = Rango::Template.new("inheritance/capture/haml/index.html")
|
47
|
+
template.render
|
48
|
+
template.blocks[:content].should match("Hello!")
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe "variables" do
|
53
|
+
before(:each) do
|
54
|
+
@template = Rango::Template.new("variables.html")
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should capture erb" do
|
58
|
+
# @template.render(title: "Hi!").should match("Hi!")
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
#!/usr/bin/env gem build
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
require "base64"
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "template-inheritance"
|
8
|
+
s.version = "0.1"
|
9
|
+
s.authors = ["Jakub Šťastný aka Botanicus"]
|
10
|
+
s.homepage = "http://github.com/botanicus/template-inheritance"
|
11
|
+
s.summary = ""
|
12
|
+
s.description = "" # TODO: long description
|
13
|
+
s.cert_chain = nil
|
14
|
+
s.email = Base64.decode64("c3Rhc3RueUAxMDFpZGVhcy5jeg==\n")
|
15
|
+
s.has_rdoc = true
|
16
|
+
|
17
|
+
# files
|
18
|
+
s.files = `git ls-files`.split("\n")
|
19
|
+
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
|
22
|
+
# Ruby version
|
23
|
+
# Current JRuby with --1.9 switch has RUBY_VERSION set to "1.9.2dev"
|
24
|
+
# and RubyGems don't play well with it, so we have to set minimal
|
25
|
+
# Ruby version to 1.9, even if it actually is 1.9.1
|
26
|
+
s.required_ruby_version = ::Gem::Requirement.new("~> 1.9")
|
27
|
+
|
28
|
+
# dependencies
|
29
|
+
s.add_dependency "tilt"
|
30
|
+
s.add_dependency "haml"
|
31
|
+
|
32
|
+
# begin
|
33
|
+
# require "changelog"
|
34
|
+
# rescue LoadError
|
35
|
+
# warn "You have to have changelog gem installed for post install message"
|
36
|
+
# else
|
37
|
+
# s.post_install_message = CHANGELOG.new.version_changes
|
38
|
+
# end
|
39
|
+
|
40
|
+
# RubyForge
|
41
|
+
s.rubyforge_project = "template-inheritance"
|
42
|
+
end
|
metadata
CHANGED
@@ -4,28 +4,22 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
-
- 0
|
8
|
-
- 0
|
9
|
-
- 0
|
10
|
-
- 0
|
11
|
-
- 0
|
12
|
-
- 0
|
13
|
-
- 0
|
14
7
|
- 1
|
15
|
-
version: 0.
|
8
|
+
version: "0.1"
|
16
9
|
platform: ruby
|
17
10
|
authors:
|
18
11
|
- "Jakub \xC5\xA0\xC5\xA5astn\xC3\xBD aka Botanicus"
|
19
12
|
autorequire:
|
20
13
|
bindir: bin
|
21
14
|
cert_chain:
|
22
|
-
date: 2010-
|
15
|
+
date: 2010-10-02 00:00:00 +01:00
|
23
16
|
default_executable:
|
24
17
|
dependencies:
|
25
18
|
- !ruby/object:Gem::Dependency
|
26
19
|
name: tilt
|
27
20
|
prerelease: false
|
28
21
|
requirement: &id001 !ruby/object:Gem::Requirement
|
22
|
+
none: false
|
29
23
|
requirements:
|
30
24
|
- - ">="
|
31
25
|
- !ruby/object:Gem::Version
|
@@ -34,6 +28,19 @@ dependencies:
|
|
34
28
|
version: "0"
|
35
29
|
type: :runtime
|
36
30
|
version_requirements: *id001
|
31
|
+
- !ruby/object:Gem::Dependency
|
32
|
+
name: haml
|
33
|
+
prerelease: false
|
34
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
35
|
+
none: false
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
segments:
|
40
|
+
- 0
|
41
|
+
version: "0"
|
42
|
+
type: :runtime
|
43
|
+
version_requirements: *id002
|
37
44
|
description: ""
|
38
45
|
email: stastny@101ideas.cz
|
39
46
|
executables: []
|
@@ -43,7 +50,54 @@ extensions: []
|
|
43
50
|
extra_rdoc_files: []
|
44
51
|
|
45
52
|
files:
|
53
|
+
- .gitignore
|
46
54
|
- README.textile
|
55
|
+
- example/base.html.haml
|
56
|
+
- example/example.rb
|
57
|
+
- example/shared/google_analytics.html.haml
|
58
|
+
- example/site/_wtf.html.haml
|
59
|
+
- example/site/base.html.haml
|
60
|
+
- example/site/post.html.haml
|
61
|
+
- lib/template-inheritance.rb
|
62
|
+
- lib/template-inheritance/exts/haml.rb
|
63
|
+
- lib/template-inheritance/exts/tilt.rb
|
64
|
+
- lib/template-inheritance/helpers.rb
|
65
|
+
- spec/spec.opts
|
66
|
+
- spec/spec_helper.rb
|
67
|
+
- spec/stubs/block/block.html.haml
|
68
|
+
- spec/stubs/block/blocks.html.haml
|
69
|
+
- spec/stubs/block/error.html.haml
|
70
|
+
- spec/stubs/block/getter.html.haml
|
71
|
+
- spec/stubs/block/value.html.haml
|
72
|
+
- spec/stubs/context_id.html.haml
|
73
|
+
- spec/stubs/enhance_block/basic.html.haml
|
74
|
+
- spec/stubs/enhance_block/error.html.haml
|
75
|
+
- spec/stubs/enhance_block/name_error.html.haml
|
76
|
+
- spec/stubs/enhance_block/nil.html.haml
|
77
|
+
- spec/stubs/enhance_block/standalone.html.haml
|
78
|
+
- spec/stubs/extend_block/basic.html.haml
|
79
|
+
- spec/stubs/extend_block/error.html.haml
|
80
|
+
- spec/stubs/extend_block/error2.html.haml
|
81
|
+
- spec/stubs/extend_block/name_error.html.haml
|
82
|
+
- spec/stubs/extend_block/nil.html.haml
|
83
|
+
- spec/stubs/includes/base.html.haml
|
84
|
+
- spec/stubs/includes/basic.html.haml
|
85
|
+
- spec/stubs/includes/includes.html.haml
|
86
|
+
- spec/stubs/includes/integration.html.haml
|
87
|
+
- spec/stubs/includes/integration2.html.haml
|
88
|
+
- spec/stubs/index.html.haml
|
89
|
+
- spec/stubs/inheritance/basic/base.html.haml
|
90
|
+
- spec/stubs/inheritance/basic/index.html.haml
|
91
|
+
- spec/stubs/inheritance/capture/haml/base.html.haml
|
92
|
+
- spec/stubs/inheritance/capture/haml/index.html.haml
|
93
|
+
- spec/stubs/library.html.haml
|
94
|
+
- spec/stubs/test.html.haml
|
95
|
+
- spec/stubs/variables.html.haml
|
96
|
+
- spec/templates/exts/haml_spec.rb
|
97
|
+
- spec/templates/exts/tilt_spec.rb
|
98
|
+
- spec/templates/helpers_spec.rb
|
99
|
+
- spec/templates/template_spec.rb
|
100
|
+
- template-inheritance.gemspec
|
47
101
|
has_rdoc: true
|
48
102
|
homepage: http://github.com/botanicus/template-inheritance
|
49
103
|
licenses: []
|
@@ -54,6 +108,7 @@ rdoc_options: []
|
|
54
108
|
require_paths:
|
55
109
|
- lib
|
56
110
|
required_ruby_version: !ruby/object:Gem::Requirement
|
111
|
+
none: false
|
57
112
|
requirements:
|
58
113
|
- - ~>
|
59
114
|
- !ruby/object:Gem::Version
|
@@ -62,6 +117,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
62
117
|
- 9
|
63
118
|
version: "1.9"
|
64
119
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
120
|
+
none: false
|
65
121
|
requirements:
|
66
122
|
- - ">="
|
67
123
|
- !ruby/object:Gem::Version
|
@@ -71,7 +127,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
71
127
|
requirements: []
|
72
128
|
|
73
129
|
rubyforge_project: template-inheritance
|
74
|
-
rubygems_version: 1.3.
|
130
|
+
rubygems_version: 1.3.7
|
75
131
|
signing_key:
|
76
132
|
specification_version: 3
|
77
133
|
summary: ""
|