orthoses 1.7.0 → 1.9.0

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.
Files changed (94) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +10 -3
  3. data/lib/orthoses/attribute.rb +28 -55
  4. data/lib/orthoses/builder.rb +7 -3
  5. data/lib/orthoses/constant.rb +0 -1
  6. data/lib/orthoses/content/duplication_checker.rb +67 -35
  7. data/lib/orthoses/content/environment.rb +1 -1
  8. data/lib/orthoses/content.rb +20 -13
  9. data/lib/orthoses/create_file_by_name.rb +38 -7
  10. data/lib/orthoses/lazy_trace_point.rb +37 -43
  11. data/lib/orthoses/missing_name.rb +81 -0
  12. data/lib/orthoses/mixin.rb +29 -34
  13. data/lib/orthoses/rbs_prototype_runtime.rb +23 -4
  14. data/lib/orthoses/trace/attribute.rb +26 -8
  15. data/lib/orthoses/utils.rb +2 -2
  16. data/lib/orthoses/version.rb +1 -1
  17. data/lib/orthoses.rb +1 -0
  18. data/sig/orthoses/_call.rbs +1 -1
  19. data/sig/orthoses/_middle_ware.rbs +1 -1
  20. data/sig/orthoses/attribute/hook.rbs +1 -1
  21. data/sig/orthoses/attribute.rbs +3 -3
  22. data/sig/orthoses/autoload/hook.rbs +1 -1
  23. data/sig/orthoses/autoload.rbs +2 -1
  24. data/sig/orthoses/builder/call_logable.rbs +1 -1
  25. data/sig/orthoses/builder.rbs +7 -4
  26. data/sig/orthoses/call_tracer/capturable.rbs +1 -1
  27. data/sig/orthoses/call_tracer/capture.rbs +2 -2
  28. data/sig/orthoses/call_tracer/lazy.rbs +4 -2
  29. data/sig/orthoses/call_tracer.rbs +3 -1
  30. data/sig/orthoses/const_load_error.rbs +4 -1
  31. data/sig/orthoses/constant.rbs +3 -3
  32. data/sig/orthoses/content/array_io.rbs +2 -1
  33. data/sig/orthoses/content/duplication_checker.rbs +10 -3
  34. data/sig/orthoses/content/environment.rbs +11 -6
  35. data/sig/orthoses/content/header_builder.rbs +3 -1
  36. data/sig/orthoses/content.rbs +7 -1
  37. data/sig/orthoses/create_file_by_name.rbs +8 -3
  38. data/sig/orthoses/delegate_class/hook.rbs +1 -1
  39. data/sig/orthoses/delegate_class.rbs +2 -2
  40. data/sig/orthoses/filter.rbs +4 -3
  41. data/sig/orthoses/lazy_trace_point/method_added_hook.rbs +6 -0
  42. data/sig/orthoses/lazy_trace_point/signleton_method_added_hook.rbs +5 -0
  43. data/sig/orthoses/lazy_trace_point.rbs +7 -7
  44. data/sig/orthoses/load_rbs.rbs +3 -3
  45. data/sig/orthoses/missing_name/missing_class.rbs +8 -0
  46. data/sig/orthoses/missing_name/missing_module.rbs +8 -0
  47. data/sig/orthoses/missing_name.rbs +7 -0
  48. data/sig/orthoses/mixin/hook.rbs +2 -2
  49. data/sig/orthoses/mixin.rbs +6 -4
  50. data/sig/orthoses/name_space_error.rbs +1 -1
  51. data/sig/orthoses/object_space_all.rbs +2 -2
  52. data/sig/orthoses/outputable/avoid_recursive_ancestor_error.rbs +2 -2
  53. data/sig/orthoses/outputable/constantizable_filter.rbs +2 -1
  54. data/sig/orthoses/outputable/uniq_content_body.rbs +2 -1
  55. data/sig/orthoses/outputable.rbs +3 -1
  56. data/sig/orthoses/path_helper.rbs +3 -1
  57. data/sig/orthoses/pp.rbs +2 -1
  58. data/sig/orthoses/rbs_prototype_rb.rbs +6 -4
  59. data/sig/orthoses/rbs_prototype_runtime.rbs +10 -3
  60. data/sig/orthoses/sort.rbs +2 -1
  61. data/sig/orthoses/store.rbs +2 -1
  62. data/sig/orthoses/tap.rbs +3 -2
  63. data/sig/orthoses/trace/attribute/hook.rbs +11 -0
  64. data/sig/orthoses/trace/attribute.rbs +4 -1
  65. data/sig/orthoses/trace/method/info.rbs +1 -1
  66. data/sig/orthoses/trace/method.rbs +6 -1
  67. data/sig/orthoses/trace/targetable.rbs +1 -1
  68. data/sig/orthoses/trace.rbs +3 -1
  69. data/sig/orthoses/utils/type_list.rbs +2 -1
  70. data/sig/orthoses/utils/underscore.rbs +1 -1
  71. data/sig/orthoses/utils.rbs +4 -2
  72. data/sig/orthoses/walk.rbs +3 -3
  73. data/sig/orthoses/writer.rbs +3 -2
  74. data/sig/orthoses.rbs +6 -6
  75. metadata +9 -21
  76. data/Gemfile +0 -16
  77. data/Gemfile.lock +0 -86
  78. data/examples/minitest/Rakefile +0 -56
  79. data/examples/rack-test/Gemfile +0 -8
  80. data/examples/rack-test/Gemfile.lock +0 -30
  81. data/examples/rack-test/Rakefile +0 -7
  82. data/examples/rack-test/generate.rb +0 -31
  83. data/examples/rack-test/out/rack/test/cookie.rbs +0 -28
  84. data/examples/rack-test/out/rack/test/cookie_jar.rbs +0 -28
  85. data/examples/rack-test/out/rack/test/fake_app.rbs +0 -13
  86. data/examples/rack-test/out/rack/test/input_rewinder.rbs +0 -8
  87. data/examples/rack-test/out/rack/test/methods.rbs +0 -98
  88. data/examples/rack-test/out/rack/test/session.rbs +0 -186
  89. data/examples/rack-test/out/rack/test/uploaded_file.rbs +0 -18
  90. data/examples/rack-test/out/rack/test/utils.rbs +0 -112
  91. data/examples/rack-test/out/rack/test.rbs +0 -3
  92. data/examples/simple_middleware.rb +0 -11
  93. data/examples/simple_middleware.rbs +0 -7
  94. data/orthoses.gemspec +0 -36
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e2915ad2b524676ac4ea67e61fcc90713bdaa8e79cb2ff01aeba74fe54b4885d
4
- data.tar.gz: 0f8823a7e88b4030eb6554ac9f35a068730ac944378832f923c99cf3b4e8e6be
3
+ metadata.gz: dd4d2682b08812b2f8fa633aa299702d38fd509439e4e2ac0b8751a64358b32a
4
+ data.tar.gz: 28118eed7d772c490670f83277ccdabc4d2938ca329f5051f8aebd40be1f08a6
5
5
  SHA512:
