tapping_device 0.4.3 → 0.4.4
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/.github/workflows/ruby.yml +2 -0
- data/Gemfile.lock +7 -7
- data/README.md +51 -20
- data/lib/tapping_device.rb +66 -60
- data/lib/tapping_device/manageable.rb +37 -0
- data/lib/tapping_device/payload.rb +23 -2
- data/lib/tapping_device/sql_tapping_methods.rb +1 -0
- data/lib/tapping_device/trackable.rb +4 -14
- 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: b224ae2b5f110b0b4f6cfce578eaf6d4170d646e8141aa087e0abbd7780938d1
|
4
|
+
data.tar.gz: 836334b532fad8983c63070ab322f88cce8578a61ff79b57fe76bd2f65f141a5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6c78d7c9adad4b705e8a8ec65316fe182764090cbfdfd3f0ab8a4931675f8527317031a1e0e1ac8726d613c87e777a9895b668c3376dbcc61ad9fc59501df34e
|
7
|
+
data.tar.gz: 0a6388a5c2096ce392d08951fd89f63c0f9967bbe21e99b2462893d75f454552288e49b231c2692b056b09567751c38756ff52bafd53fc7c09615534cee9c8a9
|
data/.github/workflows/ruby.yml
CHANGED
@@ -19,6 +19,8 @@ jobs:
|
|
19
19
|
ruby-version: ${{ matrix.ruby_version }}
|
20
20
|
- name: Install sqlite
|
21
21
|
run: |
|
22
|
+
# See https://github.community/t5/GitHub-Actions/ubuntu-latest-Apt-repository-list-issues/td-p/41122/page/2
|
23
|
+
sudo rm -f /etc/apt/sources.list.d/dotnetdev.list /etc/apt/sources.list.d/microsoft-prod.list
|
22
24
|
sudo apt-get update
|
23
25
|
sudo apt-get install libsqlite3-dev
|
24
26
|
|
data/Gemfile.lock
CHANGED
@@ -1,18 +1,18 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
tapping_device (0.4.
|
4
|
+
tapping_device (0.4.4)
|
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.2)
|
11
|
+
activesupport (= 6.0.2)
|
12
|
+
activerecord (6.0.2)
|
13
|
+
activemodel (= 6.0.2)
|
14
|
+
activesupport (= 6.0.2)
|
15
|
+
activesupport (6.0.2)
|
16
16
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
17
17
|
i18n (>= 0.7, < 2)
|
18
18
|
minitest (~> 5.1)
|
data/README.md
CHANGED
@@ -2,7 +2,6 @@
|
|
2
2
|
|
3
3
|

