ree 1.2.1 → 1.2.3

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: 26a7ec18c35a9a6d79aad3f91f111bda3338547bc61f6f979ba226d2d5d393ab
4
- data.tar.gz: b330811d3d6623eeb4aab2deb85f9687c3cbe53142987cb03bb3938608d7c786
3
+ metadata.gz: 1b2a4983f915c5d0761d563998b0c5a6fa9bd344d39e61046e47c088b5f53b4d
4
+ data.tar.gz: 2ec723f70527921b5784989d5f4294a211c5cb5015fe2e16b181990829979dde
5
5
  SHA512:
6
- metadata.gz: 10e3c7af5fa2f7255884071f960c4795dc2eb32c7974a8ce8daf2a6d44f7a02a09e088a2ceab09fe71a738694279576c325480193e6c6b896ecb830bbc5e99a0
7
- data.tar.gz: 2e3f44006a5aa0764f4ff2dc2addd46a23845ba50abec919e770de25d6d8eac02dc1fa93d9b87d5d1ac59387b74f29f66ab812b36acc057f575c48626a8ad27d
6
+ metadata.gz: f86a204bf1134fbc800898d98e4d379bd9b7e9275db0e4ae6aa278142b6e3af3ef7b5f276eb4a301f711b024737c70dcb0d3e251985854747b0042899fea69f0
7
+ data.tar.gz: 4e20a1cc184a8b94472c707c07512827e5359626ddf5deb1f9e507fd46f37caac565330d526cee852de120585d3d9b949b809820c3492bc25463cafcd2eea919
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- ree (1.2.1)
4
+ ree (1.2.3)
5
5
  commander (~> 5.0.0)
6
6
  logger (~> 1.6.5)
7
7
 
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Ree::BenchmarkMethodPlugin
4
+ def self.active?
5
+ Ree.benchmark_mode?
6
+ end
7
+
8
+ def initialize(method_name, is_class_method, target)
9
+ @method_name = method_name
10
+ @is_class_method = is_class_method
11
+ @target = target
12
+ end
13
+
14
+ def call
15
+ config = @target.instance_variable_get(:@__ree_benchmark_config)
16
+
17
+ if @method_name == :call
18
+ if config
19
+ wrap_as_entry_point(config)
20
+ else
21
+ wrap_as_collector
22
+ end
23
+ end
24
+ end
25
+
26
+ private
27
+
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) }
39
+ deep = config.fetch(:deep, true)
40
+ once = config.fetch(:once, false)
41
+ benchmark_done = false
42
+
43
+ alias_target.define_method(method_name) do |*args, **kwargs, &block|
44
+ if once && benchmark_done
45
+ Ree::BenchmarkTracer.collect(benchmark_name) do
46
+ send(method_alias, *args, **kwargs, &block)
47
+ end
48
+ else
49
+ benchmark_done = true if once
50
+ Ree::BenchmarkTracer.trace(benchmark_name, output_proc: output_proc, deep: deep) do
51
+ send(method_alias, *args, **kwargs, &block)
52
+ end
53
+ end
54
+ end
55
+ end
56
+
57
+ def wrap_as_collector
58
+ alias_target = @is_class_method ? eigenclass : @target
59
+ method_name = @method_name
60
+ method_alias = :"__benchmark_#{method_name}"
61
+
62
+ return if alias_target.method_defined?(method_alias)
63
+
64
+ alias_target.alias_method(method_alias, method_name)
65
+
66
+ benchmark_name = build_benchmark_name
67
+
68
+ alias_target.define_method(method_name) do |*args, **kwargs, &block|
69
+ Ree::BenchmarkTracer.collect(benchmark_name) do
70
+ send(method_alias, *args, **kwargs, &block)
71
+ end
72
+ end
73
+ end
74
+
75
+ def build_benchmark_name
76
+ pkg = @target.instance_variable_get(:@__ree_package_name)
77
+ obj = @target.instance_variable_get(:@__ree_object_name)
78
+
79
+ base = if pkg && obj
80
+ "#{pkg}/#{obj}"
81
+ else
82
+ @target.name || @target.to_s
83
+ end
84
+
85
+ @method_name == :call ? base : "#{base}##{@method_name}"
86
+ end
87
+
88
+ def eigenclass
89
+ class << @target; self; end
90
+ end
91
+ end
@@ -6,10 +6,12 @@ class Ree::BenchmarkTracer
6
6
  THREAD_KEY = :ree_benchmark_tracer
