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.
Files changed (49) hide show
  1. data/.gitignore +9 -0
  2. data/README.textile +18 -1
  3. data/example/base.html.haml +31 -0
  4. data/example/example.rb +16 -0
  5. data/example/shared/google_analytics.html.haml +8 -0
  6. data/example/site/_wtf.html.haml +3 -0
  7. data/example/site/base.html.haml +38 -0
  8. data/example/site/post.html.haml +17 -0
  9. data/lib/template-inheritance.rb +118 -0
  10. data/lib/template-inheritance/exts/haml.rb +33 -0
  11. data/lib/template-inheritance/exts/tilt.rb +29 -0
  12. data/lib/template-inheritance/helpers.rb +144 -0
  13. data/spec/spec.opts +5 -0
  14. data/spec/spec_helper.rb +51 -0
  15. data/spec/stubs/block/block.html.haml +2 -0
  16. data/spec/stubs/block/blocks.html.haml +5 -0
  17. data/spec/stubs/block/error.html.haml +2 -0
  18. data/spec/stubs/block/getter.html.haml +3 -0
  19. data/spec/stubs/block/value.html.haml +1 -0
  20. data/spec/stubs/context_id.html.haml +1 -0
  21. data/spec/stubs/enhance_block/basic.html.haml +5 -0
  22. data/spec/stubs/enhance_block/error.html.haml +3 -0
  23. data/spec/stubs/enhance_block/name_error.html.haml +1 -0
  24. data/spec/stubs/enhance_block/nil.html.haml +3 -0
  25. data/spec/stubs/enhance_block/standalone.html.haml +2 -0
  26. data/spec/stubs/extend_block/basic.html.haml +5 -0
  27. data/spec/stubs/extend_block/error.html.haml +5 -0
  28. data/spec/stubs/extend_block/error2.html.haml +1 -0
  29. data/spec/stubs/extend_block/name_error.html.haml +1 -0
  30. data/spec/stubs/extend_block/nil.html.haml +4 -0
  31. data/spec/stubs/includes/base.html.haml +1 -0
  32. data/spec/stubs/includes/basic.html.haml +1 -0
  33. data/spec/stubs/includes/includes.html.haml +7 -0
  34. data/spec/stubs/includes/integration.html.haml +4 -0
  35. data/spec/stubs/includes/integration2.html.haml +4 -0
  36. data/spec/stubs/index.html.haml +1 -0
  37. data/spec/stubs/inheritance/basic/base.html.haml +2 -0
  38. data/spec/stubs/inheritance/basic/index.html.haml +2 -0
  39. data/spec/stubs/inheritance/capture/haml/base.html.haml +2 -0
  40. data/spec/stubs/inheritance/capture/haml/index.html.haml +3 -0
  41. data/spec/stubs/library.html.haml +3 -0
  42. data/spec/stubs/test.html.haml +1 -0
  43. data/spec/stubs/variables.html.haml +1 -0
  44. data/spec/templates/exts/haml_spec.rb +0 -0
  45. data/spec/templates/exts/tilt_spec.rb +0 -0
  46. data/spec/templates/helpers_spec.rb +139 -0
  47. data/spec/templates/template_spec.rb +61 -0
  48. data/template-inheritance.gemspec +42 -0
  49. metadata +66 -10
