private_please 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. data/.gitignore +0 -1
  2. data/.travis.yml +10 -0
  3. data/CHANGELOG +8 -0
  4. data/README.md +92 -42
  5. data/bin/pp_ruby +62 -0
  6. data/bin/ruby_pp +62 -0
  7. data/doc/dev_notes.txt +32 -0
  8. data/doc/fixtures/complex.rb +103 -0
  9. data/doc/fixtures/empty_class.rb +7 -0
  10. data/doc/fixtures/fixture_helper.rb +8 -0
  11. data/doc/fixtures/sample.rb +57 -0
  12. data/lib/private_please/candidate.rb +10 -2
  13. data/lib/private_please/report/table.rb +44 -0
  14. data/lib/private_please/report.rb +6 -0
  15. data/lib/private_please/reporter/base.rb +28 -0
  16. data/lib/private_please/reporter/helpers/options_helpers.rb +12 -0
  17. data/lib/private_please/reporter/helpers/text_table_helpers.rb +9 -0
  18. data/lib/private_please/{report/reporter.rb → reporter/simple_text.rb} +12 -18
  19. data/lib/private_please/reporter/templates/simple.txt.erb +77 -0
  20. data/lib/private_please/reporter.rb +8 -0
  21. data/lib/private_please/storage/methods_names.rb +1 -0
  22. data/lib/private_please/storage.rb +8 -0
  23. data/lib/private_please/tracking/extension.rb +3 -10
  24. data/lib/private_please/tracking/instrumentor.rb +13 -35
  25. data/lib/private_please/tracking/instruments_all_methods_below.rb +28 -0
  26. data/lib/private_please/tracking/instruments_automatically_all_methods_in_all_classes.rb +52 -0
  27. data/lib/private_please/tracking/line_change_tracker.rb +8 -18
  28. data/lib/private_please/tracking/utils.rb +34 -0
  29. data/lib/private_please/tracking.rb +48 -0
  30. data/lib/private_please/version.rb +1 -1
  31. data/lib/private_please.rb +27 -44
  32. data/private_please.gemspec +7 -1
  33. data/spec/{01_marking_candidate_methods_to_observe_spec.rb → 01_tracking_candidate_methods/explicitely_with_the_private_please_command_spec.rb} +35 -70
  34. data/spec/01_tracking_candidate_methods/systematically_in_auto_mode_spec.rb +187 -0
  35. data/spec/{02_logging_calls_on_candidate_methods_spec.rb → 03_logging_calls_on_candidate_methods_spec.rb} +2 -4
  36. data/spec/04_instrumented_program_activity_observation_result_spec.rb +2 -5
  37. data/spec/05_reporting/report_table_spec.rb +51 -0
  38. data/spec/06_at_exit_spec.rb +18 -0
  39. data/spec/fixtures/bug_22.rb +17 -0
  40. data/spec/fixtures/sample_class_for_report.rb +2 -0
  41. data/spec/sandbox/Gemfile +4 -0
  42. data/spec/sandbox/Gemfile.lock +14 -0
  43. data/spec/sandbox/README.txt +26 -0
  44. data/spec/sandbox/normal.rb +7 -0
  45. data/spec/sandbox/normal_prepended_with_require.rb +8 -0
  46. data/spec/spec_helper.rb +6 -2
  47. metadata +50 -13
  48. data/lib/private_please/report/templates/simple.txt.erb +0 -61
  49. data/lib/private_please/tracking/instruments_all_below.rb +0 -41
@@ -1,60 +1,12 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe PrivatePlease, 'collecting the details of candidate-methods to observe' do
3
+ describe PrivatePlease, 'collecting the details of selected candidate-methods to observe' do
4
4
  module MarkingTest; end
5
5
 
6
6
  let(:candidates_store) { PrivatePlease.storage }
7
7
 
8
8
  # ----------------
