orthoses-rails 0.9.0 → 1.1.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 (62) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +20 -65
  3. data/lib/generators/orthoses/rails/install_generator.rb +18 -0
  4. data/lib/generators/orthoses/rails/templates/rails.rake +31 -0
  5. data/lib/orthoses/active_record/belongs_to.rb +15 -4
  6. data/lib/orthoses/active_record/enum.rb +5 -0
  7. data/lib/orthoses/active_record/generated_attribute_methods.rb +47 -55
  8. data/lib/orthoses/active_record/has_many.rb +15 -4
  9. data/lib/orthoses/active_record/has_one.rb +12 -2
  10. data/lib/orthoses/active_record/relation.rb +56 -0
  11. data/lib/orthoses/active_record/secure_token.rb +28 -0
  12. data/lib/orthoses/active_record.rb +37 -0
  13. data/lib/orthoses/active_storage/attached/model.rb +47 -0
  14. data/lib/orthoses/active_storage.rb +3 -0
  15. data/lib/orthoses/active_support/aliasing.rb +34 -0
  16. data/lib/orthoses/active_support/delegation.rb +17 -6
  17. data/lib/orthoses/active_support/time_with_zone.rb +30 -21
  18. data/lib/orthoses/active_support.rb +1 -0
  19. data/lib/orthoses/rails/application.rb +47 -0
  20. data/lib/orthoses/rails/version.rb +1 -1
  21. data/lib/orthoses/rails.rb +4 -2
  22. metadata +13 -45
  23. data/examples/rails/Gemfile +0 -16
  24. data/examples/rails/Gemfile.lock +0 -179
  25. data/examples/rails/Rakefile +0 -328
  26. data/examples/rails/config/database.yml +0 -8
  27. data/examples/rails/config/storage.yml +0 -3
  28. data/examples/rails/known_sig/actionpack/7.0/action_controller/metal.rbs +0 -4
  29. data/examples/rails/known_sig/activemodel/7.0/active_model/serialization.rbs +0 -4
  30. data/examples/rails/known_sig/activemodel/7.0/active_model/validations.rbs +0 -4
  31. data/examples/rails/known_sig/activerecord/6.0/_active_record_relation.rbs +0 -49
  32. data/examples/rails/known_sig/activerecord/6.0/_active_record_relation_class_methods.rbs +0 -45
  33. data/examples/rails/known_sig/activerecord/6.0/active_record/result.rbs +0 -5
  34. data/examples/rails/known_sig/activerecord/6.1/_active_record_relation.rbs +0 -49
  35. data/examples/rails/known_sig/activerecord/6.1/_active_record_relation_class_methods.rbs +0 -45
  36. data/examples/rails/known_sig/activerecord/6.1/active_record/result.rbs +0 -5
  37. data/examples/rails/known_sig/activerecord/7.0/_active_record_relation.rbs +0 -49
  38. data/examples/rails/known_sig/activerecord/7.0/_active_record_relation_class_methods.rbs +0 -45
  39. data/examples/rails/known_sig/activerecord/7.0/active_record/encryption/context.rbs +0 -9
  40. data/examples/rails/known_sig/activerecord/7.0/active_record/result.rbs +0 -5
  41. data/examples/rails/known_sig/activesupport/7.0/active_support/callbacks/callback_chain.rbs +0 -9
  42. data/examples/rails/known_sig/activesupport/7.0/active_support/duration.rbs +0 -4
  43. data/examples/rails/known_sig/activesupport/7.0/active_support/hash_with_indifferent_access.rbs +0 -6
  44. data/examples/rails/known_sig/activesupport/7.0/active_support/multibyte/chars.rbs +0 -7
  45. data/examples/rails/known_sig/activesupport/7.0/active_support/time_with_zone.rbs +0 -5
  46. data/examples/rails/known_sig/activesupport/7.0/active_support/time_zone.rbs +0 -3
  47. data/examples/rails/known_sig/activesupport/7.0/date.rbs +0 -6
  48. data/examples/rails/known_sig/activesupport/7.0/date_and_time/zones.rbs +0 -4
  49. data/examples/rails/known_sig/activesupport/7.0/hash_with_indifferent_access.rbs +0 -2
  50. data/examples/rails/known_sig/activesupport/7.0/integer.rbs +0 -9
  51. data/examples/rails/known_sig/activesupport/7.0/numeric.rbs +0 -15
  52. data/examples/rails/known_sig/activesupport/7.0/string.rbs +0 -4
  53. data/examples/rails/known_sig/activesupport/7.0/time.rbs +0 -46
  54. data/examples/rails/tasks/action_pack.rake +0 -141
  55. data/examples/rails/tasks/action_view.rake +0 -77
  56. data/examples/rails/tasks/active_job.rake +0 -99
  57. data/examples/rails/tasks/active_model.rake +0 -46
  58. data/examples/rails/tasks/active_record.rake +0 -88
  59. data/examples/rails/tasks/active_storage.rake +0 -94
  60. data/examples/rails/tasks/active_support.rake +0 -99
  61. data/examples/rails/tasks/railties.rake +0 -34
  62. data/orthoses-rails.gemspec +0 -36
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a43ce53af9e7aec6ef02751c5649146d817e76770a9b15bb2fb5a24fa1b0447b
4
- data.tar.gz: 83c78756ff7794059a1001902f887509d8be7b42f116a2ce30b1cdb27d75ff6a
3
+ metadata.gz: ec337b1da4ecda39f953797824239ede59c536c74da48cc1dbd01e46eed1d2b0
4
+ data.tar.gz: b146481d3fda9b3568a1593fd05608f09c811e1181356f2e36d2b010c30dd130
5
5
  SHA512:
