boba 0.0.11 → 0.0.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0a299592dce0ee68ddd4ad96f0c6d35a665739102bfa02c8e5d3f3cf26ed0531
4
- data.tar.gz: 71d4ebe6c98f11586fef81e8d7b8892adce83bb2fbeafa516369947cbcd0dd9e
3
+ metadata.gz: 0c88ef5656da55d5a864ad1bf3b5231907260c62f2239964f47921368dbffea8
4
+ data.tar.gz: c32f3fe6f3063407146db5559cee2d6714474e9f69eb769ce39278c961908537
5
5
  SHA512:
6
- metadata.gz: b34ab72d0801c768da549880ad247e978538e29197c51ff09b8d0e3c6d18b23e4433a808948613b98f3621ed0f486cd5fc7f44dad2f0e4587aec1070bf14cb0a
7
- data.tar.gz: 1fca80c8c8a75f2763e4682b4b3e5543af3700abe8b2bd3c0fcb097c749d9292b67375c4121fc0ec43c49502678e0c8ee5fd3da59f02ce930a39ec5a1971f302
6
+ metadata.gz: ba9b4ccc7ffb27011848c62428a8cbed4cf9556bc3b63c04a4db867ed08ca12c2f16f48bc9f4c1f0ade04bd769d5033a977bc1e34f85115995c31dcdd65d21e0
7
+ data.tar.gz: dfe9d28722a6191722271c44b64ba2aec62eca438534b9617b2806d460c35c171635a662d6a6033d96220de6f3ff40cf0d4dc5e4122643ecad4fe3b6b6ac9b52
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Boba
2
2
 
3
- > :warning: This gem is in pre-release and is not ready for use.
3
+ > :warning: This software is currently under active development. It should not be considered stable until 1.0.0.
4
4
 
5
5
  Boba is a collection of compilers for Sorbet & Tapioca.
6
6
 
@@ -36,7 +36,8 @@ If you'd like to use relation types in your sigs that are less broad than `Activ
36
36
  gem 'boba'
37
37
  ```
38
38
 
39
- The railtie will automatically define the `PrivateRelation` constant on each model that inherits from `ActiveRecord::Base`. It can then be used in typing, like thus:
39
+ The railtie will automatically define the `PrivateRelation`, `PrivateAssociationRelation`, and `PrivateCollectionProxy` constants on each model that inherits from `ActiveRecord::Base`. These are defined as their corresponding private `ActiveRecord` classes, so runtime type checking works as expected. They can then be used in typing, like so:
40
+
40
41
  ```ruby
41
42
  class Post < ::ActiveRecord::Base
42
43
  scope :recent -> { where('created_at > ?', Date.current) }
@@ -49,7 +50,7 @@ sig { params(author: Author).returns(Post::PrivateRelation) }
49
50
  def posts_from_author(author); end
50
51
  ```
51
52
 
52
- and the following should not raise an error:
53
+ and the following should not raise a Sorbet error:
53
54
 
54
55
  ```ruby
55
56
  sig { params(author: Author).returns(Post::PrivateRelation) }
@@ -58,6 +59,15 @@ def recent_posts_from_author(author)
58
59
  end
