tapioca 0.4.8 → 0.4.13

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.
@@ -15,7 +15,7 @@ module Tapioca
15
15
  module Compilers
16
16
  module Dsl
17
17
  # `Tapioca::Compilers::Dsl::SmartProperties` generates RBI files for classes that include
18
- # `SmartProperties` (see https://github.com/t6d/smart_properties).
18
+ # [`SmartProperties`](https://github.com/t6d/smart_properties).
19
19
  #
20
20
  # For example, with the following class that includes `SmartProperties`:
21
21
  #
@@ -15,10 +15,12 @@ end
15
15
  module Tapioca
16
16
  module Compilers
17
17
  module Dsl
18
- # `RbiGenerator::StateMachines` generates RBI files for classes that setup a `state_machine`
19
- # (see https://github.com/state-machines/state_machines). The generator also processes the extra
20
- # methods generated by [StateMachines Active Record](https://github.com/state-machines/state_machines-activerecord)
21
- # and [StateMachines ActiveModel](https://github.com/state-machines/state_machines-activemodel) integrations.
18
+ # `Tapioca::Compilers::Dsl::StateMachines` generates RBI files for classes that setup a
19
+ # [`state_machine`](https://github.com/state-machines/state_machines). The generator also
20
+ # processes the extra methods generated by
21
+ # [StateMachines Active Record](https://github.com/state-machines/state_machines-activerecord)
22
+ # and [StateMachines Active Model](https://github.com/state-machines/state_machines-activemodel)
23
+ # integrations.
22
24
  #
23
25
  # For example, with the following `Vehicle` class:
24
26
  #
@@ -45,75 +47,75 @@ module Tapioca
45
47
  # # vehicle.rbi
46
48
  # # typed: true
47
49
  # class Vehicle
48
- # include Vehicle::StateMachineInstanceHelperModule
49
- # extend Vehicle::StateMachineClassHelperModule
50
- # end
50
+ # include StateMachineInstanceHelperModule
51
+ # extend StateMachineClassHelperModule
51
52
  #
52
- # module Vehicle::StateMachineClassHelperModule
53
- # sig { params(event: T.any(String, Symbol)).returns(String) }
54
- # def human_alarm_state_event_name(event); end
53
+ # module StateMachineClassHelperModule
54
+ # sig { params(event: T.any(String, Symbol)).returns(String) }
55
+ # def human_alarm_state_event_name(event); end
55
56
  #
56
- # sig { params(state: T.any(String, Symbol)).returns(String) }
57
- # def human_alarm_state_name(state); end
58
- # end
57
+ # sig { params(state: T.any(String, Symbol)).returns(String) }
58
+ # def human_alarm_state_name(state); end
59
+ # end
59
60
  #
60
- # module Vehicle::StateMachineInstanceHelperModule
61
- # sig { returns(T::Boolean) }
62
- # def alarm_active?; end
61
+ # module StateMachineInstanceHelperModule
62
+ # sig { returns(T::Boolean) }
63
+ # def alarm_active?; end
63
64
  #
64
- # sig { returns(T::Boolean) }
65
- # def alarm_off?; end
65
+ # sig { returns(T::Boolean) }
66
+ # def alarm_off?; end
66
67
  #
67
- # sig { returns(Integer) }
68
- # def alarm_state; end
68
+ # sig { returns(Integer) }
69
+ # def alarm_state; end
69
70
  #
70
- # sig { params(value: Integer).returns(Integer) }
71
- # def alarm_state=(value); end
71
+ # sig { params(value: Integer).returns(Integer) }
72
+ # def alarm_state=(value); end
72
73
  #
73
- # sig { params(state: T.any(String, Symbol)).returns(T::Boolean) }
74
- # def alarm_state?(state); end
74
+ # sig { params(state: T.any(String, Symbol)).returns(T::Boolean) }
75
+ # def alarm_state?(state); end
75
76
  #