6
- metadata.gz: bce1abdf1faf93222005ba51aa4c74675fc98edef98a546faecd52280bf03ce11dc896379ab7269c3c7bda3be43eddae81cf01ab6fc701e8a2e96d4f841dd7b8
7
- data.tar.gz: be065396c52d757e110c2668e5eb90ada5f58275376bc93f0e9e4d25be4968735bccae5ace6d2ad7e08c8642b75b1ff5f5bd63b93c4019faba66101f1de34757
6
+ metadata.gz: 2bec23dc332048a010d10a2dc01754b969f76d2dd19bfa4b50393c1a764b3de496a35d018560aca04fe43630de497e900bb580774698bda3073ea33dcbf44bb5
7
+ data.tar.gz: e2dac22ee12a3bf049a73e5609d4a3ca1693c2dbc851c3bb06256e4566e9adad6c991fa9a60b0aaa7b22d6ea19e0c7cbc65c403cd136669d4c98b58deb7d90e7
data/README.md CHANGED
@@ -3,38 +3,26 @@
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
+ If you have never performed rbs collection initialization, you need to do it.
9
+
10
+ ```
11
+ $ bundle exec rbs collection init
12
+ $ bundle exec rbs collection install
13
+ ```
14
+
15
+ Build your Rake task for RBS generation.
16
+
17
+ ```
18
+ $ bin/rails generate orthoses:rails:install
19
+ ```
20
+
21
+ Then run the rake task.
22
+
23
+ ```
24
+ $ bin/rails orthoses:rails
25
+ ```
38
26
 
39
27
  ## Installation
40
28
 
@@ -46,39 +34,6 @@ If bundler is not being used to manage dependencies, install the gem by executin
46
34
 
47
35
  $ gem install orthoses-rails
48
36
 
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
37
  ## Development
83
38
 
84
39
  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
