tapioca 0.5.1 → 0.5.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -1
  3. data/lib/tapioca/cli.rb +54 -139
  4. data/lib/tapioca/compilers/dsl/active_model_secure_password.rb +101 -0
  5. data/lib/tapioca/compilers/dsl/active_record_enum.rb +1 -1
  6. data/lib/tapioca/compilers/dsl/active_record_fixtures.rb +86 -0
  7. data/lib/tapioca/compilers/dsl/active_record_typed_store.rb +41 -33
  8. data/lib/tapioca/compilers/dsl/active_support_concern.rb +0 -2
  9. data/lib/tapioca/compilers/dsl/base.rb +12 -0
  10. data/lib/tapioca/compilers/dsl/identity_cache.rb +0 -1
  11. data/lib/tapioca/compilers/dsl/mixed_in_class_attributes.rb +74 -0
  12. data/lib/tapioca/compilers/dsl/smart_properties.rb +4 -4
  13. data/lib/tapioca/compilers/dsl_compiler.rb +7 -6
  14. data/lib/tapioca/compilers/dynamic_mixin_compiler.rb +198 -0
  15. data/lib/tapioca/compilers/sorbet.rb +0 -1
  16. data/lib/tapioca/compilers/symbol_table/symbol_generator.rb +84 -153
  17. data/lib/tapioca/compilers/symbol_table_compiler.rb +5 -11
  18. data/lib/tapioca/config.rb +1 -0
  19. data/lib/tapioca/config_builder.rb +1 -0
  20. data/lib/tapioca/constant_locator.rb +7 -1
  21. data/lib/tapioca/gemfile.rb +11 -5
  22. data/lib/tapioca/generators/base.rb +61 -0
  23. data/lib/tapioca/generators/dsl.rb +362 -0
  24. data/lib/tapioca/generators/gem.rb +345 -0
  25. data/lib/tapioca/generators/init.rb +79 -0
  26. data/lib/tapioca/generators/require.rb +52 -0
  27. data/lib/tapioca/generators/todo.rb +76 -0
  28. data/lib/tapioca/generators.rb +9 -0
  29. data/lib/tapioca/internal.rb +1 -2
  30. data/lib/tapioca/loader.rb +2 -2
  31. data/lib/tapioca/rbi_ext/model.rb +44 -0
  32. data/lib/tapioca/reflection.rb +8 -1
  33. data/lib/tapioca/version.rb +1 -1
  34. data/lib/tapioca.rb +2 -0
  35. metadata +34 -12
  36. data/lib/tapioca/generator.rb +0 -717
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0f8af6316243d26a0446636e00af5c9210296f5e5ac1f6065506af426d72c066
4
- data.tar.gz: 7124feeb9e7ecc16c2de3897ae9dd2bd3a432be6295d66ab5c440c4377f6d1e1
3
+ metadata.gz: 6051054b5414fe5f3383fa6fefc8fabdfd426380c026f55fdf87ecee8e10d964
4
+ data.tar.gz: 5054dacc20c7a8881c957540d0fa624d7ec406d57f8486a9dab9e088e2d270d0
5
5
  SHA512:
6
- metadata.gz: 720a2022478adcffea11b8a52b486dec808544611c9792c8508224c8565a3a4aaaed9f0a512bf51417da1259275ba6dbbd09b318f5e8e438544ae54558e0e028
7
- data.tar.gz: 8b5148ed02e2105b0c44134b98c99cb0e2c03144e8eca2293c8cf558b8dcff3141a5812141cb369e83cd830acb847a540f14303d2472ba7877518d4de55545ca
6
+ metadata.gz: e67122acb9f97bef99088a6dd2cded0c29a73bde763ab8acaad0fae6470ba0b1fc4d2a9139e781ca9ba569595560a743eba69dc701b0632712b31104e41ce2cf
7
+ data.tar.gz: 3ebf64268dc86b2740e68d3a508fdc67d458f976028781a4a9bce95eba883d2852840560fec32d3792d7193cdd224898bdfe913ffe800ddf3d5ad7be8a914ca6
data/Gemfile CHANGED
@@ -11,7 +11,6 @@ gem("pry-byebug")
11
11
  gem("rubocop-shopify", require: false)
