pippi 0.0.4 → 0.0.5

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 (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