tapping_device 0.5.4 → 0.6.1

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: 231efa59a44f630df303db52d0d3bb460698c02a116374838e4bad4d71d7f69e
4
- data.tar.gz: ca80df8a0ea00350c1e074628425848b5cba4d7f53a7aa4176947d55a3164929
3
+ metadata.gz: 9700574881872ce91eedf976fd5fa7c8506acfa1c0ea8819dc15eeef6e519a9b
4
+ data.tar.gz: 71b65b11ed57273a0c061f23141d5da92078a16f9a7c7f75e9aebb2de71c32c4
5
5
  SHA512:
6
- metadata.gz: 77131aab9e85a2661694ee57470b9067ee44d479d574cbe395eefabd7f88350eaf31071c4ed0d117b1608856e9e6beff9d7ccae592a665c8969231425dafdc43
7
- data.tar.gz: c06dedd14da7798cf93d80ae0d4866928c9e2522184f453588691fdf1a3220b21cfdf953f04575f1015cc1cb58c5bcb2cb5673fd9384cf5515855d048d0bbdb3
6
+ metadata.gz: 7ead5f7a86fd942005a85394a0b59e309fc41bcf625369006ec848d2688722cf5fcc8651ad925c103536bef49e1d0a50f4aab75a177242c48873bc5703aea8cd
7
+ data.tar.gz: f7cab27f0e27937f8c5a8f8c08283ae376e5b7880dffa21857e48c3bcf6e17ed1cb265b5ef399b2f2d294bf5af97b6b26aef16589f97f502c9ebfa29178de0a8
@@ -1,6 +1,11 @@
1
1
  name: Ruby
2
2
 
3
- on: [push, pull_request]
3
+ on:
4
+ workflow_dispatch:
5
+ push:
6
+ branches:
7
+ - master
8
+ pull_request:
4
9
 
5
10
  jobs:
6
11
  test:
@@ -8,9 +13,12 @@ jobs:
8
13
  runs-on: ${{ matrix.os }}
9
14
  strategy:
10
15
  matrix:
11
- rails_version: ['5.2', '6']
12
- ruby_version: ['2.5', '2.6']
16
+ rails_version: ['5.2', '6.0.0', '6.1.0']
17
+ ruby_version: ['2.7', '3.0']
13
18
  os: [ubuntu-latest]
19
+ exclude:
20
+ - ruby_version: '3.0'
21
+ rails_version: '5.2'
14
22
  steps:
15
23
  - uses: actions/checkout@v1
16
24
 
@@ -34,7 +42,9 @@ jobs:
34
42
  bundle install --jobs 4 --retry 3
35
43
 
36
44
  - name: Run test with Rails ${{ matrix.rails_version }}
37
- run: bundle exec rake
45
+ env:
46
+ RAILS_VERSION: ${{ matrix.rails_version }}
47
+ run: make test
38
48
 
39
49
  - name: Publish Test Coverage
40
50
  uses: paambaati/codeclimate-action@v2.6.0
data/.gitignore CHANGED
@@ -7,5 +7,7 @@
7
7
  /spec/reports/
8
8
  /tmp/
9
9
 
10
+ Gemfile.lock
11
+
10
12
  # rspec failure tracking
11
13
  .rspec_status
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 2.6.5
1
+ 3.0.1
data/CHANGELOG.md CHANGED
@@ -1,5 +1,76 @@
1
1
  # Changelog
2
2
 
