tapioca 0.16.5 → 0.16.6

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: 8efd13a85ae108c14208f510ff83132f745b956a841afe2dd870ec35f67f19f6
4
- data.tar.gz: ef25365cfc4c0b2442e57a9309f9554254f7ba27af19a77e7f187e1e7222fb27
3
+ metadata.gz: 5a42a478b0396f43dd9e6505c18ac69ca8c32e6ef00b298e6d6e4a8c12536b17
4
+ data.tar.gz: f24ab38ee1d78f1633f7d580ea5be8f93e13c9c6550d11b5d5bc4171d974929b
5
5
  SHA512:
6
- metadata.gz: acfd5b028a620f619372ec911269e2c0386c5ab0e4a59d6f2d76718875e7b027518a4334f4eb5df6d5f5c8fc07982456b52c55c026bcb0e9db08a7c24435a7fa
7
- data.tar.gz: 40b55848a0aa050064596c194a1c9ead65b1ac45ea34ec14d2dfad1d5fe14c89f97da8a35ad6e02d36ce478cbe9f56928246d15a243b8949ee1a1174a25a95fc
6
+ metadata.gz: f623d3b5e66a081728d9ae4b3dde4c6c976829ffbd6e606a64def2e8aff489c09911396ca72146c901f72a11d432d6c86ae840e278b8d85fd8ee756e97776542
7
+ data.tar.gz: e7686bcfed28858bfb8f162ee0bd8a7c9a6cdf286c7227a86032025934b04fa72c29f21c41d7a3c5668edb8aad2d39bf2808a2d98799a7434e94aa9b921c3cf8
data/README.md CHANGED
@@ -999,7 +999,16 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/Shopif
999
999
 
1000
1000
  ### DSL compilers
1001
1001
 
