ltdtemplate 0.2.4 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. data/CHANGELOG +10 -1
  2. data/Gemfile +2 -1
  3. data/RESOURCES +18 -32
  4. data/TEMPLATE_MANUAL.html +126 -47
  5. data/lib/ltdtemplate.rb +352 -242
  6. data/lib/ltdtemplate/code.rb +14 -87
  7. data/lib/ltdtemplate/code/call.rb +20 -16
  8. data/lib/ltdtemplate/code/parameters.rb +28 -31
  9. data/lib/ltdtemplate/code/sequence.rb +39 -0
  10. data/lib/ltdtemplate/code/subscript.rb +57 -50
  11. data/lib/ltdtemplate/code/variable.rb +22 -39
  12. data/lib/ltdtemplate/proxy.rb +26 -0
  13. data/lib/ltdtemplate/proxy/array.rb +258 -0
  14. data/lib/ltdtemplate/proxy/boolean.rb +74 -0
  15. data/lib/ltdtemplate/proxy/match.rb +40 -0
  16. data/lib/ltdtemplate/proxy/nil.rb +27 -0
  17. data/lib/ltdtemplate/proxy/number.rb +77 -0
  18. data/lib/ltdtemplate/proxy/regexp.rb +74 -0
  19. data/lib/ltdtemplate/proxy/string.rb +196 -0
  20. data/lib/ltdtemplate/value.rb +94 -0
  21. data/lib/ltdtemplate/value/array_splat.rb +34 -0
  22. data/lib/ltdtemplate/value/code_block.rb +21 -17
  23. data/lib/ltdtemplate/value/namespace.rb +77 -79
  24. data/ltdtemplate.gemspec +2 -2
  25. data/test/04number.rb +0 -7
  26. data/test/05string.rb +0 -7
  27. data/test/06array.rb +0 -9
  28. data/test/07each.rb +3 -3
  29. data/test/08interpolate.rb +1 -1
  30. data/test/10missing_meth.rb +1 -1
  31. data/test/11classes.rb +9 -9
  32. metadata +15 -13
  33. data/lib/ltdtemplate/code/code_block.rb +0 -30
  34. data/lib/ltdtemplate/value/array.rb +0 -210
  35. data/lib/ltdtemplate/value/boolean.rb +0 -82
  36. data/lib/ltdtemplate/value/nil.rb +0 -30
  37. data/lib/ltdtemplate/value/number.rb +0 -96
  38. data/lib/ltdtemplate/value/string.rb +0 -215
  39. data/lib/test.rb +0 -10
  40. data/test/03tpl_singletons.rb +0 -48