12
12
  gem("rubocop-sorbet", ">= 0.4.1")
13
13
  gem("sorbet")
14
- gem("yard", "~> 0.9.25")
15
14
 
16
15
  group(:deployment, :development) do
17
16
  gem("rake")
@@ -35,4 +34,5 @@ group(:development, :test) do
35
34
  gem("nokogiri", require: false)
36
35
  gem("config", require: false)
37
36
  gem("aasm", require: false)
37
+ gem("bcrypt", require: false)
38
38
  end
data/lib/tapioca/cli.rb CHANGED
@@ -5,8 +5,6 @@ require "thor"
5
5
 
6
6
  module Tapioca
7
7
  class Cli < Thor
8
- include(Thor::Actions)
9
-
10
8
  class_option :outdir,
11
9
  aliases: ["--out", "-o"],
12
10
  banner: "directory",
@@ -29,22 +27,37 @@ module Tapioca
29
27
 
30
28
  desc "init", "initializes folder structure"
31
29
  def init
32
- create_config
33
- create_post_require
34
- generate_binstub
30
+ generator = Generators::Init.new(
31
+ sorbet_config: Config::SORBET_CONFIG,
32
+ default_postrequire: Config::DEFAULT_POSTREQUIRE,
33
+ default_command: Config::DEFAULT_COMMAND
34
+ )
35
+ generator.generate
35
36
  end
36
37
 
37
38
  desc "require", "generate the list of files to be required by tapioca"
38
39
  def require
40
+ generator = Generators::Require.new(
41
+ requires_path: ConfigBuilder.from_options(:require, options).postrequire,
42
+ sorbet_config_path: Config::SORBET_CONFIG,
43
+ default_command: Config::DEFAULT_COMMAND
44
+ )
39
45
  Tapioca.silence_warnings do
40
- generator.build_requires
46
+ generator.generate
41
47
  end
42
48
  end
43
49
 
44
50
  desc "todo", "generate the list of unresolved constants"
45
51
  def todo
52
+ current_command = T.must(current_command_chain.first)
53
+ config = ConfigBuilder.from_options(current_command, options)
54
+ generator = Generators::Todo.new(
55
+ todos_path: config.todos_path,
56
+ file_header: config.file_header,
57
+ default_command: Config::DEFAULT_COMMAND
58
+ )
46
59
  Tapioca.silence_warnings do
47
- generator.build_todos
60
+ generator.generate
48
61
  end
49
62
  end
50
63
 
@@ -67,13 +80,23 @@ module Tapioca
67
80
  type: :boolean,
68
81
  desc: "Supresses file creation output"
69
82
  def dsl(*constants)
83
+ current_command = T.must(current_command_chain.first)
84
+ config = ConfigBuilder.from_options(current_command, options)
85
+ generator = Generators::Dsl.new(
86
+ requested_constants: constants,
87
+ outpath: config.outpath,
88
+ generators: config.generators,
89
+ exclude_generators: config.exclude_generators,
90
+ file_header: config.file_header,
91
+ compiler_path: Tapioca::Compilers::Dsl::COMPILERS_PATH,
92
+ tapioca_path: Config::TAPIOCA_PATH,
93
+ default_command: Config::DEFAULT_COMMAND,
94
+ should_verify: options[:verify],
95
+ quiet: options[:quiet],
96
+ verbose: options[:verbose]
97
+ )
70
98
  Tapioca.silence_warnings do
71
- generator.build_dsl(
72
- constants,
73
- should_verify: options[:verify],
74
- quiet: options[:quiet],
75
- verbose: options[:verbose]
76
- )
99
+ generator.generate
77
100
  end
78
101
  end
79
102
 
@@ -104,10 +127,26 @@ module Tapioca
104
127
  type: :boolean,
105
128
  default: false,
