Class: CachedNestedFileReader
- Includes:
- Exceptions
- Defined in:
- lib/cached_nested_file_reader.rb
Overview
The CachedNestedFileReader class provides functionality to read file lines with the ability to process ‘#import filename’ directives. When such a directive is encountered in a file, the corresponding ‘filename’ is read and its contents are inserted at that location. This class caches read files to avoid re-reading the same file multiple times. It allows clients to read lines with or without providing a block.
Class Method Summary collapse
Instance Method Summary collapse
- #error_handler(name = '', opts = {}) ⇒ Object
-
#initialize(import_directive_line_pattern:, import_directive_parameter_scan:, import_parameter_variable_assignment:, shell:, shell_block_name:, symbol_command_substitution:, symbol_evaluated_expression:, symbol_force_quoted_literal:, symbol_raw_literal:, symbol_variable_reference:, hide_shebang: true) ⇒ CachedNestedFileReader
constructor
A new instance of CachedNestedFileReader.
-
#readlines(filename, depth = 0, context: '', import_paths: nil, indention: '', substitutions: {}, use_template_delimiters: false, clear_cache: true, read_cache: false, &block) ⇒ Object
yield each line to the block return the processed lines.
Methods included from Exceptions
Constructor Details
#initialize(import_directive_line_pattern:, import_directive_parameter_scan:, import_parameter_variable_assignment:, shell:, shell_block_name:, symbol_command_substitution:, symbol_evaluated_expression:, symbol_force_quoted_literal:, symbol_raw_literal:, symbol_variable_reference:, hide_shebang: true) ⇒ CachedNestedFileReader
Returns a new instance of CachedNestedFileReader.
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
# File 'lib/cached_nested_file_reader.rb', line 26 def initialize( import_directive_line_pattern:, import_directive_parameter_scan:, import_parameter_variable_assignment:, shell:, shell_block_name:, symbol_command_substitution:, symbol_evaluated_expression:, symbol_force_quoted_literal:, symbol_raw_literal:, symbol_variable_reference:, hide_shebang: true ) @file_cache = {} @import_directive_line_pattern = import_directive_line_pattern @import_directive_parameter_scan = import_directive_parameter_scan @import_parameter_variable_assignment = import_parameter_variable_assignment @shell = shell @shell_block_name = shell_block_name @symbol_command_substitution = symbol_command_substitution @symbol_evaluated_expression = symbol_evaluated_expression @symbol_force_quoted_literal = symbol_force_quoted_literal @symbol_raw_literal = symbol_raw_literal @symbol_variable_reference = symbol_variable_reference @hide_shebang = hide_shebang end |
Class Method Details
.warn_format(name, message, opts = {}) ⇒ Object
61 62 63 64 65 66 |
# File 'lib/cached_nested_file_reader.rb', line 61 def self.warn_format(name, , opts = {}) Exceptions.warn_format( "CachedNestedFileReader.#{name} -- #{message}", opts ) end |
Instance Method Details
#error_handler(name = '', opts = {}) ⇒ Object
53 54 55 56 57 58 59 |
# File 'lib/cached_nested_file_reader.rb', line 53 def error_handler(name = '', opts = {}) Exceptions.error_handler( "CachedNestedFileReader.#{name} -- #{$!}", caller.deref(6), opts ) end |
#readlines(filename, depth = 0, context: '', import_paths: nil, indention: '', substitutions: {}, use_template_delimiters: false, clear_cache: true, read_cache: false, &block) ⇒ Object
yield each line to the block return the processed lines
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 |
# File 'lib/cached_nested_file_reader.rb', line 70 def readlines( filename, depth = 0, context: '', import_paths: nil, indention: '', substitutions: {}, use_template_delimiters: false, clear_cache: true, read_cache: false, &block ) # clear cache if requested @file_cache.clear if clear_cache cache_key = build_cache_key(filename, substitutions) if @file_cache.key?(cache_key) return ["# dup #{cache_key}"] unless read_cache @file_cache[cache_key].each(&block) if block return @file_cache[cache_key] # do not return duplicates per filename and substitutions # return an indicator that the file was already read end raise Errno::ENOENT, filename unless filename directory_path = File.dirname(filename) processed_lines = [] continued_line = nil # continued import directive File.readlines(filename, chomp: true).each.with_index do |segment, ind| wwt :readline, 'depth:', depth, 'filename:', filename, 'ind:', ind, 'segment:', segment # [REQ:SHEBANG_HIDING] Filter shebang lines from processed output # [IMPL:SHEBANG_FILTERING] [ARCH:SHEBANG_EXTRACTION] [REQ:SHEBANG_HIDING] # first line is a shebang line if it starts with '#!' next if @hide_shebang && is_shebang_line?(segment, ind == 0) if continued_line || (Regexp.new(@import_directive_line_pattern) =~ segment) line = (continued_line || '') + segment # if segment ends in a continuation, prepend to next line if line.end_with?('\\') continued_line = line.chomp('\\') next end continued_line = nil # apply substitutions to the @import line line_sub1 = apply_line_substitutions(line, substitutions, use_template_delimiters) # parse the @import line Regexp.new(@import_directive_line_pattern) =~ line_sub1 name_strip = $~[:name].strip params_string = $~[:params] || '' import_indention = indention + $~[:indention] # Parse parameters for text substitution import_substitutions, add_code = parse_import_params(params_string) if add_code # strings as NestedLines add_lines = add_code.map.with_index do |line2, ind2| nested_line = NestedLine.new( line2, depth + 1, import_indention, filename, ind2 ) block&.call(nested_line) nested_line end ww 'add_lines:', add_lines processed_lines += add_lines end merged_substitutions = substitutions.merge(import_substitutions) included_file_path = if name_strip =~ %r{^/} name_strip elsif import_paths find_files(name_strip, import_paths + [directory_path])&.first else File.join(directory_path, name_strip) end raise Errno::ENOENT, name_strip unless included_file_path # Create a cache key for the imported file that includes both filename and parameters imported_cache_key = build_import_cache_key(included_file_path, name_strip, params_string, merged_substitutions) # Check if we've already loaded this specific import if @file_cache.key?(imported_cache_key) imported_lines = @file_cache[imported_cache_key] else imported_lines = readlines( included_file_path, depth + 1, context: "#{filename}:#{ind + 1}", import_paths: import_paths, indention: import_indention, substitutions: merged_substitutions, use_template_delimiters: use_template_delimiters, clear_cache: false, &block ) # Cache the imported lines with the specific import cache key @file_cache[imported_cache_key] = imported_lines end # Apply text substitutions to imported content processed_imported_lines = apply_substitutions( imported_lines, import_substitutions, use_template_delimiters ) processed_lines += processed_imported_lines else # Apply substitutions to the current line substituted_line = apply_line_substitutions(segment, substitutions, use_template_delimiters) nested_line = NestedLine.new(substituted_line, depth, indention, filename, ind) processed_lines.push(nested_line) block&.call(nested_line) end end wwt :read_document_code, 'processed_lines:', processed_lines @file_cache[cache_key] = processed_lines rescue Errno::ENOENT => err CachedNestedFileReader.warn_format( 'readlines', "#{err} @@ #{context}", { abort: true } ) rescue StandardError wwe $! end |