9
- context 'observing with `private_please(<method names>)`' do
10
- # ----------------
11
-
12
- it('stores the instance methods names in the candidates list, indexed by their owning class') do
13
- class MarkingTest::Simple1
14
- def foo ; 'foo' end
15
- def bar ; 'bar' end
16
- def buz ; 'bar' end
17
- private_please :bar, 'buz' # <<-- what we test
18
- end
19
-
20
- assert_instance_methods_candidates 'MarkingTest::Simple1' =>[:bar, :buz]
21
- end
22
-
23
-
24
- it('does not work with class methods') do
25
- class MarkingTest::Simple1b
26
- def self.found ; 'I am a class method' end
27
- private_please :found # <<-- class method => not observed
28
- end
29
-
30
- assert_instance_methods_candidates ({})
31
- assert_class_methods_candidates ({})
32
- end
33
-
34
- it('ignores invalid candidates (method not found in the class)') do
35
- class MarkingTest::Simple2
36
- def found ; 'foo' end
37
- private_please :found # <<-- valid
38
- private_please :not_found_method # <<-- not found => INvalid
39
- end
40
-
41
- assert_instance_methods_candidates 'MarkingTest::Simple2' =>[:found]
42
- end
43
-
44
- it('ignores duplicates') do
45
- class MarkingTest::Simple3
46
- def found ; 'foo' end
47
- private_please :found #
48
- private_please :found # duplicate -> ignore
49
- end
50
-
51
- assert_instance_methods_candidates 'MarkingTest::Simple3' =>[:found]
52
- end
53
- end
54
-
55
-
56
- # ----------------
57
- context 'observing with `private_please()`' do
9
+ context 'observing with `private_please`' do
58
10
  # ----------------
59
11
 
60
12
  example 'all the instance methods defined after `private_please` are stored as candidates' do
@@ -79,11 +31,10 @@ describe PrivatePlease, 'collecting the details of candidate-methods to observe'
79
31
  def self.baz ; end # YES
80
32
  public # *
81
33
  def self.qux ; end # YES
82
- def self.included ; end # * special name, but valid candidate (in classes).
83
34
  end
84
35
 
85
36
  assert_instance_methods_candidates ({})
86
- assert_class_methods_candidates 'MarkingTest::Automatic1b' =>[:baz, :qux, :included]
37
+ assert_class_methods_candidates 'MarkingTest::Automatic1b' =>[:baz, :qux]
87
38
  end
88
39
 
89
40
  example ('already private methods are ignored/not observed') do
@@ -106,8 +57,6 @@ describe PrivatePlease, 'collecting the details of candidate-methods to observe'
106
57
 
107
58
 
108
59
  example 'method coming from an included module are observed too' do
109
- #TODO : find a better way to instrument modules
110
- #TODO : find a way to instrument modules automatically (? possible)
111
60
 
112
61
  module Extra002 ; private_please end # <<=== Pre-instrument.
113
62
  module Extra002::ClassMethods ; private_please end # <<=== Pre-instrument.
@@ -118,7 +67,7 @@ describe PrivatePlease, 'collecting the details of candidate-methods to observe'
118
67
  base.extend ClassMethods #
119
68
  end #
120
69
  module ClassMethods #
121
- def cm_from_modulex ; end #
70
+ def cm_from_module ; end #
122
71
  end #
123
72
  end #
124
73
  assert_instance_methods_candidates 'Extra002' =>[:im_from_module],
@@ -127,27 +76,43 @@ describe PrivatePlease, 'collecting the details of candidate-methods to observe'
127
76
  end
128
77
  end
129
78
 
130
-
131
79
  # ----------------
132
- context 'observing with `include PrivatePlease::Tracking::InstrumentsAllBelow`' do
80
+ describe 'overridden methods are not tracked' do
133
81
  # ----------------
82
+ module ExplOverridingTest ; end
83
+
84
+ example 'overridden methods are NOT marked as candidate' do
85
+ module ExplOverridingTest::UserClasses
86
+ class Base #
87
+ def base_foo ; end #
88
+ def self.c_base_foo ; end #
89
+ private_please # <start tracking>
90
+ def base_t_bar ; end # is tracked
91
+ def self.c_base_t_bar ; end # is tracked
92
+ private
93
+ def base_priv ; end # private => not tracked
94
+ end
134
95
 
