tapioca 0.4.4 → 0.4.5

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: f9b10c318332131210706b023ccbe3a63a5889153e3b365a9c454ee7311d2498
4
- data.tar.gz: 6c5a5ef82c6a952b535f6f04b3c3127968bca4d8d101e321e13e8e5968ea3222
3
+ metadata.gz: a6ca30be86eac3958354ca4204904993088bfeeef36c72d63b428ccc5ff3eff3
4
+ data.tar.gz: e218a8ed57c6eb6c46b00f423771fa1a4874a8f0a945d8f5683e849fe5ca9e91
5
5
  SHA512:
6
- metadata.gz: f1df6d85dd2924d25e3d6460749cc5a5ce75022bf999e663c813d6dd483e130b6a1c9873872f2412046d6d15f87effc125bc2f3941323f0ac3eca27a0bfa89a7
7
- data.tar.gz: f6b81c3d32e1cec689ed022c17f57ada8655c0594c944c396d26a481e32dce029b77ed8f877063bfee138405019f6bb5408f2de3148d437a2b8db01a699063c5
6
+ metadata.gz: 39b2b67d35896d998f0f11dcdeda386e6e8312e124b12d8e5e252650417ba3c8a452aedfe014059064ca69e465d97089bfad55f1801d6667612902a4edc24903
7
+ data.tar.gz: 0fa4fe00a45d1f0e870f6d2cf985291ee1ed3f78d339c1f27be5653cff99490de31c6907abc462f1c1c7c197d7eb3eba63c2a90764c56a89fd607fd950c4a493
data/Gemfile CHANGED
@@ -11,6 +11,7 @@ group(:deployment, :development) do
11
11
  end
12
12
 
13
13
  gem("bundler", "~> 1.17")
14
+ gem("yard", "~> 0.9.25")
14
15
  gem("pry-byebug")
15
16
  gem("minitest")
16
17
  gem("minitest-hooks")
data/README.md CHANGED
@@ -97,6 +97,12 @@ Command: `tapioca todo`
97
97
 
98
98
  This will generate the file `sorbet/rbi/todo.rbi` defining all unresolved constants as empty modules.
99
99
 
100
+ ### Generate DSL RBI files
101
+
102
+ Command: `tapioca dsl [constant...]`
103
+
104
+ This will generate DSL RBIs for specified constants (or for all handled constants, if a constant name is not supplied). You can read about DSL RBI generators supplied by `tapioca` in [the manual](manual/generators.md).
105
+
100
106
  ### Flags
101
107
 
102
108
  - `--prerequire [file]`: A file to be required before `Bundler.require` is called.
@@ -105,10 +111,6 @@ This will generate the file `sorbet/rbi/todo.rbi` defining all unresolved consta
105
111
  - `--generate-command [command]`: The command to run to regenerate RBI files (used in header comment of the RBI files), defaults to the current command.
