flay 1.2.1 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (7) hide show
  1. data.tar.gz.sig +0 -0
  2. data/History.txt +10 -0
  3. data/Rakefile +8 -39
  4. data/lib/flay.rb +67 -63
  5. data/test/test_flay.rb +8 -67
  6. metadata +15 -10
  7. metadata.gz.sig +0 -0
data.tar.gz.sig CHANGED
Binary file
@@ -1,3 +1,13 @@
1
+ === 1.3.0 / 2009-06-23
2
+
3
+ * 5 minor enhancements:
4
+
5
+ * Added --summary to display flay scores per file.
6
+ * Added --verbose to display processing progress.
7
+ * Protect against syntax errors in bad code and continue flaying.
8
+ * Removed fuzzy matching. Never got it to feel right. Slow. Broken on 1.9
9
+ * Renamed --verbose to --diff.
10
+
1
11
  === 1.2.1 / 2009-03-16
2
12
 
3
13
  * 3 minor enhancements:
data/Rakefile CHANGED
@@ -6,47 +6,16 @@ require 'hoe'
6
6
  Hoe::add_include_dirs("../../sexp_processor/dev/lib",
7
7
  "../../ruby_parser/dev/lib")
8
8
 
9
- begin
10
- require 'flay'
11
- rescue LoadError
12
- load 'lib/flay.rb'
13
- end
14
-
15
- Hoe.new('flay', Flay::VERSION) do |flay|
16
- flay.rubyforge_name = 'seattlerb'
17
- flay.developer('Ryan Davis', 'ryand-ruby@zenspider.com')
18
-
19
- flay.flay_threshold = 250
20
-
21
- flay.extra_deps << ['sexp_processor', '>= 3.0.0']
22
- flay.extra_deps << ['ruby_parser', '>= 1.1.0']
23
- end
24
-
25
- begin
26
- require 'rcov/rcovtask'
27
- Rcov::RcovTask.new do |t|
28
- pattern = ENV['PATTERN'] || 'test/test_*.rb'
9
+ Hoe.plugin :seattlerb
29
10
 
30
- t.test_files = FileList[pattern]
31
- t.verbose = true
32
- t.rcov_opts << "--threshold 80"
33
- t.rcov_opts << "--no-color"
34
- end
11
+ Hoe.spec 'flay' do
12
+ developer 'Ryan Davis', 'ryand-ruby@zenspider.com'
35
13
 
36
- task :rcov_info do
37
- pattern = ENV['PATTERN'] || "test/test_*.rb"
38
- ruby "-Ilib -S rcov --text-report --save coverage.info -x rcov,sexp_processor --test-unit-only #{pattern}"
39
- end
14
+ self.rubyforge_name = 'seattlerb'
15
+ self.flay_threshold = 250
40
16
 
41
- task :rcov_overlay do
42
- rcov, eol = Marshal.load(File.read("coverage.info")).last[ENV["FILE"]], 1
43
- puts rcov[:lines].zip(rcov[:coverage]).map { |line, coverage|
44
- bol, eol = eol, eol + line.length
45
- [bol, eol, "#ffcccc"] unless coverage
46
- }.compact.inspect
47
- end
48
- rescue LoadError
49
- # skip
17
+ extra_deps << ['sexp_processor', '~> 3.0']
18
+ extra_deps << ['ruby_parser', '~> 2.0']
50
19
  end
51
20
 
52
- # vim: syntax=Ruby
21
+ # vim: syntax=ruby
@@ -8,16 +8,15 @@ require 'rubygems'
8
8
  require 'sexp_processor'
9
9
  require 'ruby_parser'
10
10
 
11
- abort "update rubygems to >= 1.3.1" unless Gem.respond_to? :find_files
12
-
13
11
  class Flay
14
- VERSION = '1.2.1'
12
+ VERSION = '1.3.0'
15
13
 
16
14
  def self.default_options
17
15
  {
18
- :fuzzy => false,
19
- :verbose => false,
16
+ :diff => false,
20
17
  :mass => 16,
18
+ :summary => false,
19
+ :verbose => false,
21
20
  }
22
21
  end
23
22
 
@@ -37,23 +36,37 @@ class Flay
37
36
  exit
38
37
  end
39
38
 
40
- opts.on('-f', '--fuzzy', "Attempt to do fuzzy similarities. (SLOW)") do
41
- options[:fuzzy] = true
39
+ opts.on('-f', '--fuzzy', "DEAD: fuzzy similarities.") do
40
+ abort "--fuzzy is no longer supported. Sorry. It sucked."
42
41
  end
43
42
 
44
43
  opts.on('-m', '--mass MASS', Integer, "Sets mass threshold") do |m|
45
44
  options[:mass] = m.to_i
46
45
  end
47
46
 