135
- example 'all the methods defined subsequently are stored as candidates' do
136
-
137
- class MarkingTest::Automatic2
138
- def foo ; end
139
- def bar ; end
140
- include PrivatePlease::Tracking::InstrumentsAllBelow # ---> # <start observing>
141
- def baz ; end # YES
142
- def self.class_m1; end # YES
143
- protected #
144
- def qux ; end # YES
96
+ class Overrider < Base
97
+ private_please # <start tracking>
98
+ def base_priv ; end # overriding a private method => not tracked
99
+ def base_yes ; end # new -> tracked
100
+ def self.c_base_yes ; end # new -> tracked
101
+
102
+ def base_foo ; end # NOT tracked
103
+ def base_t_bar ; end # NOT tracked
104
+ def self.c_base_foo ; end # NOT tracked
105
+ def self.c_base_t_bar ; end # NOT tracked
106
+
107
+ def to_s ; end # NOT tracked
108
+ end
145
109
  end
146
110
 
147
- assert_instance_methods_candidates 'MarkingTest::Automatic2' =>[:baz, :qux]
148
- assert_class_methods_candidates 'MarkingTest::Automatic2' =>[:class_m1]
111
+ assert_instance_methods_candidates 'ExplOverridingTest::UserClasses::Base' => [:base_t_bar],
112
+ 'ExplOverridingTest::UserClasses::Overrider' => [:base_yes]
113
+ assert_class_methods_candidates 'ExplOverridingTest::UserClasses::Base' => [:c_base_t_bar],
114
+ 'ExplOverridingTest::UserClasses::Overrider' => [:c_base_yes]
149
115
  end
150
-
151
116
  end
152
117
 
153
118
  end
