tapping_device 0.4.7 → 0.5.0

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: 72cee52830dca423c383eb154be4d7352b187cd2bcd848af5ed8d38af0f5ad40
4
- data.tar.gz: 96d64f49adfd1de6b3174c9ecdb38d0f8d7e6f03bc0b1b85a79bf3b86613e349
3
+ metadata.gz: 9806974025c4e1ba9042bf2df5a4b4211a7e4a9630b291e05892d472e54263cc
4
+ data.tar.gz: fc609ed5be23cac4943e90c34d7126aaffd53816910320f7300d2a33a9d40585
5
5
  SHA512:
6
- metadata.gz: a6675be23b1bbd4e0dfd6b1c4ea0ccbd39fba970af38a567928066935ab85a20003ecaf157f09f8e20223f2ec0717a72403261541d1b6836c393832c7240d35e
7
- data.tar.gz: b93a1bd72210aa7f528687da0edaca7e812cb0849d7d0da8578ba8f5551a2134f6014a1f8c3b7989d750a15bdb734db1826d748de42bf4b995e3f7850cec00de
6
+ metadata.gz: 15ee39e171f665492e47c378bcffedd3d7bc1c8204269607e54c1ac174f44fe5ee4cc33a0ffcd82d4b732abc63def63eefb8b1fda386d4e40503a4bd959e71e2
7
+ data.tar.gz: 3b4bb1b05949fd4735fde568995e8c92c64634ad39f196f76649e2f401834d8fdc605080546655f90920e8c2cfc1cebb4bf686b7da34494217c677e058f6c73b
Binary file
@@ -22,7 +22,7 @@ jobs:
22
22
  - name: Install sqlite
23
23
  run: |
24
24
  # See https://github.community/t5/GitHub-Actions/ubuntu-latest-Apt-repository-list-issues/td-p/41122/page/2
25
- sudo rm -f /etc/apt/sources.list.d/dotnetdev.list /etc/apt/sources.list.d/microsoft-prod.list
25
+ for apt_file in `grep -lr microsoft /etc/apt/sources.list.d/`; do sudo rm $apt_file; done
26
26
  sudo apt-get update
27
27
  sudo apt-get install libsqlite3-dev
28
28
 
@@ -0,0 +1 @@
1
+ 2.6.5
@@ -1,37 +1,37 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- tapping_device (0.4.7)
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.2.1)
11
- activesupport (= 6.0.2.1)
12
- activerecord (6.0.2.1)
13
- activemodel (= 6.0.2.1)
14
- activesupport (= 6.0.2.1)
15
- activesupport (6.0.2.1)
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
- concurrent-ruby (1.1.5)
22
+ concurrent-ruby (1.1.6)
23
23
  database_cleaner (1.7.0)
24
24
  diff-lcs (1.3)
25
25
  docile (1.3.2)
26
- i18n (1.7.0)
26
+ i18n (1.8.2)
27
27
  concurrent-ruby (~> 1.0)
28
28
  json (2.3.0)
29
29
  method_source (0.9.2)
30
- minitest (5.13.0)
30
+ minitest (5.14.1)
31
31
  pry (0.12.2)
32
32
  coderay (~> 1.1.0)
33
33
  method_source (~> 0.9.0)
34
- rake (10.0.4)
34
+ rake (13.0.1)
35
35
  rspec (3.8.0)
36
36
  rspec-core (~> 3.8.0)
37
37
  rspec-expectations (~> 3.8.0)
@@ -52,9 +52,9 @@ GEM
52
52
  simplecov-html (0.10.2)
53
53
  sqlite3 (1.4.1)
54
54
  thread_safe (0.3.6)
55
- tzinfo (1.2.6)
55
+ tzinfo (1.2.7)
56
56
  thread_safe (~> 0.1)
57
- zeitwerk (2.2.2)
57
+ zeitwerk (2.3.0)
58
58
 
59
59
  PLATFORMS
60
60
  ruby
@@ -63,11 +63,11 @@ DEPENDENCIES
63
63
  bundler (~> 2.0)
64
64
  database_cleaner
65
65
  pry
