lemon 0.9.0 → 0.9.1
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.
- data/.ruby +23 -14
- data/.yardopts +6 -0
- data/Config.rb +14 -0
- data/{HISTORY.rdoc → HISTORY.md} +26 -11
- data/LICENSE.txt +27 -0
- data/README.md +42 -28
- data/SPECSHEET.md +314 -0
- data/bin/{lemonade → lemons} +0 -0
- data/lib/lemon.yml +23 -14
- data/lib/lemon/cli.rb +19 -8
- data/lib/lemon/cli/base.rb +50 -20
- data/lib/lemon/cli/generate.rb +51 -16
- data/lib/lemon/cli/lemon.ascii +84 -0
- data/lib/lemon/cli/obrother.rb +35 -0
- data/lib/lemon/cli/scaffold.rb +116 -0
- data/lib/lemon/core_ext.rb +2 -2
- data/lib/lemon/core_ext/module.rb +9 -0
- data/lib/lemon/coverage/analyzer.rb +76 -5
- data/lib/lemon/coverage/cover_unit.rb +38 -14
- data/lib/lemon/coverage/formats/verbose.rb +1 -1
- data/lib/lemon/coverage/generator.rb +196 -0
- data/lib/lemon/coverage/snapshot.rb +16 -16
- data/lib/lemon/coverage/source_parser.rb +103 -37
- data/lib/lemon/ignore_callers.rb +19 -0
- data/lib/lemon/test_case.rb +135 -26
- data/lib/lemon/test_class.rb +16 -3
- data/lib/lemon/test_class_method.rb +58 -0
- data/lib/lemon/test_method.rb +57 -68
- data/lib/lemon/test_module.rb +47 -44
- data/lib/lemon/test_proc.rb +28 -2
- data/lib/lemon/test_scope.rb +14 -0
- data/lib/lemon/test_setup.rb +1 -1
- data/lib/lemon/test_world.rb +7 -0
- data/{work/deprecated/features/support → spec/applique}/ae.rb +0 -0
- data/spec/coverage/{01_complete.rdoc → 01_complete.md} +3 -3
- data/spec/coverage/{02_incomplete.rdoc → 02_incomplete.md} +2 -2
- data/spec/coverage/{03_extensions.rdoc → 03_extensions.md} +2 -2
- data/try/case_scope.rb +19 -0
- metadata +50 -102
- data/.gemspec +0 -152
- data/.gitignore +0 -8
- data/.reap/digest +0 -678
- data/.reap/test.reap +0 -7
- data/Assembly +0 -37
- data/COPYING.rdoc +0 -33
- data/MANIFEST +0 -55
- data/PROFILE +0 -30
- data/Rakefile +0 -23
- data/VERSION +0 -1
- data/lib/lemon/core_ext/omission.rb +0 -18
- data/lib/lemon/generator.rb +0 -149
- data/notes/2010-05-05-coverage.rdoc +0 -47
- data/notes/2010-05-06-files-not-classes.rdoc +0 -19
- data/notes/2010-07-11-acid-testing.rdoc +0 -52
- data/notes/2010-08-02-enforcing-the-unit.md +0 -68
- data/notes/2010-08-03-new-api.md +0 -37
- data/notes/2011-07-07-nailing-down-the-nomenclature.md +0 -6
- data/site/.rsync-filter +0 -8
- data/site/assets/images/cut-lemon.png +0 -0
- data/site/assets/images/forkme.png +0 -0
- data/site/assets/images/github-logo.png +0 -0
- data/site/assets/images/lemon.jpg +0 -0
- data/site/assets/images/lemon.svg +0 -39
- data/site/assets/images/lemons-are-good.png +0 -0
- data/site/assets/images/opensource.png +0 -0
- data/site/assets/images/ruby-logo.png +0 -0
- data/site/assets/images/skin.jpg +0 -0
- data/site/assets/images/skin1.jpg +0 -0
- data/site/assets/images/tap.png +0 -0
- data/site/assets/images/title.png +0 -0
- data/site/assets/styles/class.css +0 -6
- data/site/assets/styles/reset.css +0 -17
- data/site/assets/styles/site.css +0 -33
- data/site/index.html +0 -218
- data/work/deprecated/command/abstract.rb +0 -29
- data/work/deprecated/command/coverage.rb +0 -115
- data/work/deprecated/command/generate.rb +0 -124
- data/work/deprecated/command/test.rb +0 -112
- data/work/deprecated/cucumber.yml +0 -3
- data/work/deprecated/features/coverage.feature +0 -65
- data/work/deprecated/features/generate.feature +0 -66
- data/work/deprecated/features/step_definitions/coverage_steps.rb +0 -1
- data/work/deprecated/features/support/aruba.rb +0 -1
- data/work/deprecated/features/test.feature +0 -67
- data/work/deprecated/model/dsl/advice.rb +0 -78
- data/work/deprecated/model/dsl/subject.rb +0 -40
- data/work/deprecated/model/main.rb +0 -87
- data/work/deprecated/model/test.rb +0 -54
- data/work/deprecated/model/test_base_dsl.rb +0 -88
- data/work/deprecated/model/test_clause.rb +0 -112
- data/work/deprecated/model/test_context.rb +0 -90
- data/work/deprecated/model/test_feature.rb +0 -128
- data/work/deprecated/model/test_scenario.rb +0 -137
- data/work/deprecated/model/test_suite.rb +0 -297
- data/work/deprecated/rake.rb +0 -103
- data/work/deprecated/test/case_coverage_analyzer.rb +0 -25
- data/work/deprecated/test/case_test_case_dsl.rb +0 -46
- data/work/deprecated/test/fixtures/case_complete.rb +0 -25
- data/work/deprecated/test/fixtures/case_inclusion.rb +0 -18
- data/work/deprecated/test/fixtures/case_incomplete.rb +0 -12
- data/work/deprecated/test/fixtures/example.rb +0 -13
- data/work/deprecated/test/fixtures/helper.rb +0 -13
- data/work/deprecated/test/runner +0 -2
- data/work/old-tests/case_example.rb +0 -15
- data/work/old-tests/feature_example.rb +0 -40
- data/work/reference/dsl2.rb +0 -140
- data/work/reference/dynamic_constant_lookup.rb +0 -76
|
@@ -41,7 +41,7 @@ module Lemon::CoverReports
|
|
|
41
41
|
|
|
42
42
|
#
|
|
43
43
|
def unit_line(unit, color)
|
|
44
|
-
data = [unit.to_s.ansi(color), unit.access.to_s, unit.
|
|
44
|
+
data = [unit.to_s.ansi(color), unit.access.to_s, unit.singleton? ? 'class method' : 'instance method']
|
|
45
45
|
"* %s %s %s" % data
|
|
46
46
|
end
|
|
47
47
|
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
module Lemon
|
|
2
|
+
|
|
3
|
+
require 'lemon/coverage/analyzer'
|
|
4
|
+
require 'lemon/coverage/source_parser'
|
|
5
|
+
|
|
6
|
+
# TODO: Add option to add `require <file>` for each test file genetated.
|
|
7
|
+
|
|
8
|
+
# Test Scaffold Generator.
|
|
9
|
+
#
|
|
10
|
+
class Generator
|
|
11
|
+
|
|
12
|
+
# New Scaffold Generator.
|
|
13
|
+
#
|
|
14
|
+
# @option options [Array] :files
|
|
15
|
+
# Ruby scripts for which to generate tests.
|
|
16
|
+
#
|
|
17
|
+
# @option options [Array] :tests
|
|
18
|
+
# Test files that already exist.
|
|
19
|
+
#
|
|
20
|
+
# @option options [Array] :namespaces
|
|
21
|
+
# List of class/module names to limit scaffolding.
|
|
22
|
+
#
|
|
23
|
+
# @option options [Boolean] :private
|
|
24
|
+
# Include private methods in scaffolding.
|
|
25
|
+
#
|
|
26
|
+
# @option options [Symbol] :group
|
|
27
|
+
# Group by `:case` or by `:file`.
|
|
28
|
+
#
|
|
29
|
+
def initialize(options={})
|
|
30
|
+
@files = options[:files] || []
|
|
31
|
+
@tests = options[:tests] || []
|
|
32
|
+
@group = options[:group] || :case
|
|
33
|
+
|
|
34
|
+
@namespaces = options[:namespaces]
|
|
35
|
+
#@coverage = options[:coverage]
|
|
36
|
+
@private = options[:private]
|
|
37
|
+
@caps = options[:caps]
|
|
38
|
+
|
|
39
|
+
#if @namespaces
|
|
40
|
+
# @coverage ||= :all
|
|
41
|
+
#else
|
|
42
|
+
# @coverage ||= :uncovered
|
|
43
|
+
#end
|
|
44
|
+
|
|
45
|
+
#@snapshot = Snapshot.capture
|
|
46
|
+
|
|
47
|
+
@analyzer = CoverageAnalyzer.new(files + tests, options)
|
|
48
|
+
@suite = @analyzer.suite
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
#
|
|
52
|
+
attr :files
|
|
53
|
+
|
|
54
|
+
#
|
|
55
|
+
attr :tests
|
|
56
|
+
|
|
57
|
+
# Group tests by `:case` or `:file`.
|
|
58
|
+
attr :group
|
|
59
|
+
|
|
60
|
+
# List of class and module namespaces to limit scaffolding.
|
|
61
|
+
attr :namespaces
|
|
62
|
+
|
|
63
|
+
# Target coverage `:all`, `:uncovered` or `:covered`.
|
|
64
|
+
#def coverage
|
|
65
|
+
# @coverage
|
|
66
|
+
#end
|
|
67
|
+
|
|
68
|
+
# Include private and protected methods.
|
|
69
|
+
def private?
|
|
70
|
+
@private
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Returns CoverageAnalyzer instance.
|
|
74
|
+
def analyzer
|
|
75
|
+
@analyzer
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Units in groups, by file or by case.
|
|
79
|
+
def grouped_units
|
|
80
|
+
case group
|
|
81
|
+
when :case
|
|
82
|
+
units_by_case
|
|
83
|
+
when :file
|
|
84
|
+
units_by_file
|
|
85
|
+
else
|
|
86
|
+
units_by_case # default ?
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Generate test template(s).
|
|
91
|
+
def generate
|
|
92
|
+
render_map = {}
|
|
93
|
+
|
|
94
|
+
if tests.empty?
|
|
95
|
+
grouped_units.each do |group, units|
|
|
96
|
+
units = filter(units)
|
|
97
|
+
render_map[group] = render(units)
|
|
98
|
+
end
|
|
99
|
+
else
|
|
100
|
+
uncovered_units = analyzer.uncovered
|
|
101
|
+
grouped_units.each do |group, units|
|
|
102
|
+
units = filter(units)
|
|
103
|
+
units = units & uncovered_units
|
|
104
|
+
render_map[group] = render(units)
|
|
105
|
+
end
|
|
106
|
+
#when :covered
|
|
107
|
+
# covered_units = analyzer.target.units
|
|
108
|
+
# grouped_units.each do |group, units|
|
|
109
|
+
# units = filter(units)
|
|
110
|
+
# units = units & covered_units
|
|
111
|
+
# map[group] = render(units)
|
|
112
|
+
# end
|
|
113
|
+
#else
|
|
114
|
+
# #units = Snapshot.capture(namespaces).units
|
|
115
|
+
# #units = (units - @snapshot.units)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
render_map
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# TODO: If units knew which file they came from that would make
|
|
122
|
+
# the code more efficient b/c then we could group after all
|
|
123
|
+
# filtering instead of before.
|
|
124
|
+
|
|
125
|
+
#
|
|
126
|
+
def units_by_case
|
|
127
|
+
units = []
|
|
128
|
+
files.each do |file|
|
|
129
|
+
units.concat SourceParser.parse_units(File.read(file))
|
|
130
|
+
end
|
|
131
|
+
units.group_by{ |u| u.namespace }
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
#
|
|
135
|
+
def units_by_file
|
|
136
|
+
map = {}
|
|
137
|
+
files.each do |file|
|
|
138
|
+
units = SourceParser.parse_units(File.read(file))
|
|
139
|
+
map[file] = units
|
|
140
|
+
end
|
|
141
|
+
map
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# Filter targets to include only specified namespaces.
|
|
145
|
+
def filter(units)
|
|
146
|
+
return units if namespaces.nil? or namespaces.empty?
|
|
147
|
+
units.select do |u|
|
|
148
|
+
namespaces.any? do |ns|
|
|
149
|
+
/^#{ns}/ =~ u.namespace.to_s
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# Generate code template.
|
|
155
|
+
def render(units)
|
|
156
|
+
code = []
|
|
157
|
+
mods = units.group_by{ |u| u.namespace }
|
|
158
|
+
mods.each do |mod, units|
|
|
159
|
+
code << "#{term_case} #{mod} do"
|
|
160
|
+
units.each do |unit|
|
|
161
|
+
next unless private? or unit.public?
|
|
162
|
+
if unit.singleton?
|
|
163
|
+
code << "\n #{term_class_method} :#{unit.method} do"
|
|
164
|
+
code << "\n test '' do"
|
|
165
|
+
code << "\n end"
|
|
166
|
+
code << "\n end"
|
|
167
|
+
else
|
|
168
|
+
code << "\n #{term_method} :#{unit.method} do"
|
|
169
|
+
code << "\n test '' do"
|
|
170
|
+
code << "\n end"
|
|
171
|
+
code << "\n end"
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
code << "\nend\n"
|
|
175
|
+
end
|
|
176
|
+
code.join("\n")
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
#
|
|
180
|
+
def term_case
|
|
181
|
+
@caps ? 'TestCase' : 'test_case'
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
#
|
|
185
|
+
def term_class_method
|
|
186
|
+
@caps ? 'ClassMethod' : 'class_method'
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
#
|
|
190
|
+
def term_method
|
|
191
|
+
@caps ? 'Method' : 'method'
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
end
|
|
@@ -64,7 +64,7 @@ module Lemon
|
|
|
64
64
|
methods = mod.__send__("#{access}methods", false)
|
|
65
65
|
#methods -= Object.__send__("#{access}methods", true)
|
|
66
66
|
methods.each do |method|
|
|
67
|
-
@units << Unit.new(mod, method, :access=>access, :
|
|
67
|
+
@units << Unit.new(mod, method, :access=>access, :singleton=>true)
|
|
68
68
|
end
|
|
69
69
|
end
|
|
70
70
|
return @units
|
|
@@ -105,19 +105,19 @@ module Lemon
|
|
|
105
105
|
attr :method
|
|
106
106
|
|
|
107
107
|
# Is the method a "class method", rather than an instance method.
|
|
108
|
-
attr :
|
|
108
|
+
attr :singleton
|
|
109
109
|
|
|
110
110
|
def initialize(target, method, props={})
|
|
111
|
-
@target
|
|
112
|
-
@method
|
|
113
|
-
@
|
|
114
|
-
@covered
|
|
111
|
+
@target = target
|
|
112
|
+
@method = method.to_sym
|
|
113
|
+
@singleton = props[:singleton] ? true : false
|
|
114
|
+
@covered = props[:covered]
|
|
115
115
|
|
|
116
|
-
if @
|
|
116
|
+
if @singleton
|
|
117
117
|
@private = @target.private_methods.find{ |m| m.to_sym == @method }
|
|
118
118
|
@protected = @target.protected_methods.find{ |m| m.to_sym == @method }
|
|
119
119
|
else
|
|
120
|
-
@private
|
|
120
|
+
@private = @target.private_instance_methods.find{ |m| m.to_sym == @method }
|
|
121
121
|
@protected = @target.protected_instance_methods.find{ |m| m.to_sym == @method }
|
|
122
122
|
end
|
|
123
123
|
end
|
|
@@ -131,8 +131,8 @@ module Lemon
|
|
|
131
131
|
end
|
|
132
132
|
|
|
133
133
|
#
|
|
134
|
-
def
|
|
135
|
-
@
|
|
134
|
+
def singleton?
|
|
135
|
+
@singleton
|
|
136
136
|
end
|
|
137
137
|
|
|
138
138
|
# Method access is public?
|
|
@@ -164,12 +164,12 @@ module Lemon
|
|
|
164
164
|
|
|
165
165
|
#
|
|
166
166
|
def hash
|
|
167
|
-
@target.hash ^ @method.hash ^ @
|
|
167
|
+
@target.hash ^ @method.hash ^ @singleton.hash
|
|
168
168
|
end
|
|
169
169
|
|
|
170
170
|
#
|
|
171
171
|
def to_s
|
|
172
|
-
if @
|
|
172
|
+
if @singleton
|
|
173
173
|
"#{@target}.#{@method}"
|
|
174
174
|
else
|
|
175
175
|
"#{@target}##{@method}"
|
|
@@ -181,19 +181,19 @@ module Lemon
|
|
|
181
181
|
return false unless Unit === other
|
|
182
182
|
return false unless target == other.target
|
|
183
183
|
return false unless method == other.method
|
|
184
|
-
return false unless
|
|
184
|
+
return false unless singleton == other.singleton
|
|
185
185
|
return true
|
|
186
186
|
end
|
|
187
187
|
|
|
188
188
|
def inspect
|
|
189
|
-
"#{target}#{
|
|
189
|
+
"#{target}#{singleton ? '.' : '#'}#{method}"
|
|
190
190
|
end
|
|
191
191
|
|
|
192
192
|
def <=>(other)
|
|
193
193
|
c = (target.name <=> other.target.name)
|
|
194
194
|
return c unless c == 0
|
|
195
|
-
return -1 if
|
|
196
|
-
return 1 if !
|
|
195
|
+
return -1 if singleton && !other.singleton
|
|
196
|
+
return 1 if !singleton && other.singleton
|
|
197
197
|
method.to_s <=> other.method.to_s
|
|
198
198
|
end
|
|
199
199
|
end
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
require 'ripper'
|
|
2
|
+
require 'lemon/coverage/snapshot'
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
module Lemon
|
|
4
5
|
|
|
5
6
|
#
|
|
6
7
|
class SourceParser
|
|
@@ -8,24 +9,38 @@ module Lemon
|
|
|
8
9
|
#
|
|
9
10
|
# text - A String of Ruby code.
|
|
10
11
|
#
|
|
11
|
-
# Returns a Hash with each key a namespace and each value another
|
|
12
|
-
# Hash or a TomDoc::Scope.
|
|
12
|
+
# Returns a Hash with each key a namespace and each value another Hash or Scope.
|
|
13
13
|
def self.parse(text)
|
|
14
14
|
new.parse(text)
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
# Converts Ruby code into a data structure.
|
|
18
|
+
#
|
|
19
|
+
# text - A String of Ruby code.
|
|
20
|
+
#
|
|
21
|
+
# Returns an Array of Snapshot::Unit objects.
|
|
22
|
+
def self.parse_units(text)
|
|
23
|
+
sp = new
|
|
24
|
+
sp.parse(text)
|
|
25
|
+
sp.units
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
attr_accessor :parser
|
|
29
|
+
|
|
30
|
+
attr_accessor :scopes
|
|
31
|
+
|
|
32
|
+
attr_accessor :options
|
|
18
33
|
|
|
19
34
|
# Each instance of SourceParser accumulates scopes with each
|
|
20
35
|
# parse, making it easy to parse an entire project in chunks but
|
|
21
36
|
# more difficult to parse disparate files in one go. Create
|
|
22
37
|
# separate instances for separate global scopes.
|
|
23
38
|
#
|
|
24
|
-
# Returns an instance of
|
|
39
|
+
# Returns an instance of SourceParser.
|
|
25
40
|
def initialize(options = {})
|
|
26
41
|
@options = {}
|
|
27
|
-
@parser = RubyParser.new
|
|
28
42
|
@scopes = {}
|
|
43
|
+
#@parser = RubyParser.new
|
|
29
44
|
end
|
|
30
45
|
|
|
31
46
|
# Resets the state of the parser to a pristine one. Maintains options.
|
|
@@ -43,14 +58,13 @@ module Lemon
|
|
|
43
58
|
# text - A String of Ruby code.
|
|
44
59
|
#
|
|
45
60
|
# Examples
|
|
46
|
-
# @parser =
|
|
61
|
+
# @parser = SourceParser.new
|
|
47
62
|
# files.each do |file|
|
|
48
63
|
# @parser.parse(File.read(file))
|
|
49
64
|
# end
|
|
50
65
|
# pp @parser.scopes
|
|
51
66
|
#
|
|
52
|
-
# Returns a Hash with each key a namespace and each value another
|
|
53
|
-
# Hash or a TomDoc::Scope.
|
|
67
|
+
# Returns a Hash with each key a namespace and each value another Hash or Scope.
|
|
54
68
|
def parse(text)
|
|
55
69
|
process(tokenize(sexp(text)))
|
|
56
70
|
@scopes
|
|
@@ -62,7 +76,8 @@ module Lemon
|
|
|
62
76
|
#
|
|
63
77
|
# Returns a Sexp representing the AST.
|
|
64
78
|
def sexp(text)
|
|
65
|
-
|
|
79
|
+
Ripper.sexp(text)
|
|
80
|
+
#@parser.parse(text)
|
|
66
81
|
end
|
|
67
82
|
|
|
68
83
|
# Converts a tokenized Array of classes, modules, and methods into
|
|
@@ -73,13 +88,14 @@ module Lemon
|
|
|
73
88
|
# scope - An optional Scope object for nested classes or modules.
|
|
74
89
|
#
|
|
75
90
|
# Returns nothing.
|
|
76
|
-
def process(ast, scope
|
|
91
|
+
def process(ast, scope=nil)
|
|
77
92
|
case Array(ast)[0]
|
|
78
93
|
when :module, :class
|
|
79
94
|
name = ast[1]
|
|
80
95
|
new_scope = Scope.new(name, ast[2])
|
|
81
96
|
|
|
82
97
|
if scope
|
|
98
|
+
new_scope.parent = scope
|
|
83
99
|
scope.scopes[name] = new_scope
|
|
84
100
|
elsif @scopes[name]
|
|
85
101
|
new_scope = @scopes[name]
|
|
@@ -119,22 +135,22 @@ module Lemon
|
|
|
119
135
|
def tokenize(node)
|
|
120
136
|
case Array(node)[0]
|
|
121
137
|
when :module
|
|
122
|
-
name = node[1]
|
|
123
|
-
[ :module, name,
|
|
138
|
+
name = node[1][1][1]
|
|
139
|
+
[ :module, name, '', tokenize(node[2]) ]
|
|
124
140
|
when :class
|
|
125
|
-
name = node[1]
|
|
126
|
-
[ :class, name,
|
|
127
|
-
when :
|
|
128
|
-
name = node[1]
|
|
141
|
+
name = node[1][1][1]
|
|
142
|
+
[ :class, name, '', tokenize(node[3]) ]
|
|
143
|
+
when :def
|
|
144
|
+
name = node[1][1]
|
|
129
145
|
args = args_for_node(node[2])
|
|
130
|
-
[ :imethod, name,
|
|
146
|
+
[ :imethod, name, '', args ]
|
|
131
147
|
when :defs
|
|
132
|
-
name = node[
|
|
133
|
-
args = args_for_node(node[
|
|
134
|
-
[ :cmethod, name,
|
|
148
|
+
name = node[3][1]
|
|
149
|
+
args = args_for_node(node[4])
|
|
150
|
+
[ :cmethod, name, '', args ]
|
|
135
151
|
when :block
|
|
136
152
|
tokenize(node[1..-1])
|
|
137
|
-
when :scope
|
|
153
|
+
when :program, :bodystmt, :scope
|
|
138
154
|
tokenize(node[1])
|
|
139
155
|
when Array
|
|
140
156
|
node.map { |n| tokenize(n) }.compact
|
|
@@ -143,27 +159,34 @@ module Lemon
|
|
|
143
159
|
|
|
144
160
|
# Given a method sexp, returns an array of the args.
|
|
145
161
|
def args_for_node(node)
|
|
146
|
-
Array(node)[1..-1].select
|
|
162
|
+
Array(node)[1..-1].select{ |arg| arg.is_a? Symbol }
|
|
147
163
|
end
|
|
148
164
|
|
|
149
|
-
#
|
|
150
|
-
|
|
165
|
+
#
|
|
166
|
+
def units
|
|
167
|
+
list = []
|
|
168
|
+
@scopes.each do |name, scope|
|
|
169
|
+
list.concat(scope.to_units)
|
|
170
|
+
end
|
|
171
|
+
list
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
# A Scope is a Module or Class, and may contain other scopes.
|
|
151
175
|
class Scope
|
|
152
176
|
include Enumerable
|
|
153
177
|
|
|
154
178
|
attr_accessor :name, :comment, :instance_methods, :class_methods
|
|
179
|
+
|
|
180
|
+
attr_accessor :parent
|
|
181
|
+
|
|
155
182
|
attr_accessor :scopes
|
|
156
183
|
|
|
157
|
-
def initialize(name, comment
|
|
158
|
-
@name
|
|
159
|
-
@comment
|
|
184
|
+
def initialize(name, comment='', instance_methods=[], class_methods=[])
|
|
185
|
+
@name = name
|
|
186
|
+
@comment = comment
|
|
160
187
|
@instance_methods = instance_methods
|
|
161
|
-
@class_methods
|
|
162
|
-
@scopes
|
|
163
|
-
end
|
|
164
|
-
|
|
165
|
-
def tomdoc
|
|
166
|
-
@tomdoc ||= TomDoc.new(@comment)
|
|
188
|
+
@class_methods = class_methods
|
|
189
|
+
@scopes = {}
|
|
167
190
|
end
|
|
168
191
|
|
|
169
192
|
def [](scope)
|
|
@@ -183,16 +206,59 @@ module Lemon
|
|
|
183
206
|
end
|
|
184
207
|
|
|
185
208
|
def inspect
|
|
186
|
-
scopes
|
|
209
|
+
scopes = @scopes.keys.join(', ')
|
|
187
210
|
imethods = @instance_methods.inspect
|
|
188
211
|
cmethods = @class_methods.inspect
|
|
189
212
|
|
|
190
213
|
"<#{name} scopes:[#{scopes}] :#{cmethods}: ##{imethods}#>"
|
|
191
214
|
end
|
|
192
215
|
|
|
216
|
+
#
|
|
217
|
+
def target
|
|
218
|
+
if parent
|
|
219
|
+
parent.target.const_get(name)
|
|
220
|
+
else
|
|
221
|
+
Object.const_get(name)
|
|
222
|
+
end
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
#
|
|
226
|
+
def to_units
|
|
227
|
+
units = []
|
|
228
|
+
@instance_methods.each do |imethod|
|
|
229
|
+
units << Snapshot::Unit.new(target, imethod, :singleton=>false)
|
|
230
|
+
end
|
|
231
|
+
@class_methods.each do |imethod|
|
|
232
|
+
units << Snapshot::Unit.new(target, imethod, :singleton=>true)
|
|
233
|
+
end
|
|
234
|
+
@scopes.each do |name, scope|
|
|
235
|
+
units.concat(scope.to_units)
|
|
236
|
+
end
|
|
237
|
+
units
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
# A Method can be instance or class level.
|
|
243
|
+
class Method
|
|
244
|
+
attr_accessor :name, :comment, :args
|
|
245
|
+
|
|
246
|
+
def initialize(name, comment='', args=[])
|
|
247
|
+
@name = name
|
|
248
|
+
@comment = comment
|
|
249
|
+
@args = args || []
|
|
250
|
+
end
|
|
251
|
+
alias_method :to_s, :name
|
|
252
|
+
|
|
253
|
+
def to_sym
|
|
254
|
+
name.to_sym
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
def inspect
|
|
258
|
+
"#{name}(#{args.join(', ')})"
|
|
259
|
+
end
|
|
193
260
|
end
|
|
194
261
|
|
|
195
262
|
end
|
|
196
263
|
|
|
197
264
|
end
|
|
198
|
-
|