template-inheritance 0.0.0.0.0.0.0.0.1 → 0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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: ""
|