state_inspector 0.8.2 → 1.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 11c918b3bbe97063aa8b36861f654cdaf140e48f
4
- data.tar.gz: 2569e2b7650a72d046ed13de7abbf52459704081
3
+ metadata.gz: 7e7bd7bd17c476a6dc4860357ebfc4a827b2d7ed
4
+ data.tar.gz: 8d046a1fa6385c108e45b321c50a3a8091ff6602
5
5
  SHA512:
6
- metadata.gz: 77a143fabe9ef4ac580d0e7a63e269fce0e62bc84e20a50ec04cc6c9bba3725e292bf9df64e84b768b8e4e32b0031581c8db1f741a9a719f443fc7b925d59827
7
- data.tar.gz: ab042bd6d6a00d6e30c9563b2d2a085a0d76c65cde4325c4d7b6cef2fc4c9700f089b68548ad030ab065880c9c166ee16983799a954f2c08ede016ef5a403d3d
6
+ metadata.gz: 5b4210564e7f9fee7f03d54cb8d2bcd779d616844940a6cc82b592b3fb35737e4ed9928008b891cb076ab900d5e2aab939cf690addf052ff3dc1b4fef5d18623
7
+ data.tar.gz: 1540ec589c07d4c6cf5dc14f3887d833244a3b6d0cb8063606759b88c743629e0ea1f4ac74406536f5d36e2b66a001d22ab0c3227571ef4f0640c01ff8807c9c
data/README.md CHANGED
@@ -1,11 +1,10 @@
1
1
  # StateInspector