@@ -0,0 +1,187 @@
1
+ require 'spec_helper'
2
+
3
+ describe PrivatePlease, 'in $automatic mode, all the methods are tracked for observation by PP' do
4
+
5
+ before { PrivatePlease.pp_automatic_mode_enable }
6
+ after { PrivatePlease.pp_automatic_mode_disable}
7
+
8
+ let(:candidates_store) { PrivatePlease.storage }
9
+
10
+ # ----------------
11
+ context 'in a flat class,' do # simple class; no module nor inheritance
12
+ # ----------------
13
+ module AutFlatClass; end
14
+
15
+ example 'instance and singleton methods are tracked' do
16
+ class AutFlatClass::Case1
17
+ def foo ; end #
18
+ def baz ; end #
19
+ def self.foo ; end #
20
+ def self.baz ; end #
21
+ end
22
+
23
+ assert_instance_methods_candidates 'AutFlatClass::Case1' => [:baz, :foo]
24
+ assert_singleton_methods_candidates 'AutFlatClass::Case1' => [:baz, :foo]
25
+ end
26
+
27
+
28
+ example '.included and #included are 2 methods we track in classes.' do
29
+ class AutFlatClass::Case2
30
+ def foo ; end #
31
+
32
+ def self.baz ; end #
33
+ end
34
+
35
+ assert_instance_methods_candidates 'AutFlatClass::Case2' => [:foo]
36
+ assert_singleton_methods_candidates 'AutFlatClass::Case2' => [:baz]
37
+ end
38
+
39
+
40
+ example 'already private methods are not tracked' do
41
+ class AutFlatClass::Case3
42
+ def foo ; end # YES
43
+ def self.baz ; end # YES
44
+ private
45
+ def already_private ; end # NO
46
+ class << self
47
+ private
48
+ def class_already_private ; end # NO
49
+ end
50
+ end
51
+
52
+ assert_instance_methods_candidates 'AutFlatClass::Case3' => [:foo]
53
+ assert_singleton_methods_candidates 'AutFlatClass::Case3' => [:baz]
54
+ end
55
+
56
+ example 'metaprograpping-related Class methods are not tracked' do
57
+ class AutFlatClass::Case4
58
+ def singleton_method_added(_) ; end # NO
59
+ def method_added( _) ; end # NO
60
+ end
61
+
62
+ assert_instance_methods_candidates ({})
63
+ assert_singleton_methods_candidates ({})
64
+ end
65
+ end
66
+
67
+ # ----------------
68
+ context 'in a flat module,' do # no inheritance
69
+ # ----------------
70
+ module AutFlatModule; end
71
+
72
+ example 'instance and singleton methods are tracked' do
73
+ module AutFlatModule::Case1
74
+ def foo ; end #
75
+ def self.baz ; end #
76
+ end
77
+
78
+ assert_instance_methods_candidates 'AutFlatModule::Case1' => [:foo]
79
+ assert_singleton_methods_candidates 'AutFlatModule::Case1' => [:baz]
80
+ end
81
+
82
+ example 'we track #included but not .included' do
83
+ module AutFlatModule::Case2
84
+ def foo ; end #
85
+ def included ; end # : not a special name in classes
86
+
87
+ def self.baz ; end #
88
+ def self.included ; end # : not a special name in classes
89
+ end
90
+
91
+ assert_instance_methods_candidates 'AutFlatModule::Case2' => [:foo, :included]
92
+ assert_singleton_methods_candidates 'AutFlatModule::Case2' => [:baz]
93
+ end
94
+
95
+
96
+ example 'already private methods are not tracked' do
97
+ module AutFlatModule::Case3
98
+ private
99
+ def already_private ; end # NO
100
+ class << self
101
+ private
102
+ def class_already_private ; end # NO
103
+ end
104
+ end
105
+
106
+ assert_instance_methods_candidates ({})
107
+ assert_singleton_methods_candidates ({})
108
+ end
109
+
110
+ example 'metaprograpping-related Module methods are not tracked' do
111
+ module AutFlatModule::Case4
112
+ def singleton_method_added(_) ; end # NO
113
+ def method_added( _) ; end # NO
114
+ def self.included(base) ; end # NO
115
+ end
116
+
117
+ assert_instance_methods_candidates ({})
118
+ assert_singleton_methods_candidates ({})
119
+ end
120
+
121
+ end
122
+
123
+
124
+ # ----------------
125
+ context '1 class including 1 module' do
126
+ # ----------------
127
+ module IncludedModule ; end
128
+
129
+ example 'the tracked module methods are associated to the module, not the class(es) that includes the module' do
130
+ module IncludedModule
131
+ module Module1 #
132
+ def im_from_module ; end # YES
133
+ def self.included(base) # NO
134
+ base.extend ClassMethods #
135
+ end #
136
+ module ClassMethods #
137
+ def cm_from_module ; end # YES
138
+ end #
139
+ end #
140
+ class Case1
141
+ include Module1 # NO methods are associated to this class.
142
+ end
143
+ end
144
+
145
+ assert_instance_methods_candidates 'IncludedModule::Module1' =>[:im_from_module],
146
+ 'IncludedModule::Module1::ClassMethods' =>[:cm_from_module]
147
+ assert_singleton_methods_candidates ({})
148
+ end
149
+
150
+ end
151
+
152
+
153
+ # ----------------
154
+ describe 'overridden methods are not tracked in classes' do
155
+ # ----------------
156
+ module ImplOverridingTest ; end
157
+
158
+ example 'overridden methods are NOT marked as candidate' do
159
+ module ImplOverridingTest
160
+ class Base
161
+ def base_t_foo ; end # is tracked
162
+ def self.c_base_t_foo ; end # is tracked
163
+ private
164
+ def base_priv ; end # private => not tracked
165
+ end
166
+
167
+ class Overrider < Base
168
+ def base_priv ; end # overriding a private method => not tracked
169
+ def base_yes ; end # new -> tracked
170
+ def self.c_base_yes ; end # new -> tracked
171
+
172
+ def base_t_foo ; end # NOT tracked
173
+ def self.c_base_t_foo ; end # NOT tracked
174
+
175
+ def to_s ; end # NOT tracked
176
+ end
177
+ end
178
+
179
+ assert_instance_methods_candidates 'ImplOverridingTest::Base' => [:base_t_foo],
180
+ 'ImplOverridingTest::Overrider' => [:base_yes]
181
+ assert_class_methods_candidates 'ImplOverridingTest::Base' => [:c_base_t_foo],
182
+ 'ImplOverridingTest::Overrider' => [:c_base_yes]
183
+ end
184
+ end
185
+
186
+
187
+ end
@@ -4,12 +4,10 @@ describe PrivatePlease, 'calling observed methods and logging calls in 2 categor
4
4
 