6
- metadata.gz: '06038e9dd28433a1c9358915720fe942f597caee2d68651ce7c54b47136633cc1d55288c7b8053766f393bacda9974b1ee662337f80d78b0796d405453b2cc32'
7
- data.tar.gz: 92074fd8665515453126f44627604f7fd3da1dcb7a85ec25b1ef51424c6797f9b1f67925d2349db05b7ce7aace06dc7f3f2d2be737cfb5399383bed4f846b11f
6
+ metadata.gz: 1f4262320cb3d9ef5304cbdb53af7cb0e0895bac03e7d98697cf38fc73dabc25b579c6fd4a213ae5c0d97d1763f88dabde2f7f1e6455a084c424ef6b042a3acb
7
+ data.tar.gz: 35e2b8378b602ad6b853ba7f543d807aab514d0f2a7d8b931230912084d40c7222e45d09cfe4b3eddd614f62bcce578cac2b6e14dac08f5578089ecbb7d7895c
data/README.md CHANGED
@@ -29,11 +29,13 @@ For example, You can write script in Rakefile.
29
29
  require 'orthoses'
30
30
 
31
31
  namespace :rbs do
32
- desc "build RBS to sig/out"
32
+ desc "build RBS to sig/orthoses"
33
33
  task :build do
34
34
  Orthoses::Builder.new do
