pippi 0.0.10 → 0.0.11
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/README.md +4 -1
- data/lib/pippi/auto_runner.rb +3 -4
- data/lib/pippi/checks/method_sequence_checker.rb +20 -13
- data/lib/pippi/checks/method_sequence_finder.rb +29 -11
- data/lib/pippi/checks/select_followed_by_first.rb +7 -24
- data/lib/pippi/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c05baba246fbeb0947f998387800ef5e59bea9d0
|
4
|
+
data.tar.gz: 88364a935624811ace53ee739bf5f7bde545ad0e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8d7e3c193f1d5c74835b4eaf2e94cb5c4038a2a202ce7b89febeab9f7529ee8db5a6f49acd89f77578e16772846321898fb777502104064a72596232cbf5bbda
|
7
|
+
data.tar.gz: 4fa3c78158aa1469e4354e97826ccf4cba4cd41d560dfd7ca24bc00783d174d78417bd236161f1e0573eee081b798b8022ee0981ffbf967aa58592f3142d7c50
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -49,7 +49,7 @@ Here's how pippi stacks up using the [Aaron Quint](https://twitter.com/aq) [Ruby
|
|
49
49
|
* Realtimedness - finds stuff right away
|
50
50
|
* Special Abilities - ?
|
51
51
|
|
52
|
-
Finally, why "pippi"? Because Pippi Longstocking was a Thing-Finder
|
52
|
+
Finally, why "pippi"? Because Pippi Longstocking was a <a href="http://www.laredoisd.org/cdbooks/NOVELS/Pippi%20Longstocking/CH02.txt">Thing-Finder</a>, and pippi finds things.
|
53
53
|
|
54
54
|
## Usage
|
55
55
|
|
@@ -61,6 +61,8 @@ Finally, why "pippi"? Because Pippi Longstocking was a Thing-Finder, and pippi f
|
|
61
61
|
```ruby
|
62
62
|
if ENV['USE_PIPPI'].present?
|
63
63
|
Pippi::AutoRunner.new(:checkset => ENV['PIPPI_CHECKSET'] || "basic")
|
64
|
+
# you can also pass in an IO:
|
65
|
+
# Pippi::AutoRunner.new(:checkset => "basic", :io => $stdout)
|
64
66
|
end
|
65
67
|
```
|
66
68
|
* Run it:
|
@@ -373,6 +375,7 @@ rm -rf pippi_debug.log pippi.log .bundle/gems/pippi-0.0.1/ .bundle/cache/pippi-0
|
|
373
375
|
* [Evan Phoenix](https://twitter.com/evanphx)([@evanphx](https://github.com/evanphx)) for the idea of watching method invocations at runtime using metaprogramming rather than using `Tracepoint`.
|
374
376
|
* Hubert Dąbrowski: Ruby 2.0.0 fixes
|
375
377
|
* [Igor Kapkov](https://twitter.com/igasgeek)([@igas](https://github.com/igas)) documentation fixes
|
378
|
+
* [Josh Bodah](https://github.com/jbodah): Better logging support
|
376
379
|
* [LivingSocial](https://www.livingsocial.com/) for letting me develop and open source this utility.
|
377
380
|
* [Michael Bernstein](https://twitter.com/mrb_bk)([@mrb](https://github.com/mrb)) (of [CodeClimate](https://codeclimate.com/) fame) for an inspirational discussion of code anaysis in general.
|
378
381
|
* [Olle Jonsson](https://twitter.com/olleolleolle)([@olleolleolle](https://github.com/olleolleolle)) rubocop fixes
|
data/lib/pippi/auto_runner.rb
CHANGED
@@ -4,6 +4,7 @@ module Pippi
|
|
4
4
|
|
5
5
|
def initialize(opts = {})
|
6
6
|
checkset = opts.fetch(:checkset, 'basic')
|
7
|
+
@io = opts.fetch(:io, File.open('log/pippi.log', 'w'))
|
7
8
|
@ctx = Pippi::Context.new
|
8
9
|
|
9
10
|
@ctx.checks = Pippi::CheckLoader.new(@ctx, checkset).checks
|
@@ -15,10 +16,8 @@ module Pippi
|
|
15
16
|
if @ctx.checks.one? && @ctx.checks.first.kind_of?(Pippi::Checks::MethodSequenceFinder)
|
16
17
|
@ctx.checks.first.dump
|
17
18
|
end
|
18
|
-
|
19
|
-
@
|
20
|
-
outfile.syswrite("#{problem.to_text}\n")
|
21
|
-
end
|
19
|
+
@ctx.report.problems.each do |problem|
|
20
|
+
@io.puts(problem.to_text)
|
22
21
|
end
|
23
22
|
end
|
24
23
|
end
|
@@ -24,24 +24,31 @@ class MethodSequenceChecker
|
|
24
24
|
end
|
25
25
|
|
26
26
|
# e.g., "size" in "select followed by size"
|
27
|
-
second_method_decorator = Module
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
self.class
|
33
|
-
|
27
|
+
second_method_decorator = if method_sequence_check_instance.second_method_arity_type.kind_of?(Module)
|
28
|
+
method_sequence_check_instance.second_method_arity_type
|
29
|
+
else
|
30
|
+
Module.new do
|
31
|
+
define_method(method_sequence_check_instance.method2) do |*args, &blk|
|
32
|
+
# Using "self.class" implies that the first method invocation returns the same type as the receiver
|
33
|
+
# e.g., Array#select returns an Array. Would need to further parameterize this to get
|
34
|
+
# different behavior.
|
35
|
+
self.class.instance_variable_get(name).add_problem
|
36
|
+
if method_sequence_check_instance.should_check_subsequent_calls && method_sequence_check_instance.clazz_to_decorate == Array
|
37
|
+
problem_location = caller_locations.find { |c| c.to_s !~ /byebug|lib\/pippi\/checks/ }
|
38
|
+
self.class.instance_variable_get(name).method_names_that_indicate_this_is_being_used_as_a_collection.each do |this_means_its_ok_sym|
|
39
|
+
define_singleton_method(this_means_its_ok_sym, self.class.instance_variable_get(name).clear_fault_proc(self.class.instance_variable_get(name), problem_location))
|
40
|
+
end
|
41
|
+
end
|
42
|
+
if method_sequence_check_instance.second_method_arity_type == ARITY_TYPE_BLOCK_ARG
|
43
|
+
super(&blk)
|
44
|
+
elsif method_sequence_check_instance.second_method_arity_type == ARITY_TYPE_NONE
|
45
|
+
super()
|
34
46
|
end
|
35
|
-
end
|
36
|
-
if method_sequence_check_instance.second_method_arity_type == ARITY_TYPE_BLOCK_ARG
|
37
|
-
super(&blk)
|
38
|
-
elsif method_sequence_check_instance.second_method_arity_type == ARITY_TYPE_NONE
|
39
|
-
super()
|
40
47
|
end
|
41
48
|
end
|
42
49
|
end
|
43
50
|
|
44
|
-
# e.g., "select" in "select followed
|
51
|
+
# e.g., "select" in "select followed by size"
|
45
52
|
first_method_decorator = Module.new do
|
46
53
|
define_method(method_sequence_check_instance.method1) do |*args, &blk|
|
47
54
|
result = if method_sequence_check_instance.first_method_arity_type == ARITY_TYPE_BLOCK_ARG
|
@@ -1,10 +1,10 @@
|
|
1
1
|
module Pippi::Checks
|
2
2
|
|
3
3
|
module MyModule
|
4
|
-
def
|
4
|
+
def strip(*args, &blk)
|
5
5
|
result = super
|
6
6
|
if self.class._pippi_method_call_sequences
|
7
|
-
|
7
|
+
self.class._pippi_method_call_sequences.methods_to_track.each do |track_this|
|
8
8
|
result.define_singleton_method(track_this, track_it_proc(track_this))
|
9
9
|
end
|
10
10
|
end
|
@@ -23,18 +23,15 @@ module Pippi::Checks
|
|
23
23
|
end
|
24
24
|
end
|
25
25
|
end
|
26
|
-
|
27
|
-
def string_methods_to_track
|
28
|
-
[:ascii_only?, :b, :between?, :bytes, :bytesize, :byteslice, :capitalize, :capitalize!, :casecmp, :center, :chars, :chomp, :chomp!, :chop, :chop!, :chr, :clear, :codepoints, :concat, :count, :crypt, :delete, :delete!, :downcase, :downcase!, :dump, :each_byte, :each_char, :each_codepoint, :each_line, :empty?, :encode, :encode!, :encoding, :end_with?, :force_encoding, :getbyte, :gsub, :gsub!, :hex, :index, :insert, :intern, :length, :lines, :ljust, :lstrip, :lstrip!, :match, :next, :next!, :oct, :ord, :partition, :replace, :reverse, :reverse!, :rindex, :rjust, :rpartition, :rstrip, :rstrip!, :scan, :scrub, :scrub!, :setbyte, :size, :slice, :slice!, :split, :squeeze, :squeeze!, :start_with?, :strip, :strip!, :sub, :sub!, :succ, :succ!, :sum, :swapcase, :swapcase!, :to_c, :to_f, :to_i, :to_r, :to_str, :to_sym, :tr, :tr!, :tr_s, :tr_s!, :unpack, :upcase, :upcase!, :upto, :valid_encoding?]
|
29
|
-
end
|
30
26
|
|
31
|
-
def array_methods_to_track
|
32
|
-
[:all?, :any?, :assoc, :at, :bsearch, :chunk, :clear, :collect, :collect!, :collect_concat, :combination, :compact, :compact!, :concat, :count, :cycle, :delete, :delete_at, :delete_if, :detect, :drop, :drop_while, :each, :each_cons, :each_entry, :each_index, :each_slice, :each_with_index, :each_with_object, :empty?, :entries, :fetch, :fill, :find, :find_all, :find_index, :first, :flat_map, :flatten, :flatten!, :grep, :group_by, :histogram, :index, :inject, :insert, :join, :keep_if, :last, :lazy, :length, :map, :map!, :max, :max_by, :member?, :min, :min_by, :minmax, :minmax_by, :none?, :one?, :pack, :partition, :permutation, :pop, :product, :push, :rassoc, :reduce, :reject, :reject!, :repeated_combination, :repeated_permutation, :replace, :reverse, :reverse!, :reverse_each, :rindex, :rotate, :rotate!, :sample, :select, :select!, :shift, :shuffle, :shuffle!, :size, :slice, :slice!, :slice_before, :sort, :sort!, :sort_by, :sort_by!, :take, :take_while, :to_a, :to_ary, :to_h, :transpose, :uniq, :uniq!, :unshift, :values_at, :zip]
|
33
|
-
end
|
34
27
|
end
|
35
28
|
|
36
29
|
class MethodSequenceFinder < Check
|
37
30
|
|
31
|
+
STRING_METHODS_TO_TRACK = [:ascii_only?, :b, :between?, :bytes, :bytesize, :byteslice, :capitalize, :capitalize!, :casecmp, :center, :chars, :chomp, :chomp!, :chop, :chop!, :chr, :clear, :codepoints, :concat, :count, :crypt, :delete, :delete!, :downcase, :downcase!, :dump, :each_byte, :each_char, :each_codepoint, :each_line, :empty?, :encode, :encode!, :encoding, :end_with?, :force_encoding, :getbyte, :gsub, :gsub!, :hex, :index, :insert, :intern, :length, :lines, :ljust, :lstrip, :lstrip!, :match, :next, :next!, :oct, :ord, :partition, :replace, :reverse, :reverse!, :rindex, :rjust, :rpartition, :rstrip, :rstrip!, :scan, :scrub, :scrub!, :setbyte, :size, :slice, :slice!, :split, :squeeze, :squeeze!, :start_with?, :strip, :strip!, :sub, :sub!, :succ, :succ!, :sum, :swapcase, :swapcase!, :to_c, :to_f, :to_i, :to_r, :to_str, :to_sym, :tr, :tr!, :tr_s, :tr_s!, :unpack, :upcase, :upcase!, :upto, :valid_encoding?]
|
32
|
+
|
33
|
+
ARRAY_METHODS_TO_TRACK = [:all?, :any?, :assoc, :at, :bsearch, :chunk, :clear, :collect, :collect!, :collect_concat, :combination, :compact, :compact!, :concat, :count, :cycle, :delete, :delete_at, :delete_if, :detect, :drop, :drop_while, :each, :each_cons, :each_entry, :each_index, :each_slice, :each_with_index, :each_with_object, :empty?, :entries, :fetch, :fill, :find, :find_all, :find_index, :first, :flat_map, :flatten, :flatten!, :grep, :group_by, :histogram, :index, :inject, :insert, :join, :keep_if, :last, :lazy, :length, :map, :map!, :max, :max_by, :member?, :min, :min_by, :minmax, :minmax_by, :none?, :one?, :pack, :partition, :permutation, :pop, :product, :push, :rassoc, :reduce, :reject, :reject!, :repeated_combination, :repeated_permutation, :replace, :reverse, :reverse!, :reverse_each, :rindex, :rotate, :rotate!, :sample, :select, :select!, :shift, :shuffle, :shuffle!, :size, :slice, :slice!, :slice_before, :sort, :sort!, :sort_by, :sort_by!, :take, :take_while, :to_a, :to_ary, :to_h, :transpose, :uniq, :uniq!, :unshift, :values_at, :zip]
|
34
|
+
|
38
35
|
class Record
|
39
36
|
attr_reader :path, :lineno, :meth1, :meth2
|
40
37
|
def initialize(path, lineno, meth1, meth2)
|
@@ -52,22 +49,43 @@ module Pippi::Checks
|
|
52
49
|
end
|
53
50
|
end
|
54
51
|
|
55
|
-
attr_reader :sequences, :clazz_to_decorate
|
52
|
+
attr_reader :sequences, :clazz_to_decorate, :starting_method
|
53
|
+
|
54
|
+
=begin
|
55
|
+
To add a new sequence finder where the first method is on String, change the starting_method below
|
56
|
+
and also change the method name up in MyModule. To change it to a different class you must also
|
57
|
+
change :clazz_to_decorate
|
56
58
|
|
59
|
+
=end
|
57
60
|
def initialize(ctx)
|
58
61
|
super
|
59
62
|
@clazz_to_decorate = String
|
63
|
+
@starting_method = "strip"
|
64
|
+
|
60
65
|
@sequences = Set.new
|
61
66
|
end
|
62
67
|
|
68
|
+
def methods_to_track
|
69
|
+
if clazz_to_decorate == String
|
70
|
+
STRING_METHODS_TO_TRACK
|
71
|
+
elsif clazz_to_decorate == Array
|
72
|
+
ARRAY_METHODS_TO_TRACK
|
73
|
+
else
|
74
|
+
raise "Unhandled class #{clazz_to_decorate}"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
63
78
|
def dump
|
64
79
|
@sequences.map {|r| "#{r.meth1}:#{r.meth2}" }.inject({}) {|m,i| m[i] ||= 0 ; m[i] += 1 ; m }.to_a.sort_by {|x| x[1] }.reverse.each do |r|
|
65
80
|
puts "#{r[0].split(':')[0]} followed by #{r[0].split(':')[1]} occurred #{r[1]} times\n"
|
81
|
+
@sequences.select {|s| (s.meth1 == r[0].split(':')[0]) && (s.meth2 == r[0].split(':')[1].to_sym) }.each do |s|
|
82
|
+
puts "#{s.path}: #{s.lineno}"
|
83
|
+
end
|
66
84
|
end
|
67
85
|
end
|
68
86
|
|
69
87
|
def found_sequence(method_name, location)
|
70
|
-
@sequences << Record.new(location.path, location.lineno,
|
88
|
+
@sequences << Record.new(location.path, location.lineno, starting_method, method_name)
|
71
89
|
end
|
72
90
|
|
73
91
|
def decorate
|
@@ -1,11 +1,10 @@
|
|
1
1
|
module Pippi::Checks
|
2
2
|
class SelectFollowedByFirst < Check
|
3
3
|
|
4
|
-
|
5
|
-
module MyFirst
|
4
|
+
module MyModule
|
6
5
|
def first(elements = nil)
|
7
6
|
unless elements
|
8
|
-
self.class.
|
7
|
+
self.class._pippi_check_selectfollowedbyfirst.add_problem
|
9
8
|
end
|
10
9
|
if elements
|
11
10
|
super(elements)
|
@@ -15,29 +14,13 @@ module Pippi::Checks
|
|
15
14
|
end
|
16
15
|
end
|
17
16
|
|
18
|
-
|
19
|
-
|
20
|
-
result = super
|
21
|
-
if self.class._pippi_check_select_followed_by_first.nil?
|
22
|
-
# Ignore Array subclasses since select or first may have difference meanings
|
23
|
-
else
|
24
|
-
result.extend MyFirst
|
25
|
-
self.class._pippi_check_select_followed_by_first.array_mutator_methods.each do |this_means_its_ok_sym|
|
26
|
-
result.define_singleton_method(this_means_its_ok_sym, self.class._pippi_check_select_followed_by_first.its_ok_watcher_proc(MyFirst, :first))
|
27
|
-
end
|
28
|
-
end
|
29
|
-
result
|
30
|
-
end
|
17
|
+
def decorate
|
18
|
+
@mycheck.decorate
|
31
19
|
end
|
32
20
|
|
33
|
-
def
|
34
|
-
|
35
|
-
|
36
|
-
class << self
|
37
|
-
attr_reader :_pippi_check_select_followed_by_first
|
38
|
-
end
|
39
|
-
prepend MySelect
|
40
|
-
end
|
21
|
+
def initialize(ctx)
|
22
|
+
super
|
23
|
+
@mycheck = MethodSequenceChecker.new(self, Array, "select", "first", MethodSequenceChecker::ARITY_TYPE_BLOCK_ARG, MyModule, true)
|
41
24
|
end
|
42
25
|
|
43
26
|
class Documentation
|
data/lib/pippi/version.rb
CHANGED
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
|
+
version: 0.0.11
|
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-12-
|
11
|
+
date: 2014-12-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|