pippi 0.0.9 → 0.0.10
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 +18 -3
- data/doc/docs.md +16 -0
- data/lib/pippi.rb +3 -0
- data/lib/pippi/auto_runner.rb +6 -1
- data/lib/pippi/check_set_mapper.rb +4 -0
- data/lib/pippi/checks/map_followed_by_flatten.rb +2 -0
- data/lib/pippi/checks/method_sequence_checker.rb +80 -0
- data/lib/pippi/checks/method_sequence_finder.rb +84 -0
- data/lib/pippi/checks/reverse_followed_by_each.rb +2 -0
- data/lib/pippi/checks/select_followed_by_any.rb +5 -33
- data/lib/pippi/checks/select_followed_by_empty.rb +5 -33
- data/lib/pippi/checks/select_followed_by_first.rb +2 -0
- data/lib/pippi/checks/select_followed_by_none.rb +28 -0
- data/lib/pippi/checks/select_followed_by_select.rb +5 -26
- data/lib/pippi/checks/select_followed_by_size.rb +5 -31
- data/lib/pippi/context.rb +3 -0
- data/lib/pippi/tasks.rb +1 -1
- data/lib/pippi/version.rb +1 -1
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5ad992cb9bc5aa2b1e2ea7ee7acbe531cbb2432e
|
4
|
+
data.tar.gz: 564a5feffa9d49e8eb28d878f4635c93ba19921e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9e1046f43ec28739b33a224136a3c852ea9a0c91579f5c22bff9f102e0fff483bb0ce9c97a9b93d2cb34d4a1c6fca390bfff9de1ec2020488d96d7ff6d88faec
|
7
|
+
data.tar.gz: 822431d1b25e296483f2031a8ec581788615e6c1d89b02b9fee1fd4474172c72e8b753cbcaaae81f1e51564cc881a6ae38a7dd92ff3dfa5ec9dad96bb15f5879
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -189,6 +189,22 @@ Instead, consider doing this:
|
|
189
189
|
[1,2,3].detect {|x| x > 1 }
|
190
190
|
```
|
191
191
|
|
192
|
+
#### SelectFollowedByNone
|
193
|
+
|
194
|
+
Don't use select followed by none?; use none? with a block instead
|
195
|
+
|
196
|
+
For example, rather than doing this:
|
197
|
+
|
198
|
+
```ruby
|
199
|
+
[1,2,3].select {|x| x > 1 }.none?
|
200
|
+
```
|
201
|
+
|
202
|
+
Instead, consider doing this:
|
203
|
+
|
204
|
+
```ruby
|
205
|
+
[1,2,3].none? {|x| x > 1 }
|
206
|
+
```
|
207
|
+
|
192
208
|
#### SelectFollowedBySelect
|
193
209
|
|
194
210
|
Don't use consecutive select blocks; use a single select instead
|
@@ -257,8 +273,6 @@ Instead, consider doing this:
|
|
257
273
|
## Ideas for other problems to detect:
|
258
274
|
|
259
275
|
```ruby
|
260
|
-
# Don't use select followed by compact, use select with the nil inside the block
|
261
|
-
|
262
276
|
# unnecessary assignment since String#strip! mutates receiver
|
263
277
|
# wrong
|
264
278
|
x = x.strip!
|
@@ -320,7 +334,8 @@ end
|
|
320
334
|
|
321
335
|
* Clean up this initial hacked out metaprogramming
|
322
336
|
* Do more checks
|
323
|
-
*
|
337
|
+
* Finish refactoring duplicated code into MethodSequenceChecker
|
338
|
+
* Use MethodSequenceFinder to do something with String
|
324
339
|
|
325
340
|
## Developing
|
326
341
|
|
data/doc/docs.md
CHANGED
@@ -64,6 +64,22 @@ Instead, consider doing this:
|
|
64
64
|
[1,2,3].detect {|x| x > 1 }
|
65
65
|
```
|
66
66
|
|
67
|
+
#### SelectFollowedByNone
|
68
|
+
|
69
|
+
Don't use select followed by none?; use none? with a block instead
|
70
|
+
|
71
|
+
For example, rather than doing this:
|
72
|
+
|
73
|
+
```ruby
|
74
|
+
[1,2,3].select {|x| x > 1 }.none?
|
75
|
+
```
|
76
|
+
|
77
|
+
Instead, consider doing this:
|
78
|
+
|
79
|
+
```ruby
|
80
|
+
[1,2,3].none? {|x| x > 1 }
|
81
|
+
```
|
82
|
+
|
67
83
|
#### SelectFollowedBySelect
|
68
84
|
|
69
85
|
Don't use consecutive select blocks; use a single select instead
|
data/lib/pippi.rb
CHANGED
@@ -14,6 +14,9 @@ require 'pippi/checks/select_followed_by_first'
|
|
14
14
|
require 'pippi/checks/select_followed_by_size'
|
15
15
|
require 'pippi/checks/select_followed_by_empty'
|
16
16
|
require 'pippi/checks/select_followed_by_any'
|
17
|
+
require 'pippi/checks/select_followed_by_none'
|
17
18
|
require 'pippi/checks/select_followed_by_select'
|
19
|
+
require 'pippi/checks/method_sequence_finder'
|
20
|
+
require 'pippi/checks/method_sequence_checker'
|
18
21
|
require 'pippi/checks/assert_with_nil'
|
19
22
|
require 'pippi/checks/debug_check'
|
data/lib/pippi/auto_runner.rb
CHANGED
@@ -5,11 +5,16 @@ module Pippi
|
|
5
5
|
def initialize(opts = {})
|
6
6
|
checkset = opts.fetch(:checkset, 'basic')
|
7
7
|
@ctx = Pippi::Context.new
|
8
|
-
|
8
|
+
|
9
|
+
@ctx.checks = Pippi::CheckLoader.new(@ctx, checkset).checks
|
10
|
+
@ctx.checks.each(&:decorate)
|
9
11
|
at_exit { dump }
|
10
12
|
end
|
11
13
|
|
12
14
|
def dump
|
15
|
+
if @ctx.checks.one? && @ctx.checks.first.kind_of?(Pippi::Checks::MethodSequenceFinder)
|
16
|
+
@ctx.checks.first.dump
|
17
|
+
end
|
13
18
|
File.open('log/pippi.log', 'w') do |outfile|
|
14
19
|
@ctx.report.problems.each do |problem|
|
15
20
|
outfile.syswrite("#{problem.to_text}\n")
|
@@ -23,12 +23,16 @@ module Pippi
|
|
23
23
|
"SelectFollowedByFirst",
|
24
24
|
"SelectFollowedBySize",
|
25
25
|
"SelectFollowedByAny",
|
26
|
+
"SelectFollowedByNone",
|
26
27
|
"SelectFollowedByEmpty",
|
27
28
|
"ReverseFollowedByEach",
|
28
29
|
"SelectFollowedBySelect"
|
29
30
|
],
|
30
31
|
"training" => [
|
31
32
|
],
|
33
|
+
"research" => [
|
34
|
+
"MethodSequenceFinder",
|
35
|
+
],
|
32
36
|
"buggy" => [
|
33
37
|
"AssertWithNil",
|
34
38
|
"MapFollowedByFlatten",
|
@@ -0,0 +1,80 @@
|
|
1
|
+
class MethodSequenceChecker
|
2
|
+
|
3
|
+
ARITY_TYPE_BLOCK_ARG = 1
|
4
|
+
ARITY_TYPE_NONE = 2
|
5
|
+
|
6
|
+
attr_reader :check, :clazz_to_decorate, :method1, :method2, :first_method_arity_type, :second_method_arity_type, :should_check_subsequent_calls
|
7
|
+
|
8
|
+
def initialize(check, clazz_to_decorate, method1, method2, first_method_arity_type, second_method_arity_type, should_check_subsequent_calls)
|
9
|
+
@check = check
|
10
|
+
@clazz_to_decorate = clazz_to_decorate
|
11
|
+
@method1 = method1
|
12
|
+
@method2 = method2
|
13
|
+
@first_method_arity_type = first_method_arity_type
|
14
|
+
@second_method_arity_type = second_method_arity_type
|
15
|
+
@should_check_subsequent_calls = should_check_subsequent_calls
|
16
|
+
end
|
17
|
+
|
18
|
+
def decorate
|
19
|
+
clazz_to_decorate.class_exec(check, self) do |my_check, method_sequence_check_instance|
|
20
|
+
name = "@_pippi_check_#{my_check.class.name.split('::').last.downcase}"
|
21
|
+
self.instance_variable_set(name, my_check)
|
22
|
+
self.class.send(:define_method, name[1..-1]) do
|
23
|
+
instance_variable_get(name)
|
24
|
+
end
|
25
|
+
|
26
|
+
# e.g., "size" in "select followed by size"
|
27
|
+
second_method_decorator = Module.new do
|
28
|
+
define_method(method_sequence_check_instance.method2) do |*args, &blk|
|
29
|
+
self.class.instance_variable_get(name).add_problem
|
30
|
+
if method_sequence_check_instance.should_check_subsequent_calls && method_sequence_check_instance.clazz_to_decorate == Array
|
31
|
+
problem_location = caller_locations.find { |c| c.to_s !~ /byebug|lib\/pippi\/checks/ }
|
32
|
+
self.class.instance_variable_get(name).method_names_that_indicate_this_is_being_used_as_a_collection.each do |this_means_its_ok_sym|
|
33
|
+
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))
|
34
|
+
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
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# e.g., "select" in "select followed ARITY_TYPE_NONEze"
|
45
|
+
first_method_decorator = Module.new do
|
46
|
+
define_method(method_sequence_check_instance.method1) do |*args, &blk|
|
47
|
+
result = if method_sequence_check_instance.first_method_arity_type == ARITY_TYPE_BLOCK_ARG
|
48
|
+
super(&blk)
|
49
|
+
elsif method_sequence_check_instance.first_method_arity_type == ARITY_TYPE_NONE
|
50
|
+
super()
|
51
|
+
end
|
52
|
+
if self.class.instance_variable_get(name)
|
53
|
+
result.extend second_method_decorator
|
54
|
+
self.class.instance_variable_get(name).array_mutator_methods.each do |this_means_its_ok_sym|
|
55
|
+
result.define_singleton_method(this_means_its_ok_sym, self.class.instance_variable_get(name).its_ok_watcher_proc(second_method_decorator, method_sequence_check_instance.method2))
|
56
|
+
end
|
57
|
+
end
|
58
|
+
result
|
59
|
+
end
|
60
|
+
end
|
61
|
+
prepend first_method_decorator
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def array_mutator_methods
|
66
|
+
[:collect!, :compact!, :flatten!, :map!, :reject!, :reverse!, :rotate!, :select!, :shuffle!, :slice!, :sort!, :sort_by!, :uniq!]
|
67
|
+
end
|
68
|
+
|
69
|
+
def its_ok_watcher_proc(clazz, method_name)
|
70
|
+
proc do |*args, &blk|
|
71
|
+
begin
|
72
|
+
singleton_class.ancestors.find { |x| x == clazz }.instance_eval { remove_method method_name }
|
73
|
+
rescue NameError
|
74
|
+
return super(*args, &blk)
|
75
|
+
else
|
76
|
+
return super(*args, &blk)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module Pippi::Checks
|
2
|
+
|
3
|
+
module MyModule
|
4
|
+
def split(&blk)
|
5
|
+
result = super
|
6
|
+
if self.class._pippi_method_call_sequences
|
7
|
+
array_methods_to_track.each do |track_this|
|
8
|
+
result.define_singleton_method(track_this, track_it_proc(track_this))
|
9
|
+
end
|
10
|
+
end
|
11
|
+
result
|
12
|
+
end
|
13
|
+
|
14
|
+
def track_it_proc(method_name)
|
15
|
+
location = caller_locations.find { |c| c.to_s !~ /byebug|lib\/pippi\/checks/ }
|
16
|
+
proc do |*args, &blk|
|
17
|
+
begin
|
18
|
+
self.class._pippi_method_call_sequences.found_sequence(method_name, location)
|
19
|
+
rescue NameError
|
20
|
+
return super(*args, &blk)
|
21
|
+
else
|
22
|
+
return super(*args, &blk)
|
23
|
+
end
|
24
|
+
end
|
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
|
+
|
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
|
+
end
|
35
|
+
|
36
|
+
class MethodSequenceFinder < Check
|
37
|
+
|
38
|
+
class Record
|
39
|
+
attr_reader :path, :lineno, :meth1, :meth2
|
40
|
+
def initialize(path, lineno, meth1, meth2)
|
41
|
+
@path = path
|
42
|
+
@lineno = lineno
|
43
|
+
@meth1 = meth1
|
44
|
+
@meth2 = meth2
|
45
|
+
end
|
46
|
+
def eql?(other)
|
47
|
+
path == other.path && lineno == other.lineno && meth1 == other.meth1 && meth2 == other.meth2
|
48
|
+
end
|
49
|
+
def hash
|
50
|
+
require 'zlib'
|
51
|
+
Zlib.crc32("#{path}:#{lineno}:#{meth1}:#{meth2}")
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
attr_reader :sequences, :clazz_to_decorate
|
56
|
+
|
57
|
+
def initialize(ctx)
|
58
|
+
super
|
59
|
+
@clazz_to_decorate = String
|
60
|
+
@sequences = Set.new
|
61
|
+
end
|
62
|
+
|
63
|
+
def dump
|
64
|
+
@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
|
+
puts "#{r[0].split(':')[0]} followed by #{r[0].split(':')[1]} occurred #{r[1]} times\n"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def found_sequence(method_name, location)
|
70
|
+
@sequences << Record.new(location.path, location.lineno, "map", method_name)
|
71
|
+
end
|
72
|
+
|
73
|
+
def decorate
|
74
|
+
clazz_to_decorate.class_exec(self) do |my_check|
|
75
|
+
@_pippi_method_call_sequences = my_check
|
76
|
+
class << self
|
77
|
+
attr_reader :_pippi_method_call_sequences
|
78
|
+
end
|
79
|
+
prepend MyModule
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
end
|
@@ -2,41 +2,13 @@ module Pippi::Checks
|
|
2
2
|
|
3
3
|
class SelectFollowedByAny < Check
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
self.class._pippi_check_select_followed_by_any.add_problem
|
8
|
-
problem_location = caller_locations.find { |c| c.to_s !~ /byebug|lib\/pippi\/checks/ }
|
9
|
-
self.class._pippi_check_select_followed_by_any.method_names_that_indicate_this_is_being_used_as_a_collection.each do |this_means_its_ok_sym|
|
10
|
-
define_singleton_method(this_means_its_ok_sym, self.class._pippi_check_select_followed_by_any.clear_fault_proc(self.class._pippi_check_select_followed_by_any, problem_location))
|
11
|
-
end
|
12
|
-
super
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
module MySelect
|
17
|
-
def select(&blk)
|
18
|
-
result = super
|
19
|
-
if self.class._pippi_check_select_followed_by_any.nil?
|
20
|
-
# Ignore Array subclasses since select or any may have difference meanings
|
21
|
-
# elsif defined?(ActiveRecord::Relation) && self.class.kind_of?(ActiveRecord::Relation) # maybe also this
|
22
|
-
else
|
23
|
-
result.extend MyAny
|
24
|
-
self.class._pippi_check_select_followed_by_any.array_mutator_methods.each do |this_means_its_ok_sym|
|
25
|
-
result.define_singleton_method(this_means_its_ok_sym, self.class._pippi_check_select_followed_by_any.its_ok_watcher_proc(MyAny, :any?))
|
26
|
-
end
|
27
|
-
end
|
28
|
-
result
|
29
|
-
end
|
5
|
+
def decorate
|
6
|
+
@mycheck.decorate
|
30
7
|
end
|
31
8
|
|
32
|
-
def
|
33
|
-
|
34
|
-
|
35
|
-
def self._pippi_check_select_followed_by_any
|
36
|
-
@_pippi_check_select_followed_by_any
|
37
|
-
end
|
38
|
-
prepend MySelect
|
39
|
-
end
|
9
|
+
def initialize(ctx)
|
10
|
+
super
|
11
|
+
@mycheck = MethodSequenceChecker.new(self, Array, "select", "any?", MethodSequenceChecker::ARITY_TYPE_BLOCK_ARG, MethodSequenceChecker::ARITY_TYPE_NONE, true)
|
40
12
|
end
|
41
13
|
|
42
14
|
class Documentation
|
@@ -2,41 +2,13 @@ module Pippi::Checks
|
|
2
2
|
|
3
3
|
class SelectFollowedByEmpty < Check
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
self.class._pippi_check_select_followed_by_empty.add_problem
|
8
|
-
problem_location = caller_locations.find { |c| c.to_s !~ /byebug|lib\/pippi\/checks/ }
|
9
|
-
self.class._pippi_check_select_followed_by_empty.method_names_that_indicate_this_is_being_used_as_a_collection.each do |this_means_its_ok_sym|
|
10
|
-
define_singleton_method(this_means_its_ok_sym, self.class._pippi_check_select_followed_by_empty.clear_fault_proc(self.class._pippi_check_select_followed_by_empty, problem_location))
|
11
|
-
end
|
12
|
-
super
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
module MySelect
|
17
|
-
def select(&blk)
|
18
|
-
result = super
|
19
|
-
if self.class._pippi_check_select_followed_by_empty.nil?
|
20
|
-
# Ignore Array subclasses since select or empty may have difference meanings
|
21
|
-
# elsif defined?(ActiveRecord::Relation) && self.class.kind_of?(ActiveRecord::Relation) # maybe also this
|
22
|
-
else
|
23
|
-
result.extend MyEmpty
|
24
|
-
self.class._pippi_check_select_followed_by_empty.array_mutator_methods.each do |this_means_its_ok_sym|
|
25
|
-
result.define_singleton_method(this_means_its_ok_sym, self.class._pippi_check_select_followed_by_empty.its_ok_watcher_proc(MyEmpty, :empty?))
|
26
|
-
end
|
27
|
-
end
|
28
|
-
result
|
29
|
-
end
|
5
|
+
def decorate
|
6
|
+
@mycheck.decorate
|
30
7
|
end
|
31
8
|
|
32
|
-
def
|
33
|
-
|
34
|
-
|
35
|
-
def self._pippi_check_select_followed_by_empty
|
36
|
-
@_pippi_check_select_followed_by_empty
|
37
|
-
end
|
38
|
-
prepend MySelect
|
39
|
-
end
|
9
|
+
def initialize(ctx)
|
10
|
+
super
|
11
|
+
@mycheck = MethodSequenceChecker.new(self, Array, "select", "empty?", MethodSequenceChecker::ARITY_TYPE_BLOCK_ARG, MethodSequenceChecker::ARITY_TYPE_NONE, true)
|
40
12
|
end
|
41
13
|
|
42
14
|
class Documentation
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Pippi::Checks
|
2
|
+
|
3
|
+
class SelectFollowedByNone < Check
|
4
|
+
|
5
|
+
def decorate
|
6
|
+
@mycheck.decorate
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize(ctx)
|
10
|
+
super
|
11
|
+
@mycheck = MethodSequenceChecker.new(self, Array, "select", "none?", MethodSequenceChecker::ARITY_TYPE_BLOCK_ARG, MethodSequenceChecker::ARITY_TYPE_NONE, true)
|
12
|
+
end
|
13
|
+
|
14
|
+
class Documentation
|
15
|
+
def description
|
16
|
+
"Don't use select followed by none?; use none? with a block instead"
|
17
|
+
end
|
18
|
+
def sample
|
19
|
+
"[1,2,3].select {|x| x > 1 }.none?"
|
20
|
+
end
|
21
|
+
def instead_use
|
22
|
+
"[1,2,3].none? {|x| x > 1 }"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
@@ -2,34 +2,13 @@ module Pippi::Checks
|
|
2
2
|
|
3
3
|
class SelectFollowedBySelect < Check
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
self.class._pippi_check_select_followed_by_select.add_problem
|
8
|
-
super
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
module MyFirstSelect
|
13
|
-
def select(&blk)
|
14
|
-
result = super
|
15
|
-
if !self.class._pippi_check_select_followed_by_select.nil?
|
16
|
-
result.extend MySecondSelect
|
17
|
-
self.class._pippi_check_select_followed_by_select.array_mutator_methods.each do |this_means_its_ok_sym|
|
18
|
-
result.define_singleton_method(this_means_its_ok_sym, self.class._pippi_check_select_followed_by_select.its_ok_watcher_proc(MySecondSelect, :select))
|
19
|
-
end
|
20
|
-
end
|
21
|
-
result
|
22
|
-
end
|
5
|
+
def decorate
|
6
|
+
@mycheck.decorate
|
23
7
|
end
|
24
8
|
|
25
|
-
def
|
26
|
-
|
27
|
-
|
28
|
-
def self._pippi_check_select_followed_by_select
|
29
|
-
@_pippi_check_select_followed_by_select
|
30
|
-
end
|
31
|
-
prepend MyFirstSelect
|
32
|
-
end
|
9
|
+
def initialize(ctx)
|
10
|
+
super
|
11
|
+
@mycheck = MethodSequenceChecker.new(self, Array, "select", "select", MethodSequenceChecker::ARITY_TYPE_BLOCK_ARG, MethodSequenceChecker::ARITY_TYPE_BLOCK_ARG, false)
|
33
12
|
end
|
34
13
|
|
35
14
|
class Documentation
|
@@ -1,39 +1,13 @@
|
|
1
1
|
module Pippi::Checks
|
2
2
|
class SelectFollowedBySize < Check
|
3
|
-
module MySize
|
4
|
-
def size
|
5
|
-
self.class._pippi_check_select_followed_by_size.add_problem
|
6
|
-
problem_location = caller_locations.find { |c| c.to_s !~ /byebug|lib\/pippi\/checks/ }
|
7
|
-
self.class._pippi_check_select_followed_by_size.method_names_that_indicate_this_is_being_used_as_a_collection.each do |this_means_its_ok_sym|
|
8
|
-
define_singleton_method(this_means_its_ok_sym, self.class._pippi_check_select_followed_by_size.clear_fault_proc(self.class._pippi_check_select_followed_by_size, problem_location))
|
9
|
-
end
|
10
|
-
super()
|
11
|
-
end
|
12
|
-
end
|
13
3
|
|
14
|
-
|
15
|
-
|
16
|
-
result = super
|
17
|
-
if self.class._pippi_check_select_followed_by_size.nil?
|
18
|
-
# Ignore Array subclasses since select or size may have difference meanings
|
19
|
-
else
|
20
|
-
result.extend MySize
|
21
|
-
self.class._pippi_check_select_followed_by_size.array_mutator_methods.each do |this_means_its_ok_sym|
|
22
|
-
result.define_singleton_method(this_means_its_ok_sym, self.class._pippi_check_select_followed_by_size.its_ok_watcher_proc(MySize, :size))
|
23
|
-
end
|
24
|
-
end
|
25
|
-
result
|
26
|
-
end
|
4
|
+
def decorate
|
5
|
+
@mycheck.decorate
|
27
6
|
end
|
28
7
|
|
29
|
-
def
|
30
|
-
|
31
|
-
|
32
|
-
class << self
|
33
|
-
attr_reader :_pippi_check_select_followed_by_size
|
34
|
-
end
|
35
|
-
prepend MySelect
|
36
|
-
end
|
8
|
+
def initialize(ctx)
|
9
|
+
super
|
10
|
+
@mycheck = MethodSequenceChecker.new(self, Array, "select", "size", MethodSequenceChecker::ARITY_TYPE_BLOCK_ARG, MethodSequenceChecker::ARITY_TYPE_NONE, true)
|
37
11
|
end
|
38
12
|
|
39
13
|
class Documentation
|
data/lib/pippi/context.rb
CHANGED
data/lib/pippi/tasks.rb
CHANGED
@@ -4,7 +4,7 @@ module Pippi
|
|
4
4
|
class Documentation
|
5
5
|
def generate
|
6
6
|
str = ''
|
7
|
-
Pippi::CheckSetMapper.new("").predefined_sets.sort.select {|k,v| v.any? }.each do |checkset_name, checks|
|
7
|
+
Pippi::CheckSetMapper.new("").predefined_sets.sort.select {|k,v| k != "research" && v.any? }.each do |checkset_name, checks|
|
8
8
|
str << "### #{checkset_name}\n"
|
9
9
|
checks.sort.each do |check|
|
10
10
|
obj = Object.const_get("Pippi::Checks::#{check}::Documentation").new
|
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.10
|
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-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -73,10 +73,13 @@ files:
|
|
73
73
|
- lib/pippi/checks/check.rb
|
74
74
|
- lib/pippi/checks/debug_check.rb
|
75
75
|
- lib/pippi/checks/map_followed_by_flatten.rb
|
76
|
+
- lib/pippi/checks/method_sequence_checker.rb
|
77
|
+
- lib/pippi/checks/method_sequence_finder.rb
|
76
78
|
- lib/pippi/checks/reverse_followed_by_each.rb
|
77
79
|
- lib/pippi/checks/select_followed_by_any.rb
|
78
80
|
- lib/pippi/checks/select_followed_by_empty.rb
|
79
81
|
- lib/pippi/checks/select_followed_by_first.rb
|
82
|
+
- lib/pippi/checks/select_followed_by_none.rb
|
80
83
|
- lib/pippi/checks/select_followed_by_select.rb
|
81
84
|
- lib/pippi/checks/select_followed_by_size.rb
|
82
85
|
- lib/pippi/context.rb
|