tapping_device 0.4.9 → 0.4.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.ruby-version +1 -0
- data/Gemfile.lock +1 -1
- data/README.md +5 -47
- data/lib/tapping_device.rb +26 -17
- data/lib/tapping_device/payload.rb +1 -0
- data/lib/tapping_device/trackable.rb +2 -0
- data/lib/tapping_device/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 23d3086bd40ed2665b040147abdbd7ca9cf2fd6e8d0e4300e0dfe8c6c14def70
|
4
|
+
data.tar.gz: b980e4cbf079204151b81645aa9c097254c0be1f514c2e09482ee3a4ede1ec68
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4d731a0cc9436e48354a55afe094cd16ce93778876fd541702813645be21e2d77cdfa0580b93486c0a59ddf48c614df9106d752483842da5bec2bb19ee06e97a
|
7
|
+
data.tar.gz: 1de7093fd6b474260a7b819ddffa8839dc26ff5e5553d450084515e9ce6b1d69cb97066c69fa49f48777548e18768dd52dc8114efc5a469e63e870f3005086c2
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.6.5
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -7,9 +7,11 @@
|
|
7
7
|
[](https://www.codetriage.com/st0012/tapping_device)
|
8
8
|
|
9
9
|
## Related Posts
|
10
|
+
- [Optimize Your Debugging Process With Object-Oriented Tracing and tapping_device](http://bit.ly/object-oriented-tracing)
|
10
11
|
- [Debug Rails issues effectively with tapping_device](https://dev.to/st0012/debug-rails-issues-effectively-with-tappingdevice-c7c)
|
11
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)
|
12
13
|
|
14
|
+
|
13
15
|
## Table of Content
|
14
16
|
- [Introduction](#introduction)
|
15
17
|
- [Print Object’s Traces](print-objects-traces)
|
@@ -30,7 +32,9 @@
|
|
30
32
|
- [Advance Usages](#advance-usages)
|
31
33
|
|
32
34
|
## Introduction
|
33
|
-
`tapping_device` is a
|
35
|
+
`tapping_device` is a debugging tool built based on a concept called `object-oriented tracing` and on top of Ruby's `TracePoint` class. It allows you to inspect an object’s behavior, and thus build the program’s execution path for later debugging. Here’s a post to explain how to use `object-oriented tracing` and this gem to improve your debugging workflow: [Optimize Your Debugging Process With Object-Oriented Tracing and tapping_device](http://bit.ly/object-oriented-tracing).
|
36
|
+
|
37
|
+
Sample usage:
|
34
38
|
|
35
39
|
### Print Object’s Traces
|
36
40
|
|
@@ -38,8 +42,6 @@ Let your objects report to you, so you don’t need to guess how they work!
|
|
38
42
|
|
39
43
|
```ruby
|
40
44
|
class OrdersController < ApplicationController
|
41
|
-
include TappingDevice::Trackable
|
42
|
-
|
43
45
|
def create
|
44
46
|
@cart = Cart.find(order_params[:cart_id])
|
45
47
|
print_traces(@cart, exclude_by_paths: [/gems/])
|
@@ -59,42 +61,6 @@ Called :apply_discount FROM /Users/st0012/projects/tapping_device-demo/app/servi
|
|
59
61
|
|
60
62
|
(Also see [print_calls_in_detail](#print_calls_in_detail))
|
61
63
|
|
62
|
-
### Track Calls that Generates SQL Queries
|
63
|
-
|
64
|
-
`tap_sql!` method helps you track which method calls to generate SQL queries. This is particularly helpful when tracking calls created from a reused `ActiveRecord::Relation` object.
|
65
|
-
|
66
|
-
```ruby
|
67
|
-
class PostsController < ApplicationController
|
68
|
-
def index
|
69
|
-
# simulate current_user
|
70
|
-
@current_user = User.last
|
71
|
-
# reusable ActiveRecord::Relation
|
72
|
-
@posts = Post.all
|
73
|
-
|
74
|
-
tap_sql!(@posts) do |payload|
|
75
|
-
puts("Method: #{payload[:method_name]} generated sql: #{payload[:sql]} from #{payload[:filepath]}:#{payload[:line_number]}")
|
76
|
-
end
|
77
|
-
end
|
78
|
-
end
|
79
|
-
```
|
80
|
-
|
81
|
-
```erb
|
82
|
-
<h1>Posts (<%= @posts.count %>)</h1>
|
83
|
-
......
|
84
|
-
<% @posts.each do |post| %>
|
85
|
-
......
|
86
|
-
<% end %>
|
87
|
-
......
|
88
|
-
<p>Posts created by you: <%= @posts.where(user: @current_user).count %></p>
|
89
|
-
```
|
90
|
-
|
91
|
-
And the output would be
|
92
|
-
|
93
|
-
```
|
94
|
-
Method: count generated sql: SELECT COUNT(*) FROM "posts" from /PROJECT_PATH/rails-6-sample/app/views/posts/index.html.erb:3
|
95
|
-
Method: each generated sql: SELECT "posts".* FROM "posts" from /PROJECT_PATH/rails-6-sample/app/views/posts/index.html.erb:16
|
96
|
-
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
|
97
|
-
```
|
98
64
|
|
99
65
|
However, depending on the size of your application, tapping any object could **harm the performance significantly**. **Don't use this on production**
|
100
66
|
|
@@ -118,7 +84,6 @@ $ gem install tapping_device
|
|
118
84
|
```
|
119
85
|
|
120
86
|
## Usages
|
121
|
-
In order to use `tapping_device`, you need to include `TappingDevice::Trackable` module in where you want to track your code and call the following helpers.
|
122
87
|
|
123
88
|
### print_traces
|
124
89
|
|
@@ -126,8 +91,6 @@ It prints the object's trace. It's like mounting a GPS tracker + a spy camera on
|
|
126
91
|
|
127
92
|
```ruby
|
128
93
|
class OrdersController < ApplicationController
|
129
|
-
include TappingDevice::Trackable
|
130
|
-
|
131
94
|
def create
|
132
95
|
@cart = Cart.find(order_params[:cart_id])
|
133
96
|
print_traces(@cart, exclude_by_paths: [/gems/])
|
@@ -154,8 +117,6 @@ It prints the object's calls in detail (including call location, arguments, and
|
|
154
117
|
|
155
118
|
```ruby
|
156
119
|
class OrdersController < ApplicationController
|
157
|
-
include TappingDevice::Trackable
|
158
|
-
|
159
120
|
def create
|
160
121
|
@cart = Cart.find(order_params[:cart_id])
|
161
122
|
service = OrderCreationService.new
|
@@ -207,8 +168,6 @@ puts(calls.to_s) #=> [[:initialize, {:name=>"Stan", :age=>18}], [:initialize, {:
|
|
207
168
|
|
208
169
|
```ruby
|
209
170
|
class PostsController < ApplicationController
|
210
|
-
include TappingDevice::Trackable
|
211
|
-
|
212
171
|
before_action :set_post, only: [:show, :edit, :update, :destroy]
|
213
172
|
|
214
173
|
def show
|
@@ -233,7 +192,6 @@ Also check the `track_as_records` option if you want to track `ActiveRecord` rec
|
|
233
192
|
|
234
193
|
```ruby
|
235
194
|
class PostsController < ApplicationController
|
236
|
-
include TappingDevice::Trackable
|
237
195
|
# GET /posts/new
|
238
196
|
def new
|
239
197
|
@post = Post.new
|
data/lib/tapping_device.rb
CHANGED
@@ -30,7 +30,10 @@ class TappingDevice
|
|
30
30
|
|
31
31
|
def tap_init!(klass)
|
32
32
|
raise "argument should be a class, got #{klass}" unless klass.is_a?(Class)
|
33
|
-
track(klass, condition: :tap_init?)
|
33
|
+
track(klass, condition: :tap_init?) do |payload|
|
34
|
+
payload[:return_value] = payload[:receiver]
|
35
|
+
payload[:receiver] = klass
|
36
|
+
end
|
34
37
|
end
|
35
38
|
|
36
39
|
def tap_on!(object)
|
@@ -81,7 +84,7 @@ class TappingDevice
|
|
81
84
|
|
82
85
|
private
|
83
86
|
|
84
|
-
def track(object, condition
|
87
|
+
def track(object, condition:, &payload_block)
|
85
88
|
@target = object
|
86
89
|
@trace_point = TracePoint.new(options[:event_type]) do |tp|
|
87
90
|
if send(condition, object, tp)
|
@@ -89,7 +92,7 @@ class TappingDevice
|
|
89
92
|
|
90
93
|
next if should_be_skipped_by_paths?(filepath)
|
91
94
|
|
92
|
-
payload = build_payload(tp: tp, filepath: filepath, line_number: line_number)
|
95
|
+
payload = build_payload(tp: tp, filepath: filepath, line_number: line_number, &payload_block)
|
93
96
|
|
94
97
|
record_call!(payload)
|
95
98
|
|
@@ -130,15 +133,12 @@ class TappingDevice
|
|
130
133
|
end
|
131
134
|
|
132
135
|
def build_payload(tp:, filepath:, line_number:)
|
133
|
-
|
134
|
-
tp.binding.local_variables.each { |name| arguments[name] = tp.binding.local_variable_get(name) }
|
135
|
-
|
136
|
-
Payload.init({
|
136
|
+
payload = Payload.init({
|
137
137
|
target: @target,
|
138
138
|
receiver: tp.self,
|
139
139
|
method_name: tp.callee_id,
|
140
140
|
method_object: get_method_object_from(tp.self, tp.callee_id),
|
141
|
-
arguments:
|
141
|
+
arguments: collect_arguments(tp),
|
142
142
|
return_value: (tp.return_value rescue nil),
|
143
143
|
filepath: filepath,
|
144
144
|
line_number: line_number,
|
@@ -146,6 +146,10 @@ class TappingDevice
|
|
146
146
|
trace: get_traces(tp),
|
147
147
|
tp: tp
|
148
148
|
})
|
149
|
+
|
150
|
+
yield(payload) if block_given?
|
151
|
+
|
152
|
+
payload
|
149
153
|
end
|
150
154
|
|
151
155
|
def tap_init?(klass, tp)
|
@@ -176,15 +180,7 @@ class TappingDevice
|
|
176
180
|
return false if is_from_target?(self, tp)
|
177
181
|
return false if tp.defined_class == TappingDevice::Trackable || tp.defined_class == TappingDevice
|
178
182
|
|
179
|
-
|
180
|
-
return false unless method_object.is_a?(Method)
|
181
|
-
# if a no-arugment method is called, tp.binding.local_variables will be those local variables in the same scope
|
182
|
-
# so we need to make sure the method takes arguments, then we can be sure that the locals are arguments
|
183
|
-
return false unless method_object && method_object.arity.to_i > 0
|
184
|
-
|
185
|
-
argument_values = tp.binding.local_variables.map { |name| tp.binding.local_variable_get(name) }
|
186
|
-
|
187
|
-
argument_values.any? do |value|
|
183
|
+
collect_arguments(tp).values.any? do |value|
|
188
184
|
# during comparison, Ruby might perform data type conversion like calling `to_sym` on the value
|
189
185
|
# but not every value supports every conversion methods
|
190
186
|
object == value rescue false
|
@@ -204,6 +200,19 @@ class TappingDevice
|
|
204
200
|
nil
|
205
201
|
end
|
206
202
|
|
203
|
+
def collect_arguments(tp)
|
204
|
+
parameters =
|
205
|
+
if RUBY_VERSION.to_f >= 2.6
|
206
|
+
tp.parameters
|
207
|
+
else
|
208
|
+
get_method_object_from(tp.self, tp.callee_id)&.parameters || []
|
209
|
+
end.map { |parameter| parameter[1] }
|
210
|
+
|
211
|
+
tp.binding.local_variables.each_with_object({}) do |name, args|
|
212
|
+
args[name] = tp.binding.local_variable_get(name) if parameters.include?(name)
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
207
216
|
def process_options(options)
|
208
217
|
options[:filter_by_paths] ||= []
|
209
218
|
options[:exclude_by_paths] ||= []
|
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.
|
4
|
+
version: 0.4.10
|
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-02-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -147,6 +147,7 @@ files:
|
|
147
147
|
- ".github/workflows/ruby.yml"
|
148
148
|
- ".gitignore"
|
149
149
|
- ".rspec"
|
150
|
+
- ".ruby-version"
|
150
151
|
- ".travis.yml"
|
151
152
|
- CODE_OF_CONDUCT.md
|
152
153
|
- Gemfile
|