summaryrefslogtreecommitdiff
path: root/spec/ruby/core/kernel/shared/load.rb
blob: 120619abef6e936580a281fdabd8ad7e8c781752 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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
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
main = self

describe :kernel_load, shared: true do
  before :each do
    CodeLoadingSpecs.spec_setup
    @path = File.expand_path "load_fixture.rb", CODE_LOADING_DIR
  end

  after :each do
    CodeLoadingSpecs.spec_cleanup
  end

  it "loads a non-extensioned file as a Ruby source file" do
    path = File.expand_path "load_fixture", CODE_LOADING_DIR
    @object.load(path).should be_true
    ScratchPad.recorded.should == [:no_ext]
  end

  it "loads a non .rb extensioned file as a Ruby source file" do
    path = File.expand_path "load_fixture.ext", CODE_LOADING_DIR
    @object.load(path).should be_true
    ScratchPad.recorded.should == [:no_rb_ext]
  end

  it "loads from the current working directory" do
    Dir.chdir CODE_LOADING_DIR do
      @object.load("load_fixture.rb").should be_true
      ScratchPad.recorded.should == [:loaded]
    end
  end

  it "loads a file that recursively requires itself" do
    path = File.expand_path "recursive_require_fixture.rb", CODE_LOADING_DIR
    -> {
      @object.load(path).should be_true
    }.should complain(/circular require considered harmful/, verbose: true)
    ScratchPad.recorded.should == [:loaded, :loaded]
  end

  it "loads a file that recursively loads itself" do
    path = File.expand_path "recursive_load_fixture.rb", CODE_LOADING_DIR
    @object.load(path).should be_true
    ScratchPad.recorded.should == [:loaded, :loaded]
  end

  it "loads a file each time the method is called" do
    @object.load(@path).should be_true
    @object.load(@path).should be_true
    ScratchPad.recorded.should == [:loaded, :loaded]
  end

  it "loads a file even when the name appears in $LOADED_FEATURES" do
    $LOADED_FEATURES << @path
    @object.load(@path).should be_true
    ScratchPad.recorded.should == [:loaded]
  end

  it "loads a file that has been loaded by #require" do
    @object.require(@path).should be_true
    @object.load(@path).should be_true
    ScratchPad.recorded.should == [:loaded, :loaded]
  end

  it "loads file even after $LOAD_PATH change" do
    $LOAD_PATH << CODE_LOADING_DIR
    @object.load("load_fixture.rb").should be_true
    $LOAD_PATH.unshift CODE_LOADING_DIR + "/gem"
    @object.load("load_fixture.rb").should be_true
    ScratchPad.recorded.should == [:loaded, :loaded_gem]
  end

  it "does not cause #require with the same path to fail" do
    @object.load(@path).should be_true
    @object.require(@path).should be_true
    ScratchPad.recorded.should == [:loaded, :loaded]
  end

  it "does not add the loaded path to $LOADED_FEATURES" do
    saved_loaded_features = $LOADED_FEATURES.dup
    @object.load(@path).should be_true
    $LOADED_FEATURES.should == saved_loaded_features
  end

  it "raises a LoadError if passed a non-extensioned path that does not exist but a .rb extensioned path does exist" do
    path = File.expand_path "load_ext_fixture", CODE_LOADING_DIR
    -> { @object.load(path) }.should raise_error(LoadError)
  end

  describe "when passed true for 'wrap'" do
    it "loads from an existing path" do
      path = File.expand_path "wrap_fixture.rb", CODE_LOADING_DIR
      @object.load(path, true).should be_true
    end

    it "sets the enclosing scope to an anonymous module" do
      path = File.expand_path "wrap_fixture.rb", CODE_LOADING_DIR
      @object.load(path, true)

      Object.const_defined?(:LoadSpecWrap).should be_false

      wrap_module = ScratchPad.recorded[1]
      wrap_module.should be_an_instance_of(Module)
    end

    it "allows referencing outside namespaces" do
      path = File.expand_path "wrap_fixture.rb", CODE_LOADING_DIR
      @object.load(path, true)

      ScratchPad.recorded[0].should equal(String)
    end

    it "sets self as a copy of the top-level main" do
      path = File.expand_path "wrap_fixture.rb", CODE_LOADING_DIR
      @object.load(path, true)

      top_level = ScratchPad.recorded[2]
      top_level.to_s.should == "main"
      top_level.method(:to_s).owner.should == top_level.singleton_class
      top_level.should_not equal(main)
      top_level.should be_an_instance_of(Object)
    end

    it "includes modules included in main's singleton class in self's class" do
      mod = Module.new
      main.extend(mod)

      main_ancestors = main.singleton_class.ancestors[1..-1]
      main_ancestors.first.should == mod

      path = File.expand_path "wrap_fixture.rb", CODE_LOADING_DIR
      @object.load(path, true)

      top_level = ScratchPad.recorded[2]
      top_level_ancestors = top_level.singleton_class.ancestors[-main_ancestors.size..-1]
      top_level_ancestors.should == main_ancestors

      wrap_module = ScratchPad.recorded[1]
      top_level.singleton_class.ancestors.should == [top_level.singleton_class, wrap_module, *main_ancestors]
    end

    describe "with top-level methods" do
      before :each do
        path = File.expand_path "load_wrap_method_fixture.rb", CODE_LOADING_DIR
        @object.load(path, true)
      end

      it "allows calling top-level methods" do
        ScratchPad.recorded.last.should == :load_wrap_loaded
      end

      it "does not pollute the receiver" do
        -> { @object.send(:top_level_method) }.should raise_error(NameError)
      end
    end
  end

  describe "(shell expansion)" do
    before :each do
      @env_home = ENV["HOME"]
      ENV["HOME"] = CODE_LOADING_DIR
    end

    after :each do
      ENV["HOME"] = @env_home
    end

    it "expands a tilde to the HOME environment variable as the path to load" do
      @object.require("~/load_fixture.rb").should be_true
      ScratchPad.recorded.should == [:loaded]
    end
  end
end