temple 0.4.1 → 0.5.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.
@@ -4,3 +4,4 @@ rvm:
4
4
  - ruby-head
5
5
  - jruby
6
6
  - rbx-18mode
7
+ - rbx-19mode
data/CHANGES CHANGED
@@ -1,3 +1,37 @@
1
+ 0.5.4
2
+
3
+ * HTML::AttributeMerger fixed, it didn't remove first empty attribute values
4
+ * Add HTML::AttributeRemover back, :remove_empty_attrs must be an Array of Strings now
5
+ of the attributes to be removed if empty
6
+ * Simplify [:case] expression grammar
7
+ * Ignore parameter :outvar by sinatra since sinatra assumes also that the buffer is a String,
8
+ they should set :buffer and :generator explicitly if they need the access
9
+
10
+ 0.5.3
11
+
12
+ * Only print an message if invalid options are passed to Temple filters or engines
13
+ since many libraries seem to use Slim and Temple in an incorrect way
14
+
15
+ 0.5.2
16
+
17
+ * Fix the :outvar problem really
18
+
19
+ 0.5.1
20
+
21
+ * Support Sinatra :outvar option in Tilt template
22
+
23
+ 0.5.0
24
+
25
+ * Added exception Temple::FilterError which should be thrown by filters
26
+ * Added Temple::Parser as default base class for parsers
27
+ * escape_html doesn't escape / anymore
28
+ * HTML::AttributeSorter uses stable sorting now
29
+ * HTML::AttributeRemover removed (Was too Slim specific)
30
+ * Engine option :chain removed
31
+ * Option validation implemented (Use define_options in your filters)
32
+ * Deprecated options implemented (Use deprecated_options in your filters)
33
+ * ThreadOptions added, Method #with_options
34
+
1
35
  0.4.1
2
36
 
3
37
  * Generators: produce optimized code
@@ -1,9 +1,11 @@
1
1
  require 'temple/version'
2
2
 
3
3
  module Temple
4
- autoload :InvalidExpression, 'temple/generators'
4
+ autoload :InvalidExpression, 'temple/exceptions'
5
+ autoload :FilterError, 'temple/exceptions'
5
6
  autoload :Generator, 'temple/generators'
6
7
  autoload :Generators, 'temple/generators'
8
+ autoload :Parser, 'temple/parser'
7
9
  autoload :Engine, 'temple/engine'
8
10
  autoload :Utils, 'temple/utils'
9
11
  autoload :Filter, 'temple/filter'
@@ -11,6 +13,7 @@ module Temple
11
13
  autoload :Grammar, 'temple/grammar'
12
14
  autoload :ImmutableHash, 'temple/hash'
13
15
  autoload :MutableHash, 'temple/hash'
16
+ autoload :OptionHash, 'temple/hash'
14
17
 
15
18
  module Mixins
16
19
  autoload :Dispatcher, 'temple/mixins/dispatcher'
@@ -45,7 +48,7 @@ module Temple
45
48
  autoload :Fast, 'temple/html/fast'
46
49
  autoload :Pretty, 'temple/html/pretty'
47
50
  autoload :AttributeMerger, 'temple/html/attribute_merger'
48
- autoload :AttributeSorter, 'temple/html/attribute_sorter'
49
51
  autoload :AttributeRemover, 'temple/html/attribute_remover'
52
+ autoload :AttributeSorter, 'temple/html/attribute_sorter'
50
53
  end
51
54
  end
@@ -33,16 +33,17 @@ module Temple
33
33
  include Mixins::EngineDSL
34
34
  extend Mixins::EngineDSL
35
35
 
36
+ define_options :file, :streaming, :buffer
37
+
36
38
  attr_reader :chain
37
39
 
38
40
  def self.chain
39
41
  @chain ||= superclass.respond_to?(:chain) ? superclass.chain.dup : []
40
42
  end
41
43
 
42
- def initialize(o = {})
44
+ def initialize(opts = {})
43
45
  super
44
46
  @chain = self.class.chain.dup
45
- [*options[:chain]].compact.each {|block| block.call(self) }
46
47
  end
47
48
 
48
49
  def call(input)
@@ -3,9 +3,7 @@ module Temple
3
3
  # Example ERB parser
4
4
  #
5
5
  # @api public
