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
@@ -10,8 +10,7 @@ class LtdTemplate::Code::Variable < LtdTemplate::Code
|
|
10
10
|
|
11
11
|
def initialize (template, name)
|
12
12
|
super template
|
13
|
-
|
14
|
-
when '@', '^'
|
13
|
+
if name.size > 1 && (name[0] == '@' || name[0] == '^')
|
15
14
|
# @var is in the root namespace
|
16
15
|
# ^var is in the parent namespace
|
17
16
|
@modifier = name[0]
|
@@ -24,6 +23,25 @@ class LtdTemplate::Code::Variable < LtdTemplate::Code
|
|
24
23
|
end
|
25
24
|
end
|
26
25
|
|
26
|
+
#
|
27
|
+
# Evaluate
|
28
|
+
#
|
29
|
+
def evaluate (opts = {})
|
30
|
+
case opts[:method]
|
31
|
+
when '=', '?='
|
32
|
+
if opts[:method] != '?=' || self.namespace[@name].nil?
|
33
|
+
params = opts[:parameters]
|
34
|
+
params = params[0] if params.is_a? LtdTemplate::Univalue
|
35
|
+
self.namespace[@name] = params
|
36
|
+
end
|
37
|
+
nil
|
38
|
+
else
|
39
|
+
self.namespace[@name] = "" if
|
40
|
+
opts[:method] == 'methods' && self.namespace[@name].nil?
|
41
|
+
rubyversed(self.namespace[@name]).evaluate opts
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
27
45
|
#
|
28
46
|
# Return the namespace in which this variable currently resides
|
29
47
|
# (or would reside, if it doesn't currently exist).
|
@@ -37,41 +55,6 @@ class LtdTemplate::Code::Variable < LtdTemplate::Code
|
|
37
55
|
base.find_item(@name) || base
|
38
56
|
end
|
39
57
|
|
40
|
-
#
|
41
|
-
# Return the namespace item for this variable.
|
42
|
-
#
|
43
|
-
def target; namespace.get_item(@name); end
|
44
|
-
|
45
|
-
#
|
46
|
-
# Implement the subscripting interface.
|
47
|
-
#
|
48
|
-
def has_item? (key); target.has_item? key; end
|
49
|
-
def get_item (key); target.get_item key; end
|
50
|
-
def set_item (key, value); target.set_item key, value; end
|
51
|
-
|
52
|
-
#
|
53
|
-
# Try to set the value.
|
54
|
-
#
|
55
|
-
def set_value (value)
|
56
|
-
namespace.set_item(@name, value)
|
57
|
-
end
|
58
|
-
|
59
|
-
#
|
60
|
-
# Is this variable set?
|
61
|
-
# Among other possible uses, this is needed for determining when to
|
62
|
-
# auto-vivicate array subscripts.
|
63
|
-
#
|
64
|
-
def is_set?; namespace.has_item? @name; end
|
65
|
-
|
66
|
-
def get_value (opts = {})
|
67
|
-
case opts[:method]
|
68
|
-
when '=' then do_set opts # see LtdTemplate::Code
|
69
|
-
when '?='
|
70
|
-
if is_set? then @template.nil
|
71
|
-
else do_set opts
|
72
|
-
end
|
73
|
-
else target.get_value opts
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
58
|
end
|
59
|
+
|
60
|
+
# END
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# LtdTemplate::Proxy - Common code for LtdTemplate value proxies
|
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/value'
|
8
|
+
|
9
|
+
class LtdTemplate::Proxy
|
10
|
+
|
11
|
+
include LtdTemplate::Value
|
12
|
+
|
13
|
+
def initialize (template, original)
|
14
|
+
super template
|
15
|
+
@original = original
|
16
|
+
end
|
17
|
+
|
18
|
+
# Return the Rubyverse original object.
|
19
|
+
def rubyverse_original; @original; end
|
20
|
+
|
21
|
+
# Shortcut to rubyversed in the tamplate.
|
22
|
+
def rubyversed (obj); @template.rubyversed obj; end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
# END
|
@@ -0,0 +1,258 @@
|
|
1
|
+
# LtdTemplate::Proxy::Array - Proxy for arrays, hashes, and Sarahs
|
2
|
+
# in an LtdTemplate
|
3
|
+
#
|
4
|
+
# @author Brian Katzung <briank@kappacs.com>, Kappa Computer Solutions, LLC
|
5
|
+
# @copyright 2013-2014 Brian Katzung and Kappa Computer Solutions, LLC
|
6
|
+
# @license MIT License
|
7
|
+
|
8
|
+
require 'sarah'
|
9
|
+
require 'xkeys'
|
10
|
+
require 'ltdtemplate/proxy'
|
11
|
+
require 'ltdtemplate/value/array_splat'
|
12
|
+
|
13
|
+
class LtdTemplate::Proxy::Array < LtdTemplate::Proxy
|
14
|
+
|
15
|
+
# Proxy methods to access the underlying data structure.
|
16
|
+
# These must be #include'd before XKeys::Hash for proper
|
17
|
+
# method resolution.
|
18
|
+
Module.new do
|
19
|
+
def [] (*args); @original.[] *args; end
|
20
|
+
def []= (*args); @original.[]= *args; end
|
21
|
+
def fetch (*args); @original.fetch *args; end
|
22
|
+
def push (*args); @original.push *args; end
|
23
|
+
end.tap { |mod| include mod }
|
24
|
+
include XKeys::Hash
|
25
|
+
|
26
|
+
# Evaluate supported array methods.
|
27
|
+
def evaluate (opts = {})
|
28
|
+
# Methods supported for all proxied types.
|
29
|
+
case opts[:method]
|
30
|
+
when nil, 'call' then return @original
|
31
|
+
when 'class' then return 'Array'
|
32
|
+
when 'each', 'each_rnd', 'each_seq' then return do_each opts
|
33
|
+
when 'rnd_size' then return self.named.size
|
34
|
+
when 'seq_size' then return self.positional.size
|
35
|
+
when 'size' then return @original.size
|
36
|
+
when 'type' then return 'array'
|
37
|
+
when '/'
|
38
|
+
return @template.factory(:array_splat, self.positional,
|
39
|
+
self.named.to_a.flatten(1)).tap do |splat|
|
40
|
+
size = splat.positional.size + splat.named.size
|
41
|
+
# RESOURCE array_growth: Increases in array sizes
|
42
|
+
@template.use :array_growth, size
|
43
|
+
# RESOURCE array_size: Size of largest modified array
|
44
|
+
@template.using :array_size, size
|
45
|
+
end
|
46
|
+
when '%'
|
47
|
+
return @template.factory(:array_splat, [],
|
48
|
+
self.positional).tap do |splat|
|
49
|
+
@template.use :array_growth, splat.named.size
|
50
|
+
@template.using :array_size, splat.named.size
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Methods supported by Array and Sarah objects.
|
55
|
+
case @original
|
56
|
+
when ::Array, Sarah
|
57
|
+
case opts[:method]
|
58
|
+
when 'join' then return do_join opts
|
59
|
+
when 'pop', '->' then return do_pop opts
|
60
|
+
when 'push', '+>' then return do_push opts
|
61
|
+
when 'shift', '<-' then return do_shift opts
|
62
|
+
when 'unshift', '<+' then return do_unshift opts
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
super opts
|
67
|
+
end
|
68
|
+
|
69
|
+
# Meter usage when modifying the array.
|
70
|
+
#
|
71
|
+
# @param node [Array,Hash,Sarah] The array being updated.
|
72
|
+
# @param key [Object] The index/key being added/updated.
|
73
|
+
def meter (node, key)
|
74
|
+
case node
|
75
|
+
when ::Array
|
76
|
+
if key == :[] then growth = 1 # (push)
|
77
|
+
elsif key > node.size then growth = key - node.size
|
78
|
+
else growth = 0 # existing index
|
79
|
+
end
|
80
|
+
when Hash, Sarah
|
81
|
+
growth = node.has_key?(key) ? 0 : 1
|
82
|
+
end
|
83
|
+
@template.use :array_growth, growth if growth > 0
|
84
|
+
@template.using :array_size, node.size + growth
|
85
|
+
end
|
86
|
+
|
87
|
+
# Access named (random-access) parts of the data structure.
|
88
|
+
def named
|
89
|
+
case @original
|
90
|
+
when Hash then @original
|
91
|
+
when Sarah
|
92
|
+
# RESOURCE array: Total number of arrays created
|
93
|
+
@template.use :array
|
94
|
+
size = @original.size :nsq
|
95
|
+
@template.use :array_growth, size
|
96
|
+
@template.using :array_size, size
|
97
|
+
@original.to_h :nsq
|
98
|
+
else {}
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# Access positional (sequential) parts of the data structure.
|
103
|
+
def positional
|
104
|
+
case @original
|
105
|
+
when ::Array then @original
|
106
|
+
when Sarah
|
107
|
+
@template.use :array
|
108
|
+
size = @original.size :seq
|
109
|
+
@template.use :array_growth, size
|
110
|
+
@template.using :array_size, size
|
111
|
+
@original.values :seq
|
112
|
+
else []
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
# Reflect original respond_to? :push, etc. for XKeys.
|
117
|
+
def respond_to? (method)
|
118
|
+
case method
|
119
|
+
when :[], :[]=, :fetch, :push then @original.respond_to? method
|
120
|
+
else super method
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# The template text value is the concatenation of sequential text values.
|
125
|
+
def tpl_text
|
126
|
+
self.positional.map { |val| rubyversed(val).tpl_text }.join ''
|
127
|
+
end
|
128
|
+
|
129
|
+
# Return a new array (Sarah) for auto-vivification.
|
130
|
+
def xkeys_new (k2, info, opts)
|
131
|
+
meter info[:node], info[:key1]
|
132
|
+
@template.factory :array
|
133
|
+
end
|
134
|
+
|
135
|
+
# Check array growth on final assignment
|
136
|
+
def xkeys_on_final (node, key, value)
|
137
|
+
meter node, key
|
138
|
+
end
|
139
|
+
|
140
|
+
############################################################
|
141
|
+
|
142
|
+
# Loop over each key, value
|
143
|
+
def do_each (opts)
|
144
|
+
results = @template.factory :array
|
145
|
+
if params = opts[:parameters] and params.size(:seq) > 0
|
146
|
+
body = params[0]
|
147
|
+
if opts[:method] != 'each_rnd'
|
148
|
+
(seq = self.positional).each_index do |idx|
|
149
|
+
@template.use :iterations
|
150
|
+
each_params = @template.factory(:array).
|
151
|
+
push idx, self[idx]
|
152
|
+
results.push body.evaluate(:method => 'each_seq',
|
153
|
+
:parameters => each_params)
|
154
|
+
@template.using :array_size, results.size
|
155
|
+
end
|
156
|
+
end
|
157
|
+
if opts[:method] != 'each_seq'
|
158
|
+
(rnd = self.named).each do |key, val|
|
159
|
+
@template.use :iterations
|
160
|
+
each_params = @template.factory(:array).
|
161
|
+
push key, val
|
162
|
+
results.push body.evaluate(:method => 'each_rnd',
|
163
|
+
:parameters => each_params)
|
164
|
+
@template.using :array_size, results.size
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
results
|
169
|
+
end
|
170
|
+
|
171
|
+
# Combine sequential array element values into a string
|
172
|
+
def do_join (opts)
|
173
|
+
two = first = middle = last = ''
|
174
|
+
if params = opts[:parameters]
|
175
|
+
if params.size(:seq) > 3
|
176
|
+
two, first, middle, last = params[0..3].values
|
177
|
+
elsif params.size(:seq) > 0
|
178
|
+
two = first = middle = last = params[0]
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
text = self.positional.map { |val| rubyversed(val).tpl_text }
|
183
|
+
case text.size
|
184
|
+
when 0 then ''
|
185
|
+
when 1 then text[0]
|
186
|
+
when 2 then "#{text[0]}#{two}#{text[1]}"
|
187
|
+
else "#{text[0]}#{first}" + text[1..-2].join(middle) +
|
188
|
+
"#{last}#{text[-1]}"
|
189
|
+
end.tap do |str|
|
190
|
+
# RESOURCE string_total: Combined length of computed strings
|
191
|
+
@template.use :string_total, str.size
|
192
|
+
# RESOURCE string_length: Length of longest modified string
|
193
|
+
@template.using :string_length, str.size
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
# Pop a value off the right end of the array.
|
198
|
+
def do_pop (opts)
|
199
|
+
if @original.respond_to? :pop
|
200
|
+
@template.use :array_growth, -1
|
201
|
+
@original.pop
|
202
|
+
else nil
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
# Push values onto the right end of the array.
|
207
|
+
def do_push (opts)
|
208
|
+
if params = opts[:parameters]
|
209
|
+
case @original
|
210
|
+
when ::Array
|
211
|
+
@template.use :array_growth, params.size(:seq)
|
212
|
+
@original.push *params.values(:seq)
|
213
|
+
when Sarah
|
214
|
+
# Assume worst-case growth, then "push"
|
215
|
+
@template.use :array_growth, params.size
|
216
|
+
adjust = @original.size + params.size
|
217
|
+
@original.append! params
|
218
|
+
|
219
|
+
# Adjust actual growth if needed
|
220
|
+
adjust = @original.size - adjust
|
221
|
+
@template.use :array_growth, adjust if adjust < 0
|
222
|
+
end
|
223
|
+
@template.using :array_size, @original.size
|
224
|
+
end
|
225
|
+
nil
|
226
|
+
end
|
227
|
+
|
228
|
+
# Shift a value off the left end of the array.
|
229
|
+
def do_shift (opts)
|
230
|
+
if @original.respond_to? :shift
|
231
|
+
@template.use :array_growth, -1
|
232
|
+
@original.shift
|
233
|
+
else nil
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
# Unshift values onto the left end of the array.
|
238
|
+
def do_unshift (opts)
|
239
|
+
if params = opts[:parameters]
|
240
|
+
case @original
|
241
|
+
when ::Array
|
242
|
+
@template.use :array_growth, params.size(:seq)
|
243
|
+
@original.unshift *params.values(:seq)
|
244
|
+
when Sarah
|
245
|
+
@template.use :array_growth, params.size
|
246
|
+
adjust = @original.size + params.size
|
247
|
+
@original.insert! params
|
248
|
+
adjust = @original.size - adjust
|
249
|
+
@template.use :array_growth, adjust if adjust < 0
|
250
|
+
end
|
251
|
+
@template.using :array_size, @original.size
|
252
|
+
end
|
253
|
+
nil
|
254
|
+
end
|
255
|
+
|
256
|
+
end
|
257
|
+
|
258
|
+
# END
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# LtdTemplate::Proxy::Boolean - Represents true/false 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::Boolean < LtdTemplate::Proxy
|
10
|
+
|
11
|
+
# Evaluate supported methods on boolean objects.
|
12
|
+
def evaluate (opts = {})
|
13
|
+
case opts[:method]
|
14
|
+
when nil, 'call' then @original
|
15
|
+
when 'class' then 'Boolean'
|
16
|
+
when 'str', 'string' then @original ? 'true' : 'false'
|
17
|
+
when 'type' then 'boolean'
|
18
|
+
when '+', '|', 'or' then do_or opts
|
19
|
+
when '*', '&', 'and' then do_and opts
|
20
|
+
when '!', 'not' then do_not opts
|
21
|
+
else super opts
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# The template boolean value is the same as the original boolean value.
|
26
|
+
def tpl_boolean; @original; end
|
27
|
+
|
28
|
+
# Booleans have no textual value in templates.
|
29
|
+
def tpl_text; ''; end
|
30
|
+
|
31
|
+
##################################################
|
32
|
+
|
33
|
+
# Implement +/| (or):
|
34
|
+
# bool|(bool1, ..., boolN)
|
35
|
+
# True if ANY boolean is true. Evaluates {} blocks until true.
|
36
|
+
def do_or (opts)
|
37
|
+
if !@original && (params = opts[:parameters])
|
38
|
+
params.each(:seq) do |idx, expr|
|
39
|
+
return true if rubyversed(expr).evaluate(:method => 'call').
|
40
|
+
in_rubyverse(@template).tpl_boolean
|
41
|
+
end
|
42
|
+
end
|
43
|
+
@original
|
44
|
+
end
|
45
|
+
|
46
|
+
# Implement */& (and):
|
47
|
+
# bool&(bool1, ..., boolN)
|
48
|
+
# True if ALL booleans are true. Evaluates {} blocks until false.
|
49
|
+
def do_and (opts)
|
50
|
+
if @original && (params = opts[:parameters])
|
51
|
+
params.each(:seq) do |idx, expr|
|
52
|
+
return false unless rubyversed(expr).
|
53
|
+
evaluate(:method => 'call').in_rubyverse(@template).
|
54
|
+
tpl_boolean
|
55
|
+
end
|
56
|
+
end
|
57
|
+
@original
|
58
|
+
end
|
59
|
+
|
60
|
+
# Implement ! (not):
|
61
|
+
# bool!(bool1, ..., boolN)
|
62
|
+
# True if ALL booleans are false. Evaluates {} blocks until true.
|
63
|
+
def do_not (opts)
|
64
|
+
if !@original && (params = opts[:parameters])
|
65
|
+
params.each(:seq) do |idx, expr|
|
66
|
+
return false if rubyversed(expr).
|
67
|
+
evaluate(:method => 'call').in_rubyverse(@template).
|
68
|
+
to_boolean
|
69
|
+
end
|
70
|
+
end
|
71
|
+
!@original
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# LtdTemplate::Proxy::Match - Proxies a regexp match in an LtdTemplate
|
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::Match < LtdTemplate::Proxy
|
10
|
+
|
11
|
+
# Access array-like results
|
12
|
+
def [] (*args); @original[*args]; end
|
13
|
+
|
14
|
+
# Evaluate supported methods for regexp matches.
|
15
|
+
def evaluate (opts = {})
|
16
|
+
case opts[:method]
|
17
|
+
when nil, 'call' then @original
|
18
|
+
when 'begin', 'end', 'offset' then do_offset opts
|
19
|
+
when 'class' then 'Match'
|
20
|
+
when 'length', 'size' then @original.size
|
21
|
+
when 'type' then 'match'
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Renders as empty string in a template.
|
26
|
+
def tpl_text; ''; end
|
27
|
+
|
28
|
+
##################################################
|
29
|
+
|
30
|
+
def do_offset (opts)
|
31
|
+
if (params = opts[:parameters]) && params.size(:seq) > 0 &&
|
32
|
+
params[0].is_a?(::Numeric)
|
33
|
+
@original.send opts[:method].to_sym, params[0]
|
34
|
+
else nil
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
# END
|