1002
- Contributions to existing DSL compilers are welcome. However, new compilers that support DSLs for gems other than Rails should live outside of Tapioca. Please refer to [writing custom dsl compilers](https://github.com/Shopify/tapioca?tab=readme-ov-file#writing-custom-dsl-compilers) for more information.
1002
+ Tapioca ships with a small collection of high quality DSL compilers for popular Ruby gems that are used heavily at Shopify, like Rails and GraphQL. We encourage the community to contribute new DSL compilers, though they shouldn't necessarily live in the Tapioca repo itself.
1003
+
1004
+ It's best for DSL compilers to be contributed directly to gems they apply to ([example](https://github.com/Shopify/measured/tree/main/lib/tapioca/dsl/compilers)). This way, when changes are made to the gem's DSL, the gem's DSL compiler can be updated at the same time and be versioned/released together.
1005
+
1006
+ If an upstream gem's maintainers don't wish to host a DSL compiler themselves, you can propose contributing it to:
1007
+
1008
+ 1. Tapioca, if it's a gem that Shopify uses (ask us in an issue or PR)
1009
+ 2. A third party DSL compiler repository, like [AngelList/Boba](https://github.com/angellist/boba). These are not supported by Shopify.
1010
+
1011
+ For help writing a DSL compiler, please refer to [writing custom dsl compilers](https://github.com/Shopify/tapioca?tab=readme-ov-file#writing-custom-dsl-compilers).
1003
1012
 
1004
1013
  ## License
1005
1014
 
data/lib/tapioca/cli.rb CHANGED
@@ -145,7 +145,7 @@ module Tapioca
145
145
  default: {}
146
146
  option :lsp_addon,
147
147
  type: :boolean,
148
- desc: "Generate DSL RBIs from the LSP addon. Internal to tapioca and not intended for end-users",
148
+ desc: "Generate DSL RBIs from the LSP add-on. Internal to Tapioca and not intended for end-users",
149
149
  default: false,
150
150
  hide: true
151
151
  def dsl(*constant_or_paths)
@@ -266,6 +266,11 @@ module Tapioca
266
266
  type: :boolean,
267
267
  desc: "Halt upon a load error while loading the Rails application",
268
268
  default: true
269
+ option :lsp_addon,
270
+ type: :boolean,
271
+ desc: "Generate Gem RBIs from the LSP add-on. Internal to Tapioca and not intended for end-users",
272
+ default: false,
273
+ hide: true
269
274
  def gem(*gems)
270
275
  set_environment(options)
271
276
 
@@ -300,6 +305,7 @@ module Tapioca
300
305
  dsl_dir: options[:dsl_dir],
301
306
  rbi_formatter: rbi_formatter(options),
302
307
  halt_upon_load_error: options[:halt_upon_load_error],
308
+ lsp_addon: options[:lsp_addon],
303
309
  }
304
310
 
305
311
  command = if verify
@@ -27,6 +27,7 @@ module Tapioca
27
27
  dsl_dir: String,
28
28
  rbi_formatter: RBIFormatter,
29
29
  halt_upon_load_error: T::Boolean,
30
+ lsp_addon: T.nilable(T::Boolean),
30
31
  ).void
31
32
  end
32
33
  def initialize(
@@ -45,7 +46,8 @@ module Tapioca
45
46
  auto_strictness: true,
46
47
  dsl_dir: DEFAULT_DSL_DIR,
47
48
  rbi_formatter: DEFAULT_RBI_FORMATTER,
48
- halt_upon_load_error: true
49
+ halt_upon_load_error: true,
50
+ lsp_addon: false
49
51
  )
50
52
  @gem_names = gem_names
51
53
  @exclude = exclude
@@ -59,6 +61,7 @@ module Tapioca
59
61
  @auto_strictness = auto_strictness
60
62
  @dsl_dir = dsl_dir
61
63
  @rbi_formatter = rbi_formatter
64
+ @lsp_addon = lsp_addon
62
65
 
63
66
  super()
64
67
 
@@ -73,39 +76,6 @@ module Tapioca
73
76
 
74
77
  private
75
78
 
76
- sig { params(gem_names: T::Array[String]).returns(T::Array[Gemfile::GemSpec]) }
77
- def gems_to_generate(gem_names)
78
- return @bundle.dependencies if gem_names.empty?
79
-
80
- gem_names.each_with_object([]) do |gem_name, gems|
81
- gem = @bundle.gem(gem_name)
82
-
83
- if gem.nil?
84
- raise Thor::Error, set_color("Error: Cannot find gem '#{gem_name}'", :red)
85
- end
86
-
87
- gems.concat(gem_dependencies(gem)) if @include_dependencies
88
- gems << gem
89
- end
90
- end
91
-
92
- sig do
93
- params(
94
- gem: Gemfile::GemSpec,
95
- dependencies: T::Array[Gemfile::GemSpec],
96
- ).returns(T::Array[Gemfile::GemSpec])
97
- end
98
- def gem_dependencies(gem, dependencies = [])
99
- direct_dependencies = gem.dependencies.filter_map { |dependency| @bundle.gem(dependency.name) }
100
- gems = dependencies | direct_dependencies
101
-
102
- if direct_dependencies.empty?
103
- gems
104
- else
105
- direct_dependencies.reduce(gems) { |result, gem| gem_dependencies(gem, result) }
106
- end
107
- end
108
-
109
79
  sig { params(gem: Gemfile::GemSpec).void }
110
80
  def compile_gem_rbi(gem)
111
81
  gem_name = set_color(gem.name, :yellow, :bold)
@@ -46,6 +46,41 @@ module Tapioca
46
46
  ensure
47
47
  GitAttributes.create_generated_attribute_file(@outpath)
48
48
  end
49
+
50
+ sig { params(gem_names: T::Array[String]).returns(T::Array[Gemfile::GemSpec]) }
51
+ def gems_to_generate(gem_names)
52
+ return @bundle.dependencies if gem_names.empty?
53
+
54
+ (gem_names - @exclude).each_with_object([]) do |gem_name, gems|
55
+ gem = @bundle.gem(gem_name)
56
+
57
+ if gem.nil?
58
+ next if @lsp_addon
59
+
60
+ raise Thor::Error, set_color("Error: Cannot find gem '#{gem_name}'", :red)
61
+ end
62
+
63
+ gems.concat(gem_dependencies(gem)) if @include_dependencies
64
+ gems << gem
65
+ end
66
+ end
67
+
68
+ sig do
69
+ params(
70
+ gem: Gemfile::GemSpec,
71
+ dependencies: T::Array[Gemfile::GemSpec],
72
+ ).returns(T::Array[Gemfile::GemSpec])
73
+ end
74
+ def gem_dependencies(gem, dependencies = [])
75
+ direct_dependencies = gem.dependencies.filter_map { |dependency| @bundle.gem(dependency.name) }
76
+ gems = dependencies | direct_dependencies
77
+
78
+ if direct_dependencies.empty?
79
+ gems
80
+ else
81
+ direct_dependencies.reduce(gems) { |result, gem| gem_dependencies(gem, result) }
82
+ end
83
+ end
49
84
  end
50
85
  end
51
86
  end
@@ -101,26 +101,26 @@ module Tapioca
101
101
 
102
102
  sig { params(attribute_type_value: T.untyped).returns(::String) }
103
103
  def type_for(attribute_type_value)
104
- type = case attribute_type_value
104
+ case attribute_type_value
105
105
  when ActiveModel::Type::Boolean
106
- "T::Boolean"
106
+ as_nilable_type("T::Boolean")
107
107
  when ActiveModel::Type::Date
108
- "::Date"
108
+ as_nilable_type("::Date")
109
109
  when ActiveModel::Type::DateTime, ActiveModel::Type::Time
110
- "::Time"
110
+ as_nilable_type("::Time")
111
111
  when ActiveModel::Type::Decimal
112
- "::BigDecimal"
112
+ as_nilable_type("::BigDecimal")
113
113
  when ActiveModel::Type::Float
114
- "::Float"
114
+ as_nilable_type("::Float")
115
115
  when ActiveModel::Type::Integer
116
- "::Integer"
116
+ as_nilable_type("::Integer")
117
117
  when ActiveModel::Type::String
118
- "::String"
118
+ as_nilable_type("::String")
119
119
  else
120
- Helpers::ActiveModelTypeHelper.type_for(attribute_type_value)
120
+ type = Helpers::ActiveModelTypeHelper.type_for(attribute_type_value)
121
+ type = as_nilable_type(type) if Helpers::ActiveModelTypeHelper.assume_nilable?(attribute_type_value)
122
+ type
121
123
  end
122
-
123
- as_nilable_type(type)
124
124
  end
125
125
 
126
126
  sig { params(klass: RBI::Scope, method: String, type: String).void }
@@ -372,7 +372,7 @@ module Tapioca
372
372
  return_type: "T.self_type",
373
373
  )
