tapioca 0.10.5 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9df4814fb4f17b9f1d53aab7a9b9b23498cfea0330fb48b47d909af119ac6ea0
4
- data.tar.gz: 7f2d233a47a7ceb2b4a1f4894c794d65c2cca9c9e1547022630d7b395de0cd47
3
+ metadata.gz: f32d4fd559ee85f540926b3faa454aaf2ddd919b502438bf2deda24a4b04f855
4
+ data.tar.gz: 9368c2329923554e639310003630d36f9bf7a2f895df619cfe844dacd9a2c176
5
5
  SHA512:
6
- metadata.gz: f65d462c261005c62b31f0864df29b550d5a07551433203f485f8f66c57a8e100fccdb1deb1b7f29e185a1f6e8bf5b4e9f59387a1ba24ea8b4cff168b1b4ca12
7
- data.tar.gz: 721c40a48f379eda0e6302ff59b2a3228f0262c5d2b2bac1111016d92ec02597e9d35c28a38a6fcc903a8ac13b0b8c4dddcc7e1d747907390762f47601d19e68
6
+ metadata.gz: c997e7598a5cef84e52935b0f9681ca636fef5fb866d30f1e6d19e2912ae38b5ec7499e69cde6cd147b4a723214d80abb109dbd523e48f143eae0b1edbefd46e
7
+ data.tar.gz: 33882ab5de1b8b43d775bdb20b0fab39d97ebe2ce7b346bb9828b64e27dd99bb7d46814ac441dd1b6b7aadb3b4aa02f93a652d4e6fa42c34ac83241eb9eb78d7
data/lib/tapioca/cli.rb CHANGED
@@ -130,11 +130,15 @@ module Tapioca
130
130
  type: :string,
131
131
  desc: "The path to the Rails application",
132
132
  default: "."
133
- def dsl(*constants)
133
+ def dsl(*constant_or_paths)
134
134
  set_environment(options)
135
135
 