48
- opts.on('-v', '--verbose', "Verbose. Display N-Way diff for ruby.") do
47
+ opts.on('-v', '--verbose', "Verbose. Show progress processing files.") do
49
48
  options[:verbose] = true
50
49
  end
51
50
 
51
+ opts.on('-d', '--diff', "Diff Mode. Display N-Way diff for ruby.") do
52
+ options[:diff] = true
53
+ end
54
+
55
+ opts.on('-s', '--summary', "Summarize. Show flay score per file only.") do
56
+ options[:summary] = true
57
+ end
58
+
52
59
  extensions = ['rb'] + Flay.load_plugins
53
60
 
54
61
  opts.separator ""
55
62
  opts.separator "Known extensions: #{extensions.join(', ')}"
56
- end.parse!
63
+
64
+ begin
65
+ opts.parse!
66
+ rescue => e
67
+ abort "#{e}\n\n#{opts}"
68
+ end
69
+ end
57
70
 
58
71
  options
59
72
  end
@@ -85,6 +98,8 @@ class Flay
85
98
  @@plugins = plugins.map { |f| File.basename(f, '.rb').sub(/^flay_/, '') }
86
99
  end
87
100
  @@plugins
101
+ rescue
102
+ # ignore
88
103
  end
89
104
 
90
105
  attr_accessor :mass_threshold, :total, :identical, :masses
@@ -99,12 +114,12 @@ class Flay
99
114
  self.total = 0
100
115
  self.mass_threshold = @option[:mass]
101
116
 
102
- require 'ruby2ruby' if @option[:verbose]
117
+ require 'ruby2ruby' if @option[:diff]
103
118
  end
104
119
 
105
120
  def process(*files) # TODO: rename from process - should act as SexpProcessor
106
121
  files.each do |file|
107
- warn "Processing #{file}"
122
+ warn "Processing #{file}" if option[:verbose]
108
123
 
109
124
  ext = File.extname(file).sub(/^\./, '')
110
125
  ext = "rb" if ext.nil? || ext.empty?
@@ -115,21 +130,23 @@ class Flay
115
130
  msg = "process_rb"
116
131
  end
117
132
 
118
- sexp = begin
119
- send msg, file
120
- rescue => e
121
- warn " #{e.message.strip}"
122
- warn " skipping #{file}"
123
- nil
124
- end
133
+ begin
134
+ sexp = begin
135
+ send msg, file
136
+ rescue => e
137
+ warn " #{e.message.strip}"
138
+ warn " skipping #{file}"
139
+ nil
140
+ end
125
141
 
126
- next unless sexp
142
+ next unless sexp
127
143
 
128
- process_sexp sexp
144
+ process_sexp sexp
145
+ rescue SyntaxError => e
146
+ warn " skipping #{file}: #{e.message}"
147
+ end
129
148
  end
130
149
 
131
- process_fuzzy_similarities if option[:fuzzy]
132
-
133
150
  analyze
134
151
  end
135
152
 
@@ -157,42 +174,6 @@ class Flay
157
174
  end
158
175
  end
159
176
 
160
- def process_fuzzy_similarities
161
- all_hashes, detected = {}, {}
162
-
163
- self.hashes.values.each do |nodes|
164
- nodes.each do |node|
165
- next if node.mass > 4 * self.mass_threshold
166
- # TODO: try out with fuzzy_hash
167
- # all_hashes[node] = node.grep(Sexp).map { |s| [s.hash] * s.mass }.flatten
168
- all_hashes[node] = node.grep(Sexp).map { |s| [s.hash] }.flatten
169
- end
170
- end
171
-
172
- # warn "looking for copy/paste/edit code across #{all_hashes.size} nodes"
173
-
174
- all_hashes = all_hashes.to_a
175
- all_hashes.each_with_index do |(s1, h1), i|
176
- similar = [s1]
177
- all_hashes[i+1..-1].each do |(s2, h2)|
178
- next if detected[h2]
179
- intersection = h1.intersection h2
180
- max = [h1.size, h2.size].max
181
- if intersection.size >= max * 0.60 then
182
- similarity = s1.similarity(s2)
183
- if similarity > 0.60 then
184
- similar << s2
185
- detected[h2] = true
186
- else
187
- p [similarity, s1, s2]
188
- end
189
- end
190
- end
191
-
192
- self.hashes[similar.first.hash].push(*similar) if similar.size > 1
193
- end
194
- end
195
-
196
177
  def prune
197
178
  # prune trees that aren't duped at all, or are too small
198
179
  self.hashes.delete_if { |_,nodes| nodes.size == 1 }
@@ -242,10 +223,33 @@ class Flay
242
223
  groups.flatten.join("\n")
243
224
  end
244
225
 
