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
@@ -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