robe-server 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+ require 'robe/sash/includes_tracker'
3
+
4
+ describe Robe::Sash::IncludesTracker do
5
+ klass = described_class
6
+
7
+ let(:m) { Module.new { def foo; end } }
8
+ let(:names) { Names.new }
9
+
10
+ context "after change in environment" do
11
+ before do
12
+ expect(klass.method_owner_and_inst(m, names)).to eq([nil, true])
13
+ end
14
+
15
+ it "detects an included module" do
16
+ n = Module.new
17
+ stub_const("N", n)
18
+ n.send(:include, m)
19
+ expect(klass.method_owner_and_inst(m, names)).to eq(["N", true])
20
+ end
21
+
22
+ it "detects an extended module" do
23
+ n = Module.new
24
+ stub_const("N", n)
25
+ n.send(:extend, m)
26
+ expect(klass.method_owner_and_inst(m, names)).to eq(["N", nil])
27
+ end
28
+ end
29
+
30
+ class Names
31
+ def [](mod)
32
+ mod.__name__
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,420 @@
1
+ require 'spec_helper'
2
+ require 'support/mocks'
3
+ require 'robe/sash'
4
+
5
+ describe Robe::Sash do
6
+ klass = described_class
7
+
8
+ context "#modules" do
9
+ it "returns module names" do
10
+ mock_space = BlindVisor.new(*%w(A B C).map { |n| OpenStruct.new(__name__: n) })
11
+ expect(klass.new(mock_space).modules).to eq %w(A B C)
12
+ end
13
+ end
14
+
15
+ context "#class_locations" do
16
+ it "shows location when class has methods" do
17
+ k = klass.new(double(resolve_context: Class.new { def foo; end }))
18
+ expect(k.class_locations(nil, nil)).to eq([__FILE__])
19
+ end
20
+
21
+ it "shows no location for class without methods" do
22
+ k = klass.new(double(resolve_context: Class.new))
23
+ expect(k.class_locations(nil, nil)).to be_empty
24
+ end
25
+ end
26
+
27
+ context "#targets" do
28
+ context "value is a module" do
29
+ let(:m) do
30
+ Module.new do
31
+ def foo; end
32
+ private
33
+ def baz; end
34
+ class << self
35
+ alias_method :inspect, :name
36
+ def oom; end
37
+ private
38
+ def tee; end
39
+ end
40
+ end
41
+ end
42
+
43
+ before { stub_const("M", m) }
44
+
45
+ let(:k) { klass.new }
46
+
47
+ subject { k.targets("M")[1..-1] }
48
+
49
+ specify { expect(k.targets("M")[0]).to eq("M") }
50
+
51
+ it { should include_spec("M#foo") }
52
+ it { should include_spec("M#baz") }
53
+ it { should include_spec("M.oom") }
54
+ it { expect(subject.select { |(_, _, m)| m == :tee }).to be_empty }
55
+ end
56
+
57
+ it "looks at the class if the value is not a module" do
58
+ expect(klass.new.targets("Math::E")).to include_spec("Float#ceil")
59
+ end
60
+ end
61
+
62
+ context "#find_method" do
63
+ let(:k) { klass.new }
64
+
65
+ it { expect(k.find_method(String, true, :gsub).name).to eq(:gsub) }
66
+ it { expect(k.find_method(String, nil, :freeze).name).to eq(:freeze) }
67
+ end
68
+
69
+ context "#find_method_owner" do
70
+ let(:k) { klass.new }
71
+
72
+ it { expect(k.find_method_owner(File, nil, :open)).to eq(IO.singleton_class)}
73
+ it { expect(k.find_method_owner(String, true, :split)).to eq(String)}
74
+ it { expect(k.find_method_owner(Class.new, nil, :boo)).to be_nil}
75
+ end
76
+
77
+ context "#method_spec" do
78
+ let(:k) { klass.new }
79
+
80
+ it "works on String#gsub" do
81
+ match = if RUBY_ENGINE == "rbx"
82
+ start_with("String", true, :gsub)
83
+ else
84
+ eq(["String", true, :gsub, [[:rest]]])
85
+ end
86
+ expect(k.method_spec(String.instance_method(:gsub))).to match
87
+ end
88
+
89
+ it "includes method location" do
90
+ m = Module.new { def foo; end }
91
+ expect(k.method_spec(m.instance_method(:foo))[4..5])
92
+ .to eq([__FILE__, __LINE__ - 2])
93
+ end
94
+
95
+ it "includes method parameters" do
96
+ m = Module.new { def foo(a, *b, &c); end }
97
+ expect(k.method_spec(m.instance_method(:foo)))
98
+ .to eq([nil, true, :foo, [[:req, :a], [:rest, :b], [:block, :c]],
99
+ __FILE__, anything])
100
+ end
101
+
102
+ it "ignores overridden name method" do
103
+ # Celluloid::Actor in celluloid <~ 0.15
104
+ # https://github.com/celluloid/celluloid/issues/354
105
+
106
+ m = Module.new do
107
+ def self.name
108
+ raise TooCoolForSchoolError
109
+ end
110
+
111
+ def self.__name__
112
+ "baa"
113
+ end
114
+
115
+ def qux
116
+ end
117
+ end
118
+
119
+ expect(k.method_spec(m.instance_method(:qux)))
120
+ .to eq(["baa", true, :qux, [], __FILE__, anything])
121
+ end
122
+
123
+ context "eigenclass" do
124
+ let(:c) do
125
+ Class.new do
126
+ class << self
127
+ def foo; end
128
+ end
129
+ end
130
+ end
131
+
132
+ it "substitutes eigenclass with the actual class name" do
133
+ stub_const("M::C", c)
134
+ expect(k.method_spec(c.singleton_class.instance_method(:foo))[0])
135
+ .to eq("M::C")
136
+ end
137
+
138
+ it "skips anonymous one" do
139
+ expect(k.method_spec(c.singleton_class.instance_method(:foo))[0])
140
+ .to be_nil
141
+ end
142
+
143
+ it "recognizes ActiveRecord classes" do
144
+ arc = Class.new do
145
+ class << self
146
+ def bar; end
147
+ def inspect
148
+ "Record(id: integer)"
149
+ end
150
+ end
151
+ end
152
+
153
+ stub_const("Record", arc)
154
+
155
+ expect(k.method_spec(arc.singleton_class.instance_method(:bar))[0]).to eq("Record")
156
+ end
157
+ end
158
+
159
+ context "anonymous owner" do
160
+ let(:m) { Module.new { def foo; end} }
161
+
162
+ it "returns nil first element" do
163
+ expect(k.method_spec(m.instance_method(:foo))[0]).to be_nil
164
+ end
165
+
166
+ it "substitutes anonymous module with including class name" do
167
+ stub_const("C", Class.new.send(:include, m) )
168
+ spec = k.method_spec(m.instance_method(:foo))
169
+ expect(spec[0]).to eq("C")
170
+ expect(spec[1]).to eq(true)
171
+ end
172
+
173
+ it "substitutes anonymous modules with extending module name" do
174
+ stub_const("M", Module.new.send(:extend, m) )
175
+ spec = k.method_spec(m.instance_method(:foo))
176
+ expect(spec[0]).to eq("M")
177
+ expect(spec[1]).to eq(nil)
178
+ end
179
+ end
180
+ end
181
+
182
+ context "#doc_for" do
183
+ it "returns doc hash for instance method" do
184
+ k = klass.new
185
+ hash = k.doc_for("Set", true, "replace")
186
+ expect(hash[:docstring]).to start_with("Replaces the contents")
187
+ end
188
+
189
+ it "returns doc hash for module method" do
190
+ k = klass.new
191
+ hash = k.doc_for("Set", nil, "[]")
192
+ expect(hash[:docstring]).to start_with("Creates a new set containing")
193
+ end
194
+ end
195
+
196
+ context "#method_targets" do
197
+ it "returns empty array when not found" do
198
+ k = klass.new(ScopedVisor.new)
199
+ expect(k.method_targets("a", "b", "c", true, nil, nil)).to be_empty
200
+ end
201
+
202
+ context "examples" do
203
+ let(:k) { klass.new }
204
+
205
+ it "returns class method candidate" do
206
+ expect(k.method_targets("open", "File", nil, nil, nil, nil))
207
+ .to have_one_spec("IO.open")
208
+ end
209
+
210
+ it "returns the method on Class" do
211
+ expect(k.method_targets("superclass", "Object", nil, nil, nil, nil))
212
+ .to have_one_spec("Class#superclass")
213
+ end
214
+
215
+ it "returns the non-overridden method" do
216
+ targets = k.method_targets("new", "Object", nil, nil, nil, nil)
217
+ expect(targets).to include_spec("BasicObject#initialize")
218
+ expect(targets).not_to include_spec("Class#new")
219
+ end
220
+
221
+ it "returns #new overridden in the given class" do
222
+ c = Class.new do
223
+ def self.new
224
+ end
225
+ end
226
+
227
+ stub_const("C", c)
228
+
229
+ targets = k.method_targets("new", "C", nil, nil, nil, nil)
230
+ expect(targets).to include_spec("C.new")
231
+ expect(targets).not_to include_spec("Class#new")
232
+ expect(targets).not_to include_spec("BasicObject#initialize")
233
+ end
234
+
235
+ it "doesn't return overridden method" do
236
+ expect(k.method_targets("to_s", "Hash", nil, true, nil, nil))
237
+ .to have_one_spec("Hash#to_s")
238
+ end
239
+
240
+ context "unknown target" do
241
+ it "returns String method candidate" do
242
+ expect(k.method_targets("split", "s", nil, true, nil, nil))
243
+ .to include_spec("String#split")
244
+ end
245
+
246
+ it "does not return wrong candidates" do
247
+ candidates = k.method_targets("split", "s", nil, true, nil, nil)
248
+ expect(candidates).to be_all { |c| c[2] == :split }
249
+ end
250
+ end
251
+
252
+ it "returns no candidates for target when conservative" do
253
+ expect(k.method_targets("split", nil, nil, true, nil, true))
254
+ .to be_empty
255
+ end
256
+
257
+ it "returns single instance method from superclass" do
258
+ expect(k.method_targets("map", nil, "Array", true, true, nil))
259
+ .to have_one_spec("Enumerable#map")
260
+ end
261
+
262
+ it "returns single method from target class" do
263
+ expect(k.method_targets("map", nil, "Array", true, nil, nil))
264
+ .to have_one_spec("Array#map")
265
+ end
266
+
267
+ it "checks for instance Kernel methods when the target is a module" do
268
+ # Not 100% accurate: the including class may derive from BasicObject
269
+ stub_const("M", Module.new)
270
+ expect(k.method_targets("puts", nil, "M", true, nil, true))
271
+ .to have_one_spec("Kernel#puts")
272
+ end
273
+
274
+ it "checks private Kernel methods when no primary candidates" do
275
+ k = klass.new(BlindVisor.new)
276
+ expect(k.method_targets("puts", nil, nil, true, nil, nil))
277
+ .to have_one_spec("Kernel#puts")
278
+ end
279
+
280
+ it "sorts results list" do
281
+ extend ScannerHelper
282
+
283
+ a = named_module("A", "a", "b", "c", "d")
284
+ b = named_module("A::B", "a", "b", "c", "d")
285
+ c = new_module("a", "b", "c", "d")
286
+ k = klass.new(ScopedVisor.new(*[b, c, a].shuffle))
287
+ expect(k.method_targets("a", nil, nil, true, nil, nil).map(&:first))
288
+ .to eq(["A", "A::B", nil])
289
+ end
290
+ end
291
+ end
292
+
293
+ context "#complete_method" do
294
+ let(:k) { klass.new }
295
+
296
+ it "completes instance methods" do
297
+ expect(k.complete_method("gs", nil, nil, true))
298
+ .to include_spec("String#gsub", "String#gsub!")
299
+ end
300
+
301
+ context "class methods" do
302
+ let(:k) { klass.new(ScopedVisor.new(Class, {"Object" => Object})) }
303
+
304
+ it "completes public" do
305
+ expect(k.complete_method("su", nil, nil, nil))
306
+ .to include_spec("Class#superclass")
307
+ end
308
+
309
+ it "no private methods with explicit target" do
310
+ expect(k.complete_method("attr", "Object", nil, nil))
311
+ .not_to include_spec("Module#attr_reader")
312
+ end
313
+
314
+ it "no private methods with no target at all" do
315
+ expect(k.complete_method("attr", "Object", nil, nil))
316
+ .not_to include_spec("Module#attr_reader")
317
+ end
318
+
319
+ it "completes private methods with implicit target" do
320
+ expect(k.complete_method("attr", nil, "Object", nil))
321
+ .to include_spec("Module#attr_reader", "Module#attr_writer")
322
+ end
323
+ end
324
+ end
325
+
326
+ context "#complete_const" do
327
+ let(:m) do
328
+ Module.new do
329
+ self::ACONST = 1
330
+
331
+ module self::AMOD; end
332
+ module self::BMOD
333
+ module self::C; end
334
+ end
335
+
336
+ class self::ACLS; end
337
+ end
338
+ end
339
+ let(:v) { ScopedVisor.new({"Test" => m})}
340
+ let(:k) { klass.new(v) }
341
+
342
+ context "sandboxed" do
343
+ it "completes all constants" do
344
+ expect(k.complete_const("Test::A", nil))
345
+ .to match_array(%w(Test::ACONST Test::AMOD Test::ACLS))
346
+ end
347
+
348
+ it "requires names to begin with prefix" do
349
+ expect(k.complete_const("Test::MOD", nil)).to be_empty
350
+ end
351
+
352
+ it "completes the constant in the nesting" do
353
+ expect(k.complete_const("A", "Test")).to include("ACONST")
354
+ end
355
+ end
356
+
357
+ it "completes with bigger nesting" do
358
+ expect(k.complete_const("BMOD::C", "Test")).to eq(["BMOD::C"])
359
+ end
360
+
361
+ it "completes global constants" do
362
+ expect(k.complete_const("Ob", nil)).to include("Object", "ObjectSpace")
363
+ end
364
+
365
+ it "completes the constants in all containing scopes" do
366
+ k = klass.new
367
+ expect(k.complete_const("C", "Encoding"))
368
+ .to include("Converter", "Class", "Complex")
369
+ end
370
+
371
+ it "uses the full access path from the request" do
372
+ k = klass.new
373
+ expect(k.complete_const("Object::File::S", nil)).to include("Object::File::Stat")
374
+ end
375
+
376
+ it "keeps the global qualifier" do
377
+ k = klass.new
378
+ expect(k.complete_const("::Obj", nil)).to match_array(["::Object", "::ObjectSpace"])
379
+ end
380
+ end
381
+
382
+ context "#load_path" do
383
+ it 'returns an appropriate value' do
384
+ expect(klass.new.load_path).to eq($:)
385
+ end
386
+ end
387
+
388
+ it { expect(klass.new.ping).to be_true }
389
+
390
+ RSpec::Matchers.define :include_spec do |*specs|
391
+ match do |candidates|
392
+ actual = candidates.map { |mod, instance, sym|
393
+ MethodSpec.to_str(mod, instance, sym)
394
+ }
395
+ RSpec::Matchers::BuiltIn::Include.new(*specs).matches?(actual)
396
+ end
397
+ end
398
+
399
+ RSpec::Matchers.define :have_one_spec do |spec|
400
+ match do |candidates|
401
+ candidates.length == 1 and
402
+ MethodSpec.new(spec) == candidates[0]
403
+ end
404
+ end
405
+
406
+ class MethodSpec
407
+ def initialize(str)
408
+ @str = str
409
+ end
410
+
411
+ def self.to_str(mod, inst, sym)
412
+ "#{mod}#{inst ? '#' : '.'}#{sym}"
413
+ end
414
+
415
+ def ==(other)
416
+ other.is_a?(Array) && other.length > 2 &&
417
+ self.class.to_str(*other[0..2]) == @str
418
+ end
419
+ end
420
+ end