@@ -0,0 +1,27 @@
1
+ # LtdTemplate::Proxy::Nil - Represents nil in an LtdTemplate
2
+ #
3
+ # @author Brian Katzung <briank@kappacs.com>, Kappa Computer Solutions, LLC
4
+ # @copyright 2013-2014 Brian Katzung and Kappa Computer Solutions, LLC
5
+ # @license MIT License
6
+
7
+ require 'ltdtemplate/proxy'
8
+
9
+ class LtdTemplate::Proxy::Nil < LtdTemplate::Proxy
10
+
11
+ # Evaluate supported nil object methods.
12
+ def evaluate (opts = {})
13
+ case opts[:method]
14
+ when nil, 'call' then nil
15
+ when 'class' then 'Nil'
16
+ when 'type' then 'nil'
17
+ else super opts
18
+ end
19
+ end
20
+
21
+ # The template boolean value is false.
22
+ def tpl_boolean; false; end
23
+
24
+ # The template text for nil is the empty string.
25
+ def tpl_text; ''; end
26
+
27
+ end
@@ -0,0 +1,77 @@
1
+ # LtdTemplate::Proxy::Number - Proxy for a number in an LtdTemplate
2
+ #
3
+ # @author Brian Katzung <briank@kappacs.com>, Kappa Computer Solutions, LLC
4
+ # @copyright 2013-2014 Brian Katzung and Kappa Computer Solutions, LLC
5
+ # @license MIT License
6
+
7
+ require 'ltdtemplate/proxy'
8
+
9
+ class LtdTemplate::Proxy::Number < LtdTemplate::Proxy
10
+
11
+ # Evaluate supported methods for numeric objects.
12
+ def evaluate (opts = {})
13
+ case opts[:method]
14
+ when nil, 'call' then @original
15
+ when 'abs', 'ceil', 'floor'
16
+ @original.send opts[:method].to_sym
17
+ when 'class' then 'Number'
18
+ when 'flt', 'float' then @original.to_f
19
+ when 'int' then @original.to_i
20
+ when 'str', 'string' then @original.to_s
21
+ when 'type' then 'number'
22
+ when '+' then do_sequential(opts) { |a, b| a + b }
23
+ when '-' then do_subtract opts
24
+ when '*' then do_sequential(opts) { |a, b| a * b }
25
+ when '/' then do_sequential(opts) { |a, b| a / b }
26
+ when '%' then do_sequential(opts) { |a, b| a % b }
27
+ when '&' then do_sequential(opts) { |a, b| a & b }
28
+ when '|' then do_sequential(opts) { |a, b| a | b }
29
+ when '^' then do_sequential(opts) { |a, b| a ^ b }
30
+ when '<', '<=', '==', '!=', '>=', '>' then do_compare opts
31
+ else super opts
32
+ end
33
+ end
34
+
35
+ def tpl_text; @original.to_s; end
36
+
37
+ ##################################################
38
+
39
+ # Implement numeric comparison operators
40
+ def do_compare (opts)
41
+ if (params = opts[:parameters]) && params.size(:seq) > 0 &&
42
+ params[0].is_a?(Numeric)
43
+ diff = params[0]
44
+ else diff = 0
45
+ end
46
+ diff = @original - diff
47
+ case opts[:method]
48
+ when '<' then diff < 0
49
+ when '<=' then diff <= 0
50
+ when '==' then diff == 0
51
+ when '!=' then diff != 0
52
+ when '>=' then diff >= 0
53
+ when '>' then diff > 0
54
+ end
55
+ end
56
+
57
+ # Implement sequential operations (+, *, /, %, &, |, ^)
58
+ def do_sequential (opts = {}, &block)
59
+ if params = opts[:parameters]
60
+ params.values(:seq).select { |val| val.is_a? Numeric }.
61
+ inject(@original, &block)
62
+ else @original
63
+ end
64
+ end
65
+
66
+ # Implement "-" method (subtraction/negation)
67
+ def do_subtract (opts)
68
+ sum = @original
69
+ params = opts[:parameters]
70
+ if !params || params.size(:seq) == 0 then -@original
71
+ else do_sequential(opts) { |a, b| a - b }
72
+ end
73
+ end
74
+
75
+ end
76
+
77
+ # END
@@ -0,0 +1,74 @@
1
+ # LtdTemplate::Proxy::Regexp - Proxy for Regexp objects in an LtdTemplates
2
+ #
3
+ # @author Brian Katzung <briank@kappacs.com>, Kappa Computer Solutions, LLC
4
+ # @copyright 2014 Brian Katzung and Kappa Computer Solutions, LLC
5
+ # @license MIT License
6
+
7
+ require 'ltdtemplate/proxy'
8
+
9
+ class LtdTemplate::Proxy::Regexp < LtdTemplate::Proxy
10
+
11
+ # Evaluate supported methods on Regexp (regular expression) objects.
12
+ def evaluate (opts = {})
13
+ # These methods are supported regardless of whether regexp
14
+ # is enabled.
15
+ case opts[:method]
16
+ when nil, 'call'
17
+ if @template.options[:regexp] then return @original
18
+ else return nil
19
+ end
20
+ when 'class' then return 'Regexp'
21
+ when 'str', 'string'
22
+ return @original.to_s.tap do |str|
23
+ # RESOURCE string_total: Combined length of computed strings
24
+ @template.use :string_total, str.size
25
+ # RESOURCE string_length: Length of longest modified string
26
+ @template.using :string_length, str.size
27
+ end
28
+ when 'type' then return 'regexp'
29
+ end
30
+
31
+ # These methods are disabled unless regexp is enabled.
32
+ if @template.options[:regexp]
33
+ case opts[:method]
34
+ when 'ci', 'ignorecase'
35
+ if (@original.options & ::Regexp::IGNORECASE) != 0
36
+ return @original
37
+ else return ::Regexp.new(@original.source,
38
+ @original.options | ::Regexp::IGNORECASE)
39
+ end
40
+ when 'ext', 'extended'
41
+ if (@original.options & ::Regexp::EXTENDED) != 0
42
+ return @original
43
+ else return ::Regexp.new(@original.source,
44
+ @original.options | ::Regexp::EXTENDED)
45
+ end
46
+ when 'match' then return do_match opts
47
+ when 'multi', 'multiline'
48
+ if (@original.options & ::Regexp::MULTILINE) != 0
49
+ return @original
50
+ else return ::Regexp.new(@original.source,
51
+ @original.options | ::Regexp::MULTILINE)
52
+ end
53
+ end
54
+ end
55
+
56
+ super opts
57
+ end
58
+
59
+ def tpl_text; ''; end
60
+
61
+ ##################################################
62
+
63
+ def do_match (opts)
64
+ if (params = opts[:parameters]) && params.size(:seq) > 0 &&
65
+ params[0].is_a?(::String)
66
+ # Track array size generated??
67
+ @original.match(*params[0..1].values)
68
+ else nil
69
+ end
70
+ end
71
+
72
+ end
73
+
74
+ # END
@@ -0,0 +1,196 @@
1
+ # LtdTemplate::Proxy::String - Proxies a string in an LtdTemplate
2
+ #
3
+ # @author Brian Katzung <briank@kappacs.com>, Kappa Computer Solutions, LLC
4
+ # @copyright 2013-2014 Brian Katzung and Kappa Computer Solutions, LLC
5
+ # @license MIT License
6
+
7
+ require 'ltdtemplate/proxy'
8
+ require 'sarah'
9
+
10
+ class LtdTemplate::Proxy::String < LtdTemplate::Proxy
11
+
12
+ # Evaluate supported methods for strings.
13
+ def evaluate (opts = {})
14
+ case opts[:method]
15
+ when nil, 'call', 'str', 'string' then @original
16
+ when 'capcase' then meter @original.capitalize
17
+ when 'class' then 'String'
18
+ when 'downcase' then meter @original.downcase
19
+ when 'flt', 'float' then @original.to_f
20
+ when 'html'
21
+ require 'htmlentities'
22
+ meter(HTMLEntities.new(:html4).encode(@original, :basic,
23
+ :named, :decimal))
24
+ when 'idx', 'index', 'ridx', 'rindex' then do_index opts
25
+ when 'int' then @original.to_i
26
+ when 'join' then do_join opts
27
+ when 'len', 'length' then @original.length
28
+ when 'match' then do_match opts
29
+ when 'pcte'
30
+ meter(@original.gsub(/[^a-z0-9]/i) { |c| sprintf "%%%2x", c.ord })
31
+ when 'regexp'
32
+ if @template.options[:regexp] then ::Regexp.new @original
33
+ else nil
34
+ end
35
+ when 'rep', 'rep1', 'replace', 'replace1' then do_replace opts
36
+ when 'rng', 'range', 'slc', 'slice' then do_range_slice opts
37
+ when 'split' then do_split opts
38
+ when 'type' then 'string'
39
+ when 'upcase' then meter @original.upcase
40
+ when '+' then do_add opts
41
+ when '*' then do_multiply opts
42
+ when '<', '<=', '==', '!=', '>=', '>' then do_compare opts
43
+ else super opts
44
+ end
45
+ end
46
+
47
+ # Meter string resource usage
48
+ def meter (str)
49
+ # RESOURCE string_total: Combined length of computed strings
50
+ @template.use :string_total, str.size
51
+ # RESOURCE string_length: Length of longest modified string
52
+ @template.using :string_length, str.size
53
+ str
54
+ end
55
+
56
+ def tpl_text; @original; end
57
+
58
+ ##################################################
59
+
60
+ # "Add" (concatenate) strings
61
+ def do_add (opts)
62
+ combined = @original
63
+ if params = opts[:parameters]
64
+ params.each(:seq) do |key, val|
65
+ val = rubyversed(val).tpl_text
66
+ @template.using :string_length, (combined.length + val.length)
67
+ combined += val
68
+ end
69
+ end
70
+ meter combined
71
+ end
72
+
73
+ # Match a regular expression
74
+ def do_match (opts)
75
+ if (params = opts[:parameters]) && params.size(:seq) > 0 &&
76
+ params[0].is_a?(::Regexp)
77
+ params[0].in_rubyverse(@template).evaluate :method => 'match',
78
+ :parameters => Sarah[ @original, *params[1..-1].values ]
79
+ else nil
80
+ end
81
+ end
82
+
83
+ # "Multiply" (repeat) strings
84
+ def do_multiply (opts)
85
+ str = ''
86
+ if (params = opts[:parameters]) && params.size(:seq) > 0
87
+ times = params[0]
88
+ if times.is_a? Integer
89
+ str = @original
90
+ if times < 0
91
+ str = str.reverse
92
+ times = -times
93
+ end
94
+ @template.use :string_total, (str.length * times)
95
+ @template.using :string_length, (str.length * times)
96
+ str = str * times
97
+ end
98
+ end
99
+ meter str
100
+ end
101
+
102
+ # Implement string comparison operators
103
+ def do_compare (opts)
104
+ if (params = opts[:parameters]) && (params.size(:seq) > 0)
105
+ diff = rubyversed(params[0]).tpl_text
106
+ else
107
+ diff = ''
108
+ end
109
+
110
+ diff = @original <=> diff
111
+ case opts[:method]
112
+ when '<' then diff < 0
113
+ when '<=' then diff <= 0
114
+ when '==' then diff == 0
115
+ when '!=' then diff != 0
116
+ when '>=' then diff >= 0
117
+ when '>' then diff > 0
118
+ end
119
+ end
120
+
121
+ # Index and rindex
122
+ # str.index(substring[, offset])
123
+ # str.rindex(substring[, offset]
124
+ def do_index (opts)
125
+ substr, offset = '', nil
126
+ params = opts[:parameters]
127
+ if params && params.size(:seq) > 0
128
+ substr = rubyversed(params[0]).tpl_text
129
+ end
130
+ offset = params[1] if params && params.size(:seq) > 1
131
+ case opts[:method][0]
132
+ when 'r'
133
+ offset = -1 unless offset.is_a? Integer
134
+ @original.rindex(substr, offset) || -1
135
+ else
136
+ offset = 0 unless offset.is_a? Integer
137
+ @original.index(substr, offset) || -1
138
+ end
139
+ end
140
+
141
+ # String join
142
+ # str.join(list)
143
+ def do_join (opts)
144
+ params = opts[:parameters]
145
+ if params && params.size(:seq) > 0
146
+ meter(params.values(:seq).map { |val| rubyversed(val).tpl_text }.
147
+ join(@original))
148
+ else ''
149
+ end
150
+ end
151
+
152
+ # Range and slice:
153
+ # str.range([begin[, end]])
154
+ # str.slice([begin[, length]])
155
+ def do_range_slice (opts)
156
+ op1, op2 = 0, -1
157
+ params = opts[:parameters]
158
+ op1 = params[0] if params && params.size(:seq) > 0
159
+ op2 = params[1] if params && params.size(:seq) > 1
160
+ if opts[:method][0] == 'r' || op2 < 0
161
+ str = @original[op1..op2]
162
+ else str = @original[op1, op2]
163
+ end
164
+ meter(str || '')
165
+ end
166
+
167
+ # Replace and replace one
168
+ # str.replace(pattern, replacement)
169
+ # str.replace1(pattern, replacement)
170
+ def do_replace (opts)
171
+ if (params = opts[:parameters]) && params.size(:seq) > 1
172
+ pat, repl = params[0..1]
173
+ if opts[:method][-1] == '1'
174
+ # replace one
175
+ meter @original.sub(pat, repl)
176
+ else
177
+ # replace all
178
+ meter @original.gsub(pat, repl)
179
+ end
180
+ else @original
181
+ end
182
+ end
183
+
184
+ # Split
185
+ # str.split(pattern[, limit])
186
+ def do_split (opts)
187
+ if opts[:parameters]
188
+ params = opts[:parameters][0..1].values
189
+ else params = []
190
+ end
191
+ @original.split(*params).tap { |ary| ary.each { |str| meter str } }
192
+ end
193
+
194
+ end
195
+
196
+ # END
@@ -0,0 +1,94 @@
1
+ # LtdTemplate::Value - Common code for LtdTemplate value objects
2
+ #
3
+ # @author Brian Katzung (briank@kappacs.com), Kappa Computer Solutions, LLC
4
+ # @copyright 2013-2014 Brian Katzung and Kappa Computer Solutions, LLC
5
+ # @license MIT License
6
+
7
+ require 'ltdtemplate'
8
+
9
+ module LtdTemplate::Value
10
+
11
+ class Code_Block; end
12
+
13
+ # Classes that include this module are their own method handlers
14
+ # and take the template as an initialization parameter.
15
+ include LtdTemplate::Method_Handler
16
+ def self.included (base); base.extend LtdTemplate::Consumer; end
17
+
18
+ # @!attribute [r] runtime_methods
19
+ # @return [Array<LtdTemplate::Value::Code_Block>]
20
+ # This object's run-time methods.
21
+ attr_reader :runtime_methods
22
+
23
+ # Initialize the object with a link to the associated template.
24
+ #
25
+ # @param template [LtdTemplate] The associated template object.
26
+ def initialize (template)
27
+ @template = template
28
+ @runtime_methods = {}
29
+ end
30
+
31
+ # Common operations for all values
32
+ def evaluate (opts = {})
33
+ case opts[:method]
34
+ when 'methods' then do_methods opts
35
+ else do_run_method opts
36
+ end
37
+ end
38
+
39
+ # Avoid "spilling our guts" when inspected
40
+ def inspect
41
+ "#<#{self.class.name}##{self.object_id} for #{@template.inspect}>"
42
+ end
43
+
44
+ # Shortcut to rubyversed in the template.
45
+ def rubyversed (obj); @template.rubyversed(obj); end
46
+
47
+ # Default boolean value is true
48
+ def tpl_boolean; true; end
49
+
50
+ ##################################################
51
+
52
+ # Set or get run-time methods.
53
+ #
54
+ # @param opts [Hash] A hash of method options.
55
+ # @return [nil]
56
+ def do_methods (opts)
57
+ if params = opts[:parameters]
58
+ params.values(:seq).each_slice(2) do |pair|
59
+ return @runtime_methods[pair[0]] if pair.size == 1
60
+ if pair[1].nil? then @runtime_methods.delete pair[0]
61
+ else @runtime_methods[pair[0]] = pair[1]
62
+ end
63
+ end
64
+ end
65
+ nil
66
+ end
67
+
68
+ # Try to execute run-time methods bound to the object or object
69
+ # class. Returns the return value from the code block or nil.
70
+ #
71
+ # @param opts [Hash] A hash of method options.
72
+ def do_run_method (opts)
73
+ method = nil
74
+ if name = opts[:method]
75
+ method = @runtime_methods[name]
76
+ class_name = self.evaluate :method => 'class'
77
+ if !method && class_name
78
+ class_var = @template.factory(:variable, class_name).evaluate
79
+ method = rubyversed(class_var).runtime_methods[name] if class_var
80
+ end
81
+ end
82
+ if method.is_a? LtdTemplate::Value::Code_Block
83
+ opts[:target] = self
84
+ rubyversed(method).evaluate opts
85
+ elsif !method.nil? then method
86
+ elsif mmproc = @template.options[:missing_method]
87
+ mmproc.call(@template, self, opts)
88
+ else nil
89
+ end
90
+ end
91
+
92
+ end
93
+
94
+ # END