ltdtemplate 0.2.4 → 1.0.0
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/CHANGELOG +10 -1
- data/Gemfile +2 -1
- data/RESOURCES +18 -32
- data/TEMPLATE_MANUAL.html +126 -47
- data/lib/ltdtemplate.rb +352 -242
- data/lib/ltdtemplate/code.rb +14 -87
- data/lib/ltdtemplate/code/call.rb +20 -16
- data/lib/ltdtemplate/code/parameters.rb +28 -31
- data/lib/ltdtemplate/code/sequence.rb +39 -0
- data/lib/ltdtemplate/code/subscript.rb +57 -50
- data/lib/ltdtemplate/code/variable.rb +22 -39
- data/lib/ltdtemplate/proxy.rb +26 -0
- data/lib/ltdtemplate/proxy/array.rb +258 -0
- data/lib/ltdtemplate/proxy/boolean.rb +74 -0
- data/lib/ltdtemplate/proxy/match.rb +40 -0
- data/lib/ltdtemplate/proxy/nil.rb +27 -0
- data/lib/ltdtemplate/proxy/number.rb +77 -0
- data/lib/ltdtemplate/proxy/regexp.rb +74 -0
- data/lib/ltdtemplate/proxy/string.rb +196 -0
- data/lib/ltdtemplate/value.rb +94 -0
- data/lib/ltdtemplate/value/array_splat.rb +34 -0
- data/lib/ltdtemplate/value/code_block.rb +21 -17
- data/lib/ltdtemplate/value/namespace.rb +77 -79
- data/ltdtemplate.gemspec +2 -2
- data/test/04number.rb +0 -7
- data/test/05string.rb +0 -7
- data/test/06array.rb +0 -9
- data/test/07each.rb +3 -3
- data/test/08interpolate.rb +1 -1
- data/test/10missing_meth.rb +1 -1
- data/test/11classes.rb +9 -9
- metadata +15 -13
- data/lib/ltdtemplate/code/code_block.rb +0 -30
- data/lib/ltdtemplate/value/array.rb +0 -210
- data/lib/ltdtemplate/value/boolean.rb +0 -82
- data/lib/ltdtemplate/value/nil.rb +0 -30
- data/lib/ltdtemplate/value/number.rb +0 -96
- data/lib/ltdtemplate/value/string.rb +0 -215
- data/lib/test.rb +0 -10
- 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
|