6
- class Parser
7
- include Mixins::Options
8
-
6
+ class Parser < Temple::Parser
9
7
  ERB_PATTERN = /(\n|<%%|%%>)|<%(==?|\#)?(.*?)?-?%>/m
10
8
 
11
9
  def call(input)
@@ -7,6 +7,8 @@ module Temple
7
7
  #
8
8
  # @api public
9
9
  class Trimming < Filter
10
+ define_options :trim_mode
11
+
10
12
  def on_multi(*exps)
11
13
  case options[:trim_mode]
12
14
  when '>'
@@ -0,0 +1,13 @@
1
+ module Temple
2
+ # Exception raised if invalid temple expression is found
3
+ #
4
+ # @api public
5
+ class InvalidExpression < RuntimeError
6
+ end
7
+
8
+ # Exception raised if something bad happens in a Temple filter
9
+ #
10
+ # @api public
11
+ class FilterError < RuntimeError
12
+ end
13
+ end
@@ -20,9 +20,8 @@ module Temple
20
20
  def on_case(arg, *cases)
21
21
  result = [:multi, [:code, arg ? "case (#{arg})" : 'case']]
22
22
  cases.map do |c|
23
- condition, *exps = c
24
- result << [:code, condition == :else ? 'else' : "when #{condition}"]
25
- exps.each {|e| result << compile(e) }
23
+ condition, exp = c
24
+ result << [:code, condition == :else ? 'else' : "when #{condition}"] << compile(exp)
26
25
  end
27
26
  result << [:code, 'end']
28
27
  result
@@ -5,7 +5,7 @@ module Temple
5
5
  # @api public
6
6
  class Eraser < Filter
7
7
  # [] is the empty type => keep all
8
- default_options[:keep] = [[]]
8
+ define_options :keep => [[]], :erase => nil
9
9
 
10
10
  def compile(exp)
11
11
  exp.first == :multi || (do?(:keep, exp) && !do?(:erase, exp)) ?
@@ -8,8 +8,9 @@ module Temple
8
8
  # @api public
9
9
  class Escapable < Filter
10
10
  # Activate the usage of html_safe? if it is available (for Rails 3 for example)
11
- set_default_options :use_html_safe => ''.respond_to?(:html_safe?),
12
- :disable_escape => false
11
+ define_options :escape_code,
12
+ :use_html_safe => ''.respond_to?(:html_safe?),
13
+ :disable_escape => false
13
14
 
14
15
  def initialize(opts = {})
15
16
  super
@@ -4,7 +4,7 @@ module Temple
4
4
  #
5
5
  # @api public
6
6
  class Validator < Filter
7
- default_options[:grammar] = Temple::Grammar
7
+ define_options :grammar => Temple::Grammar
8
8
 
9
9
  def compile(exp)
10
10
  options[:grammar].validate!(exp)
@@ -14,7 +14,8 @@ module Temple
14
14
  include Mixins::CompiledDispatcher
15
15
  include Mixins::Options
16
16
 
17
- default_options[:buffer] = '_buf'
17
+ define_options :capture_generator,
18
+ :buffer => '_buf'
18
19
 
19
20
  def call(exp)
20
21
  [preamble, compile(exp), postamble].join('; ')
@@ -134,10 +135,11 @@ module Temple
134
135
  #
135
136
  # @api public
136
137
  class RailsOutputBuffer < StringBuffer
137
- set_default_options :buffer_class => 'ActiveSupport::SafeBuffer',
138
- :buffer => '@output_buffer',
139
- # output_buffer is needed for Rails 3.1 Streaming support
140
- :capture_generator => RailsOutputBuffer
138
+ define_options :streaming,
139
+ :buffer_class => 'ActiveSupport::SafeBuffer',
140
+ :buffer => '@output_buffer',
141
+ # output_buffer is needed for Rails 3.1 Streaming support
142
+ :capture_generator => RailsOutputBuffer
141
143
 
142
144
  def call(exp)
143
145
  [preamble, compile(exp), postamble].join('; ')
@@ -157,5 +159,5 @@ module Temple
157
159
  end
158
160
  end
159
161
 
160
- Generator.default_options[:capture_generator] = Temple::Generators::StringBuffer
162
+ Generator.default_options[:capture_generator] = Generators::StringBuffer
161
163
  end
@@ -44,7 +44,7 @@ module Temple
44
44
  Symbol | String
45
45
 
46
46
  Case <<
47
- [Condition, 'Expression*']
47
+ [Condition, Expression]
48
48
 
49
49
  Condition <<
50
50
  String | :else
@@ -28,6 +28,12 @@ module Temple
28
28
  def values
29
29
  keys.map {|k| self[k] }
30
30
  end
31
+
32
+ def to_hash
33
+ result = {}
34
+ each {|k, v| result[k] = v }
35
+ result
36
+ end
31
37
  end
32
38
 
33
39
  # Mutable hash class which supports hash merging
@@ -45,4 +51,54 @@ module Temple
45
51
  @hash.first.update(hash)
46
52
  end
47
53
  end
54
+
55
+ class OptionHash < MutableHash
56
+ def initialize(*hash, &block)
57
+ super(*hash)
58
+ @handler = block
59
+ @valid = {}
60
+ @deprecated = {}
61
+ end
62
+
63
+ def []=(key, value)
64
+ validate_key!(key)
65
+ super
66
+ end
67
+
68
+ def update(hash)
69
+ validate_hash!(hash)
70
+ super
71
+ end
72
+
73
+ def valid_keys
74
+ keys.concat(@valid.keys).uniq
75
+ end
76
+
77
+ def add_valid_keys(*keys)
78
+ keys.flatten.each { |key| @valid[key] = true }
79
+ end
80
+
81
+ def add_deprecated_keys(*keys)
82
+ keys.flatten.each { |key| @valid[key] = @deprecated[key] = true }
83
+ end
84
+
85
+ def validate_hash!(hash)
86
+ hash.to_hash.keys.each {|key| validate_key!(key) }
87
+ end
88
+
89
+ def validate_key!(key)
90
+ @handler.call(self, key, true) if deprecated_key?(key)
91
+ @handler.call(self, key, false) unless valid_key?(key)
92
+ end
93
+
94
+ def deprecated_key?(key)
95
+ @deprecated.include?(key) ||
96
+ @hash.any? {|h| h.deprecated_key?(key) if h.respond_to?(:deprecated_key?) }
97
+ end
98
+
99
+ def valid_key?(key)
100
+ include?(key) || @valid.include?(key) ||
101
+ @hash.any? {|h| h.valid_key?(key) if h.respond_to?(:valid_key?) }
102
+ end
103
+ end
48
104
  end
@@ -3,45 +3,44 @@ module Temple
3
3
  # This filter merges html attributes (e.g. used for id and class)
4
4
  # @api public
5
5
  class AttributeMerger < Filter
6
- default_options[:attr_delimiter] = {'id' => '_', 'class' => ' '}
6
+ define_options :attr_delimiter => {'id' => '_', 'class' => ' '}
7
7
 
8
8
  def on_html_attrs(*attrs)
9
9
  names = []
10
- result = {}
10
+ values = {}
11
+
11
12
  attrs.each do |attr|
12
- raise(InvalidExpression, 'Attribute is not a html attr') if attr[0] != :html || attr[1] != :attr
13
13
  name, value = attr[2].to_s, attr[3]
14
- if result[name]
15
- delimiter = options[:attr_delimiter][name]
16
- raise "Multiple #{name} attributes specified" unless delimiter
17
- if empty_exp?(value)
18
- result[name] = [:html, :attr, name,
19
- [:multi,
20
- result[name][3],
21
- value]]
22
- elsif contains_static?(value)
23
- result[name] = [:html, :attr, name,
24
- [:multi,
25
- result[name][3],
26
- [:static, delimiter],
27
- value]]
14
+ if values[name]
15
+ raise(FilterError, "Multiple #{name} attributes specified") unless options[:attr_delimiter][name]
16
+ values[name] << value
17
+ else
18
+ values[name] = [value]
19
+ names << name
20
+ end
21
+ end
22
+
23
+ attrs = names.map do |name|
24
+ value = values[name]
25
+ if (delimiter = options[:attr_delimiter][name]) && value.size > 1
26
+ exp = [:multi]
27
+ if value.all? {|v| contains_nonempty_static?(v) }
28
+ exp << value.first
29
+ value[1..-1].each {|v| exp << [:static, delimiter] << v }
30
+ [:html, :attr, name, exp]
28
31
  else
29
- tmp = unique_name
30
- result[name] = [:html, :attr, name,
31
- [:multi,
32
- result[name][3],
33
- [:capture, tmp, value],
34
- [:if, "!#{tmp}.empty?",
35
- [:multi,
36
- [:static, delimiter],
37
- [:dynamic, tmp]]]]]
32
+ captures = unique_name
33
+ exp << [:code, "#{captures} = []"]
34
+ value.each_with_index {|v, i| exp << [:capture, "#{captures}[#{i}]", v] }
35
+ exp << [:dynamic, "#{captures}.reject(&:empty?).join(#{delimiter.inspect})"]
38
36
  end
37
+ [:html, :attr, name, exp]
39
38
  else
40
- result[name] = attr
41
- names << name
39
+ [:html, :attr, name, value.first]
42
40
  end
43
41
  end
44
- [:html, :attrs, *names.map {|name| result[name] }]
42
+
43
+ [:html, :attrs, *attrs]
45
44
  end
46
45
  end
47
46
  end
@@ -3,17 +3,24 @@ module Temple
3
3
  # This filter removes empty attributes
4
4
  # @api public
5
5
  class AttributeRemover < Filter
6
- default_options[:remove_empty_attrs] = true
6
+ define_options :remove_empty_attrs => %w(id class)
7
+
8
+ def initialize(opts = {})
9
+ super
10
+ raise ArgumentError, "Option :remove_empty_attrs must be an Array of Strings" unless Array === options[:remove_empty_attrs] &&
11
+ options[:remove_empty_attrs].all? {|a| String === a }
12
+ end
7
13
 
8
14
  def on_html_attrs(*attrs)
9
- [:multi, *(options[:remove_empty_attrs] ?
10
- attrs.map {|attr| compile(attr) } : attrs)]
15
+ [:multi, *attrs.map {|attr| compile(attr) }]
11
16
  end
12
17
 
13
18
  def on_html_attr(name, value)
19
+ return super unless options[:remove_empty_attrs].include?(name.to_s)
20
+
14
21
  if empty_exp?(value)
15
22
  value
16
- elsif contains_static?(value)
23
+ elsif contains_nonempty_static?(value)
17
24
  [:html, :attr, name, value]
18
25
  else
19
26
  tmp = unique_name
@@ -3,14 +3,18 @@ module Temple
3
3
  # This filter sorts html attributes.
4
4
  # @api public
5
5
  class AttributeSorter < Filter
6
- default_options[:sort_attrs] = true
6
+ define_options :sort_attrs => true
7
+
8
+ def call(exp)
9
+ options[:sort_attrs] ? super : exp
10
+ end
7
11
 
8
12
  def on_html_attrs(*attrs)
9
- attrs = attrs.sort_by do |attr|
13
+ n = 0 # Use n to make sort stable. This is important because the merger could be executed afterwards.
14
+ [:html, :attrs, *attrs.sort_by do |attr|
10
15
  raise(InvalidExpression, 'Attribute is not a html attr') if attr[0] != :html || attr[1] != :attr
11
- attr[2].to_s
12
- end if options[:sort_attrs]
13
- [:html, :attrs, *attrs]
16
+ [attr[2].to_s, n += 1]
17
+ end]
14
18
  end
15
19
  end
16
20
  end
@@ -21,16 +21,16 @@ module Temple
21
21
  'transitional' => '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">',
22
22
  }.freeze
