Class: DeepCover::AutoloadTracker

Inherits:
Object
  • Object
show all
Defined in:
lib/deep_cover/autoload_tracker.rb

Defined Under Namespace

Classes: AutoloadEntry

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeAutoloadTracker

Returns a new instance of AutoloadTracker.



27
28
29
30
# File 'lib/deep_cover/autoload_tracker.rb', line 27

def initialize
  @autoloads_by_basename = {}
  @interceptor_files_by_path = {}
end

Class Attribute Details

.warned_for_frozen_moduleObject

Returns the value of attribute warned_for_frozen_module.



112
113
114
# File 'lib/deep_cover/autoload_tracker.rb', line 112

def warned_for_frozen_module
  @warned_for_frozen_module
end

Instance Attribute Details

#autoloads_by_basenameObject (readonly)

Returns the value of attribute autoloads_by_basename.



26
27
28
# File 'lib/deep_cover/autoload_tracker.rb', line 26

def autoloads_by_basename
  @autoloads_by_basename
end

#interceptor_files_by_pathObject (readonly)

Returns the value of attribute interceptor_files_by_path.



26
27
28
# File 'lib/deep_cover/autoload_tracker.rb', line 26

def interceptor_files_by_path
  @interceptor_files_by_path
end

Class Method Details

.warn_frozen_module(mod) ⇒ Object

Using frozen modules/classes is almost unheard of, but a warning makes things easier if someone does it



117
118
119
120
121
122
# File 'lib/deep_cover/autoload_tracker.rb', line 117

def self.warn_frozen_module(mod)
  return if warned_for_frozen_module
  self.warned_for_frozen_module ||= true
  warn "There is an autoload on a frozen module/class: #{mod}, DeepCover cannot handle those, failure is probable. " \
       "This warning won't be displayed again (even for different module/class)"
end

Instance Method Details

#autoload_path_for(mod, name, path) ⇒ Object



32
33
34
35
36
37
38
39
40
# File 'lib/deep_cover/autoload_tracker.rb', line 32

def autoload_path_for(mod, name, path)
  interceptor_path = setup_interceptor_for(mod, name, path)

  if DeepCover.custom_requirer.is_being_required?(path)
    already_loaded_feature
  else
    interceptor_path
  end
end

#initialize_autoloaded_paths(mods = ObjectSpace.each_object(Module)) ⇒ Object

This is only used on MRI, so ObjectSpace is alright.



71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/deep_cover/autoload_tracker.rb', line 71

def initialize_autoloaded_paths(mods = ObjectSpace.each_object(Module)) # &do_autoload_block
  mods.each do |mod|
    # Module's constants are shared with Object. But if you set autoloads directly on Module, they
    # appear on multiple classes. So just skip, Object will take care of those.
    next if mod == Module

    if mod.frozen?
      if mod.constants.any? { |name| mod.autoload?(name) }
        self.class.warn_frozen_module(mod)
      end
      next
    end

    mod.constants.each do |name|
      path = mod.autoload?(name)
      next unless path
      interceptor_path = setup_interceptor_for(mod, name, path)
      yield mod, name, interceptor_path
    end
  end
end

#possible_autoload_target?(requested_path) ⇒ Boolean

Returns:

  • (Boolean)


42
43
44
45
46
# File 'lib/deep_cover/autoload_tracker.rb', line 42

def possible_autoload_target?(requested_path)
  basename = basename_without_extension(requested_path)
  autoloads = @autoloads_by_basename[basename]
  autoloads && !autoloads.empty?
end

#remove_interceptorsObject

We need to remove the interceptor hooks, otherwise, the problem if manually requiring something that is autoloaded will cause issues.



95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/deep_cover/autoload_tracker.rb', line 95

def remove_interceptors # &do_autoload_block
  @autoloads_by_basename.each do |basename, entries|
    entries.each do |entry|
      mod = entry.mod_if_available
      next unless mod
      # Module's constants are shared with Object. But if you set autoloads directly on Module, they
      # appear on multiple classes. So just skip, Object will take care of those.
      next if mod == Module
      yield mod, entry.name, entry.target_path
    end
  end

  @autoloaded_paths = {}
  @interceptor_files_by_path = {}
end

#wrap_require(requested_path, absolute_path_found) ⇒ Object

&block



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/deep_cover/autoload_tracker.rb', line 48

def wrap_require(requested_path, absolute_path_found) # &block
  entries = entries_for_target(requested_path, absolute_path_found)

  begin
    entries.each do |entry|
      mod = entry.mod_if_available
      next unless mod
      mod.autoload_without_deep_cover(entry.name, already_loaded_feature)
    end

    yield
  ensure
    entries = entries_for_target(requested_path, absolute_path_found)
    entries.each do |entry|
      mod = entry.mod_if_available
      next unless mod
      # Putting the autoloads back back since we couldn't complete the require
      mod.autoload_without_deep_cover(entry.name, entry.interceptor_path)
    end
  end
end