106
129
  desc: "Verifies RBIs are up-to-date"
130
+ option :doc,
131
+ type: :boolean,
132
+ desc: "Include YARD documentation from sources when generating RBIs. Warning: this might be slow"
107
133
  def gem(*gems)
108
134
  Tapioca.silence_warnings do
109
135
  all = options[:all]
110
136
  verify = options[:verify]
137
+ current_command = T.must(current_command_chain.first)
138
+ config = ConfigBuilder.from_options(current_command, options)
139
+ generator = Generators::Gem.new(
140
+ gem_names: all ? [] : gems,
141
+ gem_excludes: config.exclude,
142
+ prerequire: config.prerequire,
143
+ postrequire: config.postrequire,
144
+ typed_overrides: config.typed_overrides,
145
+ default_command: Config::DEFAULT_COMMAND,
146
+ outpath: config.outpath,
147
+ file_header: config.file_header,
148
+ doc: config.doc
149
+ )
111
150
 
112
151
  raise MalformattedArgumentError, "Options '--all' and '--verify' are mutually exclusive" if all && verify
113
152
 
@@ -117,146 +156,22 @@ module Tapioca
117
156
  end
118
157
 
119
158
  if gems.empty? && !all
120
- generator.sync_rbis_with_gemfile(should_verify: verify)
159
+ generator.sync(should_verify: verify)
121
160
  else
122
- generator.build_gem_rbis(all ? [] : gems)
161
+ generator.generate
123
162
  end
124
163
  end
125
164
  end
126
165
 
127
- desc "generate [gem...]", "DEPRECATED: generate RBIs from gems"
128
- option :prerequire,
129
- aliases: ["--pre", "-b"],
130
- banner: "file",
131
- desc: "A file to be required before Bundler.require is called"
132
- option :postrequire,
133
- aliases: ["--post", "-a"],
134
- banner: "file",
135
- desc: "A file to be required after Bundler.require is called"
136
- option :exclude,
137
- aliases: ["-x"],
138
- type: :array,
139
- banner: "gem [gem ...]",
140
- desc: "Excludes the given gem(s) from RBI generation"
141
- option :typed_overrides,
142
- aliases: ["--typed", "-t"],
143
- type: :hash,
144
- banner: "gem:level [gem:level ...]",
145
- desc: "Overrides for typed sigils for generated gem RBIs"
146
- def generate(*gems)
147
- gem_names = if gems.empty?
148
- "--all"
149
- else
150
- gems.join(" ")
151
- end
152
- deprecation_message = <<~MSG
153
- DEPRECATION: The `generate` command will be removed in a future release.
154
-
155
- Start using `bin/tapioca gem #{gem_names}` instead.
156
- MSG
157
-
158
- say(deprecation_message, :red)
159
- say("")
160
-
161
- Tapioca.silence_warnings do
162
- generator.build_gem_rbis(gems)
163
- end
164
-
165
- say("")
166
- say(deprecation_message, :red)
167
- end
168
-
169
- desc "sync", "DEPRECATED: sync RBIs to Gemfile"
170
- option :prerequire,
171
- aliases: ["--pre", "-b"],
172
- banner: "file",
173
- desc: "A file to be required before Bundler.require is called"
174
- option :postrequire,
175
- aliases: ["--post", "-a"],
176
- banner: "file",
177
- desc: "A file to be required after Bundler.require is called"
178
- option :exclude,
179
- aliases: ["-x"],
180
- type: :array,
181
- banner: "gem [gem ...]",
182
- desc: "Excludes the given gem(s) from RBI generation"
183
- option :typed_overrides,
184
- aliases: ["--typed", "-t"],
185
- type: :hash,
186
- banner: "gem:level [gem:level ...]",
187
- desc: "Overrides for typed sigils for generated gem RBIs"
188
- option :verify,
189
- type: :boolean,
190
- default: false,
191
- desc: "Verifies RBIs are up-to-date"
192
- def sync
193
- deprecation_message = <<~MSG
194
- DEPRECATION: The `sync` command will be removed in a future release.
195
-
196
- Start using `bin/tapioca gem` instead.
197
- MSG
198
-
199
- say(deprecation_message, :red)
200
- say("")
201
-
202
- Tapioca.silence_warnings do
203
- generator.sync_rbis_with_gemfile(should_verify: options[:verify])
204
- end
205
-
206
- say("")
207
- say(deprecation_message, :red)
208
- end
209
-
210
166
  desc "--version, -v", "show version"