226
+ def summary
227
+ score = Hash.new 0
228
+
229
+ masses.each do |hash, mass|
230
+ sexps = hashes[hash]
231
+ mass_per_file = mass.to_f / sexps.size
232
+ sexps.each do |sexp|
233
+ score[sexp.file] += mass_per_file
234
+ end
235
+ end
236
+
237
+ score
238
+ end
239
+
245
240
  def report prune = nil
246
241
  puts "Total score (lower is better) = #{self.total}"
247
242
  puts
248
243
 
244
+ if option[:summary] then
245
+
246
+ self.summary.sort_by { |_,v| -v }.each do |file, score|
247
+ puts "%8.2f: %s" % [score, file]
248
+ end
249
+
250
+ return
251
+ end
252
+
249
253
  count = 0
250
254
  masses.sort_by { |h,m| [-m, hashes[h].first.file] }.each do |hash, mass|
251
255
  nodes = hashes[hash]
@@ -265,16 +269,16 @@ class Flay
265
269
  puts "%d) %s code found in %p (mass%s = %d)" %
266
270
  [count, match, node.first, bonus, mass]
267
271
 
268
- nodes.each_with_index do |node, i|
269
- if option[:verbose] then
272
+ nodes.each_with_index do |x, i|
273
+ if option[:diff] then
270
274
  c = (?A + i).chr
271
- puts " #{c}: #{node.file}:#{node.line}"
275
+ puts " #{c}: #{x.file}:#{x.line}"
272
276
  else
273
- puts " #{node.file}:#{node.line}"
277
+ puts " #{x.file}:#{x.line}"
274
278
  end
275
279
  end
276
280
 
277
- if option[:verbose] then
281
+ if option[:diff] then
278
282
  puts
279
283
  r2r = Ruby2Ruby.new
280
284
  puts n_way_diff(*nodes.map { |s| r2r.process(s.deep_clone) })
@@ -5,11 +5,15 @@ require 'flay'
5
5
 
6
6
  require 'pp' # TODO: remove
7
7
 
8
+ ON_1_9 = RUBY_VERSION =~ /1\.9/
9
+ SKIP_1_9 = true && ON_1_9 # HACK
10
+
8
11
  class Symbol # for testing only, makes the tests concrete
9
12
  def hash
10
13
  to_s.hash
11
14
  end
12
15
 
16
+ alias :crap :<=> if :blah.respond_to? :<=>
13
17
  def <=> o
14
18
  Symbol === o && self.to_s <=> o.to_s
15
19
  end
@@ -65,11 +69,11 @@ class TestSexp < Test::Unit::TestCase
65
69
  assert_equal hash, @s.fuzzy_hash, "ivar from setup"
66
70
  assert_equal hash, @s.deep_clone.fuzzy_hash, "deep clone"
67
71
  assert_equal hash, s.deep_clone.fuzzy_hash, "copy deep clone"
68
- end
72
+ end unless SKIP_1_9
69
73
 
70
74
  def test_all_subhashes
71
75
  expected = [-704571402, -282578980, -35395725,
72
- 160138040, 815971090, 927228382] # , 955256285]
76
+ 160138040, 815971090, 927228382]
73
77
 
74
78
  assert_equal expected, @s.all_subhashes.sort.uniq
75
79
 
@@ -80,7 +84,7 @@ class TestSexp < Test::Unit::TestCase
80
84
  end
81
85
 
82
86
  assert_equal expected, x.sort.uniq
83
- end
87
+ end unless SKIP_1_9
84
88
 
85
89
  def test_process_sexp
86
90
  flay = Flay.new
@@ -123,7 +127,6 @@ class TestSexp < Test::Unit::TestCase
123
127
  [:block],
124
128
  [:call, :call],
125
129
  [:call],
126
- # HACK [:defn],
127
130
  [:if],
128
131
  [:return],
129
132
  [:return],
@@ -133,7 +136,7 @@ class TestSexp < Test::Unit::TestCase
133
136
 
134
137
  actual = flay.hashes.values.map { |sexps| sexps.map { |sexp| sexp.first } }
135
138
 
136
- assert_equal expected, actual.sort_by { |a| a.first.to_s }
139
+ assert_equal expected, actual.sort_by { |a| a.inspect }
137
140
  end
138
141
 
139
142
  def test_process_sexp_no_structure
@@ -142,68 +145,6 @@ class TestSexp < Test::Unit::TestCase
142
145
 
143
146
  assert flay.hashes.empty?
144
147
  end