374
374
 
375
- CALCULATION_METHODS.each do |method_name|
375
+ (CALCULATION_METHODS + [:size]).each do |method_name|
376
376
  case method_name
377
377
  when :average, :maximum, :minimum
378
378
  klass.create_method(
@@ -400,9 +400,9 @@ module Tapioca
400
400
  ],
401
401
  return_type: "T::Hash[T.untyped, Integer]",
402
402
  )
403
- when :sum
403
+ when :sum, :size
404
404
  klass.create_method(
405
- "sum",
405
+ method_name.to_s,
406
406
  parameters: [
407
407
  create_opt_param("column_name", type: "T.nilable(T.any(String, Symbol))", default: "nil"),
408
408
  create_block_param("block", type: "T.nilable(T.proc.params(record: T.untyped).returns(T.untyped))"),
@@ -586,6 +586,22 @@ module Tapioca
586
586
  parameters: parameters,
587
587
  return_type: return_type,
588
588
  )
589
+ when :select
590
+ [relation_methods_module, association_relation_methods_module].each do |mod|
591
+ mod.create_method(method_name.to_s) do |method|
592
+ method.add_rest_param("args")
593
+ method.add_block_param("blk")
594
+
595
+ method.add_sig do |sig|
596
+ sig.add_param("args", "T.untyped")
597
+ sig.return_type = mod == relation_methods_module ? RelationClassName : AssociationRelationClassName
598
+ end
599
+ method.add_sig do |sig|
600
+ sig.add_param("blk", "T.proc.params(record: #{constant_name}).returns(T::Boolean)")
601
+ sig.return_type = "T::Array[#{constant_name}]"
602
+ end
603
+ end
604
+ end
589
605
  else
590
606
  create_relation_method(
591
607
  method_name,
@@ -147,7 +147,17 @@ module Tapioca
147
147
  return "T.untyped" if type.nil?
148
148
 
149
149
  sorbet_type = if type.respond_to?(:sorbet_type)
150
+ line, file = type.method(:sorbet_type).source_location
151
+
152
+ $stderr.puts <<~MESSAGE
153
+ WARNING: `#sorbet_type` is deprecated. Please rename your method to `#__tapioca_type`."
154
+
155
+ Defined on line #{line} of #{file}
156
+ MESSAGE
157
+
150
158
  type.sorbet_type
159
+ elsif type.respond_to?(:__tapioca_type)
160
+ type.__tapioca_type
151
161
  elsif type == ::JsonApiClient::Schema::Types::Integer
152
162
  "::Integer"
153
163
  elsif type == ::JsonApiClient::Schema::Types::String
@@ -14,7 +14,8 @@ module Tapioca
14
14
  def type_for(type_value)
15
15
  return "T.untyped" if Runtime::GenericTypeRegistry.generic_type_instance?(type_value)
16
16
 
17
- type = lookup_return_type_of_method(type_value, :deserialize) ||
17
+ type = lookup_tapioca_type(type_value) ||
18
+ lookup_return_type_of_method(type_value, :deserialize) ||
18
19
  lookup_return_type_of_method(type_value, :cast) ||
19
20
  lookup_return_type_of_method(type_value, :cast_value) ||
20
21
  lookup_arg_type_of_method(type_value, :serialize) ||
@@ -22,6 +23,11 @@ module Tapioca
22
23
  type.to_s
23
24
  end
24
25
 
26
+ sig { params(type_value: T.untyped).returns(T::Boolean) }
27
+ def assume_nilable?(type_value)
28
+ !type_value.respond_to?(:__tapioca_type)
29
+ end
30
+
25
31
  private
26
32
 
27
33
  MEANINGLESS_TYPES = T.let(
@@ -39,6 +45,11 @@ module Tapioca
39
45
  !MEANINGLESS_TYPES.include?(type)
40
46
  end
41
47
 
48
+ sig { params(obj: T.untyped).returns(T.nilable(T::Types::Base)) }
49
+ def lookup_tapioca_type(obj)
50
+ T::Utils.coerce(obj.__tapioca_type) if obj.respond_to?(:__tapioca_type)
51
+ end
52
+
42
53
  sig { params(obj: T.untyped, method: Symbol).returns(T.nilable(T::Types::Base)) }
43
54
  def lookup_return_type_of_method(obj, method)
44
55
  return_type = lookup_signature_of_method(obj, method)&.return_type
@@ -211,6 +211,11 @@ module Tapioca
211
211
  ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Range === type
212
212
  }
213
213
  "T::Range[#{type_for_activerecord_value(column_type.subtype, column_nullability:)}]"
214
+ when ->(type) {
215
+ defined?(ActiveRecord::Locking::LockingType) &&
216
+ ActiveRecord::Locking::LockingType === type
217
+ }
218
+ as_non_nilable_if_persisted_and_not_nullable("::Integer", column_nullability:)
214
219
  else
215
220
  as_non_nilable_if_persisted_and_not_nullable(
216
221
  ActiveModelTypeHelper.type_for(column_type),
@@ -66,15 +66,24 @@ module Tapioca
66
66
 
67
67
  sig { returns([T::Enumerable[Spec], T::Array[String]]) }
68
68
  def materialize_deps
69
- deps = definition.locked_gems.dependencies.values
70
- materialized_dependencies = definition.resolve.materialize(deps)
71
- missing_spec_names = materialized_dependencies.missing_specs.map(&:name).to_set
72
- missing_specs = materialized_dependencies.missing_specs.map do |spec|
73
- "#{spec.name} (#{spec.version})"
74
- end
75
- materialized_dependencies = materialized_dependencies.to_a.reject do |spec|
76
- missing_spec_names.include?(spec.name)
69
+ deps = definition.locked_gems.dependencies.except(*@excluded_gems).values
70
+ resolve = definition.resolve
71
+ materialized_dependencies = resolve.materialize(deps)
72
+
73
+ if Bundler::VERSION >= "2.6.0"
74
+ missing_specs = resolve.missing_specs.map do |spec|
75
+ "#{spec.name} (#{spec.version})"
76
+ end
77
+ else
78
+ missing_spec_names = materialized_dependencies.missing_specs.map(&:name).to_set
79
+ missing_specs = materialized_dependencies.missing_specs.map do |spec|
80
+ "#{spec.name} (#{spec.version})"
81
+ end
82
+ materialized_dependencies = materialized_dependencies.to_a.reject do |spec|
83
+ missing_spec_names.include?(spec.name)
84
+ end
77
85
  end
86
+
78
87
  [materialized_dependencies, missing_specs]
79
88
  end
80
89
 
@@ -85,7 +85,12 @@ module Tapioca
85
85
  def load_dsl_compilers
86
86
  say("Loading DSL compiler classes... ")
87
87
 
88
- # Load built-in compilers and custom compilers exported from gems
88
+ # Load all built-in compilers before any custom compilers
89
+ Dir.glob("#{Tapioca::LIB_ROOT_DIR}/tapioca/dsl/compilers/*.rb").each do |compiler|
90
+ require File.expand_path(compiler)
91
+ end
92
+
93
+ # Load all custom compilers exported from gems
89
94
  ::Gem.find_files("tapioca/dsl/compilers/*.rb").each do |compiler|
90
95
  require File.expand_path(compiler)
91
96
  end
@@ -2,5 +2,5 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Tapioca
5
- VERSION = "0.16.5"
5
+ VERSION = "0.16.6"
6
6
  end
metadata CHANGED
@@ -1,17 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tapioca
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.16.5
4
+ version: 0.16.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ufuk Kayserilioglu
8
8
  - Alan Wu
9
9
  - Alexandre Terrasa
10
10
  - Peter Zhu
11
- autorequire:
12
11
  bindir: exe
13
12
  cert_chain: []
14
- date: 2024-11-27 00:00:00.000000000 Z
13
+ date: 2025-01-06 00:00:00.000000000 Z
15
14
  dependencies:
16
15
  - !ruby/object:Gem::Dependency
17
16
  name: bundler
@@ -125,7 +124,6 @@ dependencies:
125
124
  - - ">="
126
125
  - !ruby/object:Gem::Version
127
126
  version: '0'
128
- description:
129
127
  email:
130
128
  - ruby@shopify.com
131
129
  executables:
@@ -271,7 +269,6 @@ licenses:
271
269
  - MIT
272
270
  metadata:
273
271
  allowed_push_host: https://rubygems.org
274
- post_install_message:
275
272
  rdoc_options: []
276
273
  require_paths:
277
274
  - lib
@@ -286,8 +283,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
286
283
  - !ruby/object:Gem::Version
287
284
  version: '0'
288
285
  requirements: []
289
- rubygems_version: 3.5.23
290
- signing_key:
286
+ rubygems_version: 3.6.2
291
287
  specification_version: 4
292
288
  summary: A Ruby Interface file generator for gems, core types and the Ruby standard
293
289
  library