7
7
 
8
8
  class << self
9
- def trace(name)
9
+ # Entry point trace — starts collection, outputs when root completes
10
+ def trace(name, output_proc: nil, deep: true)
10
11
  stack = Thread.current[THREAD_KEY] ||= []
11
12
  node = Node.new(name, Process.clock_gettime(Process::CLOCK_MONOTONIC), nil, [])
12
13
  stack.last.children.push(node) if stack.last
14
+ is_root = stack.empty? || stack.last.nil?
13
15
  stack.push(node)
14
16
 
15
17
  begin
@@ -20,20 +22,55 @@ class Ree::BenchmarkTracer
20
22
 
21
23
  if stack.empty?
22
24
  Thread.current[THREAD_KEY] = nil
23
- print_tree(node)
25
+
26
+ if output_proc
27
+ output_proc.call(format_tree(node, deep: deep))
28
+ else
29
+ $stdout.puts(format_tree(node, deep: deep))
30
+ end
24
31
  end
25
32
  end
26
33
 
27
34
  result
28
35
  end
29
36
 
37
+ # Collector trace — only participates if a trace is already active
38
+ def collect(name)
39
+ stack = Thread.current[THREAD_KEY]
40
+ return yield unless stack && !stack.empty?
41
+
42
+ node = Node.new(name, Process.clock_gettime(Process::CLOCK_MONOTONIC), nil, [])
43
+ stack.last.children.push(node)
44
+ stack.push(node)
45
+
46
+ begin
47
+ result = yield
48
+ ensure
49
+ node.duration = Process.clock_gettime(Process::CLOCK_MONOTONIC) - node.start_time
50
+ stack.pop
51
+ end
52
+
53
+ result
54
+ end
55
+
56
+ # Format tree as a string
57
+ # deep: false → only root node, deep: true → full tree
58
+ def format_tree(node, deep: true)
59
+ lines = []
60
+ build_tree_lines(node, 0, lines, deep: deep)
61
+ lines.join("\n")
62
+ end
63
+
30
64
  private
31
65
 
32
- def print_tree(node, depth = 0)
66
+ def build_tree_lines(node, depth, lines, deep: true)
33
67
  indent = " " * depth
34
68
  duration_ms = (node.duration * 1000).round(2)
35
- $stdout.puts "#{indent}#{node.name} (#{duration_ms}ms)"
36
- node.children.each { |child| print_tree(child, depth + 1) }
69
+ lines << "#{indent}#{node.name} (#{duration_ms}ms)"
70
+
71
+ if deep
72
+ node.children.each { |child| build_tree_lines(child, depth + 1, lines) }
73
+ end
37
74
  end
38
75
  end
39
76
  end
@@ -7,11 +7,13 @@ 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?
10
11
  MethodDecorator.new(name, false, self).call
11
12
  super
12
13
  end
13
14
 
14
15
  def singleton_method_added(name)
16
+ return if _ree_method_added_hook_active?
15
17
  MethodDecorator.new(name, true, self).call
16
18
  super
17
19
  end
@@ -28,5 +30,11 @@ module Ree::Contracts
28
30
  engine.add_contract(*args, &block)
29
31
  EngineProxy.new(engine)
30
32
  end
33
+
34
+ private
35
+
36
+ def _ree_method_added_hook_active?
37
+ is_a?(Ree::MethodAddedHook)
38
+ end
31
39
  end
32
40
  end
@@ -3,6 +3,10 @@
3
3
  module Ree::Contracts
4
4
  class MethodDecorator
5
5
  class << self
6
+ def active?
7
+ !Ree::Contracts.no_contracts?
8
+ end
9
+
6
10
  def decorator_id(target, method_name, is_class_method)
7
11
  "#{target.object_id}#{target.name}#{method_name}#{is_class_method}"
8
12
  end
@@ -53,8 +57,15 @@ module Ree::Contracts
53
57
 
54
58
  self.class.add_decorator(self)
55
59
 
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
+
56
67
  @method_parameters = alias_target
57
- .instance_method(method_name)
68
+ .instance_method(param_source)
58
69
  .parameters
