reflexive 0.0.6 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,4 +1,5 @@
1
1
  require "reflexive/core_ext/kernel/singleton_class"
2
+ require "reflexive/core_ext/module/reflexive_instance_methods"
2
3
 
3
4
  module Reflexive
4
5
  class Methods
@@ -36,7 +37,7 @@ module Reflexive
36
37
  def each_immediate_class_and_instance_method(&block)
37
38
  VISIBILITIES.each do |visibility|
38
39
  [ @klass_or_module, @klass_or_module.singleton_class ].each do |klass|
39
- methods = klass.send("#{ visibility }_instance_methods", false)
40
+ methods = klass.send("reflexive_#{ visibility }_instance_methods", false)
40
41
  methods.each { |m| block.call(klass.instance_method(m)) }
41
42
  end
42
43
  end
@@ -123,7 +124,7 @@ module Reflexive
123
124
  # when no methods are found - returns nil
124
125
  def collect_instance_methods(klass)
125
126
  methods_with_visibility = VISIBILITIES.map do |visibility|
126
- methods = klass.send("#{ visibility }_instance_methods", false)
127
+ methods = klass.send("reflexive_#{ visibility }_instance_methods", false)
127
128
  [visibility, methods] unless methods.empty?
128
129
  end.compact
129
130
  Hash[methods_with_visibility] unless methods_with_visibility.empty?
@@ -91,7 +91,17 @@ module Reflexive
91
91
  return unless params_event
92
92
  params_event = params_event[1] if params_event[0] == :paren # ?
93
93
  found = false
94
- for scanner_event in extract_scanner_events_from_tree(params_event)
94
+
95
+ if options_arguments = params_event[2]
96
+ options_arguments.each do |optional_argument|
97
+ if scanner_event?(event = optional_argument[0])
98
+ add_local_variable(event)
99
+ keep_walking(optional_argument[1..-1])
100
+ end
101
+ end
102
+ end
103
+
104
+ for scanner_event in extract_scanner_events_from_tree(params_event.values_at(1,3,4,5))
95
105
  if scanner_event[:ident]
96
106
  found = true
97
107
  add_local_variable(scanner_event)
@@ -167,10 +177,11 @@ module Reflexive
167
177
 
168
178
  def on_def(name, params, body)
169
179
  push_local_variables_context
180
+ # TODO this is hack :(
181
+ push_namespace_instance_scope unless @in_singleton_class_defition
170
182
  add_local_variables_from_params_event(params)
171
- push_namespace_instance_scope
172
183
  keep_walking(body)
173
- pop_namespace_scope
184
+ pop_namespace_scope unless @in_singleton_class_defition
174
185
  pop_local_variables_context
175
186
  end
176
187
 
@@ -182,7 +193,7 @@ module Reflexive
182
193
  end
183
194
 
184
195
  def on_class(name, ancestor, body)
185
- keep_walking(name)
196
+ keep_walking(name, ancestor)
186
197
  push_local_variables_context
187
198
  push_namespace_scope(resolve_constant_ref(name))
188
199
  keep_walking(body)
@@ -192,7 +203,9 @@ module Reflexive
192
203
 
193
204
  def on_sclass(target, body)
194
205
  push_local_variables_context
206
+ @in_singleton_class_defition = true
195
207
  keep_walking(body)
208
+ @in_singleton_class_defition = false
196
209
  pop_local_variables_context
197
210
  end
198
211
 
@@ -231,13 +244,61 @@ module Reflexive
231
244
 
232
245
  def on_command(operation, command_args)
233
246
  method_call(operation, nil) if is_ident?(operation)
247
+ if operation[:ident] == "autoload" &&
248
+ (arguments = resolve_arguments(command_args))
249
+
250
+ if [:const, :tstring_content].include?(arguments[0].keys.first)
251
+ constant_access(arguments[0], arguments[0].values.first)
252
+ end
253
+ end
234
254
  keep_walking(command_args)
235
255
  end
236
256
 
