pippi 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +3 -0
  3. data/README.md +108 -39
  4. data/bin/pippi +2 -2
  5. data/doc/docs.md +38 -20
  6. data/lib/pippi.rb +2 -1
  7. data/lib/pippi/auto_runner.rb +3 -7
  8. data/lib/pippi/check_loader.rb +2 -5
  9. data/lib/pippi/check_set_mapper.rb +2 -3
  10. data/lib/pippi/checks/assert_with_nil.rb +5 -6
  11. data/lib/pippi/checks/check.rb +10 -8
  12. data/lib/pippi/checks/debug_check.rb +1 -4
  13. data/lib/pippi/checks/map_followed_by_flatten.rb +7 -9
  14. data/lib/pippi/checks/reverse_followed_by_each.rb +6 -8
  15. data/lib/pippi/checks/select_followed_by_empty.rb +55 -0
  16. data/lib/pippi/checks/select_followed_by_first.rb +7 -9
  17. data/lib/pippi/checks/select_followed_by_size.rb +6 -12
  18. data/lib/pippi/context.rb +3 -7
  19. data/lib/pippi/exec_runner.rb +12 -7
  20. data/lib/pippi/problem.rb +0 -1
  21. data/lib/pippi/report.rb +2 -6
  22. data/lib/pippi/tasks.rb +11 -13
  23. data/lib/pippi/version.rb +2 -2
  24. metadata +4 -37
  25. data/.gitignore +0 -6
  26. data/.ruby-version +0 -1
  27. data/Gemfile +0 -3
  28. data/Gemfile.lock +0 -24
  29. data/Rakefile +0 -11
  30. data/pippi.gemspec +0 -23
  31. data/sample/map_followed_by_flatten.rb +0 -6
  32. data/test/check_test.rb +0 -41
  33. data/test/rails_core_extensions.rb +0 -13
  34. data/test/test_helper.rb +0 -7
  35. data/test/unit/assert_with_nil_test.rb +0 -50
  36. data/test/unit/check_set_mapper_test.rb +0 -17
  37. data/test/unit/map_followed_by_flatten_test.rb +0 -38
  38. data/test/unit/problem_test.rb +0 -23
  39. data/test/unit/report_test.rb +0 -25
  40. data/test/unit/reverse_followed_by_each_test.rb +0 -29
  41. data/test/unit/select_followed_by_first_test.rb +0 -33
  42. data/test/unit/select_followed_by_size_test.rb +0 -47
  43. data/vendor/cache/byebug-2.7.0.gem +0 -0
  44. data/vendor/cache/columnize-0.8.9.gem +0 -0
  45. data/vendor/cache/debugger-linecache-1.2.0.gem +0 -0
  46. data/vendor/cache/minitest-5.4.2.gem +0 -0
  47. data/vendor/cache/rake-10.1.0.gem +0 -0
@@ -3,16 +3,13 @@ require 'pippi'
3
3
  module Pippi
4
4
  class Documentation
5
5
  def generate