136
+ # Assume anything starting with a capital letter or colon is a class, otherwise a path
137
+ constants, paths = constant_or_paths.partition { |c| c =~ /\A[A-Z:]/ }
138
+
136
139
  command = Commands::Dsl.new(
137
140
  requested_constants: constants,
141
+ requested_paths: paths.map { |p| Pathname.new(p) },
138
142
  outpath: Pathname.new(options[:outdir]),
139
143
  only: options[:only],
140
144
  exclude: options[:exclude],
@@ -189,6 +189,8 @@ module Tapioca
189
189
 
190
190
  sig { params(name: String, content: String).returns(String) }
191
191
  def add_header(name, content)
192
+ # WARNING: Changing this header could impact how GitHub determines if the file should be hidden:
193
+ # https://github.com/github/linguist/pull/6143
192
194
  header = <<~COMMENT
193
195
  # DO NOT EDIT MANUALLY
194
196
  # This file was pulled from a central RBI files repository.
@@ -41,6 +41,7 @@ module Tapioca
41
41
  create_file(@sorbet_config, <<~CONTENT, skip: true, force: false)
42
42
  --dir
43
43
  .
44
+ --ignore=tmp/
44
45
  --ignore=vendor/
45
46
  CONTENT
46
47
  end
@@ -10,6 +10,7 @@ module Tapioca
10
10
  sig do
11
11
  params(
12
12
  requested_constants: T::Array[String],
13
+ requested_paths: T::Array[Pathname],
13
14
  outpath: Pathname,
14
15
  only: T::Array[String],
15
16
  exclude: T::Array[String],
@@ -27,6 +28,7 @@ module Tapioca
27
28
  end
28
29
  def initialize(
29
30
  requested_constants:,
31
+ requested_paths:,
30
32
  outpath:,
31
33
  only:,
32
34
  exclude:,
@@ -42,6 +44,7 @@ module Tapioca
42
44
  app_root: "."
43
45
  )
44
46
  @requested_constants = requested_constants
47
+ @requested_paths = requested_paths
45
48
  @outpath = outpath
46
49
  @only = only
47
50
  @exclude = exclude
@@ -63,7 +66,7 @@ module Tapioca
63
66
  def list_compilers
64
67
  Loaders::Dsl.load_application(
65
68
  tapioca_path: @tapioca_path,
66
- eager_load: @requested_constants.empty?,
69
+ eager_load: @requested_constants.empty? && @requested_paths.empty?,
67
70
  app_root: @app_root,
68
71
  )
69
72
 
@@ -101,6 +104,15 @@ module Tapioca
101
104
  end
102
105
  say("")
103
106
 
107
+ unless @requested_paths.empty?
108
+ constants_from_paths = Static::SymbolLoader.symbols_from_paths(@requested_paths).to_a
109
+ if constants_from_paths.empty?
110
+ say_error("\nWarning: No constants found in: #{@requested_paths.map(&:to_s).join(", ")}", :yellow)
111
+ end
112
+
113
+ @requested_constants += constants_from_paths
114
+ end
115
+
104
116
  outpath = @should_verify ? Pathname.new(Dir.mktmpdir) : @outpath
105
117
  rbi_files_to_purge = existing_rbi_filenames(@requested_constants)
106
118
 
@@ -153,6 +165,7 @@ module Tapioca
153
165
  def create_pipeline
154
166
  Tapioca::Dsl::Pipeline.new(
155
167
  requested_constants: constantize(@requested_constants),
168
+ requested_paths: @requested_paths,
156
169
  requested_compilers: constantize_compilers(@only),
157
170
  excluded_compilers: constantize_compilers(@exclude),
158
171
  error_handler: ->(error) {
@@ -167,8 +180,9 @@ module Tapioca
167
180
  filenames = if requested_constants.empty?
168
181
  Pathname.glob(path / "**/*.rbi")
169
182
  else
170
- requested_constants.map do |constant_name|
171
- dsl_rbi_filename(constant_name)
183
+ requested_constants.filter_map do |constant_name|
184
+ filename = dsl_rbi_filename(constant_name)
185
+ filename if File.exist?(filename)
172
186
  end
173
187
  end
174
188
 
@@ -58,27 +58,67 @@ module Tapioca
58
58
  T::Array[String],
59
59
  )
60
60
 
61
+ # Taken directly from the AASM::Base class, here:
62
+ # https://github.com/aasm/aasm/blob/0e03746a2b86558ee1bf7bd7db873938cbb3b29b/lib/aasm/base.rb#L145-L171
63
+ GLOBAL_CALLBACKS =
64
+ T.let(
65
+ [
66
+ "after_all_transitions",
67
+ "after_all_transactions",
68
+ "before_all_transactions",
69
+ "before_all_events",
70
+ "after_all_events",
71
+ "error_on_all_events",
72
+ "ensure_on_all_events",
73
+ ].freeze,
74
+ T::Array[String],
75
+ )
76
+
61
77
  ConstantType = type_member { { fixed: T.all(::AASM::ClassMethods, Class) } }
62
78
 
63
79
  sig { override.void }
64
80
  def decorate
65
- aasm = constant.aasm
66
- return if !aasm || aasm.states.empty?
81
+ state_machine_store = ::AASM::StateMachineStore.fetch(constant)
82
+ return unless state_machine_store
83
+
84
+ state_machines = state_machine_store.machine_names.map { |n| constant.aasm(n) }
85
+ return if state_machines.all? { |m| m.states.empty? }
67
86
 
68
87
  root.create_path(constant) do |model|
69
- # Create all of the constants and methods for each state
70
- aasm.states.each do |state|
71
- model.create_constant("STATE_#{state.name.upcase}", value: "T.let(T.unsafe(nil), Symbol)")
72
- model.create_method("#{state.name}?", return_type: "T::Boolean")
73
- end
88
+ state_machines.each do |state_machine|
89
+ namespace = state_machine.__send__(:namespace)
74
90
 
75
- # Create all of the methods for each event
76
- parameters = [create_rest_param("opts", type: "T.untyped")]
77
- aasm.events.each do |event|
78
- model.create_method(event.name.to_s, parameters: parameters)
79
- model.create_method("#{event.name}!", parameters: parameters)
80
- model.create_method("#{event.name}_without_validation!", parameters: parameters)
81
- model.create_method("may_#{event.name}?", return_type: "T::Boolean")
91
+ # Create all of the constants and methods for each state
92
+ state_machine.states.each do |state|
93
+ name = state.name
94
+ name = "#{namespace}_#{name}" if namespace
95
+
96
+ model.create_constant("STATE_#{name.upcase}", value: "T.let(T.unsafe(nil), Symbol)")
97
+ model.create_method("#{name}?", return_type: "T::Boolean")
98
+ end
99
+
100
+ # Create all of the methods for each event
101
+ parameters = [create_rest_param("opts", type: "T.untyped")]
102
+ state_machine.events.each do |event|
103
+ model.create_method(event.name.to_s, parameters: parameters)
104
+ model.create_method("#{event.name}!", parameters: parameters)
105
+ model.create_method("#{event.name}_without_validation!", parameters: parameters)
106
+ model.create_method("may_#{event.name}?", return_type: "T::Boolean")
107
+
108
+ # For events, if there's a namespace the default methods are created in addition to
109
+ # namespaced ones.
110
+ next unless namespace
111
+
112
+ name = "#{event.name}_#{namespace}"
113
+
114
+ model.create_method(name.to_s, parameters: parameters)
115
+ model.create_method("#{name}!", parameters: parameters)
116
+ model.create_method("may_#{name}?", return_type: "T::Boolean")
117
+
118
+ # There's no namespaced method created for `_without_validation`, so skip
119
+ # defining a method for:
120
+ # "#{name}_without_validation!"
121
+ end
82
122
  end
83
123
 
84
124
  # Create the overall state machine method, which will return an
@@ -106,6 +146,18 @@ module Tapioca
106
146
  ],
107
147
  )
108
148
 
149
+ constant_name = name_of(constant)
150
+
151
+ GLOBAL_CALLBACKS.each do |method|
152
+ machine.create_method(
153
+ method,
154
+ parameters: [
155
+ create_opt_param("symbol", type: "T.nilable(Symbol)", default: "nil"),
156
+ create_block_param("block", type: "T.nilable(T.proc.bind(#{constant_name}).void)"),
157
+ ],
158
+ )
159
+ end
160
+
109
161
  # Create a private event class that we can pass around for the
110
162
  # purpose of binding all of the callbacks without having to
111
163
  # explicitly bind self in each one.
@@ -115,7 +167,7 @@ module Tapioca
115
167
  method,
116
168
  parameters: [
117
169
  create_opt_param("symbol", type: "T.nilable(Symbol)", default: "nil"),
118
- create_block_param("block", type: "T.nilable(T.proc.bind(#{name_of(constant)}).void)"),
170
+ create_block_param("block", type: "T.nilable(T.proc.bind(#{constant_name}).void)"),
119
171
  ],
120
172
  )
