armadillo 0.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.
- 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: []
|