59
70
  .freeze
60
71
 
@@ -5,7 +5,8 @@ class Ree::Object
5
5
  :package_name, :factory, :after_init,
6
6
  :class_name, :links, :mount_as, :freeze,
7
7
  :errors, :linked_const_list, :compiled_frozen,
8
- :singleton, :tags, :target, :with_caller
8
+ :singleton, :tags, :target, :with_caller,
9
+ :benchmark_config
9
10
 
10
11
  # @param [Symbol] name Object name
11
12
  # @param [String] schema_rpath Object schema path relative to project root dir
@@ -25,6 +26,7 @@ class Ree::Object
25
26
  @compiled_frozen = @freeze
26
27
  @linked_const_list = []
27
28
  @tags = []
29
+ @benchmark_config = nil
28
30
  end
29
31
 
30
32
  def reset
@@ -39,6 +41,7 @@ class Ree::Object
39
41
  @links = []
40
42
  @errors = []
41
43
  @linked_const_list = []
44
+ @benchmark_config = nil
42
45
  end
43
46
 
44
47
  # @param [ArrayOf[String]] list List of imported constants, modules and classes
@@ -153,6 +156,14 @@ class Ree::Object
153
156
  @after_init = val; self
154
157
  end
155
158
 
159
+ def set_benchmark_config(config)
160
+ @benchmark_config = config; self
161
+ end
162
+
163
+ def benchmark?
164
+ !!@benchmark_config
165
+ end
166
+
156
167
  def add_tags(list)
157
168
  @tags += list
158
169
  @tags.uniq!
@@ -170,6 +170,20 @@ class Ree::ObjectDsl
170
170
  @object.set_after_init(method_name)
171
171
  end
172
172
 
173
+ def benchmark(once: false, deep: true, output: -> (res) { $stdout.puts(res) })
174
+ if !@object.fn?
175
+ raise_error("`benchmark` is only available for fn objects")
176
+ end
177
+
178
+ check_bool(once, :once)
179
+ check_bool(deep, :deep)
180
+
181
+ config = { once: once, deep: deep, output: output }
182
+ @object.set_benchmark_config(config)
183
+
184
+ @object.klass.instance_variable_set(:@__ree_benchmark_config, config)
185
+ end
186
+
173
187
  # @param [Bool] flag
174
188
  def freeze(flag)
175
189
  if @object.with_caller? && flag
data/lib/ree/fn_dsl.rb CHANGED
@@ -3,6 +3,7 @@
3
3
  module Ree::FnDSL
4
4
  def self.included(base)
5
5
  base.extend(ClassMethods)
6
+ base.extend(Ree::MethodAddedHook)
6
7
  base.include(Ree::Inspectable)
7
8
  end
8
9
 
@@ -20,6 +21,9 @@ module Ree::FnDSL
20
21
  dsl.tags(["fn"])
21
22
  dsl.object.set_as_compiled(false)
22
23
 
24
+ self.instance_variable_set(:@__ree_package_name, dsl.package.name)
25
+ self.instance_variable_set(:@__ree_object_name, name)
26
+
23
27
  Ree.container.compile(dsl.package, name)
24
28
  end
25
29
  end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ree::MethodAddedHook
4
+ def method_added(name)
5
+ plugins = Ree.method_added_plugins.select(&:active?)
6
+ return super if plugins.empty?
7
+ return if @__ree_plugin_running
8
+
9
+ @__ree_plugin_running = true
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
+ begin
16
+ plugins.each do |plugin_class|
17
+ plugin_class.new(name, false, self).call
18
+ end
19
+ ensure
20
+ @__ree_plugin_running = false
21
+ end
22
+
23
+ super
24
+ end
25
+
26
+ def singleton_method_added(name)
27
+ plugins = Ree.method_added_plugins.select(&:active?)
28
+ return super if plugins.empty?
29
+ return if @__ree_singleton_plugin_running
30
+
31
+ @__ree_singleton_plugin_running = true
32
+
33
+ original_alias = :"__ree_original_#{name}"
34
+ eigenclass = class << self; self; end
35
+
36
+ if eigenclass.method_defined?(original_alias)
37
+ eigenclass.remove_method(original_alias)
38
+ end
39
+ eigenclass.alias_method(original_alias, name)
40
+
41
+ begin
42
+ plugins.each do |plugin_class|
43
+ plugin_class.new(name, true, self).call
44
+ end
45
+ ensure
46
+ @__ree_singleton_plugin_running = false
47
+ end
48
+
49
+ super
50
+ end
51
+ end
@@ -160,17 +160,6 @@ class Ree::ObjectCompiler
160
160
  #{str}