257
+ def resolve_arguments(arguments)
258
+ arguments = arguments[1] if arguments[0] == :arg_paren
259
+ if arguments[0] == :args_add_block
260
+ if arguments[1].is_a?(Array)
261
+ arguments[1].map { |a| resolve_argument(a) }
262
+ end
263
+ end
264
+ end
265
+
266
+ def resolve_argument(argument)
267
+ if argument[0] == :symbol_literal
268
+ # [:symbol_literal, [:symbol, {:const=>"C"}]]
269
+ if argument[1].is_a?(Array)
270
+ if argument[1][0] == :symbol
271
+ argument[1][1] # {:const=>"C"}
272
+ end
273
+ end
274
+ elsif argument[0] == :string_literal
275
+ # [:string_literal, [:string_content, {:tstring_content=>"C"}]]
276
+ if argument[1].is_a?(Array)
277
+ if argument[1][0] == :string_content
278
+ argument[1][1] # {:tstring_content=>"C"}
279
+ end
280
+ end
281
+ end
282
+ end
283
+
237
284
  def on_fcall(operation)
238
285
  method_call(operation, nil) if is_ident?(operation)
239
286
  end
240
287
 
288
+ def on_method_add_arg(method, arguments)
289
+
290
+ if method[0] == :fcall &&
291
+ scanner_event?(method[1]) &&
292
+ method[1][:ident] == "autoload"
293
+ if arguments = resolve_arguments(arguments)
294
+ if [:const, :tstring_content].include?(arguments[0].keys.first)
295
+ constant_access(arguments[0], arguments[0].values.first)
296
+ end
297
+ end
298
+ end
299
+ keep_walking(method, arguments)
300
+ end
301
+
241
302
  # primary_value => anything
242
303
  # operation2 : tIDENTIFIER
243
304
  # | tCONSTANT
@@ -277,9 +338,9 @@ module Reflexive
277
338
  def on_call(receiver, dot, method)
278
339
  if rcv = resolve_receiver(receiver)
279
340
  method_call(method, [rcv])
280
- else
281
- keep_walking(receiver)
282
341
  end
342
+
343
+ keep_walking(receiver)
283
344
  end
284
345
 
285
346
  def on_var_ref(ref_event)
@@ -328,8 +389,8 @@ module Reflexive
328
389
 
329
390
  def method_call(scanner_event, receiver, *args)
330
391
  unless receiver
331
- # implict self concept
332
- receiver = @scope.dup
392
+ # implict self concept (will be fetched from constant_access_scope)
393
+ receiver = @scope.last == :instance ? :instance : :class
333
394
  end