76
- # sig { params(args: T.untyped).returns(T::Array[T.any(String, Symbol)]) }
77
- # def alarm_state_events(*args); end
77
+ # sig { params(args: T.untyped).returns(T::Array[T.any(String, Symbol)]) }
78
+ # def alarm_state_events(*args); end
78
79
  #
79
- # sig { returns(T.any(String, Symbol)) }
80
- # def alarm_state_name; end
80
+ # sig { returns(T.any(String, Symbol)) }
81
+ # def alarm_state_name; end
81
82
  #
82
- # sig { params(args: T.untyped).returns(T::Array[::StateMachines::Transition]) }
83
- # def alarm_state_paths(*args); end
83
+ # sig { params(args: T.untyped).returns(T::Array[::StateMachines::Transition]) }
84
+ # def alarm_state_paths(*args); end
84
85
  #
85
- # sig { params(args: T.untyped).returns(T::Array[::StateMachines::Transition]) }
86
- # def alarm_state_transitions(*args); end
86
+ # sig { params(args: T.untyped).returns(T::Array[::StateMachines::Transition]) }
87
+ # def alarm_state_transitions(*args); end
87
88
  #
88
- # sig { returns(T::Boolean) }
89
- # def can_disable_alarm?; end
89
+ # sig { returns(T::Boolean) }
90
+ # def can_disable_alarm?; end
90
91
  #
91
- # sig { returns(T::Boolean) }
92
- # def can_enable_alarm?; end
92
+ # sig { returns(T::Boolean) }
93
+ # def can_enable_alarm?; end
93
94
  #
94
- # sig { params(args: T.untyped).returns(T::Boolean) }
95
- # def disable_alarm(*args); end
95
+ # sig { params(args: T.untyped).returns(T::Boolean) }
96
+ # def disable_alarm(*args); end
96
97
  #
97
- # sig { params(args: T.untyped).returns(T::Boolean) }
98
- # def disable_alarm!(*args); end
98
+ # sig { params(args: T.untyped).returns(T::Boolean) }
99
+ # def disable_alarm!(*args); end
99
100
  #
100
- # sig { params(args: T.untyped).returns(T.nilable(::StateMachines::Transition)) }
101
- # def disable_alarm_transition(*args); end
101
+ # sig { params(args: T.untyped).returns(T.nilable(::StateMachines::Transition)) }
102
+ # def disable_alarm_transition(*args); end
102
103
  #
103
- # sig { params(args: T.untyped).returns(T::Boolean) }
104
- # def enable_alarm(*args); end
104
+ # sig { params(args: T.untyped).returns(T::Boolean) }
105
+ # def enable_alarm(*args); end
105
106
  #
106
- # sig { params(args: T.untyped).returns(T::Boolean) }
107
- # def enable_alarm!(*args); end
107
+ # sig { params(args: T.untyped).returns(T::Boolean) }
108
+ # def enable_alarm!(*args); end
108
109
  #
109
- # sig { params(args: T.untyped).returns(T.nilable(::StateMachines::Transition)) }
110
- # def enable_alarm_transition(*args); end
110
+ # sig { params(args: T.untyped).returns(T.nilable(::StateMachines::Transition)) }
111
+ # def enable_alarm_transition(*args); end
111
112
  #
112
- # sig { params(event: T.any(String, Symbol), args: T.untyped).returns(T::Boolean) }
113
- # def fire_alarm_state_event(event, *args); end
113
+ # sig { params(event: T.any(String, Symbol), args: T.untyped).returns(T::Boolean) }
114
+ # def fire_alarm_state_event(event, *args); end
114
115
  #
115
- # sig { returns(String) }
116
- # def human_alarm_state_name; end
116
+ # sig { returns(String) }
117
+ # def human_alarm_state_name; end
118
+ # end
117
119
  # end
118
120
  # ~~~
119
121
  class StateMachines < Base
@@ -123,34 +125,34 @@ module Tapioca
123
125
  def decorate(root, constant)
124
126
  return if constant.state_machines.empty?
125
127
 
