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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c4b0c989272201bf193600af16a5d4010842b6d169c4cd97afc9af4bea38d4cd
4
- data.tar.gz: 0723ffdd0c7fdfb7220348d7bad7d0af8691f66330c24d64ff9e4d52300802dc
3
+ metadata.gz: c6f558ae43347082febc5e6f30be8afe0ec5340423b9e2cca23d9b412139ab4e
4
+ data.tar.gz: 5a1dddb63ed9e12178ff17c3150eb8910fb29b63c564d78302dddf77dcc679a0
5
5
  SHA512:
6
- metadata.gz: ca5e021ac6507f3679d44fd7fd48068f8918a5990fbd2b993f19e55db06cc1e80c42a23796a9962cdb29b1e20a2e979d7d7765030ca58997979fc7a8bf5cac1d
7
- data.tar.gz: d12e9983fea63b43385e686072922b3f8f7bc5dc0e5864a276c3d80d7fcd31e9021ed6c8337814ada8c75cd76add04eab81a903df747833b0bcc69ea30a22e78
6
+ metadata.gz: f0f8936853687e6facfd21ea46bea355f593b6cf570ac20c6c3406e1764463432623664c829d207bc2e1ccd241baf4e99efc0e409518e596cfcbf5a20af4488a
7
+ data.tar.gz: 5950203f5babaf701bd5a9c48e9e51fabda3844f9e7645f0912fcd98f1cb109c488211c39119f6af1bcb918c8c34c2e74c8d55e933cc8aabe046dd94c0013fdd
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- ree (1.2.6)
4
+ ree (1.2.7)
5
5
  base64
6
6
  commander (~> 5.0.0)
7
7
  logger (~> 1.6.5)
@@ -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
- wrap_as_entry_point(config)
21
+ build_entry_point_wrapper(benchmark_name, config)
21
22
  else
22
- wrap_as_collector
23
+ build_collector_wrapper(benchmark_name)
23
24
  end
24
25
  end
25
26
 
26
27
  private
27
28
 
28
- def wrap_as_entry_point(config)
29
- alias_target = @is_class_method ? eigenclass : @target
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
- alias_target.define_method(method_name) do |*args, **kwargs, &block|
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
- send(method_alias, *args, **kwargs, &block)
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
- send(method_alias, *args, **kwargs, &block)
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
- send(method_alias, *args, **kwargs, &block)
47
+ next_layer.call(*args, **kwargs, &block)
56
48
  end
57
49
  end
58
50
  end
59
51
  end
60
52
 
61
- def wrap_as_collector
62
- alias_target = @is_class_method ? eigenclass : @target
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
- send(method_alias, *args, **kwargs, &block)
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
- original_alias = :"__ree_original_#{method_name}"
61
- param_source = if alias_target.method_defined?(original_alias)
62
- original_alias
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(param_source)
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
- make_alias
81
- make_definition
82
- end
83
-
84
- def execute_on(target, args, kwargs, &blk)
85
- @args.call(args, kwargs, blk)
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 make_alias
118
- alias_target.alias_method(method_alias, method_name)
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 make_definition
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.execute_on(self, args, kwargs, &blk)
162
+ decorator.validate_and_call(self, #{method_alias.inspect}, args, kwargs, &blk)
128
163
  end
129
164
  ), file, line - 3)
130
165
 
131
- make_private if private_method?
132
- make_protected if protected_method?
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?(method_alias) if is_class_method
137
- target.private_instance_methods.include?(method_alias)
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?(method_alias) if is_class_method
142
- target.protected_instance_methods.include?(method_alias)
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.each do |plugin_class|
17
- plugin_class.new(name, false, self).call
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.each do |plugin_class|
43
- plugin_class.new(name, true, self).call
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Ree
4
- VERSION = "1.2.6"
4
+ VERSION = "1.2.7"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ree
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.6
4
+ version: 1.2.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ruslan Gatiyatov