3
+ ## [v0.6.0](https://github.com/st0012/tapping_device/tree/v0.6.0) (2021-04-25)
4
+
5
+ [Full Changelog](https://github.com/st0012/tapping_device/compare/v0.5.7...v0.6.0)
6
+
7
+ **Implemented enhancements:**
8
+
9
+ - Support Ruby 3.0 [\#71](https://github.com/st0012/tapping_device/pull/71) ([st0012](https://github.com/st0012))
10
+
11
+ **Merged pull requests:**
12
+
13
+ - Drop activerecord requirement [\#73](https://github.com/st0012/tapping_device/pull/73) ([st0012](https://github.com/st0012))
14
+ - Improve file-writing tests [\#72](https://github.com/st0012/tapping_device/pull/72) ([st0012](https://github.com/st0012))
15
+ - Simplify output logic with Ruby' Logger class [\#70](https://github.com/st0012/tapping_device/pull/70) ([st0012](https://github.com/st0012))
16
+ - Refactor Payload classes [\#68](https://github.com/st0012/tapping_device/pull/68) ([st0012](https://github.com/st0012))
17
+
18
+ ## [v0.5.7](https://github.com/st0012/tapping_device/tree/v0.5.7) (2020-09-09)
19
+
20
+ [Full Changelog](https://github.com/st0012/tapping_device/compare/v0.5.6...v0.5.7)
21
+
22
+ **Implemented enhancements:**
23
+
24
+ - Use pastel in output payload [\#62](https://github.com/st0012/tapping_device/issues/62)
25
+
26
+ **Closed issues:**
27
+
28
+ - Support tag option [\#64](https://github.com/st0012/tapping_device/issues/64)
29
+
30
+ **Merged pull requests:**
31
+
32
+ - Use pastel to replace handmade colorizing logic [\#66](https://github.com/st0012/tapping_device/pull/66) ([st0012](https://github.com/st0012))
33
+ - Add tag option [\#65](https://github.com/st0012/tapping_device/pull/65) ([st0012](https://github.com/st0012))
34
+
35
+ ## [v0.5.6](https://github.com/st0012/tapping_device/tree/v0.5.6) (2020-07-17)
36
+
37
+ [Full Changelog](https://github.com/st0012/tapping_device/compare/v0.5.5...v0.5.6)
38
+
39
+ ## [v0.5.5](https://github.com/st0012/tapping_device/tree/v0.5.5) (2020-07-16)
40
+
41
+ [Full Changelog](https://github.com/st0012/tapping_device/compare/v0.5.4...v0.5.5)
42
+
43
+ **Fixed bugs:**
44
+
45
+ - InitializationTracker's logic can cause error [\#60](https://github.com/st0012/tapping_device/issues/60)
46
+
47
+ **Closed issues:**
48
+
49
+ - Refactor get\_method\_from\_object [\#59](https://github.com/st0012/tapping_device/issues/59)
50
+
51
+ **Merged pull requests:**
52
+
53
+ - Fix init tracker [\#61](https://github.com/st0012/tapping_device/pull/61) ([st0012](https://github.com/st0012))
54
+
55
+ ## [v0.5.4](https://github.com/st0012/tapping_device/tree/v0.5.4) (2020-07-05)
56
+
57
+ [Full Changelog](https://github.com/st0012/tapping_device/compare/v0.5.3...v0.5.4)
58
+
59
+ **Closed issues:**
60
+
61
+ - Add with\_print\_calls method [\#52](https://github.com/st0012/tapping_device/issues/52)
62
+ - Tapping any instance of class [\#51](https://github.com/st0012/tapping_device/issues/51)
63
+ - Add ignore\_private option [\#50](https://github.com/st0012/tapping_device/issues/50)
64
+
65
+ **Merged pull requests:**
66
+
67
+ - Restructure README.md [\#58](https://github.com/st0012/tapping_device/pull/58) ([st0012](https://github.com/st0012))
68
+ - Better support on private methods [\#57](https://github.com/st0012/tapping_device/pull/57) ([st0012](https://github.com/st0012))
69
+ - Add with\_\* helpers \(e.g. with\_print\_calls\) [\#56](https://github.com/st0012/tapping_device/pull/56) ([st0012](https://github.com/st0012))
70
+ - Add force\_recording option for debugging [\#55](https://github.com/st0012/tapping_device/pull/55) ([st0012](https://github.com/st0012))
71
+ - Add print\_instance\_\* and write\_instance\_\* helpers [\#54](https://github.com/st0012/tapping_device/pull/54) ([st0012](https://github.com/st0012))
72
+ - Fix tap\_init by adding c\_\* event type [\#53](https://github.com/st0012/tapping_device/pull/53) ([st0012](https://github.com/st0012))
73
+
3
74
  ## [v0.5.3](https://github.com/st0012/tapping_device/tree/v0.5.3) (2020-06-21)
4
75
 
5
76
  [Full Changelog](https://github.com/st0012/tapping_device/compare/v0.5.2...v0.5.3)
data/Gemfile CHANGED
@@ -2,3 +2,20 @@ source "https://rubygems.org"
2
2
 
3
3
  # Specify your gem's dependencies in tapping_device.gemspec
4
4
  gemspec
5
+
6
+ rails_version = ENV["RAILS_VERSION"]
7
+ rails_version = "6.1.0" if rails_version.nil?
8
+
9
+ if rails_version.to_f < 6
10
+ gem "sqlite3", "~> 1.3.0"
11
+ else
12
+ gem "sqlite3"
13
+ end
14
+
15
+ gem "activerecord", "~> #{rails_version}"
16
+
17
+ gem "rake", "~> 13.0"
18
+ gem "rspec", "~> 3.0"
19
+ gem "simplecov", "~> 0.17.1"
20
+ gem "database_cleaner", "~> 2.0.0"
21
+ gem "pry"
data/Makefile ADDED
@@ -0,0 +1,3 @@
1
+ test:
2
+ bundle exec rspec
3
+ WITH_ACTIVE_RECORD=true bundle exec rspec spec/active_record_spec.rb
@@ -1,13 +1,11 @@
1
- require "active_record"
2
- require "active_support/core_ext/module/introspection"
3
- require "pry" # for using Method#source
1
+ require "method_source" # for using Method#source
4
2
 
5
3
  require "tapping_device/version"
6
4
  require "tapping_device/manageable"
7
5
  require "tapping_device/payload"
8
6
  require "tapping_device/output"
9
7
  require "tapping_device/trackable"
10
- require "tapping_device/configurable"
8
+ require "tapping_device/configuration"
11
9
  require "tapping_device/exceptions"
12
10
  require "tapping_device/method_hijacker"
13
11
  require "tapping_device/trackers/initialization_tracker"
@@ -28,7 +26,6 @@ class TappingDevice
28
26
 
29
27
  extend Manageable
30
28
 
31
- include Configurable
32
29
  include Output::Helpers
33
30
 
34
31
  def initialize(options = {}, &block)
@@ -120,8 +117,10 @@ class TappingDevice
120
117
 
121
118
  # this needs to be placed upfront so we can exclude noise before doing more work
122
119
  def should_be_skipped_by_paths?(filepath)
123
- options[:exclude_by_paths].any? { |pattern| pattern.match?(filepath) } ||
124
- (options[:filter_by_paths].present? && !options[:filter_by_paths].any? { |pattern| pattern.match?(filepath) })
120
+ exclude_by_paths = options[:exclude_by_paths]
121
+ filter_by_paths = options[:filter_by_paths]
122
+ exclude_by_paths.any? { |pattern| pattern.match?(filepath) } ||
123
+ (filter_by_paths && !filter_by_paths.empty? && !filter_by_paths.any? { |pattern| pattern.match?(filepath) })
125
124
  end
126
125
 
127
126
  def is_tapping_device_call?(tp)
@@ -137,11 +136,11 @@ class TappingDevice
137
136
  end
138
137
 
139
138
  def with_condition_satisfied?(payload)
140
- @with_condition.blank? || @with_condition.call(payload)
139
+ @with_condition.nil? || @with_condition.call(payload)
141
140
  end
142
141
 
143
142
  def build_payload(tp:, filepath:, line_number:)
144
- Payload.init({
143
+ Payload.new(
145
144
  target: @target,
146
145
  receiver: tp.self,
147
146
  method_name: tp.callee_id,
@@ -152,16 +151,14 @@ class TappingDevice
152
151
  line_number: line_number,
153
152
  defined_class: tp.defined_class,
154
153
  trace: get_traces(tp),
155
- is_private_call?: tp.defined_class.private_method_defined?(tp.callee_id),
154
+ is_private_call: tp.defined_class.private_method_defined?(tp.callee_id),
155
+ tag: options[:tag],
156
156
  tp: tp
157
- })
157
+ )
158
158
  end
159
159
 
160
160
  def get_method_object_from(target, method_name)
161
- target.method(method_name)
162
- rescue ArgumentError
163
- method_method = Object.method(:method).unbind
164
- method_method.bind(target).call(method_name)
161
+ Object.instance_method(:method).bind(target).call(method_name)
165
162
  rescue NameError
166
163
  # if any part of the program uses Refinement to extend its methods
167
164
  # we might still get NoMethodError when trying to get that method outside the scope
@@ -1,10 +1,5 @@
1
- require "active_support/configurable"
2
- require "active_support/concern"
3
-
4
1
  class TappingDevice
5
- module Configurable
6
- extend ActiveSupport::Concern
7
-
2
+ class Configuration
8
3
  DEFAULTS = {
9
4
  filter_by_paths: [],
10
5
  exclude_by_paths: [],
@@ -16,12 +11,24 @@ class TappingDevice
16
11
  only_private: false
17
12
  }.merge(TappingDevice::Output::DEFAULT_OPTIONS)
18
13
 
19
- included do
20
- include ActiveSupport::Configurable
14
+ def initialize
15
+ @options = {}
21
16
 
22
17
  DEFAULTS.each do |key, value|
23
- config[key] = value
18
+ @options[key] = value
24
19
  end
25
20
  end
21
+
22
+ def [](key)
23
+ @options[key]
24
+ end
25
+
26
+ def []=(key, value)
27
+ @options[key] = value
28
+ end
29
+ end
30
+
31
+ def self.config
32
+ @config ||= Configuration.new
26
33
  end
27
34
  end
@@ -20,10 +20,14 @@ class TappingDevice
20
20
 
21
21
  def is_writer_method?(method_name)
22
22
  has_definition_source?(method_name) && method_name.match?(/\w+=/) && target.method(method_name).source.match?(/attr_writer|attr_accessor/)
23
+ rescue MethodSource::SourceNotFoundError
24
+ false
23
25
  end
24
26
 
25
27
  def is_reader_method?(method_name)
26
28
  has_definition_source?(method_name) && target.method(method_name).source.match?(/attr_reader|attr_accessor/)
29
+ rescue MethodSource::SourceNotFoundError
30
+ false
27
31
  end
28
32
 
29
33
  def has_definition_source?(method_name)
@@ -1,7 +1,6 @@
1
- require "tapping_device/output/payload"
1
+ require "logger"
2
+ require "tapping_device/output/payload_wrapper"
2
3
  require "tapping_device/output/writer"
3
- require "tapping_device/output/stdout_writer"
4
- require "tapping_device/output/file_writer"
5
4
 
6
5
  class TappingDevice
7
6
  module Output
@@ -13,16 +12,16 @@ class TappingDevice
13
12
 
14
13
  module Helpers
15
14
  def and_write(payload_method = nil, options: {}, &block)
16
- and_output(payload_method, options: options, writer_klass: FileWriter, &block)
15
+ and_output(payload_method, options: options, logger: Logger.new(options[:log_file]), &block)
17
16
  end
18
17
 
19
18
  def and_print(payload_method = nil, options: {}, &block)
20
- and_output(payload_method, options: options, writer_klass: StdoutWriter, &block)
19
+ and_output(payload_method, options: options, logger: Logger.new($stdout), &block)
21
20
  end
22
21
 
23
- def and_output(payload_method = nil, options: {}, writer_klass:, &block)
22
+ def and_output(payload_method = nil, options: {}, logger:, &block)
24
23
  output_block = generate_output_block(payload_method, block)
25
- @output_writer = writer_klass.new(options, output_block)
24
+ @output_writer = Writer.new(options: options, output_block: output_block, logger: logger)
26
25
  self
27
26
  end
28
27
 
@@ -1,17 +1,44 @@
1
+ require "pastel"
2
+
1
3
  class TappingDevice
2
4
  module Output
3
- class Payload < Payload
5
+ class PayloadWrapper
4
6
  UNDEFINED = "[undefined]"
7
+ PRIVATE_MARK = " (private)"
8
+
9
+ PASTEL = Pastel.new
10
+ PASTEL.alias_color(:orange, :bright_red, :bright_yellow)
11
+
12
+ TappingDevice::Payload::ATTRS.each do |attr|
13
+ define_method attr do |options = {}|
14
+ @payload.send(attr)
15
+ end
16
+ end
17
+
18
+ alias :is_private_call? :is_private_call
19
+
20
+ def method_head
21
+ @payload.method_head
22
+ end
23
+
24
+ def location(options = {})
25
+ @payload.location(options)
26
+ end
5
27
 
6
28
  alias :raw_arguments :arguments
7
29
  alias :raw_return_value :return_value
8
30
 
31
+ def initialize(payload)
32
+ @payload = payload
33
+ end
34
+
9
35
  def method_name(options = {})
10
- if is_private_call?
11
- ":#{super(options)} (private)"
12
- else
13
- ":#{super(options)}"
14
- end
36
+ name = ":#{@payload.method_name}"
37
+
38
+ name += " [#{tag}]" if tag
39
+ name += PRIVATE_MARK if is_private_call?
40
+
41
+ name
15
42
  end
16
43
 
17
44
  def arguments(options = {})
@@ -22,29 +49,13 @@ class TappingDevice
22
49
  generate_string_result(raw_return_value, options[:inspect])
23
50
  end
24
51
 
25
- COLOR_CODES = {
26
- green: 10,
27
- yellow: 11,
28
- blue: 12,
29
- megenta: 13,
30
- cyan: 14,
31
- orange: 214
32
- }
33
-
34
- COLORS = COLOR_CODES.each_with_object({}) do |(name, code), hash|
35
- hash[name] = "\u001b[38;5;#{code}m"
36
- end.merge(
37
- reset: "\u001b[0m",
38
- nocolor: ""
39
- )
40
-
41
52
  PAYLOAD_ATTRIBUTES = {
42
- method_name: {symbol: "", color: COLORS[:blue]},
43
- location: {symbol: "from:", color: COLORS[:green]},
44
- return_value: {symbol: "=>", color: COLORS[:megenta]},
45
- arguments: {symbol: "<=", color: COLORS[:orange]},
46
- ivar_changes: {symbol: "changes:\n", color: COLORS[:blue]},
47
- defined_class: {symbol: "#", color: COLORS[:yellow]}
53
+ method_name: {symbol: "", color: :bright_blue},
54
+ location: {symbol: "from:", color: :green},
55
+ return_value: {symbol: "=>", color: :magenta},
56
+ arguments: {symbol: "<=", color: :orange},
57
+ ivar_changes: {symbol: "changes:\n", color: :blue},
58
+ defined_class: {symbol: "#", color: :yellow}
48
59
  }
49
60
 
50
61
  PAYLOAD_ATTRIBUTES.each do |attribute, attribute_options|
@@ -54,10 +65,10 @@ class TappingDevice
54
65
 
55
66
  # regenerate attributes with `colorize: true` support
56
67
  define_method attribute do |options = {}|
57
- call_result = send("original_#{attribute}", options)
68
+ call_result = send("original_#{attribute}", options).to_s
58
69
 
59
70
  if options[:colorize]
60
- "#{color}#{call_result}#{COLORS[:reset]}"
71
+ PASTEL.send(color, call_result)
61
72
  else
62
73
  call_result
63
74
  end
@@ -87,7 +98,7 @@ class TappingDevice
87
98
  return unless arg_name
88
99
 
89
100
  arg_name = ":#{arg_name}"
90
- arg_name = value_with_color(arg_name, :orange) if options[:colorize]
101
+ arg_name = PASTEL.orange(arg_name) if options[:colorize]
91
102
  msg = "Passed as #{arg_name} in '#{defined_class(options)}##{method_name(options)}' at #{location(options)}\n"
92
103
  msg += " > #{method_head}\n" if with_method_head
93
104
  msg
@@ -104,17 +115,17 @@ class TappingDevice
104
115
  end
105
116
 
106
117
  def ivar_changes(options = {})
107
- super.map do |ivar, value_changes|
118
+ @payload.ivar_changes.map do |ivar, value_changes|
108
119
  before = generate_string_result(value_changes[:before], options[:inspect])
109
120
  after = generate_string_result(value_changes[:after], options[:inspect])
110
121
 
111
122
  if options[:colorize]
112
- ivar = "#{COLORS[:orange]}#{ivar}#{COLORS[:reset]}"
113
- before = "#{COLORS[:blue]}#{before.to_s}#{COLORS[:reset]}"
114
- after = "#{COLORS[:blue]}#{after.to_s}#{COLORS[:reset]}"
123
+ ivar = PASTEL.orange(ivar.to_s)
124
+ before = PASTEL.bright_blue(before.to_s)
125
+ after = PASTEL.bright_blue(after.to_s)
115
126
  end
116
127
 
117
- " #{ivar}: #{before.to_s} => #{after.to_s}"
128
+ " #{ivar}: #{before} => #{after}"
118
129
  end.join("\n")
119
130
  end
120
131
 
@@ -130,10 +141,6 @@ class TappingDevice
130
141
 
131
142
  private
132
143
 
133
- def value_with_color(value, color)
134
- "#{COLORS[color]}#{value}#{COLORS[:reset]}"
135
- end
136
-
137
144
  def generate_string_result(obj, inspect)
138
145
  case obj
139
146
  when Array
@@ -1,19 +1,21 @@
1
1
  class TappingDevice
2
2
  module Output
3
3
  class Writer
4
- def initialize(options, output_block)
4
+ def initialize(options:, output_block:, logger:)
5
5
  @options = options
6
6
  @output_block = output_block
7
+ @logger = logger
7
8
  end
8
9
 
9
10
  def write!(payload)
10
- raise NotImplementedError
11
+ output = generate_output(payload)
12
+ @logger << output
11
13
  end
12
14
 
13
15
  private
14
16
 
15
17
  def generate_output(payload)
16
- @output_block.call(Output::Payload.init(payload), @options)
18
+ @output_block.call(PayloadWrapper.new(payload), @options)
17
19
  end
18
20
  end
19
21
  end
@@ -1,22 +1,32 @@
1
1
  class TappingDevice
2
- class Payload < Hash
2
+ class Payload
3
3
  ATTRS = [
4
4
  :target, :receiver, :method_name, :method_object, :arguments, :return_value, :filepath, :line_number,
5
- :defined_class, :trace, :tp, :ivar_changes, :is_private_call?
5
+ :defined_class, :trace, :tag, :tp, :ivar_changes, :is_private_call
6
6
  ]
7
7
 
8
- ATTRS.each do |attr|
9
- define_method attr do |options = {}|
10
- self[attr]
11
- end
12
- end
8
+ attr_accessor(*ATTRS)
9
+
10
+ alias :is_private_call? :is_private_call
13
11
 
14
- def self.init(hash)
15
- h = new
16
- hash.each do |k, v|
17
- h[k] = v
18
- end
19
- h
12
+ def initialize(
13
+ target:, receiver:, method_name:, method_object:, arguments:, return_value:, filepath:, line_number:,
14
+ defined_class:, trace:, tag:, tp:, is_private_call:
15
+ )
16
+ @target = target
17
+ @receiver = receiver
18
+ @method_name = method_name
19
+ @method_object = method_object
20
+ @arguments = arguments
21
+ @return_value = return_value
22
+ @filepath = filepath
23
+ @line_number = line_number
24
+ @defined_class = defined_class
25
+ @trace = trace
26
+ @tag = tag
27
+ @tp = tp
28
+ @ivar_changes = {}
29
+ @is_private_call = is_private_call
20
30
  end
21
31
 
22
32
  def method_head
@@ -36,7 +36,7 @@ class TappingDevice
36
36
  define_method "#{output_action}_instance_#{subject}" do |target_klass, options = {}|
37
37
  collection_proxy = AsyncCollectionProxy.new
38
38
 
39
- tap_init!(target_klass, options) do |payload|
39
+ tap_init!(target_klass, options.merge(force_recording: true)) do |payload|
40
40
  collection_proxy << send(helper_method_name, payload.return_value, options)
41
41
  end
42
42
 
@@ -9,10 +9,19 @@ class TappingDevice
9
9
  @options[:event_type] = [event_type, "c_#{event_type}"]
10
10
  end
11
11
 
12
+ def track(object)
13
+ super
14
+ @is_active_record_model = defined?(ActiveRecord) && target.ancestors.include?(ActiveRecord::Base)
15
+ self
16
+ end
17
+
12
18
  def build_payload(tp:, filepath:, line_number:)
13
19
  payload = super
14
- payload[:return_value] = payload[:receiver]
15
- payload[:receiver] = target
20
+
21
+ return payload if @is_active_record_model
22
+
23
+ payload.return_value = payload.receiver
24
+ payload.receiver = target
16
25
  payload
17
26
  end
18
27
 
@@ -24,8 +33,17 @@ class TappingDevice
24
33
  receiver = tp.self
25
34
  method_name = tp.callee_id
26
35
 
27
- if target.ancestors.include?(ActiveRecord::Base)
28
- method_name == :new && receiver.ancestors.include?(target)
36
+ if @is_active_record_model
37
+ # ActiveRecord redefines model classes' .new method,
38
+ # so instead of calling Model#initialize, it'll actually call Model.new
39
+ # see https://github.com/rails/rails/blob/master/activerecord/lib/active_record/inheritance.rb#L50
40
+ method_name == :new &&
41
+ receiver.is_a?(Class) &&
42
+ # this checks if the model class is the target class or a subclass of it
43
+ receiver.ancestors.include?(target) &&
44
+ # Model.new triggers both c_return and return events. so we should only return in 1 type of the events
45
+ # otherwise the callback will be triggered twice
46
+ tp.event == :return
29
47
  else
30
48
  method_name == :initialize && receiver.is_a?(target)
31
49
  end
@@ -47,7 +47,7 @@ class TappingDevice
47
47
  payload = super
48
48
 
49
49
  if change_capturing_event?(tp)
50
- payload[:ivar_changes] = capture_ivar_changes
50
+ payload.ivar_changes = capture_ivar_changes
51
51
  end
52
52
 
53
53
  payload
@@ -55,15 +55,14 @@ class TappingDevice
55
55
 
56
56
  def capture_ivar_changes
57
57
  changes = {}
58
-
59
58
  additional_keys = @latest_instance_variables.keys - @instance_variables_snapshot.keys
60
59
  additional_keys.each do |key|
61
- changes[key] = {before: Output::Payload::UNDEFINED, after: @latest_instance_variables[key]}
60
+ changes[key] = {before: Output::PayloadWrapper::UNDEFINED, after: @latest_instance_variables[key]}
62
61
  end
63
62
 
64
63
  removed_keys = @instance_variables_snapshot.keys - @latest_instance_variables.keys
65
64
  removed_keys.each do |key|
66
- changes[key] = {before: @instance_variables_snapshot[key], after: Output::Payload::UNDEFINED}
65
+ changes[key] = {before: @instance_variables_snapshot[key], after: Output::PayloadWrapper::UNDEFINED}
67
66
  end
68
67
 
69
68
  remained_keys = @latest_instance_variables.keys - additional_keys
@@ -1,3 +1,3 @@
1
1
  class TappingDevice
2
- VERSION = "0.5.4"
2
+ VERSION = "0.6.1"
3
3
  end
@@ -15,30 +15,15 @@ Gem::Specification.new do |spec|
15
15
 
16
16
  spec.metadata["homepage_uri"] = spec.homepage
17
17
  spec.metadata["source_code_uri"] = "https://github.com/st0012/tapping_device"
18
- spec.metadata["changelog_uri"] = "https://github.com/st0012/tapping_device/releases"
18
+ spec.metadata["changelog_uri"] = "https://github.com/st0012/tapping_device/CHANGELOG.md"
19
19
 
20
20
  # Specify which files should be added to the gem when it is released.
21
21
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
22
22
  spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
23
23
  `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
24
24
  end
25
- spec.bindir = "exe"
26
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
27
25
  spec.require_paths = ["lib"]
28
26
 
29
- if ENV["RAILS_VERSION"]
30
- spec.add_dependency "activerecord", "~> #{ENV["RAILS_VERSION"]}"
31
- else
32
- spec.add_dependency "activerecord", ">= 5.2"
33
- end
34
-
35
- spec.add_dependency "pry" # for using Method#source in MutationTracker
36
- spec.add_dependency "activesupport"
37
-
38
- spec.add_development_dependency "sqlite3", ">= 1.3.6"
39
- spec.add_development_dependency "database_cleaner"
40
- spec.add_development_dependency "bundler", "~> 2.0"
41
- spec.add_development_dependency "rake", "~> 13.0"
42
- spec.add_development_dependency "rspec", "~> 3.0"
43
- spec.add_development_dependency "simplecov", "0.17.1"
27
+ spec.add_dependency "method_source", "~> 1.0.0"
28
+ spec.add_dependency "pastel", "~> 0.7"
44
29
  end
metadata CHANGED
@@ -1,141 +1,43 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tapping_device
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.4
4
+ version: 0.6.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - st0012
8
8
  autorequire:
9
- bindir: exe
9
+ bindir: bin
10
10
  cert_chain: []
11
- date: 2020-07-05 00:00:00.000000000 Z
11
+ date: 2021-05-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: activerecord
14
+ name: method_source
15
15
  requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - ">="
18
- - !ruby/object:Gem::Version
19
- version: '5.2'
20
- type: :runtime
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - ">="
25
- - !ruby/object:Gem::Version
26
- version: '5.2'
27
- - !ruby/object:Gem::Dependency
28
- name: pry
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ">="
32
- - !ruby/object:Gem::Version
33
- version: '0'
34
- type: :runtime
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - ">="
39
- - !ruby/object:Gem::Version
40
- version: '0'
41
- - !ruby/object:Gem::Dependency
42
- name: activesupport
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - ">="
46
- - !ruby/object:Gem::Version
47
- version: '0'
48
- type: :runtime
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - ">="
53
- - !ruby/object:Gem::Version
54
- version: '0'
55
- - !ruby/object:Gem::Dependency
56
- name: sqlite3
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - ">="
60
- - !ruby/object:Gem::Version
61
- version: 1.3.6
62
- type: :development
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - ">="
67
- - !ruby/object:Gem::Version
68
- version: 1.3.6
69
- - !ruby/object:Gem::Dependency
70
- name: database_cleaner
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - ">="
74
- - !ruby/object:Gem::Version
75
- version: '0'
76
- type: :development
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - ">="
81
- - !ruby/object:Gem::Version
82
- version: '0'
83
- - !ruby/object:Gem::Dependency
84
- name: bundler
85
- requirement: !ruby/object:Gem::Requirement
86
- requirements:
87
- - - "~>"
88
- - !ruby/object:Gem::Version
89
- version: '2.0'
90
- type: :development
91
- prerelease: false
92
- version_requirements: !ruby/object:Gem::Requirement
93
16
  requirements:
94
17
  - - "~>"
95
18
  - !ruby/object:Gem::Version
96
- version: '2.0'
97
- - !ruby/object:Gem::Dependency
98
- name: rake
99
- requirement: !ruby/object:Gem::Requirement
100
- requirements:
101
- - - "~>"
102
- - !ruby/object:Gem::Version
103
- version: '13.0'
104
- type: :development
19
+ version: 1.0.0
20
+ type: :runtime
105
21
  prerelease: false
106
22
  version_requirements: !ruby/object:Gem::Requirement
107
23
  requirements:
108
24
  - - "~>"
109
25
  - !ruby/object:Gem::Version
110
- version: '13.0'
26
+ version: 1.0.0
111
27
  - !ruby/object:Gem::Dependency
112
- name: rspec
28
+ name: pastel
113
29
  requirement: !ruby/object:Gem::Requirement
114
30
  requirements:
115
31
  - - "~>"
116
32
  - !ruby/object:Gem::Version
117
- version: '3.0'
118
- type: :development
33
+ version: '0.7'
34
+ type: :runtime
119
35
  prerelease: false
120
36
  version_requirements: !ruby/object:Gem::Requirement
121
37
  requirements:
122
38
  - - "~>"
123
39
  - !ruby/object:Gem::Version
124
- version: '3.0'
125
- - !ruby/object:Gem::Dependency
126
- name: simplecov
127
- requirement: !ruby/object:Gem::Requirement
128
- requirements:
129
- - - '='
130
- - !ruby/object:Gem::Version
131
- version: 0.17.1
132
- type: :development
133
- prerelease: false
134
- version_requirements: !ruby/object:Gem::Requirement
135
- requirements:
136
- - - '='
137
- - !ruby/object:Gem::Version
138
- version: 0.17.1
40
+ version: '0.7'
139
41
  description: tapping_device lets you understand what your Ruby objects do without
140
42
  digging into the code
141
43
  email:
@@ -154,8 +56,8 @@ files:
154
56
  - CHANGELOG.md
155
57
  - CODE_OF_CONDUCT.md
156
58
  - Gemfile
157
- - Gemfile.lock
158
59
  - LICENSE.txt
60
+ - Makefile
159
61
  - README.md
160
62
  - Rakefile
161
63
  - bin/console
@@ -165,14 +67,12 @@ files:
165
67
  - images/print_mutations.png
166
68
  - images/print_traces.png
167
69
  - lib/tapping_device.rb
168
- - lib/tapping_device/configurable.rb
70
+ - lib/tapping_device/configuration.rb
169
71
  - lib/tapping_device/exceptions.rb
170
72
  - lib/tapping_device/manageable.rb
171
73
  - lib/tapping_device/method_hijacker.rb
172
74
  - lib/tapping_device/output.rb
173
- - lib/tapping_device/output/file_writer.rb
174
- - lib/tapping_device/output/payload.rb
175
- - lib/tapping_device/output/stdout_writer.rb
75
+ - lib/tapping_device/output/payload_wrapper.rb
176
76
  - lib/tapping_device/output/writer.rb
177
77
  - lib/tapping_device/payload.rb
178
78
  - lib/tapping_device/trackable.rb
@@ -189,7 +89,7 @@ licenses:
189
89
  metadata:
190
90
  homepage_uri: https://github.com/st0012/tapping_device
191
91
  source_code_uri: https://github.com/st0012/tapping_device
192
- changelog_uri: https://github.com/st0012/tapping_device/releases
92
+ changelog_uri: https://github.com/st0012/tapping_device/CHANGELOG.md
193
93
  post_install_message:
194
94
  rdoc_options: []
195
95
  require_paths:
@@ -205,7 +105,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
205
105
  - !ruby/object:Gem::Version
206
106
  version: '0'
207
107
  requirements: []
208
- rubygems_version: 3.0.3
108
+ rubygems_version: 3.2.15
209
109
  signing_key:
210
110
  specification_version: 4
211
111
  summary: tapping_device lets you understand what your Ruby objects do without digging
data/Gemfile.lock DELETED
@@ -1,74 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- tapping_device (0.5.4)
5
- activerecord (>= 5.2)
6
- activesupport
7
- pry
8
-
9
- GEM
10
- remote: https://rubygems.org/
11
- specs:
12
- activemodel (6.0.3.2)
13
- activesupport (= 6.0.3.2)
14
- activerecord (6.0.3.2)
15
- activemodel (= 6.0.3.2)
16
- activesupport (= 6.0.3.2)
17
- activesupport (6.0.3.2)
18
- concurrent-ruby (~> 1.0, >= 1.0.2)
19
- i18n (>= 0.7, < 2)
20
- minitest (~> 5.1)
21
- tzinfo (~> 1.1)
22
- zeitwerk (~> 2.2, >= 2.2.2)
23
- coderay (1.1.3)
24
- concurrent-ruby (1.1.6)
25
- database_cleaner (1.7.0)
26
- diff-lcs (1.3)
27
- docile (1.3.2)
28
- i18n (1.8.3)
29
- concurrent-ruby (~> 1.0)
30
- json (2.3.0)
31
- method_source (1.0.0)
32
- minitest (5.14.1)
33
- pry (0.13.1)
34
- coderay (~> 1.1)
35
- method_source (~> 1.0)
36
- rake (13.0.1)
37
- rspec (3.8.0)
38
- rspec-core (~> 3.8.0)
39
- rspec-expectations (~> 3.8.0)
40
- rspec-mocks (~> 3.8.0)
41
- rspec-core (3.8.2)
42
- rspec-support (~> 3.8.0)
43
- rspec-expectations (3.8.4)
44
- diff-lcs (>= 1.2.0, < 2.0)
45
- rspec-support (~> 3.8.0)
46
- rspec-mocks (3.8.1)
47
- diff-lcs (>= 1.2.0, < 2.0)
48
- rspec-support (~> 3.8.0)
49
- rspec-support (3.8.2)
50
- simplecov (0.17.1)
51
- docile (~> 1.1)
52
- json (>= 1.8, < 3)
53
- simplecov-html (~> 0.10.0)
54
- simplecov-html (0.10.2)
55
- sqlite3 (1.4.1)
56
- thread_safe (0.3.6)
57
- tzinfo (1.2.7)
58
- thread_safe (~> 0.1)
59
- zeitwerk (2.3.1)
60
-
61
- PLATFORMS
62
- ruby
63
-
64
- DEPENDENCIES
65
- bundler (~> 2.0)
66
- database_cleaner
67
- rake (~> 13.0)
68
- rspec (~> 3.0)
69
- simplecov (= 0.17.1)
70
- sqlite3 (>= 1.3.6)
71
- tapping_device!
72
-
73
- BUNDLED WITH
74
- 2.1.1
@@ -1,21 +0,0 @@
1
- class TappingDevice
2
- module Output
3
- class FileWriter < Writer
4
- def initialize(options, output_block)
5
- @path = options[:log_file]
6
-
7
- File.write(@path, "") # clean file
8
-
9
- super
10
- end
11
-
12
- def write!(payload)
13
- output = generate_output(payload)
14
-
15
- File.open(@path, "a") do |f|
16
- f << output
17
- end
18
- end
19
- end
20
- end
21
- end
@@ -1,9 +0,0 @@
1
- class TappingDevice
2
- module Output
3
- class StdoutWriter < Writer
4
- def write!(payload)
5
- puts(generate_output(payload))
6
- end
7
- end
8
- end
9
- end