tapioca 0.11.15 → 0.11.17

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: 4f089694c098df2b42355826c7622448368a7f766bd44de3cd2ece4242ebc0fa
4
- data.tar.gz: 0140650be1098b51150d5a74fac1f349e9a8da6400fcc5ad3ed1e0da84fb7a91
3
+ metadata.gz: 8a74fea1cf7f8de26296fa1fb617362cceac2f800740cfaac4e95b8b0106dfc9
4
+ data.tar.gz: 911ce69b722d3b30d07f2806ae155e0c390d015eea45ba85bf69c0796e6b5bf5
5
5
  SHA512:
6
- metadata.gz: 95bbca796e14a6af94dbbaa9f15a3cd15c46d414472b03385ab7ff6b8ab495ba885ee362e7349bbdc7ed0c60639d22fb2db52e0d901faab582fca96a93dfd408
7
- data.tar.gz: 179e7b4ef4c30819d7a3e1c3cb856b800ffc17c63d20419341002b4273b56c9cec2d9d06e03d28678275ac2c9e8e82030f7cfb2d8f5bf4bdc99ead5e361d001a
6
+ metadata.gz: 3fa1c5dcd97eee61f4385ba685c8f0b7e9290bc9a79822bdf46dcbe681b7b40bcef256cf8a293911b4bcbcb39ea325b1280da1eabbfde069d6d21161d1a6a6b0
7
+ data.tar.gz: b7d3e1b1e0341bd8b835daa3f4a405271a97376eca922ea46a5680b7d388560426819d77ea9b64623f91deb68d76548372b78488d626386f5cfb35ca6531934b
@@ -0,0 +1,100 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ begin
5
+ require "action_text"
6
+ rescue LoadError
7
+ return
8
+ end
9
+
10
+ module Tapioca
11
+ module Dsl
12
+ module Compilers
13
+ # `Tapioca::Dsl::Compilers::ActionText` decorates RBI files for subclasses of
14
+ # `ActiveRecord::Base` that declare [has_rich_text](https://edgeguides.rubyonrails.org/action_text_overview.html#creating-rich-text-content)
15
+ #
16
+ # For example, with the following `ActiveRecord::Base` subclass:
17
+ #
18
+ # ~~~rb
19
+ # class Post < ApplicationRecord
20
+ # has_rich_text :body
21
+ # has_rich_text :title, encrypted: true
22
+ # end
23
+ # ~~~
24
+ #
25
+ # this compiler will produce the RBI file `post.rbi` with the following content:
26
+ #
27
+ # ~~~rbi
28
+ # # typed: strong
29
+ #
30
+ # class Post
31
+ # sig { returns(ActionText::RichText) }
32
+ # def body; end
33
+ #
34
+ # sig { params(value: T.nilable(T.any(ActionText::RichText, String))).returns(T.untyped) }
35
+ # def body=(value); end
36
+ #
37
+ # sig { returns(T::Boolean) }
38
+ # def body?; end
39
+ #
40
+ # sig { returns(ActionText::EncryptedRichText) }
41
+ # def title; end
42
+ #
43
+ # sig { params(value: T.nilable(T.any(ActionText::EncryptedRichText, String))).returns(T.untyped) }
44
+ # def title=(value); end
45
+ #
46
+ # sig { returns(T::Boolean) }
47
+ # def title?; end
48
+ # end
49
+ # ~~~
50
+ class ActionText < Compiler
51
+ extend T::Sig
52
+
53
+ ConstantType = type_member { { fixed: T.class_of(::ActiveRecord::Base) } }
54
+
55
+ sig { override.void }
56
+ def decorate
57
+ root.create_path(constant) do |scope|
58
+ self.class.action_text_associations(constant).each do |name|
59
+ reflection = constant.reflections.fetch(name)
60
+ type = reflection.options.fetch(:class_name)
61
+ name = reflection.name.to_s.sub("rich_text_", "")
62
+ scope.create_method(
63
+ name,
64
+ return_type: type,
65
+ )
66
+ scope.create_method(
67
+ "#{name}?",
68
+ return_type: "T::Boolean",
69
+ )
70
+ scope.create_method(
71
+ "#{name}=",
72
+ parameters: [create_param("value", type: "T.nilable(T.any(#{type}, String))")],
73
+ return_type: "T.untyped",
74
+ )
75
+ end
76
+ end
77
+ end
78
+
79
+ class << self
80
+ extend T::Sig
81
+
82
+ sig { params(constant: T.class_of(::ActiveRecord::Base)).returns(T::Array[String]) }
83
+ def action_text_associations(constant)
84
+ # Implementation copied from https://github.com/rails/rails/blob/31052d0e518b9da103eea2f79d250242ed1e3705/actiontext/lib/action_text/attribute.rb#L66
85
+ constant.reflect_on_all_associations(:has_one)
86
+ .map(&:name).map(&:to_s)
87
+ .select { |n| n.start_with?("rich_text_") }
88
+ end
89
+
90
+ sig { override.returns(T::Enumerable[Module]) }
91
+ def gather_constants
92
+ descendants_of(::ActiveRecord::Base)
93
+ .reject(&:abstract_class?)
94
+ .select { |c| action_text_associations(c).any? }
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -83,6 +83,7 @@ module Tapioca
83
83
  end
