orthoses-rails 0.8.0 → 1.0.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 (56) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +13 -65
  3. data/lib/generators/orthoses/rails/install_generator.rb +18 -0
  4. data/lib/orthoses/active_record/belongs_to.rb +15 -4
  5. data/lib/orthoses/active_record/enum.rb +5 -0
  6. data/lib/orthoses/active_record/generated_attribute_methods.rb +47 -55
  7. data/lib/orthoses/active_record/has_many.rb +15 -4
  8. data/lib/orthoses/active_record/has_one.rb +12 -2
  9. data/lib/orthoses/active_record/relation.rb +56 -0
  10. data/lib/orthoses/active_record.rb +36 -0
  11. data/lib/orthoses/active_support/delegation.rb +20 -9
  12. data/lib/orthoses/active_support/time_with_zone.rb +35 -28
  13. data/lib/orthoses/rails/application.rb +33 -0
  14. data/lib/orthoses/rails/version.rb +1 -1
  15. data/lib/orthoses/rails.rb +3 -2
  16. metadata +10 -47
  17. data/examples/rails/Gemfile +0 -16
  18. data/examples/rails/Gemfile.lock +0 -179
  19. data/examples/rails/Rakefile +0 -328
  20. data/examples/rails/config/database.yml +0 -8
  21. data/examples/rails/config/storage.yml +0 -3
  22. data/examples/rails/known_sig/actionpack/7.0/action_controller/metal.rbs +0 -4
  23. data/examples/rails/known_sig/activemodel/7.0/active_model/serialization.rbs +0 -4
  24. data/examples/rails/known_sig/activemodel/7.0/active_model/validations.rbs +0 -4
  25. data/examples/rails/known_sig/activerecord/6.0/_active_record_relation.rbs +0 -49
  26. data/examples/rails/known_sig/activerecord/6.0/_active_record_relation_class_methods.rbs +0 -45
  27. data/examples/rails/known_sig/activerecord/6.0/active_record/result.rbs +0 -5
  28. data/examples/rails/known_sig/activerecord/6.1/_active_record_relation.rbs +0 -49
  29. data/examples/rails/known_sig/activerecord/6.1/_active_record_relation_class_methods.rbs +0 -45
  30. data/examples/rails/known_sig/activerecord/6.1/active_record/result.rbs +0 -5
  31. data/examples/rails/known_sig/activerecord/7.0/_active_record_relation.rbs +0 -49
  32. data/examples/rails/known_sig/activerecord/7.0/_active_record_relation_class_methods.rbs +0 -45
  33. data/examples/rails/known_sig/activerecord/7.0/active_record/encryption/context.rbs +0 -9
  34. data/examples/rails/known_sig/activerecord/7.0/active_record/result.rbs +0 -5
  35. data/examples/rails/known_sig/activesupport/7.0/active_support/callbacks/callback_chain.rbs +0 -9
  36. data/examples/rails/known_sig/activesupport/7.0/active_support/duration.rbs +0 -4
  37. data/examples/rails/known_sig/activesupport/7.0/active_support/hash_with_indifferent_access.rbs +0 -6
  38. data/examples/rails/known_sig/activesupport/7.0/active_support/multibyte/chars.rbs +0 -7
  39. data/examples/rails/known_sig/activesupport/7.0/active_support/time_with_zone.rbs +0 -5
  40. data/examples/rails/known_sig/activesupport/7.0/active_support/time_zone.rbs +0 -3
  41. data/examples/rails/known_sig/activesupport/7.0/date.rbs +0 -6
  42. data/examples/rails/known_sig/activesupport/7.0/date_and_time/zones.rbs +0 -4
  43. data/examples/rails/known_sig/activesupport/7.0/hash_with_indifferent_access.rbs +0 -2
  44. data/examples/rails/known_sig/activesupport/7.0/integer.rbs +0 -9
  45. data/examples/rails/known_sig/activesupport/7.0/numeric.rbs +0 -15
  46. data/examples/rails/known_sig/activesupport/7.0/string.rbs +0 -4
  47. data/examples/rails/known_sig/activesupport/7.0/time.rbs +0 -46
  48. data/examples/rails/tasks/action_pack.rake +0 -141
  49. data/examples/rails/tasks/action_view.rake +0 -77
  50. data/examples/rails/tasks/active_job.rake +0 -99
  51. data/examples/rails/tasks/active_model.rake +0 -46
  52. data/examples/rails/tasks/active_record.rake +0 -88
  53. data/examples/rails/tasks/active_storage.rake +0 -94
  54. data/examples/rails/tasks/active_support.rake +0 -99
  55. data/examples/rails/tasks/railties.rake +0 -34
  56. data/orthoses-rails.gemspec +0 -36
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 70f9def2ba0ccd5b1537ee8aa3ee68c2700e99d0f134d313069939cc1db3277d
4
- data.tar.gz: 922e2e304b3aa7f5cd34cb215ea3656c6a4c79c34875b7fd7ed691743230ea10
3
+ metadata.gz: 620d49df1787f2c79e641435da0cf872d0d8604930729bc746a72ec787677b78
4
+ data.tar.gz: fc06f6c06c7941f5ca36bbc1aabf6730e641e7cac2ea2a4f7ee1242284ebb4de
5
5
  SHA512:
6
- metadata.gz: 42f23b91ba796e225541f7da52cc74916a53ef584f1711d95457b0cc239441146678c4ebbb5a271a52710d928f55edba2d5032bec56a4fe9f9b0e47bf7e44c6a
7
- data.tar.gz: 1f9c63c9d2b9b8f11d820a27ef660d61cb727d3a3b3b644afa93fb3b7bdb48dbd150ca7ab96f636d02fb330e2de27c902862e04b89a514f20d5e3d51d5172dc8
6
+ metadata.gz: 63e8a69d07faab3bda23d71df987b87352e245b46d14cf824576c2a66502ad95e2523bc8f11eb8030e3325707b7b9b8e2e28103c560dba01bb0234ecf23fb54c
7
+ data.tar.gz: 13fd887d6564a51e19a0cdea17b80b5d3cfedd5a56f4096c1ec44a27dca3ba3e6a57c34dd22aa069a808fea34ec2eab1f265f0fa7d42f1394f8c00937642eb03
data/README.md CHANGED
@@ -3,38 +3,19 @@
3
3
  [Orthoses](https://github.com/ksss/orthoses) extension for Ruby on Rails.
4
4
  Orthoses::Rails automatically generates RBS for methods added by Rails.
5
5
 
6
- ## Features
7
-
8
- ### Orthoses::ActiveModel
9
-
10
- - `HasSecurePassword`
11
- - Add signatures that generated form `has_secure_password`.
12
-
13
- ### Orthoses::ActiveRecord
14
-
15
- - `BelongsTo`
16
- - Add signatures that generated form `belongs_to`.
17
- - `DelegatedType`
18
- - Add signatures that generated from `delegated_type`
19
- - `Enum`
20
- - Add signatures that generated from `enum`
21
- - `HasMany`
22
- - Add signatures that generated form `has_many`.
23
- - `HasOne`
24
- - Add signatures that generated form `has_one`.
25
- - `Scope`
26
- - Add signatures that generated form `scope`.
27
-
28
- ### Orthoses::ActiveSupport
29
-
30
- - `ClassAttribute`
31
- - Add signatures that generated form `class_attribute`.
32
- - `Configurable`
33
- - Add signatures that generated from `config_accessor`
34
- - `Delegation`
35
- - Add signatures that generated from `delegate`. The type definition of the method or instance variable specified by `to` is required.
36
- - `MattrAccessor`
37
- - Add signatures that generated form `mattr_accessor`, `cattr_accessor`, `mattr_reader`, `cattr_reader`, `mattr_writer` and `cattr_writer`.
6
+ ## Usage
7
+
8
+ Build your Rake task for RBS generation.
9
+
10
+ ```rb
11
+ $ bin/rails generate orthoses:rails:install
12
+ ```
13
+
14
+ Then run the rake task.
15
+
16
+ ```
17
+ $ bin/rails orthoses:rails
18
+ ```
38
19
 
39
20
  ## Installation
40
21
 
@@ -46,39 +27,6 @@ If bundler is not being used to manage dependencies, install the gem by executin
46
27
 
47
28
  $ gem install orthoses-rails
48
29
 
49
- ## Usage
50
-
51
- Build your Rake task for RBS generation.
52
-
53
- ```rb
54
- task :rbs_generate => :environment do
55
- Orthoses::Builder.new do
56
- use Orthoses::CreateFileByName
57
- base_dir: Rails.root.join("sig/out"),
58
- header: "# !!! GENERATED CODE !!!"
59
- use Orthoses::Filter do |name, _content|
60
- path, _lineno = Object.const_source_location(name)
61
- return false unless path
62
- %r{app/models}.match?(path)
63
- end
64
- use YourCustom::Middleware
65
- use Orthoses::ActiveModel::HasSecurePassword
66
- use Orthoses::ActiveRecord::BelongsTo
67
- use Orthoses::ActiveRecord::HasMany
68
- use Orthoses::ActiveRecord::HasOne
69
- use Orthoses::ActiveSupport::ClassAttribute
70
- use Orthoses::ActiveSupport::MattrAccessor
71
- use Orthoses::Mixin
72
- use Orthoses::Constant, strict: false
73
- use Orthoses::ObjectSpaceAll
74
- use Orthoses::Autoload
75
- run -> {
76
- Rails.application.eager_load!
77
- }
78
- end.call
79
- end
80
- ```
81
-
82
30
  ## Development
83
31
 
84
32
  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.
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails/generators/base'
4
+ require 'orthoses/rails'
5
+
6
+ module Orthoses
7
+ module Rails
8
+ module Generators
9
+ class InstallGenerator < ::Rails::Generators::Base
10
+ source_root File.expand_path('templates', __dir__)
11
+
12
+ def copy_rake_task_file
13
+ copy_file "rails.rake", "lib/tasks/orthoses/rails.rake"
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -15,7 +15,19 @@ module Orthoses
15
15
 
16
16
  lines = base.reflect_on_all_associations(:belongs_to).flat_map do |ref|
17
17
  # FIXME: Can I get list of class for polymorphic?
18
- type = ref.polymorphic? ? 'untyped' : ref.klass.to_s
18
+ type = if ref.polymorphic?
19
+ 'untyped'
20
+ else
21
+ begin
22
+ Utils.module_name(ref.klass) or next
23
+ rescue NameError => e
24
+ while e
25
+ Orthoses.logger.warn(e.message)
26
+ e = e.cause
27
+ end
28
+ next
29
+ end
30
+ end
19
31
  opt = "#{type}?"
20
32
 
21
33
  [
@@ -38,9 +50,8 @@ module Orthoses
38
50
  content.concat(lines)
39
51
  end
40
52
 
41
- store[base_name].tap do |content|
42
- store[base_name] << "include #{generated_association_methods}"
43
- end
53
+ sig = "include #{generated_association_methods}"
54
+ store[base_name] << sig if !store[base_name].body.include?(sig)
44
55
  end
45
56
  end
46
57
  end
@@ -20,6 +20,11 @@ module Orthoses
20
20
  enum.captures.each do |capture|
21
21
  base_name = Utils.module_name(capture.method.receiver) or next
22
22
 
23
+ sig = "include #{base_name}::ActiveRecord_Enum_EnumMethods"
24
+ if !store[base_name].body.include?(sig)
25
+ store[base_name] << sig
26
+ end
27
+
23
28
  if capture.argument[:definitions]
24
29
  # on rails 6
25
30
  definitions = capture.argument[:definitions].slice!(:_prefix, :_suffix, :_scopes, :_default)
@@ -3,6 +3,30 @@
3
3
  module Orthoses
4
4
  module ActiveRecord
5
5
  class GeneratedAttributeMethods
6
+ TARGET_TYPE_MAP = {
7
+ "attribute" => "() -> %<type>s",
8
+ "attribute=" => "(%<type>s) -> %<type>s",
9
+ "attribute?" => "() -> bool",
10
+ "attribute_before_last_save" => "() -> %<opt>s",
11
+ "attribute_before_type_cast" => "() -> %<type>s",
12
+ "attribute_came_from_user?" => "() -> bool",
13
+ "attribute_change" => "() -> [%<opt>s, %<opt>s]",
14
+ "attribute_change_to_be_saved" => "() -> Array[%<opt>s]?",
15
+ "attribute_changed?" => "() -> bool",
16
+ "attribute_for_database" => "() -> %<type>s",
17
+ "attribute_in_database" => "() -> %<opt>s",
18
+ "attribute_previous_change" => "() -> Array[%<opt>s]?",
19
+ "attribute_previously_changed?" => "() -> bool",
20
+ "attribute_previously_was" => "() -> %<opt>s",
21
+ "attribute_was" => "() -> %<opt>s",
22
+ "attribute_will_change!" => "() -> void",
23
+ "clear_attribute_change" => "() -> void",
24
+ "restore_attribute!" => "() -> void",
25
+ "saved_change_to_attribute" => "() -> Array[%<opt>s]?",
26
+ "saved_change_to_attribute?" => "() -> bool",
27
+ "will_save_change_to_attribute?" => "() -> bool",
28
+ }
29
+
6
30
  def initialize(loader)
7
31
  @loader = loader
8
32
  end
@@ -12,71 +36,39 @@ module Orthoses
12
36
  ::ActiveRecord::Base.descendants.each do |klass|
13
37
  next if klass.abstract_class?
14
38
 
15
- name = Utils.module_name(klass) || next
16
- lines = klass.columns.flat_map do |col|
17
- req = sql_type_to_rbs(col.type)
39
+ base_klass_name = Utils.module_name(klass) || next
40
+ begin
41
+ klass.columns
42
+ rescue ::ActiveRecord::StatementInvalid => e
43
+ Orthoses.logger.warn(e.to_s)
44
+ next
45
+ end
46
+
47
+ lines = []
48
+ klass.columns_hash.each do |name, col|
49
+ req = ActiveRecord.sql_type_to_rbs(col.type)
18
50
  opt = "#{req}?"
19
51
  type = col.null ? opt : req
20
52
 
21
- [
22
- "def #{col.name}: () -> #{type}",
23
- "def #{col.name}=: (#{type}) -> #{type}",
24
- "def #{col.name}?: () -> bool",
25
- "def #{col.name}_changed?: () -> bool",
26
- "def #{col.name}_change: () -> [#{opt}, #{opt}]",
27
- "def #{col.name}_will_change!: () -> void",
28
- "def #{col.name}_was: () -> #{opt}",
29
- "def #{col.name}_previously_changed?: () -> bool",
30
- "def #{col.name}_previous_change: () -> Array[#{opt}]?",
31
- "def #{col.name}_previously_was: () -> #{opt}",
32
- "def #{col.name}_before_last_save: () -> #{opt}",
33
- "def #{col.name}_change_to_be_saved: () -> Array[#{opt}]?",
34
- "def #{col.name}_in_database: () -> #{opt}",
35
- "def saved_change_to_#{col.name}: () -> Array[#{opt}]?",
36
- "def saved_change_to_#{col.name}?: () -> bool",
37
- "def will_save_change_to_#{col.name}?: () -> bool",
38
- "def restore_#{col.name}!: () -> void",
39
- "def clear_#{col.name}_change: () -> void",
40
- ]
53
+ ::ActiveRecord::Base.attribute_method_matchers.each do |matcher|
54
+ tmpl = TARGET_TYPE_MAP[matcher.target]
55
+ lines << "def #{matcher.method_name(name)}: #{tmpl % {type: type, opt: opt}}"
56
+ end
57
+ end
58
+ klass.attribute_aliases.each do |alias_name, column_name|
59
+ ::ActiveRecord::Base.attribute_method_matchers.each do |matcher|
60
+ lines << "alias #{matcher.method_name(alias_name)} #{matcher.method_name(column_name)}"
61
+ end
41
62
  end
42
- generated_attribute_methods = "#{name}::AttributeMethods::GeneratedAttributeMethods"
43
- store[name] << "include #{generated_attribute_methods}"
44
63
 
45
- store["#{name}::AttributeMethods"].header = "module #{name}::AttributeMethods"
64
+ generated_attribute_methods = "#{base_klass_name}::GeneratedAttributeMethods"
65
+ store[base_klass_name] << "include #{generated_attribute_methods}"
66
+
46
67
  store[generated_attribute_methods].header = "module #{generated_attribute_methods}"
47
68
  store[generated_attribute_methods].concat(lines)
48
69
  end
49
70
  end
50
71
  end
51
-
52
- # Thanks https://github.com/pocke/rbs_rails/blob/8a128a8d29f0861aa2c25aa4110ff7c2ea674865/lib/rbs_rails/active_record.rb#L525-L551
53
- def sql_type_to_rbs(t)
54
- case t
55
- when :integer
56
- 'Integer'
57
- when :float
58
- 'Float'
59
- when :decimal
60
- 'BigDecimal'
61
- when :string, :text, :citext, :uuid, :binary
62
- 'String'
63
- when :datetime
64
- 'ActiveSupport::TimeWithZone'
65
- when :boolean
66
- "bool"
67
- when :jsonb, :json
68
- "untyped"
69
- when :date
70
- 'Date'
71
- when :time
72
- 'Time'
73
- when :inet
74
- "IPAddr"
75
- else
76
- # Unknown column type, give up
77
- 'untyped'
78
- end
79
- end
80
72
  end
81
73
  end
82
74
  end
@@ -13,14 +13,23 @@ module Orthoses
13
13
  next if base.abstract_class?
14
14
  base_name = Utils.module_name(base) || next
15
15
 
16
- collection_proxy = "#{base_name}::ActiveRecord_Associations_CollectionProxy"
17
-
18
16
  lines = base.reflect_on_all_associations(:has_many).flat_map do |ref|
19
17
  singular_name = ref.name.to_s.singularize
18
+ type =
19
+ begin
20
+ Utils.module_name(ref.klass)
21
+ rescue NameError => e
22
+ while e
23
+ Orthoses.logger.warn(e.message)
24
+ e = e.cause
25
+ end
26
+ next
27
+ end
20
28
 
29
+ collection_proxy = "::#{type}::ActiveRecord_Associations_CollectionProxy"
21
30
  [
22
31
  "def #{ref.name}: () -> #{collection_proxy}",
23
- "def #{ref.name}=: (#{collection_proxy} | Array[#{ref.klass}]) -> (#{collection_proxy} | Array[#{ref.klass}])",
32
+ "def #{ref.name}=: (#{collection_proxy} | Array[#{type}]) -> (#{collection_proxy} | Array[#{type}])",
24
33
  "def #{singular_name}_ids: () -> Array[Integer]",
25
34
  "def #{singular_name}_ids=: (Array[Integer]) -> Array[Integer]",
26
35
  ]
@@ -29,7 +38,9 @@ module Orthoses
29
38
  generated_association_methods = "#{base_name}::GeneratedAssociationMethods"
30
39
  store[generated_association_methods].header = "module #{generated_association_methods}"
31
40
  store[generated_association_methods].concat(lines)
32
- store[base_name] << "include #{generated_association_methods}"
41
+
42
+ sig = "include #{generated_association_methods}"
43
+ store[base_name] << sig if !store[base_name].body.include?(sig)
33
44
  end
34
45
  end
35
46
  end
@@ -14,7 +14,16 @@ module Orthoses
14
14
  base_name = Utils.module_name(base) || next
15
15
 
16
16
  lines = base.reflect_on_all_associations(:has_one).flat_map do |ref|
17
- type = ref.klass.to_s
17
+ type =
18
+ begin
19
+ Utils.module_name(ref.klass) or next
20
+ rescue NameError => e
21
+ while e
22
+ Orthoses.logger.warn(e.message)
23
+ e = e.cause
24
+ end
25
+ next
26
+ end
18
27
  opt = "#{type}?"
19
28
 
20
29
  [
@@ -31,7 +40,8 @@ module Orthoses
31
40
  store[generated_association_methods].header = "module #{generated_association_methods}"
32
41
  store[generated_association_methods].concat(lines)
33
42
 
34
- store[base_name] << "include #{generated_association_methods}"
43
+ sig = "include #{generated_association_methods}"
44
+ store[base_name] << sig if !store[base_name].body.include?(sig)
35
45
  end
36
46
  end
37
47
  end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Orthoses
4
+ module ActiveRecord
5
+ class Relation
6
+ def initialize(loader)
7
+ @loader = loader
8
+ end
9
+
10
+ def call
11
+ @loader.call.tap do |store|
12
+ ::ActiveRecord::Base.descendants.each do |klass|
13
+ next if klass.abstract_class?
14
+
15
+ primary_key = fetch_primary_key(klass)
16
+
17
+ model_name = Utils.module_name(klass) or next
18
+ class_specific_relation = "#{model_name}::ActiveRecord_Relation"
19
+ class_specific_proxy = "#{model_name}::ActiveRecord_Associations_CollectionProxy"
20
+ class_specific_generated_relation_methods = "#{model_name}::GeneratedRelationMethods"
21
+
22
+ store[class_specific_generated_relation_methods].tap do |c|
23
+ end
24
+
25
+ store[class_specific_relation].tap do |c|
26
+ c.header = "class #{class_specific_relation} < ::ActiveRecord::Relation"
27
+ c << "include #{class_specific_generated_relation_methods}"
28
+ c << "include _ActiveRecord_Relation[#{model_name}, #{primary_key}]"
29
+ c << "include Enumerable[#{model_name}]"
30
+ end
31
+
32
+ store[class_specific_proxy].tap do |c|
33
+ c.header = "class #{class_specific_proxy} < ::ActiveRecord::Associations::CollectionProxy"
34
+ c << "include _ActiveRecord_Relation[#{model_name}, #{primary_key}]"
35
+ end
36
+
37
+ store[model_name].tap do |c|
38
+ c << "extend _ActiveRecord_Relation_ClassMethods[#{model_name}, #{class_specific_relation}, #{primary_key}]"
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+ private
45
+
46
+ def fetch_primary_key(klass)
47
+ type = klass.type_for_attribute(klass.primary_key).type
48
+ ActiveRecord.sql_type_to_rbs(type)
49
+ rescue ::ActiveRecord::StatementInvalid => e
50
+ Orthoses.logger.warn(e.to_s)
51
+ "untyped"
52
+ end
53
+ end
54
+ end
55
+ end
56
+
@@ -7,4 +7,40 @@ require_relative 'active_record/generated_attribute_methods'
7
7
  require_relative 'active_record/has_many'
8
8
  require_relative 'active_record/has_one'
9
9
  require_relative 'active_record/query_methods'
10
+ require_relative 'active_record/relation'
10
11
  require_relative 'active_record/scope'
12
+
13
+ module Orthoses
14
+ module ActiveRecord
15
+ # Thanks https://github.com/pocke/rbs_rails/blob/8a128a8d29f0861aa2c25aa4110ff7c2ea674865/lib/rbs_rails/active_record.rb#L525-L551
16
+ def self.sql_type_to_rbs(t)
17
+ case t
18
+ when :integer, :big_integer
19
+ '::Integer'
20
+ when :float
21
+ '::Float'
22
+ when :decimal
23
+ '::BigDecimal'
24
+ when :string, :text, :citext, :uuid, :binary, :immutable_string
25
+ '::String'
26
+ when :datetime
27
+ '::ActiveSupport::TimeWithZone'
28
+ when :boolean
29
+ "bool"
30
+ when :date
31
+ '::Date'
32
+ when :time
33
+ '::Time'
34
+ when :cidr, :inet
35
+ "::IPAddr"
36
+ when :bit, :bit_varying, :enum, :hstore,
37
+ :interval, :jsonb, :json, :legacy_point, :money, :point, :vector, :xml
38
+ # FIXME
39
+ 'untyped'
40
+ else
41
+ # Unknown column type, give up
42
+ 'untyped'
43
+ end
44
+ end
45
+ end
46
+ end
@@ -20,14 +20,16 @@ module Orthoses
20
20
  delegate.captures.each do |capture|
21
21
  receiver_name = Utils.module_name(capture.method.receiver) or next
22
22
  receiver_content = store[receiver_name]
23
+ prefix = capture.argument[:private] ? "private " : ""
23
24
 
24
25
  case capture.argument[:to]
26
+ # delegate :foo, to: Foo
25
27
  when Module
26
28
  to_module_name = Utils.module_name(capture.argument[:to]) or next
27
29
  capture.argument[:methods].each do |arg|
28
30
  if sig = resource.build_signature(to_module_name, arg, :singleton, false)
29
31
  receiver_content << "# defined by `delegate` to: #{to_module_name}"
30
- receiver_content << sig
32
+ receiver_content << "#{prefix}#{sig}"
31
33
  else
32
34
  Orthoses.logger.warn("[ActiveSupport::Delegation] Ignore since missing type for #{to_module_name}.#{arg.inspect} in #{capture.argument.inspect}")
33
35
  end
@@ -35,20 +37,28 @@ module Orthoses
35
37
  else
36
38
  to_name = capture.argument[:to].to_s.to_sym
37
39
  tag, to_return_type = resource.find(receiver_name, to_name, :instance, false)
38
- raise "bug" if tag == :multi
40
+ if tag == :multi
41
+ to_return_type = if to_return_type.length == 1
42
+ to_return_type.first.type.return_type
43
+ else
44
+ nil
45
+ end
46
+ end
39
47
 
40
48
  case to_return_type
41
49
  when nil, RBS::Types::Bases::Any
50
+ # no type found
42
51
  capture.argument[:methods].each do |method|
43
52
  receiver_content << "# defined by `delegate` to: #{to_return_type}##{to_name}"
44
- receiver_content << "def #{method}: (*untyped, **untyped) -> untyped"
53
+ receiver_content << "#{prefix}def #{method}: (*untyped, **untyped) -> untyped"
45
54
  end
46
55
  else
56
+ # found return type in store or env
47
57
  to_typename = to_return_type.name.relative!.to_s
48
58
  capture.argument[:methods].each do |method|
49
59
  if sig = resource.build_signature(to_typename, method, :instance, true)
50
60
  receiver_content << "# defined by `delegate` to #{to_return_type}##{to_name}"
51
- receiver_content << sig
61
+ receiver_content << "#{prefix}#{sig}"
52
62
  else
53
63
  Orthoses.logger.warn("[ActiveSupport::Delegation] Ignore since missing type for #{to_typename}##{method.inspect} in #{capture.argument.inspect}")
54
64
  end
@@ -73,6 +83,7 @@ module Orthoses
73
83
  typename = TypeName(mod_name).absolute!
74
84
 
75
85
  if definition_method = build_definition(typename, kind)&.methods&.[](name)
86
+ # found in env
76
87
  return [:multi, definition_method.defs.map(&:type)]
77
88
  end
78
89
  resolve_type_by_name(@store[mod_name].to_decl.members, name, kind, argument)
@@ -85,7 +96,7 @@ module Orthoses
85
96
  when :singleton
86
97
  @definition_builder.build_singleton(typename)
87
98
  else
88
- raise "big"
99
+ raise "bug"
89
100
  end
90
101
  rescue RuntimeError => e
91
102
  if e.message.match?(/\AUnknown name for/)
@@ -115,13 +126,13 @@ module Orthoses
115
126
  when RBS::AST::Members::MethodDefinition
116
127
  next unless member.name == name && member.kind == kind
117
128
  if argument
118
- return [:multi, member.types]
129
+ return [:multi, member.overloads.map { |o| o.method_type }]
119
130
  else
120
- type = member.types.find do |method_type|
131
+ method_type = member.overloads.map(&:method_type).find do |method_type|
121
132
  method_type.type.required_positionals.empty? && method_type.type.required_keywords.empty?
122
133
  end
123
- next unless type
124
- return [:single, type.type.return_type]
134
+ next unless method_type
135
+ return [:single, method_type.type.return_type]
125
136
  end
126
137
  when RBS::AST::Members::Var
127
138
  next unless member.name == name
@@ -13,6 +13,7 @@ module Orthoses
13
13
  store = @loader.call
14
14
 
15
15
  time_with_zone_store = store["ActiveSupport::TimeWithZone"]
16
+ time_with_zone_store.body.replace(filter_decl(time_with_zone_store))
16
17
  each_line_from_core_time_definition(store) do |line|
17
18
  time_with_zone_store << line
18
19
  end
@@ -22,63 +23,69 @@ module Orthoses
22
23
 
23
24
  private
24
25
 
25
- NOT_DELEGATE_METHODS = Set.new(%i[
26
- utc
27
- getgm
28
- getutc
29
- gmtime
30
- localtime
31
- ])
32
-
33
26
  TYPE_MERGE_METHODS = Set.new(%i[
34
27
  +
35
28
  -
36
29
  ])
37
30
 
31
+ def filter_decl(time_with_zone_store)
32
+ writer = RBS::Writer.new(out: StringIO.new)
33
+ time_with_zone_store.to_decl.members.each do |member|
34
+ # ActiveSupport::TimeWithZone.name has been deprecated
35
+ next if member.instance_of?(RBS::AST::Members::MethodDefinition) && member.kind == :singleton && member.name == :name
36
+ writer.write_member(member)
37
+ end
38
+ writer.out.string.each_line.to_a
39
+ end
40
+
41
+ def add_signature(env, content)
42
+ buffer, directives, decls = RBS::Parser.parse_signature(content.to_rbs)
43
+ env.add_signature(buffer: buffer, directives: directives, decls: decls)
44
+ end
45
+
38
46
  def each_line_from_core_time_definition(store)
39
47
  type_name_time = TypeName("::Time")
40
48
  type_name_time_with_zone = TypeName("::ActiveSupport::TimeWithZone")
41
49
  env = Utils.rbs_environment(collection: true, cache: false)
42
- env << store["Time"].to_decl
43
- env << store["DateAndTime"].to_decl
44
- env << store["DateAndTime::Zones"].to_decl
45
- env << store["DateAndTime::Calculations"].to_decl
46
- env << store["DateAndTime::Compatibility"].to_decl
47
- env << store["ActiveSupport"].to_decl
48
- env << store["ActiveSupport::TimeZone"].to_decl
49
- env << store["ActiveSupport::Duration"].to_decl
50
- env << store["ActiveSupport::TimeWithZone"].to_decl
50
+
51
+ add_signature(env, store["Time"])
52
+ add_signature(env, store["DateAndTime"])
53
+ add_signature(env, store["DateAndTime::Zones"])
54
+ add_signature(env, store["DateAndTime::Calculations"])
55
+ add_signature(env, store["DateAndTime::Compatibility"])
56
+ add_signature(env, store["ActiveSupport"])
57
+ add_signature(env, store["ActiveSupport::TimeZone"])
58
+ add_signature(env, store["ActiveSupport::Duration"])
59
+ add_signature(env, store["ActiveSupport::TimeWithZone"])
51
60
 
52
61
  builder = RBS::DefinitionBuilder.new(env: env.resolve_type_names)
53
- one_ancestors = builder.ancestor_builder.one_instance_ancestors(type_name_time)
54
- one_ancestors.included_modules.each do |included_module|
55
- yield "include #{included_module.source.name}"
56
- end
57
62
  twz_methods = builder.build_instance(type_name_time_with_zone).methods
58
63
  builder.build_instance(type_name_time).methods.each do |sym, definition_method|
64
+ next if twz_methods.has_key?(sym) && !TYPE_MERGE_METHODS.include?(sym)
59
65
  next if !definition_method.public?
66
+
60
67
  definition_method.defs.reject! do |type_def|
61
68
  type_def.implemented_in != type_name_time
62
69
  end
63
70
  next if definition_method.defs.empty?
64
71
 
65
- if !NOT_DELEGATE_METHODS.include?(sym)
66
- definition_method.method_types.each do |method_type|
67
- rt = method_type.type.return_type
68
- if rt.instance_of?(RBS::Types::ClassInstance) && rt.name.to_s == "::Time"
69
- rt.instance_variable_set(:@name, RBS::Types::Bases::Self.new(location: nil))
70
- end
72
+ definition_method.method_types.each do |method_type|
73
+ rt = method_type.type.return_type
74
+ if rt.instance_of?(RBS::Types::ClassInstance) && rt.name.to_s == "::Time"
75
+ rt.instance_variable_set(:@name, RBS::Types::Bases::Self.new(location: nil))
71
76
  end
72
77
  end
73
78
 
74
79
  if definition_method.alias_of.nil?
75
80
  method_types = definition_method.method_types
76
81
 
82
+ # merge method types (e.g. :+, :-
83
+ # TimeWithZone -delegate-> Time(core_ext) -delegate-> Time(core)
77
84
  if TYPE_MERGE_METHODS.include?(sym)
78
85
  if twz_definition_method = twz_methods[sym]
79
86
  twz_definition_method.defs.each do |type_def|
80
87
  if type_def.implemented_in == type_name_time_with_zone
81
- method_types << type_def.type
88
+ method_types.unshift(type_def.type)
82
89
  end
83
90
  end
84
91
  end