23
23
 
24
- set_default_options :format => :xhtml,
25
- :attr_wrapper => "'",
26
- :autoclose => %w[meta img link br hr input area param col base]
24
+ define_options :format => :xhtml,
25
+ :attr_wrapper => "'",
26
+ :autoclose => %w[meta img link br hr input area param col base]
27
27
 
28
28
  HTML = [:html, :html4, :html5]
29
29
 
30
30
  def initialize(opts = {})
31
31
  super
32
32
  unless [:xhtml, *HTML].include?(options[:format])
33
- raise "Invalid format #{options[:format].inspect}"
33
+ raise ArgumentError, "Invalid format #{options[:format].inspect}"
34
34
  end
35
35
  end
36
36
 
@@ -46,13 +46,13 @@ module Temple
46
46
  type = type.to_s.downcase
47
47
 
48
48
  if type =~ /^xml(\s+(.+))?$/
49
- raise 'Invalid xml directive in html mode' if html?
49
+ raise(FilterError, 'Invalid xml directive in html mode') if html?
50
50
  w = options[:attr_wrapper]
51
51
  str = "<?xml version=#{w}1.0#{w} encoding=#{w}#{$2 || 'utf-8'}#{w} ?>"
52
52
  elsif html?
53
- str = HTML_DOCTYPES[type] || raise("Invalid html doctype #{type}")
53
+ str = HTML_DOCTYPES[type] || raise(FilterError, "Invalid html doctype #{type}")
54
54
  else
