Stencil 0.1 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/doc/README +49 -2
- data/doc/Specifications +9 -2
- data/lib/stencil/directives/term-style.rb +71 -49
- data/lib/stencil/template.rb +2 -1
- metadata +3 -3
data/doc/README
CHANGED
@@ -1,2 +1,49 @@
|
|
1
|
-
==
|
2
|
-
===
|
1
|
+
== Stencil
|
2
|
+
=== Free text templating system
|
3
|
+
|
4
|
+
Stencil is a templating system designed with a free text target in mind, and built around encouraging a separation of concerns. To that end, Stencil templates don't embed arbitrary Ruby, don't assume their output will be HTML, CSS or XML, and don't use any Ruby variable as a source.
|
5
|
+
|
6
|
+
Admittedly, the goals and design of Stencil are probably idiosyncratic, but
|
7
|
+
they do reflect needs I had, and I figured someone else might want something
|
8
|
+
similar.
|
9
|
+
|
10
|
+
Stencil is structured around data supplied in the form of hashes and arrays.
|
11
|
+
Data is referenced using a reasonably compact (even terse) path. Recognizing
|
12
|
+
that usually, data comes from disparate sources, there's a module that can be
|
13
|
+
included into an object to smoothly extract a view or subview suitable for
|
14
|
+
rendering.
|
15
|
+
|
16
|
+
Once views have been extracted, there's an RSpec matcher 'has_subview' that
|
17
|
+
allows the details of a view to be tested easily.
|
18
|
+
|
19
|
+
Stencils directives are defined using a reasonably straightforward sub-classing
|
20
|
+
design, and it's my experience that it's reasonably painless to add new
|
21
|
+
directives.
|
22
|
+
|
23
|
+
Stencil also provides dynamic templates, that allow for output like JSON or XML from hashes and arrays. They can be applied within a normal template.
|
24
|
+
|
25
|
+
Stencil provides a number of pretty straightforward directives:
|
26
|
+
|
27
|
+
* [;= <path>;] will interpolate the data referred to by the path. Methods on the data can be called, but by design, Ruby constants and Kernel methods aren't available.
|
28
|
+
* [;if <condition>;] works similarly to [;=;]
|
29
|
+
* [;each <path> <as>;] for simple loops
|
30
|
+
* [;with <path>;] a common use case: make sure that a value exists, and assign it to the current context
|
31
|
+
* [;/;] or [;end;] terminates if, each and with. For convenience [;/ 3;] will dig out of 3 levels of nesting.
|
32
|
+
* [;apply <kind>;] Apply a dynamic template
|
33
|
+
* [;include <path>;] Because templates should be able to include each other.
|
34
|
+
|
35
|
+
Some oddball directives, available by inclusion only,
|
36
|
+
* [;indent <depth>;] (depth can come from a reference) - lets you indent text, per line
|
37
|
+
* [;wrap <width>;] Allow lines to be wrapped to a particular size.
|
38
|
+
* [;fcolor "red";] Terminal escapes to change text color
|
39
|
+
* [;bcolor "blue";] Terminal escapes for the background color
|
40
|
+
* [;bold;] [;inverse;] [;underline;] All terminal escapes
|
41
|
+
* [;nl;] Is the same as "\n", just a little prettier.
|
42
|
+
|
43
|
+
Stencil allows whitespace between the directive delimiters ([; ;]) and the
|
44
|
+
directives themselves, which means that templates can be laid out more readably
|
45
|
+
without wrecking the layout of the final output. (e.g. [; if true ;])
|
46
|
+
|
47
|
+
The directive delimiters were chosen first because they don't collide with any output I could think of, are easy to parse, an are quick to type. The end-of-block code: [;/;] is typed all without modifier (no shift) with one hand. It might sound weird, but I've come to really like it.
|
48
|
+
|
49
|
+
Templates can be defined in strings, or loaded from a path.
|
data/doc/Specifications
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
|
2
2
|
Stencil::Template parsing an invalid template
|
3
3
|
- should indicate the line and column of the error
|
4
|
+
- should fail if a path reference is missing (PENDING: TODO)
|
4
5
|
|
5
6
|
Stencil::RenderError
|
6
7
|
- should has a useful string representation
|
@@ -77,7 +78,13 @@ Stencil::View
|
|
77
78
|
Stencil::TermStyleDirective
|
78
79
|
- should change foreground color
|
79
80
|
- should nest stylings
|
81
|
+
- should style loops properly
|
80
82
|
|
81
|
-
|
83
|
+
Pending:
|
82
84
|
|
83
|
-
|
85
|
+
Stencil::Template parsing an invalid template should fail if a path reference is missing (TODO)
|
86
|
+
./spec/error_handling.rb:17
|
87
|
+
|
88
|
+
Finished in 0.26494 seconds
|
89
|
+
|
90
|
+
37 examples, 0 failures, 1 pending
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module Stencil
|
2
|
-
|
3
|
-
|
2
|
+
Styles = {
|
3
|
+
:foreground => {
|
4
4
|
'black' => 30,
|
5
5
|
'red' => 31,
|
6
6
|
'green' => 32,
|
@@ -10,30 +10,52 @@ module Stencil
|
|
10
10
|
'cyan' => 36,
|
11
11
|
'white' => 37,
|
12
12
|
'default' => 39
|
13
|
+
},
|
14
|
+
|
15
|
+
:bold => {
|
16
|
+
'set' => 1,
|
17
|
+
'default' => 22
|
18
|
+
},
|
19
|
+
|
20
|
+
:underline => {
|
21
|
+
'set' => 4,
|
22
|
+
'default' => 24
|
23
|
+
},
|
24
|
+
|
25
|
+
:blink => {
|
26
|
+
'set' => 5,
|
27
|
+
'default' => 25
|
28
|
+
},
|
29
|
+
|
30
|
+
:inverse => {
|
31
|
+
'set' => 7,
|
32
|
+
'default' => 27
|
13
33
|
}
|
34
|
+
}
|
14
35
|
|
15
|
-
|
36
|
+
Styles[:background] = {}
|
16
37
|
|
17
|
-
|
18
|
-
|
38
|
+
Styles[:foreground].each() do |name, value|
|
39
|
+
Styles[:background][name] = value + 10
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
class Template::State
|
44
|
+
def terminal_style
|
45
|
+
@style ||= Styles.keys.inject({}){|style, key|
|
46
|
+
style.merge({key => 'default'})
|
47
|
+
}
|
48
|
+
return @style
|
19
49
|
end
|
20
50
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
'unbold' => 22,
|
25
|
-
'underline' => 4,
|
26
|
-
'ununderline' => 24,
|
27
|
-
'blink' => 5,
|
28
|
-
'unblink' => 25,
|
29
|
-
'inverse' => 7,
|
30
|
-
'uninverse' => 27
|
31
|
-
}
|
51
|
+
def terminal_style=(style)
|
52
|
+
@style = style
|
53
|
+
end
|
32
54
|
end
|
33
55
|
|
34
56
|
class TermStyleDirective < Block
|
35
57
|
def initialize(location, string)
|
36
|
-
@
|
58
|
+
@old_style = nil
|
37
59
|
@style = nil
|
38
60
|
super
|
39
61
|
end
|
@@ -46,29 +68,36 @@ module Stencil
|
|
46
68
|
end
|
47
69
|
end
|
48
70
|
|
49
|
-
def escape_codes(
|
50
|
-
return "\e[0m" if
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
71
|
+
def escape_codes(new_options, old_options)
|
72
|
+
return "\e[0m" if new_options.empty?
|
73
|
+
options = new_options.dup
|
74
|
+
old_options.each_pair do |key, value|
|
75
|
+
if options[key] == value
|
76
|
+
options.delete(key)
|
77
|
+
end
|
78
|
+
end
|
55
79
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
super
|
80
|
+
options.keys.inject("") do |codes, key|
|
81
|
+
codes + code_for(Styles[key], options[key])
|
82
|
+
end
|
60
83
|
end
|
61
84
|
|
62
85
|
def old_style(state)
|
63
|
-
@old_style ||= @previous_style.style(state) rescue {:foreground => 'default', :background => 'default'}
|
64
86
|
end
|
65
87
|
|
66
88
|
def style(state)
|
67
|
-
@style ||= update_style(old_style(state), state.data)
|
68
89
|
end
|
69
90
|
|
70
91
|
def render(state)
|
71
|
-
|
92
|
+
@old_style = state.terminal_style
|
93
|
+
@style = update_style(@old_style, state.data)
|
94
|
+
state.terminal_style = @style
|
95
|
+
result =
|
96
|
+
[escape_codes(@style, @old_style)] +
|
97
|
+
super +
|
98
|
+
[escape_codes(@old_style, @style)]
|
99
|
+
state.terminal_style = @old_style
|
100
|
+
result
|
72
101
|
end
|
73
102
|
|
74
103
|
def update_style(style, state)
|
@@ -87,41 +116,34 @@ module Stencil
|
|
87
116
|
end
|
88
117
|
end
|
89
118
|
|
119
|
+
class BackgroundColor < ForegroundColor
|
120
|
+
register :bcolor
|
121
|
+
def update_style(style, state)
|
122
|
+
return style.merge(:background => interpret(@color_text, state))
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
90
126
|
class Bold < TermStyleDirective
|
91
127
|
register :bold
|
92
128
|
|
93
|
-
def
|
94
|
-
|
95
|
-
super +
|
96
|
-
[code_for(Styles::Extra, 'unbold')]
|
129
|
+
def update_style(style, state)
|
130
|
+
return style.merge({:bold => 'set'})
|
97
131
|
end
|
98
132
|
end
|
99
133
|
|
100
134
|
class Underline < TermStyleDirective
|
101
135
|
register :underline
|
102
136
|
|
103
|
-
def
|
104
|
-
|
105
|
-
super +
|
106
|
-
[code_for(Styles::Extra, 'ununderline')]
|
137
|
+
def update_style(style, state)
|
138
|
+
return style.merge({:underline => 'set'})
|
107
139
|
end
|
108
140
|
end
|
109
141
|
|
110
142
|
class Inverse < TermStyleDirective
|
111
143
|
register :inverse
|
112
144
|
|
113
|
-
|
114
|
-
def render(state)
|
115
|
-
[code_for(Styles::Extra, 'inverse')] +
|
116
|
-
super +
|
117
|
-
[code_for(Styles::Extra, 'uninverse')]
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
class BackgroundColor < ForegroundColor
|
122
|
-
register :bcolor
|
123
145
|
def update_style(style, state)
|
124
|
-
return style.merge(:
|
146
|
+
return style.merge({:inverse => 'set'})
|
125
147
|
end
|
126
148
|
end
|
127
149
|
end
|
data/lib/stencil/template.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: Stencil
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Judson Lester
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date:
|
12
|
+
date: 2010-01-04 00:00:00 -08:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -44,7 +44,7 @@ rdoc_options:
|
|
44
44
|
- --main
|
45
45
|
- doc/README
|
46
46
|
- --title
|
47
|
-
- Stencil-0.1 RDoc
|
47
|
+
- Stencil-0.1.1 RDoc
|
48
48
|
require_paths:
|
49
49
|
- lib
|
50
50
|
required_ruby_version: !ruby/object:Gem::Requirement
|