pippi 0.0.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.
- checksums.yaml +7 -0
- data/.gitignore +5 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +4 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +24 -0
- data/README.md +177 -0
- data/Rakefile +11 -0
- data/bin/pippi +7 -0
- data/doc/README +1 -0
- data/doc/docs.md +64 -0
- data/lib/pippi.rb +15 -0
- data/lib/pippi/auto_runner.rb +24 -0
- data/lib/pippi/check_loader.rb +23 -0
- data/lib/pippi/check_set_mapper.rb +35 -0
- data/lib/pippi/checks/check.rb +39 -0
- data/lib/pippi/checks/debug_check.rb +14 -0
- data/lib/pippi/checks/map_followed_by_flatten.rb +55 -0
- data/lib/pippi/checks/reverse_followed_by_each.rb +53 -0
- data/lib/pippi/checks/select_followed_by_first.rb +58 -0
- data/lib/pippi/checks/select_followed_by_size.rb +59 -0
- data/lib/pippi/context.rb +31 -0
- data/lib/pippi/exec_runner.rb +34 -0
- data/lib/pippi/problem.rb +24 -0
- data/lib/pippi/report.rb +27 -0
- data/lib/pippi/tasks.rb +41 -0
- data/lib/pippi/version.rb +3 -0
- data/pippi.gemspec +23 -0
- data/sample/map_followed_by_flatten.rb +6 -0
- data/test/check_test.rb +41 -0
- data/test/rails_core_extensions.rb +5 -0
- data/test/test_helper.rb +7 -0
- data/test/unit/map_followed_by_flatten_test.rb +38 -0
- data/test/unit/problem_test.rb +23 -0
- data/test/unit/report_test.rb +25 -0
- data/test/unit/reverse_followed_by_each_test.rb +29 -0
- data/test/unit/select_followed_by_first_test.rb +33 -0
- data/test/unit/select_followed_by_size_test.rb +33 -0
- data/vendor/cache/byebug-2.7.0.gem +0 -0
- data/vendor/cache/columnize-0.8.9.gem +0 -0
- data/vendor/cache/debugger-linecache-1.2.0.gem +0 -0
- data/vendor/cache/minitest-5.4.2.gem +0 -0
- data/vendor/cache/rake-10.1.0.gem +0 -0
- metadata +139 -0
data/test/check_test.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'tempfile'
|
2
|
+
|
3
|
+
class CheckTest < MiniTest::Test
|
4
|
+
|
5
|
+
CodeSample = Struct.new(:code_text, :eval_to_execute)
|
6
|
+
|
7
|
+
def assert_no_problems(str)
|
8
|
+
assert execute_pippi_on(foo_bar_code_sample(str)).empty?
|
9
|
+
end
|
10
|
+
|
11
|
+
def assert_problems(str, opts={})
|
12
|
+
assert_equal opts[:count] || 1, execute_pippi_on(foo_bar_code_sample(str), opts).size
|
13
|
+
end
|
14
|
+
|
15
|
+
def output_file_name
|
16
|
+
@output_file_name ||= Tempfile.new("pippi_output").path
|
17
|
+
end
|
18
|
+
|
19
|
+
def tmp_code_sample_file_name
|
20
|
+
@tmp_code_sample_file_name ||= Tempfile.new("pippi_codesample").path
|
21
|
+
end
|
22
|
+
|
23
|
+
def foo_bar_code_sample(code)
|
24
|
+
CodeSample.new.tap {|c| c.code_text = "class Foo ; def bar ; #{code} ; end ; end" ; c.eval_to_execute = "Foo.new.bar" }
|
25
|
+
end
|
26
|
+
|
27
|
+
def execute_pippi_on(code, opts={})
|
28
|
+
File.open(tmp_code_sample_file_name, "w") {|f| f.syswrite(code.code_text) }
|
29
|
+
maybe_extensions = opts[:include_rails_core_extensions].nil? ? "" : "-r#{Dir.pwd}/test/rails_core_extensions.rb"
|
30
|
+
cmd = "bundle exec ruby #{maybe_extensions} bin/pippi #{tmp_code_sample_file_name} #{check_for_test} #{code.eval_to_execute} #{output_file_name}"
|
31
|
+
IO.popen(cmd).close
|
32
|
+
report = File.read(output_file_name).split
|
33
|
+
FileUtils.rm_f(output_file_name) if File.exists?(output_file_name)
|
34
|
+
report
|
35
|
+
end
|
36
|
+
|
37
|
+
def check_for_test
|
38
|
+
self.class.name.sub(/Test/, "")
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class MapFollowedByFlattenTest < CheckTest
|
4
|
+
|
5
|
+
def test_canonical_case_is_found
|
6
|
+
assert_problems "[1,2,3].map {|x| [x,x+1] }.flatten(1)"
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_requires_first_call_be_to_map
|
10
|
+
assert_no_problems "[1,2,3].select {|x| [x,x+1] }.flatten(1)"
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_requires_last_call_be_to_flatten
|
14
|
+
assert_no_problems "[1,2,3].map {|x| [x] }.first"
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_works_across_statements
|
18
|
+
assert_problems "tmp = [1,2,3].map {|x| [x] } ; tmp.flatten(1)"
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_requires_arg_to_flatten_to_be_one
|
22
|
+
assert_no_problems "[1,2,3].map {|x| [x] }.flatten"
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_will_not_flag_if_theres_an_intervening_method
|
26
|
+
assert_no_problems "[1,2,3].map {|x| [x] }.select {|x| x.to_s > '1' }.flatten(1)"
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_will_not_flag_if_mutator_invoked
|
30
|
+
assert_no_problems "t = [1,2,3].map {|x| [x] } ; t.select! {|x| rand > 0.5 } ; t.flatten(1)"
|
31
|
+
end
|
32
|
+
|
33
|
+
=begin
|
34
|
+
def test_dfa_issue
|
35
|
+
assert_no_problems "t = [1,2,3].map {|x| [x] } ; if (rand > 0.5) ; t.sort! ; end ; t.flatten(1)"
|
36
|
+
end
|
37
|
+
=end
|
38
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class ProblemTest < MiniTest::Test
|
4
|
+
|
5
|
+
def test_eql_should_say_equal_things_are_equal
|
6
|
+
p1 = Pippi::Problem.new(:file_path => "foo", :line_number => 42, :check_class => "String")
|
7
|
+
p2 = Pippi::Problem.new(:file_path => "foo", :line_number => 42, :check_class => "String")
|
8
|
+
assert p1.eql?(p2)
|
9
|
+
assert p2.eql?(p1)
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_eql_should_say_unequal_things_are_unequal
|
13
|
+
p1 = Pippi::Problem.new(:file_path => "foo", :line_number => 42, :check_class => "String")
|
14
|
+
p2 = Pippi::Problem.new(:file_path => "foo", :line_number => 43, :check_class => "String")
|
15
|
+
assert !p1.eql?(p2)
|
16
|
+
assert !p2.eql?(p1)
|
17
|
+
p3 = Pippi::Problem.new(:file_path => "foo2", :line_number => 42, :check_class => "String")
|
18
|
+
assert !p1.eql?(p3)
|
19
|
+
p4 = Pippi::Problem.new(:file_path => "foo", :line_number => 42, :check_class => "Array")
|
20
|
+
assert !p1.eql?(p4)
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class ReportTest < MiniTest::Test
|
4
|
+
|
5
|
+
def test_can_add_a_problem
|
6
|
+
report = Pippi::Report.new
|
7
|
+
report.add(Pippi::Problem.new(:file_path => "foo", :line_number => 42, :check_class => Pippi::Checks::SelectFollowedByFirst))
|
8
|
+
assert_equal 1, report.problems.size
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_filters_duplicates
|
12
|
+
report = Pippi::Report.new
|
13
|
+
report.add(Pippi::Problem.new(:file_path => "foo", :line_number => 42, :check_class => Pippi::Checks::SelectFollowedByFirst))
|
14
|
+
report.add(Pippi::Problem.new(:file_path => "foo", :line_number => 42, :check_class => Pippi::Checks::SelectFollowedByFirst))
|
15
|
+
assert_equal 1, report.problems.size
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_can_remove_problem
|
19
|
+
report = Pippi::Report.new
|
20
|
+
report.add(Pippi::Problem.new(:file_path => "foo", :line_number => 42, :check_class => Pippi::Checks::SelectFollowedByFirst))
|
21
|
+
report.remove 42, "foo", Pippi::Checks::SelectFollowedByFirst
|
22
|
+
assert report.problems.empty?
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class ReverseFollowedByEachTest < CheckTest
|
4
|
+
|
5
|
+
def test_reverse_still_works
|
6
|
+
assert_no_problems "raise 'bang' unless [1,2,3].reverse == [3,2,1]"
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_canonical_case_is_found
|
10
|
+
assert_problems "[1,2,3].reverse.each {|x| x }"
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_requires_first_call_be_to_reverse
|
14
|
+
assert_no_problems "[1,2,3].sort.each{|x| [x,x+1] }"
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_requires_last_call_be_to_each
|
18
|
+
assert_no_problems "[1,2,3].reverse.select {|x| [x] }"
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_works_across_statements
|
22
|
+
assert_problems "tmp = [1,2,3].reverse ; tmp.each {|x| [x] }"
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_will_not_flag_if_mutator_invoked
|
26
|
+
assert_no_problems "t = [1,2,3].reverse ; t.sort! ; t.each {|x| x }"
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class SelectFollowedByFirstTest < CheckTest
|
4
|
+
|
5
|
+
def test_canonical_case_is_found
|
6
|
+
assert_problems "[1,2,3].select {|x| x > 1 }.first"
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_requires_first_call_be_to_select
|
10
|
+
assert_no_problems "[1,2,3].map {|x| x > 1 }.first"
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_requires_last_call_be_to_first
|
14
|
+
assert_no_problems "[1,2,3].select {|x| x > 1 }.sort"
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_works_across_statements
|
18
|
+
assert_problems "tmp = [1,2,3].select {|x| x > 1 } ; tmp.first"
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_will_not_flag_if_theres_an_intervening_method
|
22
|
+
assert_no_problems "[1,2,3].select {|x| x > 1 }.map {|x| x+1 }.first"
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_will_not_flag_if_other_method_invoked_on_select_result
|
26
|
+
assert_no_problems "tmp = [1,2,3].select {|x| x > 1 } ; tmp.reject! {|x| x } ; tmp.first"
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_will_not_flag_if_arg_passed
|
30
|
+
assert_no_problems "[1,2,3].select {|x| x > 1 }.first(1)"
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class SelectFollowedBySizeTest < CheckTest
|
4
|
+
|
5
|
+
def test_canonical_case_is_found
|
6
|
+
assert_problems "[1,2,3].select {|x| x > 1 }.size"
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_requires_first_call_be_to_select
|
10
|
+
assert_no_problems "[1,2,3].map {|x| x > 1 }.size"
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_requires_last_call_be_to_size
|
14
|
+
assert_no_problems "[1,2,3].select {|x| x > 1 }.sort"
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_works_across_statements
|
18
|
+
assert_problems "tmp = [1,2,3].select {|x| x > 1 } ; tmp.size"
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_will_not_flag_if_theres_an_intervening_method
|
22
|
+
assert_no_problems "[1,2,3].select {|x| x > 1 }.map {|x| x+1 }.size"
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_will_not_flag_if_other_method_invoked_on_select_result
|
26
|
+
assert_no_problems "tmp = [1,2,3].select {|x| x > 1 } ; tmp.reject! {|x| x } ; tmp.size"
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_will_not_flag_if_method_subsequently_invokedzz
|
30
|
+
assert_no_problems "tmp = [1,2,3].select {|x| x > 1 } ; tmp.size ; y = tmp.sort!"
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
metadata
ADDED
@@ -0,0 +1,139 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: pippi
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Tom Copeland
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-10-22 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rake
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '10.1'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '10.1'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: minitest
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '5.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '5.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: byebug
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '2.7'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '2.7'
|
55
|
+
description: Pippi is a utility for locating suboptimal Ruby class API usage.
|
56
|
+
email:
|
57
|
+
- tom@thomasleecopeland.com
|
58
|
+
executables:
|
59
|
+
- pippi
|
60
|
+
extensions: []
|
61
|
+
extra_rdoc_files: []
|
62
|
+
files:
|
63
|
+
- ".gitignore"
|
64
|
+
- ".ruby-version"
|
65
|
+
- CHANGELOG.md
|
66
|
+
- Gemfile
|
67
|
+
- Gemfile.lock
|
68
|
+
- README.md
|
69
|
+
- Rakefile
|
70
|
+
- bin/pippi
|
71
|
+
- doc/README
|
72
|
+
- doc/docs.md
|
73
|
+
- lib/pippi.rb
|
74
|
+
- lib/pippi/auto_runner.rb
|
75
|
+
- lib/pippi/check_loader.rb
|
76
|
+
- lib/pippi/check_set_mapper.rb
|
77
|
+
- lib/pippi/checks/check.rb
|
78
|
+
- lib/pippi/checks/debug_check.rb
|
79
|
+
- lib/pippi/checks/map_followed_by_flatten.rb
|
80
|
+
- lib/pippi/checks/reverse_followed_by_each.rb
|
81
|
+
- lib/pippi/checks/select_followed_by_first.rb
|
82
|
+
- lib/pippi/checks/select_followed_by_size.rb
|
83
|
+
- lib/pippi/context.rb
|
84
|
+
- lib/pippi/exec_runner.rb
|
85
|
+
- lib/pippi/problem.rb
|
86
|
+
- lib/pippi/report.rb
|
87
|
+
- lib/pippi/tasks.rb
|
88
|
+
- lib/pippi/version.rb
|
89
|
+
- pippi.gemspec
|
90
|
+
- sample/map_followed_by_flatten.rb
|
91
|
+
- test/check_test.rb
|
92
|
+
- test/rails_core_extensions.rb
|
93
|
+
- test/test_helper.rb
|
94
|
+
- test/unit/map_followed_by_flatten_test.rb
|
95
|
+
- test/unit/problem_test.rb
|
96
|
+
- test/unit/report_test.rb
|
97
|
+
- test/unit/reverse_followed_by_each_test.rb
|
98
|
+
- test/unit/select_followed_by_first_test.rb
|
99
|
+
- test/unit/select_followed_by_size_test.rb
|
100
|
+
- tmp/.gitignore
|
101
|
+
- vendor/cache/byebug-2.7.0.gem
|
102
|
+
- vendor/cache/columnize-0.8.9.gem
|
103
|
+
- vendor/cache/debugger-linecache-1.2.0.gem
|
104
|
+
- vendor/cache/minitest-5.4.2.gem
|
105
|
+
- vendor/cache/rake-10.1.0.gem
|
106
|
+
homepage: https://github.com/tcopeland/pippi
|
107
|
+
licenses:
|
108
|
+
- MIT
|
109
|
+
metadata: {}
|
110
|
+
post_install_message:
|
111
|
+
rdoc_options: []
|
112
|
+
require_paths:
|
113
|
+
- lib
|
114
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
115
|
+
requirements:
|
116
|
+
- - ">="
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: 2.0.0
|
119
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
120
|
+
requirements:
|
121
|
+
- - ">="
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
version: '0'
|
124
|
+
requirements: []
|
125
|
+
rubyforge_project: none
|
126
|
+
rubygems_version: 2.2.2
|
127
|
+
signing_key:
|
128
|
+
specification_version: 4
|
129
|
+
summary: A Ruby runtime code analyzer
|
130
|
+
test_files:
|
131
|
+
- test/check_test.rb
|
132
|
+
- test/rails_core_extensions.rb
|
133
|
+
- test/test_helper.rb
|
134
|
+
- test/unit/map_followed_by_flatten_test.rb
|
135
|
+
- test/unit/problem_test.rb
|
136
|
+
- test/unit/report_test.rb
|
137
|
+
- test/unit/reverse_followed_by_each_test.rb
|
138
|
+
- test/unit/select_followed_by_first_test.rb
|
139
|
+
- test/unit/select_followed_by_size_test.rb
|