55
- str = XHTML_DOCTYPES[type] || raise("Invalid xhtml doctype #{type}")
55
+ str = XHTML_DOCTYPES[type] || raise(FilterError, "Invalid xhtml doctype #{type}")
56
56
  end
57
57
 
58
58
  [:static, str]
@@ -3,6 +3,19 @@ module Temple
3
3
  # @api public
4
4
  class Filter < Temple::Filter
5
5
  include Dispatcher
6
+
7
+ def contains_nonempty_static?(exp)
8
+ case exp.first
9
+ when :multi
10
+ exp[1..-1].any? {|e| contains_nonempty_static?(e) }
11
+ when :escape
12
+ contains_nonempty_static?(exp.last)
13
+ when :static
14
+ !exp.last.empty?
15
+ else
16
+ false
17
+ end
18
+ end
6
19
  end
7
20
  end
8
21
  end
@@ -2,14 +2,14 @@ module Temple
2
2
  module HTML
3
3
  # @api public
4
4
  class Pretty < Fast
5
- set_default_options :indent => ' ',
6
- :pretty => true,
7
- :indent_tags => %w(article aside audio base body datalist dd div dl dt
8
- fieldset figure footer form head h1 h2 h3 h4 h5 h6
9
- header hgroup hr html li link meta nav ol p
10
- rp rt ruby section script style table tbody td tfoot
11
- th thead title tr ul video).freeze,
12
- :pre_tags => %w(code pre textarea).freeze
5
+ define_options :indent => ' ',
6
+ :pretty => true,
7
+ :indent_tags => %w(article aside audio base body datalist dd div dl dt
8
+ fieldset figure footer form head h1 h2 h3 h4 h5 h6
9
+ header hgroup hr html li link meta nav ol p
10
+ rp rt ruby section script style table tbody td tfoot
11
+ th thead title tr ul video).freeze,
12
+ :pre_tags => %w(code pre textarea).freeze
13
13
 
14
14
  def initialize(opts = {})
15
15
  super
@@ -96,11 +96,11 @@ module Temple
96
96
  end
97
97
 
98
98
  def chain_class_constructor(filter, option_filter)
99
- local_options = Hash === option_filter.last ? option_filter.pop : nil
99
+ local_options = option_filter.last.respond_to?(:to_hash) ? option_filter.pop.to_hash : {}
100
100
  raise(ArgumentError, 'Only symbols allowed in option filter') unless option_filter.all? {|o| Symbol === o }
101
+ define_options(*option_filter) if respond_to?(:define_options)
101
102
  proc do |engine|
102
- filtered_options = Hash[*option_filter.select {|k| engine.options.include?(k) }.map {|k| [k, engine.options[k]] }.flatten]
103
- filter.new(ImmutableHash.new(local_options, filtered_options))
103
+ filter.new({}.update(engine.options).delete_if {|k,v| !option_filter.include?(k) }.update(local_options))
104
104
  end
105
105
  end
106
106
 
@@ -157,7 +157,7 @@ module Temple
157
157
  curr.each {|c| c << elem }
158
158
  start
159
159
  else
160
- raise "Invalid grammar rule '#{rule.inspect}'"
160
+ raise ArgumentError, "Invalid grammar rule '#{rule.inspect}'"
161
161
  end
162
162
  end
163
163
 
@@ -2,26 +2,83 @@ module Temple
2
2
  module Mixins
3
3
  # @api public
4
4
  module DefaultOptions
5
- def set_default_options(options)
6
- default_options.update(options)
5
+ def set_default_options(opts)
6
+ default_options.update(opts)
7
7
  end
8
8
 
9
9
  def default_options
