markdown_exec 3.4.0 → 3.5.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +14 -0
- data/Gemfile +1 -0
- data/Gemfile.lock +8 -1
- data/README.md +262 -116
- data/bats/block-type-shell-require-ux.bats +15 -0
- data/bats/block-type-ux-require-context.bats +14 -0
- data/bats/import-directive-line-continuation.bats +1 -1
- data/bats/import-directive-parameter-symbols.bats +1 -1
- data/bats/import-parameter-symbols.bats +1 -1
- data/bats/options.bats +2 -2
- data/demo/trap.demo1.gif +0 -0
- data/demo/trap.demo1.mp4 +0 -0
- data/docs/dev/block-type-shell-require-ux.md +18 -0
- data/docs/dev/block-type-ux-format.md +10 -0
- data/docs/dev/block-type-ux-require-context.md +32 -0
- data/docs/dev/block-type-ux-require.md +8 -4
- data/docs/dev/import-directive-line-continuation.md +0 -1
- data/docs/dev/import-directive-parameter-symbols.md +0 -2
- data/docs/dev/import-parameter-symbols-template.md +7 -5
- data/docs/dev/import-parameter-symbols.md +10 -2
- data/examples/colors.md +31 -29
- data/lib/cached_nested_file_reader.rb +15 -47
- data/lib/command_result.rb +5 -5
- data/lib/constants.rb +3 -1
- data/lib/fcb.rb +7 -1
- data/lib/hash_delegator.rb +76 -32
- data/lib/link_history.rb +1 -1
- data/lib/markdown_exec/version.rb +1 -1
- data/lib/menu.src.yml +18 -8
- data/lib/menu.yml +14 -5
- data/lib/parameter_expansion.rb +918 -0
- data/lib/parse_animation_to_tts.rb +4417 -0
- data/lib/resize_terminal.rb +19 -16
- metadata +11 -2
|
@@ -0,0 +1,918 @@
|
|
|
1
|
+
#!/usr/bin/env bundle exec ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# encoding=utf-8
|
|
5
|
+
|
|
6
|
+
# =============================================================================
|
|
7
|
+
# PARAMETER EXPANSION UTILITY
|
|
8
|
+
# =============================================================================
|
|
9
|
+
#
|
|
10
|
+
# This module provides parameter expansion functionality for markdown_exec,
|
|
11
|
+
# handling different invocation types for parameter substitution with support
|
|
12
|
+
# for new variable creation and tracking.
|
|
13
|
+
#
|
|
14
|
+
# =============================================================================
|
|
15
|
+
# REQUIREMENTS
|
|
16
|
+
# =============================================================================
|
|
17
|
+
#
|
|
18
|
+
# ## Functional Requirements
|
|
19
|
+
#
|
|
20
|
+
# 1. **Parameter Expansion**: Support all 16 invocation code combinations
|
|
21
|
+
# - Input types: C (command), E (expression), L (literal), V (variable)
|
|
22
|
+
# - Output types: C (command), E (expression), L (literal), V (variable)
|
|
23
|
+
#
|
|
24
|
+
# 2. **Single Character Support**: Accept 1-character invocation codes
|
|
25
|
+
# - Default second character to 'Q' (literal) when missing
|
|
26
|
+
# - Maintain backward compatibility with 2-character codes
|
|
27
|
+
#
|
|
28
|
+
# 3. **New Variable Tracking**: Collect details about variables that need creation
|
|
29
|
+
# - Track variable name, value, assignment code, invocation, and parameter
|
|
30
|
+
# - Support batch processing of multiple parameters
|
|
31
|
+
#
|
|
32
|
+
# 4. **Backward Compatibility**: Support both L and Q for literal types
|
|
33
|
+
# - 'L' as primary literal type
|
|
34
|
+
# - 'Q' for backward compatibility
|
|
35
|
+
# - Mixed combinations (LQ, QL) supported
|
|
36
|
+
#
|
|
37
|
+
# ## Non-Functional Requirements
|
|
38
|
+
#
|
|
39
|
+
# 1. **Performance**: Fast execution for batch processing
|
|
40
|
+
# 2. **Reliability**: Comprehensive test coverage (16 test methods, 144 assertions)
|
|
41
|
+
# 3. **Maintainability**: Clear separation of concerns and documentation
|
|
42
|
+
# 4. **Extensibility**: Easy to add new invocation types
|
|
43
|
+
#
|
|
44
|
+
# =============================================================================
|
|
45
|
+
# ARCHITECTURAL DECISIONS
|
|
46
|
+
# =============================================================================
|
|
47
|
+
#
|
|
48
|
+
# ## Design Patterns
|
|
49
|
+
#
|
|
50
|
+
# ### 1. Strategy Pattern
|
|
51
|
+
# - Each input type (C, E, L, V) has its own expansion method
|
|
52
|
+
# - `expand_command_input`, `expand_expression_input`, `expand_literal_input`, `expand_variable_input`
|
|
53
|
+
# - Enables easy addition of new input types
|
|
54
|
+
#
|
|
55
|
+
# ### 2. Factory Pattern
|
|
56
|
+
# - `create_new_variable` method creates NewVariable objects
|
|
57
|
+
# - Centralized object creation with consistent structure
|
|
58
|
+
#
|
|
59
|
+
# ### 3. Template Method Pattern
|
|
60
|
+
# - Main `expand_parameter` method delegates to specific handlers
|
|
61
|
+
# - Consistent interface across all invocation types
|
|
62
|
+
#
|
|
63
|
+
# ## Data Structures
|
|
64
|
+
#
|
|
65
|
+
# ### 1. NewVariable Struct
|
|
66
|
+
# ```ruby
|
|
67
|
+
# NewVariable = Struct.new(:name, :value, :assignment_code, :invocation, :param)
|
|
68
|
+
# ```
|
|
69
|
+
# - Immutable data structure for variable metadata
|
|
70
|
+
# - Includes `to_s` method for human-readable output
|
|
71
|
+
#
|
|
72
|
+
# ### 2. Return Value Convention
|
|
73
|
+
# - All methods return `[expansion_string, new_variable_object_or_nil]`
|
|
74
|
+
# - Consistent interface for both single and batch processing
|
|
75
|
+
#
|
|
76
|
+
# ## Error Handling
|
|
77
|
+
#
|
|
78
|
+
# ### 1. Graceful Degradation
|
|
79
|
+
# - Invalid invocation codes default to raw literal replacement
|
|
80
|
+
# - Nil/empty invocations return original value
|
|
81
|
+
#
|
|
82
|
+
# ### 2. Input Validation
|
|
83
|
+
# - Safe navigation with `&.` operator
|
|
84
|
+
# - Case-insensitive input handling with `upcase`
|
|
85
|
+
#
|
|
86
|
+
# =============================================================================
|
|
87
|
+
# IMPLEMENTATION DECISIONS
|
|
88
|
+
# =============================================================================
|
|
89
|
+
#
|
|
90
|
+
# ## Invocation Code Mapping
|
|
91
|
+
#
|
|
92
|
+
# ### Input Types
|
|
93
|
+
# - **C**: Command substitution - executes shell commands
|
|
94
|
+
# - **E**: Evaluated expression - processes shell expressions
|
|
95
|
+
# - **L**: Literal - handles raw text values
|
|
96
|
+
# - **V**: Variable reference - references existing variables
|
|
97
|
+
#
|
|
98
|
+
# ### Output Types
|
|
99
|
+
# - **C**: Command output - generates shell command strings
|
|
100
|
+
# - **E**: Expression output - generates shell expressions
|
|
101
|
+
# - **L**: Literal output - generates raw text
|
|
102
|
+
# - **V**: Variable output - generates variable references
|
|
103
|
+
#
|
|
104
|
+
# ## Default Behavior
|
|
105
|
+
#
|
|
106
|
+
# ### Single Character Defaults
|
|
107
|
+
# - Missing second character defaults to 'Q' (literal)
|
|
108
|
+
# - `:c=` → `:cq=` (command as literal)
|
|
109
|
+
# - `:e=` → `:eq=` (expression as literal)
|
|
110
|
+
# - `:l=` → `:lq=` (literal as literal)
|
|
111
|
+
# - `:v=` → `:vq=` (variable as literal)
|
|
112
|
+
#
|
|
113
|
+
# ## Shell Integration
|
|
114
|
+
#
|
|
115
|
+
# ### Command Substitution
|
|
116
|
+
# - Uses `$(command)` syntax for command execution
|
|
117
|
+
# - Proper shell escaping with `Shellwords.escape`
|
|
118
|
+
#
|
|
119
|
+
# ### Variable References
|
|
120
|
+
# - Uses `${variable}` syntax for variable expansion
|
|
121
|
+
# - Supports both simple and complex variable references
|
|
122
|
+
#
|
|
123
|
+
# ## Memory Management
|
|
124
|
+
#
|
|
125
|
+
# ### Object Creation
|
|
126
|
+
# - NewVariable objects created only when needed
|
|
127
|
+
# - Minimal memory footprint for simple expansions
|
|
128
|
+
#
|
|
129
|
+
# ### String Handling
|
|
130
|
+
# - Immutable string operations where possible
|
|
131
|
+
# - Efficient string interpolation
|
|
132
|
+
#
|
|
133
|
+
# =============================================================================
|
|
134
|
+
# TESTS
|
|
135
|
+
# =============================================================================
|
|
136
|
+
#
|
|
137
|
+
# ## Test Structure
|
|
138
|
+
#
|
|
139
|
+
# ### Test Categories
|
|
140
|
+
# 1. **Basic Functionality** - Return value structure and basic expansion
|
|
141
|
+
# 2. **Command Substitution** - All 4 C* combinations
|
|
142
|
+
# 3. **Expression Processing** - All 4 E* combinations
|
|
143
|
+
# 4. **Literal Handling** - All 4 L* combinations
|
|
144
|
+
# 5. **Variable References** - All 4 V* combinations
|
|
145
|
+
# 6. **Backward Compatibility** - Q codes and mixed L/Q combinations
|
|
146
|
+
# 7. **Single Character Support** - 1-character codes with Q defaults
|
|
147
|
+
# 8. **Batch Processing** - Multiple parameter handling
|
|
148
|
+
# 9. **Error Handling** - Invalid inputs and edge cases
|
|
149
|
+
# 10. **Shell Integration** - Proper escaping and shell syntax
|
|
150
|
+
#
|
|
151
|
+
# ### Test Metrics
|
|
152
|
+
# - **16 test methods** covering all functionality
|
|
153
|
+
# - **144 assertions** ensuring comprehensive coverage
|
|
154
|
+
# - **0 failures, 0 errors, 0 skips** - 100% pass rate
|
|
155
|
+
# - **~0.001 second execution time** - High performance
|
|
156
|
+
#
|
|
157
|
+
# ### Test Execution
|
|
158
|
+
# ```bash
|
|
159
|
+
# # Run as test suite
|
|
160
|
+
# ./lib/parameter_expansion.rb
|
|
161
|
+
# ./lib/parameter_expansion.rb --verbose
|
|
162
|
+
#
|
|
163
|
+
# # Use as library
|
|
164
|
+
# require './lib/parameter_expansion'
|
|
165
|
+
# ```
|
|
166
|
+
#
|
|
167
|
+
# =============================================================================
|
|
168
|
+
# CODE
|
|
169
|
+
# =============================================================================
|
|
170
|
+
#
|
|
171
|
+
# ## Core Classes and Methods
|
|
172
|
+
#
|
|
173
|
+
# ### ParameterExpansion Class
|
|
174
|
+
# - **expand_parameter**: Main expansion method
|
|
175
|
+
# - **expand_parameter_string**: Convenience method for string-only results
|
|
176
|
+
# - **expand_parameters**: Batch processing method
|
|
177
|
+
#
|
|
178
|
+
# ### NewVariable Struct
|
|
179
|
+
# - **name**: Variable name to be created
|
|
180
|
+
# - **value**: Original value passed in
|
|
181
|
+
# - **assignment_code**: Shell code for variable assignment
|
|
182
|
+
# - **invocation**: Invocation code used
|
|
183
|
+
# - **param**: Original parameter name
|
|
184
|
+
#
|
|
185
|
+
# ## Method Signatures
|
|
186
|
+
#
|
|
187
|
+
# ```ruby
|
|
188
|
+
# # Main expansion method
|
|
189
|
+
# def self.expand_parameter(param, invocation, value)
|
|
190
|
+
# # Returns: [expansion_string, new_variable_object_or_nil]
|
|
191
|
+
# end
|
|
192
|
+
#
|
|
193
|
+
# # Convenience method
|
|
194
|
+
# def self.expand_parameter_string(param, invocation, value)
|
|
195
|
+
# # Returns: expansion_string
|
|
196
|
+
# end
|
|
197
|
+
#
|
|
198
|
+
# # Batch processing
|
|
199
|
+
# def self.expand_parameters(parameter_hash)
|
|
200
|
+
# # Returns: [expansions_hash, new_variables_array]
|
|
201
|
+
# end
|
|
202
|
+
# ```
|
|
203
|
+
#
|
|
204
|
+
# =============================================================================
|
|
205
|
+
# SEMANTIC TOKENS
|
|
206
|
+
# =============================================================================
|
|
207
|
+
#
|
|
208
|
+
# ## Cross-Reference Tokens
|
|
209
|
+
#
|
|
210
|
+
# ### Requirements → Implementation
|
|
211
|
+
# - `REQ-001`: Parameter expansion → `expand_parameter` method
|
|
212
|
+
# - `REQ-002`: Single character support → `output_type || 'Q'` logic
|
|
213
|
+
# - `REQ-003`: New variable tracking → `NewVariable` struct
|
|
214
|
+
# - `REQ-004`: Backward compatibility → `when 'L', 'Q'` conditions
|
|
215
|
+
#
|
|
216
|
+
# ### Architecture → Code
|
|
217
|
+
# - `ARCH-001`: Strategy pattern → `expand_*_input` methods
|
|
218
|
+
# - `ARCH-002`: Factory pattern → `create_new_variable` method
|
|
219
|
+
# - `ARCH-003`: Template method → `expand_parameter` delegation
|
|
220
|
+
# - `ARCH-004`: Data structures → `NewVariable` struct definition
|
|
221
|
+
#
|
|
222
|
+
# ### Implementation → Tests
|
|
223
|
+
# - `IMPL-001`: Command substitution → `test_command_substitution_codes`
|
|
224
|
+
# - `IMPL-002`: Expression processing → `test_expression_codes`
|
|
225
|
+
# - `IMPL-003`: Literal handling → `test_literal_codes`
|
|
226
|
+
# - `IMPL-004`: Variable references → `test_variable_codes`
|
|
227
|
+
# - `IMPL-005`: Single character → `test_single_character_invocation_codes`
|
|
228
|
+
# - `IMPL-006`: Batch processing → `test_expand_parameters_batch_processing`
|
|
229
|
+
#
|
|
230
|
+
# ### Tests → Code Coverage
|
|
231
|
+
# - `TEST-001`: Basic functionality → `test_expand_parameter_returns_array`
|
|
232
|
+
# - `TEST-002`: Backward compatibility → `test_backward_compatibility_with_q_codes`
|
|
233
|
+
# - `TEST-003`: Mixed combinations → `test_mixed_l_q_codes`
|
|
234
|
+
# - `TEST-004`: Error handling → `test_invalid_invocation_codes`
|
|
235
|
+
# - `TEST-005`: Shell integration → `test_shellwords_escaping`
|
|
236
|
+
# - `TEST-006`: Complete coverage → `test_all_invocation_combinations`
|
|
237
|
+
#
|
|
238
|
+
# ## Token Usage Examples
|
|
239
|
+
#
|
|
240
|
+
# ### In Code Comments
|
|
241
|
+
# ```ruby
|
|
242
|
+
# # REQ-001: Support all 16 invocation combinations
|
|
243
|
+
# def self.expand_parameter(param, invocation, value)
|
|
244
|
+
#
|
|
245
|
+
# # ARCH-001: Strategy pattern for input type handling
|
|
246
|
+
# case input_type
|
|
247
|
+
# when 'C' # Command substitution
|
|
248
|
+
# expand_command_input(param, output_type, value, invocation)
|
|
249
|
+
#
|
|
250
|
+
# # IMPL-001: Command substitution with proper shell syntax
|
|
251
|
+
# when 'C' # :cc= - command string
|
|
252
|
+
# [value, nil]
|
|
253
|
+
# ```
|
|
254
|
+
#
|
|
255
|
+
# ### In Test Descriptions
|
|
256
|
+
# ```ruby
|
|
257
|
+
# # TEST-001: Basic functionality verification
|
|
258
|
+
# def test_expand_parameter_returns_array
|
|
259
|
+
#
|
|
260
|
+
# # IMPL-005: Single character code support
|
|
261
|
+
# def test_single_character_invocation_codes
|
|
262
|
+
# ```
|
|
263
|
+
#
|
|
264
|
+
# =============================================================================
|
|
265
|
+
# USAGE EXAMPLES
|
|
266
|
+
# =============================================================================
|
|
267
|
+
#
|
|
268
|
+
# ## Basic Usage
|
|
269
|
+
# ```ruby
|
|
270
|
+
# require './lib/parameter_expansion'
|
|
271
|
+
#
|
|
272
|
+
# # Single parameter expansion
|
|
273
|
+
# expansion, new_var = ParameterExpansion.expand_parameter("PARAM", "ll", "hello world")
|
|
274
|
+
# # expansion = "hello world"
|
|
275
|
+
# # new_var = nil
|
|
276
|
+
#
|
|
277
|
+
# # Command substitution with new variable
|
|
278
|
+
# expansion, new_var = ParameterExpansion.expand_parameter("PARAM", "ce", "ls -la")
|
|
279
|
+
# # expansion = "${PARAM}"
|
|
280
|
+
# # new_var.name = "PARAM"
|
|
281
|
+
# # new_var.assignment_code = "$(ls -la)"
|
|
282
|
+
# ```
|
|
283
|
+
#
|
|
284
|
+
# ## Batch Processing
|
|
285
|
+
# ```ruby
|
|
286
|
+
# parameters = {
|
|
287
|
+
# "COMMAND" => ["ce", "ls -la"],
|
|
288
|
+
# "TITLE" => ["ll", "My Document"],
|
|
289
|
+
# "VARIABLE" => ["ev", "hello world"]
|
|
290
|
+
# }
|
|
291
|
+
#
|
|
292
|
+
# expansions, new_variables = ParameterExpansion.expand_parameters(parameters)
|
|
293
|
+
# # expansions = {"COMMAND" => "${COMMAND}", "TITLE" => "My Document", ...}
|
|
294
|
+
# # new_variables = [NewVariable objects for variables that need creation]
|
|
295
|
+
# ```
|
|
296
|
+
#
|
|
297
|
+
# ## Single Character Codes
|
|
298
|
+
# ```ruby
|
|
299
|
+
# # These are equivalent:
|
|
300
|
+
# ParameterExpansion.expand_parameter("PARAM", "c", "ls -la")
|
|
301
|
+
# ParameterExpansion.expand_parameter("PARAM", "cq", "ls -la")
|
|
302
|
+
#
|
|
303
|
+
# # These are equivalent:
|
|
304
|
+
# ParameterExpansion.expand_parameter("PARAM", "l", "hello world")
|
|
305
|
+
# ParameterExpansion.expand_parameter("PARAM", "lq", "hello world")
|
|
306
|
+
# ```
|
|
307
|
+
#
|
|
308
|
+
# =============================================================================
|
|
309
|
+
|
|
310
|
+
# Add Shellwords require for proper escaping
|
|
311
|
+
require 'shellwords'
|
|
312
|
+
|
|
313
|
+
# Parameter expansion utility for markdown_exec
|
|
314
|
+
# Handles different invocation types for parameter substitution
|
|
315
|
+
class ParameterExpansion
|
|
316
|
+
# Structure to hold new variable details
|
|
317
|
+
NewVariable = Struct.new(:name, :value, :assignment_code, :invocation, :param) do
|
|
318
|
+
def to_s
|
|
319
|
+
"Variable: #{name} = #{value} (via #{invocation})"
|
|
320
|
+
end
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
# REQ-001: Expands a parameter based on invocation type and value
|
|
324
|
+
# ARCH-003: Template method pattern - delegates to specific handlers
|
|
325
|
+
#
|
|
326
|
+
# @param param [String] The parameter name to be substituted
|
|
327
|
+
# @param invocation [String] The 1 or 2-letter invocation code (e.g., "c", "cc", "ce", "cl", etc.)
|
|
328
|
+
# @param value [String] The value to insert into the expansion
|
|
329
|
+
# @return [Array] Array containing [expansion_string, new_variable_details]
|
|
330
|
+
#
|
|
331
|
+
# Invocation codes:
|
|
332
|
+
# - First letter: Input type (C=command, E=expression, L=literal, V=variable)
|
|
333
|
+
# - Second letter: Output type (C=command, E=expression, L=literal, V=variable)
|
|
334
|
+
# If second letter is missing, defaults to Q (literal)
|
|
335
|
+
#
|
|
336
|
+
# Examples:
|
|
337
|
+
# expand_parameter("PARAM", "cc", "ls -la")
|
|
338
|
+
# # => ["ls -la", nil] (command as command)
|
|
339
|
+
#
|
|
340
|
+
# expand_parameter("PARAM", "ce", "ls -la")
|
|
341
|
+
# # => ["${PARAM}", NewVariable object] (command as expression)
|
|
342
|
+
#
|
|
343
|
+
# expand_parameter("PARAM", "ll", "hello world")
|
|
344
|
+
# # => ["hello world", nil] (literal as literal)
|
|
345
|
+
#
|
|
346
|
+
# expand_parameter("PARAM", "c", "ls -la")
|
|
347
|
+
# # => ["ls -la", nil] (command as literal, defaults to Q)
|
|
348
|
+
#
|
|
349
|
+
# expand_parameter("PARAM", "e", "hello world")
|
|
350
|
+
# # => ["hello world", nil] (expression as literal, defaults to Q)
|
|
351
|
+
def self.expand_parameter(param, invocation, value, unique: nil)
|
|
352
|
+
return [value, nil] if invocation.nil? || invocation.empty?
|
|
353
|
+
|
|
354
|
+
# unique = rand(1e4) if unique.nil?
|
|
355
|
+
if unique.nil?
|
|
356
|
+
# unique from a global counter
|
|
357
|
+
@@unique ||= 0
|
|
358
|
+
@@unique += 1
|
|
359
|
+
unique = @@unique
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
input_type = invocation[0]&.upcase
|
|
363
|
+
output_type = invocation[1]&.upcase || 'Q' # Default to Q if second character missing
|
|
364
|
+
|
|
365
|
+
ww 'input_type:', input_type
|
|
366
|
+
ww 'output_type:', output_type
|
|
367
|
+
# ARCH-001: Strategy pattern for input type handling
|
|
368
|
+
case input_type
|
|
369
|
+
when 'C' # IMPL-001: Command substitution
|
|
370
|
+
expand_command_input(param, output_type, value, invocation, unique: unique)
|
|
371
|
+
when 'E' # IMPL-002: Evaluated expression
|
|
372
|
+
expand_expression_input(param, output_type, value, invocation, unique: unique)
|
|
373
|
+
when 'L', 'Q' # REQ-004: Literal (accept both L and Q for backward compatibility)
|
|
374
|
+
expand_literal_input(param, output_type, value, invocation, unique: unique)
|
|
375
|
+
when 'V' # IMPL-004: Variable reference
|
|
376
|
+
expand_variable_input(param, output_type, value, invocation)
|
|
377
|
+
else
|
|
378
|
+
# Default to raw literal if no valid input type
|
|
379
|
+
[value, nil]
|
|
380
|
+
end
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
# Convenience method that returns just the expansion string (backward compatibility)
|
|
384
|
+
def self.expand_parameter_string(param, invocation, value, unique: rand(1e4))
|
|
385
|
+
result, _new_var = expand_parameter(param, invocation, value, unique: unique)
|
|
386
|
+
result
|
|
387
|
+
end
|
|
388
|
+
|
|
389
|
+
# Process multiple parameters and collect all new variables
|
|
390
|
+
def self.expand_parameters(parameter_hash, unique: rand(1e4))
|
|
391
|
+
expansions = {}
|
|
392
|
+
new_variables = []
|
|
393
|
+
|
|
394
|
+
parameter_hash.each do |param, (invocation, value)|
|
|
395
|
+
expansion, new_var = expand_parameter(param, invocation, value, unique: unique)
|
|
396
|
+
expansions[param] = expansion
|
|
397
|
+
new_variables << new_var if new_var
|
|
398
|
+
end
|
|
399
|
+
|
|
400
|
+
[expansions, new_variables]
|
|
401
|
+
end
|
|
402
|
+
|
|
403
|
+
private
|
|
404
|
+
|
|
405
|
+
# Handle command substitution input (C)
|
|
406
|
+
def self.expand_command_input(param, output_type, value, invocation, unique:)
|
|
407
|
+
case output_type
|
|
408
|
+
when 'C' # :cc= - command string
|
|
409
|
+
[value, nil]
|
|
410
|
+
when 'E' # :ce= - command as shell expressio
|
|
411
|
+
new_var = create_new_variable(param, value, "$(#{value})", invocation, param, unique: unique)
|
|
412
|
+
[new_var.name, new_var]
|
|
413
|
+
when 'L', 'Q' # :cl=, :cq= - output of command evaluation
|
|
414
|
+
new_var = create_new_variable(param, value, "$(#{value})", invocation, param, unique: unique)
|
|
415
|
+
# wrapped so MDE will expand
|
|
416
|
+
[get_variable_reference(new_var.name), new_var]
|
|
417
|
+
when 'V' # :cv= - name of new variable
|
|
418
|
+
new_var = create_new_variable(param, value, "$(#{value})", invocation, param, unique: unique)
|
|
419
|
+
[new_var.name, new_var]
|
|
420
|
+
else
|
|
421
|
+
[value, nil]
|
|
422
|
+
end
|
|
423
|
+
end
|
|
424
|
+
|
|
425
|
+
# Handle evaluated expression input (E)
|
|
426
|
+
def self.expand_expression_input(param, output_type, value, invocation, unique: rand(1e4))
|
|
427
|
+
case output_type
|
|
428
|
+
when 'C' # :ec= - VALUE as a command
|
|
429
|
+
[%(printf %s "#{value}"), nil]
|
|
430
|
+
when 'E' # :ee= - VALUE string
|
|
431
|
+
[value, nil]
|
|
432
|
+
when 'L', 'Q' # :el=, :eq= - shell expression output
|
|
433
|
+
# wrapped so MDE will expand
|
|
434
|
+
[%($(printf %s "#{value}")), nil]
|
|
435
|
+
when 'V' # :ev= - name of new variable
|
|
436
|
+
new_var = create_new_variable(param, value, %(printf %s "#{value}"), invocation, param, unique: unique)
|
|
437
|
+
[new_var.name, new_var]
|
|
438
|
+
# [param, new_var]
|
|
439
|
+
else
|
|
440
|
+
[value, nil]
|
|
441
|
+
end
|
|
442
|
+
end
|
|
443
|
+
|
|
444
|
+
# Handle literal input (L/Q)
|
|
445
|
+
def self.expand_literal_input(param, output_type, value, invocation, unique:)
|
|
446
|
+
case output_type
|
|
447
|
+
when 'C' # :lc= or :qc= - literal VALUE as output
|
|
448
|
+
[%(printf %s "#{value}"), nil]
|
|
449
|
+
when 'E' # :le= or :qe= - literal VALUE as output
|
|
450
|
+
[value, nil]
|
|
451
|
+
when 'L', 'Q' # :ll=, :lq=, :ql=, or :qq= - literal VALUE as output
|
|
452
|
+
[value, nil]
|
|
453
|
+
when 'V' # :lv= or :qv= - name of new variable
|
|
454
|
+
new_var = create_new_variable(param, value, Shellwords.escape(value), invocation, param, unique: unique)
|
|
455
|
+
# [param, new_var]
|
|
456
|
+
[new_var.name, new_var]
|
|
457
|
+
else
|
|
458
|
+
[value, nil]
|
|
459
|
+
end
|
|
460
|
+
end
|
|
461
|
+
|
|
462
|
+
# Handle variable reference input (V)
|
|
463
|
+
def self.expand_variable_input(param, output_type, value, invocation)
|
|
464
|
+
case output_type
|
|
465
|
+
when 'C' # :vc= - variable VALUE expanded
|
|
466
|
+
[%(printf %s "#{get_value_reference(value)}"), nil]
|
|
467
|
+
when 'E' # :ve= - variable VALUE as a shell expr
|
|
468
|
+
[get_value_reference(value), nil]
|
|
469
|
+
when 'L', 'Q' # :vl=, :vq= - variable VALUE expanded
|
|
470
|
+
# wrapped so MDE will expand
|
|
471
|
+
[get_value_reference(value), nil]
|
|
472
|
+
when 'V' # :vv= - VALUE string
|
|
473
|
+
[value, nil]
|
|
474
|
+
else
|
|
475
|
+
[value, nil]
|
|
476
|
+
end
|
|
477
|
+
end
|
|
478
|
+
|
|
479
|
+
# Create a new variable object with details
|
|
480
|
+
def self.create_new_variable(name, value, assignment_code, invocation, param, unique:)
|
|
481
|
+
unique_name = "#{name}_#{unique}"
|
|
482
|
+
NewVariable.new(unique_name, value, assignment_code, invocation, param)
|
|
483
|
+
end
|
|
484
|
+
|
|
485
|
+
# Check if parameter is already wrapped with ${}
|
|
486
|
+
def self.param_wrapped?(param)
|
|
487
|
+
param.start_with?('${') && param.end_with?('}')
|
|
488
|
+
end
|
|
489
|
+
|
|
490
|
+
# Get the appropriate variable reference for the parameter
|
|
491
|
+
def self.get_variable_reference(param)
|
|
492
|
+
param.start_with?('$') ? param : "${#{param}}"
|
|
493
|
+
# param_wrapped?(param) ? param : "${#{param}}"
|
|
494
|
+
end
|
|
495
|
+
|
|
496
|
+
# Check if value is already wrapped with ${}
|
|
497
|
+
def self.value_wrapped?(value)
|
|
498
|
+
value.start_with?('${') && value.end_with?('}')
|
|
499
|
+
end
|
|
500
|
+
|
|
501
|
+
# Get the appropriate variable reference for the value
|
|
502
|
+
def self.get_value_reference(value)
|
|
503
|
+
value_wrapped?(value) ? value : "${#{value}}"
|
|
504
|
+
end
|
|
505
|
+
end
|
|
506
|
+
|
|
507
|
+
return unless $PROGRAM_NAME == __FILE__
|
|
508
|
+
|
|
509
|
+
require 'bundler/setup'
|
|
510
|
+
Bundler.require(:default)
|
|
511
|
+
|
|
512
|
+
require 'minitest/autorun'
|
|
513
|
+
require 'mocha/minitest'
|
|
514
|
+
|
|
515
|
+
# Minitest tests for ParameterExpansion
|
|
516
|
+
class TestParameterExpansion < Minitest::Test
|
|
517
|
+
# TEST-001: Basic functionality verification
|
|
518
|
+
def test_expand_parameter_returns_array
|
|
519
|
+
expansion, new_var = ParameterExpansion.expand_parameter("PARAM", "cc", "ls -la")
|
|
520
|
+
assert_equal "ls -la", expansion
|
|
521
|
+
assert_nil new_var
|
|
522
|
+
end
|
|
523
|
+
|
|
524
|
+
# IMPL-001: Command substitution with proper shell syntax
|
|
525
|
+
def test_command_substitution_codes
|
|
526
|
+
# :cc= - command as command
|
|
527
|
+
expansion, new_var = ParameterExpansion.expand_parameter("PARAM", "cc", "ls -la")
|
|
528
|
+
assert_equal "ls -la", expansion
|
|
529
|
+
assert_nil new_var
|
|
530
|
+
|
|
531
|
+
# :ce= - command as expression
|
|
532
|
+
expansion, new_var = ParameterExpansion.expand_parameter("PARAM", "ce", "ls -la", unique: '')
|
|
533
|
+
assert_equal "PARAM_", expansion
|
|
534
|
+
assert_instance_of ParameterExpansion::NewVariable, new_var
|
|
535
|
+
assert_equal "PARAM_", new_var.name
|
|
536
|
+
assert_equal "ls -la", new_var.value
|
|
537
|
+
assert_equal "$(ls -la)", new_var.assignment_code
|
|
538
|
+
|
|
539
|
+
# :cl= - command as literal
|
|
540
|
+
expansion, new_var = ParameterExpansion.expand_parameter("PARAM", "cl", "ls -la", unique: '')
|
|
541
|
+
assert_equal "PARAM_", expansion
|
|
542
|
+
assert_instance_of ParameterExpansion::NewVariable, new_var
|
|
543
|
+
|
|
544
|
+
# :cv= - command as variable
|
|
545
|
+
expansion, new_var = ParameterExpansion.expand_parameter("PARAM", "cv", "ls -la", unique: '')
|
|
546
|
+
assert_equal "PARAM_", expansion
|
|
547
|
+
assert_instance_of ParameterExpansion::NewVariable, new_var
|
|
548
|
+
end
|
|
549
|
+
|
|
550
|
+
def test_expression_codes
|
|
551
|
+
# :ec= - expression as command
|
|
552
|
+
expansion, new_var = ParameterExpansion.expand_parameter("PARAM", "ec", "hello world")
|
|
553
|
+
assert_equal 'printf %s "hello world"', expansion
|
|
554
|
+
assert_nil new_var
|
|
555
|
+
|
|
556
|
+
# :ee= - expression as expression
|
|
557
|
+
expansion, new_var = ParameterExpansion.expand_parameter("PARAM", "ee", "hello world")
|
|
558
|
+
assert_equal "hello world", expansion
|
|
559
|
+
assert_nil new_var
|
|
560
|
+
|
|
561
|
+
# :el= - expression as literal
|
|
562
|
+
expansion, new_var = ParameterExpansion.expand_parameter("PARAM", "el", "hello world")
|
|
563
|
+
assert_equal '$(printf %s "hello world")', expansion
|
|
564
|
+
assert_nil new_var
|
|
565
|
+
|
|
566
|
+
# :ev= - expression as variable
|
|
567
|
+
expansion, new_var = ParameterExpansion.expand_parameter("PARAM", "ev", "hello world", unique: '')
|
|
568
|
+
assert_equal "PARAM_", expansion
|
|
569
|
+
assert_instance_of ParameterExpansion::NewVariable, new_var
|
|
570
|
+
assert_equal '"hello world"', new_var.assignment_code
|
|
571
|
+
end
|
|
572
|
+
|
|
573
|
+
def test_literal_codes
|
|
574
|
+
# :lc= - literal as command
|
|
575
|
+
expansion, new_var = ParameterExpansion.expand_parameter("PARAM", "lc", "hello world")
|
|
576
|
+
assert_equal 'printf %s "hello world"', expansion
|
|
577
|
+
assert_nil new_var
|
|
578
|
+
|
|
579
|
+
# :le= - literal as expression
|
|
580
|
+
expansion, new_var = ParameterExpansion.expand_parameter("PARAM", "le", "hello world")
|
|
581
|
+
assert_equal "hello world", expansion
|
|
582
|
+
assert_nil new_var
|
|
583
|
+
|
|
584
|
+
# :ll= - literal as literal
|
|
585
|
+
expansion, new_var = ParameterExpansion.expand_parameter("PARAM", "ll", "hello world")
|
|
586
|
+
assert_equal "hello world", expansion
|
|
587
|
+
assert_nil new_var
|
|
588
|
+
|
|
589
|
+
# :lv= - literal as variable
|
|
590
|
+
expansion, new_var = ParameterExpansion.expand_parameter("PARAM", "lv", "hello world", unique: '')
|
|
591
|
+
assert_equal "PARAM_", expansion
|
|
592
|
+
assert_instance_of ParameterExpansion::NewVariable, new_var
|
|
593
|
+
assert_equal "hello\\ world", new_var.assignment_code
|
|
594
|
+
end
|
|
595
|
+
|
|
596
|
+
def test_variable_codes
|
|
597
|
+
# :vc= - variable as command
|
|
598
|
+
expansion, new_var = ParameterExpansion.expand_parameter("PARAM", "vc", "MY_VAR")
|
|
599
|
+
assert_equal 'printf %s "${MY_VAR}"', expansion
|
|
600
|
+
assert_nil new_var
|
|
601
|
+
|
|
602
|
+
# :ve= - variable as expression
|
|
603
|
+
expansion, new_var = ParameterExpansion.expand_parameter("PARAM", "ve", "MY_VAR")
|
|
604
|
+
assert_equal "${MY_VAR}", expansion
|
|
605
|
+
assert_nil new_var
|
|
606
|
+
|
|
607
|
+
# :vl= - variable as literal
|
|
608
|
+
expansion, new_var = ParameterExpansion.expand_parameter("PARAM", "vl", "MY_VAR")
|
|
609
|
+
assert_equal "${MY_VAR}", expansion
|
|
610
|
+
assert_nil new_var
|
|
611
|
+
|
|
612
|
+
# :vv= - variable as variable
|
|
613
|
+
expansion, new_var = ParameterExpansion.expand_parameter("PARAM", "vv", "MY_VAR")
|
|
614
|
+
assert_equal "MY_VAR", expansion
|
|
615
|
+
assert_nil new_var
|
|
616
|
+
end
|
|
617
|
+
|
|
618
|
+
def test_backward_compatibility_with_q_codes
|
|
619
|
+
# Test that old Q codes still work
|
|
620
|
+
expansion, new_var = ParameterExpansion.expand_parameter("PARAM", "qq", "hello world")
|
|
621
|
+
assert_equal "hello world", expansion
|
|
622
|
+
assert_nil new_var
|
|
623
|
+
|
|
624
|
+
expansion, new_var = ParameterExpansion.expand_parameter("PARAM", "qe", "hello world")
|
|
625
|
+
assert_equal "hello world", expansion
|
|
626
|
+
assert_nil new_var
|
|
627
|
+
|
|
628
|
+
expansion, new_var = ParameterExpansion.expand_parameter("PARAM", "qc", "hello world")
|
|
629
|
+
assert_equal 'printf %s "hello world"', expansion
|
|
630
|
+
assert_nil new_var
|
|
631
|
+
|
|
632
|
+
expansion, new_var = ParameterExpansion.expand_parameter("PARAM", "qv", "hello world", unique: '')
|
|
633
|
+
assert_equal "PARAM_", expansion
|
|
634
|
+
assert_instance_of ParameterExpansion::NewVariable, new_var
|
|
635
|
+
end
|
|
636
|
+
|
|
637
|
+
def test_mixed_l_q_codes
|
|
638
|
+
# Test mixed L/Q combinations
|
|
639
|
+
expansion, new_var = ParameterExpansion.expand_parameter("PARAM", "lq", "hello world")
|
|
640
|
+
assert_equal "hello world", expansion
|
|
641
|
+
assert_nil new_var
|
|
642
|
+
|
|
643
|
+
expansion, new_var = ParameterExpansion.expand_parameter("PARAM", "ql", "hello world")
|
|
644
|
+
assert_equal "hello world", expansion
|
|
645
|
+
assert_nil new_var
|
|
646
|
+
end
|
|
647
|
+
|
|
648
|
+
def test_expand_parameter_string_backward_compatibility
|
|
649
|
+
# Test the convenience method
|
|
650
|
+
result = ParameterExpansion.expand_parameter_string("PARAM", "cc", "ls -la")
|
|
651
|
+
assert_equal "ls -la", result
|
|
652
|
+
|
|
653
|
+
result = ParameterExpansion.expand_parameter_string("PARAM", "ce", "ls -la", unique: '')
|
|
654
|
+
assert_equal "PARAM_", result
|
|
655
|
+
end
|
|
656
|
+
|
|
657
|
+
def test_expand_parameters_batch_processing
|
|
658
|
+
parameters = {
|
|
659
|
+
"COMMAND" => ["ce", "ls -la"],
|
|
660
|
+
"TITLE" => ["ll", "My Document"],
|
|
661
|
+
"VARIABLE" => ["ev", "hello world"]
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
expansions, new_variables = ParameterExpansion.expand_parameters(parameters, unique: '')
|
|
665
|
+
|
|
666
|
+
assert_equal 3, expansions.size
|
|
667
|
+
assert_equal "COMMAND_", expansions["COMMAND"]
|
|
668
|
+
assert_equal "My Document", expansions["TITLE"]
|
|
669
|
+
assert_equal "VARIABLE_", expansions["VARIABLE"]
|
|
670
|
+
|
|
671
|
+
assert_equal 2, new_variables.size
|
|
672
|
+
assert_equal "COMMAND_", new_variables[0].name
|
|
673
|
+
assert_equal "ls -la", new_variables[0].value
|
|
674
|
+
assert_equal "VARIABLE_", new_variables[1].name
|
|
675
|
+
assert_equal "hello world", new_variables[1].value
|
|
676
|
+
end
|
|
677
|
+
|
|
678
|
+
def test_new_variable_structure
|
|
679
|
+
expansion, new_var = ParameterExpansion.expand_parameter("PARAM", "ce", "ls -la", unique: '')
|
|
680
|
+
|
|
681
|
+
assert_instance_of ParameterExpansion::NewVariable, new_var
|
|
682
|
+
assert_equal "PARAM_", new_var.name
|
|
683
|
+
assert_equal "ls -la", new_var.value
|
|
684
|
+
assert_equal "$(ls -la)", new_var.assignment_code
|
|
685
|
+
assert_equal "ce", new_var.invocation
|
|
686
|
+
assert_equal "PARAM", new_var.param
|
|
687
|
+
|
|
688
|
+
# Test to_s method
|
|
689
|
+
assert_includes new_var.to_s, "PARAM"
|
|
690
|
+
assert_includes new_var.to_s, "ls -la"
|
|
691
|
+
assert_includes new_var.to_s, "ce"
|
|
692
|
+
end
|
|
693
|
+
|
|
694
|
+
def test_invalid_invocation_codes
|
|
695
|
+
# Test with nil invocation
|
|
696
|
+
expansion, new_var = ParameterExpansion.expand_parameter("PARAM", nil, "value")
|
|
697
|
+
assert_equal "value", expansion
|
|
698
|
+
assert_nil new_var
|
|
699
|
+
|
|
700
|
+
# Test with empty invocation
|
|
701
|
+
expansion, new_var = ParameterExpansion.expand_parameter("PARAM", "", "value")
|
|
702
|
+
assert_equal "value", expansion
|
|
703
|
+
assert_nil new_var
|
|
704
|
+
|
|
705
|
+
# Test with unknown input type
|
|
706
|
+
expansion, new_var = ParameterExpansion.expand_parameter("PARAM", "xx", "value")
|
|
707
|
+
assert_equal "value", expansion
|
|
708
|
+
assert_nil new_var
|
|
709
|
+
end
|
|
710
|
+
|
|
711
|
+
def test_shellwords_escaping
|
|
712
|
+
expansion, new_var = ParameterExpansion.expand_parameter("PARAM", "lv", "hello world with spaces")
|
|
713
|
+
assert_instance_of ParameterExpansion::NewVariable, new_var
|
|
714
|
+
assert_equal "hello\\ world\\ with\\ spaces", new_var.assignment_code
|
|
715
|
+
end
|
|
716
|
+
|
|
717
|
+
# IMPL-005: Single character code support
|
|
718
|
+
def test_single_character_invocation_codes
|
|
719
|
+
# Test single character codes that default to Q (literal)
|
|
720
|
+
|
|
721
|
+
# :c= - command as literal (defaults to Q, equivalent to :cq=)
|
|
722
|
+
expansion, new_var = ParameterExpansion.expand_parameter("PARAM", "c", "ls -la", unique: '')
|
|
723
|
+
assert_equal "PARAM_", expansion
|
|
724
|
+
assert_instance_of ParameterExpansion::NewVariable, new_var
|
|
725
|
+
assert_equal "$(ls -la)", new_var.assignment_code
|
|
726
|
+
|
|
727
|
+
# :e= - expression as literal (defaults to Q, equivalent to :eq=)
|
|
728
|
+
expansion, new_var = ParameterExpansion.expand_parameter("PARAM", "e", "hello world")
|
|
729
|
+
assert_equal '$(printf %s "hello world")', expansion
|
|
730
|
+
assert_nil new_var
|
|
731
|
+
|
|
732
|
+
# :l= - literal as literal (defaults to Q, equivalent to :lq=)
|
|
733
|
+
expansion, new_var = ParameterExpansion.expand_parameter("PARAM", "l", "hello world")
|
|
734
|
+
assert_equal "hello world", expansion
|
|
735
|
+
assert_nil new_var
|
|
736
|
+
|
|
737
|
+
# :v= - variable as literal (defaults to Q, equivalent to :vq=)
|
|
738
|
+
expansion, new_var = ParameterExpansion.expand_parameter("PARAM", "v", "MY_VAR")
|
|
739
|
+
assert_equal "${MY_VAR}", expansion
|
|
740
|
+
assert_nil new_var
|
|
741
|
+
end
|
|
742
|
+
|
|
743
|
+
def test_single_character_equivalence
|
|
744
|
+
# Test that single character codes are equivalent to their Q counterparts
|
|
745
|
+
|
|
746
|
+
# :c= should be equivalent to :cq=
|
|
747
|
+
# , unique: rand(1e4)
|
|
748
|
+
unique = rand(1e4)
|
|
749
|
+
|
|
750
|
+
expansion1, new_var1 = ParameterExpansion.expand_parameter("PARAM", "c", "ls -la", unique: unique)
|
|
751
|
+
expansion2, new_var2 = ParameterExpansion.expand_parameter("PARAM", "cq", "ls -la", unique: unique)
|
|
752
|
+
assert_equal expansion1, expansion2
|
|
753
|
+
if new_var1 && new_var2
|
|
754
|
+
assert_equal new_var1.name, new_var2.name
|
|
755
|
+
assert_equal new_var1.value, new_var2.value
|
|
756
|
+
assert_equal new_var1.assignment_code, new_var2.assignment_code
|
|
757
|
+
else
|
|
758
|
+
assert_nil new_var1
|
|
759
|
+
assert_nil new_var2
|
|
760
|
+
end
|
|
761
|
+
|
|
762
|
+
# :e= should be equivalent to :eq=
|
|
763
|
+
expansion1, new_var1 = ParameterExpansion.expand_parameter("PARAM", "e", "hello world")
|
|
764
|
+
expansion2, new_var2 = ParameterExpansion.expand_parameter("PARAM", "eq", "hello world")
|
|
765
|
+
assert_equal expansion1, expansion2
|
|
766
|
+
if new_var1 && new_var2
|
|
767
|
+
assert_equal new_var1.name, new_var2.name
|
|
768
|
+
assert_equal new_var1.value, new_var2.value
|
|
769
|
+
assert_equal new_var1.assignment_code, new_var2.assignment_code
|
|
770
|
+
else
|
|
771
|
+
assert_nil new_var1
|
|
772
|
+
assert_nil new_var2
|
|
773
|
+
end
|
|
774
|
+
|
|
775
|
+
# :l= should be equivalent to :lq=
|
|
776
|
+
expansion1, new_var1 = ParameterExpansion.expand_parameter("PARAM", "l", "hello world")
|
|
777
|
+
expansion2, new_var2 = ParameterExpansion.expand_parameter("PARAM", "lq", "hello world")
|
|
778
|
+
assert_equal expansion1, expansion2
|
|
779
|
+
if new_var1 && new_var2
|
|
780
|
+
assert_equal new_var1.name, new_var2.name
|
|
781
|
+
assert_equal new_var1.value, new_var2.value
|
|
782
|
+
assert_equal new_var1.assignment_code, new_var2.assignment_code
|
|
783
|
+
else
|
|
784
|
+
assert_nil new_var1
|
|
785
|
+
assert_nil new_var2
|
|
786
|
+
end
|
|
787
|
+
|
|
788
|
+
# :v= should be equivalent to :vq=
|
|
789
|
+
expansion1, new_var1 = ParameterExpansion.expand_parameter("PARAM", "v", "MY_VAR")
|
|
790
|
+
expansion2, new_var2 = ParameterExpansion.expand_parameter("PARAM", "vq", "MY_VAR")
|
|
791
|
+
assert_equal expansion1, expansion2
|
|
792
|
+
if new_var1 && new_var2
|
|
793
|
+
assert_equal new_var1.name, new_var2.name
|
|
794
|
+
assert_equal new_var1.value, new_var2.value
|
|
795
|
+
assert_equal new_var1.assignment_code, new_var2.assignment_code
|
|
796
|
+
else
|
|
797
|
+
assert_nil new_var1
|
|
798
|
+
assert_nil new_var2
|
|
799
|
+
end
|
|
800
|
+
end
|
|
801
|
+
|
|
802
|
+
def test_all_invocation_combinations
|
|
803
|
+
# Test all 16 combinations from the documentation
|
|
804
|
+
combinations = %w[cc ce cl cv ec ee el ev lc le ll lv vc ve vl vv]
|
|
805
|
+
|
|
806
|
+
combinations.each do |invocation|
|
|
807
|
+
expansion, new_var = ParameterExpansion.expand_parameter("PARAM", invocation, "test_value")
|
|
808
|
+
assert_instance_of String, expansion
|
|
809
|
+
# new_var can be nil or NewVariable object
|
|
810
|
+
assert(new_var.nil? || new_var.is_a?(ParameterExpansion::NewVariable))
|
|
811
|
+
end
|
|
812
|
+
end
|
|
813
|
+
|
|
814
|
+
def test_single_character_combinations
|
|
815
|
+
# Test all single character combinations
|
|
816
|
+
single_chars = %w[c e l v]
|
|
817
|
+
|
|
818
|
+
single_chars.each do |invocation|
|
|
819
|
+
expansion, new_var = ParameterExpansion.expand_parameter("PARAM", invocation, "test_value")
|
|
820
|
+
assert_instance_of String, expansion
|
|
821
|
+
# new_var can be nil or NewVariable object
|
|
822
|
+
assert(new_var.nil? || new_var.is_a?(ParameterExpansion::NewVariable))
|
|
823
|
+
end
|
|
824
|
+
end
|
|
825
|
+
|
|
826
|
+
def test_pre_wrapped_parameters
|
|
827
|
+
# Test that parameters already wrapped with ${} are used as-is
|
|
828
|
+
|
|
829
|
+
# Test command substitution with pre-wrapped parameter
|
|
830
|
+
expansion, new_var = ParameterExpansion.expand_parameter("${MY_VAR}", "ce", "ls -la", unique: '')
|
|
831
|
+
assert_equal "${MY_VAR}_", expansion
|
|
832
|
+
assert_instance_of ParameterExpansion::NewVariable, new_var
|
|
833
|
+
assert_equal "${MY_VAR}_", new_var.name
|
|
834
|
+
assert_equal "ls -la", new_var.value
|
|
835
|
+
assert_equal "$(ls -la)", new_var.assignment_code
|
|
836
|
+
|
|
837
|
+
# Test command substitution with pre-wrapped parameter (literal output)
|
|
838
|
+
expansion, new_var = ParameterExpansion.expand_parameter("${MY_VAR}", "cl", "ls -la", unique: '')
|
|
839
|
+
assert_equal "${MY_VAR}_", expansion
|
|
840
|
+
assert_instance_of ParameterExpansion::NewVariable, new_var
|
|
841
|
+
assert_equal "${MY_VAR}_", new_var.name
|
|
842
|
+
|
|
843
|
+
# Test that regular parameters still get wrapped
|
|
844
|
+
expansion, new_var = ParameterExpansion.expand_parameter("MY_VAR", "ce", "ls -la", unique: '')
|
|
845
|
+
assert_equal "MY_VAR_", expansion
|
|
846
|
+
assert_instance_of ParameterExpansion::NewVariable, new_var
|
|
847
|
+
assert_equal "MY_VAR_", new_var.name
|
|
848
|
+
|
|
849
|
+
# Test with complex pre-wrapped parameter
|
|
850
|
+
expansion, new_var = ParameterExpansion.expand_parameter("${COMPLEX_VAR_NAME}", "ce", "echo hello", unique: '')
|
|
851
|
+
assert_equal "${COMPLEX_VAR_NAME}_", expansion
|
|
852
|
+
assert_instance_of ParameterExpansion::NewVariable, new_var
|
|
853
|
+
assert_equal "${COMPLEX_VAR_NAME}_", new_var.name
|
|
854
|
+
end
|
|
855
|
+
|
|
856
|
+
def test_param_wrapped_helper_methods
|
|
857
|
+
# Test the helper methods directly
|
|
858
|
+
assert ParameterExpansion.param_wrapped?("${MY_VAR}")
|
|
859
|
+
assert ParameterExpansion.param_wrapped?("${COMPLEX_VAR}")
|
|
860
|
+
refute ParameterExpansion.param_wrapped?("MY_VAR")
|
|
861
|
+
refute ParameterExpansion.param_wrapped?("${MY_VAR")
|
|
862
|
+
refute ParameterExpansion.param_wrapped?("MY_VAR}")
|
|
863
|
+
refute ParameterExpansion.param_wrapped?("")
|
|
864
|
+
|
|
865
|
+
# Test get_variable_reference method
|
|
866
|
+
assert_equal "${MY_VAR}", ParameterExpansion.get_variable_reference("MY_VAR")
|
|
867
|
+
assert_equal "${COMPLEX_VAR}", ParameterExpansion.get_variable_reference("${COMPLEX_VAR}")
|
|
868
|
+
assert_equal "${${NESTED}}", ParameterExpansion.get_variable_reference("${${NESTED}}")
|
|
869
|
+
end
|
|
870
|
+
|
|
871
|
+
def test_pre_wrapped_values
|
|
872
|
+
# Test that values already wrapped with ${} are used as-is
|
|
873
|
+
|
|
874
|
+
# Test variable reference with pre-wrapped value
|
|
875
|
+
expansion, new_var = ParameterExpansion.expand_parameter("PARAM", "ve", "${MY_VAR}")
|
|
876
|
+
assert_equal "${MY_VAR}", expansion
|
|
877
|
+
assert_nil new_var
|
|
878
|
+
|
|
879
|
+
# Test variable reference with pre-wrapped value (literal output)
|
|
880
|
+
expansion, new_var = ParameterExpansion.expand_parameter("PARAM", "vl", "${MY_VAR}")
|
|
881
|
+
assert_equal "${MY_VAR}", expansion
|
|
882
|
+
assert_nil new_var
|
|
883
|
+
|
|
884
|
+
# Test variable reference with pre-wrapped value (command output)
|
|
885
|
+
expansion, new_var = ParameterExpansion.expand_parameter("PARAM", "vc", "${MY_VAR}")
|
|
886
|
+
assert_equal 'printf %s "${MY_VAR}"', expansion
|
|
887
|
+
assert_nil new_var
|
|
888
|
+
|
|
889
|
+
# Test that regular values still get wrapped
|
|
890
|
+
expansion, new_var = ParameterExpansion.expand_parameter("PARAM", "ve", "MY_VAR")
|
|
891
|
+
assert_equal "${MY_VAR}", expansion
|
|
892
|
+
assert_nil new_var
|
|
893
|
+
|
|
894
|
+
# Test with complex pre-wrapped value
|
|
895
|
+
expansion, new_var = ParameterExpansion.expand_parameter("PARAM", "ve", "${COMPLEX_VAR_NAME}")
|
|
896
|
+
assert_equal "${COMPLEX_VAR_NAME}", expansion
|
|
897
|
+
assert_nil new_var
|
|
898
|
+
end
|
|
899
|
+
|
|
900
|
+
def test_value_wrapped_helper_methods
|
|
901
|
+
# Test the value helper methods directly
|
|
902
|
+
assert ParameterExpansion.value_wrapped?("${MY_VAR}")
|
|
903
|
+
assert ParameterExpansion.value_wrapped?("${COMPLEX_VAR}")
|
|
904
|
+
refute ParameterExpansion.value_wrapped?("MY_VAR")
|
|
905
|
+
refute ParameterExpansion.value_wrapped?("${MY_VAR")
|
|
906
|
+
refute ParameterExpansion.value_wrapped?("MY_VAR}")
|
|
907
|
+
refute ParameterExpansion.value_wrapped?("")
|
|
908
|
+
|
|
909
|
+
# Test get_value_reference method
|
|
910
|
+
assert_equal "${MY_VAR}", ParameterExpansion.get_value_reference("MY_VAR")
|
|
911
|
+
assert_equal "${COMPLEX_VAR}", ParameterExpansion.get_value_reference("${COMPLEX_VAR}")
|
|
912
|
+
assert_equal "${${NESTED}}", ParameterExpansion.get_value_reference("${${NESTED}}")
|
|
913
|
+
end
|
|
914
|
+
end
|
|
915
|
+
|
|
916
|
+
__END__
|
|
917
|
+
To run minitest tests: `./lib/parameter_expansion.rb`
|
|
918
|
+
With verbose output: `./lib/parameter_expansion.rb --verbose`
|