Class: DeepCover::CustomRequirer
- Inherits:
-
Object
- Object
- DeepCover::CustomRequirer
- Defined in:
- lib/deep_cover/custom_requirer.rb
Instance Attribute Summary collapse
-
#filter ⇒ Object
readonly
Returns the value of attribute filter.
-
#load_paths ⇒ Object
readonly
Returns the value of attribute load_paths.
-
#loaded_features ⇒ Object
readonly
Returns the value of attribute loaded_features.
Instance Method Summary collapse
-
#initialize(load_paths: $LOAD_PATH, loaded_features: $LOADED_FEATURES, &filter) ⇒ CustomRequirer
constructor
A new instance of CustomRequirer.
- #is_being_required?(path) ⇒ Boolean
-
#load(path) ⇒ Object
Homemade #load to be able to instrument the code before it gets executed.
-
#require(path) ⇒ Object
Homemade #require to be able to instrument the code before it gets executed.
-
#resolve_path(path, try_extensions: true) ⇒ Object
Returns a path to an existing file or nil if none can be found.
Constructor Details
#initialize(load_paths: $LOAD_PATH, loaded_features: $LOADED_FEATURES, &filter) ⇒ CustomRequirer
Returns a new instance of CustomRequirer.
6 7 8 9 10 11 12 13 14 15 16 17 |
# File 'lib/deep_cover/custom_requirer.rb', line 6 def initialize(load_paths: $LOAD_PATH, loaded_features: $LOADED_FEATURES, &filter) @load_paths = load_paths @loaded_features = loaded_features @filter = filter @paths_being_required = Set.new # A Set of the loaded_features for faster access @loaded_features_set = Set.new # A dup of the loaded_features as they are expected to be for the Set to be valid # If this is different from loaded_features, the set should be refreshed @duped_loaded_features_used_for_set = [] end |
Instance Attribute Details
#filter ⇒ Object (readonly)
Returns the value of attribute filter.
5 6 7 |
# File 'lib/deep_cover/custom_requirer.rb', line 5 def filter @filter end |
#load_paths ⇒ Object (readonly)
Returns the value of attribute load_paths.
5 6 7 |
# File 'lib/deep_cover/custom_requirer.rb', line 5 def load_paths @load_paths end |
#loaded_features ⇒ Object (readonly)
Returns the value of attribute loaded_features.
5 6 7 |
# File 'lib/deep_cover/custom_requirer.rb', line 5 def loaded_features @loaded_features end |
Instance Method Details
#is_being_required?(path) ⇒ Boolean
130 131 132 133 |
# File 'lib/deep_cover/custom_requirer.rb', line 130 def is_being_required?(path) found_path = resolve_path(path) @paths_being_required.include?(found_path) end |
#load(path) ⇒ Object
Homemade #load to be able to instrument the code before it gets executed. Note, this doesn’t support the ‘wrap` parameter that ruby’s #load has. Same yield/return behavior as CustomRequirer#require, except that it cannot return false #load doesn’t care about a file already being executed.
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 |
# File 'lib/deep_cover/custom_requirer.rb', line 111 def load(path) # &fallback_block path = path.to_s found_path = resolve_path(path, try_extensions: false) if found_path.nil? # #load has a final fallback of always trying relative to current work directory possible_path = File.absolute_path(path) found_path = possible_path if File.exist?(possible_path) end return yield(:not_found) unless found_path return yield(:not_in_covered_paths) unless DeepCover.tracked_file_path?(found_path) cover_and_execute(found_path) { |reason| return yield(reason) } true end |
#require(path) ⇒ Object
Homemade #require to be able to instrument the code before it gets executed. Returns true when everything went right. (Same as regular ruby) Returns false when the found file was already required. (Same as regular ruby) Calls &fallback_block with the reason as parameter if the work couldn’t be done. The possible reasons are:
- :not_found if the file couldn't be found.
- :not_in_covered_paths if the file is not in the paths to cover
- :cover_failed if DeepCover couldn't apply instrumentation the file found.
- :not_supported for files that are not supported (such as .so files)
- :skipped if the filter block returned `true`
Exceptions raised by the required code bubble up as normal, except for SyntaxError, which is turned into a :cover_failed which calls the fallback_block.
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 |
# File 'lib/deep_cover/custom_requirer.rb', line 76 def require(path) # &fallback_block path = path.to_s found_path = resolve_path(path) if found_path return false if @loaded_features.include?(found_path) return false if @paths_being_required.include?(found_path) end DeepCover.autoload_tracker.wrap_require(path, found_path) do begin # Either a problem with resolve_path, or a gem that will be added to the load_path by RubyGems return yield(:not_found) unless found_path @paths_being_required.add(found_path) return yield(:not_in_covered_paths) unless DeepCover.tracked_file_path?(found_path) return yield(:not_supported) if REQUIRABLE_EXTENSIONS[File.extname(found_path)] == :native_extension return yield(:skipped) if filter && filter.call(found_path) cover_and_execute(found_path) { |reason| return yield(reason) } @loaded_features << found_path ensure @paths_being_required.delete(found_path) add_last_loaded_feature_to_set end end true end |
#resolve_path(path, try_extensions: true) ⇒ Object
Returns a path to an existing file or nil if none can be found. The search follows how ruby search for files using the $LOAD_PATH, but limits those it checks based on the LoadPathsSubset.
An absolute path is returned directly if it exists, otherwise nil is returned without searching anywhere else.
25 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 52 53 54 55 56 57 58 59 60 61 62 |
# File 'lib/deep_cover/custom_requirer.rb', line 25 def resolve_path(path, try_extensions: true) extensions_to_try = if try_extensions && !REQUIRABLE_EXTENSIONS.include?(File.extname(path)) REQUIRABLE_EXTENSION_KEYS else [''] end abs_path = File.absolute_path(path) path = abs_path if path.start_with?('./', '../') paths_with_ext = extensions_to_try.map { |ext| path + ext } refresh_loaded_features_set # Doing this check in every case instead of only for absolute_path because ruby has some # built-in $LOADED_FEATURES which aren't an absolute path. Ex: enumerator.so, thread.rb path_from_loaded_features = first_path_from_loaded_features_set(paths_with_ext) return path_from_loaded_features if path_from_loaded_features if path == abs_path paths_with_ext.each do |path_with_ext| return path_with_ext if File.exist?(path_with_ext) end else possible_paths = paths_with_load_paths(paths_with_ext) path_from_loaded_features = first_path_from_loaded_features_set(possible_paths) return path_from_loaded_features if path_from_loaded_features possible_paths.each do |possible_path| next unless File.exist?(possible_path) # Ruby 2.5 changed some behaviors of require related to symlinks in $LOAD_PATH # https://bugs.ruby-lang.org/issues/10222 return File.realpath(possible_path) if RUBY_VERSION >= '2.5' return possible_path end end nil end |