flay 1.2.1 → 1.3.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.
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