ree 1.2.6 → 1.2.7
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 +4 -4
- data/Gemfile.lock +1 -1
- data/lib/ree/benchmark_method_plugin.rb +14 -36
- data/lib/ree/contracts/contractable.rb +4 -4
- data/lib/ree/contracts/method_decorator.rb +80 -60
- data/lib/ree/method_added_hook.rb +106 -14
- data/lib/ree/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c6f558ae43347082febc5e6f30be8afe0ec5340423b9e2cca23d9b412139ab4e
|
|
4
|
+
data.tar.gz: 5a1dddb63ed9e12178ff17c3150eb8910fb29b63c564d78302dddf77dcc679a0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f0f8936853687e6facfd21ea46bea355f593b6cf570ac20c6c3406e1764463432623664c829d207bc2e1ccd241baf4e99efc0e409518e596cfcbf5a20af4488a
|
|
7
|
+
data.tar.gz: 5950203f5babaf701bd5a9c48e9e51fabda3844f9e7645f0912fcd98f1cb109c488211c39119f6af1bcb918c8c34c2e74c8d55e933cc8aabe046dd94c0013fdd
|
data/Gemfile.lock
CHANGED
|
@@ -12,66 +12,48 @@ class Ree::BenchmarkMethodPlugin
|
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
def call
|
|
15
|
-
return unless @method_name == :call && ree_fn?
|
|
15
|
+
return nil unless @method_name == :call && ree_fn?
|
|
16
16
|
|
|
17
17
|
config = @target.instance_variable_get(:@__ree_benchmark_config)
|
|
18
|
-
|
|
18
|
+
benchmark_name = build_benchmark_name
|
|
19
|
+
|
|
19
20
|
if config
|
|
20
|
-
|
|
21
|
+
build_entry_point_wrapper(benchmark_name, config)
|
|
21
22
|
else
|
|
22
|
-
|
|
23
|
+
build_collector_wrapper(benchmark_name)
|
|
23
24
|
end
|
|
24
25
|
end
|
|
25
26
|
|
|
26
27
|
private
|
|
27
28
|
|
|
28
|
-
def
|
|
29
|
-
|
|
30
|
-
method_name = @method_name
|
|
31
|
-
method_alias = :"__benchmark_#{method_name}"
|
|
32
|
-
|
|
33
|
-
return if alias_target.method_defined?(method_alias)
|
|
34
|
-
|
|
35
|
-
alias_target.alias_method(method_alias, method_name)
|
|
36
|
-
|
|
37
|
-
benchmark_name = build_benchmark_name
|
|
38
|
-
output_proc = config[:output] || -> (res) { $stdout.puts(res) }
|
|
29
|
+
def build_entry_point_wrapper(benchmark_name, config)
|
|
30
|
+
output_proc = config[:output] || ->(res) { $stdout.puts(res) }
|
|
39
31
|
deep = config.fetch(:deep, true)
|
|
40
32
|
once = config.fetch(:once, false)
|
|
41
33
|
benchmark_done = false
|
|
42
34
|
|
|
43
|
-
|
|
35
|
+
Proc.new do |instance, next_layer, *args, **kwargs, &block|
|
|
44
36
|
if Ree::BenchmarkTracer.active?
|
|
45
37
|
Ree::BenchmarkTracer.collect(benchmark_name) do
|
|
46
|
-
|
|
38
|
+
next_layer.call(*args, **kwargs, &block)
|
|
47
39
|
end
|
|
48
40
|
elsif once && benchmark_done
|
|
49
41
|
Ree::BenchmarkTracer.collect(benchmark_name) do
|
|
50
|
-
|
|
42
|
+
next_layer.call(*args, **kwargs, &block)
|
|
51
43
|
end
|
|
52
44
|
else
|
|
53
45
|
benchmark_done = true if once
|
|
54
46
|
Ree::BenchmarkTracer.trace(benchmark_name, output_proc: output_proc, deep: deep) do
|
|
55
|
-
|
|
47
|
+
next_layer.call(*args, **kwargs, &block)
|
|
56
48
|
end
|
|
57
49
|
end
|
|
58
50
|
end
|
|
59
51
|
end
|
|
60
52
|
|
|
61
|
-
def
|
|
62
|
-
|
|
63
|
-
method_name = @method_name
|
|
64
|
-
method_alias = :"__benchmark_#{method_name}"
|
|
65
|
-
|
|
66
|
-
return if alias_target.method_defined?(method_alias)
|
|
67
|
-
|
|
68
|
-
alias_target.alias_method(method_alias, method_name)
|
|
69
|
-
|
|
70
|
-
benchmark_name = build_benchmark_name
|
|
71
|
-
|
|
72
|
-
alias_target.define_method(method_name) do |*args, **kwargs, &block|
|
|
53
|
+
def build_collector_wrapper(benchmark_name)
|
|
54
|
+
Proc.new do |instance, next_layer, *args, **kwargs, &block|
|
|
73
55
|
Ree::BenchmarkTracer.collect(benchmark_name) do
|
|
74
|
-
|
|
56
|
+
next_layer.call(*args, **kwargs, &block)
|
|
75
57
|
end
|
|
76
58
|
end
|
|
77
59
|
end
|
|
@@ -99,8 +81,4 @@ class Ree::BenchmarkMethodPlugin
|
|
|
99
81
|
|
|
100
82
|
facade.get_object(pkg, obj).fn?
|
|
101
83
|
end
|
|
102
|
-
|
|
103
|
-
def eigenclass
|
|
104
|
-
class << @target; self; end
|
|
105
|
-
end
|
|
106
84
|
end
|
|
@@ -7,14 +7,14 @@ require_relative 'engine_proxy'
|
|
|
7
7
|
module Ree::Contracts
|
|
8
8
|
module Contractable
|
|
9
9
|
def method_added(name)
|
|
10
|
-
return if _ree_method_added_hook_active?
|
|
11
|
-
MethodDecorator.new(name, false, self).call
|
|
10
|
+
return super if _ree_method_added_hook_active?
|
|
11
|
+
MethodDecorator.new(name, false, self).call(plugin_mode: false)
|
|
12
12
|
super
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
def singleton_method_added(name)
|
|
16
|
-
return if _ree_method_added_hook_active?
|
|
17
|
-
MethodDecorator.new(name, true, self).call
|
|
16
|
+
return super if _ree_method_added_hook_active?
|
|
17
|
+
MethodDecorator.new(name, true, self).call(plugin_mode: false)
|
|
18
18
|
super
|
|
19
19
|
end
|
|
20
20
|
|
|
@@ -51,21 +51,18 @@ module Ree::Contracts
|
|
|
51
51
|
@doc = engine.fetch_doc
|
|
52
52
|
end
|
|
53
53
|
|
|
54
|
-
def call
|
|
55
|
-
return if Ree::Contracts.no_contracts?
|
|
56
|
-
return unless contract_definition
|
|
54
|
+
def call(plugin_mode: true)
|
|
55
|
+
return nil if Ree::Contracts.no_contracts?
|
|
56
|
+
return nil unless contract_definition
|
|
57
57
|
|
|
58
|
+
# Store decorator for runtime lookups (still needed)
|
|
58
59
|
self.class.add_decorator(self)
|
|
59
60
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
else
|
|
64
|
-
method_name
|
|
65
|
-
end
|
|
66
|
-
|
|
61
|
+
# Get method parameters from the method (before aliasing)
|
|
62
|
+
# Note: We get params from the current method, not __ree_original_#{method_name}
|
|
63
|
+
# because the alias hasn't been created yet when plugins are called
|
|
67
64
|
@method_parameters = alias_target
|
|
68
|
-
.instance_method(
|
|
65
|
+
.instance_method(method_name)
|
|
69
66
|
.parameters
|
|
70
67
|
.freeze
|
|
71
68
|
|
|
@@ -77,21 +74,13 @@ module Ree::Contracts
|
|
|
77
74
|
|
|
78
75
|
@return_validator = Validators.fetch_for(contract_definition.return_contract)
|
|
79
76
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
result = target.send(method_alias, *args, **kwargs, &blk)
|
|
87
|
-
|
|
88
|
-
if !return_validator.call(result)
|
|
89
|
-
raise ReturnContractError, "Invalid return value for #{printed_name}\n #{
|
|
90
|
-
return_validator.message(result, 'returns', 0).strip
|
|
91
|
-
}"
|
|
77
|
+
if plugin_mode
|
|
78
|
+
# Plugin mode: Return wrapper lambda for composition
|
|
79
|
+
build_contract_wrapper
|
|
80
|
+
else
|
|
81
|
+
# Legacy mode: Apply wrapper directly (for Contractable standalone usage)
|
|
82
|
+
apply_contract_wrapper_directly
|
|
92
83
|
end
|
|
93
|
-
|
|
94
|
-
result
|
|
95
84
|
end
|
|
96
85
|
|
|
97
86
|
# Unique ID of this Method Decorator
|
|
@@ -99,11 +88,6 @@ module Ree::Contracts
|
|
|
99
88
|
@id ||= self.class.decorator_id(target, method_name, is_class_method)
|
|
100
89
|
end
|
|
101
90
|
|
|
102
|
-
# Alias name for original method
|
|
103
|
-
def method_alias
|
|
104
|
-
@method_alias ||= :"__original_#{method_name}_#{SecureRandom.hex}"
|
|
105
|
-
end
|
|
106
|
-
|
|
107
91
|
# Target class to be used for alias method definition
|
|
108
92
|
def alias_target
|
|
109
93
|
@alias_target ||= begin
|
|
@@ -112,54 +96,90 @@ module Ree::Contracts
|
|
|
112
96
|
end
|
|
113
97
|
end
|
|
114
98
|
|
|
99
|
+
# Public method used by legacy contract wrapper (called from class_eval'd method)
|
|
100
|
+
def validate_and_call(instance, method_alias, args, kwargs, &blk)
|
|
101
|
+
@args.call(args, kwargs, blk)
|
|
102
|
+
result = instance.send(method_alias, *args, **kwargs, &blk)
|
|
103
|
+
|
|
104
|
+
unless @return_validator.call(result)
|
|
105
|
+
raise ReturnContractError, "Invalid return value for #{printed_name}\n #{
|
|
106
|
+
@return_validator.message(result, 'returns', 0).strip
|
|
107
|
+
}"
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
result
|
|
111
|
+
end
|
|
112
|
+
|
|
115
113
|
private
|
|
116
114
|
|
|
117
|
-
def
|
|
118
|
-
|
|
115
|
+
def build_contract_wrapper
|
|
116
|
+
args_validator = @args
|
|
117
|
+
return_validator = @return_validator
|
|
118
|
+
decorator_printed_name = printed_name
|
|
119
|
+
|
|
120
|
+
Proc.new do |instance, next_layer, *args, **kwargs, &block|
|
|
121
|
+
# Validate arguments
|
|
122
|
+
args_validator.call(args, kwargs, block)
|
|
123
|
+
|
|
124
|
+
# Call next layer
|
|
125
|
+
result = next_layer.call(*args, **kwargs, &block)
|
|
126
|
+
|
|
127
|
+
# Validate return value
|
|
128
|
+
unless return_validator.call(result)
|
|
129
|
+
raise ReturnContractError, "Invalid return value for #{decorator_printed_name}\n #{
|
|
130
|
+
return_validator.message(result, 'returns', 0).strip
|
|
131
|
+
}"
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
result
|
|
135
|
+
end
|
|
119
136
|
end
|
|
120
137
|
|
|
121
|
-
def
|
|
138
|
+
def apply_contract_wrapper_directly
|
|
139
|
+
# Legacy mode for Contractable standalone usage
|
|
140
|
+
# Detect visibility BEFORE creating alias
|
|
141
|
+
visibility = if alias_target.private_instance_methods.include?(method_name)
|
|
142
|
+
:private
|
|
143
|
+
elsif alias_target.protected_instance_methods.include?(method_name)
|
|
144
|
+
:protected
|
|
145
|
+
else
|
|
146
|
+
:public
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# Create our own alias and wrapper
|
|
150
|
+
method_alias = :"__original_#{method_name}_#{SecureRandom.hex}"
|
|
151
|
+
alias_target.alias_method(method_alias, method_name)
|
|
152
|
+
|
|
153
|
+
args_validator = @args
|
|
154
|
+
return_validator = @return_validator
|
|
155
|
+
decorator_printed_name = printed_name
|
|
156
|
+
|
|
122
157
|
file, line = alias_target.instance_method(method_alias).source_location
|
|
123
158
|
|
|
124
159
|
alias_target.class_eval(%Q(
|
|
125
160
|
def #{method_name}(*args, **kwargs, &blk)
|
|
126
161
|
decorator = Ree::Contracts::MethodDecorator.get_decorator('#{id}')
|
|
127
|
-
decorator.
|
|
162
|
+
decorator.validate_and_call(self, #{method_alias.inspect}, args, kwargs, &blk)
|
|
128
163
|
end
|
|
129
164
|
), file, line - 3)
|
|
130
165
|
|
|
131
|
-
|
|
132
|
-
|
|
166
|
+
# Restore visibility
|
|
167
|
+
case visibility
|
|
168
|
+
when :private
|
|
169
|
+
alias_target.send(:private, method_name, method_alias)
|
|
170
|
+
when :protected
|
|
171
|
+
alias_target.send(:protected, method_name, method_alias)
|
|
172
|
+
end
|
|
133
173
|
end
|
|
134
174
|
|
|
135
175
|
def private_method?
|
|
136
|
-
return target.private_methods.include?(
|
|
137
|
-
target.private_instance_methods.include?(
|
|
176
|
+
return target.private_methods.include?(method_name) if is_class_method
|
|
177
|
+
target.private_instance_methods.include?(method_name)
|
|
138
178
|
end
|
|
139
179
|
|
|
140
180
|
def protected_method?
|
|
141
|
-
return target.protected_methods.include?(
|
|
142
|
-
target.protected_instance_methods.include?(
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
def make_private
|
|
146
|
-
_method_name = method_name
|
|
147
|
-
_method_alias = method_alias
|
|
148
|
-
|
|
149
|
-
alias_target.class_eval do
|
|
150
|
-
private _method_name
|
|
151
|
-
private _method_alias
|
|
152
|
-
end
|
|
153
|
-
end
|
|
154
|
-
|
|
155
|
-
def make_protected
|
|
156
|
-
_method_name = method_name
|
|
157
|
-
_method_alias = method_alias
|
|
158
|
-
|
|
159
|
-
alias_target.class_eval do
|
|
160
|
-
protected _method_name
|
|
161
|
-
protected _method_alias
|
|
162
|
-
end
|
|
181
|
+
return target.protected_methods.include?(method_name) if is_class_method
|
|
182
|
+
target.protected_instance_methods.include?(method_name)
|
|
163
183
|
end
|
|
164
184
|
|
|
165
185
|
def printed_name
|
|
@@ -8,14 +8,22 @@ module Ree::MethodAddedHook
|
|
|
8
8
|
|
|
9
9
|
@__ree_plugin_running = true
|
|
10
10
|
|
|
11
|
-
original_alias = :"__ree_original_#{name}"
|
|
12
|
-
remove_method(original_alias) if method_defined?(original_alias)
|
|
13
|
-
alias_method(original_alias, name)
|
|
14
|
-
|
|
15
11
|
begin
|
|
16
|
-
plugins
|
|
17
|
-
|
|
12
|
+
# Collect wrapper lambdas from plugins (some may return nil)
|
|
13
|
+
wrappers = plugins.map { |plugin_class| plugin_class.new(name, false, self).call }.compact
|
|
14
|
+
|
|
15
|
+
if wrappers.empty?
|
|
16
|
+
@__ree_plugin_running = false
|
|
17
|
+
return super
|
|
18
18
|
end
|
|
19
|
+
|
|
20
|
+
# Create single alias for original implementation
|
|
21
|
+
original_alias = :"__ree_original_#{name}"
|
|
22
|
+
remove_method(original_alias) if method_defined?(original_alias)
|
|
23
|
+
alias_method(original_alias, name)
|
|
24
|
+
|
|
25
|
+
# Compose all wrappers into a single method
|
|
26
|
+
compose_method(name, original_alias, wrappers)
|
|
19
27
|
ensure
|
|
20
28
|
@__ree_plugin_running = false
|
|
21
29
|
end
|
|
@@ -30,22 +38,106 @@ module Ree::MethodAddedHook
|
|
|
30
38
|
|
|
31
39
|
@__ree_singleton_plugin_running = true
|
|
32
40
|
|
|
33
|
-
original_alias = :"__ree_original_#{name}"
|
|
34
41
|
eigenclass = class << self; self; end
|
|
35
42
|
|
|
36
|
-
if eigenclass.method_defined?(original_alias)
|
|
37
|
-
eigenclass.remove_method(original_alias)
|
|
38
|
-
end
|
|
39
|
-
eigenclass.alias_method(original_alias, name)
|
|
40
|
-
|
|
41
43
|
begin
|
|
42
|
-
plugins
|
|
43
|
-
|
|
44
|
+
# Collect wrapper lambdas from plugins (some may return nil)
|
|
45
|
+
wrappers = plugins.map { |plugin_class| plugin_class.new(name, true, self).call }.compact
|
|
46
|
+
|
|
47
|
+
if wrappers.empty?
|
|
48
|
+
@__ree_singleton_plugin_running = false
|
|
49
|
+
return super
|
|
44
50
|
end
|
|
51
|
+
|
|
52
|
+
# Create single alias for original implementation
|
|
53
|
+
original_alias = :"__ree_original_#{name}"
|
|
54
|
+
eigenclass.remove_method(original_alias) if eigenclass.method_defined?(original_alias)
|
|
55
|
+
eigenclass.alias_method(original_alias, name)
|
|
56
|
+
|
|
57
|
+
# Compose all wrappers into a single method
|
|
58
|
+
compose_singleton_method(eigenclass, name, original_alias, wrappers)
|
|
45
59
|
ensure
|
|
46
60
|
@__ree_singleton_plugin_running = false
|
|
47
61
|
end
|
|
48
62
|
|
|
49
63
|
super
|
|
50
64
|
end
|
|
65
|
+
|
|
66
|
+
private
|
|
67
|
+
|
|
68
|
+
def compose_method(method_name, original_alias, wrappers)
|
|
69
|
+
# Detect original method visibility
|
|
70
|
+
visibility = if private_method_defined?(original_alias)
|
|
71
|
+
:private
|
|
72
|
+
elsif protected_method_defined?(original_alias)
|
|
73
|
+
:protected
|
|
74
|
+
else
|
|
75
|
+
:public
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Build executor chain from inside out
|
|
79
|
+
define_method(method_name) do |*args, **kwargs, &block|
|
|
80
|
+
# Innermost layer: call the original method
|
|
81
|
+
# Note: next_layer signature is: ->(){ ... } to be called with .call(*args, **kwargs, &block)
|
|
82
|
+
executor = ->(*a, **kw, &b) { send(original_alias, *a, **kw, &b) }
|
|
83
|
+
|
|
84
|
+
# Wrap from inside out (reverse to make first plugin outermost)
|
|
85
|
+
wrappers.reverse_each do |wrapper|
|
|
86
|
+
current_executor = executor
|
|
87
|
+
# Wrapper receives: (instance, next_layer, *args, **kwargs, &block)
|
|
88
|
+
# We need to create a next_layer that calls current_executor
|
|
89
|
+
next_layer = ->(*a, **kw, &b) { current_executor.call(*a, **kw, &b) }
|
|
90
|
+
executor = ->(*a, **kw, &b) { wrapper.call(self, next_layer, *a, **kw, &b) }
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Execute the composed chain
|
|
94
|
+
executor.call(*args, **kwargs, &block)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Restore original visibility
|
|
98
|
+
case visibility
|
|
99
|
+
when :private
|
|
100
|
+
private method_name
|
|
101
|
+
when :protected
|
|
102
|
+
protected method_name
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def compose_singleton_method(eigenclass, method_name, original_alias, wrappers)
|
|
107
|
+
# Detect original method visibility
|
|
108
|
+
visibility = if eigenclass.private_method_defined?(original_alias)
|
|
109
|
+
:private
|
|
110
|
+
elsif eigenclass.protected_method_defined?(original_alias)
|
|
111
|
+
:protected
|
|
112
|
+
else
|
|
113
|
+
:public
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Build executor chain from inside out
|
|
117
|
+
eigenclass.define_method(method_name) do |*args, **kwargs, &block|
|
|
118
|
+
# Innermost layer: call the original method
|
|
119
|
+
# Note: next_layer signature is: ->(){ ... } to be called with .call(*args, **kwargs, &block)
|
|
120
|
+
executor = ->(*a, **kw, &b) { send(original_alias, *a, **kw, &b) }
|
|
121
|
+
|
|
122
|
+
# Wrap from inside out (reverse to make first plugin outermost)
|
|
123
|
+
wrappers.reverse_each do |wrapper|
|
|
124
|
+
current_executor = executor
|
|
125
|
+
# Wrapper receives: (instance, next_layer, *args, **kwargs, &block)
|
|
126
|
+
# We need to create a next_layer that calls current_executor
|
|
127
|
+
next_layer = ->(*a, **kw, &b) { current_executor.call(*a, **kw, &b) }
|
|
128
|
+
executor = ->(*a, **kw, &b) { wrapper.call(self, next_layer, *a, **kw, &b) }
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Execute the composed chain
|
|
132
|
+
executor.call(*args, **kwargs, &block)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# Restore original visibility
|
|
136
|
+
case visibility
|
|
137
|
+
when :private
|
|
138
|
+
eigenclass.send(:private, method_name)
|
|
139
|
+
when :protected
|
|
140
|
+
eigenclass.send(:protected, method_name)
|
|
141
|
+
end
|
|
142
|
+
end
|
|
51
143
|
end
|
data/lib/ree/version.rb
CHANGED