10
- @default_options ||= MutableHash.new(superclass.respond_to?(:default_options) ?
11
- superclass.default_options : nil)
10
+ @default_options ||= OptionHash.new(superclass.respond_to?(:default_options) ?
11
+ superclass.default_options : nil) do |hash, key, deprecated|
12
+ unless @option_validator_disabled
13
+ if deprecated
14
+ puts "Option #{key.inspect} is deprecated by #{self}"
15
+ else
16
+ # TODO: This will raise an exception in the future!
17
+ # raise ArgumentError, "Option #{key.inspect} is not supported by #{self}"
18
+ puts "Option #{key.inspect} is not supported by #{self}"
19
+ end
20
+ end
21
+ end
22
+ end
23
+
24
+ def define_options(*opts)
25
+ if opts.last.respond_to?(:to_hash)
26
+ hash = opts.pop.to_hash
27
+ default_options.add_valid_keys(hash.keys)
28
+ default_options.update(hash)
29
+ end
30
+ default_options.add_valid_keys(opts)
31
+ end
32
+
33
+ def define_deprecated_options(*opts)
34
+ if opts.last.respond_to?(:to_hash)
35
+ hash = opts.pop.to_hash
36
+ default_options.add_deprecated_keys(hash.keys)
37
+ default_options.update(hash)
38
+ end
39
+ default_options.add_deprecated_keys(opts)
40
+ end
41
+
42
+ def disable_option_validator!
43
+ @option_validator_disabled = true
44
+ end
45
+ end
46
+
47
+ module ThreadOptions
48
+ def with_options(options)
49
+ old_options = thread_options
50
+ Thread.current[thread_options_key] = ImmutableHash.new(options, thread_options)
51
+ yield
52
+ ensure
53
+ Thread.current[thread_options_key] = old_options
54
+ end
55
+
56
+ def thread_options
57
+ Thread.current[thread_options_key]
58
+ end
59
+
60
+ protected
61
+
62
+ def thread_options_key
63
+ @thread_options_key ||= "#{self.name}-thread-options".to_sym
12
64
  end
13
65
  end
14
66
 
15
67
  # @api public
16
68
  module Options
17
69
  def self.included(base)
18
- base.class_eval { extend DefaultOptions }
70
+ base.class_eval do
71
+ extend DefaultOptions
72
+ extend ThreadOptions
73
+ end
19
74
  end
20
75
 
21
76
  attr_reader :options
22
77
 
23
- def initialize(options = {})
24
- @options = ImmutableHash.new(options, self.class.default_options)
78
+ def initialize(opts = {})
79
+ self.class.default_options.validate_hash!(opts)
80
+ self.class.default_options.validate_hash!(self.class.thread_options) if self.class.thread_options
81
+ @options = ImmutableHash.new({}.update(self.class.default_options).update(self.class.thread_options || {}).update(opts))
25
82
  end
26
83
  end
27
84
  end
@@ -4,39 +4,25 @@ module Temple
4
4
  module Template
5
5
  include DefaultOptions
6
6
 
7
- def engine(engine = nil)
8
- default_options[:engine] = engine if engine
9
- default_options[:engine]
10
- end
11
-
12
- def init
13
- # Overwrite this for class initialization
7
+ def compile(code, options)
8
+ engine = options.delete(:engine)
9
+ raise 'No engine configured' unless engine
10
+ engine.new(options).call(code)
14
11
  end
15
12
 
16
- def register_as(name)
13
+ def register_as(*names)
17
14
  raise NotImplementedError
18
15
  end
19
16
 
20
17
  def create(engine, options)
18
+ register_as = options.delete(:register_as)
21
19
  template = Class.new(self)
20
+ template.disable_option_validator!
22
21
  template.default_options[:engine] = engine
23
22
  template.default_options.update(options)
24
- template.init
25
- template.register_as(options[:register_as]) if options[:register_as]
23
+ template.register_as(*register_as) if register_as
26
24
  template
27
25
  end
28
-
29
- def build_engine(*options)
30
- raise 'No engine configured' unless engine
31
- options << default_options
32
- engine.new(ImmutableHash.new(*options))
33
- end
34
-
35
- def chain(&block)
36
- chain = (default_options[:chain] ||= [])
37
- chain << block if block
38
- chain
39
- end
40
26
  end
41
27
  end
42
28
  end
@@ -0,0 +1,8 @@
1
+ module Temple
2
+ # Temple base parser
3
+ # @api public
4
+ class Parser
5
+ include Utils
6
+ include Mixins::Options
7
+ end
8
+ end
@@ -14,12 +14,15 @@ module Temple
14
14
  extend Mixins::Template
15
15
 
16
16
  def compile(template)
17
- self.class.build_engine(:streaming => false, # Overwrite option: No streaming support in Rails < 3.1
18
- :file => template.identifier).call(template.source)
17
+ # Overwrite option: No streaming support in Rails < 3.1
18
+ opts = {}.update(self.class.default_options).update(:file => template.identifier, :streaming => false)
19
+ self.class.compile(template.source, opts)
19
20
  end
20
21
 
21
- def self.register_as(name)
22
- ActionView::Template.register_template_handler name.to_sym, self
22
+ def self.register_as(*names)
23
+ names.each do |name|
24
+ ActionView::Template.register_template_handler name.to_sym, self
25
+ end
23
26
  end
24
27
  end
25
28
  else
@@ -27,15 +30,18 @@ module Temple
27
30
  extend Mixins::Template
28
31
 
29
32
  def call(template)
