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.
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
@@ -10,8 +10,7 @@ class LtdTemplate::Code::Variable < LtdTemplate::Code
10
10
 
11
11
  def initialize (template, name)
12
12
  super template
13
- case name[0]
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