Method: ClassLoader.load

Defined in:
lib/class_loader/class_loader.rb

.load(namespace, const) ⇒ Object

Hierarchically searching for class file, according to modules hierarchy.

For example, let’s suppose that C class defined in ‘/lib/a/c.rb’ file, and we referencing it in the A::B namespace, like this - A::B::C, the following files will be checked:

  • ‘/lib/a/b/c.rb’ - there’s nothing, moving up in hierarchy.

  • ‘/lib/a/c.rb’ - got and load it.



21
22
23
24
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
63
64
65
66
67
68
69
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
# File 'lib/class_loader/class_loader.rb', line 21

def load namespace, const
  monitor.synchronize do
    original_namespace = namespace
    namespace = nil if namespace == Object or namespace == Module
    target_namespace = namespace

    # Need this hack to work with anonymous classes.
    namespace = eval "#{name_hack(namespace)}" if namespace

    # Hierarchically searching for class name.
    hierarchy = {}
    begin
      class_name = namespace ? "#{namespace.name}::#{const}" : const.to_s
      class_file_name = get_file_name class_name
      binding = namespace || Object
      hierarchy[class_file_name] = binding

      # Skip some constants.
      return nil if SKIP.any?{|c| class_name == c or class_name.include?("#{c}::")}

      # Trying to load class file, if its exist.
      loaded = begin
        require class_file_name
        true
      rescue LoadError => e
        # Not the best way - hardcoding error messages, but it's the fastest way
        # to check existence of file & load it.
        unless e.message =~ /no such file.*#{Regexp.escape(class_file_name)}/
          raise e.class, e.message, filter_backtrace(e.backtrace)
        end
        false
      end

      if loaded
        # Checking that class hasn't been loaded previously, sometimes it may be caused by
        # weird class definition code.
        if loaded_classes.include? class_name
          msg = "something wrong with '#{const}' referenced from '#{original_namespace}' scope!"
          raise NameError, msg, filter_backtrace(caller)
        end

        # Checking that class defined in correct namespace, not the another one.
        unless binding.const_defined? const, false
          msg = "class name #{class_name} doesn't correspond to file name '#{class_file_name}'!"
          raise NameError, msg, filter_backtrace(caller)
        end

        # Getting the class itself.
        klass = binding.const_get const, false

        # Firing after callbacks.
        if callbacks = after_callbacks[klass.name] then callbacks.each{|c| c.call klass} end

        loaded_classes[class_name] = klass

        return klass
      end

      # Moving to higher namespace.
      global_also_tried = namespace == nil
      namespace = Module.namespace_for namespace.name if namespace
    end until global_also_tried

    # If file not found trying to find directory and evaluate it as a module.
    hierarchy.each do |path, binding|
      $LOAD_PATH.each do |base|
        next unless File.directory? File.join(base, path)
        amodule = Module.new
        binding.const_set const, amodule
        return amodule
      end
    end

    return nil
  end
end