59
60
  ```
60
61
 
62
+ Boba also defines a type alias `RelationType` on each such class, which is defined as the union of the three relation types. This is useful because the relation types are often used interchangeably and so you may expect to return or pass any of the three classes as an argument. To use this, you will also need to use the `ActiveRecordRelationTypes` compiler to generate the type alias in the signatures as well (or define them manually in shims).
63
+
64
+ ```ruby
65
+ sig { params(author: Author).returns(Post::RelationType) }
66
+ def recent_posts_from_author(author)
67
+ posts_from_author(author).recent
68
+ end
69
+ ```
70
+
61
71
  ## Contributing
62
72
 
63
73
  Bugs and feature requests are welcome and should be [filed as issues on github](https://github.com/angellist/boba/issues).
@@ -6,13 +6,30 @@ require "rails/railtie"
6
6
  class Boba::RelationsRailtie < Rails::Railtie
7
7
  railtie_name(:boba)
8
8
 
9
- initializer("boba.add_private_relation_constant") do
9
+ initializer("boba.add_private_relation_constants") do
10
10
  ActiveSupport.on_load(:active_record) do
11
11
  module AciveRecordInheritDefineRelationTypes
12
12
  def inherited(child)
13
13
  super(child)
14
14
 
15
- child.const_set("PrivateRelation", Object)
15
+ # Tapioca defines these three classes for each active record model as proxies for the actual AR internal
16
+ # classes. In order to be able to use these as types in signatures, we need to expose them as actual constants
17
+ # at runtime. Tapioca intentionally obfuscates these classes because they're private, so exposing them is
18
+ # _slightly_ dangerous in that someone could do something naughty. But we're not super worried about it.
19
+ child.const_set("PrivateRelation", child.const_get(:ActiveRecord_Relation))
20
+ child.const_set("PrivateAssociationRelation", child.const_get(:ActiveRecord_AssociationRelation))
21
+ child.const_set("PrivateCollectionProxy", child.const_get(:ActiveRecord_Associations_CollectionProxy))
22
+
23
+ # Expose a common type so that signatures can be typed to the broader `RelationType` since the three are often
24
+ # used interchangeably.
25
+ relation_type = T.type_alias do
26
+ T.any(
27
+ child.const_get(:PrivateRelation),
28
+ child.const_get(:PrivateAssociationRelation),
29
+ child.const_get(:PrivateCollectionProxy),
30
+ )
31
+ end
32
+ child.const_set(:RelationType, relation_type)
16
33
  end
17
34
  end
18
35
 
data/lib/boba/version.rb CHANGED
@@ -2,5 +2,5 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Boba
5
- VERSION = "0.0.11"
5
+ VERSION = "0.0.13"
6
6
  end
@@ -0,0 +1,67 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ return unless defined?(ActiveRecord::Base)
5
+
6
+ require "tapioca/dsl/helpers/active_record_constants_helper"
7
+ require "tapioca/dsl/compilers/active_record_relations"
8
+
9
+ return unless defined?(Tapioca::Dsl::Compilers::ActiveRecordRelations)
10
+
11
+ module Tapioca
12
+ module Dsl
13
+ module Compilers
14
+ # `Tapioca::Dsl::Compilers::ActiveRecordRelationTypes` extends `Tapioca::Dsl::Compilers::ActiveRecordRelationTypes`
15
+ # to generate a `RelationType` type alias for each class. This type alias is defined a runtime through the Boba
16
+ # railtie, and is useful for typing signatures to accept or return relations. For instance, with the following
17
+ # `ActiveRecord::Base` subclass:
18
+ # ~~~rb
19
+ # class Post < ApplicationRecord
20
+ # end
21
+ # ~~~
22
+ #
23
+ # This compiler will produce the RBI file `post.rbi` with the following content:
24
+ # ~~~rbi
25
+ # # post.rbi
26
+ # # typed: true
27
+ #
28
+ # class Post
29
+ # RelationType = T.any(PrivateRelation, PrivateAssociationRelation, PrivateCollectionProxy)
30
+ # end
31
+ # ~~~
32
+ # So that the following method will accept any of the private relation types as an argument:
33
+ # ~~~rb
34
+ # sig { params(posts: Post::RelationType).void }
35
+ # def process_posts(posts)
36
+ # # ...
37
+ # end
38
+ # ~~~
39
+ class ActiveRecordRelationTypes < Compiler
40
+ extend T::Sig
41
+
42
+ ConstantType = type_member { { fixed: T.class_of(::ActiveRecord::Base) } }
43
+
44
+ sig { override.void }
45
+ def decorate
46
+ root.create_path(constant) do |rbi_class|
47
+ relation_type_alias = "T.any(" \
48
+ "#{Tapioca::Dsl::Helpers::ActiveRecordConstantsHelper::RelationClassName}, " \
49
+ "#{Tapioca::Dsl::Helpers::ActiveRecordConstantsHelper::AssociationRelationClassName}, " \
50
+ "#{Tapioca::Dsl::Helpers::ActiveRecordConstantsHelper::AssociationsCollectionProxyClassName}" \
51
+ ")"
52
+ rbi_class.create_type_variable("RelationType", type: "T.type_alias { #{relation_type_alias} }")
53
+ end
54
+ end
55
+
56
+ class << self
57
+ extend T::Sig
58
+
59
+ sig { override.returns(T::Enumerable[Module]) }
60
+ def gather_constants
61
+ Tapioca::Dsl::Compilers::ActiveRecordRelations.gather_constants
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -1,7 +1,7 @@
1
- # frozen_string_literal: true
2
1
  # typed: true
2
+ # frozen_string_literal: true
3
3
 
4
- return if !defined?(AttrJson::Record)
4
+ return unless defined?(AttrJson::Record)
5
5
 
6
6
  module Tapioca
7
7
  module Dsl
@@ -39,7 +39,7 @@ module Tapioca
39
39
  # Class methods module is already defined in the gem rbi, so just reference it here.
40
40
  ClassMethodsModuleName = "AttrJson::Record::ClassMethods"
41
41
  InstanceMethodModuleName = "AttrJsonGeneratedMethods"
42
- ConstantType = type_member {{ fixed: T.any(T.class_of(::AttrJson::Record), T.class_of(::AttrJson::Model)) }}
42
+ ConstantType = type_member { { fixed: T.any(T.class_of(::AttrJson::Record), T.class_of(::AttrJson::Model)) } }
43
43
 
44
44
  class << self
45
45
  extend T::Sig
@@ -65,7 +65,7 @@ module Tapioca
65
65
  private
66
66
 
67
67
  def decorate_attributes(rbi_scope)
68
- T.unsafe(constant).attr_json_registry
68
+ constant.attr_json_registry
69
69
  .definitions
70
70
  .sort_by(&:name) # this is annoying, but we need to sort to force consistent ordering or the rbi checks fail
71
71
  .each do |definition|
@@ -122,7 +122,7 @@ module Tapioca
122
122
 
123
123
  sorbet_type = "::#{sorbet_type}"
124
124
  sorbet_type = "T::Array[#{sorbet_type}]" if array
125
- sorbet_type = "T.nilable(#{sorbet_type})" if nilable # todo: improve this
125
+ sorbet_type = "T.nilable(#{sorbet_type})" if nilable # TODO: improve this
126
126
 
127
127
  sorbet_type
128
128
  end
@@ -111,7 +111,7 @@ module Tapioca
111
111
  # end
112
112
  # end
113
113
  # ~~~
114
- class StateMachines < Compiler
114
+ class StateMachinesExtended < Compiler
115
115
  extend T::Sig
116
116
 
117
117
  ACTIVE_RECORD_RELATION_MODULE_NAMES = [
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: boba
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.11
4
+ version: 0.0.13
5
5
  platform: ruby
6
6
  authors:
7
7
  - Angellist
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-11-28 00:00:00.000000000 Z
11
+ date: 2024-12-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sorbet-static-and-runtime
@@ -28,16 +28,16 @@ dependencies:
28
28
  name: tapioca
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - "<="
32
32
  - !ruby/object:Gem::Version
33
- version: 0.16.4
33
+ version: 0.16.5
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - "~>"
38
+ - - "<="
39
39
  - !ruby/object:Gem::Version
40
- version: 0.16.4
40
+ version: 0.16.5
41
41
  description:
42
42
  email:
43
43
  - alex.stathis@angellist.com
@@ -54,18 +54,19 @@ files:
54
54
  - lib/boba/version.rb
55
55
  - lib/tapioca/dsl/compilers/active_record_associations_persisted.rb
56
56
  - lib/tapioca/dsl/compilers/active_record_columns_persisted.rb
57
+ - lib/tapioca/dsl/compilers/active_record_relation_types.rb
57
58
  - lib/tapioca/dsl/compilers/attr_json.rb
58
59
  - lib/tapioca/dsl/compilers/money_rails.rb
59
60
  - lib/tapioca/dsl/compilers/paperclip.rb
60
- - lib/tapioca/dsl/compilers/state_machines.rb
61
+ - lib/tapioca/dsl/compilers/state_machines_extended.rb
61
62
  homepage: https://github.com/angellist/boba
62
63
  licenses:
63
64
  - MIT
64
65
  metadata:
65
66
  bug_tracker_uri: https://github.com/angellist/boba/issues
66
- changelog_uri: https://github.com/angellist/boba/blob/0.0.11/History.md
67
+ changelog_uri: https://github.com/angellist/boba/blob/0.0.13/History.md
67
68
  homepage_uri: https://github.com/angellist/boba
68
- source_code_uri: https://github.com/angellist/boba/tree/0.0.11
69
+ source_code_uri: https://github.com/angellist/boba/tree/0.0.13
69
70
  rubygems_mfa_required: 'true'
70
71
  post_install_message:
71
72
  rdoc_options: []