5
5
  before do
6
6
  #-----------------------------------------------------------------------------------------------------------------------
7
- require File.dirname(__FILE__) + '/fixtures/sample_class_with_all_calls_combinations'
7
+ load File.dirname(__FILE__) + '/fixtures/sample_class_with_all_calls_combinations.rb'
8
8
  #-----------------------------------------------------------------------------------------------------------------------
9
9
  end
10
-
11
-
12
- NO_CALLS_OBSERVED = {}
10
+ after { Object.send(:remove_const, :CallsSample) }
13
11
 
14
12
  example ('pure internal calls are categorized correctly') do
15
13
  CallsSample::Simple.new.make_internal_calls
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe 'PrivatePlease.report', 'observing an instrumented program activity' do
3
+ describe 'PrivatePlease.reporter', 'observing an instrumented program activity' do
4
4
 
5
5
  def run_the_instrumented_program
6
6
  ActivityTest::Simple.new.tap do |o|
@@ -49,7 +49,7 @@ describe 'PrivatePlease.report', 'observing an instrumented program activity' do
49
49
  # Note: test all in 1 go because testing in bits would fail, and it differs from real usage.
50
50
  it 'obtains the right info by observing the program activity' do
51
51
  run_the_instrumented_program
52
- the_report = PrivatePlease.report
52
+ the_report = PrivatePlease::Reporter::SimpleText.new(PrivatePlease.candidates_store, PrivatePlease.calls_store)
53
53
  {
54
54
  :good_candidates => the_report.good_candidates,
55
55
  :good_candidates_c => the_report.good_candidates_c,
@@ -81,9 +81,6 @@ describe 'PrivatePlease.report', 'observing an instrumented program activity' do
81
81
  'ActivityTest::Simple2' => mnames_for([:ignored_c3])
82
82
  }
83
83
  }
84
- puts the_report.to_s
85
- puts "\n***report building time : #{the_report.building_time} sec."
86
-
87
84
  end
88
85
 
89
86
  end
@@ -0,0 +1,51 @@
1
+ require 'spec_helper'
2
+
3
+ describe PrivatePlease::Report::Table do
4
+ let(:col_1) { %w(a b c) }
5
+ let(:col_2) { %w(X Y Z) }
6
+
7
+ def table(col_1, col_2)
8
+ PrivatePlease::Report::Table.new(col_1, col_2)
9
+ end
10
+ #------------------------------------------------------------------------------
11
+
12
+ it 'wraps 2 columns of data' do
13
+ t = table(col_1, col_2)
14
+ t.col_1.should == col_1
15
+ t.col_2.should == col_2
16
+ end
17
+
18
+ it 'pads with nil whichever column is shorter' do
19
+ table(['1'], %w(a b c)).tap do |t|
20
+ t.col_1.should == ['1', nil, nil]
21
+ t.col_2.should == %w(a b c)
22
+ end
23
+ table(%w(a b c), ['1']).tap do |t|
24
+ t.col_1.should == %w(a b c)
25
+ t.col_2.should == ['1', nil, nil]
26
+ end
27
+
28
+ end
29
+
30
+ example '#rows returns an array of [String, String]' do
31
+ t = table(['1', '2'], col_2)
32
+ t.rows.should == [
33
+ ['1', 'X'],
34
+ ['2', 'Y'],
35
+ [nil, 'Z'],
36
+ ]
37
+ end
38
+
39
+ example '#empty?' do
40
+ table(['1', '2'], []).should_not be_empty
41
+ table([], ['1', '2']).should_not be_empty
42
+ table([], [] ).should be_empty
43
+ end
44
+
45
+ example '#longest_value_length' do
46
+ table(['a', '12345'], ['b', 'c']).longest_value_length.should == '12345'.length
47
+ table(['b'], ['a', '12345'] ).longest_value_length.should == '12345'.length
48
+ table([], [] ).longest_value_length.should == 0
49
+ end
50
+
51
+ end
@@ -0,0 +1,18 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'at_exit() behaviour' do
4
+
5
+ it 'prints the PP simpletext report to $stdout (when not in test mode)' do
6
+ $private_please_tests_are_running = false
7
+ PrivatePlease::Reporter::SimpleText.stub(:new => stub(:text => '<the-report-text>'))
8
+ $stdout.
9
+ should_receive(:puts).
10
+ with('<the-report-text>')
11
+ begin
12
+ PrivatePlease.at_exit
13
+ ensure
14
+ $private_please_tests_are_running = true
15
+ end
16
+ end
17
+
18
+ end
@@ -0,0 +1,17 @@
1
+ load File.expand_path File.dirname(__FILE__) + '/../../doc/fixtures/fixture_helper.rb'
2
+
3
+ module Extra
4
+ def self.included(base)
5
+ base.extend(ClassMethods)
6
+ end
7
+ module ClassMethods
8
+ def a_class_method_via_module_Extra ; end
9
+ end
10
+ end
11
+
12
+
13
+ class Simple
14
+ include Extra
15
+ end
16
+ Simple.a_class_method_via_module_Extra
17
+
@@ -1,3 +1,5 @@
1
+ require 'private_please'
2
+
1
3
  module ReportSample
