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.
@@ -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