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 CHANGED
@@ -1,2 +1,49 @@
1
- == This is a Really Cool Ruby Thing
2
- === ( That deserves better documentation than this. )
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.
@@ -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
- Finished in 0.054271 seconds
83
+ Pending:
82
84
 
83
- 35 examples, 0 failures
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
- module Styles
3
- Foreground = {
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
- Background = {}
36
+ Styles[:background] = {}
16
37
 
17
- Foreground.each() do |name, value|
18
- Background[name] = value + 10
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
- Extra = {
22
- 'clear' => 0,
23
- 'bold' => 1,
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
- @previous_style = nil
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(options)
50
- return "\e[0m" if options.empty?
51
- return code_for(Styles::Foreground, options[:foreground]) +
52
- code_for(Styles::Background, options[:background]) +
53
- code_for(Styles::Extra, options[:extra])
54
- end
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
- def parsed(stack)
57
- reverse = Enumerable::Enumerator::new(stack, :reverse_each)
58
- @previous_style = reverse.find {|dir| TermStyleDirective === dir}
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
- [escape_codes(style(state))] + super + [escape_codes(old_style(state))]
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 render(state)
94
- [code_for(Styles::Extra, 'bold')] +
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 render(state)
104
- [code_for(Styles::Extra, 'underline')] +
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(:background => interpret(@color_text, state))
146
+ return style.merge({:inverse => 'set'})
125
147
  end
126
148
  end
127
149
  end
@@ -247,7 +247,8 @@ module Stencil
247
247
 
248
248
  def render(data)
249
249
  data_context = DataContext.new(data)
250
- render_raw(data_context).compact.join("")
250
+ raw_results = render_raw(data_context)
251
+ return raw_results.compact.join("")
251
252
  end
252
253
 
253
254
  def render_raw(data)
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: "0.1"
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: 2009-12-28 00:00:00 -08:00
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