turbo_test_constant_tracer 0.1.11
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 +7 -0
- data/.github/workflows/tests.yml +48 -0
- data/.gitignore +69 -0
- data/.rubocop.yml +67 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +8 -0
- data/Gemfile.common +3 -0
- data/Gemfile.lock +63 -0
- data/GemfileCI +9 -0
- data/GemfileCI.lock +78 -0
- data/LICENSE.txt +21 -0
- data/Makefile +44 -0
- data/README.md +26 -0
- data/Rakefile +24 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/ext/hash_lookup_with_proxy_ext/extconf.h +3 -0
- data/ext/hash_lookup_with_proxy_ext/extconf.rb +5 -0
- data/ext/hash_lookup_with_proxy_ext/hash_lookup_with_proxy_ext.c +34 -0
- data/lib/turbo_test_constant_tracer.rb +18 -0
- data/lib/turbo_test_constant_tracer/constructor.rb +59 -0
- data/lib/turbo_test_constant_tracer/definition.rb +96 -0
- data/lib/turbo_test_constant_tracer/definition/templates.rb +106 -0
- data/lib/turbo_test_constant_tracer/delegate_class.rb +72 -0
- data/lib/turbo_test_constant_tracer/delegator.rb +59 -0
- data/lib/turbo_test_constant_tracer/event_publisher.rb +54 -0
- data/lib/turbo_test_constant_tracer/hash_lookup_with_proxy.rb +42 -0
- data/lib/turbo_test_constant_tracer/klass.rb +75 -0
- data/lib/turbo_test_constant_tracer/proxy_klass.rb +11 -0
- data/lib/turbo_test_constant_tracer/regexp/tilde.rb +23 -0
- data/lib/turbo_test_constant_tracer/version.rb +5 -0
- data/turbo_test_constant_tracer.gemspec +34 -0
- metadata +147 -0
data/Makefile
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
# Adapted from https://github.com/zspencer/make-many-rubies/blob/master/Makefile
|
2
|
+
|
3
|
+
# Allows running (and re-running) of tests against several ruby versions,
|
4
|
+
# assuming you use rbenv instead of rvm.
|
5
|
+
|
6
|
+
# Uses pattern rules (task-$:) and automatic variables ($*).
|
7
|
+
# Pattern rules: http://ftp.gnu.org/old-gnu/Manuals/make-3.79.1/html_chapter/make_10.html#SEC98
|
8
|
+
# Automatic variables: http://ftp.gnu.org/old-gnu/Manuals/make-3.79.1/html_chapter/make_10.html#SEC101
|
9
|
+
|
10
|
+
# Rbenv-friendly version identifiers for supported Rubys
|
11
|
+
24_version = 2.4.10
|
12
|
+
25_version = 2.5.8
|
13
|
+
26_version = 2.6.6
|
14
|
+
27_version = 2.7.1
|
15
|
+
|
16
|
+
# The ruby version for use in a given rule.
|
17
|
+
# Requires a matched pattern rule and a supported ruby version.
|
18
|
+
#
|
19
|
+
# Given a pattern rule defined as "install-ruby-%"
|
20
|
+
# When the rule is ran as "install-ruby-193"
|
21
|
+
# Then the inner addsuffix call evaluates to "193_version"
|
22
|
+
# And given_ruby_version becomes "1.9.3-p551"
|
23
|
+
given_ruby_version = $($(addsuffix _version, $*))
|
24
|
+
|
25
|
+
# Instruct rbenv on which Ruby version to use when running a command.
|
26
|
+
# Requires a pattern rule and a supported ruby version.
|
27
|
+
#
|
28
|
+
# Given a pattern rule defined as "test-%"
|
29
|
+
# When the rule is ran as "test-187"
|
30
|
+
# Then with_given_ruby becomes "RBENV_VERSION=1.8.7-p375"
|
31
|
+
with_given_ruby = RBENV_VERSION=$(given_ruby_version)
|
32
|
+
|
33
|
+
|
34
|
+
# Runs tests for all supported ruby versions.
|
35
|
+
test: test-24 test-25 test-26 test-27
|
36
|
+
test_24: test-24
|
37
|
+
test_25: test-25
|
38
|
+
test_26: test-26
|
39
|
+
test_27: test-27
|
40
|
+
|
41
|
+
# Runs tests against a specific ruby version
|
42
|
+
test-%:
|
43
|
+
$(with_given_ruby) bundle install
|
44
|
+
$(with_given_ruby) bundle exec rake compile test
|
data/README.md
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+