121
173
  end
@@ -156,7 +156,7 @@ module Tapioca
156
156
  sig { params(mod: Module).returns(T::Array[String]) }
157
157
  def gather_includes(mod)
158
158
  mod.ancestors
159
- .reject { |ancestor| ancestor.is_a?(Class) || ancestor == mod || ancestor.name.nil? }
159
+ .reject { |ancestor| ancestor.is_a?(Class) || ancestor == mod || name_of(ancestor).nil? }
160
160
  .map { |ancestor| T.must(qualified_name_of(ancestor)) }
161
161
  .reverse
162
162
  end
@@ -16,7 +16,8 @@ module Tapioca
16
16
  # `Tapioca::Dsl::Compilers::ActiveRecordColumns` refines RBI files for subclasses of
17
17
  # [`ActiveRecord::Base`](https://api.rubyonrails.org/classes/ActiveRecord/Base.html).
18
18
  # This compiler is only responsible for defining the attribute methods that would be
19
- # created for the columns that are defined in the Active Record model.
19
+ # created for columns and virtual attributes that are defined in the Active Record
20
+ # model.
20
21
  #
21
22
  # For example, with the following model class:
22
23
  # ~~~rb
@@ -109,8 +110,7 @@ module Tapioca
109
110
 
110
111
  root.create_path(constant) do |model|
111
112
  model.create_module(AttributeMethodsModuleName) do |mod|
112
- constant.columns_hash.each_key do |column_name|
113
- column_name = column_name.to_s
113
+ constant.attribute_names.each do |column_name|
114
114
  add_methods_for_attribute(mod, column_name)
115
115
  end
116
116