2
2
  [![Gem Version](https://badge.fury.io/rb/state_inspector.svg)](http://badge.fury.io/rb/state_inspector)
3
3
  [![Build Status](https://travis-ci.org/danielpclark/state_inspector.svg?branch=master)](https://travis-ci.org/danielpclark/state_inspector)
4
+ [![SayThanks.io](https://img.shields.io/badge/SayThanks.io-%E2%98%BC-1EAEDB.svg)](https://saythanks.io/to/danielpclark)
4
5
 
5
- The original purpose of this project is to log state change on target objects. This will expand
6
- further into additional introspection as new versions are made available.
7
-
8
- **Currently this project depends on attr hooks.**
6
+ The original purpose of this project is to log state change on target objects. This now can fully
7
+ inform on method calls with parameters as well as instance variables.
9
8
 
10
9
  This project uses a variation of the observer pattern. There is a hash of Reporters where you can
11
10
  mark the key as a class instance or the class itself and point it to an Observer object. Three
@@ -47,7 +46,14 @@ end
47
46
  StateInspector::Reporter[MyClass] = SessionLoggerObserver
48
47
 
49
48
  MyClass.toggle_informant
49
+
50
+ m = MyClass.new
51
+ m.thing = :a
52
+
53
+ StateInspector::Reporter[MyClass].values
54
+ # => [["#<MyClass:0x005571fde5e498>", "@thing", nil, :a]]
50
55
  ```
56
+ SessionLoggerObserver will by default log reports to `log/state_inspector/`.
51
57
 
52
58
  Now everytime the setter method is used for `thing` a new line will be entered into a log file
53
59
  of the object, method, old value, and new value. So you will see what is changed from where in
@@ -59,6 +65,44 @@ on the class itself then simply execute that method on the instances you want to
59
65
 
60
66
  If you want to see the expected results of the current observer/reporters then see [test/reporter_test.rb](https://github.com/danielpclark/state_inspector/blob/master/test/reporter_test.rb).
61
67
 
68
+ If you want to only toggle an informant for a small area you may use a helper method to toggle the
69
+ observer on and off for you.
70
+
71
+ ```ruby
72
+ require 'state_inspector/helper'
73
+ include Helper
74
+
75
+ # instead of doing MyClass.toggle_informant as above, do this.
76
+
77
+ m = MyClass.new
78
+ toggle_snoop(m) do
79
+ # put your own code here
80
+ end
81
+ ```
82
+
83
+ When writing tests for code and using StateInspector it's very important to ensure the informant is
84
+ untoggled. Otherwise you will have sporatic behavior in your tests. You may use the helpers provided
85
+ here for your tests to ensure you won't have glitchy tests as a result of using informants.
86
+
87
+ To include it in Minitest you would do:
88
+
89
+ ```ruby
90
+ # test/test_helper.rb or test/minitest_helper.rb
91
+
92
+ require 'state_inspector/helper'
93
+ class Minitest::Test
94
+ include StateInspector::Helper
95
+ end
96
+ ```
97
+
98
+ The default behavior for toggling on an informant for the first time is to inject code to observe all
99
+ setter methods. This is true for both the Object method `toggle_informant` and the helper method
100
+ `toggle_snoop`. If you would like to avoid injecting all setter methods for reporting you may either
101
+ use `state_inspector.skip_setter_snoops` (before any toggling) or the helper `toggle_snoop_clean` which
102
+ will cleanly remove its anti-setter hook once done (meaning the next `toggle_informant` will inject the
103
+ informant code into all setters).
104
+
105
+
62
106
  ## Observers
63
107
 
64
108
  To include all Observers into scope you can do:
@@ -112,15 +156,64 @@ And to register this observer to a target class you simply write:
112
156
  StateInspector::Reporter[MyTargetClass] = ExampleObserver
113
157
  ```
114
158
 
159
+ ## Manually Create Informers
160
+
161
+ To manually create informant methods use `state_inspector.snoop_setters :a=, :b=` and
162
+ `state_inspector.snoop_methods :a, :b`.
163
+
164
+ ```ruby
165
+ require 'state_inspector'
166
+ require 'state_inspector/observers/internal_observer'
167
+ include StateInspector::Observers
168
+
169
+ class MyClass
170
+ attr_writer :a, :b, :c
171
+ def x *args; end
172
+ end
173
+
174
+ # InternalObserver (it can be used with many classes and hold all of the data)
175
+ # InternalObserver.new (will hold data for only the specific reporter object pointing to it)
176
+ StateInspector::Reporter[MyClass] = InternalObserver
177
+
178
+ # This will allow us to manually define which setter methods to inform on
179
+ MyClass.state_inspector.skip_setter_snoops
180
+
181
+ m = MyClass.new
182
+ m.toggle_informant
183
+ m.state_inspector.snoop_setters :b=, :c=
184
+ m.state_inspector.snoop_methods :x
185
+ m.a= 1
186
+ m.b= 2
187
+ m.c= 3
188
+ m.x 4, 5, 6
189
+
190
+ StateInspector::Reporter[MyClass].values
191
+ # => [
192
+ # [#<MyClass:0x005608eb9aead0 @informant=true, @a=1, @b=2, @c=3>, "@b", nil, 2],
193
+ # [#<MyClass:0x005608eb9aead0 @informant=true, @a=1, @b=2, @c=3>, "@c", nil, 3],
194
+ # [#<MyClass:0x005608eb9aead0 @informant=true, @a=1, @b=2, @c=3>, :x, 4, 5, 6]
195
+ # ]
196
+ ```
197
+ The nils in the values above represent the previous value the instance variable returned.
198
+
199
+ ## Reporter Legend
200
+
201
+ The format of a report sent will always start with the object that sent it; aka `self`. Next you will
202
+ have either a string representing and instance variable of a symbol representing a method call. If it's
203
+ an instance variable then the next value will be the old value for that instance variable, and the next
204
+ value is the value given to the method to be set as the value for that instance variable. If the second
205
+ item is a symbol then every item after that is the parameters sent to that method.
206
+
115
207
 
116
- ## Road Map
208
+ LEGEND FOR SETTER| _ | _ | _
209
+ -----------------|---|---|---
210
+ `self` | `@instance_variable` | `:old_value` | `:new_value_given`
117
211
 
118
- * 0.8.0 State inspection of all flagged objects on setter methods.
119
- Includes logger observer, internal observer, and null observer.
120
212
 
121
- * 0.9.0 Sweep for missed setter methods and prepend inspection behavior.
213
+ LEGEND FOR METHOD| _ | _
214
+ -----------------|---|---
215
+ `self` | `:method_name` | `:arguments`
122
216
 
123
- * 1.0.0 Optional reporting on all/target method calls
124
217
 
125
218
  ## Development
126
219
 
@@ -0,0 +1,21 @@
1
+ module StateInspector
2
+ module Helper
3
+ def toggle_snoop(obj)
4
+ obj.toggle_informant
5
+ yield
6
+ ensure
7
+ obj.toggle_informant
8
+ end
9
+
10
+ def toggle_snoop_clean(obj)
11
+ obj.state_inspector.skip_setter_snoops
12
+ obj.toggle_informant
13
+ yield
14
+ ensure
15
+ obj.toggle_informant
16
+ obj.instance_exec {@state_inspector = nil} if obj.
17
+ instance_variable_get(:@state_inspector).
18
+ tap {|h| h.is_a?(Hash) ? h.empty? : h}
19
+ end
20
+ end
21
+ end
@@ -7,39 +7,24 @@ module StateInspector
7
7
  base.include ClassMethods
8
8
  end
9
9
 
10
- def attr_writer *attr_names
11
- attr_names.each do |attr_name|
12
- define_method("#{attr_name}=") do |value|
13
- tell_si __method__.to_s.chop,
14
- instance_variable_get("@#{attr_name.to_s}"),
15
- value
16
-
17
- instance_variable_set("@#{attr_name.to_s}", value)
18
- end
19
- end
20
- nil
21
- end
22
-
23
- def attr_accessor *attr_names
24
- attr_names.each do |attr_name|
25
- define_method("#{attr_name}") do
26
- instance_variable_get("@#{attr_name.to_s}")
27
- end
28
-
29
- self.attr_writer(attr_name)
30
- end
31
- end
32
-
33
10
  module ClassMethods
34
11
  def state_inspector
35
- StateInspector
12
+ StateInspector.new(self)
36
13
  end
37
14
 
38
- def tell_si what, old, new
39
- state_inspector.report( self, "@#{what.to_s}", old, new ) if informant?
15
+ def tell_si *args, &block
16
+ if informant?
17
+ key = self.respond_to?(:class_eval) ? self : self.class
18
+ Reporter[key].update(self, *args, &block)
19
+ end
40
20
  end
41
21
 
42
22
  def toggle_informant
23
+ state_inspector.snoop_setters(
24
+ *(self.respond_to?(:class_eval) ? self : self.class).
25
+ instance_methods.grep(/=\z/) - Object.methods
26
+ ) unless @state_inspector || self.class.instance_variable_get(:@state_inspector)
27
+
43
28
  @informant = !@informant
44
29
  end
45
30
 
@@ -1,8 +1,56 @@
1
1
  module StateInspector
2
2
  class StateInspector
3
- def self.report who, what, old, new
4
- key = who.respond_to?(:class_eval) ? who : who.class
5
- Reporter[key].update who, what, old, new
3
+ def initialize base, **opts
4
+ @base = base # pass in self
5
+ end
6
+
7
+ def snoop_setters *setters
8
+ base.class_exec do
9
+ setters.
10
+ delete_if {|m| (@state_inspector ||= {}).fetch(m){ nil } }.
11
+ each do |m|
12
+ (@state_inspector ||= {})[m] = __method__
13
+ single = singleton_methods.include? m
14
+ original_method = (single ? singleton_method(m) : instance_method(m))
15
+ send((single ? :define_singleton_method : :define_method), m) do |*args, &block|
16
+ var = "@#{__method__.to_s.chop}"
17
+ tell_si var, instance_variable_get(var), *args, &block
18
+ original_method = (single ? original_method.unbind : original_method)
19
+ original_method.bind(self).call(*args, &block)
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+ def snoop_methods *meth0ds
26
+ base.class_exec do
27
+ meth0ds.
28
+ delete_if {|m| (@state_inspector ||= {}).fetch(m){ nil } }.
29
+ each do |m|
30
+ (@state_inspector ||= {})[m] = __method__
31
+ single = singleton_methods.include? m
32
+ original_method = (single ? singleton_method(m) : instance_method(m))
33
+ send((single ? :define_singleton_method : :define_method), m) do |*args, &block|
34
+ tell_si __method__, *args, &block
35
+ original_method = (single ? original_method.unbind : original_method)
36
+ original_method.bind(self).call(*args, &block)
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ def skip_setter_snoops
43
+ base.instance_variable_set(:@state_inspector, Hash.new) unless base.
44
+ instance_variable_defined? :@state_inspector
45
+ end
46
+
47
+ private
48
+ def base
49
+ if @base.respond_to? :class_eval
50
+ @base
51
+ else
52
+ @base.class
53
+ end
6
54
  end
7
55
  end
8
56
  end
@@ -1,3 +1,3 @@
1
1
  module StateInspector
2
- VERSION = "0.8.2"
2
+ VERSION = "1.0.0.rc1"
3
3
  end
@@ -24,4 +24,6 @@ Gem::Specification.new do |spec|
24
24
  spec.add_development_dependency "bundler", "~> 1.13"
25
25
  spec.add_development_dependency "rake", "~> 10.0"
26
26
  spec.add_development_dependency "minitest", "~> 5.0"
27
+ spec.add_development_dependency "minitest-reporters", "~> 1.1"
28
+ spec.add_development_dependency "color_pound_spec_reporter", "~> 0.0"
27
29
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: state_inspector
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.2
4
+ version: 1.0.0.rc1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel P. Clark
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-01-20 00:00:00.000000000 Z
11
+ date: 2017-01-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -52,6 +52,34 @@ dependencies:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '5.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: minitest-reporters
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.1'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.1'
69
+ - !ruby/object:Gem::Dependency
70
+ name: color_pound_spec_reporter
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '0.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '0.0'
55
83
  description: Introspection tool for reporting state change. Instance varialbes and
56
84
  method call debugging.
57
85
  email:
@@ -69,6 +97,7 @@ files:
69
97
  - bin/console
70
98
  - bin/setup
71
99
  - lib/state_inspector.rb
100
+ - lib/state_inspector/helper.rb
72
101
  - lib/state_inspector/observers.rb
73
102
  - lib/state_inspector/observers/internal_observer.rb
74
103
  - lib/state_inspector/observers/null_observer.rb
@@ -94,9 +123,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
94
123
  version: '0'
95
124
  required_rubygems_version: !ruby/object:Gem::Requirement
96
125
  requirements:
97
- - - ">="
126
+ - - ">"
98
127
  - !ruby/object:Gem::Version
99
- version: '0'
128
+ version: 1.3.1
100
129
  requirements: []
101
130
  rubyforge_project:
102
131
  rubygems_version: 2.6.8