turbo_test_constant_tracer 0.1.11

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -0,0 +1,26 @@
1
+ ![Tests](https://github.com/dunkelbraun/turbo_test_constant_tracer/workflows/Tests/badge.svg?branch=main)
2
+ [![Maintainability](https://api.codeclimate.com/v1/badges/6f848135bd1e329faba5/maintainability)](https://codeclimate.com/github/dunkelbraun/turbo_test_constant_tracer/maintainability)
3
+ [![Coverage Status](https://coveralls.io/repos/github/dunkelbraun/turbo_test_constant_tracer/badge.svg?branch=main)](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).
@@ -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
@@ -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__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,3 @@
1
+ #ifndef EXTCONF_H
2
+ #define EXTCONF_H
3
+ #endif
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "mkmf"
4
+
5
+ create_makefile "hash_lookup_with_proxy_ext"
@@ -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