30
- self.class.build_engine(:file => template.identifier).call(template.source)
33
+ opts = {}.update(self.class.default_options).update(:file => template.identifier)
34
+ self.class.compile(template.source, opts)
31
35
  end
32
36
 
33
37
  def supports_streaming?
34
38
  self.class.default_options[:streaming]
35
39
  end
36
40
 
37
- def self.register_as(name)
38
- ActionView::Template.register_template_handler name.to_sym, new
41
+ def self.register_as(*names)
42
+ names.each do |name|
43
+ ActionView::Template.register_template_handler name.to_sym, new
44
+ end
39
45
  end
40
46
  end
41
47
  end
@@ -5,14 +5,27 @@ module Temple
5
5
  class Tilt < ::Tilt::Template
6
6
  extend Mixins::Template
7
7
 
8
+ define_options :mime_type => 'text/html'
9
+
10
+ def self.default_mime_type
11
+ default_options[:mime_type]
12
+ end
13
+
14
+ def self.default_mime_type=(mime_type)
15
+ default_options[:mime_type] = mime_type
16
+ end
17
+
8
18
  # Prepare Temple template
9
19
  #
10
20
  # Called immediately after template data is loaded.
11
21
  #
12
22
  # @return [void]
13
23
  def prepare
14
- @src = self.class.build_engine({ :streaming => false, # Overwrite option: No streaming support in Tilt
15
- :file => eval_file }, options).call(data)
24
+ # Overwrite option: No streaming support in Tilt
25
+ opts = {}.update(self.class.default_options).update(options).update(:file => eval_file, :streaming => false)
26
+ opts.delete(:mime_type)
27
+ opts.delete(:outvar) # Sinatra gives us this invalid variable
28
+ @src = self.class.compile(data, opts)
16
29
  end
17
30
 
18
31
  # A string containing the (Ruby) source code for the template.
@@ -23,12 +36,8 @@ module Temple
23
36
  @src
24
37
  end
25
38
 
26
- def self.init
27
- self.default_mime_type = default_options[:mime_type] || 'text/html'
28
- end
29
-
30
- def self.register_as(name)
31
- ::Tilt.register name.to_s, self
39
+ def self.register_as(*names)
40
+ ::Tilt.register(self, *names.map(&:to_s))
32
41
  end
33
42
  end
34
43
  end
@@ -18,7 +18,7 @@ module Temple
18
18
  # @param html [String] The string to escape
19
19
  # @return [String] The escaped string
20
20
  def escape_html(html)
21
- EscapeUtils.escape_html(html.to_s)
21
+ EscapeUtils.escape_html(html.to_s, false)
22
22
  end
23
23
  elsif RUBY_VERSION > '1.9'
24
24
  # Used by escape_html
@@ -28,7 +28,6 @@ module Temple
28
28
  '"' => '&quot;',
29
29
  '<' => '&lt;',
30
30
  '>' => '&gt;',
31
- '/' => '&#47;',
32
31
  }.freeze
33
32
 
34
33
  # Returns an escaped copy of `html`.
@@ -36,7 +35,7 @@ module Temple
36
35
  # @param html [String] The string to escape
37
36
  # @return [String] The escaped string
38
37
  def escape_html(html)