126
- instance_module_name = "#{constant}::StateMachineInstanceHelperModule"
127
- class_module_name = "#{constant}::StateMachineClassHelperModule"
128
+ root.path(constant) do |klass|
129
+ instance_module_name = "StateMachineInstanceHelperModule"
130
+ class_module_name = "StateMachineClassHelperModule"
128
131
 
129
- instance_module = root.create_module(instance_module_name)
130
- class_module = root.create_module(class_module_name)
132
+ instance_module = klass.create_module(instance_module_name)
133
+ class_module = klass.create_module(class_module_name)
131
134
 
132
- constant.state_machines.each_value do |machine|
133
- state_type = state_type_for(machine)
135
+ constant.state_machines.each_value do |machine|
136
+ state_type = state_type_for(machine)
134
137
 
135
- define_state_accessor(instance_module, machine, state_type)
136
- define_state_predicate(instance_module, machine)
137
- define_event_helpers(instance_module, machine)
138
- define_path_helpers(instance_module, machine)
139
- define_name_helpers(instance_module, class_module, machine)
140
- define_scopes(class_module, machine)
138
+ define_state_accessor(instance_module, machine, state_type)
139
+ define_state_predicate(instance_module, machine)
140
+ define_event_helpers(instance_module, machine)
141
+ define_path_helpers(instance_module, machine)
142
+ define_name_helpers(instance_module, class_module, machine)
143
+ define_scopes(class_module, machine)
141
144
 
142
- define_state_methods(instance_module, machine)
143
- define_event_methods(instance_module, machine)
144
- end
145
+ define_state_methods(instance_module, machine)
146
+ define_event_methods(instance_module, machine)
147
+ end
145
148
 
146
- matching_integration_name = ::StateMachines::Integrations.match(constant)&.integration_name
149
+ matching_integration_name = ::StateMachines::Integrations.match(constant)&.integration_name
147
150
 
148
- case matching_integration_name
149
- when :active_record
150
- define_activerecord_methods(instance_module)
151
- end
151
+ case matching_integration_name
152
+ when :active_record
153
+ define_activerecord_methods(instance_module)
154
+ end
152
155
 
153
- root.path(constant) do |klass|
154
156
  klass.create_include(instance_module_name)
155
157
  klass.create_extend(class_module_name)
156
158
  end
@@ -15,8 +15,7 @@ module Tapioca
15
15
  module Compilers
16
16
  module Dsl
17
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).
18
+ # [`Rails.application.routes.url_helpers`](https://api.rubyonrails.org/v5.1.7/classes/ActionDispatch/Routing/UrlFor.html#module-ActionDispatch::Routing::UrlFor-label-URL+generation+for+named+routes).
20
19
  #
21
20
  # For example, with the following setup:
22
21
  #
@@ -32,7 +31,12 @@ module Tapioca
32
31
  # ~~~rb
33
32
  # app/models/post.rb
34
33
  # class Post
35
- # include Rails.application.routes.url_helpers
34
+ # # Use `T.unsafe` so that Sorbet does not complain about a dynamic
35
+ # # module being included. This allows the `include` to happen properly
36
+ # # at runtime but Sorbet won't see the include. However, since this
37
+ # # generator will generate the proper RBI files for the include,
38
+ # # static type checking will work as expected.
39
+ # T.unsafe(self).include Rails.application.routes.url_helpers
36
40
  # end
37
41
  # ~~~
38
42
  #
@@ -126,8 +130,8 @@ module Tapioca
126
130
  sig { params(root: Parlour::RbiGenerator::Namespace, constant: T.class_of(Module)).void }
127
131
  def generate_module_for(root, constant)
128
132
  root.create_module(T.must(constant.name)) do |mod|
129
- mod.create_include("ActionDispatch::Routing::UrlFor")
130
- mod.create_include("ActionDispatch::Routing::PolymorphicRoutes")
133
+ mod.create_include("::ActionDispatch::Routing::UrlFor")
134
+ mod.create_include("::ActionDispatch::Routing::PolymorphicRoutes")
131
135
 