|
4
4
|
|
5
|
-
**Please use 0.3.0+ versions, older versions have serious performance issues**
|
6
5
|
|
7
6
|
`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:
|
8
7
|
|
@@ -50,7 +49,7 @@ Method: :amends_order, line: /MY_PROJECT/app/models/order.rb:432
|
|
50
49
|
```
|
51
50
|
|
52
51
|
|
53
|
-
### Track calls that generates sql queries!
|
52
|
+
### Track calls that generates sql queries!
|
54
53
|
|
55
54
|
`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.
|
56
55
|
|
@@ -120,27 +119,13 @@ In order to use `tapping_device`, you need to include `TappingDevice::Trackable`
|
|
120
119
|
- `tap_sql!(activerecord_relation_or_model)` - tracks sql queries generated from the target
|
121
120
|
|
122
121
|
### Payload of the call
|
123
|
-
All tapping methods (start with `tap_`) takes a block and yield a `Payload` object as block argument.
|
124
|
-
|
125
|
-
```ruby
|
126
|
-
{
|
127
|
-
:receiver=>#<Student:0x00007fabed02aeb8 @name="Stan", @age=18, @tapping_device=[#<TracePoint:return `age'@/PROJECT_PATH/tapping_device/spec/trackable_spec.rb:17>]>,
|
128
|
-
:method_name=>:age,
|
129
|
-
:arguments=>{},
|
130
|
-
:return_value=>18,
|
131
|
-
:filepath=>"/PROJECT_PATH/tapping_device/spec/trackable_spec.rb",
|
132
|
-
:line_number=>"171",
|
133
|
-
:defined_class=>Student,
|
134
|
-
:trace=>[],
|
135
|
-
:tp=>#<TracePoint:return `age'@/PROJECT_PATH/tapping_device/spec/trackable_spec.rb:17>
|
136
|
-
}
|
137
|
-
```
|
138
|
-
|
139
|
-
The hash contains
|
122
|
+
All tapping methods (start with `tap_`) takes a block and yield a `Payload` object as block argument. It responds to
|
140
123
|
|
124
|
+
- `target` - the target for `tap_x` call
|
141
125
|
- `receiver` - the receiver object
|
142
126
|
- `method_name` - method’s name (symbol)
|
143
127
|
- e.g. `:name`
|
128
|
+
- `method_object` - the method object that’s being called. It might be `nil` in some edge cases.
|
144
129
|
- `arguments` - arguments of the method call
|
145
130
|
- e.g. `{name: “Stan”, age: 25}`
|
146
131
|
- `return_value` - return value of the method call
|
@@ -153,6 +138,19 @@ The hash contains
|
|
153
138
|
#### Some useful helpers
|
154
139
|
- `method_name_and_location` - `"Method: :initialize, line: /PROJECT_PATH/tapping_device/spec/payload_spec.rb:7"`
|
155
140
|
- `method_name_and_arguments` - `"Method: :initialize, argments: {:name=>\"Stan\", :age=>25}"`
|
141
|
+
- `passed_at` -
|
142
|
+
```
|
143
|
+
Passed as 'object' in method ':initialize'
|
144
|
+
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
|
145
|
+
```
|
146
|
+
|
147
|
+
You can also set `passed_at(with_method_head: true)` to see the method’s head
|
148
|
+
|
149
|
+
```
|
150
|
+
Passed as 'object' in method ':initialize'
|
151
|
+
> def initialize(template_object, object_name, method_name, object, tag_value)
|
152
|
+
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
|
153
|
+
```
|
156
154
|
|
157
155
|
|
158
156
|
### Options
|
@@ -221,6 +219,39 @@ Method: :user_id, line: /PROJECT_PATH/sample/app/views/posts/show.html.erb:10
|
|
221
219
|
Method: :to_param, line: /RUBY_PATH/gems/2.6.0/gems/actionpack-5.2.0/lib/action_dispatch/routing/route_set.rb:236
|
222
220
|
```
|
223
221
|
|
222
|
+
### `tap_passed!`
|
223
|
+
|
224
|
+
This is particularly useful when debugging libraries. It saves your time from jumping between files and check which path the object will go.
|
225
|
+
|
226
|
+
```ruby
|
227
|
+
class PostsController < ApplicationController
|
228
|
+
include TappingDevice::Trackable
|
229
|
+
# GET /posts/new
|
230
|
+
def new
|
231
|
+
@post = Post.new
|
232
|
+
|
233
|
+
tap_passed!(@post) do |payload|
|
234
|
+
puts(payload.passed_at(with_method_head: true))
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
```
|
239
|
+
|
240
|
+
```
|
241
|
+
Passed as 'record' in method ':polymorphic_mapping'
|
242
|
+
> def polymorphic_mapping(record)
|
243
|
+
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
|
244
|
+
Passed as 'klass' in method ':get_method_for_class'
|
245
|
+
> def get_method_for_class(klass)
|
246
|
+
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
|
247
|
+
Passed as 'record' in method ':handle_model'
|
248
|
+
> def handle_model(record)
|
249
|
+
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
|
250
|
+
Passed as 'record_or_hash_or_array' in method ':polymorphic_method'
|
251
|
+
> def self.polymorphic_method(recipient, record_or_hash_or_array, action, type, options)
|
252
|
+
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
|
253
|
+
```
|
254
|
+
|
224
255
|
### `tap_assoc!`
|
225
256
|
|
226
257
|
```ruby
|
@@ -237,7 +268,7 @@ Method: :amending_orders, line: /MY_PROJECT/app/models/order.rb:385
|
|
237
268
|
Method: :amends_order, line: /MY_PROJECT/app/models/order.rb:432
|
238
269
|
```
|
239
270
|
|
240
|
-
### `tap_sql!`
|
271
|
+
### `tap_sql!`
|
241
272
|
|
242
273
|
```ruby
|
243
274
|
class PostsController < ApplicationController
|
data/lib/tapping_device.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require "active_record"
|
2
2
|
require "tapping_device/version"
|
3
|
+
require "tapping_device/manageable"
|
3
4
|
require "tapping_device/payload"
|
4
5
|
require "tapping_device/trackable"
|
5
6
|
require "tapping_device/exceptions"
|
@@ -10,44 +11,13 @@ class TappingDevice
|
|
10
11
|
CALLER_START_POINT = 3
|
11
12
|
C_CALLER_START_POINT = 2
|
12
13
|
|
13
|
-
attr_reader :options, :calls, :trace_point
|
14
|
+
attr_reader :options, :calls, :trace_point, :target
|
14
15
|
|
15
|
-
|
16
|
-
|
16
|
+
@devices = []
|
17
|
+
@suspend_new = false
|
17
18
|
|
18
19
|
include SqlTappingMethods
|
19
|
-
|
20
|
-
def self.suspend_new
|
21
|
-
@@suspend_new
|
22
|
-
end
|
23
|
-
|
24
|
-
# list all registered devices
|
25
|
-
def self.devices
|
26
|
-
@@devices
|
27
|
-
end
|
28
|
-
|
29
|
-
# disable given device and remove it from registered list
|
30
|
-
def self.delete_device(device)
|
31
|
-
device.trace_point&.disable
|
32
|
-
@@devices -= [device]
|
33
|
-
end
|
34
|
-
|
35
|
-
# stops all registered devices and remove them from registered list
|
36
|
-
def self.stop_all!
|
37
|
-
@@devices.each(&:stop!)
|
38
|
-
end
|
39
|
-
|
40
|
-
# suspend enabling new trace points
|
41
|
-
# user can still create new Device instances, but they won't be functional
|
42
|
-
def self.suspend_new!
|
43
|
-
@@suspend_new = true
|
44
|
-
end
|
45
|
-
|
46
|
-
# reset everything to clean state and disable all devices
|
47
|
-
def self.reset!
|
48
|
-
@@suspend_new = false
|
49
|
-
stop_all!
|
50
|
-
end
|
20
|
+
extend Manageable
|
51
21
|
|
52
22
|
def initialize(options = {}, &block)
|
53
23
|
@block = block
|
@@ -66,6 +36,10 @@ class TappingDevice
|
|
66
36
|
track(object, condition: :tap_on?)
|
67
37
|
end
|
68
38
|
|
39
|
+
def tap_passed!(object)
|
40
|
+
track(object, condition: :tap_passed?)
|
41
|
+
end
|
42
|
+
|
69
43
|
def tap_assoc!(record)
|
70
44
|
raise "argument should be an instance of ActiveRecord::Base" unless record.is_a?(ActiveRecord::Base)
|
71
45
|
track(record, condition: :tap_associations?)
|
@@ -87,6 +61,7 @@ class TappingDevice
|
|
87
61
|
def create_child_device
|
88
62
|
new_device = self.class.new(@options.merge(root_device: root_device), &@block)
|
89
63
|
new_device.stop_when(&@stop_when)
|
64
|
+
new_device.instance_variable_set(:@target, @target)
|
90
65
|
self.descendants << new_device
|
91
66
|
new_device
|
92
67
|
end
|
@@ -99,26 +74,12 @@ class TappingDevice
|
|
99
74
|
options[:descendants]
|
100
75
|
end
|
101
76
|
|
102
|
-
def record_call!(payload)
|
103
|
-
return if @disabled
|
104
|
-
|
105
|
-
if @block
|
106
|
-
root_device.calls << @block.call(payload)
|
107
|
-
else
|
108
|
-
root_device.calls << payload
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
77
|
private
|
113
78
|
|
114
79
|
def track(object, condition:)
|
80
|
+
@target = object
|
115
81
|
@trace_point = TracePoint.new(:return) do |tp|
|
116
|
-
|
117
|
-
receiver: tp.self,
|
118
|
-
method_name: tp.callee_id
|
119
|
-
}
|
120
|
-
|
121
|
-
if send(condition, object, validation_params)
|
82
|
+
if send(condition, object, tp)
|
122
83
|
filepath, line_number = get_call_location(tp)
|
123
84
|
|
124
85
|
next if should_be_skipped_by_paths?(filepath)
|
@@ -131,7 +92,7 @@ class TappingDevice
|
|
131
92
|
end
|
132
93
|
end
|
133
94
|
|
134
|
-
@trace_point.enable unless
|
95
|
+
@trace_point.enable unless self.class.suspend_new
|
135
96
|
|
136
97
|
self
|
137
98
|
end
|
@@ -155,8 +116,10 @@ class TappingDevice
|
|
155
116
|
tp.binding.local_variables.each { |name| arguments[name] = tp.binding.local_variable_get(name) }
|
156
117
|
|
157
118
|
Payload.init({
|
119
|
+
target: @target,
|
158
120
|
receiver: tp.self,
|
159
121
|
method_name: tp.callee_id,
|
122
|
+
method_object: get_method_object_from(tp.self, tp.callee_id),
|
160
123
|
arguments: arguments,
|
161
124
|
return_value: (tp.return_value rescue nil),
|
162
125
|
filepath: filepath,
|
@@ -167,9 +130,9 @@ class TappingDevice
|
|
167
130
|
})
|
168
131
|
end
|
169
132
|
|
170
|
-
def tap_init?(klass,
|
171
|
-
receiver =
|
172
|
-
method_name =
|
133
|
+
def tap_init?(klass, tp)
|
134
|
+
receiver = tp.self
|
135
|
+
method_name = tp.callee_id
|
173
136
|
|
174
137
|
if klass.ancestors.include?(ActiveRecord::Base)
|
175
138
|
method_name == :new && receiver.ancestors.include?(klass)
|
@@ -178,16 +141,49 @@ class TappingDevice
|
|
178
141
|
end
|
179
142
|
end
|
180
143
|
|
181
|
-
def tap_on?(object,
|
182
|
-
|
144
|
+
def tap_on?(object, tp)
|
145
|
+
is_from_target?(object, tp)
|
183
146
|
end
|
184
147
|
|
185
|
-
def tap_associations?(object,
|
186
|
-
return false unless tap_on?(object,
|
148
|
+
def tap_associations?(object, tp)
|
149
|
+
return false unless tap_on?(object, tp)
|
187
150
|
|
188
151
|
model_class = object.class
|
189
152
|
associations = model_class.reflections
|
190
|
-
associations.keys.include?(
|
153
|
+
associations.keys.include?(tp.callee_id.to_s)
|
154
|
+
end
|
155
|
+
|
156
|
+
def tap_passed?(object, tp)
|
157
|
+
# we don't care about calls from the device instance
|
158
|
+
return false if is_from_target?(self, tp)
|
159
|
+
|
160
|
+
method_object = get_method_object_from(tp.self, tp.callee_id)
|
161
|
+
# if a no-arugment method is called, tp.binding.local_variables will be those local variables in the same scope
|
162
|
+
# so we need to make sure the method takes arguments, then we can be sure that the locals are arguments
|
163
|
+
return false unless method_object && method_object.arity.to_i > 0
|
164
|
+
|
165
|
+
argument_values = tp.binding.local_variables.map { |name| tp.binding.local_variable_get(name) }
|
166
|
+
|
167
|
+
argument_values.any? do |value|
|
168
|
+
# during comparison, Ruby might perform data type conversion like calling `to_sym` on the value
|
169
|
+
# but not every value supports every conversion methods
|
170
|
+
begin
|
171
|
+
object == value
|
172
|
+
rescue NoMethodError
|
173
|
+
false
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def get_method_object_from(target, method_name)
|
179
|
+
target.method(method_name)
|
180
|
+
rescue ArgumentError
|
181
|
+
method_method = Object.method(:method).unbind
|
182
|
+
method_method.bind(target).call(method_name)
|
183
|
+
rescue NameError
|
184
|
+
# if any part of the program uses Refinement to extend its methods
|
185
|
+
# we might still get NoMethodError when trying to get that method outside the scope
|
186
|
+
nil
|
191
187
|
end
|
192
188
|
|
193
189
|
def process_options(options)
|
@@ -209,4 +205,14 @@ class TappingDevice
|
|
209
205
|
def is_from_target?(object, tp)
|
210
206
|
object.__id__ == tp.self.__id__
|
211
207
|
end
|
208
|
+
|
209
|
+
def record_call!(payload)
|
210
|
+
return if @disabled
|
211
|
+
|
212
|
+
if @block
|
213
|
+
root_device.calls << @block.call(payload)
|
214
|
+
else
|
215
|
+
root_device.calls << payload
|
216
|
+
end
|
217
|
+
end
|
212
218
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
class TappingDevice
|
2
|
+
module Manageable
|
3
|
+
|
4
|
+
def suspend_new
|
5
|
+
@suspend_new
|
6
|
+
end
|
7
|
+
|
8
|
+
# list all registered devices
|
9
|
+
def devices
|
10
|
+
@devices
|
11
|
+
end
|
12
|
+
|
13
|
+
# disable given device and remove it from registered list
|
14
|
+
def delete_device(device)
|
15
|
+
device.trace_point&.disable
|
16
|
+
@devices -= [device]
|
17
|
+
end
|
18
|
+
|
19
|
+
# stops all registered devices and remove them from registered list
|
20
|
+
def stop_all!
|
21
|
+
@devices.each(&:stop!)
|
22
|
+
end
|
23
|
+
|
24
|
+
# suspend enabling new trace points
|
25
|
+
# user can still create new Device instances, but they won't be functional
|
26
|
+
def suspend_new!
|
27
|
+
@suspend_new = true
|
28
|
+
end
|
29
|
+
|
30
|
+
# reset everything to clean state and disable all devices
|
31
|
+
def reset!
|
32
|
+
@suspend_new = false
|
33
|
+
stop_all!
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
@@ -1,6 +1,9 @@
|
|
1
1
|
class TappingDevice
|
2
2
|
class Payload < Hash
|
3
|
-
ATTRS = [
|
3
|
+
ATTRS = [
|
4
|
+
:target, :receiver, :method_name, :method_object, :arguments, :return_value, :filepath, :line_number,
|
5
|
+
:defined_class, :trace, :tp, :sql
|
6
|
+
]
|
4
7
|
|
5
8
|
ATTRS.each do |attr|
|
6
9
|
define_method attr do
|
@@ -16,8 +19,26 @@ class TappingDevice
|
|
16
19
|
h
|
17
20
|
end
|
18
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
|
+
def method_head
|
32
|
+
source_file, source_line = method_object.source_location
|
33
|
+
IO.readlines(source_file)[source_line-1]
|
34
|
+
end
|
35
|
+
|
36
|
+
def location
|
37
|
+
"#{filepath}:#{line_number}"
|
38
|
+
end
|
39
|
+
|
19
40
|
def method_name_and_location
|
20
|
-
"Method: :#{method_name}, line: #{
|
41
|
+
"Method: :#{method_name}, line: #{location}"
|
21
42
|
end
|
22
43
|
|
23
44
|
def method_name_and_arguments
|
@@ -13,6 +13,7 @@ class TappingDevice
|
|
13
13
|
|
14
14
|
def tap_sql!(object)
|
15
15
|
@call_stack = []
|
16
|
+
@target ||= object
|
16
17
|
@trace_point = with_trace_point_on_target(object, event: [:call, :c_call]) do |start_tp|
|
17
18
|
########## Check if the call is worth recording ##########
|
18
19
|
filepath, line_number = get_call_location(start_tp, padding: 1) # we need extra padding because of `with_trace_point_on_target`
|
@@ -1,19 +1,9 @@
|
|
1
1
|
class TappingDevice
|
2
2
|
module Trackable
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
def tap_assoc!(record, options = {}, &block)
|
8
|
-
new_device(options, &block).tap_assoc!(record)
|
9
|
-
end
|
10
|
-
|
11
|
-
def tap_on!(object, options = {}, &block)
|
12
|
-
new_device(options, &block).tap_on!(object)
|
13
|
-
end
|
14
|
-
|
15
|
-
def tap_sql!(object, options = {}, &block)
|
16
|
-
new_device(options, &block).tap_sql!(object)
|
3
|
+
[:tap_on!, :tap_init!, :tap_assoc!, :tap_sql!, :tap_passed!].each do |method|
|
4
|
+
define_method method do |object, options = {}, &block|
|
5
|
+
new_device(options, &block).send(method, object)
|
6
|
+
end
|
17
7
|
end
|
18
8
|
|
19
9
|
def new_device(options, &block)
|
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.4
|
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-
|
11
|
+
date: 2019-12-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -130,6 +130,7 @@ files:
|
|
130
130
|
- bin/setup
|
131
131
|
- lib/tapping_device.rb
|
132
132
|
- lib/tapping_device/exceptions.rb
|
133
|
+
- lib/tapping_device/manageable.rb
|
133
134
|
- lib/tapping_device/payload.rb
|
134
135
|
- lib/tapping_device/sql_tapping_methods.rb
|
135
136
|
- lib/tapping_device/trackable.rb
|