211
167
  def __print_version
212
168
  puts "Tapioca v#{Tapioca::VERSION}"
213
169
  end
214
170
 
215
- private
216
-
217
- def create_config
218
- create_file(Config::SORBET_CONFIG, skip: true) do
219
- <<~CONTENT
220
- --dir
221
- .
222
- CONTENT
223
- end
224
- end
225
-
226
- def create_post_require
227
- create_file(Config::DEFAULT_POSTREQUIRE, skip: true) do
228
- <<~CONTENT
229
- # typed: true
230
- # frozen_string_literal: true
231
-
232
- # Add your extra requires here (`tapioca require` can be used to boostrap this list)
233
- CONTENT
234
- end
235
- end
236
-
237
- def generate_binstub
238
- bin_stub_exists = File.exist?("bin/tapioca")
239
- installer = Bundler::Installer.new(Bundler.root, Bundler.definition)
240
- spec = Bundler.definition.specs.find { |s| s.name == "tapioca" }
241
- installer.generate_bundler_executable_stubs(spec, { force: true })
242
- if bin_stub_exists
243
- shell.say_status(:force, "bin/tapioca", :yellow)
244
- else
245
- shell.say_status(:create, "bin/tapioca", :green)
246
- end
247
- end
248
-
249
171
  no_commands do
250
172
  def self.exit_on_failure?
251
173
  true
252
174
  end
253
-
254
- def generator
255
- current_command = T.must(current_command_chain.first)
256
- @generator ||= Generator.new(
257
- ConfigBuilder.from_options(current_command, options)
258
- )
259
- end
260
175
  end
261
176
  end
262
177
  end