6
- str = ""
7
- [Pippi::Checks::SelectFollowedBySize::Documentation,
8
- Pippi::Checks::SelectFollowedByFirst::Documentation,
9
- Pippi::Checks::ReverseFollowedByEach::Documentation,
10
- Pippi::Checks::MapFollowedByFlatten::Documentation,
11
- Pippi::Checks::AssertWithNil::Documentation,
12
- ].sort {|a,b| a.name <=> b.name }.each do |clz|
13
- obj = clz.new
14
- str << %Q{
15
- ### #{clz.name.to_s.split('::')[2]}
6
+ str = ''
7
+ Pippi::CheckSetMapper.new("").predefined_sets.sort.select {|k,v| v.any? }.each do |checkset_name, checks|
8
+ str << "### #{checkset_name}\n"
9
+ checks.sort.each do |check|
10
+ obj = Object.const_get("Pippi::Checks::#{check}::Documentation").new
11
+ str << %(
12
+ #### #{check}
16
13
 
17
14
  #{obj.description}
18
15
 
@@ -27,15 +24,16 @@ Instead, consider doing this:
27
24
  \`\`\`ruby
28
25
  #{obj.instead_use}
29
26
  \`\`\`
30
- }
27
+ )
28
+ end
31
29
  end
32
- File.open("doc/docs.md", "w") {|f| f.syswrite(str) }
30
+ File.open('doc/docs.md', 'w') { |f| f.syswrite(str) }
33
31
  end
34
32
  end
35
33
  end
36
34
 
37
35
  namespace :pippi do
38
- desc "Generate check documentation"
36
+ desc 'Generate check documentation'
39
37
  task :generate_docs do
40
38
  Pippi::Documentation.new.generate
41
39
  end
@@ -1,3 +1,3 @@
1
1
  module Pippi
2
- VERSION = "0.0.4"
3
- end
2
+ VERSION = '0.0.5'
3
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pippi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tom Copeland
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-10-30 00:00:00.000000000 Z
11
+ date: 2014-11-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -60,13 +60,8 @@ executables:
60
60
  extensions: []
61
61
  extra_rdoc_files: []
62
62
  files:
63
- - ".gitignore"
64
- - ".ruby-version"
65
63
  - CHANGELOG.md
66
- - Gemfile
67
- - Gemfile.lock
68
64
  - README.md
69
- - Rakefile
70
65
  - bin/pippi
71
66
  - doc/README
72
67
  - doc/docs.md
@@ -79,6 +74,7 @@ files:
79
74
  - lib/pippi/checks/debug_check.rb
80
75
  - lib/pippi/checks/map_followed_by_flatten.rb
81
76
  - lib/pippi/checks/reverse_followed_by_each.rb
77
+ - lib/pippi/checks/select_followed_by_empty.rb
82
78
  - lib/pippi/checks/select_followed_by_first.rb
83
79
  - lib/pippi/checks/select_followed_by_size.rb
84
80
  - lib/pippi/context.rb
@@ -87,24 +83,6 @@ files:
87
83
  - lib/pippi/report.rb
88
84
  - lib/pippi/tasks.rb
89
85
  - lib/pippi/version.rb
90
- - pippi.gemspec
91
- - sample/map_followed_by_flatten.rb
92
- - test/check_test.rb
93
- - test/rails_core_extensions.rb
94
- - test/test_helper.rb
95
- - test/unit/assert_with_nil_test.rb
96
- - test/unit/check_set_mapper_test.rb
97
- - test/unit/map_followed_by_flatten_test.rb
98
- - test/unit/problem_test.rb
99
- - test/unit/report_test.rb
100
- - test/unit/reverse_followed_by_each_test.rb
101
- - test/unit/select_followed_by_first_test.rb
102
- - test/unit/select_followed_by_size_test.rb
103
- - vendor/cache/byebug-2.7.0.gem
104
- - vendor/cache/columnize-0.8.9.gem
105
- - vendor/cache/debugger-linecache-1.2.0.gem
106
- - vendor/cache/minitest-5.4.2.gem
107
- - vendor/cache/rake-10.1.0.gem
108
86
  homepage: https://github.com/tcopeland/pippi
109
87
  licenses:
110
88
  - MIT
@@ -129,15 +107,4 @@ rubygems_version: 2.2.2
129
107
  signing_key:
130
108
  specification_version: 4
131
109
  summary: A Ruby runtime code analyzer
132
- test_files:
133
- - test/check_test.rb
134
- - test/rails_core_extensions.rb
135
- - test/test_helper.rb
136
- - test/unit/assert_with_nil_test.rb
137
- - test/unit/check_set_mapper_test.rb
138
- - test/unit/map_followed_by_flatten_test.rb
139
- - test/unit/problem_test.rb
140
- - test/unit/report_test.rb
141
- - test/unit/reverse_followed_by_each_test.rb
142
- - test/unit/select_followed_by_first_test.rb
143
- - test/unit/select_followed_by_size_test.rb
110
+ test_files: []
data/.gitignore DELETED
@@ -1,6 +0,0 @@
1
- tmp/*
2
- !tmp/baz.rb
3
- pippi.log
4
- pippi_debug.log
5
- pippi*.gem
6
- out.txt
@@ -1 +0,0 @@
1
- ruby-2.1.2
data/Gemfile DELETED
@@ -1,3 +0,0 @@
1
- source "https://rubygems.org"
2
-
3
- gemspec
@@ -1,24 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- pippi (0.0.4)
5
-
6
- GEM
7
- remote: https://rubygems.org/
8
- specs:
9
- byebug (2.7.0)
10
- columnize (~> 0.3)
11
- debugger-linecache (~> 1.2)
12
- columnize (0.8.9)
13
- debugger-linecache (1.2.0)
14
- minitest (5.4.2)
15
- rake (10.1.0)
16
-
17
- PLATFORMS
18
- ruby
19
-
20
- DEPENDENCIES
21
- byebug (~> 2.7)
22
- minitest (~> 5.0)
23
- pippi!
24
- rake (~> 10.1)
data/Rakefile DELETED
@@ -1,11 +0,0 @@
1
- require 'rake/testtask'
2
- require 'rubygems'
3
- require 'bundler/setup'
4
- require 'pippi/tasks'
5
-
6
- Rake::TestTask.new do |t|
7
- t.libs << "test"
8
- t.pattern = "test/unit/**_test.rb"
9
- end
10
-
11
- task :default => [:test]
@@ -1,23 +0,0 @@
1
- $:.push File.expand_path("../lib", __FILE__)
2
-
3
- require 'pippi/version'
4
-
5
- Gem::Specification.new do |s|
6
- s.name = 'pippi'
7
- s.version = Pippi::VERSION
8
- s.authors = ["Tom Copeland"]
9
- s.email = ["tom@thomasleecopeland.com"]
10
- s.homepage = "https://github.com/tcopeland/pippi"
11
- s.summary = "A Ruby runtime code analyzer"
12
- s.description = "Pippi is a utility for locating suboptimal Ruby class API usage."
13
- s.license = "MIT"
14
- s.rubyforge_project = "none"
15
- s.files = `git ls-files`.split("\n")
16
- s.test_files = `git ls-files test/*`.split("\n")
17
- s.executables = "pippi"
18
- s.require_paths = ["lib"]
19
- s.add_development_dependency 'rake', '~> 10.1'
20
- s.add_development_dependency 'minitest', '~> 5.0'
21
- s.add_development_dependency 'byebug', '~> 2.7'
22
- s.required_ruby_version = '>= 2.0.0'
23
- end
@@ -1,6 +0,0 @@
1
- class Foo
2
- def bar
3
- x = [1,2,3]
4
- x.map {|y| [y, y+1] }.flatten
5
- end
6
- end
@@ -1,41 +0,0 @@
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, opts={})
8
- assert execute_pippi_on(foo_bar_code_sample(str, opts[:subclass] || ""), opts).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[:subclass] || ""), 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, subclass="")
24
- CodeSample.new.tap {|c| c.code_text = "class Foo #{subclass.size > 0 ? "< #{subclass}" : ""}; 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
@@ -1,13 +0,0 @@
1
- class Object
2
- def present?
3
- self.nil? || self.size == 0
4
- end
5
- end
6
-
7
-
8
- module ActiveSupport
9
- class TestCase
10
- def assert_equal(*args)
11
- end
12
- end
13
- end
@@ -1,7 +0,0 @@
1
- require 'rubygems'
2
- require 'bundler/setup'
3
- require 'minitest/autorun'
4
- require 'check_test'
5
- require 'byebug'
6
-
7
- require 'pippi'
@@ -1,50 +0,0 @@
1
- require "test_helper"
2
-
3
- class AssertWithNilTest < CheckTest
4
-
5
- def test_canonical_case_is_found
6
- assert_problems "x = 42 ; assert_equal(nil, x)", :include_rails_core_extensions => true, :subclass => "ActiveSupport::TestCase"
7
- end
8
-
9
- def test_non_nil_first_arg_doesnt_flag
10
- assert_no_problems "x = 42 ; assert_equal(42, x)", :include_rails_core_extensions => true, :subclass => "ActiveSupport::TestCase"
11
- end
12
-
13
- # Seems like there's some way to do this, but maybe not... anyhow, moving this rule to "buggy" for now
14
- # pippi> $ echo "foo(nil)" | ruby --dump insns
15
- # == disasm: <RubyVM::InstructionSequence:<main>@->=======================
16
- # 0000 trace 1 ( 1)
17
- # 0002 putself
18
- # 0003 putnil
19
- # 0004 opt_send_simple <callinfo!mid:foo, argc:1, FCALL|ARGS_SKIP>
20
- # 0006 leave
21
- # pippi> $ echo "foo(x)" | ruby --dump insns
22
- # == disasm: <RubyVM::InstructionSequence:<main>@->=======================
23
- # 0000 trace 1 ( 1)
24
- # 0002 putself
25
- # 0003 putself
26
- # 0004 opt_send_simple <callinfo!mid:x, argc:0, FCALL|VCALL|ARGS_SKIP>
27
- # 0006 opt_send_simple <callinfo!mid:foo, argc:1, FCALL|ARGS_SKIP>
28
- # 0008 leave
29
- # also consider
30
- =begin
31
- class Bar
32
- def buz(x)
33
- end
34
- def foo
35
- y = nil
36
- buz(y)
37
- RubyVM::InstructionSequence.of(method(__method__)).disasm.split("\n").each {|x| puts x }
38
- end
39
- end
40
- Bar.new.foo
41
- =end
42
- def test_nil_reference_first_arg_doesnt_flag
43
- assert_no_problems "x = 42 ; y = nil ; assert_equal(nil, x)", :include_rails_core_extensions => true, :subclass => "ActiveSupport::TestCase"
44
- end
45
-
46
- def test_three_arg_is_flagged
47
- assert_problems "x = 42 ; assert_equal(nil, x, 'whatevs')", :include_rails_core_extensions => true, :subclass => "ActiveSupport::TestCase"
48
- end
49
-
50
- end
@@ -1,17 +0,0 @@
1
- require "test_helper"
2
-
3
- class CheckSetMapperTest < Minitest::Test
4
-
5
- def test_should_find_predefined_sets
6
- csm = Pippi::CheckSetMapper.new("basic")
7
- assert csm.check_names.include?("SelectFollowedByFirst")
8
- end
9
-
10
- def test_should_allow_comma_separated_checkset_names
11
- csm = Pippi::CheckSetMapper.new("a,b")
12
- csm.predefined_sets = {"a" => ["foo"], "b" => ["bar"]}
13
- assert csm.check_names.include?("foo")
14
- assert csm.check_names.include?("bar")
15
- end
16
-
17
- end
@@ -1,38 +0,0 @@
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
@@ -1,23 +0,0 @@
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
@@ -1,25 +0,0 @@
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
@@ -1,29 +0,0 @@
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