@@ -0,0 +1,31 @@
1
+ namespace :orthoses do
2
+ task :rails do
3
+ # Phase to load libraries
4
+ require Rails.root / "config/application"
5
+ require 'orthoses/rails'
6
+
7
+ # You can choose logger level
8
+ Orthoses.logger.level = :warn
9
+
10
+ # DSL for Orthoses.
11
+ Orthoses::Builder.new do
12
+ use Orthoses::CreateFileByName,
13
+ to: 'sig/orthoses', # Write to this dir. (require)
14
+ depth: 1, # Group files by module name path depth. (default: nil)
15
+ rmtree: true # Remove all `to` dir before generation. (default: false)
16
+
17
+ # Complement missing const name.
18
+ use Orthoses::MissingName
19
+
20
+ # You can load hand written RBS.
21
+ # use Orthoses::LoadRBS,
22
+ # paths: Dir.glob(Rails.root / "sig/hand-written/**/*.rbs")
23
+
24
+ # Middleware package for rails application.
25
+ use Orthoses::Rails::Application
26
+
27
+ # Application code loaded here is the target of the analysis.
28
+ run Orthoses::Rails::Application::Loader.new
29
+ end.call
30
+ end
31
+ 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
+
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Orthoses
4
+ module ActiveRecord
5
+ # def has_secure_token(attribute = :token, length: MINIMUM_TOKEN_LENGTH)
6
+ class SecureToken
7
+ def initialize(loader)
8
+ @loader = loader
9
+ end
10
+
11
+ def call
12
+ has_secure_token = CallTracer::Lazy.new
13
+ store = has_secure_token.trace('ActiveRecord::SecureToken::ClassMethods#has_secure_token') do
14
+ @loader.call
15
+ end
16
+
17
+ has_secure_token.captures.each do |capture|
18
+ base_name = Utils.module_name(capture.method.receiver) or next
19
+ attribute = capture.argument[:attribute]
20
+
21
+ store[base_name] << "def regenerate_#{attribute}: () -> bool"
22
+ end
23
+
24
+ store
25
+ end
26
+ end
27
+ end
28
+ end
@@ -7,4 +7,41 @@ 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
+ require_relative 'active_record/secure_token'
13
+
14
+ module Orthoses
15
+ module ActiveRecord
16
+ # Thanks https://github.com/pocke/rbs_rails/blob/8a128a8d29f0861aa2c25aa4110ff7c2ea674865/lib/rbs_rails/active_record.rb#L525-L551
17
+ def self.sql_type_to_rbs(t)
18
+ case t
19
+ when :integer, :big_integer
20
+ '::Integer'
21
+ when :float
22
+ '::Float'
23
+ when :decimal
24
+ '::BigDecimal'
25
+ when :string, :text, :citext, :uuid, :binary, :immutable_string
26
+ '::String'
27
+ when :datetime
28
+ '::ActiveSupport::TimeWithZone'
29
+ when :boolean
30
+ "bool"
31
+ when :date
32
+ '::Date'
33
+ when :time
34
+ '::Time'
35
+ when :cidr, :inet
36
+ "::IPAddr"
37
+ when :bit, :bit_varying, :enum, :hstore,
38
+ :interval, :jsonb, :json, :legacy_point, :money, :point, :vector, :xml
39
+ # FIXME
40
+ 'untyped'
41
+ else
42
+ # Unknown column type, give up
43
+ 'untyped'
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Orthoses
4
+ module ActiveStorage
5
+ module Attached
6
+ # <= 6.0
7
+ # def has_one_attached(name, dependent: :purge_later)
8
+ # >= 6.1
9
+ # def has_one_attached(name, dependent: :purge_later, service: nil, strict_loading: false)
10
+ class Model
11
+ def initialize(loader)
12
+ @loader = loader
13
+ end
14
+
15
+ def call
16
+ store = @loader.call
17
+
18
+ ::ActiveRecord::Base.descendants.each do |base|
19
+ next if base.abstract_class?
20
+ next if base.reflect_on_all_attachments.empty?
21
+
22
+ base_name = Utils.module_name(base) or next
23
+ base.reflect_on_all_attachments.each do |reflection|
24
+ type =
25
+ case reflection
26
+ when ::ActiveStorage::Reflection::HasOneAttachedReflection
27
+ "ActiveStorage::Attached::One"
28
+ when ::ActiveStorage::Reflection::HasManyAttachedReflection
29
+ "ActiveStorage::Attached::Many"
30
+ else
31
+ "untyped"
32
+ end
33
+ name = reflection.name
34
+
35
+ store[base_name].tap do |content|
36
+ content << "def #{name}: () -> #{type}"
37
+ content << "def #{name}=: (untyped attachable) -> untyped"
38
+ end
39
+ end
40
+ end
41
+
42
+ store
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'active_storage/attached/model'
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Orthoses
4
+ module ActiveSupport
5
+ # def alias_attribute(new_name, old_name)
6
+ class Aliasing
7
+ def initialize(loader)
8
+ @loader = loader
9
+ end
10
+
11
+ def call
12
+ alias_attribute = CallTracer::Lazy.new
13
+
14
+ store =
15
+ alias_attribute.trace('Module#alias_attribute') do
16
+ @loader.call
17
+ end
18
+
19
+ alias_attribute.captures.each do |capture|
20
+ base_mod_name = Utils.module_name(capture.method.receiver) or next
21
+ new_name = capture.argument[:new_name]
22
+
23
+ content = store[base_mod_name]
24
+ # TODO: Shold use alias? But, it has risc of undefined method
25
+ content << "def #{new_name}: () -> untyped"
26
+ content << "def #{new_name}?: () -> bool"
27
+ content << "def #{new_name}=: (untyped) -> untyped"
28
+ end
29
+
30
+ store
31
+ end
32
+ end
33
+ end
34
+ end