Stencil 0.1 → 0.1.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.
- 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
|