pippi 0.0.10 → 0.0.11
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 +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
|