class_source 0.0.0

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,80 @@
1
+ require 'ruby_parser'
2
+
3
+ module ClassSource
4
+ class Collator
5
+ def initialize(target_class, index)
6
+ @klass = target_class
7
+ @source = index
8
+ end
9
+
10
+ def to_hash(options = {})
11
+ full_sources = @source.locations(options).inject({}) do |results, location|
12
+ results[ location ] = source_helper(location)
13
+ results
14
+ end
15
+
16
+ return full_sources unless options[:include_nested] == false
17
+ full_sources.inject({}) do |clean_sources, (location, source)|
18
+ if nested_class_line_ranges[location.first]
19
+ complete_file = full_file(location)
20
+ target_range = (location.last - 1)..(location.last + source.lines.count - 2)
21
+ clean_sources[location] = complete_file.lines.to_a.select.with_index do |line, index|
22
+ target_range.include?(index) &&
23
+ nested_class_line_ranges[location.first].all? { |range| !range.include?(index) }
24
+ end.join("")
25
+ else
26
+ clean_sources[location] = source
27
+ end
28
+ clean_sources
29
+ end
30
+ end
31
+
32
+ def nested_class_line_ranges
33
+ nested_classes = @klass.constants.select { |c| @klass.const_get(c).is_a?(Class) }.map {|c| @klass.const_get(c) }
34
+ return @nested_class_ranges if @nested_class_ranges
35
+ @nested_class_ranges = {}
36
+ nested_classes.each do |klass|
37
+ # (klass.source_location.last-1)..(klass.source_location.last + klass.source.lines.count - 2)
38
+ klass.__source__.all.each do |(file, line), source|
39
+ @nested_class_ranges[file] ||= []
40
+ @nested_class_ranges[file] << ((line - 1)..(line + source.lines.count - 2))
41
+ end
42
+ end
43
+
44
+ @nested_class_ranges
45
+ end
46
+
47
+ def full_file(location)
48
+ File.read(location.first)
49
+ end
50
+
51
+ # source_helper and valid_expression? are lifted from method_source
52
+ # (c) 2011 John Mair (banisterfiend)
53
+ def source_helper(source_location)
54
+ return nil if !source_location.is_a?(Array)
55
+
56
+ file_name, line = source_location
57
+ File.open(file_name) do |file|
58
+ (line - 1).times { file.readline }
59
+
60
+ code = ""
61
+ loop do
62
+ val = file.readline
63
+ code << val
64
+
65
+ return code if valid_expression?(code)
66
+ end
67
+ end
68
+ end
69
+
70
+
71
+ def valid_expression?(code)
72
+ RubyParser.new.parse(code)
73
+ rescue Racc::ParseError, SyntaxError
74
+ false
75
+ else
76
+ true
77
+ end
78
+
79
+ end
80
+ end
@@ -0,0 +1,21 @@
1
+ module ClassSource
2
+ class Declarations
3
+ def self.[](key)
4
+ @declarations ||= {}
5
+ @declarations[key]
6
+ end
7
+
8
+ def self.add(klass_name, locations)
9
+ @declarations ||= {}
10
+ @declarations[klass_name] ||= []
11
+ @declarations[klass_name] += locations
12
+ end
13
+
14
+ def self.save(declarations)
15
+ declarations.each do |klass_name, locations|
16
+ add(klass_name, locations)
17
+ end
18
+ end
19
+ end
20
+ end
21
+
@@ -0,0 +1,25 @@
1
+ module ClassSource
2
+ class Guesser
3
+ def initialize(klass, source_files)
4
+ @source_files = source_files
5
+ @klass = klass
6
+ end
7
+
8
+ def locations
9
+ return if @source_files.empty?
10
+ @source_files.map do |file|
11
+ [file, find_module_name(File.read(file))]
12
+ end.select { |result| !result.last.nil? }
13
+ end
14
+
15
+ private
16
+
17
+ def find_module_name(text)
18
+ submodule_name = @klass.name.split("::").last
19
+ text.lines.each.with_index do |line, index|
20
+ return index + 1 if line.match /\b#{submodule_name}\b/
21
+ end
22
+ nil
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,41 @@
1
+ module ClassSource
2
+ class Index
3
+ def initialize(target_class, options = {})
4
+ @target_class = target_class
5
+ @options = options
6
+ end
7
+
8
+ def to_s(options={})
9
+ all(options).values.join("")
10
+ end
11
+
12
+ def ==(value)
13
+ to_s == value
14
+ end
15
+
16
+ def all(options={})
17
+ @collator ||= Collator.new(@target_class, self).to_hash(options)
18
+ end
19
+
20
+ def locations(options={})
21
+ locator.to_a
22
+ end
23
+
24
+ def methods
25
+ @method_details ||= MethodIndex.new(@target_class)
26
+ end
27
+
28
+ def class_methods
29
+ methods.klass
30
+ end
31
+
32
+ def locator
33
+ @locator ||= Locator.new(@target_class, @options)
34
+ end
35
+
36
+ def files
37
+ locator.files
38
+ end
39
+
40
+ end
41
+ end
@@ -0,0 +1,74 @@
1
+ require 'tempfile'
2
+ require 'yaml'
3
+
4
+ module ClassSource
5
+ class Locator
6
+ def initialize(target_class, options={})
7
+ @klass = target_class
8
+ @options=options
9
+ end
10
+
11
+ def to_a
12
+ source_locations
13
+ end
14
+
15
+ def methods
16
+ MethodIndex.new(@klass)
17
+ end
18
+
19
+ def files(options={})
20
+ @source_files ||= methods.locations.map(&:first).uniq
21
+ return @source_files + [@options[:file]] if @options[:file]
22
+ @source_files
23
+ end
24
+
25
+ def source_locations(options={})
26
+ return @locations if @locations
27
+ t = Tempfile.new('class_creation_events')
28
+ fork do
29
+ declarations = files(options).inject({}) do |declarations, source_file|
30
+ trace_declarations(source_file, declarations)
31
+ end
32
+ YAML.dump(declarations, t)
33
+ end
34
+ Process.wait
35
+ Declarations.save YAML.load_file(t.path)
36
+ t.close
37
+ @locations = if !Declarations[@klass.name].nil?
38
+ Declarations[@klass.name].uniq
39
+ else
40
+ Guesser.new(@klass, files).locations || []
41
+ end
42
+ end
43
+
44
+ def trace_declarations(source_file, declarations)
45
+ set_trace_func lambda { |event, file, line, id, binding, classname|
46
+ defined_class = standard_class_declared(event, binding) || dynamic_class_declared(id, classname, file, line)
47
+ break unless defined_class
48
+ defined_class_name = defined_class.is_a?(String) ? defined_class : defined_class.name
49
+ declarations[defined_class_name] ||= []
50
+ declarations[defined_class_name] << [ file, line ]
51
+ }
52
+ silence_warnings { load source_file }
53
+ declarations
54
+ end
55
+
56
+ def dynamic_class_declared(id, classname, file, line)
57
+ return unless id == :new && classname == Class
58
+ File.read(file).lines.to_a[line-1][/[A-Z][\w_:]*/, 0]
59
+ end
60
+
61
+ def standard_class_declared(event, binding)
62
+ return unless event == 'class'
63
+ event_class = eval( "Module.nesting", binding )
64
+ event_class.first
65
+ end
66
+
67
+ def silence_warnings
68
+ old_verbose, $VERBOSE = $VERBOSE, nil
69
+ yield
70
+ ensure
71
+ $VERBOSE = old_verbose
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,72 @@
1
+ module ClassSource
2
+ class MethodIndex
3
+ def initialize(target_class)
4
+ @target_class = target_class
5
+ end
6
+
7
+ def locations
8
+ @locations ||= (unique.map do |m|
9
+ @target_class.instance_method(m).source_location
10
+ end + klass.unique.map do |m|
11
+ @target_class.method(m).source_location
12
+ end).compact
13
+ end
14
+
15
+ def unique
16
+ uniquely_named_methods = all(:include_inherited_methods => false)
17
+ overridden_methods = (all - uniquely_named_methods).select do |m|
18
+ @target_class.instance_method(m).source_location != @target_class.superclass.instance_method(m).source_location
19
+ end
20
+ overridden_methods + uniquely_named_methods
21
+ end
22
+
23
+
24
+ def all(options={})
25
+ include_inherited_methods = options.has_key?(:include_inherited_methods) ? options[:include_inherited_methods] : true
26
+ target = options[:target] || @target_class
27
+ target.public_instance_methods(include_inherited_methods) +
28
+ target.private_instance_methods(include_inherited_methods) +
29
+ target.protected_instance_methods(include_inherited_methods)
30
+ end
31
+
32
+ def klass
33
+ ClassMethodIndex.new(@target_class)
34
+ end
35
+
36
+ class ClassMethodIndex
37
+ def initialize(target_class)
38
+ @target_class = target_class
39
+ end
40
+
41
+ def unique
42
+ uniquely_named + overridden - extended
43
+ end
44
+
45
+ def extended
46
+ @target_class.singleton_class.ancestors.map do |mod|
47
+ mod.instance_methods.select { |m| mod.instance_method(m).source_location == @target_class.method(m).source_location }
48
+ end.flatten
49
+ end
50
+
51
+ def uniquely_named
52
+ @target_class.singleton_methods(false)
53
+ end
54
+
55
+ def overridden
56
+ (@target_class.methods - uniquely_named).select do |m|
57
+ !ancestral_sources(m).include?(@target_class.method(m).source_location)
58
+ end
59
+ end
60
+
61
+ def ancestral_sources(method)
62
+ superclasses.map { |mod| mod.respond_to?(method) && mod.method(method).source_location }.compact
63
+ end
64
+
65
+ def superclasses
66
+ @target_class.ancestors - [@target_class]
67
+ end
68
+
69
+ end
70
+
71
+ end
72
+ end
@@ -0,0 +1,3 @@
1
+ module ClassSource
2
+ VERSION="0.0.0"
3
+ end
@@ -0,0 +1,13 @@
1
+ require 'class_source/declarations'
2
+ require 'class_source/index'
3
+ require 'class_source/method_index'
4
+ require 'class_source/locator'
5
+ require 'class_source/guesser'
6
+ require 'class_source/collator'
7
+
8
+ module ClassSource
9
+ def __source__(options={})
10
+ Index.new(self, options)
11
+ end
12
+ end
13
+
@@ -0,0 +1,223 @@
1
+ require 'spec_helper'
2
+
3
+ describe ClassSource do
4
+ after { test_unload_all }
5
+
6
+ describe "for a simple class with only an initializer" do
7
+ before do
8
+ test_load 'SimpleClass'
9
+ end
10
+ it "knows the unique methods of the class" do
11
+ SimpleClass.__source__.methods.unique.should == [:initialize]
12
+ end
13
+
14
+ it "knows the method location for each of the unique methods of the class" do
15
+ SimpleClass.__source__.methods.locations.should == [[fixtures_path(:simple_class), 4]]
16
+ end
17
+
18
+ it "can pinpoint the opening of a simple class" do
19
+ SimpleClass.__source__.locations.should == [[fixtures_path(:simple_class), 3]]
20
+ end
21
+
22
+ it "can return the full source of a simple class" do
23
+ SimpleClass.__source__.should == File.read(fixtures_path(:simple_class)).lines.to_a[2..-1].join("")
24
+ end
25
+ end
26
+
27
+ describe "for a class with public, private and protected methods" do
28
+ before { test_load 'ProtectedMethodClass' }
29
+
30
+ it "knows the unique methods of the class" do
31
+ ProtectedMethodClass.__source__.methods.unique.should =~ [:initialize, :talk, :think, :whisper]
32
+ end
33
+
34
+ it "knows the method location for each of the unique methods of the class" do
35
+ ProtectedMethodClass.__source__.methods.locations.should =~ [
36
+ [fixtures_path(:protected_method_class), 2],
37
+ [fixtures_path(:protected_method_class), 6],
38
+ [fixtures_path(:protected_method_class), 11],
39
+ [fixtures_path(:protected_method_class), 16]
40
+ ]
41
+ end
42
+
43
+ it "can pinpoint the opening" do
44
+ ProtectedMethodClass.__source__.locations.should == [[fixtures_path(:protected_method_class), 1]]
45
+ end
46
+
47
+ it "can return the full source" do
48
+ ProtectedMethodClass.__source__.should == File.read(fixtures_path(:protected_method_class)).lines.to_a[0..-1].join("")
49
+ end
50
+ end
51
+
52
+ describe "for a subclass of a user class" do
53
+ before { test_load 'ProtectedMethodClass', 'SubClass' }
54
+
55
+ it "knows the unique methods of the class" do
56
+ SubClass.__source__.methods.unique.should =~ [:talk, :think]
57
+ end
58
+
59
+ it "knows the method location for each of the unique methods of the class" do
60
+ SubClass.__source__.methods.locations.should =~ [
61
+ [fixtures_path(:sub_class), 2],
62
+ [fixtures_path(:sub_class), 6]
63
+ ]
64
+ end
65
+
66
+ it "can pinpoint the opening" do
67
+ SubClass.__source__.locations.should == [[fixtures_path(:sub_class), 1]]
68
+ end
69
+
70
+ it "can return the full source" do
71
+ SubClass.__source__.should == File.read(fixtures_path(:sub_class)).lines.to_a[0..-1].join("")
72
+ end
73
+ end
74
+
75
+ describe "for a nested class" do
76
+ before { test_load 'OuterClass' }
77
+ it "can return the full source" do
78
+ OuterClass::NestedClass.__source__.should == File.read(fixtures_path(:outer_class)).lines.to_a[12..16].join("")
79
+ end
80
+ end
81
+
82
+ describe "for a class with no unique methods" do
83
+ before { test_load 'NoMethodsClass' }
84
+ it "can return the full source if you pass a source file" do
85
+ NoMethodsClass.__source__(:file => fixtures_path(:no_methods_class)).should == File.read(fixtures_path(:no_methods_class)).lines.to_a[0..4].join("")
86
+ end
87
+ describe "for a nested class with no unique methods" do
88
+ it "can return the full source" do
89
+ NoMethodsClass::NestedClass.__source__(:file => fixtures_path(:no_methods_class)).should == File.read(fixtures_path(:no_methods_class)).lines.to_a[1..3].join("")
90
+ end
91
+ end
92
+ end
93
+
94
+ describe "for an outer class" do
95
+ before { test_load 'OuterClass' }
96
+ it "can return the full source" do
97
+ OuterClass.__source__.should == File.read(fixtures_path(:outer_class)).lines.to_a[0..-1].join("")
98
+ end
99
+
100
+ it "can also return the source minus any nested classes" do
101
+ source_lines = File.read(fixtures_path(:outer_class)).lines.to_a
102
+ non_nested_lines = (source_lines[0..3] + source_lines[8..11] + source_lines[17..-1]).join("")
103
+ OuterClass.__source__.to_s(:include_nested => false).should == non_nested_lines
104
+ end
105
+ end
106
+
107
+ describe "for dynamically defined classes" do
108
+ before do
109
+ test_load 'DynamicClass'
110
+ end
111
+
112
+ it "should have a single source location" do
113
+ DynamicClass.__source__.should have(1).locations
114
+ end
115
+
116
+ it "can return the full source" do
117
+ DynamicClass.__source__.should == File.read(fixtures_path(:dynamic_class)).lines.to_a[0..4].join("")
118
+ end
119
+
120
+ it "can return the source for a class named later in the file" do
121
+ LateNamedDynamicClass.send :extend, ClassSource
122
+ LateNamedDynamicClass.__source__.should == File.read(fixtures_path(:dynamic_class)).lines.to_a[11]
123
+ end
124
+
125
+ describe "with no methods" do
126
+ it "knows the source" do
127
+ MethodlessDynamicClass.send :extend, ClassSource
128
+ MethodlessDynamicClass.__source__.should == "MethodlessDynamicClass = Class.new\n"
129
+ end
130
+ end
131
+ end
132
+
133
+ describe "for duplicated classes" do
134
+ before { test_load 'DuplicatedClass' }
135
+ it "should point to the duplication point" do
136
+ DuplicateClass.__source__.locations.should == [
137
+ [fixtures_path(:duplicated_class), 5]
138
+ ]
139
+ end
140
+ end
141
+
142
+ describe "for cloned classes" do
143
+ before { test_load 'ClonedClass' }
144
+ it "should point to the cloning point" do
145
+ CloneClass.__source__.locations.should == [
146
+ [fixtures_path(:cloned_class), 5]
147
+ ]
148
+ end
149
+ end
150
+
151
+ describe "for classes defined within eval" do
152
+ before { test_load 'EvalClass' }
153
+ it "should point to the evaluation point" do
154
+ EvalClass.__source__.locations.should == [
155
+ [fixtures_path(:eval_class), 11]
156
+ ]
157
+ end
158
+ end
159
+
160
+ describe "for classes that are reopened in separate files" do
161
+ before { test_load 'ReOpenedClass' }
162
+ it "should have more than one source file" do
163
+ ReOpenedClass.__source__.files.length.should == 2
164
+ end
165
+
166
+ it "should have more than one source location" do
167
+ ReOpenedClass.__source__.locations.length.should == 2
168
+ end
169
+
170
+ it "should have the full source in its source" do
171
+ ReOpenedClass.__source__.all.should == {
172
+ [fixtures_path(:re_opened_class), 3] => File.read(fixtures_path(:re_opened_class)).lines.to_a[2..-1].join(""),
173
+ [fixtures_path(:re_opened_class_2), 1] => File.read(fixtures_path(:re_opened_class_2))
174
+ }
175
+ end
176
+
177
+ it "should be able to display without nested classes if needed" do
178
+ reopened_source = File.read(fixtures_path(:re_opened_class)).lines.to_a
179
+ reopened_source_2 = File.read(fixtures_path(:re_opened_class_2)).lines.to_a
180
+ ReOpenedClass.__source__.all(:include_nested => false).should == {
181
+ [fixtures_path(:re_opened_class), 3] => (reopened_source[2..6] + reopened_source[15..-1]).join(""),
182
+ [fixtures_path(:re_opened_class_2), 1] => (reopened_source_2[0..4] + reopened_source_2[10..-1]).join("")
183
+ }
184
+ end
185
+ end
186
+
187
+ describe "for a nested class that is reopened within the parent" do
188
+ before { test_load 'ReOpenedClass' }
189
+ it "should have more than one source location" do
190
+ ReOpenedClass::NestedClass.__source__.all.should == {
191
+ [fixtures_path(:re_opened_class), 8] => File.read(fixtures_path(:re_opened_class)).lines.to_a[7..10].join(""),
192
+ [fixtures_path(:re_opened_class), 12] => File.read(fixtures_path(:re_opened_class)).lines.to_a[11..14].join("")
193
+ }
194
+ end
195
+ end
196
+
197
+ describe "for class with only class methods" do
198
+ before do
199
+ test_load 'ClassMethodsClass'
200
+ end
201
+ it "knows the unique class methods of the class" do
202
+ ClassMethodsClass.__source__.class_methods.unique.should == [:simple_method]
203
+ end
204
+ it "can find the source" do
205
+ ClassMethodsClass.__source__.should == File.read(fixtures_path(:class_methods_class)).lines.to_a[0..-1].join("")
206
+ end
207
+ end
208
+
209
+ describe "it doesn't cause side effects for class-scoped code" do
210
+ before do
211
+ test_load 'SideEffectClass'
212
+ end
213
+
214
+ it "should retain the same values as set" do
215
+ SideEffectClass.should be_active
216
+ SideEffectClass.deactivate!
217
+ SideEffectClass.should_not be_active
218
+ SideEffectClass.__source__.should == File.read(fixtures_path(:side_effect_class)).lines.to_a[0..-1].join("")
219
+ SideEffectClass.should_not be_active
220
+ end
221
+ end
222
+
223
+ end
@@ -0,0 +1,5 @@
1
+ class ClassMethodsClass
2
+ def self.simple_method
3
+
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class ClonedClass
2
+ def simple_method
3
+ end
4
+ end
5
+ CloneClass = ClonedClass.clone
@@ -0,0 +1,5 @@
1
+ class DuplicatedClass
2
+ def simple_method
3
+ end
4
+ end
5
+ DuplicateClass = DuplicatedClass.dup
@@ -0,0 +1,14 @@
1
+ DynamicClass = Class.new do
2
+ def example_method
3
+
4
+ end
5
+ end
6
+
7
+ class_to_be_named_later = Class.new do
8
+ def example_method
9
+ end
10
+ end
11
+
12
+ LateNamedDynamicClass = class_to_be_named_later
13
+ MethodlessDynamicClass = Class.new
14
+
@@ -0,0 +1,11 @@
1
+ method_name = "simple_method"
2
+ class_name = "EvalClass"
3
+
4
+ code = <<-CODE
5
+ class #{class_name}
6
+ def #{method_name}
7
+ end
8
+ end
9
+ CODE
10
+
11
+ eval code, binding, __FILE__, __LINE__
@@ -0,0 +1,10 @@
1
+ class Car
2
+ def initialize
3
+ @wheels = 3
4
+ end
5
+
6
+ def go
7
+ puts "Vroom vroom"
8
+ true
9
+ end
10
+ end
@@ -0,0 +1,5 @@
1
+ class ParentClass
2
+ class ChildClass
3
+
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class NoMethodsClass
2
+ class NestedClass
3
+
4
+ end
5
+ end
@@ -0,0 +1,22 @@
1
+ class OuterClass
2
+ def simple_method_1
3
+ end
4
+
5
+ class OtherNestedClass
6
+ def example_method
7
+ end
8
+ end
9
+
10
+ def simple_method_2
11
+ end
12
+
13
+ class NestedClass
14
+ def example_method
15
+ puts "example"
16
+ end
17
+ end
18
+
19
+ def simple_method_3
20
+ end
21
+
22
+ end
@@ -0,0 +1,19 @@
1
+ class ProtectedMethodClass
2
+ def initialize(foo)
3
+
4
+ end
5
+
6
+ def talk(blab)
7
+ sku.foo = doo
8
+ end
9
+
10
+ protected
11
+ def whisper(mutter)
12
+ foo.doo = sku
13
+ end
14
+
15
+ private
16
+ def think(mentate)
17
+ doo.sku = foo
18
+ end
19
+ end
@@ -0,0 +1,16 @@
1
+ load fixtures_path(:re_opened_class_2)
2
+
3
+ class ReOpenedClass
4
+ def method1
5
+
6
+ end
7
+
8
+ class NestedClass
9
+ def simple_method
10
+ end
11
+ end
12
+ class NestedClass
13
+ def simple_method_2
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,11 @@
1
+ class ReOpenedClass
2
+ def method2
3
+
4
+ end
5
+
6
+ class NestedClass2
7
+ def simple_method
8
+ end
9
+
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ class SideEffectClass
2
+ @@active = true
3
+
4
+ def self.deactivate!
5
+ @@active = false
6
+ end
7
+
8
+ def self.active?
9
+ @@active
10
+ end
11
+ end
@@ -0,0 +1,6 @@
1
+
2
+
3
+ class SimpleClass
4
+ def initialize
5
+ end
6
+ end
@@ -0,0 +1,10 @@
1
+ class SubClass < ProtectedMethodClass
2
+ def talk
3
+ "hallo"
4
+ end
5
+
6
+ def think
7
+ "howzit"
8
+ end
9
+
10
+ end
@@ -0,0 +1,3 @@
1
+ class ChildClass
2
+
3
+ end
@@ -0,0 +1,47 @@
1
+ require 'rubygems'
2
+ SOURCE_PATH=File.expand_path(File.dirname(__FILE__), '../lib')
3
+ $LOAD_PATH << SOURCE_PATH
4
+ require 'class_source'
5
+
6
+ RSpec.configure do
7
+ def spec_path
8
+ @path ||= File.expand_path(File.dirname(__FILE__))
9
+ end
10
+
11
+ def fixtures_path(file_name)
12
+ "#{SOURCE_PATH}/fixtures/#{file_name}.rb"
13
+ end
14
+
15
+ def test_load(*args)
16
+ name = args.shift
17
+ file_name = name.to_s.gsub(/([A-Z])/) { |match| "_" + match.downcase }.slice(1..-1)
18
+ raise "can't find #{fixtures_path(file_name)}" unless File.exist?(fixtures_path(file_name))
19
+
20
+ @loaded_fixtures ||= []
21
+ @loaded_fixtures << name
22
+ existing_classes = Object.constants
23
+ load fixtures_path(file_name)
24
+ new_classes = Object.constants - existing_classes - [name.to_sym]
25
+ @loaded_fixtures += new_classes.map &:to_s
26
+ allow_source_inspection(name)
27
+
28
+ test_load(*args) unless args.empty?
29
+ new_classes.each { |new_name| allow_source_inspection(new_name) }
30
+ end
31
+
32
+ def allow_source_inspection(name, nesting = Object)
33
+ klass = nesting.const_get(name.to_sym)
34
+
35
+ klass.send :extend, ClassSource
36
+ klass.constants.select { |kc| klass.const_get(kc).is_a?(Class) }.each do |nested_class|
37
+ allow_source_inspection nested_class, klass
38
+ end
39
+ end
40
+
41
+ def test_unload_all
42
+ @loaded_fixtures.each do |klass_name|
43
+ Object.send :remove_const, klass_name if Object.const_defined?(klass_name)
44
+ end
45
+ end
46
+
47
+ end
metadata ADDED
@@ -0,0 +1,113 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: class_source
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Austin Putman, austingfromboston
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-09-21 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: &2152549200 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *2152549200
25
+ - !ruby/object:Gem::Dependency
26
+ name: ruby_parser
27
+ requirement: &2152548780 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *2152548780
36
+ description: Expose source files and full source code for each class via a simple
37
+ API
38
+ email:
39
+ - austin@rawfingertips.com
40
+ executables: []
41
+ extensions: []
42
+ extra_rdoc_files: []
43
+ files:
44
+ - lib/class_source/collator.rb
45
+ - lib/class_source/declarations.rb
46
+ - lib/class_source/guesser.rb
47
+ - lib/class_source/index.rb
48
+ - lib/class_source/locator.rb
49
+ - lib/class_source/method_index.rb
50
+ - lib/class_source/version.rb
51
+ - lib/class_source.rb
52
+ - spec/class_source_spec.rb
53
+ - spec/fixtures/class_methods_class.rb
54
+ - spec/fixtures/cloned_class.rb
55
+ - spec/fixtures/duplicated_class.rb
56
+ - spec/fixtures/dynamic_class.rb
57
+ - spec/fixtures/eval_class.rb
58
+ - spec/fixtures/example_code.rb
59
+ - spec/fixtures/nested_example.rb
60
+ - spec/fixtures/no_methods_class.rb
61
+ - spec/fixtures/outer_class.rb
62
+ - spec/fixtures/protected_method_class.rb
63
+ - spec/fixtures/re_opened_class.rb
64
+ - spec/fixtures/re_opened_class_2.rb
65
+ - spec/fixtures/side_effect_class.rb
66
+ - spec/fixtures/simple_class.rb
67
+ - spec/fixtures/sub_class.rb
68
+ - spec/fixtures/unnested_child.rb
69
+ - spec/spec_helper.rb
70
+ homepage: http://github.com/austinfromboston/class_source
71
+ licenses:
72
+ - MIT
73
+ post_install_message:
74
+ rdoc_options: []
75
+ require_paths:
76
+ - lib
77
+ required_ruby_version: !ruby/object:Gem::Requirement
78
+ none: false
79
+ requirements:
80
+ - - ! '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ required_rubygems_version: !ruby/object:Gem::Requirement
84
+ none: false
85
+ requirements:
86
+ - - ! '>='
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ requirements: []
90
+ rubyforge_project: class_source
91
+ rubygems_version: 1.8.6
92
+ signing_key:
93
+ specification_version: 3
94
+ summary: See where your classes are defined
95
+ test_files:
96
+ - spec/class_source_spec.rb
97
+ - spec/fixtures/class_methods_class.rb
98
+ - spec/fixtures/cloned_class.rb
99
+ - spec/fixtures/duplicated_class.rb
100
+ - spec/fixtures/dynamic_class.rb
101
+ - spec/fixtures/eval_class.rb
102
+ - spec/fixtures/example_code.rb
103
+ - spec/fixtures/nested_example.rb
104
+ - spec/fixtures/no_methods_class.rb
105
+ - spec/fixtures/outer_class.rb
106
+ - spec/fixtures/protected_method_class.rb
107
+ - spec/fixtures/re_opened_class.rb
108
+ - spec/fixtures/re_opened_class_2.rb
109
+ - spec/fixtures/side_effect_class.rb
110
+ - spec/fixtures/simple_class.rb
111
+ - spec/fixtures/sub_class.rb
112
+ - spec/fixtures/unnested_child.rb
113
+ - spec/spec_helper.rb