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 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