84
84
  def perform_later_parameters(parameters, constant_name)
85
85
  if ::Gem::Requirement.new(">= 7.0").satisfied_by?(::ActiveJob.gem_version)
86
+ parameters.reject! { |typed_param| RBI::BlockParam === typed_param.param }
86
87
  parameters + [create_block_param(
87
88
  "block",
88
89
  type: "T.nilable(T.proc.params(job: #{constant_name}).void)",
@@ -55,7 +55,10 @@ module Tapioca
55
55
  root.create_path(constant) do |input_object|
56
56
  arguments.each do |argument|
57
57
  name = argument.keyword.to_s
58
- input_object.create_method(name, return_type: Helpers::GraphqlTypeHelper.type_for(argument, constant))
58
+ input_object.create_method(
59
+ name,
60
+ return_type: Helpers::GraphqlTypeHelper.type_for_argument(argument, constant),
61
+ )
59
62
  end
60
63
  end
61
64
  end
@@ -76,7 +76,7 @@ module Tapioca
76
76
  def argument_type(argument, constant)
77
77
  return "T.untyped" unless argument
78
78
 
79
- Helpers::GraphqlTypeHelper.type_for(argument, constant)
79
+ Helpers::GraphqlTypeHelper.type_for_argument(argument, constant)
80
80
  end
81
81
 
82
82
  class << self
@@ -224,7 +224,6 @@ module Tapioca
224
224
  def create_aliased_fetch_by_methods(field, klass)
225
225
  type, _ = Helpers::ActiveRecordColumnTypeHelper.new(constant).type_for(field.alias_name.to_s)
226
226
  multi_type = type.delete_prefix("T.nilable(").delete_suffix(")").delete_prefix("::")
227
- length = field.key_fields.length
228
227
  suffix = field.send(:fetch_method_suffix)
229
228
 
230
229
  parameters = field.key_fields.map do |arg|
@@ -238,14 +237,12 @@ module Tapioca
238
237
  return_type: type,
239
238
  )
240
239
 
241
- if length == 1
242
- klass.create_method(
243
- "fetch_multi_#{suffix}",
244
- class_method: true,
245
- parameters: [create_param("keys", type: "T::Enumerable[T.untyped]")],
246
- return_type: COLLECTION_TYPE.call(multi_type),
247
- )
248
- end
240
+ klass.create_method(
241
+ "fetch_multi_#{suffix}",
242
+ class_method: true,
243
+ parameters: [create_param("keys", type: "T::Enumerable[T.untyped]")],
244
+ return_type: COLLECTION_TYPE.call(multi_type),
245
+ )
249
246
  end
250
247
  end
251
248
  end
@@ -85,6 +85,9 @@ module Tapioca
85
85
  "::String"
86
86
  when ActiveRecord::Type::Serialized
87
87
  serialized_column_type(column_type)
88
+ when defined?(ActiveRecord::Normalization::NormalizedValueType) &&
89
+ ActiveRecord::Normalization::NormalizedValueType
90
+ type_for_activerecord_value(column_type.cast_type)
88
91
  when defined?(ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Uuid) &&
89
92
  ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Uuid
90
93
  "::String"
@@ -15,7 +15,7 @@ module Tapioca
15
15
  constant: T.any(T.class_of(GraphQL::Schema::Mutation), T.class_of(GraphQL::Schema::InputObject)),
16
16
  ).returns(String)
17
17
  end
18
- def type_for(argument, constant)
18
+ def type_for_argument(argument, constant)
19
19
  type = if argument.loads
20
20
  loads_type = ::GraphQL::Schema::Wrapper.new(argument.loads)
21
21
  loads_type = loads_type.to_list_type if argument.type.list?
@@ -25,6 +25,36 @@ module Tapioca
25
25
  argument.type
26
26
  end
27
27
 