334
395
  merge_tags(scanner_event,
335
396
  {:method_call =>
@@ -19,8 +19,11 @@ module Reflexive
19
19
 
20
20
  def parse
21
21
  parse_tree = super
22
- require 'pp'
23
- # pp parse_tree
22
+
23
+ if ENV["DEBUG"]
24
+ require 'pp'
25
+ pp parse_tree
26
+ end
24
27
 
25
28
  ParseTreeTopDownWalker.new(parse_tree).walk
26
29
  parse_tree
@@ -1,70 +1,91 @@
1
1
  require "cgi" unless defined?(CGI) && defined?(CGI::escape)
2
2
 
3
- module RoutingHelpers
4
- # method_call_tag is the scanner event tag emitted by ReflexiveRipper
5
- def method_call_path(method_call_tag)
6
- # r method_call_tag.values_at(:name, :receiver)
7
- name, receiver = method_call_tag.values_at(:name, :receiver)
8
- if receiver.last == :instance
9
- new_method_path(receiver[0..-2].join("::"), :instance, name)
10
- else
11
- new_method_path(receiver.join("::"), :class, name)
12
- end rescue(r(method_call_tag))
13
- end
3
+ module Reflexive
4
+ module RoutingHelpers
5
+ # method_call_tag is the scanner event tag emitted by ReflexiveRipper
6
+ def method_call_path(method_call_tag)
7
+ # r method_call_tag.values_at(:name, :receiver)
8
+ name, receiver, scope = method_call_tag.values_at(:name, :receiver, :scope)
9
+ scope = scope.join("::")
14
10
 
15
- # entry point for method links (may dispatch to
16
- # class_method_definition_path or method_documentation_path based on whether
17
- # the method definition was found by with our reflection capabilities)
18
- def new_method_path(constant, level, method_name)
19
- "/reflexive/constants/#{ constant }/#{ level }_methods/#{ CGI.escape(method_name.to_s) }"
20
- end
11
+ if receiver == :class
12
+ scope = "Kernel" if scope.empty?
13
+ new_method_path(scope, :class, name)
14
+ elsif receiver == :instance
15
+ scope = "Kernel" if scope.empty?
16
+ new_method_path(scope, :instance, name)
17
+ else
18
+ receiver = receiver.join("::")
19
+ new_method_path(Reflexive.constant_lookup(receiver, scope), :class, name)
20
+ end
21
21
 
22
- def class_method_definition_path(constant, method_name)
23
- new_method_path(constant, :class, method_name) + "/definition"
24
- end
22
+ # if receiver.last == :instance
23
+ # new_method_path(receiver[0..-2].join("::"), :instance, name)
24
+ # else
25
+ # new_method_path(receiver.join("::"), :class, name)
26
+ # end rescue(r(method_call_tag))
27
+ end
25
28
 
26
- def instance_method_definition_path(constant, method_name)
27
- new_method_path(constant, :instance, method_name) + "/definition"
28
- end
29
+ # entry point for method links (may dispatch to
30
+ # class_method_definition_path or method_documentation_path based on whether
31
+ # the method definition was found by with our reflection capabilities)
32
+ def new_method_path(constant, level, method_name)
33
+ "/reflexive/constants/#{ constant }/#{ level }_methods/#{ CGI.escape(method_name.to_s) }"
34
+ end
29
35
 
30
- def method_documentation_path(constant, method_name)
31
- method_path(constant, method_name) + "/apidock"
32
- end
36
+ def method_path(constant, method_name)
37
+ "/reflexive/constants/#{ constant }/methods/#{ CGI.escape(method_name.to_s) }"
38
+ end
33
39
 
34
- def dashboard_path
35
- "/reflexive/dashboard"
36
- end
40
+ def new_method_definition_path(constant, level, method_name)
41
+ new_method_path(constant, level, method_name) + "/definition"
42
+ end
37
43
 
38
- def up_path(path)
39
- file_path(File.expand_path("../", path))
40
- end
44
+ def class_method_definition_path(constant, method_name)
45
+ new_method_path(constant, :class, method_name) + "/definition"
46
+ end
41
47
 
42
- def file_path(path)
43
- File.join("/reflexive/files", path)
44
- end
48
+ def instance_method_definition_path(constant, method_name)
49
+ new_method_path(constant, :instance, method_name) + "/definition"
50
+ end
45
51
 
46
- def constant_lookup_path(name, scope)
47
- "/reflexive/constant_lookup" <<
48
- "?name=#{ CGI.escape(name) }&scope=#{ CGI.escape(scope.join("::"))}"
49
- end
52
+ def method_documentation_path(constant, level, method_name)
53
+ new_method_path(constant, level, method_name) + "/apidock"
54
+ end
50
55
 
51
- def load_path_lookup_path(path)
52
- "/reflexive/load_path_lookup?path=#{ CGI.escape(path.to_s) }"
53
- end
56
+ def dashboard_path
57
+ "/reflexive/dashboard"
58
+ end
54
59
 
55
- def constant_path(constant)
56
- "/reflexive/constants/#{ constant }"
57
- end
60
+ def up_path(path)
61
+ file_path(File.expand_path("../", path))
62
+ end
58
63
 
59
- def method_path(constant, method_name)
60
- "/reflexive/constants/#{ constant }/methods/#{ CGI.escape(method_name.to_s) }"
61
- end
64
+ def file_path(path)
65
+ File.join("/reflexive/files", path)
66
+ end
62
67
 
63
- def apidock_path(constant, method_name)
64
- "http://apidock.com/ruby/#{ constant }/#{ CGI.escape(method_name.to_s) }"
65
- end
68
+ def constant_lookup_path(name, scope)
69
+ "/reflexive/constant_lookup" <<
70
+ "?name=#{ CGI.escape(name) }&scope=#{ CGI.escape(scope.join("::"))}"
71
+ end
72
+
73
+ def load_path_lookup_path(path)
74
+ "/reflexive/load_path_lookup?path=#{ CGI.escape(path.to_s) }"
75
+ end
76
+
77
+ def constant_path(constant)
78
+ "/reflexive/constants/#{ constant }"
79
+ end
80
+
81
+ def apidock_path(constant, level, method_name)
82
+ path = "http://apidock.com/ruby/#{ constant }/#{ CGI.escape(method_name.to_s) }"
83
+ path << "/class" if level == :class
84
+ path
85
+ end
66
86
 
67
- def method_definition_path(constant, method_name)
68
- method_path(constant, method_name) + "/definition"
87
+ def method_definition_path(constant, level, method_name)
88
+ method_path(constant, method_name) + "/definition"
89
+ end
69
90
  end
70
91
  end
@@ -102,6 +102,11 @@ pre .lva-highlight
102
102
  .dashboard .column
103
103
  {
104
104
  float: left;
105
+
106
+ }
107
+
108
+ .dashboard .column.first
109
+ {
105
110
  width: 350px;
106
111
  }
107
112
 
data/reflexive.gemspec CHANGED
@@ -13,13 +13,16 @@ Gem::Specification.new do |s|
13
13
  s.required_ruby_version = '>= 1.9.1'
14
14
 
15
15
  # keeping this in sync with Gemfile manually
16
- s.add_dependency "rack"
17
- s.add_dependency "sinatra"
18
- s.add_dependency "sinatra_more"
19
- s.add_dependency "coderay"
20
- s.add_dependency "rdiscount"
16
+ s.add_dependency "rack", "1.1.0"
17
+ s.add_dependency "sinatra", "1.0"
18
+ s.add_dependency "sinatra_more", "0.3.40"
19
+ s.add_dependency "coderay", "0.9.3"
20
+ # s.add_dependency "rdiscount"
21
21
 
22
22
  s.add_development_dependency "rails", "3.0.0.beta3"
23
23
  s.add_development_dependency "rspec", "2.0.0.beta.8"
24
- s.add_development_dependency "sinatra-reloader"
24
+ s.add_development_dependency "sinatra-reloader", "0.4.1"
25
+ s.add_development_dependency "thin", "1.2.7"
26
+ s.add_development_dependency "rack-test", "0.5.3"
27
+ s.add_development_dependency "webrat", "0.7.1"
25
28
  end
@@ -11,6 +11,12 @@ describe Reflexive::CodeRayHtmlEncoder do
11
11
  {:constant_access=>{:name=>"Cons", :scope=>[]}}]]).should(include("<a href"))