66
- rake (~> 10.0)
66
+ rake (~> 13.0)
67
67
  rspec (~> 3.0)
68
68
  simplecov (= 0.17.1)
69
69
  sqlite3 (>= 1.3.6)
70
70
  tapping_device!
71
71
 
72
72
  BUNDLED WITH
73
- 2.0.2
73
+ 2.1.1
data/README.md CHANGED
@@ -1,406 +1,180 @@
1
1
  # TappingDevice
2
2
 
3
- ![](https://github.com/st0012/tapping_device/workflows/Ruby/badge.svg)
3
+ ![GitHub Action](https://github.com/st0012/tapping_device/workflows/Ruby/badge.svg)
4
+ [![Gem Version](https://badge.fury.io/rb/tapping_device.svg)](https://badge.fury.io/rb/tapping_device)
4
5
  [![Maintainability](https://api.codeclimate.com/v1/badges/3e3732a6983785bccdbd/maintainability)](https://codeclimate.com/github/st0012/tapping_device/maintainability)
5
6
  [![Test Coverage](https://api.codeclimate.com/v1/badges/3e3732a6983785bccdbd/test_coverage)](https://codeclimate.com/github/st0012/tapping_device/test_coverage)
6
7
  [![Open Source Helpers](https://www.codetriage.com/st0012/tapping_device/badges/users.svg)](https://www.codetriage.com/st0012/tapping_device)
7
8
 
8
- ## Related Posts
9
- - [Debug Rails issues effectively with tapping_device](https://dev.to/st0012/debug-rails-issues-effectively-with-tappingdevice-c7c)
10
- - [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)
11
-
12
- ## Table of Content
13
- - [Introduction](#introduction)
14
- - [Track Method Calls](#track-method-calls)
15
- - [Track Association Calls](#track-association-calls)
16
- - [Track Calls that Generates SQL Queries](#track-calls-that-generates-sql-queries)
17
- - [Installation](#installation)
18
- - [Usages](#usages)
19
- - Tapping Methods
20
- - [tap_init!](#tap_init)
21
- - [tap_on!](#tap_on)
22
- - [tap_passed!](#tap_passed)
23
- - [tap_assoc!](#tap_assoc)
24
- - [tap_sql!](#tap_sql)
25
- - [Options](#options)
26
- - [Payload](#payload-of-the-call)
27
- - [Advance Usages](#advance-usages)
28
9
 
29
10
  ## Introduction
30
- `tapping_device` is a gem built on top of Ruby’s `TracePoint` class that allows you to tap method calls of specified objects. The purpose for this gem is to make debugging Rails applications easier. Here are some sample usages:
11
+ `TappingDevice` makes the objects tell you what they do, so you don't need to track them yourself.
31
12
 
32
- ### Track Method Calls
13
+ #### Contract Tracing For Objects
33
14
 
34
- ```ruby
35
- class PostsController < ApplicationController
36
- include TappingDevice::Trackable
37
-
38
- def show
39
- @post = Post.find(params[:id])
40
- tap_on!(@post).and_print(:method_name_and_location)
41
- end
42
- end
43
- ```
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
44
16
 
45
- And you can see these in log:
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)
46
19
 
47
- ```
48
- name FROM /PROJECT_PATH/sample/app/views/posts/show.html.erb:5
49
- user_id FROM /PROJECT_PATH/sample/app/views/posts/show.html.erb:10
50
- to_param FROM /RUBY_PATH/gems/2.6.0/gems/actionpack-5.2.0/lib/action_dispatch/routing/route_set.rb:236
51
- ```
20
+ Still sounds vague? Let's see some examples:
52
21
 
53
- ### Track Association Calls
22
+ ### `print_calls` To Track Method Calls
54
23
 
55
- Or you can use `tap_assoc!`. This is very useful for tracking potential n+1 query calls, here’s a sample from my work
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:
56
25
 
57
26
  ```ruby
58
- tap_assoc!(order).and_print(:method_name_and_location)
59
- ```
60
-
61
- ```
62
- payments FROM /RUBY_PATH/gems/2.6.0/gems/jsonapi-resources-0.9.10/lib/jsonapi/resource.rb:124
63
- line_items FROM /MY_PROJECT/app/models/line_item_container_helpers.rb:44
64
- effective_line_items FROM /MY_PROJECT/app/models/line_item_container_helpers.rb:110
65
- amending_orders FROM /MY_PROJECT/app/models/order.rb:385
66
- amends_order FROM /MY_PROJECT/app/models/order.rb:432
67
- ```
68
-
69
- ### Track Calls that Generates SQL Queries
70
-
71
- `tap_sql!` method helps you track which method calls generate sql queries. This is particularly helpful when tracking calls created from a reused `ActiveRecord::Relation` object.
72
-
73
- ```ruby
74
- class PostsController < ApplicationController
75
- def index
76
- # simulate current_user
77
- @current_user = User.last
78
- # reusable ActiveRecord::Relation
79
- @posts = Post.all
80
-
81
- tap_sql!(@posts) do |payload|
82
- puts("Method: #{payload[:method_name]} generated sql: #{payload[:sql]} from #{payload[:filepath]}:#{payload[:line_number]}")
27
+ def create
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?)
83
45
  end
84
46
  end
85
- end
86
- ```
87
-
88
- ```erb
89
- <h1>Posts (<%= @posts.count %>)</h1>
90
- ......
91
- <% @posts.each do |post| %>
92
- ......
93
- <% end %>
94
- ......
95
- <p>Posts created by you: <%= @posts.where(user: @current_user).count %></p>
96
- ```
97
-
98
- And the output would be
99
-
100
- ```
101
- Method: count generated sql: SELECT COUNT(*) FROM "posts" from /PROJECT_PATH/rails-6-sample/app/views/posts/index.html.erb:3
102
- Method: each generated sql: SELECT "posts".* FROM "posts" from /PROJECT_PATH/rails-6-sample/app/views/posts/index.html.erb:16
103
- 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
104
47
  ```
105
48
 
106
- However, depending on the size of your application, tapping any object could **harm the performance significantly**. **Don’t use this on production**
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.
107
50
 
108
- ## Installation
109
- Add this line to your application’s Gemfile:
51
+ But with `TappingDevice`. You can use `print_calls` to show what method calls the object performs
110
52
 
111
53
  ```ruby
112
- gem 'tapping_device', group: :development
54
+ def create
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
+ # .....
113
60
  ```
114
61
 
115
- And then execute:
62
+ Now, if you execute the code, like via tests:
116
63
 
117
- ```
118
- $ bundle
64
+ ```shell
65
+ $ rspec spec/requests/posts_controller_spec.rb:603
119
66
  ```
120
67
 
121
- Or install it yourself as:
68
+ You can get all the method calls it performs with basically everything you need to know
122
69
 
123
- ```
124
- $ gem install tapping_device
125
- ```
70
+ <img src="https://github.com/st0012/tapping_device/blob/master/images/print_calls.png" alt="image of print_calls output" width="50%">
126
71
 
127
- ## Usages
128
- 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.
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
129
78
 
130
- ### tap_init!
79
+ ![explanation of individual entry](https://github.com/st0012/tapping_device/blob/master/images/print_calls%20-%20single%20entry.png)
131
80
 
132
- `tap_init!(class)` - tracks a class’ instance initialization
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.
133
82
 
134
- ```ruby
135
- calls = []
136
- tap_init!(Student) do |payload|
137
- calls << [payload[:method_name], payload[:arguments]]
138
- end
139
83
 
140
- Student.new("Stan", 18)
141
- Student.new("Jane", 23)
84
+ ### `print_traces` To See The Object's Traces
142
85
 
143
- puts(calls.to_s) #=> [[:initialize, {:name=>"Stan", :age=>18}], [:initialize, {:name=>"Jane", :age=>23}]]
144
- ```
145
-
146
- ### tap_on!
147
-
148
- `tap_on!(object)` - tracks any calls received by the object
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
149
87
 
150
88
  ```ruby
151
- class PostsController < ApplicationController
152
- include TappingDevice::Trackable
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)
153
94
 
154
- before_action :set_post, only: [:show, :edit, :update, :destroy]
155
-
156
- def show
157
- tap_on!(@post).and_print(:method_name_and_location)
158
- end
159
- end
95
+ print_traces(manager)
96
+ # .....
160
97
  ```
161
98
 
162
- And you can see these in log:
163
-
164
- ```
165
- name FROM /PROJECT_PATH/sample/app/views/posts/show.html.erb:5
166
- user_id FROM /PROJECT_PATH/sample/app/views/posts/show.html.erb:10
167
- to_param FROM /RUBY_PATH/gems/2.6.0/gems/actionpack-5.2.0/lib/action_dispatch/routing/route_set.rb:236
168
- ```
169
-
170
- Also check the `track_as_records` option if you want to track `ActiveRecord` records.
171
-
172
- ### tap_passed!
99
+ And after running the test case
173
100
 
174
- `tap_passed!(target)` tracks method calls that **takes the target as its argument**. This is particularly useful when debugging libraries. It saves your time from jumping between files and check which path the object will go.
175
-
176
- ```ruby
177
- class PostsController < ApplicationController
178
- include TappingDevice::Trackable
179
- # GET /posts/new
180
- def new
181
- @post = Post.new
182
-
183
- tap_passed!(@post) do |payload|
184
- puts(payload.passed_at(with_method_head: true))
185
- end
186
- end
187
- end
188
- ```
189
-
190
- ```
191
- Passed as 'record' in method ':polymorphic_mapping'
192
- > def polymorphic_mapping(record)
193
- 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
194
- Passed as 'klass' in method ':get_method_for_class'
195
- > def get_method_for_class(klass)
196
- 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
197
- Passed as 'record' in method ':handle_model'
198
- > def handle_model(record)
199
- 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
200
- Passed as 'record_or_hash_or_array' in method ':polymorphic_method'
201
- > def self.polymorphic_method(recipient, record_or_hash_or_array, action, type, options)
202
- 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
101
+ ```shell
102
+ $ rspec spec/requests/posts_controller_spec.rb:603
203
103
  ```
204
104
 
205
- ### tap_assoc!
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.
206
106
 
207
- `tap_assoc!(activerecord_object)` tracks association calls on a record, like `post.comments`
107
+ ![image of print_traces output](https://github.com/st0012/tapping_device/blob/master/images/print_traces.png)
208
108
 
209
- ```ruby
210
- tap_assoc!(order).and_print(:method_name_and_location)
211
- ```
109
+ **You can try these examples on [my fork of discourse](https://github.com/st0012/discourse/tree/demo-for-tapping-device)**
212
110
 
213
- ```
214
- payments FROM /RUBY_PATH/gems/2.6.0/gems/jsonapi-resources-0.9.10/lib/jsonapi/resource.rb:124
215
- line_items FROM /MY_PROJECT/app/models/line_item_container_helpers.rb:44
216
- effective_line_items FROM /MY_PROJECT/app/models/line_item_container_helpers.rb:110
217
- amending_orders FROM /MY_PROJECT/app/models/order.rb:385
218
- amends_order FROM /MY_PROJECT/app/models/order.rb:432
219
- ```
220
111
 
221
- ### tap_sql!
222
-
223
- `tap_sql!(anything_that_generates_sql_queries)` tracks sql queries generated from the target
112
+ ## Installation
113
+ Add this line to your application's Gemfile:
224
114
 
225
115
  ```ruby
226
- class PostsController < ApplicationController
227
- def index
228
- # simulate current_user
229
- @current_user = User.last
230
- # reusable ActiveRecord::Relation
231
- @posts = Post.all
232
-
233
- tap_sql!(@posts) do |payload|
234
- puts("Method: #{payload[:method_name]} generated sql: #{payload[:sql]} from #{payload[:filepath]}:#{payload[:line_number]}")
235
- end
236
- end
237
- end
116
+ gem 'tapping_device', group: :development
238
117
  ```
239
118
 
240
- ```erb
241
- <h1>Posts (<%= @posts.count %>)</h1>
242
- ......
243
- <% @posts.each do |post| %>
244
- ......
245
- <% end %>
246
- ......
247
- <p>Posts created by you: <%= @posts.where(user: @current_user).count %></p>
248
- ```
119
+ And then execute:
249
120
 
250
121
  ```
251
- Method: count generated sql: SELECT COUNT(*) FROM "posts" from /PROJECT_PATH/rails-6-sample/app/views/posts/index.html.erb:3
252
- Method: each generated sql: SELECT "posts".* FROM "posts" from /PROJECT_PATH/rails-6-sample/app/views/posts/index.html.erb:16
253
- 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
254
- ```
255
-
256
-
257
- ### Options
258
- #### with_trace_to
259
- It takes an integer as the number of traces we want to put into `trace`. Default is `nil`, so `trace` would be empty.
260
-
261
- ```ruby
262
- stan = Student.new("Stan", 18)
263
- tap_on!(stan, with_trace_to: 5)
264
-
265
- stan.name
266
-
267
- puts(device.calls.first.trace) #=>
268
- /Users/st0012/projects/tapping_device/spec/tapping_device_spec.rb:287:in `block (4 levels) in <top (required)>'
269
- /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'
270
- /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'
271
- /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'
272
- /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'
273
- /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
274
123
  ```
275
124
 
276
- #### track_as_records
277
- It makes the device to track objects as they are ActiveRecord instances. For example:
125
+ Or install it directly:
278
126
 
279
- ```ruby
280
- tap_on!(@post, track_as_records: true)
281
- post = Post.find(@post.id) # same record but a different object
282
- post.title #=> this call will be recorded as well
283
127
  ```
284
-
285
- #### exclude_by_paths
286
- It takes an array of call path patterns that we want to skip. This could be very helpful when working on large project like Rails applications.
287
-
288
- ```ruby
289
- tap_on!(@post, exclude_by_paths: [/active_record/]).and_print(:method_name_and_location)
128
+ $ gem install tapping_device
290
129
  ```
291
130
 
292
- ```
293
- _read_attribute FROM /RUBY_PATH/gems/2.6.0/gems/activerecord-5.2.0/lib/active_record/attribute_methods/read.rb:40
294
- name FROM /PROJECT_PATH/sample/app/views/posts/show.html.erb:5
295
- _read_attribute FROM /RUBY_PATH/gems/2.6.0/gems/activerecord-5.2.0/lib/active_record/attribute_methods/read.rb:40
296
- user_id FROM /PROJECT_PATH/sample/app/views/posts/show.html.erb:10
297
- .......
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**
298
132
 
299
- # versus
300
133
 
301
- name FROM /PROJECT_PATH/sample/app/views/posts/show.html.erb:5
302
- user_id FROM /PROJECT_PATH/sample/app/views/posts/show.html.erb:10
303
- to_param FROM /RUBY_PATH/gems/2.6.0/gems/actionpack-5.2.0/lib/action_dispatch/routing/route_set.rb:236
304
- ```
134
+ ### Advance Usages & Options
305
135
 
306
- #### filter_by_paths
307
-
308
- Like `exclude_by_paths`, but work in an opposite way.
309
-
310
-
311
- ### Payload of The Call
312
- All tapping methods (start with `tap_`) takes a block and yield a `Payload` object as block argument. It responds to
313
-
314
- - `target` - the target for `tap_x` call
315
- - `receiver` - the receiver object
316
- - `method_name` - method’s name (symbol)
317
- - e.g. `:name`
318
- - `method_object` - the method object that’s being called. It might be `nil` in some edge cases.
319
- - `arguments` - arguments of the method call
320
- - e.g. `{name: “Stan”, age: 25}`
321
- - `return_value` - return value of the method call
322
- - `filepath` - path to the file that performs the method call
323
- - `line_number`
324
- - `defined_class` - in which class that defines the method being called
325
- - `trace` - stack trace of the call. Default is an empty array unless `with_trace_to` option is set
326
- - `sql` - sql that generated from the call (only present in `tap_sql!` payloads)
327
- - `tp` - trace point object of this call
328
-
329
-
330
- #### Symbols for Payload Helpers
331
- - `FROM` for method call’s location
332
- - `<=` for arguments
333
- - `=>` for return value
334
- - `@` for defined class
335
-
336
- #### Payload Helpers
337
- - `method_name_and_location` - `initialize FROM /PROJECT_PATH/tapping_device/spec/payload_spec.rb:7`
338
- - `method_name_and_arguments` - `initialize <= {:name=>\"Stan\", :age=>25}`
339
- - `method_name_and_return_value` - `ten => 10`
340
- - `method_name_and_defined_class` - `initialize @ Student`
341
- - `passed_at` -
342
- ```
343
- Passed as 'object' in method ':initialize'
344
- 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
345
- ```
136
+ #### Add Conditions With `.with`
346
137
 
347
- You can also set `passed_at(with_method_head: true)` to see the method’s head
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.
348
139
 
349
- ```
350
- Passed as 'object' in method ':initialize'
351
- > def initialize(template_object, object_name, method_name, object, tag_value)
352
- 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
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
353
145
  ```
354
146
 
355
- - `detail_call_info`
356
-
357
- ```
358
- initialize @ Student
359
- <= {:name=>"Stan", :age=>25}
360
- => 25
361
- FROM /Users/st0012/projects/tapping_device/spec/payload_spec.rb:7
362
- ```
147
+ #### `colorize: false`
363
148
 
364
- ### Advance Usages
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.
365
150
 
366
- Tapping methods introduced above like `tap_on!` are designed for simple use cases. They’re actually short for
367
151
 
368
152
  ```ruby
369
- device = TappingDevice.new { # tapping action }
370
- device.tap_on!(object)
153
+ print_calls(object, colorize: false)
371
154
  ```
372
155
 
373
- 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.
374
156
 
375
- #### Stop tapping
157
+ #### `inspect: true`
376
158
 
377
- Once you have a `TappingDevice` instance in hand, you will be able to stop the tapping by
378
- 1. Manually calling `device.stop!`
379
- 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:
380
160
 
381
- ```ruby
382
- device.stop_when do |payload|
383
- device.calls.count >= 10 # stop after gathering 10 calls’ data
384
- 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>
385
164
  ```
386
165
 
387
- #### Device states & Managing Devices
388
166
 
389
- Each `TappingDevice` instance can have 3 states:
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)
390
169
 
391
- - `Initial` - means the instance is initialized but hasn’t tapped on anything.
392
- - `Enabled` - means the instance are tapping on something (has called `tap_*` methods).
393
- - `Disabled` - means the instance has been disabled. It will no longer receive any call info.
394
170
 
395
- When debugging, we may create many device instances and tap objects in several places. Then it’ll be quite annoying to manage their states. So `TappingDevice` has several class methods that allows you to manage all `TappingDevice` instances:
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)
396
175
 
397
- - `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.
398
- - `TappingDevice.stop_all!` - Stops all registered devices and remove them from the `devices` list.
399
- - `TappingDevice.suspend_new!` - Suspends any device instance from changing their state from `initatial` to `enabled`. Which means any `tap_*` calls after it will no longer work.
400
- - `TappingDevice.reset!` - Cancels `suspend_new` (if called) and stops/removes all created devices. Useful to reset environment between test cases.
401
176
 
402
177
  ## Development
403
-
404
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.
405
179
 
406
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).
@@ -411,8 +185,8 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/st0012
411
185
 
412
186
  ## License
413
187
 
414
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
188
+ The gem is available as open-source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
415
189
 
416
190
  ## Code of Conduct
417
191
 
418
- Everyone interacting in the TappingDevice projects 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).
192
+ 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).
Binary file
Binary file
@@ -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,12 +26,16 @@ 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
 
31
33
  def tap_init!(klass)
32
34
  raise "argument should be a class, got #{klass}" unless klass.is_a?(Class)
33
- track(klass, condition: :tap_init?)
35
+ track(klass, condition: :tap_init?) do |payload|
36
+ payload[:return_value] = payload[:receiver]
37
+ payload[:receiver] = klass
38
+ end
34
39
  end
35
40
 
36
41
  def tap_on!(object)
@@ -46,8 +51,21 @@ class TappingDevice
46
51
  track(record, condition: :tap_associations?)
47
52
  end
48
53
 
49
- def and_print(payload_method)
50
- @output_block = -> (payload) { puts(payload.send(payload_method)) }
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
51
69
  end
52
70
 
53
71
  def set_block(&block)
@@ -81,15 +99,17 @@ class TappingDevice
81
99
 
82
100
  private
83
101
 
84
- def track(object, condition:)
102
+ def track(object, condition:, &payload_block)
85
103
  @target = object
86
- @trace_point = TracePoint.new(:return) do |tp|
104
+ @trace_point = TracePoint.new(options[:event_type]) do |tp|
87
105
  if send(condition, object, tp)
88
106
  filepath, line_number = get_call_location(tp)
89
107
 
90
108
  next if should_be_skipped_by_paths?(filepath)
91
109
 
92
- payload = build_payload(tp: tp, filepath: filepath, line_number: line_number)
110
+ payload = build_payload(tp: tp, filepath: filepath, line_number: line_number, &payload_block)
111
+
112
+ next unless with_condition_satisfied?(payload)
93
113
 
94
114
  record_call!(payload)
95
115
 
@@ -130,15 +150,12 @@ class TappingDevice
130
150
  end
131
151
 
132
152
  def build_payload(tp:, filepath:, line_number:)
133
- arguments = {}
134
- tp.binding.local_variables.each { |name| arguments[name] = tp.binding.local_variable_get(name) }
135
-
136
- Payload.init({
153
+ payload = Payload.init({
137
154
  target: @target,
138
155
  receiver: tp.self,
139
156
  method_name: tp.callee_id,
140
157
  method_object: get_method_object_from(tp.self, tp.callee_id),
141
- arguments: arguments,
158
+ arguments: collect_arguments(tp),
142
159
  return_value: (tp.return_value rescue nil),
143
160
  filepath: filepath,
144
161
  line_number: line_number,
@@ -146,6 +163,10 @@ class TappingDevice
146
163
  trace: get_traces(tp),
147
164
  tp: tp
148
165
  })
166
+
167
+ yield(payload) if block_given?
168
+
169
+ payload
149
170
  end
150
171
 
151
172
  def tap_init?(klass, tp)
@@ -176,15 +197,7 @@ class TappingDevice
176
197
  return false if is_from_target?(self, tp)
177
198
  return false if tp.defined_class == TappingDevice::Trackable || tp.defined_class == TappingDevice
178
199
 
179
- method_object = get_method_object_from(tp.self, tp.callee_id)
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|
200
+ collect_arguments(tp).values.any? do |value|
188
201
  # during comparison, Ruby might perform data type conversion like calling `to_sym` on the value
189
202
  # but not every value supports every conversion methods
190
203
  object == value rescue false
@@ -204,11 +217,25 @@ class TappingDevice
204
217
  nil
205
218
  end
206
219
 
220
+ def collect_arguments(tp)
221
+ parameters =
222
+ if RUBY_VERSION.to_f >= 2.6
223
+ tp.parameters
224
+ else
225
+ get_method_object_from(tp.self, tp.callee_id)&.parameters || []
226
+ end.map { |parameter| parameter[1] }
227
+
228
+ tp.binding.local_variables.each_with_object({}) do |name, args|
229
+ args[name] = tp.binding.local_variable_get(name) if parameters.include?(name)
230
+ end
231
+ end
232
+
207
233
  def process_options(options)
208
234
  options[:filter_by_paths] ||= []
209
235
  options[:exclude_by_paths] ||= []
210
236
  options[:with_trace_to] ||= 50
211
237
  options[:root_device] ||= self
238
+ options[:event_type] ||= :return
212
239
  options[:descendants] ||= []
213
240
  options[:track_as_records] ||= false
214
241
  options
@@ -237,7 +264,7 @@ class TappingDevice
237
264
  def record_call!(payload)
238
265
  return if @disabled
239
266
 
240
- @output_block.call(payload) if @output_block
267
+ @output_block.call(OutputPayload.init(payload)) if @output_block
241
268
 
242
269
  if @block
243
270
  root_device.calls << @block.call(payload)
@@ -245,4 +272,8 @@ class TappingDevice
245
272
  root_device.calls << payload
246
273
  end
247
274
  end
275
+
276
+ def with_condition_satisfied?(payload)
277
+ @with_condition.blank? || @with_condition.call(payload)
278
+ end
248
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,45 +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
55
- <<~MSG
56
- #{method_name_and_defined_class}
57
- <= #{arguments}
58
- => #{return_value || "nil"}
59
- FROM #{location}
60
- MSG
61
- end
62
30
  end
63
31
  end
@@ -6,8 +6,33 @@ class TappingDevice
6
6
  end
7
7
  end
8
8
 
9
+ def print_traces(target, options = {})
10
+ options[:event_type] = :call
11
+ inspect = options.delete(:inspect)
12
+ colorize = options.fetch(:colorize, true)
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)}"
16
+ end
17
+ device_2 = tap_passed!(target, options).and_print do |output_payload|
18
+ output_payload.passed_at(inspect: inspect, colorize: colorize)
19
+ end
20
+ [device_1, device_2]
21
+ end
22
+
23
+ def print_calls(target, options = {})
24
+ inspect = options.delete(:inspect)
25
+ colorize = options.fetch(:colorize, true)
26
+
27
+ tap_on!(target, options).and_print do |output_payload|
28
+ output_payload.detail_call_info(inspect: inspect, colorize: colorize)
29
+ end
30
+ end
31
+
9
32
  def new_device(options, &block)
10
33
  TappingDevice.new(options, &block)
11
34
  end
12
35
  end
13
36
  end
37
+
38
+ include TappingDevice::Trackable
@@ -1,3 +1,3 @@
1
1
  class TappingDevice
2
- VERSION = "0.4.7"
2
+ VERSION = "0.5.0"
3
3
  end
@@ -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 provides useful helpers to intercept method calls}
12
- spec.description = %q{tapping_device provides useful helpers to intercept method calls}
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
 
@@ -36,7 +36,7 @@ Gem::Specification.new do |spec|
36
36
  spec.add_development_dependency "database_cleaner"
37
37
  spec.add_development_dependency "bundler", "~> 2.0"
38
38
  spec.add_development_dependency "pry"
39
- spec.add_development_dependency "rake", "~> 10.0"
39
+ spec.add_development_dependency "rake", "~> 13.0"
40
40
  spec.add_development_dependency "rspec", "~> 3.0"
41
41
  spec.add_development_dependency "simplecov", "0.17.1"
42
42
  end
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.7
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: 2019-12-29 00:00:00.000000000 Z
11
+ date: 2020-05-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -86,14 +86,14 @@ dependencies:
86
86
  requirements:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: '10.0'
89
+ version: '13.0'
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: '10.0'
96
+ version: '13.0'
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: rspec
99
99
  requirement: !ruby/object:Gem::Requirement
@@ -122,17 +122,20 @@ dependencies:
122
122
  - - '='
123
123
  - !ruby/object:Gem::Version
124
124
  version: 0.17.1
125
- description: tapping_device provides useful helpers to intercept method calls
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"
135
137
  - ".rspec"
138
+ - ".ruby-version"
136
139
  - ".travis.yml"
137
140
  - CODE_OF_CONDUCT.md
138
141
  - Gemfile
@@ -142,9 +145,13 @@ files:
142
145
  - Rakefile
143
146
  - bin/console
144
147
  - bin/setup
148
+ - images/print_calls - single entry.png
149
+ - images/print_calls.png
150
+ - images/print_traces.png
145
151
  - lib/tapping_device.rb
146
152
  - lib/tapping_device/exceptions.rb
147
153
  - lib/tapping_device/manageable.rb
154
+ - lib/tapping_device/output_payload.rb
148
155
  - lib/tapping_device/payload.rb
149
156
  - lib/tapping_device/sql_tapping_methods.rb
150
157
  - lib/tapping_device/trackable.rb
@@ -175,5 +182,6 @@ requirements: []
175
182
  rubygems_version: 3.0.3
176
183
  signing_key:
177
184
  specification_version: 4
178
- summary: tapping_device provides useful helpers to intercept method calls
185
+ summary: tapping_device lets you understand what your Ruby objects do without digging
186
+ into the code
179
187
  test_files: []