private_please 0.0.3 → 0.0.4

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