@@ -0,0 +1,101 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ begin
5
+ require "active_model"
6
+ rescue LoadError
7
+ return
8
+ end
9
+
10
+ module Tapioca
11
+ module Compilers
12
+ module Dsl
13
+ # `Tapioca::Compilers::Dsl::ActiveModelSecurePassword` decorates RBI files for all
14
+ # classes that use [`ActiveModel::SecurePassword`](http://api.rubyonrails.org/classes/ActiveModel/SecurePassword/ClassMethods.html).
15
+ #
16
+ # For example, with the following class:
17
+ #
18
+ # ~~~rb
19
+ # class User
20
+ # include ActiveModel::SecurePassword
21
+ #
22
+ # has_secure_password
23
+ # has_secure_password :token
24
+ # end
25
+ # ~~~
26
+ #
27
+ # this generator will produce an RBI file with the following content:
28
+ # ~~~rbi
29
+ # # typed: true
30
+ #
31
+ # class User
32
+ # sig { params(unencrypted_password: T.untyped).returns(T.untyped) }
33
+ # def authenticate(unencrypted_password); end
34
+ #
35
+ # sig { params(unencrypted_password: T.untyped).returns(T.untyped) }
36
+ # def authenticate_password(unencrypted_password); end
37
+ #
38
+ # sig { params(unencrypted_password: T.untyped).returns(T.untyped) }
39
+ # def authenticate_token(unencrypted_password); end
40
+ #
41
+ # sig { returns(T.untyped) }
42
+ # def password; end
43
+ #
44
+ # sig { params(unencrypted_password: T.untyped).returns(T.untyped) }
45
+ # def password=(unencrypted_password); end
46
+ #
47
+ # sig { params(unencrypted_password: T.untyped).returns(T.untyped) }
48
+ # def password_confirmation=(unencrypted_password); end
49
+ #
50
+ # sig { returns(T.untyped) }
51
+ # def token; end
52
+ #
53
+ # sig { params(unencrypted_password: T.untyped).returns(T.untyped) }
54
+ # def token=(unencrypted_password); end
55
+ #
56
+ # sig { params(unencrypted_password: T.untyped).returns(T.untyped) }
57
+ # def token_confirmation=(unencrypted_password); end
58
+ # end
59
+ # ~~~
60
+ class ActiveModelSecurePassword < Base
61
+ extend T::Sig
62
+
63
+ sig do
64
+ override
65
+ .params(root: RBI::Tree, constant: T.all(Class, ::ActiveModel::SecurePassword::ClassMethods))
66
+ .void
67
+ end
68
+ def decorate(root, constant)
69
+ instance_methods_modules = if constant < ActiveModel::SecurePassword::InstanceMethodsOnActivation
70
+ # pre Rails 6.0, this used to be a single static module
71
+ [ActiveModel::SecurePassword::InstanceMethodsOnActivation]
72
+ else
73
+ # post Rails 6.0, this is now using a dynmaic module builder pattern
74
+ # and we can have multiple different ones included into the model
75
+ constant.ancestors.grep(ActiveModel::SecurePassword::InstanceMethodsOnActivation)
76
+ end
77
+
78
+ return if instance_methods_modules.empty?
79
+
80
+ methods = instance_methods_modules.flat_map { |mod| mod.instance_methods(false) }
81
+ return if methods.empty?
82
+
83
+ root.create_path(constant) do |klass|
84
+ methods.each do |method|
85
+ create_method_from_def(klass, constant.instance_method(method))
86
+ end
87
+ end
88
+ end
89
+
90
+ sig { override.returns(T::Enumerable[Module]) }
91
+ def gather_constants
92
+ # This selects all classes that are `ActiveModel::SecurePassword::ClassMethods === klass`.
93
+ # In other words, we select all classes that have `ActiveModel::SecurePassword::ClassMethods`
94
+ # as an ancestor of its singleton class, i.e. all classes that have extended the
95
+ # `ActiveModel::SecurePassword::ClassMethods` module.
96
+ all_classes.grep(::ActiveModel::SecurePassword::ClassMethods)
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
@@ -80,7 +80,7 @@ module Tapioca
80
80
 
81
81
  sig { override.returns(T::Enumerable[Module]) }
82
82
  def gather_constants
83
- descendants_of(::ActiveRecord::Base).reject(&:abstract_class?)
83
+ descendants_of(::ActiveRecord::Base)
84
84
  end
85
85
 
86
86
  private