12
12
  end
13
13
 
14
+ it "emits constant links for :content tokens" do
15
+ encoder = Reflexive::CodeRayHtmlEncoder.new(:wrap => :div, :css => :style)
16
+ encoder.encode_tokens([["Cons", :content,
17
+ {:constant_access=>{:name=>"Cons", :scope=>[]}}]]).should(include("<a href"))
18
+ end
19
+
14
20
  it "emits links with proper nesting info" do
15
21
  tokens = [["Cons", :constant,
16
22
  {:constant_access=>{:name=>"Cons", :scope=>["A", "B"]}}]]
@@ -26,7 +32,7 @@ describe Reflexive::CodeRayHtmlEncoder do
26
32
  it "emits class method links" do
27
33
  tokens = [ [
28
34
  "m!", :ident,
29
- {:method_call=>{:name=>"m!", :receiver=>["A"]} }
35
+ {:method_call=>{:name=>"m!", :receiver=>:class, :scope=>["A"]} }
30
36
  ] ]
31
37
  encoder.encode_tokens(tokens).should include("/constants/A/class_methods/m%21")
32
38
  end
@@ -34,7 +40,7 @@ describe Reflexive::CodeRayHtmlEncoder do
34
40
  it "emits instance method links" do
35
41
  tokens = [ [
36
42
  "m!", :ident,
37
- {:method_call=>{:name=>"m!", :receiver=>["A", :instance]} }
43
+ {:method_call=>{:name=>"m!", :receiver=>:instance, :scope => ["A"]} }
38
44
  ] ]
39
45
  encoder.encode_tokens(tokens).should include("/constants/A/instance_methods/m%21")
40
46
  end