2
4
  ###########################################
3
5
  class Simple # Internal calls : #
@@ -0,0 +1,4 @@
1
+ # A sample Gemfile
2
+ source "https://rubygems.org"
3
+
4
+ gem 'private_please', :path => '../..'
@@ -0,0 +1,14 @@
1
+ PATH
2
+ remote: ../..
3
+ specs:
4
+ private_please (0.0.4rc)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+
10
+ PLATFORMS
11
+ ruby
12
+
13
+ DEPENDENCIES
14
+ private_please!
@@ -0,0 +1,26 @@
1
+ To inspect the program
2
+ normal.rb
3
+ with PrivatePlease, launch it one of these ways :
4
+
5
+ $ ruby -rubygems -e "require 'private_please' ; load 'normal.rb'"
6
+ or
7
+ $ bundle exec ruby -e "require 'private_please' ; load 'normal.rb'"
8
+
9
+ You can also insert the
10
+ require 'private_please'
11
+ code in the program itself. (see normal_prepended_with_require.rb, line 3)
12
+
13
+
14
+ ruby normal.rb
15
+ ->
16
+ ruby -rubygems -e "require 'private_please' ; load 'normal.rb'"
17
+ ruby -rubygems simple_with_require.rb
18
+
19
+
20
+ be ruby normal.rb
21
+ ->
22
+ be ruby -e "require 'private_please' ; load 'normal.rb'"
23
+ be ruby simple_with_require.rb
24
+
25
+
26
+
@@ -0,0 +1,7 @@
1
+ puts 'opened: ' + __FILE__
2
+
3
+ class Foo
4
+ def self.entry_point; new.entry_point end
5
+ def entry_point; end
6
+ end
7
+ Foo.entry_point
@@ -0,0 +1,8 @@
1
+ puts 'opened: ' + __FILE__
2
+
3
+ require 'private_please'
4
+ class Foo
5
+ def self.entry_point; foo ; end
6
+ def self.foo; end
7
+ end
8
+ Foo.entry_point
data/spec/spec_helper.rb CHANGED
@@ -32,7 +32,7 @@ def assert_class_methods_candidates(raw_expected)
32
32
  expected = raw_expected.to_methods_names_bucket if raw_expected.is_a?(Hash)
33
33
  PrivatePlease.candidates_store.class_methods.should == expected
34
34
  end
35
-
35
+ alias :assert_singleton_methods_candidates :assert_class_methods_candidates
36
36
 
37
37
 
38
38
  def assert_calls_detected(expected)
@@ -67,8 +67,12 @@ RSpec.configure do |config|
67
67
  config.order = 'random'
68
68
 
69
69
  config.before(:each) do
70
- PrivatePlease.reset_before_new_test
70
+ PrivatePlease.reset
71
+ PrivatePlease.pp_automatic_mode_disable
71
72
  end
72
73
  end
73
74
 
74
75
  require File.dirname(__FILE__) + '/../lib/private_please'
76
+ PrivatePlease.pp_automatic_mode_disable
77
+
78
+ $private_please_tests_are_running = true