-
Notifications
You must be signed in to change notification settings - Fork 13
Low level Helpers
tap_init!(class)
- tracks a class’ instance initialization
calls = []
tap_init!(Student) do |payload|
calls << [payload[:method_name], payload[:arguments]]
end
Student.new("Stan", 18)
Student.new("Jane", 23)
puts(calls.to_s) #=> [[:initialize, {:name=>"Stan", :age=>18}], [:initialize, {:name=>"Jane", :age=>23}]]
tap_on!(object)
- tracks any calls received by the object.
class PostsController < ApplicationController
before_action :set_post, only: [:show, :edit, :update, :destroy]
def show
tap_on!(@post).and_print(:method_name_and_location)
end
end
And you can see these in log:
name FROM /PROJECT_PATH/sample/app/views/posts/show.html.erb:5
user_id FROM /PROJECT_PATH/sample/app/views/posts/show.html.erb:10
to_param FROM /RUBY_PATH/gems/2.6.0/gems/actionpack-5.2.0/lib/action_dispatch/routing/route_set.rb:236
Also check the track_as_records
option if you want to track ActiveRecord
records.
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.
class PostsController < ApplicationController
# GET /posts/new
def new
@post = Post.new
tap_passed!(@post) do |payload|
puts(payload.passed_at(with_method_head: true))
end
end
end
Passed as 'record' in method ':polymorphic_mapping'
> def polymorphic_mapping(record)
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
Passed as 'klass' in method ':get_method_for_class'
> def get_method_for_class(klass)
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
Passed as 'record' in method ':handle_model'
> def handle_model(record)
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
Passed as 'record_or_hash_or_array' in method ':polymorphic_method'
> def self.polymorphic_method(recipient, record_or_hash_or_array, action, type, options)
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
tap_assoc!(activerecord_object)
tracks association calls on a record, like post.comments
tap_assoc!(order).and_print(:method_name_and_location)
payments FROM /RUBY_PATH/gems/2.6.0/gems/jsonapi-resources-0.9.10/lib/jsonapi/resource.rb:124
line_items FROM /MY_PROJECT/app/models/line_item_container_helpers.rb:44
effective_line_items FROM /MY_PROJECT/app/models/line_item_container_helpers.rb:110
amending_orders FROM /MY_PROJECT/app/models/order.rb:385
amends_order FROM /MY_PROJECT/app/models/order.rb:432
tap_sql!(anything_that_generates_sql_queries)
tracks sql queries generated from the target
class PostsController < ApplicationController
def index
# simulate current_user
@current_user = User.last
# reusable ActiveRecord::Relation
@posts = Post.all
tap_sql!(@posts) do |payload|
puts("Method: #{payload[:method_name]} generated sql: #{payload[:sql]} from #{payload[:filepath]}:#{payload[:line_number]}")
end
end
end
<h1>Posts (<%= @posts.count %>)</h1>
......
<% @posts.each do |post| %>
......
<% end %>
......
<p>Posts created by you: <%= @posts.where(user: @current_user).count %></p>
Method: count generated sql: SELECT COUNT(*) FROM "posts" from /PROJECT_PATH/rails-6-sample/app/views/posts/index.html.erb:3
Method: each generated sql: SELECT "posts".* FROM "posts" from /PROJECT_PATH/rails-6-sample/app/views/posts/index.html.erb:16
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
It takes an integer as the number of traces we want to put into trace
. Default is nil
, so trace
would be empty.
stan = Student.new("Stan", 18)
tap_on!(stan, with_trace_to: 5)
stan.name
puts(device.calls.first.trace) #=>
/Users/st0012/projects/tapping_device/spec/tapping_device_spec.rb:287:in `block (4 levels) in <top (required)>'
/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'
/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'
/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'
/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'
/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'
It makes the device to track objects as they are ActiveRecord instances. For example:
tap_on!(@post, track_as_records: true)
post = Post.find(@post.id) # same record but a different object
post.title #=> this call will be recorded as well
It takes an array of call path patterns that we want to skip. This could be very helpful when working on a large project like Rails applications.
tap_on!(@post, exclude_by_paths: [/active_record/]).and_print(:method_name_and_location)
_read_attribute FROM /RUBY_PATH/gems/2.6.0/gems/activerecord-5.2.0/lib/active_record/attribute_methods/read.rb:40
name FROM /PROJECT_PATH/sample/app/views/posts/show.html.erb:5
_read_attribute FROM /RUBY_PATH/gems/2.6.0/gems/activerecord-5.2.0/lib/active_record/attribute_methods/read.rb:40
user_id FROM /PROJECT_PATH/sample/app/views/posts/show.html.erb:10
.......
# versus
name FROM /PROJECT_PATH/sample/app/views/posts/show.html.erb:5
user_id FROM /PROJECT_PATH/sample/app/views/posts/show.html.erb:10
to_param FROM /RUBY_PATH/gems/2.6.0/gems/actionpack-5.2.0/lib/action_dispatch/routing/route_set.rb:236
Like exclude_by_paths
, but work in the opposite way.
All tapping methods (start with tap_
) takes a block and yield a Payload
object as a block argument. It responds to
-
target
- the target fortap_x
call -
receiver
- the receiver object -
method_name
- method’s name (symbol)- e.g.
:name
- e.g.
-
method_object
- the method object that's being called. It might benil
in some edge cases. -
arguments
- arguments of the method call- e.g.
{name: “Stan”, age: 25}
- e.g.
-
return_value
- return value of the method call -
filepath
- path to the file that performs the method call line_number
-
defined_class
- in which class that defines the method being called -
trace
- stack trace of the call. Default is an empty array unlesswith_trace_to
option is set -
sql
- sql that generated from the call (only present intap_sql!
payloads) -
tp
- trace point object of this call