39
- html.to_s.gsub(/[&\"<>\/]/, ESCAPE_HTML)
38
+ html.to_s.gsub(/[&\"<>]/, ESCAPE_HTML)
40
39
  end
41
40
  else
42
41
  # Returns an escaped copy of `html`.
@@ -44,7 +43,7 @@ module Temple
44
43
  # @param html [String] The string to escape
45
44
  # @return [String] The escaped string
46
45
  def escape_html(html)
47
- html.to_s.gsub(/&/n, '&amp;').gsub(/\"/n, '&quot;').gsub(/>/n, '&gt;').gsub(/</n, '&lt;').gsub(/\//, '&#47;')
46
+ html.to_s.gsub(/&/n, '&amp;').gsub(/\"/n, '&quot;').gsub(/>/n, '&gt;').gsub(/</n, '&lt;')
48
47
  end
49
48
  end
50
49
 
@@ -58,19 +57,6 @@ module Temple
58
57
  "_#{prefix}#{@unique_name += 1}"
59
58
  end
60
59
 
61
- def contains_static?(exp)
62
- case exp[0]
63
- when :multi
64
- exp[1..-1].any? {|e| contains_static?(e) }
65
- when :escape
66
- contains_static?(exp[2])
67
- when :static
68
- true
69
- else
70
- false
71
- end
72
- end
73
-
74
60
  # Check if expression is empty
75
61
  #
76
62
  # @param exp [Array] Temple expression
@@ -1,3 +1,3 @@
1
1
  module Temple
2
- VERSION = '0.4.1'
2
+ VERSION = '0.5.4'
3
3
  end
@@ -9,7 +9,7 @@ Gem::Specification.new do |s|
9
9
 
10
10
  s.authors = ['Magnus Holm', 'Daniel Mendler']
11
11
  s.email = ['judofyr@gmail.com', 'mail@daniel-mendler.de']
12
- s.homepage = 'http://dojo.rubyforge.org/'
12
+ s.homepage = 'https://github.com/judofyr/temple'
13
13
  s.summary = 'Template compilation framework in Ruby'
14
14
 
15
15
  s.require_paths = %w(lib)
@@ -56,14 +56,13 @@ describe Temple::Filters::ControlFlow do
56
56
 
57
57
  it 'should process cond' do
58
58
  @filter.call([:cond,
59
- ['cond1', [:exp11], [:exp12]],
59
+ ['cond1', [:exp1]],
60
60
  ['cond2', [:exp2]],
61
61
  [:else, [:exp3]],
62
62
  ]).should.equal [:multi,
63
63
  [:code, 'case'],
64
64
  [:code, 'when cond1'],
65
- [:exp11],
66
- [:exp12],
65
+ [:exp1],
67
66
  [:code, 'when cond2'],
68
67
  [:exp2],
69
68
  [:code, 'else'],
@@ -74,14 +73,13 @@ describe Temple::Filters::ControlFlow do
74
73
 
75
74
  it 'should process case' do
76
75
  @filter.call([:case, 'var',
77
- ['Array', [:exp11], [:exp12]],
76
+ ['Array', [:exp1]],
78
77
  ['String', [:exp2]],
79
78
  [:else, [:exp3]],
80
79
  ]).should.equal [:multi,
81
80
  [:code, 'case (var)'],
82
81
  [:code, 'when Array'],
83
- [:exp11],
84
- [:exp12],
82
+ [:exp1],
85
83
  [:code, 'when String'],
86
84
  [:exp2],
87
85
  [:code, 'else'],
@@ -50,13 +50,10 @@ describe Temple::HTML::AttributeMerger do
50
50
  [:html, :attrs,
51
51
  [:html, :attr, "id",
52
52
  [:multi,
53
- [:dynamic, "a"],
54
- [:capture, "_temple_html_attributemerger1",
55
- [:dynamic, "b"]],
56
- [:if, "!_temple_html_attributemerger1.empty?",
57
- [:multi,
58
- [:static, "_"],
59
- [:dynamic, "_temple_html_attributemerger1"]]]]]],
53
+ [:code, "_temple_html_attributemerger1 = []"],
54
+ [:capture, "_temple_html_attributemerger1[0]", [:dynamic, "a"]],
55
+ [:capture, "_temple_html_attributemerger1[1]", [:dynamic, "b"]],
56
+ [:dynamic, "_temple_html_attributemerger1.reject(&:empty?).join(\"_\")"]]]],
60
57
  [:content]]
61
58
  end
62
59
 
@@ -69,13 +66,10 @@ describe Temple::HTML::AttributeMerger do
69
66
  [:html, :attrs,
70
67
  [:html, :attr, "class",
71
68
  [:multi,
72
- [:static, "a"],
73
- [:capture, "_temple_html_attributemerger1",
74
- [:dynamic, "b"]],
75
- [:if, "!_temple_html_attributemerger1.empty?",
76
- [:multi,
77
- [:static, " "],
78
- [:dynamic, "_temple_html_attributemerger1"]]]]]],
69
+ [:code, "_temple_html_attributemerger1 = []"],
70
+ [:capture, "_temple_html_attributemerger1[0]", [:static, "a"]],
71
+ [:capture, "_temple_html_attributemerger1[1]", [:dynamic, "b"]],
72
+ [:dynamic, "_temple_html_attributemerger1.reject(&:empty?).join(\" \")"]]]],
79
73
  [:content]]
80
74
  end
81
75
  end
@@ -3,7 +3,6 @@ require 'helper'
3
3
  describe Temple::HTML::AttributeRemover do
4
4
  before do
5
5
  @remover = Temple::HTML::AttributeRemover.new
6
- @disabled_remover = Temple::HTML::AttributeRemover.new :remove_empty_attrs => false
7
6
  end
8
7
 
9
8
  it 'should pass static attributes through' do
@@ -17,7 +16,7 @@ describe Temple::HTML::AttributeRemover do
17
16
  [:content]]
18
17
  end
19
18
 