161
161
  ruby_eval
162
162
 
163
- if Ree.benchmark_mode? && object.fn? && !klass.instance_variable_get(:@__ree_benchmark_prepended)
164
- klass.instance_variable_set(:@__ree_benchmark_prepended, true)
165
- benchmark_name = "#{object.package_name}/#{object.name}"
166
- benchmark_mod = Module.new do
167
- define_method(:call) do |*args, **kwargs, &block|
168
- Ree::BenchmarkTracer.trace(benchmark_name) { super(*args, **kwargs, &block) }
169
- end
170
- end
171
- klass.prepend(benchmark_mod)
172
- end
173
-
174
163
  # compile all linked objects
175
164
  links.each do |link|
176
165
  pckg = @packages_facade.get_loaded_package(link.package_name)
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.1"
4
+ VERSION = "1.2.3"
5
5
  end
data/lib/ree.rb CHANGED
@@ -8,6 +8,7 @@ require 'fileutils'
8
8
  module Ree
9
9
  autoload :Args, 'ree/args'
10
10
  autoload :BeanDSL, 'ree/bean_dsl'
11
+ autoload :BenchmarkMethodPlugin, 'ree/benchmark_method_plugin'
11
12
  autoload :BenchmarkTracer, 'ree/benchmark_tracer'
12
13
  autoload :CLI, 'ree/cli'
13
14
  autoload :Container, 'ree/container'
@@ -22,6 +23,7 @@ module Ree
22
23
  autoload :LinkDSL, 'ree/link_dsl'
23
24
  autoload :LinkImportBuilder, 'ree/dsl/link_import_builder'
24
25
  autoload :LinkValidator, 'ree/core/link_validator'
26
+ autoload :MethodAddedHook, 'ree/method_added_hook'
25
27
  autoload :Object, 'ree/core/object'
26
28
  autoload :ObjectCompiler, 'ree/object_compiler'
27
29
  autoload :ObjectDsl, 'ree/dsl/object_dsl'
@@ -122,6 +124,14 @@ module Ree
122
124
  !!@benchmark_mode
123
125
  end
124
126
 
127
+ def method_added_plugins
128
+ @method_added_plugins ||= []
129
+ end
130
+
131
+ def register_method_added_plugin(plugin_class)
132
+ method_added_plugins << plugin_class
133
+ end
134
+
125
135
  def set_logger_debug
126
136
  logger.level = Logger::DEBUG
127
137
  end
@@ -234,4 +244,7 @@ module Ree
234
244
  end
235
245
 
236
246
  require_relative 'ree/dsl/object_hooks'
237
- require_relative 'ree/dsl/package_require'
247
+ require_relative 'ree/dsl/package_require'
248
+
249
+ Ree.register_method_added_plugin(Ree::BenchmarkMethodPlugin)
250
+ Ree.register_method_added_plugin(Ree::Contracts::MethodDecorator)
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ree
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.1
4
+ version: 1.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ruslan Gatiyatov
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2026-02-08 00:00:00.000000000 Z
10
+ date: 2026-02-09 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: commander
@@ -75,6 +75,7 @@ files:
75
75
  - lib/ree.rb
76
76
  - lib/ree/args.rb
77
77
  - lib/ree/bean_dsl.rb
78
+ - lib/ree/benchmark_method_plugin.rb
78
79
  - lib/ree/benchmark_tracer.rb
79
80
  - lib/ree/cli.rb
80
81
  - lib/ree/cli/generate_package.rb
@@ -166,6 +167,7 @@ files:
166
167
  - lib/ree/handlers/template_handler.rb
167
168
  - lib/ree/inspectable.rb
168
169
  - lib/ree/link_dsl.rb
170
+ - lib/ree/method_added_hook.rb
169
171
  - lib/ree/object_compiler.rb
170
172
  - lib/ree/package_dsl.rb
171
173
  - lib/ree/rspec_link_dsl.rb