35
35
  use Orthoses::CreateFileByName,
36
- base_dir: Rails.root.join("sig/out"),
36
+ depth: 1,
37
+ to: "sig/orthoses",
38
+ rmtree: true,
37
39
  header: "# !!! GENERATED CODE !!!"
38
40
  use Orthoses::Filter do |name, _content|
39
41
  path, _lineno = Object.const_source_location(name)
@@ -53,7 +55,7 @@ namespace :rbs do
53
55
  end
54
56
  ```
55
57
 
56
- Then, you can see the result files under `sig/out`.
58
+ Then, you can see the result files under `sig/orthoses`.
57
59
 
58
60
  ## Utils
59
61
 
@@ -177,6 +179,11 @@ Sort signatures by class/module.
177
179
 
178
180
  Trace the argument and return value objects when the method is actually called and determine the type.
179
181
 
182
+ ## Orthoses::MissingName
183
+
184
+ Completes undefined class/module names.
185
+ If it is unknown whether it is a class or a module, it is defined as an empty module, and if it is a superclass, it is defined as an empty class.
186
+
180
187
  ## Development
181
188
 
182
189
  After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -1,98 +1,71 @@
1
1
  module Orthoses
2
2
  class Attribute
3
+ CALL_GRAPH = {}
4
+
3
5
  module Hook
4
6
  def attr(*names)
7
+ (CALL_GRAPH[self]||=[]) << [:attr, names]
5
8
  super
6
9
  end
7
10
 
8
11
  def attr_accessor(*names)
12
+ (CALL_GRAPH[self]||=[]) << [:attr_accessor, names]
9
13
  super
10
14
  end
11
15
 
12
16
  def attr_reader(*names)
17
+ (CALL_GRAPH[self]||=[]) << [:attr_reader, names]
13
18
  super
14
19
  end
15
20
 
16
21
  def attr_writer(*names)
22
+ (CALL_GRAPH[self]||=[]) << [:attr_writer, names]
17
23
  super
18
24
  end
19
25
  end
20
26
 
21
27
  def initialize(loader)
28
+ CALL_GRAPH.clear
22
29
  @loader = loader
23
30
  end
24
31
 
25
32
  def call
26
33
  ::Module.prepend(Hook)
27
34
 
28
- attr = CallTracer.new
29
- attr_accessor = CallTracer.new
30
- attr_reader = CallTracer.new
31
- attr_writer = CallTracer.new
32
-
33
- store = attr.trace(Hook.instance_method(:attr)) do
34
- attr_accessor.trace(Hook.instance_method(:attr_accessor)) do
35
- attr_reader.trace(Hook.instance_method(:attr_reader)) do
36
- attr_writer.trace(Hook.instance_method(:attr_writer)) do
37
- @loader.call
38
- end
39
- end
40
- end
41
- end
35
+ store = @loader.call
42
36
 
43
- attr.captures.each do |capture|
44
- m = capture.method.receiver.to_s.match(/#<Class:([\w:]+)>/)
37
+ CALL_GRAPH.dup.each do |base_mod, calls|
38
+ m = base_mod.to_s.match(/#<Class:([\w:]+)>/)
45
39
  if m && m[1]
46
40
  receiver_name = m[1]
47
41
  prefix = "self."
48
42
  else
49
- receiver_name = Utils.module_name(capture.method.receiver) or next
43
+ receiver_name = Utils.module_name(base_mod) or next
50
44
  prefix = nil
51
45
  end
52
46
  content = store[receiver_name]
53
- names = capture.argument[:names]
54
- if names[1].equal?(true)
55
- content << "attr_accessor #{prefix}#{names[0]}: untyped"
56
- elsif names[1].equal?(false)
57
- content << "attr_reader #{prefix}#{names[0]}: untyped"
58
- else
59
- names.each do |name|
60
- content << "attr_reader #{prefix}#{name}: untyped"
47
+
48
+ calls.each do |(a, names)|
49
+ case a
50
+ when :attr
51
+ if names[1].equal?(true)
52
+ content << "attr_accessor #{prefix}#{names[0]}: untyped"
53
+ elsif names[1].equal?(false)
54
+ content << "attr_reader #{prefix}#{names[0]}: untyped"
55
+ else
56
+ names.each do |name|
57
+ content << "attr_reader #{prefix}#{name}: untyped"
58
+ end
59
+ end
60
+ else
61
+ names.each do |name|
62
+ content << "#{a} #{prefix}#{name}: untyped"
63
+ end
61
64
  end
62
65
  end
63
66
  end
64
67
 
65
- each_definition(attr_accessor) do |receiver_name, name|
66
- store[receiver_name] << "attr_accessor #{name}: untyped"
67
- end
68
- each_definition(attr_reader) do |receiver_name, name|
69
- store[receiver_name] << "attr_reader #{name}: untyped"
70
- end
71
- each_definition(attr_writer) do |receiver_name, name|
72
- store[receiver_name] << "attr_writer #{name}: untyped"
73
- end
74
-
75
68
  store
76
69
  end
77
-
78
- private
79
-
80
- def each_definition(call_tracer)
81
- call_tracer.captures.each do |capture|
82
- m = capture.method.receiver.to_s.match(/#<Class:([\w:]+)>/)
83
- names = capture.argument[:names]
84
- if m && m[1]
85
- receiver_name = m[1]
86
- names.each do |name|
87
- yield [receiver_name, "self.#{name}"]
88
- end
89
- else
90
- receiver_name = Utils.module_name(capture.method.receiver) or next
91
- names.each do |name|
92
- yield [receiver_name, name]
93
- end
94
- end
95
- end
96
- end
97
70
  end
98
71
  end
@@ -13,7 +13,7 @@ module Orthoses
13
13
  class Builder
14
14
  def initialize(&block)
15
15
  @use = []
16
- @run = nil
16
+ @runner = nil
17
17
  instance_eval(&block) if block
18
18
  end
19
19
 
@@ -37,7 +37,11 @@ module Orthoses
37
37
 
38
38
  def run(loader)
39
39
  use Store
40
- @run = proc do
40
+ reset_runner(loader)
41
+ end
42
+
43
+ def reset_runner(loader)
44
+ @runner = proc do
41
45
  Orthoses.logger.info("[loader].call start")
42
46
  loader.call.tap do
43
47
  Orthoses.logger.info("[loader].call end")
@@ -46,7 +50,7 @@ module Orthoses
46
50
  end
47
51
 
48
52
  def to_loader
49
- @use.reverse.inject(@run) { |current, next_proc| next_proc[current] }
53
+ @use.reverse.inject(@runner) { |current, next_proc| next_proc[current] }
50
54
  end
51
55
 
52
56
  def call
@@ -14,7 +14,6 @@ module Orthoses
14
14
  @loader.call.tap do |store|
15
15
  will_add_key_and_content = []
16
16
  store.each do |name, _|
17
- next if name == :Module
18
17
  next if name.start_with?('#<')
19
18
 
20
19
  begin
@@ -4,71 +4,103 @@ module Orthoses
4
4
  class DuplicationChecker
5
5
  def initialize(decl, env: nil)
6
6
  @decl = decl
7
- @env = env || Utils.rbs_environment
7
+ @known_env = env || Utils.rbs_environment
8
+ @builder = RBS::DefinitionBuilder.new(env: @known_env)
9
+ @uniq_map = {}
8
10
  end
9
11
 
10
12
  def update_decl
11
13
  return unless @decl.respond_to?(:members)
12
14
 
13
- uniq_map = uniq_members
14
- drop_set_method_definition(uniq_map)
15
- drop_known_method_definition(uniq_map)
15
+ uniq_members
16
+ drop_set_method_definition
17
+ drop_by_singleton_instance
18
+ drop_known_method_definition
19
+ drop_known_const_definition
16
20
 
17
- @decl.members.replace(uniq_map.values)
21
+ @decl.members.replace(@uniq_map.values)
22
+
23
+ @uniq_map.clear
18
24
  end
19
25
 
20
26
  private
21
27
 
22
28
  def uniq_members
23
- {}.tap do |uniq_map|
24
- @decl.members.each do |member|
25
- if member.instance_of?(RBS::AST::Members::MethodDefinition) && member.overloading?
26
- # avoid to duplicate and keep order
27
- uniq_map[Object.new] = member
28
- else
29
- key = member_key(member)
30
- drop_member = uniq_map[key]
31
- uniq_map[key] = member
32
- if drop_member
33
- Orthoses.logger.info("#{@decl.name} \"#{member.location.source}\" was droped since duplication")
34
- end
29
+ @decl.members.each do |member|
30
+ if member.instance_of?(RBS::AST::Members::MethodDefinition) && member.overloading?
31
+ # avoid to duplicate and keep order
32
+ @uniq_map[Object.new] = member
33
+ else
34
+ key = member_key(member)
35
+ drop_member = @uniq_map[key]
36
+ @uniq_map[key] = member
37
+ if drop_member
38
+ Orthoses.logger.info("#{@decl.name} \"#{member.location.source}\" was droped since duplication")
35
39
  end
36
40
  end
37
41
  end
38
42
  end
39
43
 
40
- def drop_set_method_definition(uniq_map)
41
- attr_accessors = uniq_map.values.grep(RBS::AST::Members::AttrAccessor)
44
+ def drop_by_singleton_instance
45
+ singleton_instances = @uniq_map.values.select { |v| v.instance_of?(RBS::AST::Members::MethodDefinition) && v.kind == :singleton_instance }
46
+ singleton_instances.each do |member|
47
+ @uniq_map.delete([member.class, member.name, :instance])
48
+ @uniq_map.delete([member.class, member.name, :singleton])
49
+ end
50
+ end
51
+
52
+ def drop_set_method_definition
53
+ attr_accessors = @uniq_map.values.grep(RBS::AST::Members::AttrAccessor)
42
54
  attr_accessors.each do |member|
43
- uniq_map.delete([RBS::AST::Members::MethodDefinition, :"#{member.name}=", member.kind])
55
+ @uniq_map.delete([RBS::AST::Members::MethodDefinition, :"#{member.name}=", member.kind])
44
56
  end
45
57
  end
46
58
 
47
- def drop_known_method_definition(uniq_map)
59
+ def drop_known_method_definition
48
60
  decl_name = @decl.name.absolute!
49
- if m_entry = @env.class_decls[decl_name]
61
+ if m_entry = @known_env.class_decls[decl_name]
50
62
  m_entry.decls.each do |d|
51
- d.decl.members.each do |member|
52
- case member
53
- when RBS::AST::Members::LocationOnly
54
- # ignore
55
- when RBS::AST::Members::AttrAccessor
56
- uniq_map.delete([RBS::AST::Members::MethodDefinition, member.name, member.kind])
57
- uniq_map.delete([RBS::AST::Members::MethodDefinition, :"#{member.name}=", member.kind])
58
- else
59
- uniq_map.delete(member_key(member))
60
- end
63
+ drop_known_method_definition_recur(d)
64
+ end
65
+ end
66
+ end
67
+
68
+ def drop_known_method_definition_recur(d)
69
+ d.decl.members.each do |member|
70
+ case member
71
+ when RBS::AST::Members::MethodDefinition
72
+ if member.kind == :singleton_instance
73
+ @uniq_map.delete([member.class, member.name, :instance])
74
+ @uniq_map.delete([member.class, member.name, :singleton])
75
+ end
76
+ @uniq_map.delete(member_key(member))
77
+ when RBS::AST::Members::LocationOnly
78
+ # ignore
79
+ when RBS::AST::Members::AttrAccessor
80
+ @uniq_map.delete([RBS::AST::Members::MethodDefinition, member.name, member.kind])
81
+ @uniq_map.delete([RBS::AST::Members::MethodDefinition, :"#{member.name}=", member.kind])
82
+ when RBS::AST::Members::Include
83
+ if member.name.interface?
84
+ # If interface is included, it is shared in the same namespace.
85
+ drop_known_method_definition_recur(@known_env.interface_decls[member.name])
86
+ else
87
+ @uniq_map.delete(member_key(member))
61
88
  end
89
+ else
90
+ @uniq_map.delete(member_key(member))
62
91
  end
63
92
  end
93
+ end
64
94
 
65
- constants_in_uniq_map = uniq_map.select do |key, value|
95
+ def drop_known_const_definition
96
+ decl_name = @decl.name.absolute!
97
+ constants_in_uniq_map = @uniq_map.select do |key, value|
66
98
  value.kind_of?(RBS::AST::Declarations::Constant)
67
99
  end
68
100
  constants_in_uniq_map.each do |key, value|
69
101
  type_name = decl_name + value.name
70
- if @env.constant_decls[type_name]
71
- uniq_map.delete(key)
102
+ if @known_env.constant_decls[type_name]
103
+ @uniq_map.delete(key)
72
104
  end
73
105
  end
74
106
  end
@@ -42,7 +42,7 @@ module Orthoses
42
42
  def write_to(store:)
43
43
  each do |add_content|
44
44
  content = store[add_content.name]
45
- content.header ||= add_content.header
45
+ content.header = add_content.header
46
46
  content.comment ||= add_content.comment
47
47
  content.concat(add_content.body)
48
48
  end
@@ -102,19 +102,7 @@ module Orthoses
102
102
 
103
103
  case val
104
104
  when Class
105
- superclass =
106
- if val.superclass && val.superclass != Object
107
- super_module_name = Utils.module_name(val.superclass)
108
-
109
- if super_module_name && super_module_name != "Random::Base" # https://github.com/ruby/rbs/pull/977
110
- " < ::#{super_module_name}#{temporary_type_params(super_module_name)}"
111
- else
112
- nil
113
- end
114
- else
115
- nil
116
- end
117
- self.header = "class #{Utils.module_name(val)}#{type_params(name)}#{superclass}"
105
+ self.header = "class #{Utils.module_name(val)}#{type_params(name)}#{build_super_class(val)}"
118
106
  when Module
119
107
  self.header = "module #{Utils.module_name(val)}#{type_params(name)}"
120
108
  else
@@ -124,6 +112,25 @@ module Orthoses
124
112
 
125
113
  private
126
114
 
115
+ def build_super_class(val)
116
+ return nil unless val.superclass && val.superclass != Object
117
+
118
+ begin
119
+ # check private const
120
+ eval(val.superclass.to_s)
121
+ rescue NameError
122
+ return nil
123
+ end
124
+
125
+ super_module_name = Utils.module_name(val.superclass)
126
+ return nil unless super_module_name
127
+
128
+ # https://github.com/ruby/rbs/pull/977
129
+ return nil unless super_module_name != "Random::Base"
130
+
131
+ " < ::#{super_module_name}#{temporary_type_params(super_module_name)}"
132
+ end
133
+
127
134
  class ArrayIO
128
135
  def initialize
129
136
  @outs = []
@@ -6,31 +6,62 @@ module Orthoses
6
6
  class CreateFileByName
7
7
  prepend Outputable
8
8
 
9
- def initialize(loader, base_dir:, header: nil, if: nil)
9
+ def initialize(loader, base_dir: nil, to: nil, header: nil, if: nil, depth: nil, rmtree: false)
10
+ unless base_dir.nil?
11
+ warn "[Orthoses::CreateFileByName] base_dir: option is deprecated. Please use to: option instead."
12
+ end
13
+
14
+ to = to || base_dir
15
+ unless to
16
+ raise ArgumentError, "should set to: option"
17
+ end
18
+
19
+ relative_path_from_pwd = Pathname(to).expand_path.relative_path_from(Pathname.pwd).to_s
20
+ unless relative_path_from_pwd == "." || !relative_path_from_pwd.match?(%r{\A[/\.]})
21
+ raise ArgumentError, "to=\"#{to}\" should be under current dir=\"#{Pathname.pwd}\"."
22
+ end
23
+
10
24
  @loader = loader
11
- @base_dir = base_dir
25
+ @to = to
12
26
  @header = header
27
+ @depth = depth
28
+ @rmtree = rmtree
13
29
  @if = binding.local_variable_get(:if)
14
30
  end
15
31
 
16
32
  using Utils::Underscore
17
33
 
18
34
  def call
35
+ if @rmtree
36
+ Orthoses.logger.debug("Remove dir #{@to} since `rmtree: true`")
37
+ Pathname(@to).rmtree rescue nil
38
+ end
39
+
19
40
  store = @loader.call
20
41
 
21
- store.each do |name, content|
22
- next unless @if.nil? || @if.call(name, content)
42
+ store.select! do |name, content|
43
+ @if.nil? || @if.call(name, content)
44
+ end
45
+ grouped = store.group_by do |name, _|
46
+ splitted = name.to_s.split('::')
47
+ (@depth ? splitted[0, @depth] : splitted).join('::')
48
+ end
23
49
 
24
- file_path = Pathname("#{@base_dir}/#{name.to_s.split('::').map(&:underscore).join('/')}.rbs")
50
+ grouped.each do |group_name, group|
51
+ file_path = Pathname("#{@to}/#{group_name.split('::').map(&:underscore).join('/')}.rbs")
25
52
  file_path.dirname.mkpath
26
53
  file_path.open('w+') do |out|
27
54
  if @header
28
55
  out.puts @header
29
56
  out.puts
30
57
  end
31
- out.puts content.to_rbs
58
+ group.sort!
59
+ contents = group.map do |(name, content)|
60
+ content.to_rbs
61
+ end.join("\n")
62
+ out.puts contents
63
+ Orthoses.logger.info("Generate file to #{file_path.to_s}")
32
64
  end
33
- Orthoses.logger.info("Generate file to #{file_path.to_s}")
34
65
  end
35
66
  end
36
67
  end
@@ -10,32 +10,44 @@ module Orthoses
10
10
  # ...
11
11
  # end
12
12
  class LazyTracePoint < TracePoint
13
+ METHOD_METHOD = ::Kernel.instance_method(:method)
14
+ INSTANCE_METHOD_METHOD = ::Module.instance_method(:instance_method)
15
+ UNBOUND_NAME_METHOD = ::Module.instance_method(:name)
16
+ METHOD_ADDED_HOOKS = {}
17
+ SINGLETON_METHOD_ADDED_HOOKS = {}
18
+
13
19
  # for TracePoint target
14
- ::Module.prepend(Module.new{
20
+ module MethodAddedHook
15
21
  def method_added(id)
22
+ begin
23
+ if h = METHOD_ADDED_HOOKS[id]
24
+ if mod_name = UNBOUND_NAME_METHOD.bind(self).call
25
+ h[mod_name]&.call(self, id)
26
+ end
27
+ end
28
+ rescue TypeError => e
29
+ end
16
30
  super
17
31
  end
18
- })
19
- ::BasicObject.prepend(Module.new{
32
+ end
33
+ ::Module.prepend MethodAddedHook
34
+
35
+ module SignletonMethodAddedHook
20
36
  def singleton_method_added(id)
37
+ begin
38
+ if h = SINGLETON_METHOD_ADDED_HOOKS[id]
39
+ if mod_name = UNBOUND_NAME_METHOD.bind(self).call
40
+ h[mod_name]&.call(self, id)
41
+ end
42
+ end
43
+ rescue TypeError => e
44
+ end
21
45
  super
22
46
  end
23
- })
24
-
25
- METHOD_METHOD = ::Kernel.instance_method(:method)
26
- INSTANCE_METHOD_METHOD = ::Module.instance_method(:instance_method)
27
- UNBOUND_NAME_METHOD = ::Module.instance_method(:name)
28
- METHOD_ADDED_METHOD = ::Module.instance_method(:method_added)
29
- SINGLETON_METHOD_ADDED_METHOD = ::BasicObject.instance_method(:singleton_method_added)
30
-
31
- def initialize(*events, &block)
32
- @mod_name = nil
33
- @instance_method_id = nil
34
- @singleton_method_id = nil
35
- super
36
47
  end
48
+ ::BasicObject.prepend SignletonMethodAddedHook
37
49
 
38
- def enable(target: nil, target_line: nil, target_thread: nil, &block)
50
+ def enable(target: nil, &block)
39
51
  return super unless target.kind_of?(String)
40
52
 
41
53
  case
@@ -59,43 +71,25 @@ module Orthoses
59
71
  private
60
72
 
61
73
  def trace_instance_method(&block)
62
- # try to load
63
74
  target = Object.const_get(@mod_name).instance_method(@instance_method_id)
64
75
  enable(target: target, &block)
65
76
  rescue NameError
66
- TracePoint.new(:call) do |tp|
67
- id = tp.binding.local_variable_get(:id)
68
- next unless id == @instance_method_id
69
-
70
- mod_name = UNBOUND_NAME_METHOD.bind(tp.self).call
71
-
72
- next unless mod_name
73
- next unless mod_name == @mod_name
74
-
75
- enable(target: INSTANCE_METHOD_METHOD.bind(tp.self).call(id))
76
- tp.disable
77
- end.enable(target: METHOD_ADDED_METHOD, &block)
77
+ (METHOD_ADDED_HOOKS[@instance_method_id] ||= {})[@mod_name] = ->(const, id) {
78
+ enable(target: INSTANCE_METHOD_METHOD.bind(const).call(id))
79
+ }
80
+ block&.call
78
81
  ensure
79
82
  disable
80
83
  end
81
84
 
82
85
  def trace_singleton_method(&block)
83
- # try to load
84
86
  target = Object.const_get(@mod_name).method(@singleton_method_id)
85
87
  enable(target: target, &block)
86
88
  rescue NameError
87
- TracePoint.new(:call) do |tp|
88
- id = tp.binding.local_variable_get(:id)
89
- next unless id == @singleton_method_id
90
-
91
- mod_name = UNBOUND_NAME_METHOD.bind(tp.self).call
92
-
93
- next unless mod_name
94
- next unless mod_name == @mod_name
95
-
96
- enable(target: METHOD_METHOD.bind(tp.self).call(id))
97
- tp.disable
98
- end.enable(target: SINGLETON_METHOD_ADDED_METHOD, &block)
89
+ (SINGLETON_METHOD_ADDED_HOOKS[@singleton_method_id] ||= {})[@mod_name] = ->(const, id) {
90
+ enable(target: METHOD_METHOD.bind(const).call(id))
91
+ }
92
+ block&.call
99
93
  ensure
100
94
  disable
101
95
  end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Orthoses
4
+ class MissingName
5
+ def initialize(loader)
6
+ @loader = loader
7
+ end
8
+
9
+ def call
10
+ @loader.call.tap do |store|
11
+ MissingClass.new(store).call
12
+ MissingModule.new(store).call
13
+ end
14
+ end
15
+
16
+ private
17
+
18
+ class MissingClass
19
+ def initialize(store)
20
+ @store = store
21
+ end
22
+
23
+ def call
24
+ @store.values.each do |content|
25
+ recur(content)
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def recur(content)
32
+ content.auto_header
33
+ if content.header && content.header.include?("<")
34
+ _, superclass = content.header.split(/\s*<\s*/, 2)
35
+ superclass.sub!(/\[.*/, "")
36
+ new_name = TypeName(superclass).relative!.to_s
37
+ recur(@store[new_name])
38
+ end
39
+ end
40
+ end
41
+
42
+ class MissingModule
43
+ def initialize(store)
44
+ @store = store
45
+ end
46
+
47
+ def call
48
+ missings = []
49
+ @store.keys.each do |key_name|
50
+ missings.concat split_name(key_name)
51
+ end
52
+ missings.uniq!
53
+ missings.each do |missing|
54
+ @store[missing].header = "module #{missing}"
55
+ end
56
+ end
57
+
58
+ private
59
+
60
+ def split_name(key_name)
61
+ ret = []
62
+
63
+ type_name = TypeName(key_name).relative!
64
+ if !Utils.rbs_defined_class?(type_name.to_s) && !@store.has_key?(type_name.to_s)
65
+ ret << type_name.to_s
66
+ end
67
+
68
+ path = type_name.namespace.path
69
+ path.each_with_index do |sym, index|
70
+ name = path[0, index + 1].join("::")
71
+ next if name.empty?
72
+ if !Utils.rbs_defined_class?(name) && !@store.has_key?(name)
73
+ ret << name
74
+ end
75
+ end
76
+
77
+ ret
78
+ end
79
+ end
80
+ end
81
+ end