20
- it 'should check for empty dynamic attribute if :remove_empty_attrs is true' do
19
+ it 'should check for empty dynamic attribute if it is included in :remove_empty_attrs' do
21
20
  @remover.call([:html, :tag,
22
21
  'div',
23
22
  [:html, :attrs, [:html, :attr, 'class', [:dynamic, 'b']]],
@@ -31,14 +30,14 @@ describe Temple::HTML::AttributeRemover do
31
30
  [:content]]
32
31
  end
33
32
 
34
- it 'should not check for empty dynamic attribute if :remove_empty_attrs is false' do
35
- @disabled_remover.call([:html, :tag,
33
+ it 'should not check for empty dynamic attribute if it is not included in :remove_empty_attrs' do
34
+ @remover.call([:html, :tag,
36
35
  'div',
37
- [:html, :attrs, [:html, :attr, 'class', [:dynamic, 'b']]],
36
+ [:html, :attrs, [:html, :attr, 'name', [:dynamic, 'b']]],
38
37
  [:content]
39
38
  ]).should.equal [:html, :tag, "div",
40
39
  [:multi,
41
- [:html, :attr, "class", [:dynamic, "b"]]],
40
+ [:html, :attr, "name", [:dynamic, "b"]]],
42
41
  [:content]]
43
42
  end
44
43
  end
@@ -167,12 +167,4 @@ describe Temple::Engine do
167
167
  inherited_engine.chain.size.should.equal 9
168
168
  TestEngine.chain.size.should.equal 8
169
169
  end
170
-
171
- it 'should support chain option' do
172
- engine = TestEngine.new(:chain => proc {|e| e.remove :MyFilter1 })
173
- TestEngine.chain.size.should.equal 8
174
- engine.chain.size.should.equal 7
175
- engine.chain[0].first.should.equal :Parser
176
- engine.chain[1].first.should.equal :MyFilter2
177
- end
178
170
  end
@@ -38,13 +38,13 @@ world}
38
38
  it 'should escape automatically' do
39
39
  src = '<%= "<" %>'
40
40
  ans = '&lt;'
41
- erb(src, :auto_escape => true).should.equal ans
41
+ erb(src).should.equal ans
42
42
  end
43
43
 
44
44
  it 'should support == to disable automatic escape' do
45
45
  src = '<%== "<" %>'
46
46
  ans = '<'
47
- erb(src, :auto_escape => true).should.equal ans
47
+ erb(src).should.equal ans
48
48
  end
49
49
 
50
50
  it 'should support trim mode' do
@@ -1,6 +1,8 @@
1
1
  require 'helper'
2
2
 
3
3
  class SimpleFilter < Temple::Filter
4
+ define_options :key
5
+
4
6
  def on_test(arg)
5
7
  [:on_test, arg]
6
8
  end
@@ -10,8 +12,9 @@ describe Temple::Filter do
10
12
  it 'should support options' do
11
13
  Temple::Filter.should.respond_to :default_options
12
14
  Temple::Filter.should.respond_to :set_default_options
15
+ Temple::Filter.should.respond_to :define_options
13
16
  Temple::Filter.new.options.should.be.instance_of Temple::ImmutableHash
14
- Temple::Filter.new(:key => 3).options[:key].should.equal 3
17
+ SimpleFilter.new(:key => 3).options[:key].should.equal 3
15
18
  end
16
19
 
17
20
  it 'should implement call' do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: temple
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1
4
+ version: 0.5.4
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,11 +10,11 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2012-09-04 00:00:00.000000000 Z
13
+ date: 2012-09-28 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: tilt
17
- requirement: !ruby/object:Gem::Requirement
17
+ requirement: &6391160 !ruby/object:Gem::Requirement
18
18
  none: false
19
19
  requirements:
20
20
  - - ! '>='
@@ -22,15 +22,10 @@ dependencies:
22
22
  version: '0'
23
23
  type: :development
24
24
  prerelease: false
25
- version_requirements: !ruby/object:Gem::Requirement
26
- none: false
27
- requirements:
28
- - - ! '>='
29
- - !ruby/object:Gem::Version
30
- version: '0'
25
+ version_requirements: *6391160
31
26
  - !ruby/object:Gem::Dependency
32
27
  name: bacon
33
- requirement: !ruby/object:Gem::Requirement
28
+ requirement: &6390120 !ruby/object:Gem::Requirement
34
29
  none: false
35
30
  requirements:
36
31
  - - ! '>='
@@ -38,15 +33,10 @@ dependencies:
38
33
  version: '0'
39
34
  type: :development
40
35
  prerelease: false
41
- version_requirements: !ruby/object:Gem::Requirement
42
- none: false
43
- requirements:
44
- - - ! '>='
45
- - !ruby/object:Gem::Version
46
- version: '0'
36
+ version_requirements: *6390120
47
37
  - !ruby/object:Gem::Dependency
48
38
  name: rake
49
- requirement: !ruby/object:Gem::Requirement
39
+ requirement: &6389580 !ruby/object:Gem::Requirement
50
40
  none: false
51
41
  requirements:
52
42
  - - ! '>='
@@ -54,12 +44,7 @@ dependencies:
54
44
  version: '0'
55
45
  type: :development
56
46
  prerelease: false
57
- version_requirements: !ruby/object:Gem::Requirement
58
- none: false
59
- requirements:
60
- - - ! '>='
61
- - !ruby/object:Gem::Version
62
- version: '0'
47
+ version_requirements: *6389580
63
48
  description:
64
49
  email:
65
50
  - judofyr@gmail.com
@@ -83,6 +68,7 @@ files:
83
68
  - lib/temple/erb/parser.rb
84
69
  - lib/temple/erb/template.rb
85
70
  - lib/temple/erb/trimming.rb
71
+ - lib/temple/exceptions.rb
86
72
  - lib/temple/filter.rb
87
73
  - lib/temple/filters/control_flow.rb
88
74
  - lib/temple/filters/dynamic_inliner.rb
@@ -106,6 +92,7 @@ files:
106
92
  - lib/temple/mixins/grammar_dsl.rb
107
93
  - lib/temple/mixins/options.rb
108
94
  - lib/temple/mixins/template.rb
95
+ - lib/temple/parser.rb
109
96
  - lib/temple/templates.rb
110
97
  - lib/temple/templates/rails.rb
111
98
  - lib/temple/templates/tilt.rb
@@ -133,7 +120,7 @@ files:
133
120
  - test/test_grammar.rb
134
121
  - test/test_hash.rb
135
122
  - test/test_utils.rb
136
- homepage: http://dojo.rubyforge.org/
123
+ homepage: https://github.com/judofyr/temple
137
124
  licenses: []
138
125
  post_install_message:
139
126
  rdoc_options: []
@@ -153,7 +140,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
153
140
  version: '0'
154
141
  requirements: []
155
142
  rubyforge_project:
156
- rubygems_version: 1.8.24
143
+ rubygems_version: 1.8.15
157
144
  signing_key:
158
145
  specification_version: 3
159
146
  summary: Template compilation framework in Ruby