temple 0.1.3 → 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
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