145
-
146
- def test_process_fuzzy_similarities
147
- flay = Flay.new :mass => 7
148
-
149
- s1 = RubyParser.new.process("def w(n); a; b; c; d; e; end")
150
- s2 = RubyParser.new.process("def x(n); a; c; e; end")
151
-
152
- flay.process_sexp s1
153
- flay.process_sexp s2
154
-
155
- flay.process_fuzzy_similarities
156
-
157
- b1 = s1.scope.block
158
- b2 = s2.scope.block
159
-
160
- assert_equal [b2, b1], flay.hashes[b2.hash]
161
- end
162
-
163
- def test_process_fuzzy_similarities_2
164
- flay = Flay.new :mass => 7
165
-
166
- s1 = RubyParser.new.process("def w(n); a; b; c; d; e; end")
167
- s2 = RubyParser.new.process("def x(n); a; c; e; end")
168
- s3 = RubyParser.new.process("def y(n); a; f; c; g; e; end")
169
-
170
- flay.process_sexp s1
171
- flay.process_sexp s2
172
- flay.process_sexp s3
173
-
174
- flay.process_fuzzy_similarities
175
-
176
- b1 = s1.scope.block
177
- b2 = s2.scope.block
178
- b3 = s3.scope.block
179
-
180
- assert_equal [b3, b2, b1], flay.hashes[b3.hash]
181
- end
182
-
183
- def test_process_fuzzy_similarities_3
184
- flay = Flay.new :mass => 7
185
-
186
- s1 = RubyParser.new.process("def w (n); a; b; c; d; e; end")
187
- s2 = RubyParser.new.process("def x (n); a; c; e; end")
188
- s3 = RubyParser.new.process("def y (n); a; f; c; g; e; end")
189
- s4 = RubyParser.new.process("def z (n); f; g; h; i; j; end")
190
- s5 = RubyParser.new.process("def w1(n); a; b if x; c; d if y; e; end")
191
-
192
- flay.process_sexp s1
193
- flay.process_sexp s2
194
- flay.process_sexp s3
195
- flay.process_sexp s4
196
- flay.process_sexp s5
197
-
198
- flay.process_fuzzy_similarities
199
-
200
- b1 = s1.scope.block
201
- b2 = s2.scope.block
202
- b3 = s3.scope.block
203
- b5 = s5.scope.block
204
-
205
- assert_equal [b3, b5, b2, b1], flay.hashes[b3.hash]
206
- end
207
148
  end
208
149
 
209
150
  class ArrayIntersectionTests < Test::Unit::TestCase
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: flay
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.1
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryan Davis
@@ -30,7 +30,7 @@ cert_chain:
30
30
  FBHgymkyj/AOSqKRIpXPhjC6
31
31
  -----END CERTIFICATE-----
32
32
 
33
- date: 2009-03-16 00:00:00 -07:00
33
+ date: 2009-06-23 00:00:00 -07:00
34
34
  default_executable:
35
35
  dependencies:
36
36
  - !ruby/object:Gem::Dependency
@@ -39,9 +39,9 @@ dependencies:
39
39
  version_requirement:
40
40
  version_requirements: !ruby/object:Gem::Requirement
41
41
  requirements:
42
- - - ">="
42
+ - - ~>
43
43
  - !ruby/object:Gem::Version
44
- version: 3.0.0
44
+ version: "3.0"
45
45
  version:
46
46
  - !ruby/object:Gem::Dependency
47
47
  name: ruby_parser
@@ -49,9 +49,9 @@ dependencies:
49
49
  version_requirement:
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - ">="
52
+ - - ~>
53
53
  - !ruby/object:Gem::Version
54
- version: 1.1.0
54
+ version: "2.0"
55
55
  version:
56
56
  - !ruby/object:Gem::Dependency
57
57
  name: hoe
@@ -61,9 +61,12 @@ dependencies:
61
61
  requirements:
62
62
  - - ">="
63
63
  - !ruby/object:Gem::Version
64
- version: 1.11.0
64
+ version: 2.3.0
65
65
  version:
66
- description: Flay analyzes code for structural similarities. Differences in literal values, variable, class, method names, whitespace, programming style, braces vs do/end, etc are all ignored. Making this totally rad.
66
+ description: |-
67
+ Flay analyzes code for structural similarities. Differences in literal
68
+ values, variable, class, method names, whitespace, programming style,
69
+ braces vs do/end, etc are all ignored. Making this totally rad.
67
70
  email:
68
71
  - ryand-ruby@zenspider.com
69
72
  executables:
@@ -87,6 +90,8 @@ files:
87
90
  - test/test_flay.rb
88
91
  has_rdoc: true
89
92
  homepage: http://ruby.sadi.st/
93
+ licenses: []
94
+
90
95
  post_install_message:
91
96
  rdoc_options:
92
97
  - --main
@@ -108,9 +113,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
108
113
  requirements: []
109
114
 
110
115
  rubyforge_project: seattlerb
111
- rubygems_version: 1.3.1
116
+ rubygems_version: 1.3.4
112
117
  signing_key:
113
- specification_version: 2
118
+ specification_version: 3
114
119
  summary: Flay analyzes code for structural similarities
115
120
  test_files:
116
121
  - test/test_flay.rb
metadata.gz.sig CHANGED
Binary file