28
+ prepare = argument.prepare
29
+ prepare_method = if prepare.is_a?(Symbol) || prepare.is_a?(String)
30
+ if constant.respond_to?(prepare)
31
+ constant.method(prepare.to_sym)
32
+ end
33
+ end
34
+
35
+ type_for(
36
+ type,
37
+ ignore_nilable_wrapper: has_replaceable_default?(argument),
38
+ prepare_method: prepare_method,
39
+ )
40
+ end
41
+
42
+ sig do
43
+ params(
44
+ type: T.any(
45
+ GraphQL::Schema::Wrapper,
46
+ T.class_of(GraphQL::Schema::Scalar),
47
+ T.class_of(GraphQL::Schema::Enum),
48
+ T.class_of(GraphQL::Schema::Union),
49
+ T.class_of(GraphQL::Schema::Object),
50
+ T.class_of(GraphQL::Schema::Interface),
51
+ T.class_of(GraphQL::Schema::InputObject),
52
+ ),
53
+ ignore_nilable_wrapper: T::Boolean,
54
+ prepare_method: T.nilable(Method),
55
+ ).returns(String)
56
+ end
57
+ def type_for(type, ignore_nilable_wrapper: false, prepare_method: nil)
28
58
  unwrapped_type = type.unwrap
29
59
 
30
60
  parsed_type = case unwrapped_type
@@ -65,14 +95,11 @@ module Tapioca
65
95
  "T.untyped"
66
96
  end
67
97
 
68
- if argument.prepare.is_a?(Symbol) || argument.prepare.is_a?(String)
69
- if constant.respond_to?(argument.prepare)
70
- prepare_method = constant.method(argument.prepare)
71
- prepare_signature = Runtime::Reflection.signature_of(prepare_method)
72
-
73
- if valid_return_type?(prepare_signature&.return_type)
74
- parsed_type = prepare_signature.return_type&.to_s
75
- end
98
+ if prepare_method
99
+ prepare_signature = Runtime::Reflection.signature_of(prepare_method)
100
+ prepare_return_type = prepare_signature&.return_type
101
+ if valid_return_type?(prepare_return_type)
102
+ parsed_type = prepare_return_type&.to_s
76
103
  end
77
104
  end
78
105
 
@@ -80,7 +107,7 @@ module Tapioca
80
107
  parsed_type = "T::Array[#{parsed_type}]"
81
108
  end
82
109
 
83
- unless type.non_null? || has_replaceable_default?(argument)
110
+ unless type.non_null? || ignore_nilable_wrapper
84
111
  parsed_type = RBIHelper.as_nilable_type(parsed_type)
85
112
  end
86
113
 
@@ -391,6 +391,7 @@ module Tapioca
391
391
  def skip_alias?(name, constant)
392
392
  return true if symbol_in_payload?(name)
393
393
  return true unless constant_in_gem?(name)
394
+ return true if has_aliased_namespace?(name)
394
395
 
395
396
  false
396
397
  end
@@ -255,6 +255,23 @@ module Tapioca
255
255
  def method_defined_by_forwardable_module?(method)
256
256
  method.source_location&.first == Object.const_source_location(:Forwardable)&.first
257
257
  end
258
+
259
+ sig { params(name: String).returns(T::Boolean) }
260
+ def has_aliased_namespace?(name)
261
+ name_parts = name.split("::")
262
+ name_parts.pop # drop the constant name, leaving just the namespace
263
+
264
+ name_parts.each_with_object([]) do |name_part, namespaces|
265
+ namespaces << "#{namespaces.last}::#{name_part}".delete_prefix("::")
266
+ end.any? do |namespace|
267
+ constant = constantize(namespace)
268
+ next unless Module === constant
269
+
270
+ # If the constant name doesn't match the namespace,
271
+ # the namespace must contain an alias
272
+ name_of(constant) != namespace
273
+ end
274
+ end
258
275
  end
259
276
  end
260
277
  end
@@ -2,5 +2,5 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Tapioca
5
- VERSION = "0.11.15"
5
+ VERSION = "0.11.17"
6
6
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tapioca
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.11.15
4
+ version: 0.11.17
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ufuk Kayserilioglu
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: exe
13
13
  cert_chain: []
14
- date: 2024-01-08 00:00:00.000000000 Z
14
+ date: 2024-01-15 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: bundler
@@ -173,6 +173,7 @@ files:
173
173
  - lib/tapioca/dsl/compilers/aasm.rb
174
174
  - lib/tapioca/dsl/compilers/action_controller_helpers.rb
175
175
  - lib/tapioca/dsl/compilers/action_mailer.rb
176
+ - lib/tapioca/dsl/compilers/action_text.rb
176
177
  - lib/tapioca/dsl/compilers/active_job.rb
177
178
  - lib/tapioca/dsl/compilers/active_model_attributes.rb
178
179
  - lib/tapioca/dsl/compilers/active_model_secure_password.rb