tapping_device 0.4.11 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.DS_Store +0 -0
- data/Gemfile.lock +9 -9
- data/README.md +92 -334
- data/images/print_calls - single entry.png +0 -0
- data/images/print_calls.png +0 -0
- data/images/print_traces.png +0 -0
- data/lib/tapping_device.rb +24 -3
- data/lib/tapping_device/output_payload.rb +145 -0
- data/lib/tapping_device/payload.rb +2 -75
- data/lib/tapping_device/trackable.rb +10 -9
- data/lib/tapping_device/version.rb +1 -1
- data/tapping_device.gemspec +2 -2
- metadata +11 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9806974025c4e1ba9042bf2df5a4b4211a7e4a9630b291e05892d472e54263cc
|
4
|
+
data.tar.gz: fc609ed5be23cac4943e90c34d7126aaffd53816910320f7300d2a33a9d40585
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 15ee39e171f665492e47c378bcffedd3d7bc1c8204269607e54c1ac174f44fe5ee4cc33a0ffcd82d4b732abc63def63eefb8b1fda386d4e40503a4bd959e71e2
|
7
|
+
data.tar.gz: 3b4bb1b05949fd4735fde568995e8c92c64634ad39f196f76649e2f401834d8fdc605080546655f90920e8c2cfc1cebb4bf686b7da34494217c677e058f6c73b
|
data/.DS_Store
ADDED
Binary file
|
data/Gemfile.lock
CHANGED
@@ -1,23 +1,23 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
tapping_device (0.
|
4
|
+
tapping_device (0.5.0)
|
5
5
|
activerecord (>= 5.2)
|
6
6
|
|
7
7
|
GEM
|
8
8
|
remote: https://rubygems.org/
|
9
9
|
specs:
|
10
|
-
activemodel (6.0.
|
11
|
-
activesupport (= 6.0.
|
12
|
-
activerecord (6.0.
|
13
|
-
activemodel (= 6.0.
|
14
|
-
activesupport (= 6.0.
|
15
|
-
activesupport (6.0.
|
10
|
+
activemodel (6.0.3.1)
|
11
|
+
activesupport (= 6.0.3.1)
|
12
|
+
activerecord (6.0.3.1)
|
13
|
+
activemodel (= 6.0.3.1)
|
14
|
+
activesupport (= 6.0.3.1)
|
15
|
+
activesupport (6.0.3.1)
|
16
16
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
17
17
|
i18n (>= 0.7, < 2)
|
18
18
|
minitest (~> 5.1)
|
19
19
|
tzinfo (~> 1.1)
|
20
|
-
zeitwerk (~> 2.2)
|
20
|
+
zeitwerk (~> 2.2, >= 2.2.2)
|
21
21
|
coderay (1.1.2)
|
22
22
|
concurrent-ruby (1.1.6)
|
23
23
|
database_cleaner (1.7.0)
|
@@ -27,7 +27,7 @@ GEM
|
|
27
27
|
concurrent-ruby (~> 1.0)
|
28
28
|
json (2.3.0)
|
29
29
|
method_source (0.9.2)
|
30
|
-
minitest (5.14.
|
30
|
+
minitest (5.14.1)
|
31
31
|
pry (0.12.2)
|
32
32
|
coderay (~> 1.1.0)
|
33
33
|
method_source (~> 0.9.0)
|
data/README.md
CHANGED
@@ -6,417 +6,175 @@
|
|
6
6
|
[![Test Coverage](https://api.codeclimate.com/v1/badges/3e3732a6983785bccdbd/test_coverage)](https://codeclimate.com/github/st0012/tapping_device/test_coverage)
|
7
7
|
[![Open Source Helpers](https://www.codetriage.com/st0012/tapping_device/badges/users.svg)](https://www.codetriage.com/st0012/tapping_device)
|
8
8
|
|
9
|
-
## Related Posts
|
10
|
-
- [Optimize Your Debugging Process With Object-Oriented Tracing and tapping_device](http://bit.ly/object-oriented-tracing)
|
11
|
-
- [Debug Rails issues effectively with tapping_device](https://dev.to/st0012/debug-rails-issues-effectively-with-tappingdevice-c7c)
|
12
|
-
- [Want to know more about your Rails app? Tap on your objects!](https://dev.to/st0012/want-to-know-more-about-your-rails-app-tap-on-your-objects-bd3)
|
13
|
-
|
14
|
-
|
15
|
-
## Table of Content
|
16
|
-
- [Introduction](#introduction)
|
17
|
-
- [Print Object’s Traces](print-objects-traces)
|
18
|
-
- [Track Calls that Generates SQL Queries](#track-calls-that-generates-sql-queries)
|
19
|
-
- [Installation](#installation)
|
20
|
-
- [Usages](#usages)
|
21
|
-
- Tracing Helpers
|
22
|
-
- [print_traces](#print_traces)
|
23
|
-
- [print_calls_in_detail](#print_calls_in_detail)
|
24
|
-
- Tapping Methods
|
25
|
-
- [tap_init!](#tap_init)
|
26
|
-
- [tap_on!](#tap_on)
|
27
|
-
- [tap_passed!](#tap_passed)
|
28
|
-
- [tap_assoc!](#tap_assoc)
|
29
|
-
- [tap_sql!](#tap_sql)
|
30
|
-
- [Options](#options)
|
31
|
-
- [Payload](#payload-of-the-call)
|
32
|
-
- [Advance Usages](#advance-usages)
|
33
9
|
|
34
10
|
## Introduction
|
35
|
-
`
|
36
|
-
|
37
|
-
Sample usage:
|
38
|
-
|
39
|
-
### Print Object’s Traces
|
11
|
+
`TappingDevice` makes the objects tell you what they do, so you don't need to track them yourself.
|
40
12
|
|
41
|
-
|
13
|
+
#### Contract Tracing For Objects
|
42
14
|
|
43
|
-
|
44
|
-
class OrdersController < ApplicationController
|
45
|
-
def create
|
46
|
-
@cart = Cart.find(order_params[:cart_id])
|
47
|
-
print_traces(@cart, exclude_by_paths: [/gems/])
|
48
|
-
@order = OrderCreationService.new.perform(@cart)
|
49
|
-
end
|
50
|
-
```
|
51
|
-
|
52
|
-
```
|
53
|
-
Passed as 'cart' in 'OrderCreationService#perform' at /Users/st0012/projects/tapping_device-demo/app/controllers/orders_controller.rb:10
|
54
|
-
Passed as 'cart' in 'OrderCreationService#validate_cart' at /Users/st0012/projects/tapping_device-demo/app/services/order_creation_service.rb:8
|
55
|
-
Called :reserved_until FROM /Users/st0012/projects/tapping_device-demo/app/services/order_creation_service.rb:18
|
56
|
-
Called :errors FROM /Users/st0012/projects/tapping_device-demo/app/services/order_creation_service.rb:9
|
57
|
-
Passed as 'cart' in 'OrderCreationService#apply_discount' at /Users/st0012/projects/tapping_device-demo/app/services/order_creation_service.rb:10
|
58
|
-
Called :apply_discount FROM /Users/st0012/projects/tapping_device-demo/app/services/order_creation_service.rb:24
|
59
|
-
……
|
60
|
-
```
|
15
|
+
The concept is very simple. It's basically like [contact tracing](https://en.wikipedia.org/wiki/Contact_tracing) for your Ruby objects. You can use
|
61
16
|
|
62
|
-
(
|
17
|
+
- `print_calls(object)` to see what the object does
|
18
|
+
- `print_traces(object)` to see how the object interacts with other objects (like used as an argument)
|
63
19
|
|
20
|
+
Still sounds vague? Let's see some examples:
|
64
21
|
|
65
|
-
|
22
|
+
### `print_calls` To Track Method Calls
|
66
23
|
|
67
|
-
|
68
|
-
Add this line to your application's Gemfile:
|
24
|
+
In [Discourse](https://github.com/discourse/discourse), it uses the `Guardian` class for authorization (like policy objects). It's barely visible in controller actions, but it does many checks under the hood. Now, let's say we want to know what the `Guadian` would do when a user creates a post; here's the controller action:
|
69
25
|
|
70
26
|
```ruby
|
71
|
-
gem 'tapping_device', group: :development
|
72
|
-
```
|
73
|
-
|
74
|
-
And then execute:
|
75
|
-
|
76
|
-
```
|
77
|
-
$ bundle
|
78
|
-
```
|
79
|
-
|
80
|
-
Or install it yourself as:
|
81
|
-
|
82
|
-
```
|
83
|
-
$ gem install tapping_device
|
84
|
-
```
|
85
|
-
|
86
|
-
## Usages
|
87
|
-
|
88
|
-
### print_traces
|
89
|
-
|
90
|
-
It prints the object's trace. It's like mounting a GPS tracker + a spy camera on your object, so you can inspect your program through the object's eyes.
|
91
|
-
|
92
|
-
```ruby
|
93
|
-
class OrdersController < ApplicationController
|
94
27
|
def create
|
95
|
-
@
|
96
|
-
|
97
|
-
|
28
|
+
@manager_params = create_params
|
29
|
+
@manager_params[:first_post_checks] = !is_api?
|
30
|
+
|
31
|
+
manager = NewPostManager.new(current_user, @manager_params)
|
32
|
+
|
33
|
+
if is_api?
|
34
|
+
memoized_payload = DistributedMemoizer.memoize(signature_for(@manager_params), 120) do
|
35
|
+
result = manager.perform
|
36
|
+
MultiJson.dump(serialize_data(result, NewPostResultSerializer, root: false))
|
37
|
+
end
|
38
|
+
|
39
|
+
parsed_payload = JSON.parse(memoized_payload)
|
40
|
+
backwards_compatible_json(parsed_payload, parsed_payload['success'])
|
41
|
+
else
|
42
|
+
result = manager.perform
|
43
|
+
json = serialize_data(result, NewPostResultSerializer, root: false)
|
44
|
+
backwards_compatible_json(json, result.success?)
|
45
|
+
end
|
98
46
|
end
|
99
47
|
```
|
100
48
|
|
101
|
-
|
102
|
-
Passed as 'cart' in 'OrderCreationService#perform' at /Users/st0012/projects/tapping_device-demo/app/controllers/orders_controller.rb:10
|
103
|
-
Passed as 'cart' in 'OrderCreationService#validate_cart' at /Users/st0012/projects/tapping_device-demo/app/services/order_creation_service.rb:8
|
104
|
-
Called :reserved_until FROM /Users/st0012/projects/tapping_device-demo/app/services/order_creation_service.rb:18
|
105
|
-
Called :errors FROM /Users/st0012/projects/tapping_device-demo/app/services/order_creation_service.rb:9
|
106
|
-
Passed as 'cart' in 'OrderCreationService#apply_discount' at /Users/st0012/projects/tapping_device-demo/app/services/order_creation_service.rb:10
|
107
|
-
Called :apply_discount FROM /Users/st0012/projects/tapping_device-demo/app/services/order_creation_service.rb:24
|
108
|
-
……
|
109
|
-
```
|
110
|
-
|
111
|
-
### print_calls_in_detail
|
112
|
-
|
113
|
-
It prints the object's calls in detail (including call location, arguments, and return value). It's useful for observing an object's behavior when debugging.
|
49
|
+
As you can see, it doesn't even exist in the controller action, which makes tracking it by reading code very hard to do.
|
114
50
|
|
115
|
-
|
116
|
-
- `inspect:` - will print objects with `#inspect` instead of `#to_s` if set to `true` (very noisy when having large objects). Default is `false`.
|
51
|
+
But with `TappingDevice`. You can use `print_calls` to show what method calls the object performs
|
117
52
|
|
118
53
|
```ruby
|
119
|
-
class OrdersController < ApplicationController
|
120
54
|
def create
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
```
|
127
|
-
|
55
|
+
# you can retrieve the current guardian object by calling guardian in the controller
|
56
|
+
print_calls(guardian)
|
57
|
+
@manager_params = create_params
|
58
|
+
|
59
|
+
# .....
|
128
60
|
```
|
129
|
-
:validate_cart # OrderCreationService
|
130
|
-
<= {:cart=>#<Cart id: 1, total: 10, customer_id: 1, promotion_id: nil, reserved_until: nil, created_at: "2020-01-05 09:48:28", updated_at: "2020-01-05 09:48:28">}
|
131
|
-
=> nil
|
132
|
-
FROM /Users/st0012/projects/tapping_device-demo/app/services/order_creation_service.rb:8
|
133
|
-
:apply_discount # OrderCreationService
|
134
|
-
<= {:cart=>#<Cart id: 1, total: 5, customer_id: 1, promotion_id: 1, reserved_until: nil, created_at: "2020-01-05 09:48:28", updated_at: "2020-01-05 09:48:28">, :promotion=>#<Promotion id: 1, amount: 0.5e1, customer_id: nil, created_at: "2020-01-05 09:48:28", updated_at: "2020-01-05 09:48:28">}
|
135
|
-
=> true
|
136
|
-
FROM /Users/st0012/projects/tapping_device-demo/app/services/order_creation_service.rb:10
|
137
|
-
:create_order # OrderCreationService
|
138
|
-
<= {:cart=>#<Cart id: 1, total: 5, customer_id: 1, promotion_id: 1, reserved_until: nil, created_at: "2020-01-05 09:48:28", updated_at: "2020-01-05 09:48:28">}
|
139
|
-
=> #<Order:0x00007f9ebcb17f08>
|
140
|
-
FROM /Users/st0012/projects/tapping_device-demo/app/services/order_creation_service.rb:11
|
141
|
-
:perform # OrderCreationService
|
142
|
-
<= {:cart=>#<Cart id: 1, total: 5, customer_id: 1, promotion_id: 1, reserved_until: nil, created_at: "2020-01-05 09:48:28", updated_at: "2020-01-05 09:48:28">, :promotion=>#<Promotion id: 1, amount: 0.5e1, customer_id: nil, created_at: "2020-01-05 09:48:28", updated_at: "2020-01-05 09:48:28">}
|
143
|
-
=> #<Order:0x00007f9ebcb17f08>
|
144
|
-
FROM /Users/st0012/projects/tapping_device-demo/app/controllers/orders_controller.rb:11
|
145
|
-
```
|
146
|
-
|
147
|
-
The output's order might look strange. This is because `tapping_device` needs to wait for the call to return in order to have its return value.
|
148
|
-
|
149
|
-
### tap_init!
|
150
61
|
|
151
|
-
|
62
|
+
Now, if you execute the code, like via tests:
|
152
63
|
|
153
|
-
```
|
154
|
-
|
155
|
-
tap_init!(Student) do |payload|
|
156
|
-
calls << [payload[:method_name], payload[:arguments]]
|
157
|
-
end
|
158
|
-
|
159
|
-
Student.new("Stan", 18)
|
160
|
-
Student.new("Jane", 23)
|
161
|
-
|
162
|
-
puts(calls.to_s) #=> [[:initialize, {:name=>"Stan", :age=>18}], [:initialize, {:name=>"Jane", :age=>23}]]
|
64
|
+
```shell
|
65
|
+
$ rspec spec/requests/posts_controller_spec.rb:603
|
163
66
|
```
|
164
67
|
|
165
|
-
|
166
|
-
|
167
|
-
`tap_on!(object)` - tracks any calls received by the object.
|
68
|
+
You can get all the method calls it performs with basically everything you need to know
|
168
69
|
|
169
|
-
|
170
|
-
class PostsController < ApplicationController
|
171
|
-
before_action :set_post, only: [:show, :edit, :update, :destroy]
|
70
|
+
<img src="https://github.com/st0012/tapping_device/blob/master/images/print_calls.png" alt="image of print_calls output" width="50%">
|
172
71
|
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
72
|
+
Let's take a closer look at each entry. Everyone of them contains the method call's
|
73
|
+
- method name
|
74
|
+
- method source class/module
|
75
|
+
- call site
|
76
|
+
- arguments
|
77
|
+
- return value
|
178
78
|
|
179
|
-
|
79
|
+
![explanation of individual entry](https://github.com/st0012/tapping_device/blob/master/images/print_calls%20-%20single%20entry.png)
|
180
80
|
|
181
|
-
|
182
|
-
name FROM /PROJECT_PATH/sample/app/views/posts/show.html.erb:5
|
183
|
-
user_id FROM /PROJECT_PATH/sample/app/views/posts/show.html.erb:10
|
184
|
-
to_param FROM /RUBY_PATH/gems/2.6.0/gems/actionpack-5.2.0/lib/action_dispatch/routing/route_set.rb:236
|
185
|
-
```
|
81
|
+
These are the information you'd have to look up one by one manually (probably with many debug code writing). Now you can get all of them in just one line of code.
|
186
82
|
|
187
|
-
Also check the `track_as_records` option if you want to track `ActiveRecord` records.
|
188
83
|
|
189
|
-
###
|
84
|
+
### `print_traces` To See The Object's Traces
|
190
85
|
|
191
|
-
|
86
|
+
If you're not interested in what an object does, but what it interacts with other parts of the program, e.g., used as arguments. You can use the `print_traces` helper. Let's see how `Discourse` uses the `manager` object when creating a post
|
192
87
|
|
193
88
|
```ruby
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
89
|
+
def create
|
90
|
+
@manager_params = create_params
|
91
|
+
@manager_params[:first_post_checks] = !is_api?
|
92
|
+
|
93
|
+
manager = NewPostManager.new(current_user, @manager_params)
|
198
94
|
|
199
|
-
|
200
|
-
|
201
|
-
end
|
202
|
-
end
|
203
|
-
end
|
95
|
+
print_traces(manager)
|
96
|
+
# .....
|
204
97
|
```
|
205
98
|
|
206
|
-
|
207
|
-
Passed as 'record' in method ':polymorphic_mapping'
|
208
|
-
> def polymorphic_mapping(record)
|
209
|
-
at /Users/st0012/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/actionpack-6.0.0/lib/action_dispatch/routing/polymorphic_routes.rb:131
|
210
|
-
Passed as 'klass' in method ':get_method_for_class'
|
211
|
-
> def get_method_for_class(klass)
|
212
|
-
at /Users/st0012/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/actionpack-6.0.0/lib/action_dispatch/routing/polymorphic_routes.rb:269
|
213
|
-
Passed as 'record' in method ':handle_model'
|
214
|
-
> def handle_model(record)
|
215
|
-
at /Users/st0012/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/actionpack-6.0.0/lib/action_dispatch/routing/polymorphic_routes.rb:227
|
216
|
-
Passed as 'record_or_hash_or_array' in method ':polymorphic_method'
|
217
|
-
> def self.polymorphic_method(recipient, record_or_hash_or_array, action, type, options)
|
218
|
-
at /Users/st0012/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/actionpack-6.0.0/lib/action_dispatch/routing/polymorphic_routes.rb:139
|
219
|
-
```
|
99
|
+
And after running the test case
|
220
100
|
|
221
|
-
|
101
|
+
```shell
|
102
|
+
$ rspec spec/requests/posts_controller_spec.rb:603
|
103
|
+
```
|
222
104
|
|
223
|
-
`
|
105
|
+
You will see that it performs 2 calls: `perform` and `perform_create_post`. And it's also used as `manager` argument in various of calls of the `NewPostManager` class.
|
224
106
|
|
225
|
-
|
226
|
-
tap_assoc!(order).and_print(:method_name_and_location)
|
227
|
-
```
|
107
|
+
![image of print_traces output](https://github.com/st0012/tapping_device/blob/master/images/print_traces.png)
|
228
108
|
|
229
|
-
|
230
|
-
payments FROM /RUBY_PATH/gems/2.6.0/gems/jsonapi-resources-0.9.10/lib/jsonapi/resource.rb:124
|
231
|
-
line_items FROM /MY_PROJECT/app/models/line_item_container_helpers.rb:44
|
232
|
-
effective_line_items FROM /MY_PROJECT/app/models/line_item_container_helpers.rb:110
|
233
|
-
amending_orders FROM /MY_PROJECT/app/models/order.rb:385
|
234
|
-
amends_order FROM /MY_PROJECT/app/models/order.rb:432
|
235
|
-
```
|
109
|
+
**You can try these examples on [my fork of discourse](https://github.com/st0012/discourse/tree/demo-for-tapping-device)**
|
236
110
|
|
237
|
-
### tap_sql!
|
238
111
|
|
239
|
-
|
112
|
+
## Installation
|
113
|
+
Add this line to your application's Gemfile:
|
240
114
|
|
241
115
|
```ruby
|
242
|
-
|
243
|
-
def index
|
244
|
-
# simulate current_user
|
245
|
-
@current_user = User.last
|
246
|
-
# reusable ActiveRecord::Relation
|
247
|
-
@posts = Post.all
|
248
|
-
|
249
|
-
tap_sql!(@posts) do |payload|
|
250
|
-
puts("Method: #{payload[:method_name]} generated sql: #{payload[:sql]} from #{payload[:filepath]}:#{payload[:line_number]}")
|
251
|
-
end
|
252
|
-
end
|
253
|
-
end
|
116
|
+
gem 'tapping_device', group: :development
|
254
117
|
```
|
255
118
|
|
256
|
-
|
257
|
-
<h1>Posts (<%= @posts.count %>)</h1>
|
258
|
-
......
|
259
|
-
<% @posts.each do |post| %>
|
260
|
-
......
|
261
|
-
<% end %>
|
262
|
-
......
|
263
|
-
<p>Posts created by you: <%= @posts.where(user: @current_user).count %></p>
|
264
|
-
```
|
119
|
+
And then execute:
|
265
120
|
|
266
121
|
```
|
267
|
-
|
268
|
-
Method: each generated sql: SELECT "posts".* FROM "posts" from /PROJECT_PATH/rails-6-sample/app/views/posts/index.html.erb:16
|
269
|
-
Method: count generated sql: SELECT COUNT(*) FROM "posts" WHERE "posts"."user_id" = ? from /PROJECT_PATH/rails-6-sample/app/views/posts/index.html.erb:31
|
270
|
-
```
|
271
|
-
|
272
|
-
|
273
|
-
### Options
|
274
|
-
#### with_trace_to
|
275
|
-
It takes an integer as the number of traces we want to put into `trace`. Default is `nil`, so `trace` would be empty.
|
276
|
-
|
277
|
-
```ruby
|
278
|
-
stan = Student.new("Stan", 18)
|
279
|
-
tap_on!(stan, with_trace_to: 5)
|
280
|
-
|
281
|
-
stan.name
|
282
|
-
|
283
|
-
puts(device.calls.first.trace) #=>
|
284
|
-
/Users/st0012/projects/tapping_device/spec/tapping_device_spec.rb:287:in `block (4 levels) in <top (required)>'
|
285
|
-
/Users/st0012/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/rspec-core-3.8.2/lib/rspec/core/example.rb:257:in `instance_exec'
|
286
|
-
/Users/st0012/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/rspec-core-3.8.2/lib/rspec/core/example.rb:257:in `block in run'
|
287
|
-
/Users/st0012/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/rspec-core-3.8.2/lib/rspec/core/example.rb:503:in `block in with_around_and_singleton_context_hooks'
|
288
|
-
/Users/st0012/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/rspec-core-3.8.2/lib/rspec/core/example.rb:460:in `block in with_around_example_hooks'
|
289
|
-
/Users/st0012/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/rspec-core-3.8.2/lib/rspec/core/hooks.rb:464:in `block in run'
|
122
|
+
$ bundle
|
290
123
|
```
|
291
124
|
|
292
|
-
|
293
|
-
It makes the device to track objects as they are ActiveRecord instances. For example:
|
125
|
+
Or install it directly:
|
294
126
|
|
295
|
-
```ruby
|
296
|
-
tap_on!(@post, track_as_records: true)
|
297
|
-
post = Post.find(@post.id) # same record but a different object
|
298
|
-
post.title #=> this call will be recorded as well
|
299
127
|
```
|
300
|
-
|
301
|
-
#### exclude_by_paths
|
302
|
-
It takes an array of call path patterns that we want to skip. This could be very helpful when working on a large project like Rails applications.
|
303
|
-
|
304
|
-
```ruby
|
305
|
-
tap_on!(@post, exclude_by_paths: [/active_record/]).and_print(:method_name_and_location)
|
128
|
+
$ gem install tapping_device
|
306
129
|
```
|
307
130
|
|
308
|
-
|
309
|
-
_read_attribute FROM /RUBY_PATH/gems/2.6.0/gems/activerecord-5.2.0/lib/active_record/attribute_methods/read.rb:40
|
310
|
-
name FROM /PROJECT_PATH/sample/app/views/posts/show.html.erb:5
|
311
|
-
_read_attribute FROM /RUBY_PATH/gems/2.6.0/gems/activerecord-5.2.0/lib/active_record/attribute_methods/read.rb:40
|
312
|
-
user_id FROM /PROJECT_PATH/sample/app/views/posts/show.html.erb:10
|
313
|
-
.......
|
131
|
+
**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**
|
314
132
|
|
315
|
-
# versus
|
316
133
|
|
317
|
-
|
318
|
-
user_id FROM /PROJECT_PATH/sample/app/views/posts/show.html.erb:10
|
319
|
-
to_param FROM /RUBY_PATH/gems/2.6.0/gems/actionpack-5.2.0/lib/action_dispatch/routing/route_set.rb:236
|
320
|
-
```
|
134
|
+
### Advance Usages & Options
|
321
135
|
|
322
|
-
####
|
323
|
-
|
324
|
-
Like `exclude_by_paths`, but work in the opposite way.
|
325
|
-
|
326
|
-
|
327
|
-
### Payload of The Call
|
328
|
-
All tapping methods (start with `tap_`) takes a block and yield a `Payload` object as a block argument. It responds to
|
329
|
-
|
330
|
-
- `target` - the target for `tap_x` call
|
331
|
-
- `receiver` - the receiver object
|
332
|
-
- `method_name` - method’s name (symbol)
|
333
|
-
- e.g. `:name`
|
334
|
-
- `method_object` - the method object that's being called. It might be `nil` in some edge cases.
|
335
|
-
- `arguments` - arguments of the method call
|
336
|
-
- e.g. `{name: “Stan”, age: 25}`
|
337
|
-
- `return_value` - return value of the method call
|
338
|
-
- `filepath` - path to the file that performs the method call
|
339
|
-
- `line_number`
|
340
|
-
- `defined_class` - in which class that defines the method being called
|
341
|
-
- `trace` - stack trace of the call. Default is an empty array unless `with_trace_to` option is set
|
342
|
-
- `sql` - sql that generated from the call (only present in `tap_sql!` payloads)
|
343
|
-
- `tp` - trace point object of this call
|
344
|
-
|
345
|
-
|
346
|
-
#### Symbols for Payload Helpers
|
347
|
-
- `FROM` for method call’s location
|
348
|
-
- `<=` for arguments
|
349
|
-
- `=>` for return value
|
350
|
-
- `@` for defined class
|
351
|
-
|
352
|
-
#### Payload Helpers
|
353
|
-
- `method_name_and_location` - `initialize FROM /PROJECT_PATH/tapping_device/spec/payload_spec.rb:7`
|
354
|
-
- `method_name_and_arguments` - `initialize <= {:name=>\"Stan\", :age=>25}`
|
355
|
-
- `method_name_and_return_value` - `ten => 10`
|
356
|
-
- `method_name_and_defined_class` - `initialize @ Student`
|
357
|
-
- `passed_at` -
|
358
|
-
```
|
359
|
-
Passed as 'object' in method ':initialize'
|
360
|
-
at /Users/st0012/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/actionview-6.0.0/lib/action_view/helpers/tags/label.rb:60
|
361
|
-
```
|
136
|
+
#### Add Conditions With `.with`
|
362
137
|
|
363
|
-
|
138
|
+
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.
|
364
139
|
|
365
|
-
```
|
366
|
-
|
367
|
-
|
368
|
-
|
140
|
+
```ruby
|
141
|
+
# only prints calls with name matches /foo/
|
142
|
+
print_calls(object).with do |payload|
|
143
|
+
payload.method_name.to_s.match?(/foo/)
|
144
|
+
end
|
369
145
|
```
|
370
146
|
|
371
|
-
|
372
|
-
|
373
|
-
```
|
374
|
-
initialize @ Student
|
375
|
-
<= {:name=>"Stan", :age=>25}
|
376
|
-
=> 25
|
377
|
-
FROM /Users/st0012/projects/tapping_device/spec/payload_spec.rb:7
|
378
|
-
```
|
147
|
+
#### `colorize: false`
|
379
148
|
|
380
|
-
|
149
|
+
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.
|
381
150
|
|
382
|
-
Tapping methods introduced above like `tap_on!` are designed for simple use cases. They're actually short for
|
383
151
|
|
384
152
|
```ruby
|
385
|
-
|
386
|
-
device.tap_on!(object)
|
153
|
+
print_calls(object, colorize: false)
|
387
154
|
```
|
388
155
|
|
389
|
-
And if you want to do some more configurations like stopping them manually or setting stop condition, you must have a `TappingDevie` instance. You can either get them like the above code or save the return value of `tap_*!` method calls.
|
390
156
|
|
391
|
-
####
|
157
|
+
#### `inspect: true`
|
392
158
|
|
393
|
-
|
394
|
-
1. Manually calling `device.stop!`
|
395
|
-
2. Setting stop condition with `device.stop_when`, like
|
159
|
+
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:
|
396
160
|
|
397
|
-
```ruby
|
398
|
-
|
399
|
-
|
400
|
-
end
|
161
|
+
``` ruby
|
162
|
+
post.to_s #=> #<Post:0x00007f89a55201d0>
|
163
|
+
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>
|
401
164
|
```
|
402
165
|
|
403
|
-
#### Device states & Managing Devices
|
404
166
|
|
405
|
-
|
167
|
+
### Lower-Level Helpers
|
168
|
+
`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)
|
406
169
|
|
407
|
-
- `Initial` - means the instance is initialized but hasn't tapped on anything.
|
408
|
-
- `Enabled` - means the instance is tapping on something (has called `tap_*` methods).
|
409
|
-
- `Disabled` - means the instance has been disabled. It will no longer receive any call info.
|
410
170
|
|
411
|
-
|
171
|
+
### Related Blog Posts
|
172
|
+
- [Optimize Your Debugging Process With Object-Oriented Tracing and tapping_device](http://bit.ly/object-oriented-tracing)
|
173
|
+
- [Debug Rails issues effectively with tapping_device](https://dev.to/st0012/debug-rails-issues-effectively-with-tappingdevice-c7c)
|
174
|
+
- [Want to know more about your Rails app? Tap on your objects!](https://dev.to/st0012/want-to-know-more-about-your-rails-app-tap-on-your-objects-bd3)
|
412
175
|
|
413
|
-
- `TappingDevice.devices` - Lists all registered devices with `initial` or `enabled` state. Note that any instance that's been stopped will be removed from the list.
|
414
|
-
- `TappingDevice.stop_all!` - Stops all registered devices and remove them from the `devices` list.
|
415
|
-
- `TappingDevice.suspend_new!` - Suspends any device instance from changing their state from `initial` to `enabled`. Which means any `tap_*` calls after it will no longer work.
|
416
|
-
- `TappingDevice.reset!` - Cancels `suspend_new` (if called) and stops/removes all created devices. Useful to reset the environment between test cases.
|
417
176
|
|
418
177
|
## Development
|
419
|
-
|
420
178
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
421
179
|
|
422
180
|
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
Binary file
|
Binary file
|
Binary file
|
data/lib/tapping_device.rb
CHANGED
@@ -2,6 +2,7 @@ require "active_record"
|
|
2
2
|
require "tapping_device/version"
|
3
3
|
require "tapping_device/manageable"
|
4
4
|
require "tapping_device/payload"
|
5
|
+
require "tapping_device/output_payload"
|
5
6
|
require "tapping_device/trackable"
|
6
7
|
require "tapping_device/exceptions"
|
7
8
|
require "tapping_device/sql_tapping_methods"
|
@@ -25,6 +26,7 @@ class TappingDevice
|
|
25
26
|
@options = process_options(options)
|
26
27
|
@calls = []
|
27
28
|
@disabled = false
|
29
|
+
@with_condition = nil
|
28
30
|
self.class.devices << self
|
29
31
|
end
|
30
32
|
|
@@ -49,8 +51,21 @@ class TappingDevice
|
|
49
51
|
track(record, condition: :tap_associations?)
|
50
52
|
end
|
51
53
|
|
52
|
-
def and_print(payload_method)
|
53
|
-
@output_block =
|
54
|
+
def and_print(payload_method = nil, &block)
|
55
|
+
@output_block =
|
56
|
+
if block
|
57
|
+
-> (output_payload) { puts(block.call(output_payload)) }
|
58
|
+
elsif payload_method
|
59
|
+
-> (output_payload) { puts(output_payload.send(payload_method)) }
|
60
|
+
else
|
61
|
+
raise "need to provide either a payload method name or a block"
|
62
|
+
end
|
63
|
+
|
64
|
+
self
|
65
|
+
end
|
66
|
+
|
67
|
+
def with(&block)
|
68
|
+
@with_condition = block
|
54
69
|
end
|
55
70
|
|
56
71
|
def set_block(&block)
|
@@ -94,6 +109,8 @@ class TappingDevice
|
|
94
109
|
|
95
110
|
payload = build_payload(tp: tp, filepath: filepath, line_number: line_number, &payload_block)
|
96
111
|
|
112
|
+
next unless with_condition_satisfied?(payload)
|
113
|
+
|
97
114
|
record_call!(payload)
|
98
115
|
|
99
116
|
stop_if_condition_fulfilled(payload)
|
@@ -247,7 +264,7 @@ class TappingDevice
|
|
247
264
|
def record_call!(payload)
|
248
265
|
return if @disabled
|
249
266
|
|
250
|
-
@output_block.call(payload) if @output_block
|
267
|
+
@output_block.call(OutputPayload.init(payload)) if @output_block
|
251
268
|
|
252
269
|
if @block
|
253
270
|
root_device.calls << @block.call(payload)
|
@@ -255,4 +272,8 @@ class TappingDevice
|
|
255
272
|
root_device.calls << payload
|
256
273
|
end
|
257
274
|
end
|
275
|
+
|
276
|
+
def with_condition_satisfied?(payload)
|
277
|
+
@with_condition.blank? || @with_condition.call(payload)
|
278
|
+
end
|
258
279
|
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
class TappingDevice
|
2
|
+
class OutputPayload < Payload
|
3
|
+
alias :raw_arguments :arguments
|
4
|
+
alias :raw_return_value :return_value
|
5
|
+
|
6
|
+
def method_name(options = {})
|
7
|
+
":#{super(options)}"
|
8
|
+
end
|
9
|
+
|
10
|
+
def arguments(options = {})
|
11
|
+
generate_string_result(raw_arguments, options[:inspect])
|
12
|
+
end
|
13
|
+
|
14
|
+
def return_value(options = {})
|
15
|
+
generate_string_result(raw_return_value, options[:inspect])
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.full_color_code(code)
|
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
|
+
sql: {symbol: "QUERIES", color: COLORS[:nocolor]},
|
41
|
+
return_value: {symbol: "=>", color: COLORS[:megenta]},
|
42
|
+
arguments: {symbol: "<=", color: COLORS[:orange]},
|
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)}"
|
88
|
+
msg += "\n > #{method_head.strip}" 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
|
+
private
|
103
|
+
|
104
|
+
def value_with_color(value, color)
|
105
|
+
"#{COLORS[color]}#{value}#{COLORS[:reset]}"
|
106
|
+
end
|
107
|
+
|
108
|
+
def generate_string_result(obj, inspect)
|
109
|
+
case obj
|
110
|
+
when Array
|
111
|
+
array_to_string(obj, inspect)
|
112
|
+
when Hash
|
113
|
+
hash_to_string(obj, inspect)
|
114
|
+
when String
|
115
|
+
"\"#{obj}\""
|
116
|
+
else
|
117
|
+
inspect ? obj.inspect : obj.to_s
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def array_to_string(array, inspect)
|
122
|
+
elements_string = array.map do |elem|
|
123
|
+
generate_string_result(elem, inspect)
|
124
|
+
end.join(", ")
|
125
|
+
"[#{elements_string}]"
|
126
|
+
end
|
127
|
+
|
128
|
+
def hash_to_string(hash, inspect)
|
129
|
+
elements_string = hash.map do |key, value|
|
130
|
+
"#{key.to_s}: #{generate_string_result(value, inspect)}"
|
131
|
+
end.join(", ")
|
132
|
+
"{#{elements_string}}"
|
133
|
+
end
|
134
|
+
|
135
|
+
def obj_to_string(element, inspect)
|
136
|
+
to_string_method = inspect ? :inspect : :to_s
|
137
|
+
|
138
|
+
if !inspect && element.is_a?(String)
|
139
|
+
"\"#{element}\""
|
140
|
+
else
|
141
|
+
element.send(to_string_method)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
@@ -6,7 +6,7 @@ class TappingDevice
|
|
6
6
|
]
|
7
7
|
|
8
8
|
ATTRS.each do |attr|
|
9
|
-
define_method attr do
|
9
|
+
define_method attr do |options = {}|
|
10
10
|
self[attr]
|
11
11
|
end
|
12
12
|
end
|
@@ -19,86 +19,13 @@ class TappingDevice
|
|
19
19
|
h
|
20
20
|
end
|
21
21
|
|
22
|
-
def passed_at(with_method_head: false)
|
23
|
-
arg_name = arguments.keys.detect { |k| arguments[k] == target }
|
24
|
-
return unless arg_name
|
25
|
-
msg = "Passed as '#{arg_name}' in method ':#{method_name}'"
|
26
|
-
msg += "\n > #{method_head.strip}" if with_method_head
|
27
|
-
msg += "\n at #{location}"
|
28
|
-
msg
|
29
|
-
end
|
30
|
-
|
31
22
|
def method_head
|
32
23
|
source_file, source_line = method_object.source_location
|
33
24
|
IO.readlines(source_file)[source_line-1]
|
34
25
|
end
|
35
26
|
|
36
|
-
def location
|
27
|
+
def location(options = {})
|
37
28
|
"#{filepath}:#{line_number}"
|
38
29
|
end
|
39
|
-
|
40
|
-
SYMBOLS = {
|
41
|
-
location: "from:",
|
42
|
-
sql: "QUERIES",
|
43
|
-
return_value: "=>",
|
44
|
-
arguments: "<=",
|
45
|
-
defined_class: "#"
|
46
|
-
}
|
47
|
-
|
48
|
-
SYMBOLS.each do |name, symbol|
|
49
|
-
define_method "method_name_and_#{name}" do
|
50
|
-
":#{method_name} #{symbol} #{send(name)}"
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
def detail_call_info(inspect: false)
|
55
|
-
arguments_output = generate_string_result(arguments, inspect)
|
56
|
-
return_value_output = generate_string_result(return_value, inspect)
|
57
|
-
|
58
|
-
<<~MSG
|
59
|
-
#{method_name_and_defined_class}
|
60
|
-
from: #{location}
|
61
|
-
<= #{arguments_output}
|
62
|
-
=> #{return_value_output}
|
63
|
-
|
64
|
-
MSG
|
65
|
-
end
|
66
|
-
|
67
|
-
def generate_string_result(obj, inspect)
|
68
|
-
case obj
|
69
|
-
when Array
|
70
|
-
array_to_string(obj, inspect)
|
71
|
-
when Hash
|
72
|
-
hash_to_string(obj, inspect)
|
73
|
-
when String
|
74
|
-
"\"#{obj}\""
|
75
|
-
else
|
76
|
-
inspect ? obj.inspect : obj.to_s
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
def array_to_string(array, inspect)
|
81
|
-
elements_string = array.map do |elem|
|
82
|
-
generate_string_result(elem, inspect)
|
83
|
-
end.join(", ")
|
84
|
-
"[#{elements_string}]"
|
85
|
-
end
|
86
|
-
|
87
|
-
def hash_to_string(hash, inspect)
|
88
|
-
elements_string = hash.map do |key, value|
|
89
|
-
"#{key.to_s}: #{generate_string_result(value, inspect)}"
|
90
|
-
end.join(", ")
|
91
|
-
"{#{elements_string}}"
|
92
|
-
end
|
93
|
-
|
94
|
-
def obj_to_string(element, inspect)
|
95
|
-
to_string_method = inspect ? :inspect : :to_s
|
96
|
-
|
97
|
-
if !inspect && element.is_a?(String)
|
98
|
-
"\"#{element}\""
|
99
|
-
else
|
100
|
-
element.send(to_string_method)
|
101
|
-
end
|
102
|
-
end
|
103
30
|
end
|
104
31
|
end
|
@@ -8,23 +8,24 @@ class TappingDevice
|
|
8
8
|
|
9
9
|
def print_traces(target, options = {})
|
10
10
|
options[:event_type] = :call
|
11
|
+
inspect = options.delete(:inspect)
|
12
|
+
colorize = options.fetch(:colorize, true)
|
11
13
|
|
12
|
-
device_1 = tap_on!(target, options) do |
|
13
|
-
|
14
|
+
device_1 = tap_on!(target, options).and_print do |output_payload|
|
15
|
+
"Called #{output_payload.method_name_and_location(inspect: inspect, colorize: colorize)}"
|
14
16
|
end
|
15
|
-
device_2 = tap_passed!(target, options) do |
|
16
|
-
|
17
|
-
next unless arg_name
|
18
|
-
puts("Passed as '#{arg_name}' in '#{payload.defined_class}##{payload.method_name}' at #{payload.location}")
|
17
|
+
device_2 = tap_passed!(target, options).and_print do |output_payload|
|
18
|
+
output_payload.passed_at(inspect: inspect, colorize: colorize)
|
19
19
|
end
|
20
20
|
[device_1, device_2]
|
21
21
|
end
|
22
22
|
|
23
|
-
def
|
23
|
+
def print_calls(target, options = {})
|
24
24
|
inspect = options.delete(:inspect)
|
25
|
+
colorize = options.fetch(:colorize, true)
|
25
26
|
|
26
|
-
tap_on!(target, options) do |
|
27
|
-
|
27
|
+
tap_on!(target, options).and_print do |output_payload|
|
28
|
+
output_payload.detail_call_info(inspect: inspect, colorize: colorize)
|
28
29
|
end
|
29
30
|
end
|
30
31
|
|
data/tapping_device.gemspec
CHANGED
@@ -8,8 +8,8 @@ Gem::Specification.new do |spec|
|
|
8
8
|
spec.authors = ["st0012"]
|
9
9
|
spec.email = ["stan001212@gmail.com"]
|
10
10
|
|
11
|
-
spec.summary = %q{tapping_device
|
12
|
-
spec.description = %q{tapping_device
|
11
|
+
spec.summary = %q{tapping_device lets you understand what your Ruby objects do without digging into the code}
|
12
|
+
spec.description = %q{tapping_device lets you understand what your Ruby objects do without digging into the code}
|
13
13
|
spec.homepage = "https://github.com/st0012/tapping_device"
|
14
14
|
spec.license = "MIT"
|
15
15
|
|
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.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- st0012
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-05-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -122,13 +122,15 @@ dependencies:
|
|
122
122
|
- - '='
|
123
123
|
- !ruby/object:Gem::Version
|
124
124
|
version: 0.17.1
|
125
|
-
description: tapping_device
|
125
|
+
description: tapping_device lets you understand what your Ruby objects do without
|
126
|
+
digging into the code
|
126
127
|
email:
|
127
128
|
- stan001212@gmail.com
|
128
129
|
executables: []
|
129
130
|
extensions: []
|
130
131
|
extra_rdoc_files: []
|
131
132
|
files:
|
133
|
+
- ".DS_Store"
|
132
134
|
- ".github/workflows/gempush.yml"
|
133
135
|
- ".github/workflows/ruby.yml"
|
134
136
|
- ".gitignore"
|
@@ -143,9 +145,13 @@ files:
|
|
143
145
|
- Rakefile
|
144
146
|
- bin/console
|
145
147
|
- bin/setup
|
148
|
+
- images/print_calls - single entry.png
|
149
|
+
- images/print_calls.png
|
150
|
+
- images/print_traces.png
|
146
151
|
- lib/tapping_device.rb
|
147
152
|
- lib/tapping_device/exceptions.rb
|
148
153
|
- lib/tapping_device/manageable.rb
|
154
|
+
- lib/tapping_device/output_payload.rb
|
149
155
|
- lib/tapping_device/payload.rb
|
150
156
|
- lib/tapping_device/sql_tapping_methods.rb
|
151
157
|
- lib/tapping_device/trackable.rb
|
@@ -176,5 +182,6 @@ requirements: []
|
|
176
182
|
rubygems_version: 3.0.3
|
177
183
|
signing_key:
|
178
184
|
specification_version: 4
|
179
|
-
summary: tapping_device
|
185
|
+
summary: tapping_device lets you understand what your Ruby objects do without digging
|
186
|
+
into the code
|
180
187
|
test_files: []
|