Skip to content
This repository has been archived by the owner on May 4, 2024. It is now read-only.

Commit

Permalink
Merge pull request #4 from st0012/require-stop-condition
Browse files Browse the repository at this point in the history
Support `TappingDevice::Device#stop_when`
  • Loading branch information
st0012 committed Nov 1, 2019
2 parents 2988722 + 1092ca6 commit 7c3379d
Show file tree
Hide file tree
Showing 4 changed files with 134 additions and 0 deletions.
34 changes: 34 additions & 0 deletions lib/tapping_device/device.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
require "active_record"
require "tapping_device/exceptions"

module TappingDevice
class Device
CALLER_START_POINT = 2
FORCE_STOP_WHEN_MESSAGE = "You must set stop_when condition before start tapping"

attr_reader :options, :calls

Expand All @@ -26,6 +28,21 @@ def tap_assoc!(record)
track(record, condition: :tap_associations?, block: @block, **@options)
end

def tap_init(klass)
validate_tapping(__method__)
tap_init!(klass)
end

def tap_on(object)
validate_tapping(__method__)
tap_on!(object)
end

def tap_assoc(record)
validate_tapping(__method__)
tap_assoc!(record)
end

def set_block(&block)
@block = block
end
Expand All @@ -34,9 +51,24 @@ def stop!
@trace_point.disable if @trace_point
end

def stop_when(&block)
@stop_when = block
end

private

def validate_tapping(method_name)
unless @stop_when
raise TappingDevice::Exception.new <<~ERROR
You must set stop_when condition before calling #{method_name}. Or you can use #{method_name}! to force tapping.
Tapping without stop condition can largely slow down or even halt your application, because it'll need to
screen literally every call happened.
ERROR
end
end

def track(object, condition:, block:, with_trace_to: nil, exclude_by_paths: [], filter_by_paths: nil)

@trace_point = TracePoint.trace(:return) do |tp|
filepath, line_number = caller(CALLER_START_POINT).first.split(":")[0..1]

Expand Down Expand Up @@ -70,6 +102,8 @@ def track(object, condition:, block:, with_trace_to: nil, exclude_by_paths: [],
@calls << yield_parameters
end
end

stop! if @stop_when&.call(yield_parameters)
end

self
Expand Down
4 changes: 4 additions & 0 deletions lib/tapping_device/exceptions.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module TappingDevice
class Exception < StandardError
end
end
51 changes: 51 additions & 0 deletions spec/performance_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
require "spec_helper"
require 'benchmark'

RSpec.describe TappingDevice::Device do
let(:devices) do
devices = []
100.times { devices << TappingDevice::Device.new }
devices
end

after do
devices.each(&:stop!)
end

context "without stop_when" do
it "takes long time when tapping multiple devices" do
time = Benchmark.realtime do
devices.each do |device|
s = Student.new("foo", 10)
device.tap_on!(s)
10.times { s.name }

expect(device.calls.count).to eq(10)
end
end
devices.each { |d| d.stop! }
expect(time).to be_between(4, 10)
end
end

context "with stop_when" do
it "takes very short time when tapping multiple devices" do
time = Benchmark.realtime do
devices.each do |device|
device.stop_when do
device.calls.count == 10
end

s = Student.new("foo", 10)
device.tap_on!(s)
10.times { s.name }

expect(device.calls.count).to eq(10)
end
end

devices.each { |d| d.stop! }
expect(time).to be_between(0, 1)
end
end
end
45 changes: 45 additions & 0 deletions spec/tapping_device_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -200,4 +200,49 @@
expect(count).to eq(1)
end
end

describe "#tap_init" do
let(:device) { TappingDevice::Device.new }
let(:stan) { Student.new("stan", 25) }

it "raises error if device has no stop_when set" do
expect { device.tap_init(Student) }.to raise_error(TappingDevice::Exception)
end
end

describe "#tap_on" do
let(:device) { TappingDevice::Device.new }
let(:stan) { Student.new("stan", 25) }

it "raises error if device has no stop_when set" do
expect { device.tap_on(stan) }.to raise_error(TappingDevice::Exception)
end
end

describe "#tap_assoc" do
let(:device) { TappingDevice::Device.new }
let(:post) { Post.new }

it "raises error if device has no stop_when set" do
expect { device.tap_assoc(post) }.to raise_error(TappingDevice::Exception)
end
end

describe "#stop_when" do
it "stops tapping once fulfill stop_when condition" do
device = TappingDevice::Device.new
device.stop_when do |payload|
device.calls.count == 10
end

s = Student.new("foo#", 10)
device.tap_on!(s)

100.times do
s.name
end

expect(device.calls.count).to eq(10)
end
end
end

0 comments on commit 7c3379d

Please sign in to comment.