132
136
  constant.instance_methods(false).each do |method|
133
137
  mod.create_method(
@@ -74,9 +74,9 @@ module Tapioca
74
74
  compile(symbol, constant)
75
75
  end
76
76
 
77
- sig { params(symbol: String).returns(BasicObject).checked(:never) }
78
- def resolve_constant(symbol)
79
- Object.const_get(symbol, false)
77
+ sig { params(symbol: String, inherit: T::Boolean).returns(BasicObject).checked(:never) }
78
+ def resolve_constant(symbol, inherit: false)
79
+ Object.const_get(symbol, inherit)
80
80
  rescue NameError, LoadError, RuntimeError, ArgumentError, TypeError
81
81
  nil
82
82
  end
@@ -122,12 +122,15 @@ module Tapioca
122
122
  def compile_alias(name, constant)
123
123
  return if symbol_ignored?(name)
124
124
 
125
- constant_name = name_of(constant)
125
+ target = name_of(constant)
126
+ # If target has no name, let's make it an anonymous class or module with `Class.new` or `Module.new`
127
+ target = "#{constant.class}.new" unless target
128
+
126
129
  add_to_alias_namespace(name)
127
130
 
128
131
  return if IGNORED_SYMBOLS.include?(name)
129
132
 
130
- indented("#{name} = #{constant_name}")
133
+ indented("#{name} = #{target}")
131
134
  end
132
135
 
133
136
  sig do
@@ -292,33 +295,16 @@ module Tapioca
292
295
 
293
296
  sig { params(constant: Module).returns(String) }
294
297
  def compile_mixins(constant)
295
- ignorable_ancestors =
296
- if constant.is_a?(Class)
297
- ancestors = constant.superclass&.ancestors || Object.ancestors
298
- Set.new(ancestors)
299
- else
300
- Module.ancestors
301
- end
302
-
303
- inherited_singleton_class_ancestors =
304
- if constant.is_a?(Class)
305
- Set.new(singleton_class_of(constant.superclass).ancestors)
306
- else
307
- Module.ancestors
308
- end
298
+ singleton_class = singleton_class_of(constant)
309
299
 
310
- interesting_ancestors =
311
- constant.ancestors.reject { |mod| ignorable_ancestors.include?(mod) }
300
+ interesting_ancestors = interesting_ancestors_of(constant)
301
+ interesting_singleton_class_ancestors = interesting_ancestors_of(singleton_class)
312
302
 
313
303
  prepend = interesting_ancestors.take_while { |c| !are_equal?(constant, c) }
314
304
  include = interesting_ancestors.drop(prepend.size + 1)
315
- extend = singleton_class_of(constant).ancestors
316
- .reject do |mod|
317
- mod == singleton_class_of(constant) ||
318
- inherited_singleton_class_ancestors.include?(mod) ||
319
- !public_module?(mod) ||
320
- Module != class_of(mod)
321
- end
305
+ extend = interesting_singleton_class_ancestors.reject do |mod|
306
+ !public_module?(mod) || Module != class_of(mod) || are_equal?(mod, singleton_class)
307
+ end
322
308
 
323
309
  prepends = prepend
324
310
  .reverse
@@ -757,18 +743,59 @@ module Tapioca
757
743
  Module.instance_method(:name).bind(constant).call
758
744
  end
759
745
 
760
- sig { params(constant: BasicObject).returns(Class).checked(:never) }
746
+ sig { params(constant: Module).returns(Class) }
761
747
  def singleton_class_of(constant)
762
748
  Object.instance_method(:singleton_class).bind(constant).call
763
749
  end
764
750
 
751
+ sig { params(constant: Module).returns(T::Array[Module]) }
752
+ def ancestors_of(constant)
753
+ Module.instance_method(:ancestors).bind(constant).call
754
+ end
755
+
756
+ sig { params(constant: Module).returns(T::Array[Module]) }
757
+ def inherited_ancestors_of(constant)
758
+ if Class === constant
759
+ ancestors_of(superclass_of(constant) || Object)
760
+ else
761
+ Module.ancestors
762
+ end
763
+ end
764
+
765
+ sig { params(constant: Module).returns(T::Array[Module]) }
766
+ def interesting_ancestors_of(constant)
767
+ inherited_ancestors_ids = Set.new(
768
+ inherited_ancestors_of(constant).map { |mod| object_id_of(mod) }
769
+ )
770
+ # TODO: There is actually a bug here where this will drop modules that
771
+ # may be included twice. For example:
772
+ #
773
+ # ```ruby
774
+ # class Foo
775
+ # prepend Kernel
776
+ # end
777
+ # ````
778
+ # would give:
779
+ # ```ruby
780
+ # Foo.ancestors #=> [Kernel, Foo, Object, Kernel, BasicObject]
781
+ # ````
782
+ # but since we drop `Kernel` whenever we match it, we would miss
783
+ # the `prepend Kernel` in the output.
784
+ #
785
+ # Instead, we should only drop the tail matches of the ancestors and
786
+ # inherited ancestors, past the location of the constant itself.
787
+ constant.ancestors.reject do |mod|
788
+ inherited_ancestors_ids.include?(object_id_of(mod))
789
+ end
790
+ end
791
+
765
792
  sig { params(constant: Module).returns(T.nilable(String)) }
766
793
  def name_of(constant)
767
794
  name = name_of_proxy_target(constant)
768
795
  return name if name
769
796
  name = raw_name_of(constant)
770
797
  return if name.nil?
771
- return unless are_equal?(constant, resolve_constant(name))
798
+ return unless are_equal?(constant, resolve_constant(name, inherit: true))
772
799
  name = "Struct" if name =~ /^(::)?Struct::[^:]+$/
773
800
  name
774
801
  end
@@ -817,6 +844,11 @@ module Tapioca
817
844
  constant.to_s.gsub(/\bAttachedClass\b/, "T.attached_class")
818
845
  end
819
846
 
847
+ sig { params(object: Object).returns(T::Boolean).checked(:never) }
848
+ def object_id_of(object)
849
+ Object.instance_method(:object_id).bind(object).call
850
+ end
851
+
820
852
  sig { params(constant: Module, other: BasicObject).returns(T::Boolean).checked(:never) }
821
853
  def are_equal?(constant, other)
822
854
  BasicObject.instance_method(:equal?).bind(constant).call(other)
@@ -2,5 +2,5 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Tapioca
5
- VERSION = "0.4.8"
5
+ VERSION = "0.4.13"
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.8
4
+ version: 0.4.13
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-11-05 00:00:00.000000000 Z
14
+ date: 2021-01-15 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: pry
@@ -116,14 +116,15 @@ files:
116
116
  - lib/tapioca/compilers/dsl/active_record_associations.rb
117
117
  - lib/tapioca/compilers/dsl/active_record_columns.rb
118
118
  - lib/tapioca/compilers/dsl/active_record_enum.rb
119
- - lib/tapioca/compilers/dsl/active_record_identity_cache.rb
120
119
  - lib/tapioca/compilers/dsl/active_record_scope.rb
121
120
  - lib/tapioca/compilers/dsl/active_record_typed_store.rb
122
121
  - lib/tapioca/compilers/dsl/active_resource.rb
123
122
  - lib/tapioca/compilers/dsl/active_support_current_attributes.rb
124
123
  - lib/tapioca/compilers/dsl/base.rb
125
124
  - lib/tapioca/compilers/dsl/frozen_record.rb
125
+ - lib/tapioca/compilers/dsl/identity_cache.rb
126
126
  - lib/tapioca/compilers/dsl/protobuf.rb
127
+ - lib/tapioca/compilers/dsl/sidekiq_worker.rb
127
128
  - lib/tapioca/compilers/dsl/smart_properties.rb
128
129
  - lib/tapioca/compilers/dsl/state_machines.rb
129
130
  - lib/tapioca/compilers/dsl/url_helpers.rb