semplice 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 +19 -0
- data/README.md +182 -0
- data/lib/semplice.rb +152 -0
- data/semplice.gemspec +22 -0
- metadata +63 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 04fd3c1918454d7570629679f3147f0d760c025d
|
4
|
+
data.tar.gz: 879ea0e7f14022d7d93b5af7d592dd62b883887d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: fa9abc4e188c3a12737840e4e383043a535fefa3758a67bdefaafbf206a9b12793c0ffee2a64f256f50de7e9647cdb6a4eb740fbbb67105a440acb9e747dfa59
|
7
|
+
data.tar.gz: 101b04a47ca1025bf01c2b9a24f1973ea210c874d405d5abd9cf64a55835327d4f9d30f855407a9d660d9d776792a904d8d655b43f1857bfe3831ceb819e60a8
|
data/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2015 Matías Aguirre
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,182 @@
|
|
1
|
+
# Semplice
|
2
|
+
|
3
|
+
``Semplice`` is a [mote](https://github.com/soveran/mote) inspired
|
4
|
+
template engine with Django-like syntax and inheritance.
|
5
|
+
|
6
|
+
|
7
|
+
## Usage
|
8
|
+
|
9
|
+
``Semplice`` use is quite strightforward, put your content on a
|
10
|
+
template (or not) and call render.
|
11
|
+
|
12
|
+
```ruby
|
13
|
+
Semplice.render('path/to/template', {
|
14
|
+
foo: 'bar',
|
15
|
+
baz: 'quox'
|
16
|
+
})
|
17
|
+
```
|
18
|
+
|
19
|
+
Or you can render directly from text:
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
Semplice.render_text('hello {{ world }}', {
|
23
|
+
world: 'earthlings'
|
24
|
+
})
|
25
|
+
```
|
26
|
+
|
27
|
+
## Templates
|
28
|
+
|
29
|
+
### Values
|
30
|
+
|
31
|
+
Values passed through context can be ``rendered`` into the content by
|
32
|
+
using ``{{ ... }}`` (automatically escapes HTML) or ``{! ... !}``
|
33
|
+
(doesn't escape the content). For instance, consider ``val`` equals
|
34
|
+
to ``<strong>world</strong>``:
|
35
|
+
|
36
|
+
```
|
37
|
+
Hello {{ val }} -> Hello <strong>world</strong>
|
38
|
+
Hello {! val !} -> Hello <strong>world</strong>
|
39
|
+
```
|
40
|
+
|
41
|
+
There's also the syntax ``{- ... -}`` that will supress the output,
|
42
|
+
this is useful for small computations, usually assignments:
|
43
|
+
|
44
|
+
```
|
45
|
+
{- foo = "bar" -}
|
46
|
+
Hello {{ foo }}
|
47
|
+
```
|
48
|
+
|
49
|
+
|
50
|
+
### Code blocks
|
51
|
+
|
52
|
+
Ruby code can be injected using the ``{% ... %}...{% end %}`` syntax:
|
53
|
+
|
54
|
+
|
55
|
+
Loops:
|
56
|
+
|
57
|
+
```
|
58
|
+
{% val.map do |v| %}
|
59
|
+
...
|
60
|
+
{% end %}
|
61
|
+
```
|
62
|
+
|
63
|
+
```
|
64
|
+
{% while ... %}
|
65
|
+
...
|
66
|
+
{% end %}
|
67
|
+
```
|
68
|
+
|
69
|
+
Conditionals:
|
70
|
+
|
71
|
+
```
|
72
|
+
{% if val == "foo" %}
|
73
|
+
...
|
74
|
+
{% elsif val == "bar" %}
|
75
|
+
...
|
76
|
+
{% else %}
|
77
|
+
...
|
78
|
+
{% end %}
|
79
|
+
```
|
80
|
+
|
81
|
+
```
|
82
|
+
{% case val %}
|
83
|
+
...
|
84
|
+
{% when "foo" %}
|
85
|
+
...
|
86
|
+
{% when "bar" %}
|
87
|
+
...
|
88
|
+
{% else %}
|
89
|
+
...
|
90
|
+
{% end %}
|
91
|
+
```
|
92
|
+
|
93
|
+
Any ruby construct is valid here.
|
94
|
+
|
95
|
+
|
96
|
+
### Comments
|
97
|
+
|
98
|
+
Comments can be added with ``{# ... #}``, they will be ignored from
|
99
|
+
the output.
|
100
|
+
|
101
|
+
|
102
|
+
### Inclusion
|
103
|
+
|
104
|
+
Include other templates using the ``{% include ... %}`` tag:
|
105
|
+
|
106
|
+
```
|
107
|
+
{% include "path/to/other-template" %}
|
108
|
+
```
|
109
|
+
|
110
|
+
|
111
|
+
### Content blocks
|
112
|
+
|
113
|
+
A template can define content blocks that can be overriden later when
|
114
|
+
using inheritance. For example:
|
115
|
+
|
116
|
+
```
|
117
|
+
This content goes before the block.
|
118
|
+
{% block content %}
|
119
|
+
This is the default content of this block.
|
120
|
+
{% end %}
|
121
|
+
This content goes after the block.
|
122
|
+
```
|
123
|
+
|
124
|
+
|
125
|
+
### Inheritance
|
126
|
+
|
127
|
+
Templates can be ``extended`` using the ``{% extends ... %}`` syntax,
|
128
|
+
content blocks can be overriden to provide new content. Take for
|
129
|
+
instance this base template:
|
130
|
+
|
131
|
+
```
|
132
|
+
{# this is foo.html #}
|
133
|
+
<h1>{% block title %}Foo title{% end %}</h1>
|
134
|
+
```
|
135
|
+
|
136
|
+
Then we can extend it:
|
137
|
+
|
138
|
+
```
|
139
|
+
{# this is bar.html #}
|
140
|
+
{% extends "foo.html" %}
|
141
|
+
{% block title %}Bar title{% end %}
|
142
|
+
```
|
143
|
+
|
144
|
+
Rendering ``foo.html`` will output:
|
145
|
+
|
146
|
+
```
|
147
|
+
<h1>Foo title</h1>
|
148
|
+
```
|
149
|
+
|
150
|
+
But rendering ``bar.html``:
|
151
|
+
|
152
|
+
```
|
153
|
+
<h1>Bar title</h1>
|
154
|
+
```
|
155
|
+
|
156
|
+
## Global context
|
157
|
+
|
158
|
+
Any method defined in ``Semplice::GlobalContext`` will be available in
|
159
|
+
the template context at rendering time:
|
160
|
+
|
161
|
+
```ruby
|
162
|
+
module Semplice::GlobalContext
|
163
|
+
def twice(val)
|
164
|
+
val * 2
|
165
|
+
end
|
166
|
+
end
|
167
|
+
```
|
168
|
+
|
169
|
+
Then:
|
170
|
+
|
171
|
+
```
|
172
|
+
10 * 2 = {{ twice(10) }}
|
173
|
+
```
|
174
|
+
|
175
|
+
## Helpers
|
176
|
+
|
177
|
+
You can include the ``Helpers`` module to simplify access to
|
178
|
+
``render`` and ``render_text`` methods.
|
179
|
+
|
180
|
+
```ruby
|
181
|
+
include Semplice::Helpers
|
182
|
+
```
|
data/lib/semplice.rb
ADDED
@@ -0,0 +1,152 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
|
3
|
+
module Semplice
|
4
|
+
VERSION = '0.0.1'
|
5
|
+
TPL_PATTERN = /(\{[%\{\-#!])\s*(.*?)\s*[%\}\-#!]\}/
|
6
|
+
|
7
|
+
@locations = []
|
8
|
+
|
9
|
+
module GlobalContext
|
10
|
+
# Define your methods in this module, they will be included
|
11
|
+
# automatically in the template context
|
12
|
+
end
|
13
|
+
|
14
|
+
class Template
|
15
|
+
include GlobalContext
|
16
|
+
|
17
|
+
def self.render(__params)
|
18
|
+
new.__render(__params)
|
19
|
+
end
|
20
|
+
|
21
|
+
def __render(__params)
|
22
|
+
''
|
23
|
+
end
|
24
|
+
|
25
|
+
def include(path, params)
|
26
|
+
Semplice.render(path, params).chomp
|
27
|
+
end
|
28
|
+
|
29
|
+
def h(val)
|
30
|
+
val = CGI.escape_html(val) if val.is_a?(String)
|
31
|
+
val
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
module TemplateParser
|
36
|
+
@@template_dirs = []
|
37
|
+
|
38
|
+
def self.parse(content, names=[])
|
39
|
+
tpl = parse_content(content.split(TPL_PATTERN))
|
40
|
+
context_vars = names.map{|name| "%s = __params[%p]" % [name, name]}.join(';')
|
41
|
+
render_code = tpl[:methods].map{|name, code|
|
42
|
+
"def __block_#{name}(__params);#{context_vars};__out = [];#{code};__out.join;end"
|
43
|
+
}
|
44
|
+
|
45
|
+
unless tpl[:base]
|
46
|
+
render_code << "def __render(__params)"
|
47
|
+
render_code << context_vars
|
48
|
+
render_code << "__out = [super(__params)]"
|
49
|
+
render_code << tpl[:code].join("\n")
|
50
|
+
render_code << "__out.join.chomp"
|
51
|
+
render_code << "end"
|
52
|
+
end
|
53
|
+
|
54
|
+
cls = Class.new(tpl[:base] ? template(tpl[:base], names) : Semplice::Template)
|
55
|
+
cls.class_eval(render_code.join("\n"))
|
56
|
+
cls
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.template(path, names)
|
60
|
+
cache[path] ||= parse(content_for(path), names)
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def self.content_for(path)
|
66
|
+
unless File.exists?(path)
|
67
|
+
path = @@template_dirs.map{|l|
|
68
|
+
File.join(l, path)
|
69
|
+
}.select{|p|
|
70
|
+
File.exists?(p)
|
71
|
+
}.first
|
72
|
+
end
|
73
|
+
|
74
|
+
File.read(path)
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.template_dirs(dirs = [])
|
78
|
+
@@template_dirs = dirs
|
79
|
+
end
|
80
|
+
|
81
|
+
def self.parse_content(tokens, content: nil, stopwords: [])
|
82
|
+
content ||= {code: [], methods: {}, base: nil}
|
83
|
+
|
84
|
+
while token = tokens.shift
|
85
|
+
case token
|
86
|
+
when '{%' then
|
87
|
+
sub_token = tokens.shift
|
88
|
+
case sub_token
|
89
|
+
when /^\bblock\b/
|
90
|
+
block_name = /^block (?<name>.*)$/.match(sub_token)[:name]
|
91
|
+
block_content = parse_content(tokens, stopwords: ['end'])
|
92
|
+
content[:methods].merge!(block_content[:methods])
|
93
|
+
content[:methods][block_name.to_sym] = block_content[:code].join("\n")
|
94
|
+
content[:code] << "__out << __block_#{block_name}(__params)"
|
95
|
+
when /\bextends\b/
|
96
|
+
content[:base] = /^extends\s+["'](?<tpl>.*)["']$/.match(sub_token)[:tpl]
|
97
|
+
when /\binclude\b/
|
98
|
+
path = /^include\s+["'](?<tpl>.*)["']$/.match(sub_token)[:tpl]
|
99
|
+
content[:code] << "__out << include(\"#{path}\", __params)"
|
100
|
+
when /\bend\b/
|
101
|
+
return content if stopwords.include?('end')
|
102
|
+
content[:code] << 'end'
|
103
|
+
else
|
104
|
+
content[:code] << sub_token
|
105
|
+
end
|
106
|
+
when '{{' then # output value
|
107
|
+
content[:code] << "__out << (h(#{tokens.shift})).to_s"
|
108
|
+
when '{!' then # output value
|
109
|
+
content[:code] << "__out << (#{tokens.shift}).to_s"
|
110
|
+
when '{-' then # suppress output
|
111
|
+
content[:code] << "(#{tokens.shift}).to_s"
|
112
|
+
when '{#' then # igore comments
|
113
|
+
tokens.shift
|
114
|
+
else
|
115
|
+
content[:code] << "__out << #{token.dump}" if token != ''
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
content
|
120
|
+
end
|
121
|
+
|
122
|
+
def self.cache
|
123
|
+
Thread.current[:simplex_cache] ||= {}
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
module Helpers
|
128
|
+
def self.render(path, params={})
|
129
|
+
TemplateParser
|
130
|
+
.template(path, params.keys)
|
131
|
+
.render(params)
|
132
|
+
end
|
133
|
+
|
134
|
+
def self.render_text(content, params={})
|
135
|
+
TemplateParser
|
136
|
+
.parse(content, params.keys)
|
137
|
+
.render(params)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def self.render(*args)
|
142
|
+
Helpers.render(*args)
|
143
|
+
end
|
144
|
+
|
145
|
+
def self.render_text(*args)
|
146
|
+
Helpers.render_text(*args)
|
147
|
+
end
|
148
|
+
|
149
|
+
def self.template_dirs(dirs = [])
|
150
|
+
TemplateParser.template_dirs(dirs)
|
151
|
+
end
|
152
|
+
end
|
data/semplice.gemspec
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
require './lib/semplice'
|
3
|
+
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = 'semplice'
|
7
|
+
s.version = Semplice::VERSION
|
8
|
+
s.summary = 'Simple Template Engine.'
|
9
|
+
s.description = 'Semplice is a mote inspired template engine with Django-like syntax and inheritance.'
|
10
|
+
s.license = 'MIT'
|
11
|
+
s.authors = ['Matías Aguirre']
|
12
|
+
s.email = ['matiasaguirre@gmail.com']
|
13
|
+
s.homepage = 'http://github.com/omab/semplice'
|
14
|
+
s.files = Dir[
|
15
|
+
'LICENSE',
|
16
|
+
'README.md',
|
17
|
+
'lib/**/*.rb',
|
18
|
+
'*.gemspec',
|
19
|
+
'test/**/*.rb'
|
20
|
+
]
|
21
|
+
s.add_development_dependency 'minitest', '~> 5.7'
|
22
|
+
end
|
metadata
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: semplice
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Matías Aguirre
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-07-09 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: minitest
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '5.7'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '5.7'
|
27
|
+
description: Semplice is a mote inspired template engine with Django-like syntax and
|
28
|
+
inheritance.
|
29
|
+
email:
|
30
|
+
- matiasaguirre@gmail.com
|
31
|
+
executables: []
|
32
|
+
extensions: []
|
33
|
+
extra_rdoc_files: []
|
34
|
+
files:
|
35
|
+
- LICENSE
|
36
|
+
- README.md
|
37
|
+
- lib/semplice.rb
|
38
|
+
- semplice.gemspec
|
39
|
+
homepage: http://github.com/omab/semplice
|
40
|
+
licenses:
|
41
|
+
- MIT
|
42
|
+
metadata: {}
|
43
|
+
post_install_message:
|
44
|
+
rdoc_options: []
|
45
|
+
require_paths:
|
46
|
+
- lib
|
47
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
48
|
+
requirements:
|
49
|
+
- - ">="
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: '0'
|
52
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: '0'
|
57
|
+
requirements: []
|
58
|
+
rubyforge_project:
|
59
|
+
rubygems_version: 2.2.2
|
60
|
+
signing_key:
|
61
|
+
specification_version: 4
|
62
|
+
summary: Simple Template Engine.
|
63
|
+
test_files: []
|