tapioca 0.5.1 → 0.5.5

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 (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,