@@ -0,0 +1,227 @@
1
+ require "reflexive/application"
2
+ require "rack/test"
3
+ require "nokogiri"
4
+ require "webrat/core/matchers"
5
+
6
+ FIXTURE_FILE_PATH = File.expand_path("../integration_spec_fixture.rb", __FILE__)
7
+ require FIXTURE_FILE_PATH
8
+
9
+ describe Reflexive::Application do
10
+ include Rack::Test::Methods
11
+ include Webrat::Matchers
12
+
13
+ include Reflexive::RoutingHelpers
14
+
15
+ def app
16
+ Reflexive::Application
17
+ end
18
+
19
+ it "shows dashboard" do
20
+ get(dashboard_path)
21
+ last_response.should be_ok
22
+ last_response.body.should include("Reflexive", "$LOAD_PATH", "Favorites")
23
+ last_response.body.should have_selector('a', :content => "Date")
24
+ last_response.body.should have_selector('a', :content => "Gem")
25
+ last_response.body.should have_selector('a', :content => "FileUtils")
26
+ end
27
+
28
+ describe "class/module browser" do
29
+ def constant_browser_for(constant)
30
+ get(constant_path(constant))
31
+ last_response.should be_ok
32
+ last_response.body
33
+ end
34
+
35
+ it "shows class name" do
36
+ get(constant_path("OpenStruct"))
37
+ last_response.should be_ok
38
+ last_response.body.should include("OpenStruct")
39
+ end
40
+
41
+ it "shows files in which class is defined" do
42
+ constant_browser_for("OpenStruct").
43
+ should include("ostruct.rb")
44
+ constant_browser_for("IntegrationSpecFixture::TestClass").
45
+ should include("integration_spec_fixture.rb")
46
+ end
47
+
48
+ it "shows superclass" do
49
+ constant_browser_for("IntegrationSpecFixture::TestClass").
50
+ should include("TestBaseClass")
51
+ constant_browser_for("OpenStruct").
52
+ should include("BasicObject")
53
+ end
54
+
55
+ it "shows instance methods" do
56
+ constant_browser_for("IntegrationSpecFixture::TestClass").
57
+ should include("public_meth", "protected_meth", "private_meth")
58
+ end
59
+
60
+ it "shows class methods" do
61
+ constant_browser_for("IntegrationSpecFixture::TestClass").
62
+ should include("class_meth")
63
+ end
64
+
65
+ it "shows inherited instance methods" do
66
+ constant_browser_for("IntegrationSpecFixture::TestClass").
67
+ should include("inherited_meth")
68
+ end
69
+
70
+ it "shows inherited class methods" do
71
+ constant_browser_for("IntegrationSpecFixture::TestClass").
72
+ should include("inherited_class_meth")
73
+ end
74
+
75
+ it "shows instance methods for module" do
76
+ constant_browser_for("IntegrationSpecFixture::TestModule").
77
+ should include("module_meth")
78
+ end
79
+
80
+ it "shows class methods for module" do
81
+ constant_browser_for("IntegrationSpecFixture::TestModule").
82
+ should include("module_class_meth")
83
+ end
84
+
85
+ it "shows classes module is included in" do
86
+ constant_browser_for("IntegrationSpecFixture::TestModule").
87
+ should include("TestClass")
88
+ end
89
+
90
+ it "shows classes class is derived from" do
91
+ constant_browser_for("IntegrationSpecFixture::TestBaseClass").
92
+ should include("TestClass")
93
+ end
94
+
95
+ it "shows classes nested inside the class" do
96
+ constant_browser_for("IntegrationSpecFixture::TestClass").
97
+ should have_selector("a", :content => "NestedClass")
98
+ end
99
+ end
100
+
101
+ describe "source browser" do
102
+ def source_browser
103
+ get(file_path(FIXTURE_FILE_PATH))
104
+ last_response.should be_ok
105
+ last_response.body
106
+ end
107
+
108
+ it "shows arbitrary files from file system" do
109
+ get(file_path(FIXTURE_FILE_PATH))
110
+ last_response.should be_ok
111
+ last_response.body.should include("integration_spec_fixture.rb")
112
+ end
113
+
114
+ it "browses directories" do
115
+ dir = File.dirname(FIXTURE_FILE_PATH)
116
+ get(file_path(dir))
117
+ last_response.should be_ok
118
+
119
+ Dir["#{ dir }/*"].each do |path|
120
+ last_response.body.should include(File.basename(path))
121
+ end
122
+ end
123
+
124
+ it "highlights the code" do
125
+ source_browser.should have_selector("span.no")
126
+ source_browser.should have_selector("span.s")
127
+ source_browser.should have_selector("span.r")
128
+ source_browser.should have_selector("span.co")
129
+ end
130
+
131
+ it "links class names" do
132
+ source_browser.should have_selector('a[href$="constant_lookup?name=TestBaseClass&scope=IntegrationSpecFixture"]')
133
+ end
134
+
135
+ it "links module names" do
136
+ source_browser.should have_selector('a[href$="constant_lookup?name=IntegrationSpecFixture&scope="]')
137
+ end
138
+
139
+ it "links arguments to require/load" do
140
+ source_browser.should have_selector('a[href$="load_path_lookup?path=ostruct"]')
141
+ end
142
+
143
+ it "links method calls from top level" do
144
+ source_browser.should have_selector('a[href$="constants/Kernel/class_methods/require"]')
145
+ end
146
+
147
+ it "links method calls from class instance level" do
148
+ source_browser.should have_selector('a[href$="constants/IntegrationSpecFixture::TestClass/class_methods/inherited_class_meth"]')
149
+ end
150
+
151
+ it "links method calls from class definition level" do
152
+ source_browser.should have_selector('a[href$="constants/IntegrationSpecFixture::TestClass/class_methods/another_inherited_class_meth"]')
153
+ end
154
+
155
+ it "links local variable assignments and access" do
156
+ source_browser.should have_selector('span[id^="lv:"][id$=":local_var"]', :content => "local_var") do |local_var_assignment|
157
+ source_browser.should have_selector('a[href="#' + local_var_assignment.first["id"] + '"]')
158
+ end
159
+ source_browser.should have_selector('span[id^="lv:"][id$=":another_local_var"]', :content => "another_local_var")
160
+ # source_browser.should have_selector('a.lva', :content => "another_local_var")
161
+ end
162
+
163
+ it "links method calls" do
164
+ source_browser.should have_selector('a[href$="constants/IntegrationSpecFixture::TestClass/instance_methods/not_defined_meth"]', :content => "not_defined_meth")
165
+ end
166
+ end
167
+
168
+ describe "constant lookup" do
169
+ it "redirects to found constant" do
170
+ get(constant_lookup_path("TestModule", ["IntegrationSpecFixture::TestClass"]))
171
+
172
+ last_response.should be_redirect
173
+ last_response.body.should be_empty
174
+
175
+ follow_redirect!
176
+
177
+ last_response.body.should match(/module\s+IntegrationSpecFixture::TestModule/)
178
+ last_response.body.should include("integration_spec_fixture.rb")
179
+ end
180
+ end
181
+
182
+ describe "method lookup" do
183
+ it "redirects to found method" do
184
+ get(new_method_path("IntegrationSpecFixture::TestClass", :instance, "inherited_meth"))
185
+
186
+ last_response.should be_redirect
187
+ last_response.body.should be_empty
188
+
189
+ follow_redirect!
190
+
191
+ last_request.path.should include("constants/IntegrationSpecFixture::TestClass/instance_methods/inherited_meth/definition")
192
+ end
193
+
194
+ it "shows error message when method is not found" do
195
+ get(new_method_path("IntegrationSpecFixture::TestClass", :instance, "not_defined_meth"))
196
+
197
+ last_response.should be_ok
198
+ last_response.body.should include("Reflexive Error")
199
+ end
200
+
201
+ it "redirect to documentation for core methods" do
202
+ get(new_method_path("IntegrationSpecFixture::TestClass", :class, "class_eval"))
203
+
204
+ last_response.should be_redirect
205
+
206
+ follow_redirect!
207
+
208
+ last_request.path.should include("constants/Module/instance_methods/class_eval/apidock")
209
+ end
210
+
211
+ it "uses heuristics providing user a way to choose method for module instance methods" do
212
+ get(new_method_path("IntegrationSpecFixture::HeuristicLookupBaseModule", :instance, "meth"))
213
+
214
+ last_response.should be_ok
215
+ last_response.body.should include("HeuristicLookupIncludingClass1")
216
+ last_response.body.should include("HeuristicLookupIncludingClass2")
217
+ end
218
+
219
+ it "uses heuristics providing user a way to choose method for class instance methods" do
220
+ get(new_method_path("IntegrationSpecFixture::HeuristicLookupBaseClass", :instance, "meth"))
221
+
222
+ last_response.should be_ok
223
+ last_response.body.should include("HeuristicLookupInheritingClass1")
224
+ last_response.body.should include("HeuristicLookupInheritingClass2")
225
+ end
226
+ end
227
+ end