armadillo 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +73 -0
- data/Rakefile +7 -0
- data/armadillo.gemspec +24 -0
- data/lib/armadillo.rb +171 -0
- data/spec/spec_helper.rb +3 -0
- data/spec/template_spec.rb +81 -0
- metadata +65 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 076e70fe3bf56b1ff58d780add8495daec39c332
|
4
|
+
data.tar.gz: 3a0c4f3c77fa0d215c622647d7ce99506a9d2228
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 6abcaa901422e65f838fafe28ebd2c39c29f65d089157fa5b7bb38df037581fe8f9c294fa0456dc501aca27b418cbf7364dcaeec3e97f31e470ef824f6e00267
|
7
|
+
data.tar.gz: 058695378b0a9f846a1081f929b57ad832b267b1bc3eec8f79f3740d175d90ff74a58f378074c6d2e8d9d4c8b8b9afbaee6fb37221711c3fdbdc71cbb95c343d
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2014 Sebastian Borrazas
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
Armadillo
|
2
|
+
=========
|
3
|
+
|
4
|
+
A small library for [Django-like template inheritance](https://docs.djangoproject.com/en/dev/topics/templates/#template-inheritance)
|
5
|
+
adapted for ERB.
|
6
|
+
|
7
|
+
Usage
|
8
|
+
-----
|
9
|
+
|
10
|
+
To render an Armadillo template you need to call the `Armadillo.render` method.
|
11
|
+
|
12
|
+
This method accepts any of the following options:
|
13
|
+
* `:scope` - Any object you want to bound to the template scope.
|
14
|
+
* `:base_path` - The path of the directory for which the templates are going to
|
15
|
+
be searched on.
|
16
|
+
|
17
|
+
Note: A `.erb` extension is assumed for every file and should not be part of
|
18
|
+
the filename given as the template filename.
|
19
|
+
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
Armadillo.render("myview.html", { :items => [1, 2, 3] }, {
|
23
|
+
:base_path => File.join(Dir.pwd, "views"),
|
24
|
+
:scope => self
|
25
|
+
})
|
26
|
+
```
|
27
|
+
|
28
|
+
```erb
|
29
|
+
<!-- views/myview.html.erb -->
|
30
|
+
<% extends("base.html") %>
|
31
|
+
|
32
|
+
<% vlock(:title) do %>
|
33
|
+
<%= current_user.name %>
|
34
|
+
<% end %>
|
35
|
+
|
36
|
+
<% vlock(:body) do %>
|
37
|
+
<ul>
|
38
|
+
<% items.each do |item| %>
|
39
|
+
<li><%= item %></li>
|
40
|
+
<% end %>
|
41
|
+
</ul>
|
42
|
+
<% end %>
|
43
|
+
|
44
|
+
<!-- views/base.html.erb -->
|
45
|
+
<!DOCTYPE>
|
46
|
+
<html>
|
47
|
+
<title><% vlock(:title) %> - MyApp</title>
|
48
|
+
<body>
|
49
|
+
<% vlock(:body) %>
|
50
|
+
</body>
|
51
|
+
</html>
|
52
|
+
```
|
53
|
+
|
54
|
+
### Usage example using Cuba
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
module View
|
58
|
+
def render_view(template_name, locals = {})
|
59
|
+
content = Armadillo.render(template_name, locals, {
|
60
|
+
:base_path => File.join(APP_PATH, "views"),
|
61
|
+
:scope => self
|
62
|
+
})
|
63
|
+
res.write(content)
|
64
|
+
halt(res.finish)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
on get, root do
|
69
|
+
render_view("main/index.html", {
|
70
|
+
:items => [1, 2, 3]
|
71
|
+
})
|
72
|
+
end
|
73
|
+
```
|
data/Rakefile
ADDED
data/armadillo.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = "armadillo"
|
3
|
+
s.version = "0.0.1"
|
4
|
+
s.summary = "Template inheritance with ERB templates"
|
5
|
+
s.description = "A small library for Django-like template inheritance adapted for ERB"
|
6
|
+
s.authors = ["Sebastian Borrazas"]
|
7
|
+
s.email = ["seba.borrazas@gmail.com"]
|
8
|
+
s.homepage = "http://github.com/sborrazas/armadillo"
|
9
|
+
s.license = "MIT"
|
10
|
+
|
11
|
+
s.files = Dir[
|
12
|
+
"LICENSE",
|
13
|
+
"README.md",
|
14
|
+
"Rakefile",
|
15
|
+
"lib/**/*.rb",
|
16
|
+
"*.gemspec",
|
17
|
+
"spec/*.*"
|
18
|
+
]
|
19
|
+
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
|
22
|
+
s.add_dependency "tilt"
|
23
|
+
|
24
|
+
end
|
data/lib/armadillo.rb
ADDED
@@ -0,0 +1,171 @@
|
|
1
|
+
require "tilt"
|
2
|
+
require "delegate"
|
3
|
+
|
4
|
+
module Armadillo
|
5
|
+
|
6
|
+
VERSION = "0.0.1"
|
7
|
+
|
8
|
+
DEFAULT_OPTIONS = {
|
9
|
+
:default_encoding => Encoding.default_external,
|
10
|
+
:outvar => "@_output"
|
11
|
+
}
|
12
|
+
|
13
|
+
# @api private
|
14
|
+
class TemplateContext < SimpleDelegator
|
15
|
+
|
16
|
+
# Extend the specified template in which the inner view blocks will be
|
17
|
+
# rendered.
|
18
|
+
#
|
19
|
+
# @note
|
20
|
+
# This is a template instruction.
|
21
|
+
#
|
22
|
+
# @param template_path [String]
|
23
|
+
# @param locals [Hash]
|
24
|
+
def extends(template_path, locals = {})
|
25
|
+
@extends_data = [template_path, locals]
|
26
|
+
end
|
27
|
+
|
28
|
+
# Determine the contents or specify the place in which the view block will
|
29
|
+
# be rendered.
|
30
|
+
#
|
31
|
+
# @note
|
32
|
+
# This is a template instruction.
|
33
|
+
#
|
34
|
+
# @param block_name [Symbol]
|
35
|
+
# @param block [Block]
|
36
|
+
def vlock(block_name, &block)
|
37
|
+
raise "Invalid vlock usage" unless current_frame
|
38
|
+
|
39
|
+
if extends?
|
40
|
+
raise "No block given" unless block_given?
|
41
|
+
|
42
|
+
current_frame[:vlocks][block_name] = block
|
43
|
+
elsif (frame = get_frame(block_name, current_frame))
|
44
|
+
temporary_frame(frame[:parent_frame]) do
|
45
|
+
frame[:vlocks][block_name].call
|
46
|
+
end
|
47
|
+
elsif block_given?
|
48
|
+
block.call
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Create a new frame with previous frame as parent.
|
53
|
+
def create_frame
|
54
|
+
@current_frame = {
|
55
|
+
:vlocks => {},
|
56
|
+
:parent_frame => current_frame
|
57
|
+
}
|
58
|
+
end
|
59
|
+
|
60
|
+
# Determine if the current template should extend from a new template.
|
61
|
+
#
|
62
|
+
# @return [Boolean]
|
63
|
+
def extends?
|
64
|
+
!! @extends_data
|
65
|
+
end
|
66
|
+
|
67
|
+
# Return and delete the extract data.
|
68
|
+
#
|
69
|
+
# @return [Array<(String, Hash)>]
|
70
|
+
# The extended template name and the locals.
|
71
|
+
def extract_extends_data
|
72
|
+
@extends_data.tap { @extends_data = nil }
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
# Get the current frame. Each frame contains the blocks specified using
|
78
|
+
# #vlock and its parent frame.
|
79
|
+
#
|
80
|
+
# @return [Hash]
|
81
|
+
def current_frame
|
82
|
+
@current_frame
|
83
|
+
end
|
84
|
+
|
85
|
+
# Create a temporary current frame for the block to be executed.
|
86
|
+
#
|
87
|
+
# @param frame [Hash]
|
88
|
+
# @param block [Block]
|
89
|
+
def temporary_frame(frame, &block)
|
90
|
+
old = current_frame
|
91
|
+
@current_frame = frame
|
92
|
+
block.call
|
93
|
+
@current_frame = old
|
94
|
+
end
|
95
|
+
|
96
|
+
# Get the block from the frames stack by its name.
|
97
|
+
#
|
98
|
+
# @param block_name [Symbol]
|
99
|
+
def get_frame(block_name, frame)
|
100
|
+
if frame[:vlocks].has_key?(block_name)
|
101
|
+
frame
|
102
|
+
elsif frame[:parent_frame]
|
103
|
+
get_frame(block_name, frame[:parent_frame])
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# Render the erb template.
|
109
|
+
#
|
110
|
+
# @param template_path [String]
|
111
|
+
# @param locals [Hash]
|
112
|
+
# @option options [Object] :scope (Object.new)
|
113
|
+
# Any object you want to bound to the template scope.
|
114
|
+
# @option options [String, nil] :base_path (nil)
|
115
|
+
# The path of the directory for which the templates are going to be
|
116
|
+
# searched on.
|
117
|
+
#
|
118
|
+
# @note
|
119
|
+
# options also accepts any options offered by the Erubis templating system.
|
120
|
+
#
|
121
|
+
# @return [String]
|
122
|
+
# @api public
|
123
|
+
def self.render(template_path, locals = {}, options = {})
|
124
|
+
scope = options.fetch(:scope) { Object.new }
|
125
|
+
context = TemplateContext.new(scope)
|
126
|
+
_render(template_path, locals, context, options)
|
127
|
+
end
|
128
|
+
|
129
|
+
# Render the erb template with the given context.
|
130
|
+
#
|
131
|
+
# @param template_path [String]
|
132
|
+
# @param context [Armadillo::TemplateContext]
|
133
|
+
# @param locals [Hash]
|
134
|
+
# @option options [String] :base_path (nil)
|
135
|
+
#
|
136
|
+
# @note
|
137
|
+
# options also accepts any options offered by the Erubis templating system.
|
138
|
+
#
|
139
|
+
# @api private
|
140
|
+
def self._render(template_path, locals, context, options)
|
141
|
+
context.create_frame
|
142
|
+
template_path = "#{template_path}.erb"
|
143
|
+
if (base_path = options.fetch(:base_path, nil))
|
144
|
+
template_path = File.join(base_path, template_path)
|
145
|
+
end
|
146
|
+
template = _templates_cache.fetch(template_path) do
|
147
|
+
Tilt.new(template_path, 1, DEFAULT_OPTIONS.merge(options))
|
148
|
+
end
|
149
|
+
|
150
|
+
content = template.render(context, locals)
|
151
|
+
|
152
|
+
if context.extends?
|
153
|
+
template_path, locals = context.extract_extends_data
|
154
|
+
content = _render(template_path, locals, context, options)
|
155
|
+
end
|
156
|
+
|
157
|
+
content
|
158
|
+
end
|
159
|
+
private_class_method :_render
|
160
|
+
|
161
|
+
# Get Tilt templates cache.
|
162
|
+
#
|
163
|
+
# @return [Tilt::Cache]
|
164
|
+
#
|
165
|
+
# @api private
|
166
|
+
def self._templates_cache
|
167
|
+
Thread.current[:tilt_cache] ||= Tilt::Cache.new
|
168
|
+
end
|
169
|
+
private_class_method :_templates_cache
|
170
|
+
|
171
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
require_relative "spec_helper"
|
2
|
+
|
3
|
+
describe Armadillo do
|
4
|
+
|
5
|
+
TEMPLATES_PATH = File.join(File.dirname(__FILE__), "templates")
|
6
|
+
|
7
|
+
def assert_lines_match(content, lines)
|
8
|
+
content_lines = content.split("\n")
|
9
|
+
lines.each do |line|
|
10
|
+
assert_includes(content_lines, line)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe ".render" do
|
15
|
+
it "renders a regular erb template" do
|
16
|
+
locals = { :items => ["a", "b", "c"] }
|
17
|
+
content = Armadillo.render("basic.text", locals, {
|
18
|
+
:base_path => TEMPLATES_PATH
|
19
|
+
})
|
20
|
+
assert_lines_match(content, ["Basic", "a", "b", "c"])
|
21
|
+
end
|
22
|
+
|
23
|
+
it "renders a one-step inheritance template" do
|
24
|
+
content = Armadillo.render("one_step_1.text", {}, {
|
25
|
+
:base_path => TEMPLATES_PATH
|
26
|
+
})
|
27
|
+
assert_lines_match(content, ["Base", "Title", "Subtitle"])
|
28
|
+
end
|
29
|
+
|
30
|
+
it "allows parent templates to access locals from #extends" do
|
31
|
+
locals = { :items => ["a", "b", "c"] }
|
32
|
+
|
33
|
+
content = Armadillo.render("parent_locals_2.text", locals, {
|
34
|
+
:base_path => TEMPLATES_PATH
|
35
|
+
})
|
36
|
+
assert_lines_match(content, ["Base", locals[:items].first])
|
37
|
+
end
|
38
|
+
|
39
|
+
it "renders a two-step inheritance template" do
|
40
|
+
content = Armadillo.render("two_step_2.text", {}, {
|
41
|
+
:base_path => TEMPLATES_PATH
|
42
|
+
})
|
43
|
+
assert_lines_match(content, ["Base", "Title", "Subtitle"])
|
44
|
+
end
|
45
|
+
|
46
|
+
describe "when reusing child vlocks" do
|
47
|
+
it "renders them according to the inheritance" do
|
48
|
+
content = Armadillo.render("nested_two_step_2.text", {}, {
|
49
|
+
:base_path => TEMPLATES_PATH
|
50
|
+
})
|
51
|
+
assert_lines_match(content, ["Base", "Title - Subtitle"])
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe "when sending a scope object" do
|
56
|
+
it "access the object methods as locals" do
|
57
|
+
obj = Object.new
|
58
|
+
def obj.some_text
|
59
|
+
"text!"
|
60
|
+
end
|
61
|
+
|
62
|
+
content = Armadillo.render("scope_object.text", {}, {
|
63
|
+
:base_path => TEMPLATES_PATH,
|
64
|
+
:scope => obj
|
65
|
+
})
|
66
|
+
assert_lines_match(content, ["Base", obj.some_text])
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe "when sending :escape_html option" do
|
71
|
+
it "sanitizes the HTML by default" do
|
72
|
+
content = Armadillo.render("sanitized.html", {}, {
|
73
|
+
:base_path => TEMPLATES_PATH,
|
74
|
+
:escape_html => true
|
75
|
+
})
|
76
|
+
assert_lines_match(content, ["Sanitized &", "Not sanitized &"])
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
metadata
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: armadillo
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Sebastian Borrazas
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-10-10 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: tilt
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
description: A small library for Django-like template inheritance adapted for ERB
|
28
|
+
email:
|
29
|
+
- seba.borrazas@gmail.com
|
30
|
+
executables: []
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- LICENSE
|
35
|
+
- README.md
|
36
|
+
- Rakefile
|
37
|
+
- armadillo.gemspec
|
38
|
+
- lib/armadillo.rb
|
39
|
+
- spec/spec_helper.rb
|
40
|
+
- spec/template_spec.rb
|
41
|
+
homepage: http://github.com/sborrazas/armadillo
|
42
|
+
licenses:
|
43
|
+
- MIT
|
44
|
+
metadata: {}
|
45
|
+
post_install_message:
|
46
|
+
rdoc_options: []
|
47
|
+
require_paths:
|
48
|
+
- lib
|
49
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
55
|
+
requirements:
|
56
|
+
- - ">="
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
version: '0'
|
59
|
+
requirements: []
|
60
|
+
rubyforge_project:
|
61
|
+
rubygems_version: 2.2.0
|
62
|
+
signing_key:
|
63
|
+
specification_version: 4
|
64
|
+
summary: Template inheritance with ERB templates
|
65
|
+
test_files: []
|