106
112
  - `--typed-overrides [gem:level]`: Overrides typed sigils for generated gem RBIs for gem `gem` to level `level` (`level` can be one of `ignore`, `false`, `true`, `strict`, or `strong`, see [the Sorbet docs](https://sorbet.org/docs/static#file-level-granularity-strictness-levels) for more details).
107
113
 
108
- ### Strong typing option for ActiveRecord column methods
109
-
110
- `tapioca` gives you the option to generate stricter type signatures for your ActiveRecord column types. By default, methods generated for columns that are defined in the schema have signatures of T.untyped. However, if the object extends a module with name StrongTypeGeneration, tapioca will generate stricter signatures that follow closely with the types defined in the schema. Expectation is the StrongTypeGeneration module you define in your application won't allow objects to be initialized with "bad state". It will check all the attributes that are not nillable to ensure they are not nil.
111
-
112
114
  ## Contributing
113
115
 
114
116
  Bug reports and pull requests are welcome on GitHub at https://github.com/Shopify/tapioca. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](https://github.com/Shopify/tapioca/blob/master/CODE_OF_CONDUCT.md) code of conduct.
data/Rakefile CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "bundler/gem_tasks"
4
4
  require "rake/testtask"
5
+ Dir['tasks/**/*.rake'].each { |t| load t }
5
6
 
6
7
  Rake.application.options.trace = false
7
8
 
@@ -42,6 +42,7 @@ module Tapioca
42
42
  # # ...
43
43
  # end
44
44
  # end
45
+ # ~~~
45
46
  #
46
47
  # this generator will produce an RBI file `user_controller.rbi` with the following content:
47
48
  #
@@ -17,6 +17,17 @@ module Tapioca
17
17
  # responsible for defining the attribute methods that would be created for the columns that
18
18
  # are defined in the Active Record model.
19
19
  #
20
+ # **Note:** This generator, by default, generates weak signatures for column methods and treats each
21
+ # column to be `T.untyped`. This is done on purpose to ensure that the nilability of Active Record
22
+ # columns do not make it hard for existing code to adopt gradual typing. It is possible, however, to
23
+ # generate stricter type signatures for your ActiveRecord column types. If your ActiveRecord model extends
24
+ # a module with name `StrongTypeGeneration`, this generator will generate stricter signatures that follow
25
+ # closely with the types defined in the schema.
26
+ #
27
+ # The `StrongTypeGeneration` module you define in your application should add an `after_initialize` callback
28
+ # to the model and ensure that all the non-nilable attributes of the model are actually initialized with non-`nil`
29
+ # values.
30
+ #
20
31
  # For example, with the following model class:
21
32
  #
22
33
  # ~~~rb
@@ -16,7 +16,7 @@ module Tapioca
16
16
  module Compilers
17
17
  module Dsl
18
18
  # `Tapioca::Compilers::DSL::ActiveRecordIdentityCache` generates RBI files for ActiveRecord models
19
- # that use `include IdentityCache`
19
+ # that use `include IdentityCache`.
20
20
  # `IdentityCache` is a blob level caching solution to plug into ActiveRecord. (see https://github.com/Shopify/identity_cache).
21
21
  #
22
22
  # For example, with the following ActiveRecord class:
@@ -61,7 +61,6 @@ module Tapioca
61
61
  # def fetch_by_title_and_review_date(title, review_date, includes: nil); end
62
62
  # end
63
63
  # ~~~
64
-
65
64
  class ActiveRecordIdentityCache < Base
66
65
  extend T::Sig
67
66
 
@@ -37,7 +37,7 @@ module Tapioca
37
37
  # module Post::GeneratedRelationMethods
38
38
  # sig { params(args: T.untyped, blk: T.untyped).returns(T.untyped) }
39
39
  # def private_kind(*args, &blk); end
40
-
40
+ #
41
41
  # sig { params(args: T.untyped, blk: T.untyped).returns(T.untyped) }
42
42
  # def public_kind(*args, &blk); end
43
43
  # end
@@ -86,8 +86,6 @@ module Tapioca
86
86
  # def saved_change_to_reviewed; end
87
87
  # end
88
88
  # ~~~
89
- # end
90
-
91
89
  class ActiveRecordTypedStore < Base
92
90
  extend T::Sig
93
91
 
@@ -34,7 +34,7 @@ module Tapioca
34
34
  # # ...
35
35
  # end
36
36
  # end
37
- # ~~~rb
37
+ # ~~~
38
38
  #
39
39
  # this generator will produce an RBI file with the following content:
40
40
  # ~~~rbi
@@ -12,19 +12,19 @@ module Tapioca
12
12
  module Compilers
13
13
  module Dsl
14
14
  # `Tapioca::Compilers::Dsl::Protobuf` decorates RBI files for subclasses of
15
- # `Google::Protobuf::MessageExts`.
16
- # (see https://github.com/coinbase/protoc-gen-rbi).
15
+ # `Google::Protobuf::MessageExts` (see https://github.com/protocolbuffers/protobuf/tree/master/ruby).
17
16
  #
18
17
  # For example, with the following "cart.rb" file:
19
18
  #
20
19
  # ~~~rb
21
20
  # Google::Protobuf::DescriptorPool.generated_pool.build do
22
- # add_file("cart.proto", :syntax => :proto3) do
23
- # add_message "MyCart" do
24
- # optional :shop_id, :int32, 1
25
- # optional :customer_id, :int64, 2
26
- # optional :number_value, :double, 3
27
- # optional :string_value, :string, 4
21
+ # add_file("cart.proto", :syntax => :proto3) do
22
+ # add_message "MyCart" do
23
+ # optional :shop_id, :int32, 1
24
+ # optional :customer_id, :int64, 2
25
+ # optional :number_value, :double, 3
26
+ # optional :string_value, :string, 4
27
+ # end
28
28
  # end
29
29
  # end
30
30
  # ~~~
@@ -14,6 +14,74 @@ end
14
14
  module Tapioca
15
15
  module Compilers
16
16
  module Dsl
17
+ # `Tapioca::Compilers::Dsl::UrlHelpers` generates RBI files for classes that include or extend
18
+ # `Rails.application.routes.url_helpers`
19
+ # (see https://api.rubyonrails.org/v5.1.7/classes/ActionDispatch/Routing/UrlFor.html#module-ActionDispatch::Routing::UrlFor-label-URL+generation+for+named+routes).
20
+ #
21
+ # For example, with the following setup:
22
+ #
23
+ # ~~~rb
24
+ # # config/application.rb
25
+ # class Application < Rails::Application
26
+ # routes.draw do
27
+ # resource :index
28
+ # end
29
+ # end
30
+ # ~~~
31
+ #
32
+ # ~~~rb
33
+ # app/models/post.rb
34
+ # class Post
35
+ # include Rails.application.routes.url_helpers
36
+ # end
37
+ # ~~~
38
+ #
39
+ # this generator will produce the following RBI files:
40
+ #
41
+ # ~~~rbi
42
+ # # generated_path_helpers_module.rbi
43
+ # # typed: true
44
+ # module GeneratedPathHelpersModule
45
+ # include ActionDispatch::Routing::PolymorphicRoutes
46
+ # include ActionDispatch::Routing::UrlFor
47
+ #
48
+ # sig { params(args: T.untyped).returns(String) }
49
+ # def edit_index_path(*args); end
50
+ #
51
+ # sig { params(args: T.untyped).returns(String) }
52
+ # def index_path(*args); end
53
+ #
54
+ # sig { params(args: T.untyped).returns(String) }
55
+ # def new_index_path(*args); end
56
+ # end
57
+ # ~~~
58
+ #
59
+ # ~~~rbi
60
+ # # generated_url_helpers_module.rbi
61
+ # # typed: true
62
+ # module GeneratedUrlHelpersModule
63
+ # include ActionDispatch::Routing::PolymorphicRoutes
64
+ # include ActionDispatch::Routing::UrlFor
65
+ #
66
+ # sig { params(args: T.untyped).returns(String) }
67
+ # def edit_index_url(*args); end
68
+ #
69
+ # sig { params(args: T.untyped).returns(String) }
70
+ # def index_url(*args); end
71
+ #
72
+ # sig { params(args: T.untyped).returns(String) }
73
+ # def new_index_url(*args); end
74
+ # end
75
+ # ~~~
76
+ #
77
+ # ~~~rbi
78
+ # # post.rbi
79
+ # # typed: true
80
+ # class Post
81
+ # include GeneratedPathHelpersModule
82
+ # include GeneratedUrlHelpersModule
83
+ # end
84
+ # ~~~
17
85
  class UrlHelpers < Base
18
86
  extend T::Sig
19
87
 
@@ -383,8 +383,18 @@ module Tapioca
383
383
  indented("include(#{qualified_name_of(mod)})")
384
384
  end.join("\n")
385
385
 
386
- mixed_in_module = dynamic_extends.find do |mod|
387
- mod != constant && public_module?(mod)
386
+ ancestors = singleton_class_of(constant).ancestors
387
+ extends_as_concern = ancestors.any? do |mod|
388
+ qualified_name_of(mod) == "::ActiveSupport::Concern"
389
+ end
390
+ class_methods_module = resolve_constant("#{name_of(constant)}::ClassMethods")
391
+
392
+ mixed_in_module = if extends_as_concern && Module === class_methods_module
393
+ class_methods_module
394
+ else
395
+ dynamic_extends.find do |mod|
396
+ mod != constant && public_module?(mod)
397
+ end
388
398
  end
389
399
 
390
400
  return result if mixed_in_module.nil?
@@ -494,15 +504,16 @@ module Tapioca
494
504
  return if symbol_ignored?(symbol_name) && !method_in_gem?(method)
495
505
 
496
506
  signature = signature_of(method)
497
- method = signature.method if signature
507
+ method = T.let(signature.method, UnboundMethod) if signature
498
508
 
499
509
  method_name = method.name.to_s
500
510
  return unless valid_method_name?(method_name)
501
511
  return if struct_method?(constant, method_name)
502
512
  return if method_name.start_with?("__t_props_generated_")
503
513
 
504
- params = T.let(method.parameters, T::Array[T::Array[Symbol]])
505
- parameters = params.map do |(type, name)|
514
+ parameters = T.let(method.parameters, T::Array[[Symbol, T.nilable(Symbol)]])
515
+
516
+ sanitized_parameters = parameters.map do |type, name|
506
517
  unless name
507
518
  # For attr_writer methods, Sorbet signatures have the name
508
519
  # of the method (without the trailing = sign) as the name of
@@ -512,14 +523,15 @@ module Tapioca
512
523
  # method and the parameter is required and there is a single
513
524
  # parameter and the signature also defines a single parameter and
514
525
  # the name of the method ends with a = character.
515
- writer_method_with_sig = signature &&
516
- type == :req &&
517
- params.size == 1 &&
526
+ writer_method_with_sig = (
527
+ signature && type == :req &&
528
+ parameters.size == 1 &&
518
529
  signature.arg_types.size == 1 &&
519
530
  method_name[-1] == "="
531
+ )
520
532
 
521
533
  name = if writer_method_with_sig
522
- method_name[0...-1].to_sym
534
+ T.must(method_name[0...-1]).to_sym
523
535
  else
524
536
  :_
525
537
  end
@@ -528,6 +540,10 @@ module Tapioca
528
540
  # Sanitize param names
529
541
  name = name.to_s.gsub(/[^a-zA-Z0-9_]/, '_')
530
542
 
543
+ [type, name]
544
+ end
545
+
546
+ parameter_list = sanitized_parameters.map do |type, name|
531
547
  case type
532
548
  when :req
533
549
  name
@@ -546,25 +562,30 @@ module Tapioca
546
562
  end
547
563
  end.join(', ')
548
564
 
549
- parameters = "(#{parameters})" if parameters != ""
565
+ parameter_list = "(#{parameter_list})" if parameter_list != ""
566
+ signature_str = indented(compile_signature(signature, sanitized_parameters)) if signature
550
567
 
551
- signature_str = indented(compile_signature(signature)) if signature
552
568
  [
553
569
  signature_str,
554
- indented("def #{method_name}#{parameters}; end"),
570
+ indented("def #{method_name}#{parameter_list}; end"),
555
571
  ].compact.join("\n")
556
572
  end
557
573
 
558
574
  TYPE_PARAMETER_MATCHER = /T\.type_parameter\(:?([[:word:]]+)\)/
559
575
 
560
- sig { params(signature: T.untyped).returns(String) }
561
- def compile_signature(signature)
562
- params = signature.arg_types
563
- params += signature.kwarg_types.to_a
564
- params << [signature.rest_name, signature.rest_type] if signature.has_rest
565
- params << [signature.block_name, signature.block_type] if signature.block_name
576
+ sig { params(signature: T.untyped, parameters: T::Array[[Symbol, String]]).returns(String) }
577
+ def compile_signature(signature, parameters)
578
+ parameter_types = T.let(signature.arg_types.to_h, T::Hash[Symbol, T::Types::Base])
579
+ parameter_types.merge!(signature.kwarg_types)
580
+ parameter_types[signature.rest_name] = signature.rest_type if signature.has_rest
581
+ parameter_types[signature.keyrest_name] = signature.keyrest_type if signature.has_keyrest
582
+ parameter_types[signature.block_name] = signature.block_type if signature.block_name
583
+
584
+ params = parameters.map do |_, name|
585
+ type = parameter_types[name.to_sym]
586
+ "#{name}: #{type}"
587
+ end.join(", ")
566
588
 
567
- params = params.compact.map { |name, type| "#{name}: #{type}" }.join(", ")
568
589
  returns = type_of(signature.return_type)
569
590
 
570
591
  type_parameters = (params + returns).scan(TYPE_PARAMETER_MATCHER).flatten.uniq.map { |p| ":#{p}" }.join(", ")
@@ -2,5 +2,5 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Tapioca
5
- VERSION = "0.4.4"
5
+ VERSION = "0.4.5"
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.4.4
4
+ version: 0.4.5
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: 2020-08-26 00:00:00.000000000 Z
14
+ date: 2020-09-10 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: pry
@@ -155,7 +155,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
155
155
  requirements:
156
156
  - - ">="
157
157
  - !ruby/object:Gem::Version
158
- version: 2.3.7
158
+ version: '2.4'
159
159
  required_rubygems_version: !ruby/object:Gem::Requirement
160
160
  requirements:
161
161
  - - ">="