@@ -0,0 +1,9 @@
1
+ /*.gem
2
+
3
+ .DS_Store
4
+ .cache
5
+ .yardoc
6
+
7
+ script/*
8
+ .bundle
9
+ .rvmrc
@@ -1,3 +1,20 @@
1
1
  h1. About
2
2
 
3
- Currently this is just a placeholder, but in the future you can expect here a framework-agnostic template inheritance implementation from Rango.
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 &lt;&lt; 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.
@@ -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,8 @@
1
+ / This about this template as about a library, it
2
+ / provides some blocks (API) and it changes others.
3
+ - block(:scripts).unshift("ga.js")
4
+
5
+ - block(:google_analytics) do
6
+ / Google Analytics
7
+ %script
8
+ // Some clever JS magic.
@@ -0,0 +1,3 @@
1
+ %p
2
+ Tell me why on Earth should I use partials now? But if you want them, here you are!
3
+ == Now let's test the local variables for partials, do they work? : #{local}
@@ -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
@@ -0,0 +1,5 @@
1
+ --colour
2
+ --format
3
+ progress
4
+ --loadby
5
+ mtime
@@ -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,2 @@
1
+ = block(:second) do
2
+ a block
@@ -0,0 +1,5 @@
1
+ - block(:head, nil)
2
+ - block(:head, "first")
3
+ - block(:head, "second")
4
+
5
+ = block(:head)
@@ -0,0 +1,2 @@
1
+ = block(:content, "value") do
2
+ Content from block
@@ -0,0 +1,3 @@
1
+ - block(:content, "Hello World!")
2
+
3
+ = block(:content)
@@ -0,0 +1 @@
1
+ = block(:one, "a value")
@@ -0,0 +1 @@
1
+ = self.object_id
@@ -0,0 +1,5 @@
1
+ - block(:content) do
2
+ Original
3
+
4
+ = enhance_block(:content) do
5
+ I own the only #{block(:content)}!
@@ -0,0 +1,3 @@
1
+ = enhance_block(:content, "another value") do
2
+ Content from block
3
+
@@ -0,0 +1 @@
1
+ - enhance_block(nil)
@@ -0,0 +1,3 @@
1
+ - block(:content, "Original")
2
+
3
+ = enhance_block(:content, nil)
@@ -0,0 +1,2 @@
1
+ = enhance_block(:content) do
2
+ Hello World!
@@ -0,0 +1,5 @@
1
+ - block(:content) do
2
+ Original
3
+
4
+ = extend_block(:content) do
5
+ I own the only #{block(:content)}!
@@ -0,0 +1,5 @@
1
+ - block(:content, "value")
2
+
3
+ = extend_block(:content, "another value") do
4
+ Content from block
5
+
@@ -0,0 +1 @@
1
+ = extend_block(:content, "foo")
@@ -0,0 +1 @@
1
+ - extend_block(nil)
@@ -0,0 +1,4 @@
1
+ - block(:content) do
2
+ Original
3
+
4
+ = extend_block(:content, nil)
@@ -0,0 +1 @@
1
+ == #{block(:title)} by #{block(:author)}
@@ -0,0 +1 @@
1
+ = includes "library.html"
@@ -0,0 +1,7 @@
1
+ - includes "library.html"
2
+
3
+ %html
4
+ %head
5
+ %title= block(:title)
6
+
7
+ %body= block(:content)
@@ -0,0 +1,4 @@
1
+ - includes "library.html"
2
+ - extends "includes/base.html"
3
+
4
+ = block(:author, "Jakub Stastny")
@@ -0,0 +1,4 @@
1
+ - extends "includes/base.html"
2
+ - includes "library.html"
3
+
4
+ = block(:author, "Jakub Stastny")
@@ -0,0 +1 @@
1
+ = title
@@ -0,0 +1,2 @@
1
+ %html
2
+ %body= block(:content)
@@ -0,0 +1,2 @@
1
+ - extends "inheritance/basic/base.html"
2
+ - block(:content, "Hello!")
@@ -0,0 +1,2 @@
1
+ %html
2
+ %body= block(:content)
@@ -0,0 +1,3 @@
1
+ - extends "inheritance/capture/haml/base.html"
2
+ - block(:content) do
3
+ Hello!
@@ -0,0 +1,3 @@
1
+ - block(:title, "Greeting") # we don't want to show this in the partial
2
+ - block(:content) do
3
+ Hello World!
@@ -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.0.0.0.0.0.0.0.1
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-03-24 00:00:00 +00:00
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.6
130
+ rubygems_version: 1.3.7
75
131
  signing_key:
76
132
  specification_version: 3
77
133
  summary: ""