temple 0.1.3 → 0.1.4

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/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ coverage
2
+ .yardoc
3
+ doc
data/README.md CHANGED
@@ -18,7 +18,6 @@ Meta
18
18
  * Home: <http://github.com/judofyr/temple>
19
19
  * Bugs: <http://github.com/judofyr/temple/issues>
20
20
  * List: <http://groups.google.com/group/guardians-of-the-temple>
21
- * Core abstraction: {Temple::Core}
22
21
 
23
22
 
24
23
  Overview
@@ -28,7 +27,7 @@ Temple is built on a theory that every template consists of three elements:
28
27
 
29
28
  * Static text
30
29
  * Dynamic text (pieces of Ruby which are evaluated and sent to the client)
31
- * Blocks (pieces of Ruby which are evaluated and *not* sent to the client, but
30
+ * Blocks (pieces of Ruby which are evaluated and *not* sent to the client, but
32
31
  might change the control flow).
33
32
 
34
33
  The goal of a template engine is to take the template and eventually compile
@@ -65,13 +64,13 @@ straightforward data structure, which can easily be written by hand and
65
64
  manipulated by computers.
66
65
 
67
66
  Some examples:
68
-
67
+
69
68
  [:static, "Hello World!"]
70
-
69
+
71
70
  [:multi,
72
71
  [:static, "Hello "],
73
72
  [:dynamic, "@world"]]
74
-
73
+
75
74
  [:html, :tag, "em", "Hey hey"]
76
75
 
77
76
  *NOTE:* SexpProcessor, a library written by Ryan Davis, includes a `Sexp`
@@ -90,7 +89,7 @@ So what's an abstraction? An abstraction is when you introduce a new types:
90
89
 
91
90
  # Instead of:
92
91
  [:static, "<strong>Use the force</strong>"]
93
-
92
+
94
93
  # You use:
95
94
  [:html, :tag, "strong", [:static, "Use the force"]]
96
95
 
@@ -128,7 +127,7 @@ class. Temple then assumes the initializer takes an optional option hash:
128
127
  def initialize(options = {})
129
128
  @options = options
130
129
  end
131
-
130
+
132
131
  def compile(exp)
133
132
  # do stuff
134
133
  end
@@ -165,8 +164,8 @@ abstraction and compile it down to the core abstraction.
165
164
  A generator is a compiler which takes an Sexp and returns a string which is
166
165
  valid Ruby code.
167
166
 
168
- Most of the time you would just use {Temple::Core::ArrayBuffer} or any of the
169
- other generators in {Temple::Core}, but nothing stops you from writing your
167
+ Most of the time you would just use {Temple::Generators::ArrayBuffer} or any of the
168
+ other generators in {Temple::Generators}, but nothing stops you from writing your
170
169
  own.
171
170
 
172
171
  In fact, one of the great things about Temple is that if you write a new
@@ -182,19 +181,19 @@ When you have a chain of a parsers, some filters and a generator you can finally
182
181
  class MyEngine < Temple::Engine
183
182
  # First run MyParser, passing the :strict option
184
183
  use MyParser, :strict
185
-
184
+
186
185
  # Then a custom filter
187
186
  use MyFilter
188
-
187
+
189
188
  # Then some general optimizations filters
190
189
  filter :MultiFlattener
191
190
  filter :StaticMerger
192
191
  filter :DynamicInliner
193
-
192
+
194
193
  # Finally the generator
195
194
  generator :ArrayBuffer, :buffer
196
195
  end
197
-
196
+
198
197
  engine = MyEngine.new(:strict => "For MyParser")
199
198
  engine.compile(something)
200
199
 
@@ -210,7 +209,7 @@ template engines. This gives you a wide range of features and your engine can
210
209
  be used right away in many projects.
211
210
 
212
211
  require 'tilt'
213
-
212
+
214
213
  class MyTemplate < Tilt::Template
215
214
  def prepare
216
215
  @src = MyEngine.new(options).compile(data)
@@ -220,20 +219,20 @@ be used right away in many projects.
220
219
  @src
221
220
  end
222
221
  end
223
-
222
+
224
223
  # Register your file extension:
225
224
  Tilt.register 'ext', MyTemplate
226
-
225
+
227
226
  Tilt.new('example.ext').render # => Render a file
228
227
  MyTemplate.new { "String" }.render # => Render a string
229
-
228
+
230
229
 
231
230
  Installation
232
231
  ------------
233
232
 
234
233
  $ gem install temple
235
234
 
236
-
235
+
237
236
  Acknowledgements
238
237
  ----------------
239
238
 
data/Rakefile CHANGED
@@ -1,20 +1,25 @@
1
1
  require 'rake/testtask'
2
2
 
3
- def command?(command)
4
- system("type #{command} > /dev/null")
3
+ task :default => :test
4
+ task :test do
5
+ sh "bacon -Ilib -Itest --automatic --quiet"
5
6
  end
6
7
 
7
- task :default => :test
8
+ #Rake::TestTask.new(:test) do |t|
9
+ # t.libs << 'lib' << 'test'
10
+ # t.pattern = 'test/**/test_*.rb'
11
+ # t.verbose = false
12
+ #end
8
13
 
9
- if RUBY_VERSION[0,3] == "1.8" and command?("turn")
10
- task :test do
11
- suffix = "-n #{ENV['TEST']}" if ENV['TEST']
12
- sh "turn test/test_*.rb test/**/test_*.rb #{suffix}"
13
- end
14
- else
15
- Rake::TestTask.new do |t|
16
- t.libs << 'lib'
14
+ begin
15
+ require 'rcov/rcovtask'
16
+ Rcov::RcovTask.new do |t|
17
+ t.libs << 'lib' << 'test'
17
18
  t.pattern = 'test/**/test_*.rb'
18
19
  t.verbose = false
19
20
  end
20
- end
21
+ rescue LoadError
22
+ task :rcov do
23
+ abort "RCov is not available. In order to run rcov, you must: gem install rcov"
24
+ end
25
+ end
data/lib/temple.rb CHANGED
@@ -1,26 +1,31 @@
1
+ require 'temple/version'
2
+
1
3
  module Temple
2
- VERSION = "0.1.3"
3
-
4
- autoload :Core, 'temple/core'
5
- autoload :Engine, 'temple/engine'
6
- autoload :Generator, 'temple/generator'
7
- autoload :Utils, 'temple/utils'
8
-
9
- module Engines
10
- autoload :ERB, 'temple/engines/erb'
11
- end
12
-
13
- module Parsers
14
- autoload :ERB, 'temple/parsers/erb'
4
+ autoload :Generator, 'temple/generators'
5
+ autoload :Generators, 'temple/generators'
6
+ autoload :Engine, 'temple/engine'
7
+ autoload :Utils, 'temple/utils'
8
+ autoload :Mixins, 'temple/mixins'
9
+ autoload :Filter, 'temple/filter'
10
+ autoload :Template, 'temple/template'
11
+
12
+ module ERB
13
+ autoload :Engine, 'temple/erb/engine'
14
+ autoload :Parser, 'temple/erb/parser'
15
+ autoload :Template, 'temple/erb/template'
16
+ autoload :Trimming, 'temple/erb/trimming'
15
17
  end
16
-
18
+
17
19
  module Filters
18
20
  autoload :MultiFlattener, 'temple/filters/multi_flattener'
19
21
  autoload :StaticMerger, 'temple/filters/static_merger'
20
22
  autoload :DynamicInliner, 'temple/filters/dynamic_inliner'
23
+ autoload :EscapeHTML, 'temple/filters/escape_html'
24
+ autoload :Debugger, 'temple/filters/debugger'
21
25
  end
22
26
 
23
27
  module HTML
24
28
  autoload :Fast, 'temple/html/fast'
29
+ autoload :Pretty, 'temple/html/pretty'
25
30
  end
26
31
  end
data/lib/temple/engine.rb CHANGED
@@ -5,46 +5,41 @@ module Temple
5
5
  # class MyEngine < Temple::Engine
6
6
  # # First run MyParser, passing the :strict option
7
7
  # use MyParser, :strict
8
- #
8
+ #
9
9
  # # Then a custom filter
10
10
  # use MyFilter
11
- #
11
+ #
12
12
  # # Then some general optimizations filters
13
13
  # filter :MultiFlattener
14
14
  # filter :StaticMerger
15
15
  # filter :DynamicInliner
16
- #
16
+ #
17
17
  # # Finally the generator
18
18
  # generator :ArrayBuffer, :buffer
19
19
  # end
20
- #
20
+ #
21
21
  # engine = MyEngine.new(:strict => "For MyParser")
22
22
  # engine.compile(something)
23
- #
23
+ #
24
24
  class Engine
25
25
  def self.filters
26
26
  @filters ||= []
27
27
  end
28
-
28
+
29
29
  def self.use(filter, *args, &blk)
30
30
  filters << [filter, args, blk]
31
31
  end
32
-
33
- # Shortcut for <tt>use Temple::Parsers::parser</tt>
34
- def self.parser(parser, *args, &blk)
35
- use(Temple::Parsers.const_get(parser), *args, &blk)
36
- end
37
-
32
+
38
33
  # Shortcut for <tt>use Temple::Filters::parser</tt>
39
34
  def self.filter(filter, *args, &blk)
40
35
  use(Temple::Filters.const_get(filter), *args, &blk)
41
36
  end
42
-
43
- # Shortcut for <tt>use Temple::Generator::parser</tt>
37
+
38
+ # Shortcut for <tt>use Temple::Generators::parser</tt>
44
39
  def self.generator(compiler, *args, &blk)
45
- use(Temple::Core.const_get(compiler), *args, &blk)
40
+ use(Temple::Generators.const_get(compiler), *args, &blk)
46
41
  end
47
-
42
+
48
43
  def initialize(options = {})
49
44
  @chain = self.class.filters.map do |filter, args, blk|
50
45
  opt = args.last.is_a?(Hash) ? args.last.dup : {}
@@ -52,11 +47,11 @@ module Temple
52
47
  memo[ele] = options[ele] if options.has_key?(ele)
53
48
  memo
54
49
  end
55
-
50
+
56
51
  filter.new(opt, &blk)
57
52
  end
58
53
  end
59
-
54
+
60
55
  def compile(thing)
61
56
  @chain.inject(thing) { |m, e| e.compile(m) }
62
57
  end
@@ -0,0 +1,13 @@
1
+ module Temple
2
+ module ERB
3
+ class Engine < Temple::Engine
4
+ use Temple::ERB::Parser, :auto_escape
5
+ use Temple::ERB::Trimming, :trim_mode
6
+ filter :EscapeHTML, :use_html_safe
7
+ filter :MultiFlattener
8
+ filter :StaticMerger
9
+ filter :DynamicInliner
10
+ generator :ArrayBuffer
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,38 @@
1
+ module Temple
2
+ module ERB
3
+ class Parser
4
+ include Mixins::Options
5
+
6
+ ERB_PATTERN = /(<%%|%%>)|<%(==?|\#)?(.*?)?-?%>/m
7
+
8
+ ESCAPED = {
9
+ '<%%' => '<%',
10
+ '%%>' => '%>',
11
+ }.freeze
12
+
13
+ def compile(input)
14
+ result = [:multi]
15
+ pos = 0
16
+ input.scan(ERB_PATTERN) do |escaped, indicator, code|
17
+ m = Regexp.last_match
18
+ text = input[pos...m.begin(0)]
19
+ pos = m.end(0)
20
+ result << [:static, text] if !text.empty?
21
+ if escaped
22
+ result << [:static, ESCAPED[escaped]]
23
+ else
24
+ case indicator
25
+ when '#'
26
+ code.count("\n").times { result << [:newline] }
27
+ when /=/
28
+ result << (indicator.length > 1 || !options[:auto_escape] ? [:dynamic, code] : [:escape, :dynamic, code])
29
+ else
30
+ result << [:block, code]
31
+ end
32
+ end
33
+ end
34
+ result << [:static, input[pos..-1]]
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,7 @@
1
+ module Temple
2
+ module ERB
3
+ class Template < Temple::Template
4
+ engine Temple::ERB::Engine
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,33 @@
1
+ module Temple
2
+ module ERB
3
+ class Trimming < Filter
4
+ def on_multi(*exps)
5
+ case options[:trim_mode]
6
+ when '>'
7
+ exps.each_cons(2) do |a, b|
8
+ if code?(a) && static?(b)
9
+ b[1].gsub!(/^\n/, '')
10
+ end
11
+ end
12
+ when '<>'
13
+ exps.each_with_index do |exp, i|
14
+ if code?(exp) &&
15
+ (!exps[i-1] || static?(exps[i-1]) && exps[i-1][1] =~ /\n$/) &&
16
+ (exps[i+1] && static?(exps[i+1]) && exps[i+1][1] =~ /^\n/)
17
+ exps[i+1][1].gsub!(/^\n/, '') if exps[i+1]
18
+ end
19
+ end
20
+ end
21
+ [:multi, *exps]
22
+ end
23
+
24
+ def code?(exp)
25
+ exp[0] == :dynamic || exp[0] == :block
26
+ end
27
+
28
+ def static?(exp)
29
+ exp[0] == :static
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,7 @@
1
+ module Temple
2
+ class Filter
3
+ include Utils
4
+ include Mixins::Dispatcher
5
+ include Mixins::Options
6
+ end
7
+ end
@@ -0,0 +1,15 @@
1
+ module Temple
2
+ module Filters
3
+ # Filter which prints Temple expression
4
+ class Debugger < Filter
5
+ def compile(exp)
6
+ if options[:debug]
7
+ puts options[:prefix] if options[:prefix]
8
+ puts exp.inspect
9
+ puts
10
+ end
11
+ exp
12
+ end
13
+ end
14
+ end
15
+ end
@@ -1,25 +1,17 @@
1
1
  module Temple
2
2
  module Filters
3
3
  # Inlines several static/dynamic into a single dynamic.
4
- class DynamicInliner
5
- def initialize(options = {})
6
- @options = {}
7
- end
8
-
9
- def compile(exp)
10
- exp.first == :multi ? on_multi(*exp[1..-1]) : exp
11
- end
12
-
4
+ class DynamicInliner < Filter
13
5
  def on_multi(*exps)
14
6
  res = [:multi]
15
7
  curr = nil
16
8
  prev = []
17
9
  state = :looking
18
-
10
+
19
11
  # We add a noop because we need to do some cleanup at the end too.
20
- (exps + [:noop]).each do |exp|
12
+ (exps + [:noop]).each do |exp|
21
13
  head, arg = exp
22
-
14
+
23
15
  case head
24
16
  when :newline
25
17
  case state
@@ -30,7 +22,7 @@ module Temple
30
22
  # We've found something, so let's make sure the generated
31
23
  # dynamic contains a newline by escaping a newline and
32
24
  # starting a new string:
33
- #
25
+ #
34
26
  # "Hello "\
35
27
  # "#{@world}"
36
28
  prev << exp
@@ -61,22 +53,22 @@ module Temple
61
53
  # If we found a single exp last time, let's add it.
62
54
  res.concat(prev) if state == :single
63
55
  # Compile the current exp (unless it's the noop)
64
- res << compile(exp) unless head == :noop
56
+ res << compile!(exp) unless head == :noop
65
57
  # Now we're looking for more!
66
58
  state = :looking
67
59
  end
68
60
  end
69
-
61
+
70
62
  res
71
63
  end
72
-
64
+
73
65
  def static(str)
74
- Generator.to_ruby(str)[1..-2]
66
+ str.inspect[1..-2]
75
67
  end
76
-
68
+
77
69
  def dynamic(str)
78
70
  '#{%s}' % str
79
71
  end
80
72
  end
81
73
  end
82
- end
74
+ end