@@ -0,0 +1,86 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ begin
5
+ require "rails"
6
+ require "active_record"
7
+ require "active_record/fixtures"
8
+ require "active_support/test_case"
9
+ rescue LoadError
10
+ return
11
+ end
12
+
13
+ module Tapioca
14
+ module Compilers
15
+ module Dsl
16
+ # `Tapioca::Compilers::Dsl::ActiveRecordFixtures` decorates RBIs for test fixture methods
17
+ # that are created dynamically by Rails.
18
+ #
19
+ # For example, given an application with a posts table, we can have a fixture file
20
+ #
21
+ # ~~~yaml
22
+ # first_post:
23
+ # author: John
24
+ # title: My post
25
+ # ~~~
26
+ #
27
+ # Rails will allow us to invoke `posts(:first_post)` in tests to get the fixture record.
28
+ # The generated RBI by this generator will produce the following
29
+ #
30
+ # ~~~rbi
31
+ # # test_case.rbi
32
+ # # typed: true
33
+ # class ActiveSupport::TestCase
34
+ # sig { params(fixture_names: Symbol).returns(T.untyped) }
35
+ # def posts(*fixture_names); end
36
+ # end
37
+ # ~~~
38
+ class ActiveRecordFixtures < Base
39
+ extend T::Sig
40
+
41
+ sig { override.params(root: RBI::Tree, constant: T.class_of(ActiveSupport::TestCase)).void }
42
+ def decorate(root, constant)
43
+ method_names = fixture_loader.ancestors # get all ancestors from class that includes AR fixtures
44
+ .drop(1) # drop the anonymous class itself from the array
45
+ .reject(&:name) # only collect anonymous ancestors because fixture methods are always on an anonymous module
46
+ .map! do |mod|
47
+ [mod.private_instance_methods(false), mod.instance_methods(false)]
48
+ end
49
+ .flatten # merge methods into a single list
50
+ return if method_names.empty?
51
+
52
+ root.create_path(constant) do |mod|
53
+ method_names.each do |name|
54
+ create_fixture_method(mod, name.to_s)
55
+ end
56
+ end
57
+ end
58
+
59
+ sig { override.returns(T::Enumerable[Module]) }
60
+ def gather_constants
61
+ [ActiveSupport::TestCase]
62
+ end
63
+
64
+ private
65
+
66
+ sig { returns(Class) }
67
+ def fixture_loader
68
+ Class.new do
69
+ T.unsafe(self).include(ActiveRecord::TestFixtures)
70
+ T.unsafe(self).fixture_path = Rails.root.join("test", "fixtures")
71
+ T.unsafe(self).fixtures(:all)
72
+ end
73
+ end
74
+
75
+ sig { params(mod: RBI::Scope, name: String).void }
76
+ def create_fixture_method(mod, name)
77
+ mod.create_method(
78
+ name,
79
+ parameters: [create_rest_param("fixture_names", type: "Symbol")],
80
+ return_type: "T.untyped"
81
+ )
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
@@ -34,53 +34,57 @@ module Tapioca
34
34
  # # post.rbi
35
35
  # # typed: true
36
36
  # class Post
37
- # sig { params(review_date: T.nilable(Date)).returns(T.nilable(Date)) }
38
- # def review_date=(review_date); end
37
+ # include StoreAccessors
39
38
  #
40
- # sig { returns(T.nilable(Date)) }
41
- # def review_date; end
39
+ # module StoreAccessors
40
+ # sig { params(review_date: T.nilable(Date)).returns(T.nilable(Date)) }
41
+ # def review_date=(review_date); end
42
42
  #
43
- # sig { returns(T.nilable(Date)) }
44
- # def review_date_was; end
43
+ # sig { returns(T.nilable(Date)) }
44
+ # def review_date; end
45
45
  #
46
- # sig { returns(T::Boolean) }
47
- # def review_date_changed?; end
46
+ # sig { returns(T.nilable(Date)) }
47
+ # def review_date_was; end
48
48
  #
49
- # sig { returns(T.nilable(Date)) }
50
- # def review_date_before_last_save; end
49
+ # sig { returns(T::Boolean) }
50
+ # def review_date_changed?; end
51
51
  #
52
- # sig { returns(T::Boolean) }
53
- # def saved_change_to_review_date?; end
52
+ # sig { returns(T.nilable(Date)) }
53
+ # def review_date_before_last_save; end
54
54
  #
55
- # sig { returns(T.nilable([T.nilable(Date), T.nilable(Date)])) }
56
- # def review_date_change; end
55
+ # sig { returns(T::Boolean) }
56
+ # def saved_change_to_review_date?; end
57
57
  #
58
- # sig { returns(T.nilable([T.nilable(Date), T.nilable(Date)])) }
59
- # def saved_change_to_review_date; end
58
+ # sig { returns(T.nilable([T.nilable(Date), T.nilable(Date)])) }
59
+ # def review_date_change; end
60
60
  #
61
- # sig { params(reviewd: T::Boolean).returns(T::Boolean) }
62
- # def reviewed=(reviewed); end
61
+ # sig { returns(T.nilable([T.nilable(Date), T.nilable(Date)])) }
62
+ # def saved_change_to_review_date; end
63
63
  #
64
- # sig { returns(T::Boolean) }
65
- # def reviewed; end
64
+ # sig { params(reviewd: T::Boolean).returns(T::Boolean) }
65
+ # def reviewed=(reviewed); end
66
66
  #