|
2
|
+
[](https://codeclimate.com/github/dunkelbraun/turbo_test_constant_tracer/maintainability)
|
3
|
+
[](https://coveralls.io/github/dunkelbraun/turbo_test_constant_tracer?branch=main)
|
4
|
+
|
5
|
+
# TurboTestConstantTracer
|
6
|
+
|
7
|
+
Description @todo.
|
8
|
+
|
9
|
+
## Development
|
10
|
+
|
11
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
12
|
+
|
13
|
+
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).
|
14
|
+
|
15
|
+
## Contributing
|
16
|
+
|
17
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/dunkelbraun/turbo_test_constant_tracer. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/dunkelbraun/turbo_test_constant_tracer/blob/master/CODE_OF_CONDUCT.md).
|
18
|
+
|
19
|
+
|
20
|
+
## License
|
21
|
+
|
22
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
23
|
+
|
24
|
+
## Code of Conduct
|
25
|
+
|
26
|
+
Everyone interacting in the TurboTestConstantTracer project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/dunkelbraun/turbo_test_constant_tracer/blob/master/CODE_OF_CONDUCT.md).
|
data/Rakefile
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "bundler/gem_tasks"
|
4
|
+
require "rake/testtask"
|
5
|
+
require "rake/extensiontask"
|
6
|
+
|
7
|
+
ENV["TESTOPTS"] = "#{ENV['TESTOPTS']} --verbose"
|
8
|
+
|
9
|
+
Rake::TestTask.new(:test) do |t|
|
10
|
+
t.libs << "test"
|
11
|
+
t.libs << "lib"
|
12
|
+
t.test_files = FileList["test/**/*_test.rb"]
|
13
|
+
end
|
14
|
+
|
15
|
+
task default: :test
|
16
|
+
|
17
|
+
Rake::ExtensionTask.new "hash_lookup_with_proxy_ext" do |ext|
|
18
|
+
ext.lib_dir = "lib"
|
19
|
+
end
|
20
|
+
|
21
|
+
if ENV["CI"]
|
22
|
+
require "coveralls/rake/task"
|
23
|
+
Coveralls::RakeTask.new
|
24
|
+
end
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "turbo_test_constant_tracer"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
#include "ruby.h"
|
2
|
+
|
3
|
+
VALUE turbo_test;
|
4
|
+
VALUE turbo_test_constant_tracer;
|
5
|
+
VALUE turbo_test_hash_lookup_with_proxy;
|
6
|
+
VALUE turbo_test_hash_lookup_with_proxy_methods;
|
7
|
+
|
8
|
+
ID turbo_test_proxy_object_method;
|
9
|
+
|
10
|
+
VALUE my_rb_hash_aset(VALUE hash, VALUE key, VALUE val) {
|
11
|
+
key = rb_funcall(key, turbo_test_proxy_object_method, 0);
|
12
|
+
return rb_hash_aset(hash, key, val);
|
13
|
+
}
|
14
|
+
|
15
|
+
VALUE turbo_test_proxy_object(VALUE self){
|
16
|
+
return self;
|
17
|
+
}
|
18
|
+
|
19
|
+
VALUE add_assign_method(VALUE self) {
|
20
|
+
rb_define_method(turbo_test_hash_lookup_with_proxy_methods, "[]=", my_rb_hash_aset, 2);
|
21
|
+
return Qtrue;
|
22
|
+
}
|
23
|
+
|
24
|
+
void Init_hash_lookup_with_proxy_ext()
|
25
|
+
{
|
26
|
+
turbo_test = rb_define_module("TurboTest");
|
27
|
+
turbo_test_constant_tracer = rb_define_module_under(turbo_test, "ConstantTracer");
|
28
|
+
turbo_test_hash_lookup_with_proxy = rb_define_module_under(turbo_test_constant_tracer, "HashLookupWithProxy");
|
29
|
+
turbo_test_hash_lookup_with_proxy_methods = rb_define_module_under(turbo_test_hash_lookup_with_proxy, "Methods");
|
30
|
+
rb_define_module_function(turbo_test_hash_lookup_with_proxy_methods, "add_assign_method", add_assign_method, 0);
|
31
|
+
|
32
|
+
turbo_test_proxy_object_method = rb_intern("__turbo_test_proxied_class");
|
33
|
+
rb_define_method(rb_cBasicObject, "__turbo_test_proxied_class", turbo_test_proxy_object, 0);
|
34
|
+
}
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "turbo_test_events"
|
4
|
+
|
5
|
+
require_relative "turbo_test_constant_tracer/version"
|
6
|
+
|
7
|
+
module TurboTest
|
8
|
+
module ConstantTracer
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
require_relative "turbo_test_constant_tracer/definition"
|
13
|
+
require_relative "turbo_test_constant_tracer/proxy_klass"
|
14
|
+
require_relative "turbo_test_constant_tracer/klass"
|
15
|
+
require_relative "turbo_test_constant_tracer/regexp/tilde"
|
16
|
+
require_relative "turbo_test_constant_tracer/constructor"
|
17
|
+
require_relative "turbo_test_constant_tracer/event_publisher"
|
18
|
+
require_relative "turbo_test_constant_tracer/hash_lookup_with_proxy"
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TurboTest
|
4
|
+
module ConstantTracer
|
5
|
+
class Constructor
|
6
|
+
def initialize(original_class)
|
7
|
+
@klass = original_class
|
8
|
+
@klass_name = original_class.name.gsub("::", "")
|
9
|
+
end
|
10
|
+
|
11
|
+
def construct
|
12
|
+
return ProxyKlass.const_get(@klass_name) if proxy_class_defined?
|
13
|
+
|
14
|
+
Klass.define(@klass, @klass_name).tap do
|
15
|
+
prepend_equality_operators_to_original_class
|
16
|
+
prepend_equality_operators Numeric
|
17
|
+
prepend_equality_operators Comparable
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def proxy_class_defined?
|
24
|
+
ProxyKlass.const_defined? @klass_name, false
|
25
|
+
end
|
26
|
+
|
27
|
+
def prepend_equality_operators_to_original_class
|
28
|
+
s_class = @klass.singleton_class
|
29
|
+
s_class.prepend(CaseEquality)
|
30
|
+
s_class.prepend(Equality)
|
31
|
+
end
|
32
|
+
|
33
|
+
def prepend_equality_operators(mod)
|
34
|
+
return unless @klass.ancestors.include?(mod)
|
35
|
+
|
36
|
+
mod.singleton_class.prepend(CaseEquality)
|
37
|
+
mod.singleton_class.prepend(Equality)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
module CaseEquality
|
42
|
+
def ===(other)
|
43
|
+
if other.respond_to?(:turbo_test_proxied_class)
|
44
|
+
other = (other.turbo_test_proxied_class || other)
|
45
|
+
end
|
46
|
+
super
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
module Equality
|
51
|
+
def ==(other)
|
52
|
+
if other.respond_to?(:turbo_test_proxied_class)
|
53
|
+
other = (other.turbo_test_proxied_class || other)
|
54
|
+
end
|
55
|
+
super
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "binding_of_caller"
|
4
|
+
require_relative "definition/templates"
|
5
|
+
|
6
|
+
module TurboTest
|
7
|
+
module ConstantTracer
|
8
|
+
module Definition
|
9
|
+
class << self
|
10
|
+
include DefinitionTemplates
|
11
|
+
|
12
|
+
def internal_proxy(klass, name, constant, location)
|
13
|
+
freeze = constant.frozen?
|
14
|
+
constant = constant.dup if freeze
|
15
|
+
create_internal_proxy_methods(constant, klass, name, location)
|
16
|
+
constant.__turbo_test_tt_proxy_dup_object = constant.dup
|
17
|
+
constant.__send__(:__turbo_test_freeze) if freeze
|
18
|
+
set_class_constant(klass, name, constant)
|
19
|
+
end
|
20
|
+
|
21
|
+
def delegator_proxy(klass, name, constant, location)
|
22
|
+
proxy_class = Constructor.new(constant.class).construct
|
23
|
+
proxy_object = proxy_class.new(constant)
|
24
|
+
proxy_object.turbo_test_name = "#{klass}::#{name}"
|
25
|
+
proxy_object.turbo_test_path = location
|
26
|
+
ProxyKlass.proxied_objects[proxy_object.object_id] = true
|
27
|
+
set_class_constant(klass, name, proxy_object)
|
28
|
+
end
|
29
|
+
|
30
|
+
SPECIAL_METHODS = {
|
31
|
+
"String" => %i[scan gsub gsub! sub sub!],
|
32
|
+
"Enumerable" => %i[all? any? grep grep_v none? one? slice_before slice_after]
|
33
|
+
}.freeze
|
34
|
+
|
35
|
+
SUPER_SPECIAL_METHODS = {
|
36
|
+
"String" => [:=~]
|
37
|
+
}.freeze
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def create_internal_proxy_methods(object, klass, name, location)
|
42
|
+
singleton_class = object.singleton_class
|
43
|
+
singleton_class.class_eval { attr_accessor :__turbo_test_tt_proxy_dup_object }
|
44
|
+
|
45
|
+
methods_to_modify(object).each do |mod_method|
|
46
|
+
alias_original_method(singleton_class, mod_method, mod_method)
|
47
|
+
define_proxy_method(singleton_class, klass, mod_method, name, location)
|
48
|
+
end
|
49
|
+
|
50
|
+
modify_string_methods(object, klass, singleton_class, name, location)
|
51
|
+
modify_enumerable_methods(object, klass, singleton_class, name, location)
|
52
|
+
end
|
53
|
+
|
54
|
+
def methods_to_modify(object)
|
55
|
+
singleton_class = object.singleton_class
|
56
|
+
mod_methods = singleton_class.instance_methods.reject do |method|
|
57
|
+
method == :__send__
|
58
|
+
end
|
59
|
+
if object.class == ::String
|
60
|
+
mod_methods -= SPECIAL_METHODS["String"]
|
61
|
+
mod_methods -= SUPER_SPECIAL_METHODS["String"]
|
62
|
+
end
|
63
|
+
mod_methods -= SPECIAL_METHODS["Enumerable"] if object.is_a? ::Enumerable
|
64
|
+
mod_methods
|
65
|
+
end
|
66
|
+
|
67
|
+
def modify_string_methods(object, klass, singleton_class, name, location)
|
68
|
+
return unless object.class == ::String
|
69
|
+
|
70
|
+
SPECIAL_METHODS["String"].each do |mod_method|
|
71
|
+
original_method = mod_method
|
72
|
+
mod_method = mod_method.to_s.gsub("!", "_bang").to_sym
|
73
|
+
alias_original_method(singleton_class, mod_method, original_method)
|
74
|
+
define_proxy_string_template_method(
|
75
|
+
singleton_class, "#{klass}::#{name}", mod_method, location, original_method
|
76
|
+
)
|
77
|
+
end
|
78
|
+
define_equal_tilde_method(singleton_class, klass, name, location)
|
79
|
+
end
|
80
|
+
|
81
|
+
def modify_enumerable_methods(object, klass, singleton_class, name, location)
|
82
|
+
return unless object.is_a? ::Enumerable
|
83
|
+
|
84
|
+
SPECIAL_METHODS["Enumerable"].each do |mod_method|
|
85
|
+
original_method = mod_method
|
86
|
+
mod_method = mod_method.to_s.gsub("?", "_question_mark").to_sym
|
87
|
+
alias_original_method(singleton_class, mod_method, original_method)
|
88
|
+
define_proxy_enumerable_template_method(
|
89
|
+
singleton_class, "#{klass}::#{name}", location, original_method
|
90
|
+
)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "binding_of_caller"
|
4
|
+
require "English"
|
5
|
+
|
6
|
+
module TurboTest
|
7
|
+
module ConstantTracer
|
8
|
+
module DefinitionTemplates
|
9
|
+
private
|
10
|
+
|
11
|
+
def define_proxy_enumerable_template_method(singleton_class, name, location, original_method)
|
12
|
+
singleton_class.class_eval <<~RUBY, __FILE__, __LINE__ + 1
|
13
|
+
def #{original_method}(*args, &block)
|
14
|
+
send_block = if block
|
15
|
+
Proc.new do |match|
|
16
|
+
block_binding = block.binding
|
17
|
+
block_binding.local_variable_set(:_turbotest_tilde, $~)
|
18
|
+
block_binding.eval("$~=_turbotest_tilde")
|
19
|
+
block.call(match)
|
20
|
+
end
|
21
|
+
else
|
22
|
+
block
|
23
|
+
end
|
24
|
+
result = __turbo_test_tt_proxy_dup_object.#{original_method}(*args, &send_block)
|
25
|
+
caller_binding = binding.of_caller(1)
|
26
|
+
caller_binding.local_variable_set(:_turbotest_tilde, $~)
|
27
|
+
caller_binding.eval("$~=_turbotest_tilde")
|
28
|
+
::TurboTest::ConstantTracer::EventPublisher.publish("#{name}", "#{location}")
|
29
|
+
result
|
30
|
+
end
|
31
|
+
RUBY
|
32
|
+
end
|
33
|
+
|
34
|
+
def define_equal_tilde_method(singleton_class, _klass, _name, _location)
|
35
|
+
singleton_class.class_eval do
|
36
|
+
def =~(other)
|
37
|
+
caller_binding = binding.of_caller(1)
|
38
|
+
(::String.new(self) =~ other).tap do
|
39
|
+
caller_binding.local_variable_set(:_turbotest_tilde, $LAST_MATCH_INFO)
|
40
|
+
caller_binding.eval("$~=_turbotest_tilde")
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def alias_original_method(klass, mod_method, original_method)
|
47
|
+
klass.class_eval do
|
48
|
+
aliased_name = "__turbo_test_#{mod_method}"
|
49
|
+
alias_method aliased_name, original_method
|
50
|
+
# rubocop:disable Style/AccessModifierDeclarations
|
51
|
+
private aliased_name
|
52
|
+
# rubocop:enable Style/AccessModifierDeclarations
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def define_proxy_method(singleton_class, klass, _mod_method, name, location)
|
57
|
+
singleton_class.class_eval <<~RUBY, __FILE__, __LINE__ + 1
|
58
|
+
aliased_name = "__turbo_test_\#\{_mod_method\}"
|
59
|
+
define_method _mod_method do |*args, &block|
|
60
|
+
result = __send__ aliased_name, *args, &block
|
61
|
+
::TurboTest::ConstantTracer::EventPublisher.publish("#{klass}::#{name}", "#{location}")
|
62
|
+
result
|
63
|
+
end
|
64
|
+
RUBY
|
65
|
+
end
|
66
|
+
|
67
|
+
# rubocop:disable Layout/LineLength
|
68
|
+
def define_proxy_string_template_method(singleton_class, name, _mod_method, location, original_method)
|
69
|
+
singleton_class.class_eval <<~RUBY, __FILE__, __LINE__ + 1
|
70
|
+
aliased_name = "__turbo_test_\#\{_mod_method\}"
|
71
|
+
define_method :#{original_method} do |*args, &block|
|
72
|
+
res = unless block
|
73
|
+
__send__(aliased_name, *args)
|
74
|
+
else
|
75
|
+
my_proc = Proc.new do |match|
|
76
|
+
block_binding = block.binding
|
77
|
+
block_binding.local_variable_set(:_turbotest_tilde, $~)
|
78
|
+
block_binding.eval("$~=_turbotest_tilde")
|
79
|
+
block.call(match)
|
80
|
+
end
|
81
|
+
__send__(aliased_name, *args, &(my_proc))
|
82
|
+
end
|
83
|
+
::TurboTest::ConstantTracer::EventPublisher.publish("#{name}", "#{location}")
|
84
|
+
res
|
85
|
+
end
|
86
|
+
RUBY
|
87
|
+
end
|
88
|
+
# rubocop:enable Layout/LineLength
|
89
|
+
|
90
|
+
def set_class_constant(klass, name, object)
|
91
|
+
silence_warnings do
|
92
|
+
klass.const_set(name.gsub("::", ""), object)
|
93
|
+
end
|
94
|
+
object
|
95
|
+
end
|
96
|
+
|
97
|
+
def silence_warnings
|
98
|
+
old_stderr = $stderr
|
99
|
+
$stderr = StringIO.new
|
100
|
+
yield
|
101
|
+
ensure
|
102
|
+
$stderr = old_stderr
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|