tapping_device 0.5.2 → 0.5.3

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
  SHA256:
3
- metadata.gz: fbf652959b6f125f5e4ece39c59db0b8ebd55096e4aa639452de306857193bf7
4
- data.tar.gz: 31d13f1f4f38b6009795e4d948ba4596428355cb03d113603b3801c307c14a18
3
+ metadata.gz: 9311ddb749d3c7bf09306161da25191c0de47734dea77f645f5d5af62a55d802
4
+ data.tar.gz: 4df7c9fef5c3a3a2b3852b5b4f4db31ec071149dd19b3fceb1df6f6b1ac191e8
5
5
  SHA512:
6
- metadata.gz: 98eae30679e7e08f7607dbd7ee457b9db9565e46ddf2859a782f2b5ce817bf014937b65f8ee0f342bb7d6dbec3afcb3aef980e396362726839c4031db03cb2e5
7
- data.tar.gz: bf2d0e6e8e38466f928d57580edf745b1ee1ce18ed1ee03968dc76647b67cfb4e0feaeff0166ac7503c295c54351445344f828a0bc3878b57a74ed8a130a03cb
6
+ metadata.gz: deaece3e6096551df6fe3c7eb492f6049e72b32f7e19b7a8d4067f8d5a6dc9b6fe56a41d3bd9855220ae11c8adcc377cd188f51c043835845be5c2d9250bc6d1
7
+ data.tar.gz: 057766f277605fb3e80bd2797ee1256f0f6d81fbca56c95226199a72c7e14565dc353292d06711847e257d2f552e9eb3ddcd076ec35f1abd3aaf30cc9f686f5c
data/.DS_Store CHANGED
Binary file
@@ -0,0 +1,208 @@
1
+ # Changelog
2
+
3
+ ## [Unreleased](https://github.com/st0012/tapping_device/tree/HEAD)
4
+
5
+ [Full Changelog](https://github.com/st0012/tapping_device/compare/v0.5.2...HEAD)
6
+
7
+ **Closed issues:**
8
+
9
+ - Support write\_\* helpers [\#44](https://github.com/st0012/tapping_device/issues/44)
10
+ - Use Method\#source to replace Payload\#method\_head’s implementation [\#19](https://github.com/st0012/tapping_device/issues/19)
11
+
12
+ **Merged pull requests:**
13
+
14
+ - Support write\_\* helpers [\#47](https://github.com/st0012/tapping_device/pull/47) ([st0012](https://github.com/st0012))
15
+ - Hijack attr methods with `hijack\_attr\_methods` option [\#45](https://github.com/st0012/tapping_device/pull/45) ([st0012](https://github.com/st0012))
16
+
17
+ ## [v0.5.2](https://github.com/st0012/tapping_device/tree/v0.5.2) (2020-06-10)
18
+
19
+ [Full Changelog](https://github.com/st0012/tapping_device/compare/v0.5.1...v0.5.2)
20
+
21
+ **Closed issues:**
22
+
23
+ - Add print\_mutations [\#41](https://github.com/st0012/tapping_device/issues/41)
24
+ - Add tap\_on\_mutation! [\#18](https://github.com/st0012/tapping_device/issues/18)
25
+
26
+ **Merged pull requests:**
27
+
28
+ - Print mutations [\#43](https://github.com/st0012/tapping_device/pull/43) ([st0012](https://github.com/st0012))
29
+ - Refactorings [\#42](https://github.com/st0012/tapping_device/pull/42) ([st0012](https://github.com/st0012))
30
+
31
+ ## [v0.5.1](https://github.com/st0012/tapping_device/tree/v0.5.1) (2020-06-07)
32
+
33
+ [Full Changelog](https://github.com/st0012/tapping_device/compare/v0.5.0...v0.5.1)
34
+
35
+ **Fixed bugs:**
36
+
37
+ - Filter Out Entries From TappingDevice [\#35](https://github.com/st0012/tapping_device/issues/35)
38
+
39
+ **Merged pull requests:**
40
+
41
+ - Update GitHub Actions Configuration [\#40](https://github.com/st0012/tapping_device/pull/40) ([st0012](https://github.com/st0012))
42
+ - Fix typo: Guadian -\> Guardian [\#39](https://github.com/st0012/tapping_device/pull/39) ([skade](https://github.com/skade))
43
+ - Filter out calls about TappingDevice [\#38](https://github.com/st0012/tapping_device/pull/38) ([st0012](https://github.com/st0012))
44
+ - Drop tap\_sql! [\#37](https://github.com/st0012/tapping_device/pull/37) ([st0012](https://github.com/st0012))
45
+ - Add CollectionProxy class [\#36](https://github.com/st0012/tapping_device/pull/36) ([st0012](https://github.com/st0012))
46
+
47
+ ## [v0.5.0](https://github.com/st0012/tapping_device/tree/v0.5.0) (2020-05-25)
48
+
49
+ [Full Changelog](https://github.com/st0012/tapping_device/compare/v0.4.11...v0.5.0)
50
+
51
+ **Closed issues:**
52
+
53
+ - Colorize output of tracing helpers [\#25](https://github.com/st0012/tapping_device/issues/25)
54
+
55
+ **Merged pull requests:**
56
+
57
+ - Update README.md [\#34](https://github.com/st0012/tapping_device/pull/34) ([st0012](https://github.com/st0012))
58
+ - Colorize output [\#33](https://github.com/st0012/tapping_device/pull/33) ([st0012](https://github.com/st0012))
59
+ - Add TappingDevice\#with to register a with condition [\#32](https://github.com/st0012/tapping_device/pull/32) ([st0012](https://github.com/st0012))
60
+
61
+ ## [v0.4.11](https://github.com/st0012/tapping_device/tree/v0.4.11) (2020-04-19)
62
+
63
+ [Full Changelog](https://github.com/st0012/tapping_device/compare/v0.4.10...v0.4.11)
64
+
65
+ **Merged pull requests:**
66
+
67
+ - Update rake requirement from ~\> 10.0 to ~\> 13.0 [\#31](https://github.com/st0012/tapping_device/pull/31) ([dependabot[bot]](https://github.com/apps/dependabot))
68
+
69
+ ## [v0.4.10](https://github.com/st0012/tapping_device/tree/v0.4.10) (2020-02-05)
70
+
71
+ [Full Changelog](https://github.com/st0012/tapping_device/compare/v0.4.9...v0.4.10)
72
+
73
+ **Implemented enhancements:**
74
+
75
+ - Usability improvements [\#30](https://github.com/st0012/tapping_device/pull/30) ([st0012](https://github.com/st0012))
76
+
77
+ **Merged pull requests:**
78
+
79
+ - Fix tap\_init!'s payload content [\#29](https://github.com/st0012/tapping_device/pull/29) ([st0012](https://github.com/st0012))
80
+ - Refactorings and fixes [\#28](https://github.com/st0012/tapping_device/pull/28) ([st0012](https://github.com/st0012))
81
+
82
+ ## [v0.4.9](https://github.com/st0012/tapping_device/tree/v0.4.9) (2020-01-20)
83
+
84
+ [Full Changelog](https://github.com/st0012/tapping_device/compare/v0.4.8...v0.4.9)
85
+
86
+ **Implemented enhancements:**
87
+
88
+ - Improve detail\_call\_info's output format [\#27](https://github.com/st0012/tapping_device/pull/27) ([st0012](https://github.com/st0012))
89
+
90
+ ## [v0.4.8](https://github.com/st0012/tapping_device/tree/v0.4.8) (2020-01-05)
91
+
92
+ [Full Changelog](https://github.com/st0012/tapping_device/compare/v0.4.7...v0.4.8)
93
+
94
+ **Closed issues:**
95
+
96
+ - Provide options for tapping on call or return events [\#23](https://github.com/st0012/tapping_device/issues/23)
97
+
98
+ **Merged pull requests:**
99
+
100
+ - Add tracing helpers [\#24](https://github.com/st0012/tapping_device/pull/24) ([st0012](https://github.com/st0012))
101
+
102
+ ## [v0.4.7](https://github.com/st0012/tapping_device/tree/v0.4.7) (2019-12-29)
103
+
104
+ [Full Changelog](https://github.com/st0012/tapping_device/compare/v0.4.6...v0.4.7)
105
+
106
+ **Implemented enhancements:**
107
+
108
+ - Config test coverage for codeclimate [\#22](https://github.com/st0012/tapping_device/pull/22) ([st0012](https://github.com/st0012))
109
+
110
+ **Closed issues:**
111
+
112
+ - Support tracking ActiveRecord::Base instances by their ids [\#17](https://github.com/st0012/tapping_device/issues/17)
113
+
114
+ **Merged pull requests:**
115
+
116
+ - Refactor tests and some minor fixes [\#21](https://github.com/st0012/tapping_device/pull/21) ([st0012](https://github.com/st0012))
117
+ - Support track\_as\_records option [\#20](https://github.com/st0012/tapping_device/pull/20) ([st0012](https://github.com/st0012))
118
+
119
+ ## [v0.4.6](https://github.com/st0012/tapping_device/tree/v0.4.6) (2019-12-25)
120
+
121
+ [Full Changelog](https://github.com/st0012/tapping_device/compare/v0.4.5...v0.4.6)
122
+
123
+ **Merged pull requests:**
124
+
125
+ - Add TappingDevice\#and\_print method [\#16](https://github.com/st0012/tapping_device/pull/16) ([st0012](https://github.com/st0012))
126
+ - Add Payload\#detail\_call\_info and improve method\_name's format [\#15](https://github.com/st0012/tapping_device/pull/15) ([st0012](https://github.com/st0012))
127
+
128
+ ## [v0.4.5](https://github.com/st0012/tapping_device/tree/v0.4.5) (2019-12-15)
129
+
130
+ [Full Changelog](https://github.com/st0012/tapping_device/compare/v0.4.4...v0.4.5)
131
+
132
+ ## [v0.4.4](https://github.com/st0012/tapping_device/tree/v0.4.4) (2019-12-15)
133
+
134
+ [Full Changelog](https://github.com/st0012/tapping_device/compare/v0.4.3...v0.4.4)
135
+
136
+ **Merged pull requests:**
137
+
138
+ - Implement tap\_passed! [\#14](https://github.com/st0012/tapping_device/pull/14) ([st0012](https://github.com/st0012))
139
+
140
+ ## [v0.4.3](https://github.com/st0012/tapping_device/tree/v0.4.3) (2019-12-09)
141
+
142
+ [Full Changelog](https://github.com/st0012/tapping_device/compare/v0.4.2...v0.4.3)
143
+
144
+ ## [v0.4.2](https://github.com/st0012/tapping_device/tree/v0.4.2) (2019-12-09)
145
+
146
+ [Full Changelog](https://github.com/st0012/tapping_device/compare/v0.4.1...v0.4.2)
147
+
148
+ **Merged pull requests:**
149
+
150
+ - Refactor tap\_sql! [\#13](https://github.com/st0012/tapping_device/pull/13) ([st0012](https://github.com/st0012))
151
+ - Improve tap sql [\#12](https://github.com/st0012/tapping_device/pull/12) ([st0012](https://github.com/st0012))
152
+
153
+ ## [v0.4.1](https://github.com/st0012/tapping_device/tree/v0.4.1) (2019-12-06)
154
+
155
+ [Full Changelog](https://github.com/st0012/tapping_device/compare/v0.4.0...v0.4.1)
156
+
157
+ **Merged pull requests:**
158
+
159
+ - Add TappingDevice::Payload class [\#11](https://github.com/st0012/tapping_device/pull/11) ([st0012](https://github.com/st0012))
160
+
161
+ ## [v0.4.0](https://github.com/st0012/tapping_device/tree/v0.4.0) (2019-11-25)
162
+
163
+ [Full Changelog](https://github.com/st0012/tapping_device/compare/v0.3.0...v0.4.0)
164
+
165
+ **Merged pull requests:**
166
+
167
+ - Support tap\_sql! [\#10](https://github.com/st0012/tapping_device/pull/10) ([st0012](https://github.com/st0012))
168
+ - Minor adjustment [\#9](https://github.com/st0012/tapping_device/pull/9) ([NickWarm](https://github.com/NickWarm))
169
+
170
+ ## [v0.3.0](https://github.com/st0012/tapping_device/tree/v0.3.0) (2019-11-03)
171
+
172
+ [Full Changelog](https://github.com/st0012/tapping_device/compare/v0.2.0...v0.3.0)
173
+
174
+ **Implemented enhancements:**
175
+
176
+ - Largely improve performance by fixing bad design [\#8](https://github.com/st0012/tapping_device/pull/8) ([st0012](https://github.com/st0012))
177
+
178
+ ## [v0.2.0](https://github.com/st0012/tapping_device/tree/v0.2.0) (2019-11-02)
179
+
180
+ [Full Changelog](https://github.com/st0012/tapping_device/compare/v0.1.1...v0.2.0)
181
+
182
+ **Implemented enhancements:**
183
+
184
+ - Add Device class [\#3](https://github.com/st0012/tapping_device/pull/3) ([st0012](https://github.com/st0012))
185
+
186
+ **Merged pull requests:**
187
+
188
+ - Remove tapping\_deivce/device.rb [\#7](https://github.com/st0012/tapping_device/pull/7) ([st0012](https://github.com/st0012))
189
+ - Reduce namespace [\#6](https://github.com/st0012/tapping_device/pull/6) ([st0012](https://github.com/st0012))
190
+ - Register and control all devices from Device class [\#5](https://github.com/st0012/tapping_device/pull/5) ([st0012](https://github.com/st0012))
191
+ - Support `TappingDevice::Device\#stop\_when` [\#4](https://github.com/st0012/tapping_device/pull/4) ([st0012](https://github.com/st0012))
192
+
193
+ ## [v0.1.1](https://github.com/st0012/tapping_device/tree/v0.1.1) (2019-10-20)
194
+
195
+ [Full Changelog](https://github.com/st0012/tapping_device/compare/v0.1.0...v0.1.1)
196
+
197
+ **Implemented enhancements:**
198
+
199
+ - More filters, Refactoring and Readme update [\#2](https://github.com/st0012/tapping_device/pull/2) ([st0012](https://github.com/st0012))
200
+ - Support tapping ActiveRecord class/instance [\#1](https://github.com/st0012/tapping_device/pull/1) ([st0012](https://github.com/st0012))
201
+
202
+ ## [v0.1.0](https://github.com/st0012/tapping_device/tree/v0.1.0) (2019-10-19)
203
+
204
+ [Full Changelog](https://github.com/st0012/tapping_device/compare/61039c87a55c664661b788e24311e263b28a3ee8...v0.1.0)
205
+
206
+
207
+
208
+ \* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)*
@@ -1,19 +1,20 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- tapping_device (0.5.2)
4
+ tapping_device (0.5.3)
5
5
  activerecord (>= 5.2)
6
+ activesupport
6
7
  pry
7
8
 
8
9
  GEM
9
10
  remote: https://rubygems.org/
10
11
  specs:
11
- activemodel (6.0.3.1)
12
- activesupport (= 6.0.3.1)
13
- activerecord (6.0.3.1)
14
- activemodel (= 6.0.3.1)
15
- activesupport (= 6.0.3.1)
16
- activesupport (6.0.3.1)
12
+ activemodel (6.0.3.2)
13
+ activesupport (= 6.0.3.2)
14
+ activerecord (6.0.3.2)
15
+ activemodel (= 6.0.3.2)
16
+ activesupport (= 6.0.3.2)
17
+ activesupport (6.0.3.2)
17
18
  concurrent-ruby (~> 1.0, >= 1.0.2)
18
19
  i18n (>= 0.7, < 2)
19
20
  minitest (~> 5.1)
data/README.md CHANGED
@@ -174,6 +174,20 @@ Now you can see what method changes which states. And more importantly, you get
174
174
 
175
175
  **You can try these examples on [my fork of discourse](https://github.com/st0012/discourse/tree/demo-for-tapping-device)**
176
176
 
177
+ ### `write_*` helpers
178
+
179
+ `tapping_device` also provides helpers that write the events into files:
180
+
181
+ - `write_calls(object)`
182
+ - `write_traces(object)`
183
+ - `write_mutations(object)`
184
+
185
+ The default destination is `/tmp/tapping_device.log`. You can change it with the `log_file` option:
186
+
187
+ ```ruby
188
+ write_calls(object, log_file: "/tmp/another_file")
189
+ ```
190
+
177
191
 
178
192
  ## Installation
179
193
  Add this line to your application's Gemfile:
@@ -197,9 +211,9 @@ $ gem install tapping_device
197
211
  **Depending on the size of your application, `TappingDevice` could harm the performance significantly. So make sure you don't put it inside the production group**
198
212
 
199
213
 
200
- ### Advance Usages & Options
214
+ ## Advance Usages & Options
201
215
 
202
- #### Add Conditions With `.with`
216
+ ### Add Conditions With `.with`
203
217
 
204
218
  Sometimes we don't need to know all the calls or traces of an object; we just want some of them. In those cases, we can chain the helpers with `.with` to filter the calls/traces.
205
219
 
@@ -210,7 +224,29 @@ print_calls(object).with do |payload|
210
224
  end
211
225
  ```
212
226
 
213
- #### `colorize: false`
227
+ ### Options
228
+
229
+ There are many options you can pass when using a helper method. You can list all available options and their default value with
230
+
231
+ ```ruby
232
+ TappingDevice::Configurable::DEFAULTS #=> {
233
+ :filter_by_paths=>[],
234
+ :exclude_by_paths=>[],
235
+ :with_trace_to=>50,
236
+ :event_type=>:return,
237
+ :hijack_attr_methods=>false,
238
+ :track_as_records=>false,
239
+ :inspect=>false,
240
+ :colorize=>true,
241
+ :log_file=>"/tmp/tapping_device.log"
242
+ }
243
+ ```
244
+
245
+ Here are some commonly used options:
246
+
247
+ #### `colorize: false`
248
+
249
+ - default: `true`
214
250
 
215
251
  By default `print_calls` and `print_traces` colorize their output. If you don't want the colors, you can use `colorize: false` to disable it.
216
252
 
@@ -220,7 +256,9 @@ print_calls(object, colorize: false)
220
256
  ```
221
257
 
222
258
 
223
- #### `inspect: true`
259
+ #### `inspect: true`
260
+
261
+ - default: `false`
224
262
 
225
263
  As you might have noticed, all the objects are converted into strings with `#to_s` instead of `#inspect`. This is because when used on some Rails objects, `#inspect` can generate a significantly larger string than `#to_s`. For example:
226
264
 
@@ -229,6 +267,53 @@ post.to_s #=> #
229
267
  post.inspect #=> #<Post id: 649, user_id: 3, topic_id: 600, post_number: 1, raw: "Hello world", cooked: "<p>Hello world</p>", created_at: "2020-05-24 08:07:29", updated_at: "2020-05-24 08:07:29", reply_to_post_number: nil, reply_count: 0, quote_count: 0, deleted_at: nil, off_topic_count: 0, like_count: 0, incoming_link_count: 0, bookmark_count: 0, score: nil, reads: 0, post_type: 1, sort_order: 1, last_editor_id: 3, hidden: false, hidden_reason_id: nil, notify_moderators_count: 0, spam_count: 0, illegal_count: 0, inappropriate_count: 0, last_version_at: "2020-05-24 08:07:29", user_deleted: false, reply_to_user_id: nil, percent_rank: 1.0, notify_user_count: 0, like_score: 0, deleted_by_id: nil, edit_reason: nil, word_count: 2, version: 1, cook_method: 1, wiki: false, baked_at: "2020-05-24 08:07:29", baked_version: 2, hidden_at: nil, self_edits: 0, reply_quoted: false, via_email: false, raw_email: nil, public_version: 1, action_code: nil, image_url: nil, locked_by_id: nil, image_upload_id: nil>
230
268
  ```
231
269
 
270
+ #### `hijack_attr_methods: true`
271
+
272
+ - default: `false`
273
+ - except for `tap_mutation!` and `print_mutations`
274
+
275
+ Because `TracePoint` doesn't track methods generated by `attr_*` helpers (see [this issue](https://bugs.ruby-lang.org/issues/16383) for more info), we need to redefine those methods with the normal method definition.
276
+
277
+ For example, it generates
278
+
279
+ ```ruby
280
+ def name=(val)
281
+ @name = val
282
+ end
283
+ ```
284
+
285
+ for
286
+
287
+ ```ruby
288
+ attr_writer :name
289
+ ```
290
+
291
+ This hack will only be applied to the target instance with `instance_eval`. So other instances of the class remain untouched.
292
+
293
+ The default is `false` because
294
+
295
+ 1. Checking what methods are generated by `attr_*` helpers isn't free. It's an `O(n)` operation, where `n` is the number of methods the target object has.
296
+ 2. It's still unclear if this hack safe enough for most applications.
297
+
298
+
299
+ ### Global Configuration
300
+
301
+ If you don't want to pass options every time you use a helper, you can use global configuration to change the default values:
302
+
303
+ ```ruby
304
+ TappingDevice.config[:colorize] = false
305
+ TappingDevice.config[:hijack_attr_methods] = true
306
+ ```
307
+
308
+ And if you're using Rails, you can put the configs under `config/initializers/tapping_device.rb` like this:
309
+
310
+ ```ruby
311
+ if defined?(TappingDevice)
312
+ TappingDevice.config[:colorize] = false
313
+ TappingDevice.config[:hijack_attr_methods] = true
314
+ end
315
+ ```
316
+
232
317
 
233
318
  ### Lower-Level Helpers
234
319
  `print_calls` and `print_traces` aren't the only helpers you can get from `TappingDevice`. They are actually built on top of other helpers, which you can use as well. To know more about them, please check [this page](https://github.com/st0012/tapping_device/wiki/Advance-Usages)
@@ -256,4 +341,3 @@ The gem is available as open-source under the terms of the [MIT License](https:/
256
341
  ## Code of Conduct
257
342
 
258
343
  Everyone interacting in the TappingDevice project's codebases, issue trackers, chat rooms, and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/tapping_device/blob/master/CODE_OF_CONDUCT.md).
259
-
@@ -1,10 +1,15 @@
1
1
  require "active_record"
2
+ require "active_support/core_ext/module/introspection"
3
+ require "pry" # for using Method#source
4
+
2
5
  require "tapping_device/version"
3
6
  require "tapping_device/manageable"
4
7
  require "tapping_device/payload"
5
- require "tapping_device/output_payload"
8
+ require "tapping_device/output"
6
9
  require "tapping_device/trackable"
10
+ require "tapping_device/configurable"
7
11
  require "tapping_device/exceptions"
12
+ require "tapping_device/method_hijacker"
8
13
  require "tapping_device/trackers/initialization_tracker"
9
14
  require "tapping_device/trackers/passed_tracker"
10
15
  require "tapping_device/trackers/association_call_tracker"
@@ -23,6 +28,9 @@ class TappingDevice
23
28
 
24
29
  extend Manageable
25
30
 
31
+ include Configurable
32
+ include Output::Helpers
33
+
26
34
  def initialize(options = {}, &block)
27
35
  @block = block
28
36
  @output_block = nil
@@ -33,19 +41,6 @@ class TappingDevice
33
41
  TappingDevice.devices << self
34
42
  end
35
43
 
36
- def and_print(payload_method = nil, &block)
37
- @output_block =
38
- if block
39
- -> (output_payload) { puts(block.call(output_payload)) }
40
- elsif payload_method
41
- -> (output_payload) { puts(output_payload.send(payload_method)) }
42
- else
43
- raise "need to provide either a payload method name or a block"
44
- end
45
-
46
- self
47
- end
48
-
49
44
  def with(&block)
50
45
  @with_condition = block
51
46
  end
@@ -83,6 +78,8 @@ class TappingDevice
83
78
  @target = object
84
79
  validate_target!
85
80
 
81
+ MethodHijacker.new(@target).hijack_methods! if options[:hijack_attr_methods]
82
+
86
83
  @trace_point = build_minimum_trace_point(event_type: options[:event_type]) do |payload|
87
84
  record_call!(payload)
88
85
 
@@ -130,7 +127,7 @@ class TappingDevice
130
127
 
131
128
  if Module.respond_to?(:module_parents)
132
129
  tp.defined_class.module_parents.include?(TappingDevice)
133
- else
130
+ elsif Module.respond_to?(:parents)
134
131
  tp.defined_class.parents.include?(TappingDevice)
135
132
  end
136
133
  end
@@ -201,13 +198,15 @@ class TappingDevice
201
198
  end
202
199
 
203
200
  def process_options(options)
204
- options[:filter_by_paths] ||= []
205
- options[:exclude_by_paths] ||= []
206
- options[:with_trace_to] ||= 50
207
- options[:root_device] ||= self
208
- options[:event_type] ||= :return
201
+ options[:filter_by_paths] ||= config[:filter_by_paths]
202
+ options[:exclude_by_paths] ||= config[:exclude_by_paths]
203
+ options[:with_trace_to] ||= config[:with_trace_to]
204
+ options[:event_type] ||= config[:event_type]
205
+ options[:hijack_attr_methods] ||= config[:hijack_attr_methods]
206
+ options[:track_as_records] ||= config[:track_as_records]
207
+
209
208
  options[:descendants] ||= []
210
- options[:track_as_records] ||= false
209
+ options[:root_device] ||= self
211
210
  options
212
211
  end
213
212
 
@@ -227,7 +226,7 @@ class TappingDevice
227
226
  def record_call!(payload)
228
227
  return if @disabled
229
228
 
230
- @output_block.call(OutputPayload.init(payload)) if @output_block
229
+ write_output!(payload) if @output_writer
231
230
 
232
231
  if @block
233
232
  root_device.calls << @block.call(payload)
@@ -236,10 +235,18 @@ class TappingDevice
236
235
  end
237
236
  end
238
237
 
238
+ def write_output!(payload)
239
+ @output_writer.write!(payload)
240
+ end
241
+
239
242
  def stop_if_condition_fulfilled!(payload)
240
243
  if @stop_when&.call(payload)
241
244
  stop!
242
245
  root_device.stop!
243
246
  end
244
247
  end
248
+
249
+ def config
250
+ TappingDevice.config
251
+ end
245
252
  end
@@ -0,0 +1,25 @@
1
+ require "active_support/configurable"
2
+ require "active_support/concern"
3
+
4
+ class TappingDevice
5
+ module Configurable
6
+ extend ActiveSupport::Concern
7
+
8
+ DEFAULTS = {
9
+ filter_by_paths: [],
10
+ exclude_by_paths: [],
11
+ with_trace_to: 50,
12
+ event_type: :return,
13
+ hijack_attr_methods: false,
14
+ track_as_records: false,
15
+ }.merge(TappingDevice::Output::DEFAULT_OPTIONS)
16
+
17
+ included do
18
+ include ActiveSupport::Configurable
19
+
20
+ DEFAULTS.each do |key, value|
21
+ config[key] = value
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,51 @@
1
+ class TappingDevice
2
+ class MethodHijacker
3
+ attr_reader :target
4
+
5
+ def initialize(target)
6
+ @target = target
7
+ end
8
+
9
+ def hijack_methods!
10
+ target.methods.each do |method_name|
11
+ if is_writer_method?(method_name)
12
+ redefine_writer_method!(method_name)
13
+ elsif is_reader_method?(method_name)
14
+ redefine_reader_method!(method_name)
15
+ end
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def is_writer_method?(method_name)
22
+ has_definition_source?(method_name) && method_name.match?(/\w+=/) && target.method(method_name).source.match?(/attr_writer|attr_accessor/)
23
+ end
24
+
25
+ def is_reader_method?(method_name)
26
+ has_definition_source?(method_name) && target.method(method_name).source.match?(/attr_reader|attr_accessor/)
27
+ end
28
+
29
+ def has_definition_source?(method_name)
30
+ target.method(method_name).source_location
31
+ end
32
+
33
+ def redefine_writer_method!(method_name)
34
+ ivar_name = "@#{method_name.to_s.sub("=", "")}"
35
+
36
+ target.instance_eval <<-RUBY, __FILE__, __LINE__ + 1
37
+ def #{method_name}(val)
38
+ #{ivar_name} = val
39
+ end
40
+ RUBY
41
+ end
42
+
43
+ def redefine_reader_method!(method_name)
44
+ target.instance_eval <<-RUBY, __FILE__, __LINE__ + 1
45
+ def #{method_name}
46
+ @#{method_name}
47
+ end
48
+ RUBY
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,42 @@
1
+ require "tapping_device/output/payload"
2
+ require "tapping_device/output/writer"
3
+ require "tapping_device/output/stdout_writer"
4
+ require "tapping_device/output/file_writer"
5
+
6
+ class TappingDevice
7
+ module Output
8
+ DEFAULT_OPTIONS = {
9
+ inspect: false,
10
+ colorize: true,
11
+ log_file: "/tmp/tapping_device.log"
12
+ }
13
+
14
+ module Helpers
15
+ def and_write(payload_method = nil, options: {}, &block)
16
+ and_output(payload_method, options: options, writer_klass: FileWriter, &block)
17
+ end
18
+
19
+ def and_print(payload_method = nil, options: {}, &block)
20
+ and_output(payload_method, options: options, writer_klass: StdoutWriter, &block)
21
+ end
22
+
23
+ def and_output(payload_method = nil, options: {}, writer_klass:, &block)
24
+ output_block = generate_output_block(payload_method, block)
25
+ @output_writer = writer_klass.new(options, output_block)
26
+ self
27
+ end
28
+
29
+ private
30
+
31
+ def generate_output_block(payload_method, block)
32
+ if block
33
+ block
34
+ elsif payload_method
35
+ -> (output_payload, output_options) { output_payload.send(payload_method, output_options) }
36
+ else
37
+ raise "need to provide either a payload method name or a block"
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,21 @@
1
+ class TappingDevice
2
+ module Output
3
+ class FileWriter < Writer
4
+ def initialize(options, output_block)
5
+ @path = options[:log_file]
6
+
7
+ File.write(@path, "") # clean file
8
+
9
+ super
10
+ end
11
+
12
+ def write!(payload)
13
+ output = generate_output(payload)
14
+
15
+ File.open(@path, "a") do |f|
16
+ f << output
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,175 @@
1
+ class TappingDevice
2
+ module Output
3
+ class Payload < Payload
4
+ UNDEFINED = "[undefined]"
5
+
6
+ alias :raw_arguments :arguments
7
+ alias :raw_return_value :return_value
8
+
9
+ def method_name(options = {})
10
+ ":#{super(options)}"
11
+ end
12
+
13
+ def arguments(options = {})
14
+ generate_string_result(raw_arguments, options[:inspect])
15
+ end
16
+
17
+ def return_value(options = {})
18
+ generate_string_result(raw_return_value, options[:inspect])
19
+ end
20
+
21
+ COLOR_CODES = {
22
+ green: 10,
23
+ yellow: 11,
24
+ blue: 12,
25
+ megenta: 13,
26
+ cyan: 14,
27
+ orange: 214
28
+ }
29
+
30
+ COLORS = COLOR_CODES.each_with_object({}) do |(name, code), hash|
31
+ hash[name] = "\u001b[38;5;#{code}m"
32
+ end.merge(
33
+ reset: "\u001b[0m",
34
+ nocolor: ""
35
+ )
36
+
37
+ PAYLOAD_ATTRIBUTES = {
38
+ method_name: {symbol: "", color: COLORS[:blue]},
39
+ location: {symbol: "from:", color: COLORS[:green]},
40
+ return_value: {symbol: "=>", color: COLORS[:megenta]},
41
+ arguments: {symbol: "<=", color: COLORS[:orange]},
42
+ ivar_changes: {symbol: "changes:\n", color: COLORS[:blue]},
43
+ defined_class: {symbol: "#", color: COLORS[:yellow]}
44
+ }
45
+
46
+ PAYLOAD_ATTRIBUTES.each do |attribute, attribute_options|
47
+ color = attribute_options[:color]
48
+
49
+ alias_method "original_#{attribute}".to_sym, attribute
50
+
51
+ # regenerate attributes with `colorize: true` support
52
+ define_method attribute do |options = {}|
53
+ call_result = send("original_#{attribute}", options)
54
+
55
+ if options[:colorize]
56
+ "#{color}#{call_result}#{COLORS[:reset]}"
57
+ else
58
+ call_result
59
+ end
60
+ end
61
+
62
+ define_method "#{attribute}_with_color" do |options = {}|
63
+ send(attribute, options.merge(colorize: true))
64
+ end
65
+
66
+ PAYLOAD_ATTRIBUTES.each do |and_attribute, and_attribute_options|
67
+ next if and_attribute == attribute
68
+
69
+ define_method "#{attribute}_and_#{and_attribute}" do |options = {}|
70
+ "#{send(attribute, options)} #{and_attribute_options[:symbol]} #{send(and_attribute, options)}"
71
+ end
72
+
73
+ define_method "#{attribute}_and_#{and_attribute}_with_color" do |options = {}|
74
+ send("#{attribute}_and_#{and_attribute}", options.merge(colorize: true))
75
+ end
76
+ end
77
+ end
78
+
79
+ def passed_at(options = {})
80
+ with_method_head = options.fetch(:with_method_head, false)
81
+ arg_name = raw_arguments.keys.detect { |k| raw_arguments[k] == target }
82
+
83
+ return unless arg_name
84
+
85
+ arg_name = ":#{arg_name}"
86
+ arg_name = value_with_color(arg_name, :orange) if options[:colorize]
87
+ msg = "Passed as #{arg_name} in '#{defined_class(options)}##{method_name(options)}' at #{location(options)}\n"
88
+ msg += " > #{method_head}\n" if with_method_head
89
+ msg
90
+ end
91
+
92
+ def detail_call_info(options = {})
93
+ <<~MSG
94
+ #{method_name_and_defined_class(options)}
95
+ from: #{location(options)}
96
+ <= #{arguments(options)}
97
+ => #{return_value(options)}
98
+
99
+ MSG
100
+ end
101
+
102
+ def ivar_changes(options = {})
103
+ super.map do |ivar, value_changes|
104
+ before = generate_string_result(value_changes[:before], options[:inspect])
105
+ after = generate_string_result(value_changes[:after], options[:inspect])
106
+
107
+ if options[:colorize]
108
+ ivar = "#{COLORS[:orange]}#{ivar}#{COLORS[:reset]}"
109
+ before = "#{COLORS[:blue]}#{before.to_s}#{COLORS[:reset]}"
110
+ after = "#{COLORS[:blue]}#{after.to_s}#{COLORS[:reset]}"
111
+ end
112
+
113
+ " #{ivar}: #{before.to_s} => #{after.to_s}"
114
+ end.join("\n")
115
+ end
116
+
117
+ def call_info_with_ivar_changes(options = {})
118
+ <<~MSG
119
+ #{method_name_and_defined_class(options)}
120
+ from: #{location(options)}
121
+ changes:
122
+ #{ivar_changes(options)}
123
+
124
+ MSG
125
+ end
126
+
127
+ private
128
+
129
+ def value_with_color(value, color)
130
+ "#{COLORS[color]}#{value}#{COLORS[:reset]}"
131
+ end
132
+
133
+ def generate_string_result(obj, inspect)
134
+ case obj
135
+ when Array
136
+ array_to_string(obj, inspect)
137
+ when Hash
138
+ hash_to_string(obj, inspect)
139
+ when UNDEFINED
140
+ UNDEFINED
141
+ when String
142
+ "\"#{obj}\""
143
+ when nil
144
+ "nil"
145
+ else
146
+ inspect ? obj.inspect : obj.to_s
147
+ end
148
+ end
149
+
150
+ def array_to_string(array, inspect)
151
+ elements_string = array.map do |elem|
152
+ generate_string_result(elem, inspect)
153
+ end.join(", ")
154
+ "[#{elements_string}]"
155
+ end
156
+
157
+ def hash_to_string(hash, inspect)
158
+ elements_string = hash.map do |key, value|
159
+ "#{key.to_s}: #{generate_string_result(value, inspect)}"
160
+ end.join(", ")
161
+ "{#{elements_string}}"
162
+ end
163
+
164
+ def obj_to_string(element, inspect)
165
+ to_string_method = inspect ? :inspect : :to_s
166
+
167
+ if !inspect && element.is_a?(String)
168
+ "\"#{element}\""
169
+ else
170
+ element.send(to_string_method)
171
+ end
172
+ end
173
+ end
174
+ end
175
+ end
@@ -0,0 +1,9 @@
1
+ class TappingDevice
2
+ module Output
3
+ class StdoutWriter < Writer
4
+ def write!(payload)
5
+ puts(generate_output(payload))
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,20 @@
1
+ class TappingDevice
2
+ module Output
3
+ class Writer
4
+ def initialize(options, output_block)
5
+ @options = options
6
+ @output_block = output_block
7
+ end
8
+
9
+ def write!(payload)
10
+ raise NotImplementedError
11
+ end
12
+
13
+ private
14
+
15
+ def generate_output(payload)
16
+ @output_block.call(Output::Payload.init(payload), @options)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -20,8 +20,7 @@ class TappingDevice
20
20
  end
21
21
 
22
22
  def method_head
23
- source_file, source_line = method_object.source_location
24
- IO.readlines(source_file)[source_line-1]
23
+ method_object.source.strip if method_object.source_location
25
24
  end
26
25
 
27
26
  def location(options = {})
@@ -21,38 +21,67 @@ class TappingDevice
21
21
  end
22
22
 
23
23
  def print_traces(target, options = {})
24
- output_options = extract_output_options(options)
25
- options[:event_type] = :call
24
+ output_traces(target, options, output_action: :and_print)
25
+ end
26
26
 
27
- device_1 = tap_on!(target, options).and_print do |output_payload|
28
- "Called #{output_payload.method_name_and_location(output_options)}"
29
- end
30
- device_2 = tap_passed!(target, options).and_print do |output_payload|
31
- output_payload.passed_at(output_options)
32
- end
33
- CollectionProxy.new([device_1, device_2])
27
+ def write_traces(target, options = {})
28
+ output_traces(target, options, output_action: :and_write)
34
29
  end
35
30
 
36
31
  def print_calls(target, options = {})
37
- output_options = extract_output_options(options)
32
+ output_calls(target, options, output_action: :and_print)
33
+ end
34
+
35
+ def write_calls(target, options = {})
36
+ output_calls(target, options, output_action: :and_write)
37
+ end
38
+
39
+ def print_mutations(target, options = {})
40
+ output_mutations(target, options, output_action: :and_print)
41
+ end
42
+
43
+ def write_mutations(target, options = {})
44
+ output_mutations(target, options, output_action: :and_write)
45
+ end
38
46
 
39
- tap_on!(target, options).and_print do |output_payload|
47
+ private
48
+
49
+ def output_calls(target, options = {}, output_action:)
50
+ device_options, output_options = separate_options(options)
51
+
52
+ tap_on!(target, device_options).send(output_action, options: output_options) do |output_payload, output_options|
40
53
  output_payload.detail_call_info(output_options)
41
54
  end
42
55
  end
43
56
 
44
- def print_mutations(target, options = {})
45
- output_options = extract_output_options(options)
57
+ def output_traces(target, options = {}, output_action:)
58
+ device_options, output_options = separate_options(options)
59
+ device_options[:event_type] = :call
46
60
 
47
- tap_mutation!(target, options).and_print do |output_payload|
61
+ device_1 = tap_on!(target, device_options).send(output_action, options: output_options) do |output_payload, output_options|
62
+ "Called #{output_payload.method_name_and_location(output_options)}\n"
63
+ end
64
+ device_2 = tap_passed!(target, device_options).send(output_action, options: output_options) do |output_payload, output_options|
65
+ output_payload.passed_at(output_options)
66
+ end
67
+ CollectionProxy.new([device_1, device_2])
68
+ end
69
+
70
+ def output_mutations(target, options = {}, output_action:)
71
+ device_options, output_options = separate_options(options)
72
+
73
+ tap_mutation!(target, device_options).send(output_action, options: output_options) do |output_payload, output_options|
48
74
  output_payload.call_info_with_ivar_changes(output_options)
49
75
  end
50
76
  end
51
77
 
52
- private
78
+ def separate_options(options)
79
+ output_options = Output::DEFAULT_OPTIONS.keys.each_with_object({}) do |key, hash|
80
+ hash[key] = options.fetch(key, TappingDevice.config[key])
81
+ options.delete(key)
82
+ end
53
83
 
54
- def extract_output_options(options)
55
- {inspect: options.delete(:inspect), colorize: options.fetch(:colorize, true)}
84
+ [options, output_options]
56
85
  end
57
86
 
58
87
  class CollectionProxy
@@ -1,16 +1,14 @@
1
- require "pry" # for using Method#source
2
-
3
1
  class TappingDevice
4
2
  module Trackers
5
3
  class MutationTracker < TappingDevice
6
4
  def initialize(options, &block)
5
+ options[:hijack_attr_methods] = true
7
6
  super
8
7
  @snapshot_stack = []
9
8
  end
10
9
 
11
10
  def track(object)
12
11
  super
13
- hijack_attr_writers
14
12
  insert_snapshot_taking_trace_point
15
13
  self
16
14
  end
@@ -45,24 +43,6 @@ class TappingDevice
45
43
  end
46
44
  end
47
45
 
48
- def hijack_attr_writers
49
- writer_methods = target.methods.grep(/\w+=/)
50
- writer_methods.each do |method_name|
51
- if target.method(method_name).source.match?(/attr_writer|attr_accessor/)
52
- ivar_name = "@#{method_name.to_s.sub("=", "")}"
53
-
54
- # need to use instance_eval to make the call site location consistent with normal methods
55
- target.instance_eval(
56
- <<~CODE
57
- def #{method_name}(val)
58
- #{ivar_name} = val
59
- end
60
- CODE
61
- )
62
- end
63
- end
64
- end
65
-
66
46
  def build_payload(tp:, filepath:, line_number:)
67
47
  payload = super
68
48
 
@@ -78,12 +58,12 @@ class TappingDevice
78
58
 
79
59
  additional_keys = @latest_instance_variables.keys - @instance_variables_snapshot.keys
80
60
  additional_keys.each do |key|
81
- changes[key] = {before: OutputPayload::UNDEFINED, after: @latest_instance_variables[key]}
61
+ changes[key] = {before: Output::Payload::UNDEFINED, after: @latest_instance_variables[key]}
82
62
  end
83
63
 
84
64
  removed_keys = @instance_variables_snapshot.keys - @latest_instance_variables.keys
85
65
  removed_keys.each do |key|
86
- changes[key] = {before: @instance_variables_snapshot[key], after: OutputPayload::UNDEFINED}
66
+ changes[key] = {before: @instance_variables_snapshot[key], after: Output::Payload::UNDEFINED}
87
67
  end
88
68
 
89
69
  remained_keys = @latest_instance_variables.keys - additional_keys
@@ -1,3 +1,3 @@
1
1
  class TappingDevice
2
- VERSION = "0.5.2"
2
+ VERSION = "0.5.3"
3
3
  end
@@ -33,6 +33,7 @@ Gem::Specification.new do |spec|
33
33
  end
34
34
 
35
35
  spec.add_dependency "pry" # for using Method#source in MutationTracker
36
+ spec.add_dependency "activesupport"
36
37
 
37
38
  spec.add_development_dependency "sqlite3", ">= 1.3.6"
38
39
  spec.add_development_dependency "database_cleaner"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tapping_device
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.2
4
+ version: 0.5.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - st0012
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-06-10 00:00:00.000000000 Z
11
+ date: 2020-06-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: activesupport
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: sqlite3
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -137,6 +151,7 @@ files:
137
151
  - ".rspec"
138
152
  - ".ruby-version"
139
153
  - ".travis.yml"
154
+ - CHANGELOG.md
140
155
  - CODE_OF_CONDUCT.md
141
156
  - Gemfile
142
157
  - Gemfile.lock
@@ -150,9 +165,15 @@ files:
150
165
  - images/print_mutations.png
151
166
  - images/print_traces.png
152
167
  - lib/tapping_device.rb
168
+ - lib/tapping_device/configurable.rb
153
169
  - lib/tapping_device/exceptions.rb
154
170
  - lib/tapping_device/manageable.rb
155
- - lib/tapping_device/output_payload.rb
171
+ - lib/tapping_device/method_hijacker.rb
172
+ - lib/tapping_device/output.rb
173
+ - lib/tapping_device/output/file_writer.rb
174
+ - lib/tapping_device/output/payload.rb
175
+ - lib/tapping_device/output/stdout_writer.rb
176
+ - lib/tapping_device/output/writer.rb
156
177
  - lib/tapping_device/payload.rb
157
178
  - lib/tapping_device/trackable.rb
158
179
  - lib/tapping_device/trackers/association_call_tracker.rb
@@ -1,173 +0,0 @@
1
- class TappingDevice
2
- class OutputPayload < Payload
3
- UNDEFINED = "[undefined]"
4
-
5
- alias :raw_arguments :arguments
6
- alias :raw_return_value :return_value
7
-
8
- def method_name(options = {})
9
- ":#{super(options)}"
10
- end
11
-
12
- def arguments(options = {})
13
- generate_string_result(raw_arguments, options[:inspect])
14
- end
15
-
16
- def return_value(options = {})
17
- generate_string_result(raw_return_value, options[:inspect])
18
- end
19
-
20
- COLOR_CODES = {
21
- green: 10,
22
- yellow: 11,
23
- blue: 12,
24
- megenta: 13,
25
- cyan: 14,
26
- orange: 214
27
- }
28
-
29
- COLORS = COLOR_CODES.each_with_object({}) do |(name, code), hash|
30
- hash[name] = "\u001b[38;5;#{code}m"
31
- end.merge(
32
- reset: "\u001b[0m",
33
- nocolor: ""
34
- )
35
-
36
- PAYLOAD_ATTRIBUTES = {
37
- method_name: {symbol: "", color: COLORS[:blue]},
38
- location: {symbol: "from:", color: COLORS[:green]},
39
- return_value: {symbol: "=>", color: COLORS[:megenta]},
40
- arguments: {symbol: "<=", color: COLORS[:orange]},
41
- ivar_changes: {symbol: "changes:\n", color: COLORS[:blue]},
42
- defined_class: {symbol: "#", color: COLORS[:yellow]}
43
- }
44
-
45
- PAYLOAD_ATTRIBUTES.each do |attribute, attribute_options|
46
- color = attribute_options[:color]
47
-
48
- alias_method "original_#{attribute}".to_sym, attribute
49
-
50
- # regenerate attributes with `colorize: true` support
51
- define_method attribute do |options = {}|
52
- call_result = send("original_#{attribute}", options)
53
-
54
- if options[:colorize]
55
- "#{color}#{call_result}#{COLORS[:reset]}"
56
- else
57
- call_result
58
- end
59
- end
60
-
61
- define_method "#{attribute}_with_color" do |options = {}|
62
- send(attribute, options.merge(colorize: true))
63
- end
64
-
65
- PAYLOAD_ATTRIBUTES.each do |and_attribute, and_attribute_options|
66
- next if and_attribute == attribute
67
-
68
- define_method "#{attribute}_and_#{and_attribute}" do |options = {}|
69
- "#{send(attribute, options)} #{and_attribute_options[:symbol]} #{send(and_attribute, options)}"
70
- end
71
-
72
- define_method "#{attribute}_and_#{and_attribute}_with_color" do |options = {}|
73
- send("#{attribute}_and_#{and_attribute}", options.merge(colorize: true))
74
- end
75
- end
76
- end
77
-
78
- def passed_at(options = {})
79
- with_method_head = options.fetch(:with_method_head, false)
80
- arg_name = raw_arguments.keys.detect { |k| raw_arguments[k] == target }
81
-
82
- return unless arg_name
83
-
84
- arg_name = ":#{arg_name}"
85
- arg_name = value_with_color(arg_name, :orange) if options[:colorize]
86
- msg = "Passed as #{arg_name} in '#{defined_class(options)}##{method_name(options)}' at #{location(options)}"
87
- msg += "\n > #{method_head.strip}" if with_method_head
88
- msg
89
- end
90
-
91
- def detail_call_info(options = {})
92
- <<~MSG
93
- #{method_name_and_defined_class(options)}
94
- from: #{location(options)}
95
- <= #{arguments(options)}
96
- => #{return_value(options)}
97
-
98
- MSG
99
- end
100
-
101
- def ivar_changes(options = {})
102
- super.map do |ivar, value_changes|
103
- before = generate_string_result(value_changes[:before], options[:inspect])
104
- after = generate_string_result(value_changes[:after], options[:inspect])
105
-
106
- if options[:colorize]
107
- ivar = "#{COLORS[:orange]}#{ivar}#{COLORS[:reset]}"
108
- before = "#{COLORS[:blue]}#{before.to_s}#{COLORS[:reset]}"
109
- after = "#{COLORS[:blue]}#{after.to_s}#{COLORS[:reset]}"
110
- end
111
-
112
- " #{ivar}: #{before.to_s} => #{after.to_s}"
113
- end.join("\n")
114
- end
115
-
116
- def call_info_with_ivar_changes(options = {})
117
- <<~MSG
118
- #{method_name_and_defined_class(options)}
119
- from: #{location(options)}
120
- changes:
121
- #{ivar_changes(options)}
122
-
123
- MSG
124
- end
125
-
126
- private
127
-
128
- def value_with_color(value, color)
129
- "#{COLORS[color]}#{value}#{COLORS[:reset]}"
130
- end
131
-
132
- def generate_string_result(obj, inspect)
133
- case obj
134
- when Array
135
- array_to_string(obj, inspect)
136
- when Hash
137
- hash_to_string(obj, inspect)
138
- when UNDEFINED
139
- UNDEFINED
140
- when String
141
- "\"#{obj}\""
142
- when nil
143
- "nil"
144
- else
145
- inspect ? obj.inspect : obj.to_s
146
- end
147
- end
148
-
149
- def array_to_string(array, inspect)
150
- elements_string = array.map do |elem|
151
- generate_string_result(elem, inspect)
152
- end.join(", ")
153
- "[#{elements_string}]"
154
- end
155
-
156
- def hash_to_string(hash, inspect)
157
- elements_string = hash.map do |key, value|
158
- "#{key.to_s}: #{generate_string_result(value, inspect)}"
159
- end.join(", ")
160
- "{#{elements_string}}"
161
- end
162
-
163
- def obj_to_string(element, inspect)
164
- to_string_method = inspect ? :inspect : :to_s
165
-
166
- if !inspect && element.is_a?(String)
167
- "\"#{element}\""
168
- else
169
- element.send(to_string_method)
170
- end
171
- end
172
- end
173
- end