67
- # sig { returns(T::Boolean) }
68
- # def reviewed_was; end
67
+ # sig { returns(T::Boolean) }
68
+ # def reviewed; end
69
69
  #
70
- # sig { returns(T::Boolean) }
71
- # def reviewed_changed?; end
70
+ # sig { returns(T::Boolean) }
71
+ # def reviewed_was; end
72
72
  #
73
- # sig { returns(T::Boolean) }
74
- # def reviewed_before_last_save; end
73
+ # sig { returns(T::Boolean) }
74
+ # def reviewed_changed?; end
75
75
  #
76
- # sig { returns(T::Boolean) }
77
- # def saved_change_to_reviewed?; end
76
+ # sig { returns(T::Boolean) }
77
+ # def reviewed_before_last_save; end
78
78
  #
79
- # sig { returns(T.nilable([T::Boolean, T::Boolean])) }
80
- # def reviewed_change; end
79
+ # sig { returns(T::Boolean) }
80
+ # def saved_change_to_reviewed?; end
81
81
  #
82
- # sig { returns(T.nilable([T::Boolean, T::Boolean])) }
83
- # def saved_change_to_reviewed; end
82
+ # sig { returns(T.nilable([T::Boolean, T::Boolean])) }
83
+ # def reviewed_change; end
84
+ #
85
+ # sig { returns(T.nilable([T::Boolean, T::Boolean])) }
86
+ # def saved_change_to_reviewed; end
87
+ # end
84
88
  # end
85
89
  # ~~~
86
90
  class ActiveRecordTypedStore < Base
@@ -105,7 +109,11 @@ module Tapioca
105
109
  type = type_for(field.type_sym)
106
110
  type = "T.nilable(#{type})" if field.null && type != "T.untyped"
107
111
 
108
- generate_methods(model, field.name.to_s, type)
112
+ model.create_module("StoreAccessors") do |store_accessors_module|
113
+ generate_methods(store_accessors_module, field.name.to_s, type)
114
+ end
115
+
116
+ model.create_include("StoreAccessors")
109
117
  end
110
118
  end
111
119
  end
@@ -9,8 +9,6 @@ rescue LoadError
9
9
  return
10
10
  end
11
11
 
12
- return unless Tapioca::Compilers::Sorbet.supports?(:mixes_in_class_methods_multiple_args)
13
-
14
12
  module Tapioca
15
13
  module Compilers
16
14
  module Dsl
@@ -6,6 +6,8 @@ require "tapioca/rbi_ext/model"
6
6
  module Tapioca
7
7
  module Compilers
8
8
  module Dsl
9
+ COMPILERS_PATH = T.let(File.expand_path("..", __FILE__).to_s, String)
10
+
9
11
  class Base
10
12
  extend T::Sig
11
13
  extend T::Helpers
@@ -17,9 +19,13 @@ module Tapioca
17
19
  sig { returns(T::Set[Module]) }
18
20
  attr_reader :processable_constants
19
21
 
22
+ sig { returns(T::Array[String]) }
23
+ attr_reader :errors
24
+
20
25
  sig { void }
21
26
  def initialize
22
27
  @processable_constants = T.let(Set.new(gather_constants), T::Set[Module])
28
+ @errors = T.let([], T::Array[String])
23
29
  end
24
30
 
25
31
  sig { params(constant: Module).returns(T::Boolean) }
@@ -41,6 +47,12 @@ module Tapioca
41
47
  sig { abstract.returns(T::Enumerable[Module]) }
42
48
  def gather_constants; end
43
49
 
50
+ # NOTE: This should eventually accept an `Error` object or `Exception` rather than simply a `String`.
51
+ sig { params(error: String).void }
52
+ def add_error(error)
53
+ @errors << error
54
+ end
55
+
44
56
  private
45
57
 
46
58
  sig { returns(T::Enumerable[Class]) }
@@ -2,7 +2,6 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  begin
5
- require "rails/railtie"
6
5
  require "identity_cache"
7
6
  rescue LoadError
8
7
  # means IdentityCache is not installed,